From de260565c70c1a39acd02961f76ede6ff094b881 Mon Sep 17 00:00:00 2001 From: nabbydude Date: Sat, 24 May 2025 19:34:18 -0400 Subject: [PATCH] Optimize editor#above and allow passing a location that doesnt exist as long as its parent exists (#5880) * optimize editor#apply and allow potential paths as input * fix Editor#above regression for cross-node ranges * add test to prevent regressions * add changeset * improve comment * factor out for-loop that will never actually loop * aw crud I didnt lint. fixing --- .changeset/popular-bags-relax.md | 5 +++ packages/slate/src/editor/above.ts | 32 ++++++++---------- .../Editor/above/potential-parent.tsx | 33 +++++++++++++++++++ 3 files changed, 51 insertions(+), 19 deletions(-) create mode 100644 .changeset/popular-bags-relax.md create mode 100644 packages/slate/test/interfaces/Editor/above/potential-parent.tsx diff --git a/.changeset/popular-bags-relax.md b/.changeset/popular-bags-relax.md new file mode 100644 index 000000000..59cc2ec51 --- /dev/null +++ b/.changeset/popular-bags-relax.md @@ -0,0 +1,5 @@ +--- +'slate': patch +--- + +Optimize editor#above and allow passing a location that doesnt exist as long as its parent exists diff --git a/packages/slate/src/editor/above.ts b/packages/slate/src/editor/above.ts index e616c1caa..0cc9b400e 100644 --- a/packages/slate/src/editor/above.ts +++ b/packages/slate/src/editor/above.ts @@ -1,6 +1,5 @@ import { Editor, EditorInterface } from '../interfaces/editor' -import { Text } from '../interfaces/text' -import { Range } from '../interfaces/range' +import { Range } from '../interfaces' import { Path } from '../interfaces/path' export const above: EditorInterface['above'] = (editor, options = {}) => { @@ -15,27 +14,22 @@ export const above: EditorInterface['above'] = (editor, options = {}) => { return } - const path = Editor.path(editor, at) + let path = Editor.path(editor, at) + + // If `at` is a Range that spans mulitple nodes, `path` will be their common ancestor. + // Otherwise `path` will be a text node and/or the same as `at`, in which cases we want to start with its parent. + if (!Range.isRange(at) || Path.equals(at.focus.path, at.anchor.path)) { + if (path.length === 0) return + path = Path.parent(path) + } + const reverse = mode === 'lowest' - for (const [n, p] of Editor.levels(editor, { + const [firstMatch] = Editor.levels(editor, { at: path, voids, match, reverse, - })) { - if (Text.isText(n)) continue - if (Range.isRange(at)) { - if ( - Path.isAncestor(p, at.anchor.path) && - Path.isAncestor(p, at.focus.path) - ) { - return [n, p] - } - } else { - if (!Path.equals(path, p)) { - return [n, p] - } - } - } + }) + return firstMatch // if nothing matches this returns undefined } diff --git a/packages/slate/test/interfaces/Editor/above/potential-parent.tsx b/packages/slate/test/interfaces/Editor/above/potential-parent.tsx new file mode 100644 index 000000000..abbe58809 --- /dev/null +++ b/packages/slate/test/interfaces/Editor/above/potential-parent.tsx @@ -0,0 +1,33 @@ +/** @jsx jsx */ +import { Editor, Element } from 'slate' +import { jsx } from '../../..' + +// `above` can never return the location passed into it, and shouldnt care if it exists, only if its parent exists. + +export const input = ( + + + + one + {/* path points here */} + + two + + +) + +const path = [0, 0, 1] + +export const test = editor => { + return Editor.above(editor, { + at: path, + match: n => Element.isElement(n) && Editor.isBlock(editor, n), + }) +} + +export const output = [ + + one + , + [0, 0], +]