EditorDragHandle

GitLab
Uma alça arrastável para reordenar e selecionar blocos no editor.

Uso

O componente EditorDragHandle oferece funcionalidade de arrastar e soltar para reordenar blocos do editor usando o pacote @tiptap/extension-drag-handle-vue-3.

Ele deve ser usado dentro do slot padrão de um componente Editor para ter acesso à instância do editor.

Ele estende o componente Button, então você pode passar qualquer propriedade como color, variant, size, etc.

Saiba mais sobre a extensão Drag Handle na documentação do TipTap.

Ícone

Use a prop icon para personalizar o ícone da alça de arrastar.

<template>
  <NEditor v-slot="{ editor }">
    <NEditorDragHandle :editor="editor" icon="i-lucide-move" />
  </NEditor>
</template>
Você pode personalizar esse ícone globalmente no seu app.config.ts na chave ui.icons.drag.
Você pode personalizar esse ícone globalmente no seu vite.config.ts na chave ui.icons.drag.

Opções

Use a prop options para personalizar o comportamento de posicionamento usando as opções do Floating UI.

O deslocamento é calculado automaticamente para centralizar a alça em blocos pequenos e alinhá-la ao topo em blocos mais altos.
<template>
  <NEditor v-slot="{ editor }">
    <NEditorDragHandle
      :editor="editor"
      :options="{
        placement: 'left'
      }"
    />
  </NEditor>
</template>

Exemplos

Com dropdown menu

Use o slot padrão para adicionar um DropdownMenu com ações no nível do bloco, como duplicar, excluir, mover para cima/baixo ou transformar blocos em tipos diferentes.

Escute o evento @node-change para acompanhar o node atualmente sob o cursor e sua posição, e então use editor.chain().setMeta('lockDragHandle', open).run() para travar a posição da alça enquanto o menu está aberto.

Este exemplo usa o utilitário mapEditorItems de @nitro/ui/utils/editor para mapear automaticamente os tipos de handler (como duplicate, delete, moveUp, etc.) para os comandos correspondentes do editor, com o devido gerenciamento de estado.

Com menu de sugestões

Use o slot padrão para adicionar um Button ao lado da alça de arrastar para abrir o EditorSuggestionMenu.

Chame a função de slot onClick para obter a posição atual do node e então use handlers.suggestion?.execute(editor, { pos: node?.pos }).run() para inserir novos blocos naquela posição.

API

Props

Prop Default Type
as'button'any

The element or component this component should render as when not a link.

editorEditor
iconappConfig.ui.icons.dragany
color'neutral' "error" | "neutral" | "primary" | "secondary" | "accent" | "success" | "info" | "warning"
variant'ghost' "ghost" | "solid" | "outline" | "soft" | "subtle" | "link"
options{ strategy: 'absolute', placement: 'left-start' } FloatingUIOptions

The options for positioning the drag handle. Those are passed to Floating UI and include options for the placement, offset, flip, shift, size, autoPlacement, hide, and inline middleware.

pluginKey string | PluginKey<any>
nestedOptions NormalizedNestedOptions
onElementDragStart (e: DragEvent): void
onElementDragEnd (e: DragEvent): void
getReferencedVirtualElement (): VirtualElement | null
dragImageProperties string[]
nestedboolean | NestedOptions

Enable drag handles for nested content (list items, blockquotes, etc.).

When enabled, the drag handle will appear for nested blocks, not just top-level blocks. A rule-based scoring system determines which node to target based on cursor position and configured rules.

autofocus false | true | "true" | "false"
disabledboolean
name string
type'button' "reset" | "submit" | "button"

The type of the button when not a link.

label string
activeColor "error" | "neutral" | "primary" | "secondary" | "accent" | "success" | "info" | "warning"
activeVariant "ghost" | "solid" | "outline" | "soft" | "subtle" | "link"
size'sm' "sm" | "xs" | "md" | "lg" | "xl"
squareboolean

Render the button with equal padding on all sides.

blockboolean

Render the button full width.

loadingAutoboolean

Set loading state automatically based on the @click promise state

avatar AvatarProps

Display an avatar on the left side.

leadingboolean

When true, the icon will be displayed on the left side.

leadingIconany

Display an icon on the left side.

trailingboolean

When true, the icon will be displayed on the right side.

trailingIconany

Display an icon on the right side.

loadingboolean

When true, the loading icon will be displayed.

loadingIconappConfig.ui.icons.loadingany

The icon when the loading prop is true.

ui { root?: SlotClass; handle?: SlotClass; } & { base?: SlotClass; label?: SlotClass; leadingIcon?: SlotClass; leadingAvatar?: SlotClass; leadingAvatarSize?: SlotClass; trailingIcon?: SlotClass; }

Slots

Slot Type
default{ ui: object; }

Emits

Event Type
nodeChange[{ node: JSONContent; pos: number; }]
hover[{ node: JSONContent; pos: number; }]

Tema

app.config.ts
export default defineAppConfig({
  ui: {
    editorDragHandle: {
      slots: {
        root: 'hidden sm:flex items-center justify-center transition-all duration-200 ease-out',
        handle: 'cursor-grab px-1'
      }
    }
  }
})
vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nitro/ui/vite'

export default defineConfig({
  plugins: [
    vue(),
    ui({
      ui: {
        editorDragHandle: {
          slots: {
            root: 'hidden sm:flex items-center justify-center transition-all duration-200 ease-out',
            handle: 'cursor-grab px-1'
          }
        }
      }
    })
  ]
})

Changelog

No recent changes