Skip to content

Conversation

@solidprinciples
Copy link

@solidprinciples solidprinciples commented Jan 27, 2026

❓ Type of change

  • πŸ“– Documentation (updates to the documentation or readme)
  • 🐞 Bug fix (a non-breaking change that fixes an issue)
  • πŸ‘Œ Enhancement (improving an existing functionality)
  • ✨ New feature (a non-breaking change that adds functionality)
  • 🧹 Chore (updates to the build process or auxiliary tools and libraries)
  • ⚠️ Breaking change (fix or feature that would cause existing functionality to change)

πŸ“š Description

While working with the editor, I noticed that nested drag handle options are documented but weren’t being explicitly passed through. This PR updates the implementation to correctly pick up the nested drag handle options exposed by TipTap’s DragHandleProps type.

It also includes a small nit fix to omit the same keys from the button props, since they aren’t used there.

This change fixes and enables nested drag handles.

I ran into this while trying to get nesting working locally and realized it was a straightforward fix worth contributing.

πŸ“ Checklist

  • I have linked an issue or discussion.
  • I have updated the documentation accordingly.

…d 'nestedOptions', omit the same from button props
@github-actions github-actions bot added the v4 #4488 label Jan 27, 2026
@coderabbitai
Copy link

coderabbitai bot commented Jan 27, 2026

πŸ“ Walkthrough

Walkthrough

The pull request modifies the EditorDragHandle component to emit a new hover event when valid nodes are detected, updates property forwarding to include nested and nestedOptions in the drag handle props while omitting them from button props, and changes click event handling from direct binding to slot-provided callbacks. Additionally, the changes extend the FloatingUIOptions type definition with an optional custom Middleware array property and update the buildFloatingUIMiddleware utility function to inject custom middleware supplied through options.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

πŸš₯ Pre-merge checks | βœ… 3
βœ… Passed checks (3 passed)
Check name Status Explanation
Title check βœ… Passed The title accurately reflects the main changes: enabling nested drag handle props, adding custom middleware support, and fixing the onNodeChange emit behavior.
Description check βœ… Passed The description is related to the changeset, explaining the motivation for forwarding nested props and mentioning it as a bug fix.
Docstring Coverage βœ… Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
πŸ§ͺ Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❀️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Jan 27, 2026

npm i https://pkg.pr.new/@nuxt/ui@5960

commit: 5b6e015

@solidprinciples solidprinciples changed the title fix(EditorDragHandle): update props forwarding to include 'nested' and 'nestedOptions', omit these from button props fix(editor): forward nested props and omit from button Jan 28, 2026
@solidprinciples solidprinciples changed the title fix(editor): forward nested props and omit from button fix(EditorDragHandle): pass nested option props, add custom middleware Jan 28, 2026
@solidprinciples solidprinciples changed the title fix(EditorDragHandle): pass nested option props, add custom middleware fix(EditorDragHandle): pass nested + nestedOptions, add custom middleware Jan 28, 2026
@solidprinciples solidprinciples changed the title fix(EditorDragHandle): pass nested + nestedOptions, add custom middleware feat(EditorDragHandle): pass nested props, custom middleware, fix onNodeChange Jan 28, 2026
@solidprinciples
Copy link
Author

solidprinciples commented Jan 28, 2026

@benjamincanac - noticed a few bugs/things that didn't quite align with documentation.

Here's a small concise PR for the fixes:

  • nested, nestedOptions weren't passed - omitted these from buttons - so it now supports nesting!
  • emit @node-change is documented as emitting on hover, but was only emitted onClick - fixed!
  • onClick is not impacted - so no breaking changes!

πŸ˜„ first contribution, long time user of nuxt ui ❀️ - custom middleware seemed simple enough, and opens doors for flexibility. If its not robust enough, I can revert it.

@solidprinciples solidprinciples changed the title feat(EditorDragHandle): pass nested props, custom middleware, fix onNodeChange feat(EditorDragHandle): pass tiptap props, custom middleware, fix onNodeChange emit Jan 28, 2026
Copy link
Member

@benjamincanac benjamincanac left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When building the Editor I did try to store the current node and send it inside the nodeChange even like you did but for a reason I never understood this breaks the drag handle completely, I even tried to pass it inside default slot props but didn't work either. My guess is the node.toJSON() object is just too big.

You can try in the playground and see it no longer displays when hovering elements.

@solidprinciples
Copy link
Author

solidprinciples commented Jan 28, 2026

When building the Editor I did try to store the current node and send it inside the nodeChange even like you did but for a reason I never understood this breaks the drag handle completely, I even tried to pass it inside default slot props but didn't work either. My guess is the node.toJSON() object is just too big.

You can try in the playground and see it no longer displays when hovering elements.

Interestingly enough - nodeChanged works - is this a prop/event collision in vue? It would unfortunately constitute a breaking change, but that did get it functional in playground - debug prints before/after emits for proof:

{43F91052-047A-4916-93EA-7B5631A19112} {E8534173-EF03-4663-BC2D-8BE2C6C7FABE} {0315453D-F56B-4D63-96C9-07C681E51EB9}

Thoughts? Right there with you on don't understand why - even why this fix works, seemingly. Good that it's only ~1 month old, so a minor breaking fix to a small (early) set of users. :)

Nobody even flagged these as issues/features, just helping out where I can!

@solidprinciples
Copy link
Author

For performance impact - it does look optimized under the hood - only called when actually changing - not retriggering on mouse movement.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

πŸ€– Fix all issues with AI agents
In `@src/runtime/components/EditorDragHandle.vue`:
- Around line 42-44: The emit was renamed from nodeChange to nodeChanged,
breaking consumers who used `@node-change`; update the API docs and changelog to
document this breaking change and provide a migration note (replace `@node-change`
with `@node-changed`), and ensure the type/interface EditorDragHandleEmits and any
usages of nodeChange in EditorDragHandle.vue and elsewhere are updated to
nodeChanged so templates and listeners reflect the new event name.

Comment on lines 42 to 44
export interface EditorDragHandleEmits {
nodeChange: [{ node: JSONContent, pos: number }]
nodeChanged: [{ node: JSONContent, pos: number }]
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟑 Minor

Breaking change: emit renamed from nodeChange to nodeChanged.

This rename changes the event from @node-change to @node-changed in Vue templates. While this aligns with Vue's naming convention for past-tense events (indicating something has happened), it's a breaking change for existing consumers.

Ensure the changelog documents this breaking change for users migrating from the previous API.

πŸ€– Prompt for AI Agents
In `@src/runtime/components/EditorDragHandle.vue` around lines 42 - 44, The emit
was renamed from nodeChange to nodeChanged, breaking consumers who used
`@node-change`; update the API docs and changelog to document this breaking change
and provide a migration note (replace `@node-change` with `@node-changed`), and
ensure the type/interface EditorDragHandleEmits and any usages of nodeChange in
EditorDragHandle.vue and elsewhere are updated to nodeChanged so templates and
listeners reflect the new event name.

@benjamincanac
Copy link
Member

@solidprinciples We can't introduce a breaking change like this unfortunately, what are you trying to solve here? The @node-change event is used for this example: https://ui.nuxt.com/docs/components/editor-drag-handle#with-dropdown-menu so it's fine to trigger it on click in my opinion.

@solidprinciples
Copy link
Author

Looking to be able to track currently selected node by the handle upon hover.

@benjamincanac
Copy link
Member

What about adding a new hover event? πŸ€”

diff --git a/src/runtime/components/EditorDragHandle.vue b/src/runtime/components/EditorDragHandle.vue
index 2c1824881..862cc47b9 100644
--- a/src/runtime/components/EditorDragHandle.vue
+++ b/src/runtime/components/EditorDragHandle.vue
@@ -40,6 +40,10 @@ export interface EditorDragHandleSlots {
 
 export interface EditorDragHandleEmits {
   nodeChange: [{ node: JSONContent, pos: number }]
+  /**
+   * Emitted when the hovered node changes.
+   */
+  hover: [{ node: JSONContent, pos: number }]
 }
 </script>
 
@@ -114,6 +118,13 @@ const currentNodePos = ref<number | null>()
 
 function onNodeChange({ pos }: { pos: number }) {
   currentNodePos.value = pos
+
+  if (!props.editor || pos < 0) return
+
+  const node = props.editor.state.doc.nodeAt(pos)
+  if (node) {
+    emit('hover', { node: node.toJSON(), pos })
+  }
 }
 
 function onClick() {

@solidprinciples
Copy link
Author

solidprinciples commented Jan 28, 2026

@benjamincanac - reverted earlier changes around @node-change -> @node-changed, added @hover emit - works as expected. Was able to get an example working to save the value externally. Whipping up a semi-useful example. πŸ‘

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/runtime/components/EditorDragHandle.vue (1)

154-164: Default handle no longer triggers onClick.

After removing the root @click, the fallback <UButton> isn’t wired to onClick, so nodeChange will never fire for the default slot. Please bind the handler in the fallback content to avoid a regression.

πŸ› οΈ Suggested fix
       <UButton
         v-bind="{
           ...buttonProps,
           icon: props.icon || appConfig.ui.icons.drag,
           ...$attrs
         }"
         data-slot="handle"
         :class="ui.handle({ class: [props.ui?.handle, props.class] })"
         :ui="transformUI(ui, props.ui)"
+        `@click`="onClick"
       />

@solidprinciples
Copy link
Author

@benjamincanac hmm. same issue - I've at least diagnosed it down to this:

  • no external parent state modifications: renders fine (console.log, etc)
  • external parent state (setting ref, emit, etc): breaks rendering

Haven't seen anything on tiptap's repo :(

@solidprinciples
Copy link
Author

@benjamincanac - https://github.com/solidprinciples/tiptap-draghandle-state-update-bug/tree/bug/drag-handle

Tiptap bug - same thing happens in their extension demo. Investigating to see feasibility of fixing upstream.

@solidprinciples
Copy link
Author

@benjamincanac fixed in tip-tap - small one-liner in ueberdosis/tiptap#7472

πŸ‘ - seems like the DragHandle is re-rendering and re-applying visibility: hidden in these cases.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

v4 #4488

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants