Skip to content

Guard drops whose position resolves outside of the editor - #6105

Open
janpaepke wants to merge 1 commit into
ianstormtaylor:mainfrom
janpaepke:fix/drop-range-fallback
Open

Guard drops whose position resolves outside of the editor#6105
janpaepke wants to merge 1 commit into
ianstormtaylor:mainfrom
janpaepke:fix/drop-range-fallback

Conversation

@janpaepke

Copy link
Copy Markdown
Contributor

Description
Dropping into <Editable> throws Cannot resolve a Slate point from DOM point when the browser resolves the drop position to a node outside of the editor, and the drop is lost. onDrop checks that event.target is inside the editor, then resolves the range from clientX/clientY via caretRangeFromPoint. The two can disagree: WebKit dispatches drop to the element captured at the last dragover while the coordinates are the drop's own, so a page scroll during the drag lands the caret on neighbouring text. Seen in production in Safari 18.6 on macOS, where the caret resolved to the end of a <label> above the editor.

With this change the dropped data is inserted at the current selection (or the end of the document when there is none), and an internal drag without a resolvable target leaves its content where it is.

Issue
No dedicated issue. Related: #5107 (the same caretRangeFromPoint fragility inside shadow DOM).

Example

// before: throws when the caret position lies outside of the editor
const range = ReactEditor.findEventRange(editor, event)

// after: opt into `null` instead, like `toSlatePoint` / `toSlateRange`
const range = ReactEditor.findEventRange(editor, event, { suppressThrow: true })
if (range) Transforms.select(editor, range)

Before: the handler threw, nothing was inserted and the error surfaced as uncaught. After: the data lands at the selection.

Context

  • DOMEditor.findEventRange gets an optional suppressThrow, following toSlatePoint/toSlateRange and the option useSelected gained in fix(slate-react): prevent useSelected from throwing when element is removed #6073. It forwards the flag to toSlateRange and returns null for the two early throws as well.
  • Unlike toSlatePoint/toSlateRange, the options object is optional and the generic defaults to false, so existing callers keep the plain Range return type; only callers passing true see Range | null.
  • <Editable>'s onDrop branches on the result: with a range, behaviour is unchanged; without one and an internal drag, it returns after preventDefault; otherwise it selects the end of the document when nothing is selected and calls insertData as before. The children.length > 0 guard mirrors focus.
  • Tests: four onDrop cases dispatching a real drop event through React with a stubbed caretRangeFromPoint (jsdom implements neither it nor DataTransfer).

Checks

  • The new code matches the existing patterns and styles.
  • The tests pass with yarn test:jest (101 passed).
  • The linter passes (yarn lint:typescript; eslint and prettier on the changed files).
  • The relevant examples still work. (not run; the resolvable-position path is unchanged)
  • Changeset added (slate-dom minor, slate-react patch).

… of the editor

`<Editable>` resolved the drop target via `caretRangeFromPoint`, which can land on
a node outside of the editor although the event's target is inside it: the drop
is dispatched to the target of the last `dragover` while the coordinates are the
drop's own, so a page scroll in between separates them. The resulting "Cannot
resolve a Slate point from DOM point" escaped the handler and the drop was lost.

`findEventRange` gains `suppressThrow`, mirroring `toSlatePoint` and
`toSlateRange`, and the drop handler falls back to the current selection (or the
end of the document) when there is no range. An internal drag without a target
leaves its content in place rather than re-inserting it over itself.
@changeset-bot

changeset-bot Bot commented Sep 2, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ed1bde2

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
slate-dom Minor
slate-react Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant