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 text-black dark:text-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 size-5 block" />
      </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 size-5 block" />
      </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 text-black dark:text-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 size-5 block" />
      </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 size-5 block" />
      </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 text-black dark:text-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 size-5 block" />
      </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 size-5 block" />
      </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 text-black dark:text-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 size-5 block"></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 size-5 block"></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 text-black dark:text-white">
      <div class="relative w-full flex-1 box-border overflow-y-scroll">
        <div
          ref="editorRef"
          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 size-5 block" />
    </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 size-5 block" />
    </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>