Example: block-handle
import 'prosekit/basic/style.css'
import 'prosekit/basic/typography.css'
import { useMemo } from 'preact/hooks'
import { createEditor } from 'prosekit/core'
import { ProseKit } from 'prosekit/preact'
import BlockHandle from './block-handle'
import { DEFAULT_DRAG_AND_DROP_CONTENT } from './default-content-drag-and-drop'
import DropIndicator from './drop-indicator'
import { defineExtension } from './extension'
export default function Editor() {
const editor = useMemo(() => {
const extension = defineExtension()
return createEditor({ extension, defaultContent: DEFAULT_DRAG_AND_DROP_CONTENT })
}, [])
return (
<ProseKit editor={editor}>
<div className="box-border h-full w-full min-h-36 overflow-y-hidden overflow-x-hidden rounded-md border border-solid border-gray-200 dark:border-gray-700 shadow-sm flex flex-col bg-white dark:bg-gray-950 color-black dark:color-white">
<div className="relative w-full flex-1 box-border overflow-y-scroll">
<div ref={editor.mount} className="ProseMirror box-border min-h-full px-[max(4rem,calc(50%-20rem))] py-8 outline-hidden outline-0 [&_span[data-mention=user]]:text-blue-500 [&_span[data-mention=tag]]:text-violet-500"></div>
<BlockHandle />
<DropIndicator />
</div>
</div>
</ProseKit>
)
}
import { defineBasicExtension } from 'prosekit/basic'
import { union } from 'prosekit/core'
export function defineExtension() {
return union([defineBasicExtension()])
}
export type EditorExtension = ReturnType<typeof defineExtension>
import {
BlockHandleAdd,
BlockHandleDraggable,
BlockHandlePopover,
} from 'prosekit/preact/block-handle'
export default function BlockHandle() {
return (
<BlockHandlePopover className="flex items-center flex-row box-border justify-center transition border-0 [&:not([data-state])]:hidden will-change-transform data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=open]:fade-in-0 data-[state=closed]:fade-out-0 data-[state=open]:zoom-in-95 data-[state=closed]:zoom-out-95 data-[state=open]:animate-duration-150 data-[state=closed]:animate-duration-200">
<BlockHandleAdd className="flex items-center box-border justify-center h-[1.5em] w-[1.5em] hover:bg-gray-100 dark:hover:bg-gray-800 rounded-sm text-gray-500/50 dark:text-gray-500/50 cursor-pointer">
<div className="i-lucide-plus h-5 w-5" />
</BlockHandleAdd>
<BlockHandleDraggable className="flex items-center box-border justify-center h-[1.5em] w-[1.2em] hover:bg-gray-100 dark:hover:bg-gray-800 rounded-sm text-gray-500/50 dark:text-gray-500/50 cursor-grab">
<div className="i-lucide-grip-vertical h-5 w-5" />
</BlockHandleDraggable>
</BlockHandlePopover>
)
}
// This is the default content for drag and drop examples.
export const DEFAULT_DRAG_AND_DROP_CONTENT = `
<h1>Drag and Drop Demo</h1>
<p>Try dragging any paragraph or heading by clicking on the handle that appears on the left when you hover over it.</p>
<h2>Getting Started</h2>
<p>Hover over any block to see the drag handle appear. Click and drag to reorder content.</p>
<p>This paragraph can be moved above or below other blocks.</p>
<h2>Different Block Types</h2>
<p>You can drag paragraphs, headings, lists, code blocks, and more.</p>
<h3>Lists Work Too</h3>
<ul>
<li>This entire list can be dragged</li>
<li>Individual list items stay together</li>
<li>Try moving this list around</li>
</ul>
<ol>
<li>Ordered lists also support dragging</li>
<li>The numbering updates automatically</li>
<li>Drag this list to see it in action</li>
</ol>
<h3>Code Blocks</h3>
<p>Even code blocks can be moved:</p>
<pre data-language="javascript"><code>// This code block can be dragged
function dragAndDrop() {
return "Easy to rearrange!"
}</code></pre>
<h2>Nested Content</h2>
<blockquote>
<p>This blockquote can be moved as a single unit.</p>
<blockquote>
<p>Nested blockquotes move together with their parent.</p>
</blockquote>
</blockquote>
<h2>Try It Yourself</h2>
<p>Practice by moving this paragraph to the top of the document.</p>
<p>Or drag this one to between the headings above.</p>
<p>The drag handles make it easy to reorganize your content exactly how you want it.</p>
`
import { DropIndicator as BaseDropIndicator } from 'prosekit/preact/drop-indicator'
export default function DropIndicator() {
return <BaseDropIndicator className="z-50 transition-all bg-blue-500" />
}
import 'prosekit/basic/style.css'
import 'prosekit/basic/typography.css'
import { createEditor } from 'prosekit/core'
import { ProseKit } from 'prosekit/react'
import { useMemo } from 'react'
import BlockHandle from './block-handle'
import { DEFAULT_DRAG_AND_DROP_CONTENT } from './default-content-drag-and-drop'
import DropIndicator from './drop-indicator'
import { defineExtension } from './extension'
export default function Editor() {
const editor = useMemo(() => {
const extension = defineExtension()
return createEditor({ extension, defaultContent: DEFAULT_DRAG_AND_DROP_CONTENT })
}, [])
return (
<ProseKit editor={editor}>
<div className="box-border h-full w-full min-h-36 overflow-y-hidden overflow-x-hidden rounded-md border border-solid border-gray-200 dark:border-gray-700 shadow-sm flex flex-col bg-white dark:bg-gray-950 color-black dark:color-white">
<div className="relative w-full flex-1 box-border overflow-y-scroll">
<div ref={editor.mount} className="ProseMirror box-border min-h-full px-[max(4rem,calc(50%-20rem))] py-8 outline-hidden outline-0 [&_span[data-mention=user]]:text-blue-500 [&_span[data-mention=tag]]:text-violet-500"></div>
<BlockHandle />
<DropIndicator />
</div>
</div>
</ProseKit>
)
}
import { defineBasicExtension } from 'prosekit/basic'
import { union } from 'prosekit/core'
export function defineExtension() {
return union([defineBasicExtension()])
}
export type EditorExtension = ReturnType<typeof defineExtension>
import {
BlockHandleAdd,
BlockHandleDraggable,
BlockHandlePopover,
} from 'prosekit/react/block-handle'
export default function BlockHandle() {
return (
<BlockHandlePopover className="flex items-center flex-row box-border justify-center transition border-0 [&:not([data-state])]:hidden will-change-transform data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=open]:fade-in-0 data-[state=closed]:fade-out-0 data-[state=open]:zoom-in-95 data-[state=closed]:zoom-out-95 data-[state=open]:animate-duration-150 data-[state=closed]:animate-duration-200">
<BlockHandleAdd className="flex items-center box-border justify-center h-[1.5em] w-[1.5em] hover:bg-gray-100 dark:hover:bg-gray-800 rounded-sm text-gray-500/50 dark:text-gray-500/50 cursor-pointer">
<div className="i-lucide-plus h-5 w-5" />
</BlockHandleAdd>
<BlockHandleDraggable className="flex items-center box-border justify-center h-[1.5em] w-[1.2em] hover:bg-gray-100 dark:hover:bg-gray-800 rounded-sm text-gray-500/50 dark:text-gray-500/50 cursor-grab">
<div className="i-lucide-grip-vertical h-5 w-5" />
</BlockHandleDraggable>
</BlockHandlePopover>
)
}
// This is the default content for drag and drop examples.
export const DEFAULT_DRAG_AND_DROP_CONTENT = `
<h1>Drag and Drop Demo</h1>
<p>Try dragging any paragraph or heading by clicking on the handle that appears on the left when you hover over it.</p>
<h2>Getting Started</h2>
<p>Hover over any block to see the drag handle appear. Click and drag to reorder content.</p>
<p>This paragraph can be moved above or below other blocks.</p>
<h2>Different Block Types</h2>
<p>You can drag paragraphs, headings, lists, code blocks, and more.</p>
<h3>Lists Work Too</h3>
<ul>
<li>This entire list can be dragged</li>
<li>Individual list items stay together</li>
<li>Try moving this list around</li>
</ul>
<ol>
<li>Ordered lists also support dragging</li>
<li>The numbering updates automatically</li>
<li>Drag this list to see it in action</li>
</ol>
<h3>Code Blocks</h3>
<p>Even code blocks can be moved:</p>
<pre data-language="javascript"><code>// This code block can be dragged
function dragAndDrop() {
return "Easy to rearrange!"
}</code></pre>
<h2>Nested Content</h2>
<blockquote>
<p>This blockquote can be moved as a single unit.</p>
<blockquote>
<p>Nested blockquotes move together with their parent.</p>
</blockquote>
</blockquote>
<h2>Try It Yourself</h2>
<p>Practice by moving this paragraph to the top of the document.</p>
<p>Or drag this one to between the headings above.</p>
<p>The drag handles make it easy to reorganize your content exactly how you want it.</p>
`
import { DropIndicator as BaseDropIndicator } from 'prosekit/react/drop-indicator'
export default function DropIndicator() {
return <BaseDropIndicator className="z-50 transition-all bg-blue-500" />
}
import 'prosekit/basic/style.css'
import 'prosekit/basic/typography.css'
import { createEditor } from 'prosekit/core'
import { ProseKit } from 'prosekit/solid'
import BlockHandle from './block-handle'
import { DEFAULT_DRAG_AND_DROP_CONTENT } from './default-content-drag-and-drop'
import DropIndicator from './drop-indicator'
import { defineExtension } from './extension'
export default function Editor() {
const editor = createEditor({
extension: defineExtension(),
defaultContent: DEFAULT_DRAG_AND_DROP_CONTENT,
})
return (
<ProseKit editor={editor}>
<div class="box-border h-full w-full min-h-36 overflow-y-hidden overflow-x-hidden rounded-md border border-solid border-gray-200 dark:border-gray-700 shadow-sm flex flex-col bg-white dark:bg-gray-950 color-black dark:color-white">
<div class="relative w-full flex-1 box-border overflow-y-scroll">
<div ref={editor.mount} class="ProseMirror box-border min-h-full px-[max(4rem,calc(50%-20rem))] py-8 outline-hidden outline-0 [&_span[data-mention=user]]:text-blue-500 [&_span[data-mention=tag]]:text-violet-500" />
<BlockHandle />
<DropIndicator />
</div>
</div>
</ProseKit>
)
}
import { defineBasicExtension } from 'prosekit/basic'
import { union } from 'prosekit/core'
export function defineExtension() {
return union([defineBasicExtension()])
}
export type EditorExtension = ReturnType<typeof defineExtension>
import {
BlockHandleAdd,
BlockHandleDraggable,
BlockHandlePopover,
} from 'prosekit/solid/block-handle'
export default function BlockHandle() {
return (
<BlockHandlePopover class="flex items-center flex-row box-border justify-center transition border-0 [&:not([data-state])]:hidden will-change-transform data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=open]:fade-in-0 data-[state=closed]:fade-out-0 data-[state=open]:zoom-in-95 data-[state=closed]:zoom-out-95 data-[state=open]:animate-duration-150 data-[state=closed]:animate-duration-200">
<BlockHandleAdd class="flex items-center box-border justify-center h-[1.5em] w-[1.5em] hover:bg-gray-100 dark:hover:bg-gray-800 rounded-sm text-gray-500/50 dark:text-gray-500/50 cursor-pointer">
<div class="i-lucide-plus h-5 w-5" />
</BlockHandleAdd>
<BlockHandleDraggable class="flex items-center box-border justify-center h-[1.5em] w-[1.2em] hover:bg-gray-100 dark:hover:bg-gray-800 rounded-sm text-gray-500/50 dark:text-gray-500/50 cursor-grab">
<div class="i-lucide-grip-vertical h-5 w-5" />
</BlockHandleDraggable>
</BlockHandlePopover>
)
}
// This is the default content for drag and drop examples.
export const DEFAULT_DRAG_AND_DROP_CONTENT = `
<h1>Drag and Drop Demo</h1>
<p>Try dragging any paragraph or heading by clicking on the handle that appears on the left when you hover over it.</p>
<h2>Getting Started</h2>
<p>Hover over any block to see the drag handle appear. Click and drag to reorder content.</p>
<p>This paragraph can be moved above or below other blocks.</p>
<h2>Different Block Types</h2>
<p>You can drag paragraphs, headings, lists, code blocks, and more.</p>
<h3>Lists Work Too</h3>
<ul>
<li>This entire list can be dragged</li>
<li>Individual list items stay together</li>
<li>Try moving this list around</li>
</ul>
<ol>
<li>Ordered lists also support dragging</li>
<li>The numbering updates automatically</li>
<li>Drag this list to see it in action</li>
</ol>
<h3>Code Blocks</h3>
<p>Even code blocks can be moved:</p>
<pre data-language="javascript"><code>// This code block can be dragged
function dragAndDrop() {
return "Easy to rearrange!"
}</code></pre>
<h2>Nested Content</h2>
<blockquote>
<p>This blockquote can be moved as a single unit.</p>
<blockquote>
<p>Nested blockquotes move together with their parent.</p>
</blockquote>
</blockquote>
<h2>Try It Yourself</h2>
<p>Practice by moving this paragraph to the top of the document.</p>
<p>Or drag this one to between the headings above.</p>
<p>The drag handles make it easy to reorganize your content exactly how you want it.</p>
`
import { DropIndicator as BaseDropIndicator } from 'prosekit/solid/drop-indicator'
export default function DropIndicator() {
return <BaseDropIndicator class="z-50 transition-all bg-blue-500" />
}
<script lang="ts">
import 'prosekit/basic/style.css'
import 'prosekit/basic/typography.css'
import { createEditor } from 'prosekit/core'
import { ProseKit } from 'prosekit/svelte'
import BlockHandle from './block-handle.svelte'
import { DEFAULT_DRAG_AND_DROP_CONTENT } from './default-content-drag-and-drop'
import DropIndicator from './drop-indicator.svelte'
import { defineExtension } from './extension'
const editor = createEditor({ extension: defineExtension(), defaultContent: DEFAULT_DRAG_AND_DROP_CONTENT })
const mount = (element: HTMLElement) => {
editor.mount(element)
return { destroy: () => editor.unmount() }
}
</script>
<ProseKit {editor}>
<div class="box-border h-full w-full min-h-36 overflow-y-hidden overflow-x-hidden rounded-md border border-solid border-gray-200 dark:border-gray-700 shadow-sm flex flex-col bg-white dark:bg-gray-950 color-black dark:color-white">
<div class="relative w-full flex-1 box-border overflow-y-scroll">
<div use:mount class="ProseMirror box-border min-h-full px-[max(4rem,calc(50%-20rem))] py-8 outline-hidden outline-0 [&_span[data-mention=user]]:text-blue-500 [&_span[data-mention=tag]]:text-violet-500"></div>
<BlockHandle />
<DropIndicator />
</div>
</div>
</ProseKit>
import { defineBasicExtension } from 'prosekit/basic'
import { union } from 'prosekit/core'
export function defineExtension() {
return union([defineBasicExtension()])
}
export type EditorExtension = ReturnType<typeof defineExtension>
<script lang="ts">
import {
BlockHandleAdd,
BlockHandleDraggable,
BlockHandlePopover,
} from 'prosekit/svelte/block-handle'
</script>
<BlockHandlePopover class="flex items-center flex-row box-border justify-center transition border-0 [&:not([data-state])]:hidden will-change-transform data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=open]:fade-in-0 data-[state=closed]:fade-out-0 data-[state=open]:zoom-in-95 data-[state=closed]:zoom-out-95 data-[state=open]:animate-duration-150 data-[state=closed]:animate-duration-200">
<BlockHandleAdd class="flex items-center box-border justify-center h-[1.5em] w-[1.5em] hover:bg-gray-100 dark:hover:bg-gray-800 rounded-sm text-gray-500/50 dark:text-gray-500/50 cursor-pointer">
<div class="i-lucide-plus h-5 w-5"></div>
</BlockHandleAdd>
<BlockHandleDraggable class="flex items-center box-border justify-center h-[1.5em] w-[1.2em] hover:bg-gray-100 dark:hover:bg-gray-800 rounded-sm text-gray-500/50 dark:text-gray-500/50 cursor-grab">
<div class="i-lucide-grip-vertical h-5 w-5"></div>
</BlockHandleDraggable>
</BlockHandlePopover>
// This is the default content for drag and drop examples.
export const DEFAULT_DRAG_AND_DROP_CONTENT = `
<h1>Drag and Drop Demo</h1>
<p>Try dragging any paragraph or heading by clicking on the handle that appears on the left when you hover over it.</p>
<h2>Getting Started</h2>
<p>Hover over any block to see the drag handle appear. Click and drag to reorder content.</p>
<p>This paragraph can be moved above or below other blocks.</p>
<h2>Different Block Types</h2>
<p>You can drag paragraphs, headings, lists, code blocks, and more.</p>
<h3>Lists Work Too</h3>
<ul>
<li>This entire list can be dragged</li>
<li>Individual list items stay together</li>
<li>Try moving this list around</li>
</ul>
<ol>
<li>Ordered lists also support dragging</li>
<li>The numbering updates automatically</li>
<li>Drag this list to see it in action</li>
</ol>
<h3>Code Blocks</h3>
<p>Even code blocks can be moved:</p>
<pre data-language="javascript"><code>// This code block can be dragged
function dragAndDrop() {
return "Easy to rearrange!"
}</code></pre>
<h2>Nested Content</h2>
<blockquote>
<p>This blockquote can be moved as a single unit.</p>
<blockquote>
<p>Nested blockquotes move together with their parent.</p>
</blockquote>
</blockquote>
<h2>Try It Yourself</h2>
<p>Practice by moving this paragraph to the top of the document.</p>
<p>Or drag this one to between the headings above.</p>
<p>The drag handles make it easy to reorganize your content exactly how you want it.</p>
`
<script lang="ts">
import { DropIndicator } from 'prosekit/svelte/drop-indicator'
</script>
<DropIndicator class="z-50 transition-all bg-blue-500" />
<script setup lang="ts">
import 'prosekit/basic/style.css'
import 'prosekit/basic/typography.css'
import { createEditor } from 'prosekit/core'
import { ProseKit } from 'prosekit/vue'
import {
ref,
watchPostEffect,
} from 'vue'
import BlockHandle from './block-handle.vue'
import { DEFAULT_DRAG_AND_DROP_CONTENT } from './default-content-drag-and-drop'
import DropIndicator from './drop-indicator.vue'
import { defineExtension } from './extension'
const editor = createEditor({
extension: defineExtension(),
defaultContent: DEFAULT_DRAG_AND_DROP_CONTENT,
})
const editorRef = ref<HTMLDivElement | null>(null)
watchPostEffect((onCleanup) => {
editor.mount(editorRef.value)
onCleanup(() => editor.unmount())
})
</script>
<template>
<ProseKit :editor="editor">
<div class="box-border h-full w-full min-h-36 overflow-y-hidden overflow-x-hidden rounded-md border border-solid border-gray-200 dark:border-gray-700 shadow-sm flex flex-col bg-white dark:bg-gray-950 color-black dark:color-white">
<div class="relative w-full flex-1 box-border overflow-y-scroll">
<div
ref="editorRef"
spellcheck="false"
class="ProseMirror box-border min-h-full px-[max(4rem,calc(50%-20rem))] py-8 outline-hidden outline-0 [&_span[data-mention=user]]:text-blue-500 [&_span[data-mention=tag]]:text-violet-500"
/>
<BlockHandle />
<DropIndicator />
</div>
</div>
</ProseKit>
</template>
import { defineBasicExtension } from 'prosekit/basic'
import { union } from 'prosekit/core'
export function defineExtension() {
return union([defineBasicExtension()])
}
export type EditorExtension = ReturnType<typeof defineExtension>
<script setup lang="ts">
import {
BlockHandleAdd,
BlockHandleDraggable,
BlockHandlePopover,
} from 'prosekit/vue/block-handle'
</script>
<template>
<BlockHandlePopover class="flex items-center flex-row box-border justify-center transition border-0 [&:not([data-state])]:hidden will-change-transform data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=open]:fade-in-0 data-[state=closed]:fade-out-0 data-[state=open]:zoom-in-95 data-[state=closed]:zoom-out-95 data-[state=open]:animate-duration-150 data-[state=closed]:animate-duration-200">
<BlockHandleAdd class="flex items-center box-border justify-center h-[1.5em] w-[1.5em] hover:bg-gray-100 dark:hover:bg-gray-800 rounded-sm text-gray-500/50 dark:text-gray-500/50 cursor-pointer">
<div class="i-lucide-plus h-5 w-5" />
</BlockHandleAdd>
<BlockHandleDraggable class="flex items-center box-border justify-center h-[1.5em] w-[1.2em] hover:bg-gray-100 dark:hover:bg-gray-800 rounded-sm text-gray-500/50 dark:text-gray-500/50 cursor-grab">
<div class="i-lucide-grip-vertical h-5 w-5" />
</BlockHandleDraggable>
</BlockHandlePopover>
</template>
// This is the default content for drag and drop examples.
export const DEFAULT_DRAG_AND_DROP_CONTENT = `
<h1>Drag and Drop Demo</h1>
<p>Try dragging any paragraph or heading by clicking on the handle that appears on the left when you hover over it.</p>
<h2>Getting Started</h2>
<p>Hover over any block to see the drag handle appear. Click and drag to reorder content.</p>
<p>This paragraph can be moved above or below other blocks.</p>
<h2>Different Block Types</h2>
<p>You can drag paragraphs, headings, lists, code blocks, and more.</p>
<h3>Lists Work Too</h3>
<ul>
<li>This entire list can be dragged</li>
<li>Individual list items stay together</li>
<li>Try moving this list around</li>
</ul>
<ol>
<li>Ordered lists also support dragging</li>
<li>The numbering updates automatically</li>
<li>Drag this list to see it in action</li>
</ol>
<h3>Code Blocks</h3>
<p>Even code blocks can be moved:</p>
<pre data-language="javascript"><code>// This code block can be dragged
function dragAndDrop() {
return "Easy to rearrange!"
}</code></pre>
<h2>Nested Content</h2>
<blockquote>
<p>This blockquote can be moved as a single unit.</p>
<blockquote>
<p>Nested blockquotes move together with their parent.</p>
</blockquote>
</blockquote>
<h2>Try It Yourself</h2>
<p>Practice by moving this paragraph to the top of the document.</p>
<p>Or drag this one to between the headings above.</p>
<p>The drag handles make it easy to reorganize your content exactly how you want it.</p>
`
<script setup lang="ts">
import { DropIndicator } from 'prosekit/vue/drop-indicator'
</script>
<template>
<DropIndicator class-name="z-50 transition-all bg-blue-500" />
</template>