UNPKG

@portabletext/editor

Version:

Portable Text Editor made in React

1 lines 937 kB
{"version":3,"file":"index.cjs","sources":["../src/editor-event-listener.tsx","../src/internal-utils/debug.ts","../src/internal-utils/values.ts","../src/internal-utils/slate-utils.ts","../src/internal-utils/event-position.ts","../src/internal-utils/paths.ts","../src/internal-utils/ranges.ts","../src/internal-utils/selection.ts","../src/internal-utils/weakMaps.ts","../src/editor/editor-actor-context.ts","../src/editor/components/drop-indicator.tsx","../src/editor/components/render-default-object.tsx","../src/internal-utils/drag-selection.ts","../src/editor/key-generator.ts","../src/priority/priority.types.ts","../src/priority/priority.core.ts","../src/behaviors/behavior.core.block-element.ts","../src/editor/components/use-core-block-element-behaviors.ts","../src/editor/components/render-block-object.tsx","../src/editor/components/render-inline-object.tsx","../src/editor/components/render-text-block.tsx","../src/editor/components/render-element.tsx","../src/editor/hooks/usePortableTextEditor.ts","../src/internal-utils/stop-actor.ts","../src/converters/converter.json.ts","../src/converters/converter.portable-text.ts","../src/converters/converter.text-html.ts","../src/converters/converter.text-plain.ts","../src/converters/converters.core.ts","../src/internal-utils/schema.ts","../src/internal-utils/create-placeholder-block.ts","../src/editor/get-active-annotations.ts","../src/editor/get-active-decorators.ts","../src/operations/behavior.operation.insert.text.ts","../../../node_modules/.pnpm/@portabletext+toolkit@2.0.17/node_modules/@portabletext/toolkit/dist/index.js","../src/internal-utils/sibling-utils.ts","../src/internal-utils/withChanges.ts","../src/internal-utils/withUndoRedo.ts","../src/editor/editor-selector.ts","../src/editor/plugins/createWithPortableTextMarkModel.ts","../../../node_modules/.pnpm/@sanity+diff-match-patch@3.2.0/node_modules/@sanity/diff-match-patch/dist/index.js","../src/editor/with-undo-step.ts","../src/editor/plugins/createWithUndoRedo.ts","../src/operations/behavior.operation.annotation.add.ts","../src/operations/behavior.operation.annotation.remove.ts","../src/operations/behavior.operation.block.set.ts","../src/operations/behavior.operation.block.unset.ts","../src/operations/behavior.operation.decorator.add.ts","../src/operations/behavior.operation.delete.ts","../src/operations/behavior.operation.insert-inline-object.ts","../src/operations/behavior.operation.insert-span.ts","../src/operations/behavior.operation.insert.block.ts","../src/operations/behavior.operation.move.backward.ts","../src/operations/behavior.operation.move.block.ts","../src/operations/behavior.operation.move.forward.ts","../src/operations/behavior.operation.select.ts","../src/operations/behavior.operations.ts","../src/editor/with-applying-behavior-operations.ts","../src/editor/plugins/create-with-event-listeners.ts","../src/editor/plugins/createWithMaxBlocks.ts","../src/editor/plugins/createWithObjectKeys.ts","../src/internal-utils/applyPatch.ts","../src/internal-utils/operation-to-patches.ts","../src/internal-utils/withoutPatching.ts","../src/editor/plugins/createWithPatches.ts","../src/editor/plugins/createWithPlaceholderBlock.ts","../src/editor/plugins/createWithSchemaTypes.ts","../src/editor/plugins/createWithUtils.ts","../src/internal-utils/mark-state.ts","../src/editor/plugins/slate-plugin.update-mark-state.ts","../src/editor/plugins/slate-plugin.update-selection.ts","../src/internal-utils/portable-text-node.ts","../src/internal-utils/apply-operation-to-portable-text.ts","../src/editor/plugins/slate-plugin.update-value.ts","../src/editor/plugins/with-plugins.ts","../src/editor/create-slate-editor.tsx","../src/behaviors/behavior.core.annotations.ts","../src/internal-utils/is-hotkey.ts","../src/behaviors/behavior.core.block-objects.ts","../src/behaviors/behavior.core.decorators.ts","../src/internal-utils/compound-client-rect.ts","../src/behaviors/behavior.core.dnd.ts","../src/behaviors/behavior.core.insert-break.ts","../src/behaviors/behavior.core.lists.ts","../src/behaviors/behavior.core.ts","../src/internal-utils/selection-elements.ts","../src/behaviors/behavior.abstract.annotation.ts","../src/behaviors/behavior.abstract.decorator.ts","../src/behaviors/behavior.abstract.delete.ts","../src/behaviors/behavior.abstract.insert.ts","../src/internal-utils/key-is.ts","../src/behaviors/behavior.abstract.keyboard.ts","../src/behaviors/behavior.abstract.list-item.ts","../src/behaviors/behavior.abstract.move.ts","../src/behaviors/behavior.abstract.select.ts","../src/behaviors/behavior.abstract.split.ts","../src/behaviors/behavior.abstract.style.ts","../src/behaviors/behavior.abstract.ts","../src/behaviors/behavior.types.event.ts","../src/behaviors/behavior.perform-event.ts","../src/priority/priority.sort.ts","../src/editor/editor-snapshot.ts","../src/editor/editor-machine.ts","../src/editor/legacy-schema.ts","../src/editor/editor-schema.ts","../src/editor/mutation-machine.ts","../src/editor/plugins/createWithEditableAPI.ts","../src/editor/relay-machine.ts","../src/internal-utils/validateValue.ts","../src/editor/sync-machine.ts","../src/editor/create-editor.ts","../src/editor/hooks/usePortableTextEditorSelection.tsx","../src/editor/relay-actor-context.ts","../src/editor/route-events-to-changes.tsx","../src/editor/PortableTextEditor.tsx","../src/editor/components/render-span.tsx","../src/editor/components/render-leaf.tsx","../src/editor/components/render-text.tsx","../src/editor/plugins/createWithHotKeys.ts","../src/editor/range-decorations-machine.ts","../src/editor/Editable.tsx","../src/internal-utils/use-constant.ts","../src/editor/editor-provider.tsx"],"sourcesContent":["import {useEffect} from 'react'\nimport {useEffectEvent} from 'use-effect-event'\nimport type {EditorEmittedEvent} from './editor/relay-machine'\nimport {useEditor} from './editor/use-editor'\n\n/**\n * @public\n * @deprecated\n * This component has been renamed. Use `EventListenerPlugin` instead.\n *\n * ```\n * import {EventListenerPlugin} from '@portabletext/editor/plugins'\n * ```\n */\nexport function EditorEventListener(props: {\n on: (event: EditorEmittedEvent) => void\n}) {\n const editor = useEditor()\n const on = useEffectEvent(props.on)\n\n useEffect(() => {\n const subscription = editor.on('*', on)\n\n return () => {\n subscription.unsubscribe()\n }\n }, [editor])\n\n return null\n}\n","import debug from 'debug'\n\nconst rootName = 'sanity-pte:'\n\nexport default debug(rootName)\nexport function debugWithName(name: string): debug.Debugger {\n const namespace = `${rootName}${name}`\n if (debug && debug.enabled(namespace)) {\n return debug(namespace)\n }\n return debug(rootName)\n}\n","import type {\n PathSegment,\n PortableTextBlock,\n PortableTextChild,\n PortableTextObject,\n PortableTextTextBlock,\n} from '@sanity/types'\nimport {isEqual} from 'lodash'\nimport {Element, Text, type Descendant, type Node} from 'slate'\nimport type {EditorSchema} from '../editor/editor-schema'\n\nexport const EMPTY_MARKDEFS: PortableTextObject[] = []\n\nexport const VOID_CHILD_KEY = 'void-child'\n\ntype Partial<T> = {\n [P in keyof T]?: T[P]\n}\n\nfunction keepObjectEquality(\n object: PortableTextBlock | PortableTextChild,\n keyMap: Record<string, PortableTextBlock | PortableTextChild>,\n) {\n const value = keyMap[object._key]\n if (value && isEqual(object, value)) {\n return value\n }\n keyMap[object._key] = object\n return object\n}\n\nexport function toSlateValue(\n value: PortableTextBlock[] | undefined,\n {schemaTypes}: {schemaTypes: EditorSchema},\n keyMap: Record<string, any> = {},\n): Descendant[] {\n if (value && Array.isArray(value)) {\n return value.map((block) => {\n const {_type, _key, ...rest} = block\n const isPortableText = block && block._type === schemaTypes.block.name\n if (isPortableText) {\n const textBlock = block as PortableTextTextBlock\n let hasInlines = false\n const hasMissingStyle = typeof textBlock.style === 'undefined'\n const hasMissingMarkDefs = typeof textBlock.markDefs === 'undefined'\n const hasMissingChildren = typeof textBlock.children === 'undefined'\n\n const children = (textBlock.children || []).map((child) => {\n const {_type: cType, _key: cKey, ...cRest} = child\n // Return 'slate' version of inline object where the actual\n // value is stored in the `value` property.\n // In slate, inline objects are represented as regular\n // children with actual text node in order to be able to\n // be selected the same way as the rest of the (text) content.\n if (cType !== 'span') {\n hasInlines = true\n return keepObjectEquality(\n {\n _type: cType,\n _key: cKey,\n children: [\n {\n _key: VOID_CHILD_KEY,\n _type: 'span',\n text: '',\n marks: [],\n },\n ],\n value: cRest,\n __inline: true,\n },\n keyMap,\n )\n }\n // Original child object (span)\n return child\n })\n // Return original block\n if (\n !hasMissingStyle &&\n !hasMissingMarkDefs &&\n !hasMissingChildren &&\n !hasInlines &&\n Element.isElement(block)\n ) {\n // Original object\n return block\n }\n // TODO: remove this when we have a better way to handle missing style\n if (hasMissingStyle) {\n rest.style = schemaTypes.styles[0].name\n }\n return keepObjectEquality({_type, _key, ...rest, children}, keyMap)\n }\n return keepObjectEquality(\n {\n _type,\n _key,\n children: [\n {\n _key: VOID_CHILD_KEY,\n _type: 'span',\n text: '',\n marks: [],\n },\n ],\n value: rest,\n },\n keyMap,\n )\n }) as Descendant[]\n }\n return []\n}\n\nexport function fromSlateValue(\n value: Descendant[],\n textBlockType: string,\n keyMap: Record<string, PortableTextBlock | PortableTextChild> = {},\n): PortableTextBlock[] {\n return value.map((block) => {\n const {_key, _type} = block\n if (!_key || !_type) {\n throw new Error('Not a valid block')\n }\n if (\n _type === textBlockType &&\n 'children' in block &&\n Array.isArray(block.children) &&\n _key\n ) {\n let hasInlines = false\n const children = block.children.map((child) => {\n const {_type: _cType} = child\n if ('value' in child && _cType !== 'span') {\n hasInlines = true\n const {\n value: v,\n _key: k,\n _type: t,\n __inline: _i,\n children: _c,\n ...rest\n } = child\n return keepObjectEquality(\n {...rest, ...v, _key: k as string, _type: t as string},\n keyMap,\n )\n }\n return child\n })\n if (!hasInlines) {\n return block as PortableTextBlock // Original object\n }\n return keepObjectEquality(\n {...block, children, _key, _type},\n keyMap,\n ) as PortableTextBlock\n }\n const blockValue = 'value' in block && block.value\n return keepObjectEquality(\n {_key, _type, ...(typeof blockValue === 'object' ? blockValue : {})},\n keyMap,\n ) as PortableTextBlock\n })\n}\n\nexport function isEqualToEmptyEditor(\n children: Descendant[] | PortableTextBlock[],\n schemaTypes: EditorSchema,\n): boolean {\n return (\n children === undefined ||\n (children && Array.isArray(children) && children.length === 0) ||\n (children &&\n Array.isArray(children) &&\n children.length === 1 &&\n Element.isElement(children[0]) &&\n children[0]._type === schemaTypes.block.name &&\n 'style' in children[0] &&\n children[0].style === schemaTypes.styles[0].name &&\n !('listItem' in children[0]) &&\n Array.isArray(children[0].children) &&\n children[0].children.length === 1 &&\n Text.isText(children[0].children[0]) &&\n children[0].children[0]._type === 'span' &&\n !children[0].children[0].marks?.join('') &&\n children[0].children[0].text === '')\n )\n}\n\nexport function findBlockAndIndexFromPath(\n firstPathSegment: PathSegment,\n children: (Node | Partial<Node>)[],\n): [Element | undefined, number | undefined] {\n let blockIndex = -1\n const isNumber = Number.isInteger(Number(firstPathSegment))\n if (isNumber) {\n blockIndex = Number(firstPathSegment)\n } else if (children) {\n blockIndex = children.findIndex(\n (blk) =>\n Element.isElement(blk) && isEqual({_key: blk._key}, firstPathSegment),\n )\n }\n if (blockIndex > -1) {\n return [children[blockIndex] as Element, blockIndex]\n }\n return [undefined, -1]\n}\n\nexport function findChildAndIndexFromPath(\n secondPathSegment: PathSegment,\n block: Element,\n): [Element | Text | undefined, number] {\n let childIndex = -1\n const isNumber = Number.isInteger(Number(secondPathSegment))\n if (isNumber) {\n childIndex = Number(secondPathSegment)\n } else {\n childIndex = block.children.findIndex((child) =>\n isEqual({_key: child._key}, secondPathSegment),\n )\n }\n if (childIndex > -1) {\n return [block.children[childIndex] as Element | Text, childIndex]\n }\n return [undefined, -1]\n}\n\nexport function getValueOrInitialValue(\n value: unknown,\n initialValue: PortableTextBlock[],\n): PortableTextBlock[] | undefined {\n if (value && Array.isArray(value) && value.length > 0) {\n return value\n }\n return initialValue\n}\n","import type {PortableTextSpan} from '@sanity/types'\nimport {Editor, Element, Node, Range, type Path, type Point} from 'slate'\nimport type {EditorSchema} from '../editor/editor-schema'\nimport type {EditorSelection, PortableTextSlateEditor} from '../types/editor'\nimport {fromSlateValue} from './values'\n\nexport function getBlockPath({\n editor,\n _key,\n}: {\n editor: PortableTextSlateEditor\n _key: string\n}): [number] | undefined {\n const [, blockPath] = Array.from(\n Editor.nodes(editor, {\n at: [],\n match: (n) => n._key === _key,\n }),\n ).at(0) ?? [undefined, undefined]\n\n const blockIndex = blockPath?.at(0)\n\n if (blockIndex === undefined) {\n return undefined\n }\n\n return [blockIndex]\n}\n\nexport function getAnchorBlock({\n editor,\n}: {\n editor: PortableTextSlateEditor\n}): [node: Node, path: Path] | [undefined, undefined] {\n if (!editor.selection) {\n return [undefined, undefined]\n }\n\n try {\n return (\n Editor.node(editor, editor.selection.anchor.path.slice(0, 1)) ?? [\n undefined,\n undefined,\n ]\n )\n } catch {\n return [undefined, undefined]\n }\n}\n\nexport function getFocusBlock({\n editor,\n}: {\n editor: PortableTextSlateEditor\n}): [node: Node, path: Path] | [undefined, undefined] {\n if (!editor.selection) {\n return [undefined, undefined]\n }\n\n try {\n return (\n Editor.node(editor, editor.selection.focus.path.slice(0, 1)) ?? [\n undefined,\n undefined,\n ]\n )\n } catch {\n return [undefined, undefined]\n }\n}\n\nexport function getFocusSpan({\n editor,\n}: {\n editor: PortableTextSlateEditor\n}): [node: PortableTextSpan, path: Path] | [undefined, undefined] {\n if (!editor.selection) {\n return [undefined, undefined]\n }\n\n try {\n const [node, path] = Editor.node(editor, editor.selection.focus.path)\n\n if (editor.isTextSpan(node)) {\n return [node, path]\n }\n } catch {\n return [undefined, undefined]\n }\n\n return [undefined, undefined]\n}\n\nexport function getSelectionStartBlock({\n editor,\n}: {\n editor: PortableTextSlateEditor\n}): [node: Node, path: Path] | [undefined, undefined] {\n if (!editor.selection) {\n return [undefined, undefined]\n }\n\n const selectionStartPoint = Range.start(editor.selection)\n\n return getPointBlock({editor, point: selectionStartPoint})\n}\n\nexport function getSelectionEndBlock({\n editor,\n}: {\n editor: PortableTextSlateEditor\n}): [node: Node, path: Path] | [undefined, undefined] {\n if (!editor.selection) {\n return [undefined, undefined]\n }\n\n const selectionEndPoint = Range.end(editor.selection)\n\n return getPointBlock({editor, point: selectionEndPoint})\n}\n\nexport function getPointBlock({\n editor,\n point,\n}: {\n editor: PortableTextSlateEditor\n point: Point\n}): [node: Node, path: Path] | [undefined, undefined] {\n try {\n const [block] = Editor.node(editor, point.path.slice(0, 1)) ?? [\n undefined,\n undefined,\n ]\n return block ? [block, point.path.slice(0, 1)] : [undefined, undefined]\n } catch {\n return [undefined, undefined]\n }\n}\n\nexport function getFocusChild({\n editor,\n}: {\n editor: PortableTextSlateEditor\n}): [node: Node, path: Path] | [undefined, undefined] {\n const [focusBlock, focusBlockPath] = getFocusBlock({editor})\n const childIndex = editor.selection?.focus.path.at(1)\n\n if (!focusBlock || !focusBlockPath || childIndex === undefined) {\n return [undefined, undefined]\n }\n\n try {\n const focusChild = Node.child(focusBlock, childIndex)\n\n return focusChild\n ? [focusChild, [...focusBlockPath, childIndex]]\n : [undefined, undefined]\n } catch {\n return [undefined, undefined]\n }\n}\n\nfunction getPointChild({\n editor,\n point,\n}: {\n editor: PortableTextSlateEditor\n point: Point\n}): [node: Node, path: Path] | [undefined, undefined] {\n const [block, blockPath] = getPointBlock({editor, point})\n const childIndex = point.path.at(1)\n\n if (!block || !blockPath || childIndex === undefined) {\n return [undefined, undefined]\n }\n\n try {\n const pointChild = Node.child(block, childIndex)\n\n return pointChild\n ? [pointChild, [...blockPath, childIndex]]\n : [undefined, undefined]\n } catch {\n return [undefined, undefined]\n }\n}\n\nexport function getFirstBlock({\n editor,\n}: {\n editor: PortableTextSlateEditor\n}): [node: Node, path: Path] | [undefined, undefined] {\n if (editor.children.length === 0) {\n return [undefined, undefined]\n }\n\n const firstPoint = Editor.start(editor, [])\n const firstBlockPath = firstPoint.path.at(0)\n\n try {\n return firstBlockPath !== undefined\n ? (Editor.node(editor, [firstBlockPath]) ?? [undefined, undefined])\n : [undefined, undefined]\n } catch {\n return [undefined, undefined]\n }\n}\n\nexport function getLastBlock({\n editor,\n}: {\n editor: PortableTextSlateEditor\n}): [node: Node, path: Path] | [undefined, undefined] {\n if (editor.children.length === 0) {\n return [undefined, undefined]\n }\n\n const lastPoint = Editor.end(editor, [])\n const lastBlockPath = lastPoint.path.at(0)\n\n try {\n return lastBlockPath !== undefined\n ? (Editor.node(editor, [lastBlockPath]) ?? [undefined, undefined])\n : [undefined, undefined]\n } catch {\n return [undefined, undefined]\n }\n}\n\nexport function getNodeBlock({\n editor,\n schema,\n node,\n}: {\n editor: PortableTextSlateEditor\n schema: EditorSchema\n node: Node\n}) {\n if (Editor.isEditor(node)) {\n return undefined\n }\n\n if (isBlockElement({editor, schema}, node)) {\n return elementToBlock({schema, element: node})\n }\n\n const parent = Array.from(\n Editor.nodes(editor, {\n mode: 'highest',\n at: [],\n match: (n) =>\n isBlockElement({editor, schema}, n) &&\n n.children.some((child) => child._key === node._key),\n }),\n )\n .at(0)\n ?.at(0)\n\n return Element.isElement(parent)\n ? elementToBlock({\n schema,\n element: parent,\n })\n : undefined\n}\n\nfunction elementToBlock({\n schema,\n element,\n}: {\n schema: EditorSchema\n element: Element\n}) {\n return fromSlateValue([element], schema.block.name)?.at(0)\n}\n\nfunction isBlockElement(\n {editor, schema}: {editor: PortableTextSlateEditor; schema: EditorSchema},\n node: Node,\n): node is Element {\n return (\n Element.isElement(node) &&\n !editor.isInline(node) &&\n (schema.block.name === node._type ||\n schema.blockObjects.some(\n (blockObject) => blockObject.name === node._type,\n ))\n )\n}\n\nexport function isListItemActive({\n editor,\n listItem,\n}: {\n editor: Editor\n listItem: string\n}): boolean {\n if (!editor.selection) {\n return false\n }\n\n const selectedBlocks = [\n ...Editor.nodes(editor, {\n at: editor.selection,\n match: (node) => editor.isTextBlock(node),\n }),\n ]\n\n if (selectedBlocks.length > 0) {\n return selectedBlocks.every(\n ([node]) => editor.isListBlock(node) && node.listItem === listItem,\n )\n }\n\n return false\n}\n\nexport function isStyleActive({\n editor,\n style,\n}: {\n editor: Editor\n style: string\n}): boolean {\n if (!editor.selection) {\n return false\n }\n\n const selectedBlocks = [\n ...Editor.nodes(editor, {\n at: editor.selection,\n match: (node) => editor.isTextBlock(node),\n }),\n ]\n\n if (selectedBlocks.length > 0) {\n return selectedBlocks.every(([node]) => node.style === style)\n }\n\n return false\n}\n\nexport function slateRangeToSelection({\n schema,\n editor,\n range,\n}: {\n schema: EditorSchema\n editor: PortableTextSlateEditor\n range: Range\n}): EditorSelection {\n const [anchorBlock] = getPointBlock({\n editor,\n point: range.anchor,\n })\n const [focusBlock] = getPointBlock({\n editor,\n point: range.focus,\n })\n\n if (!anchorBlock || !focusBlock) {\n return null\n }\n\n const [anchorChild] =\n anchorBlock._type === schema.block.name\n ? getPointChild({\n editor,\n point: range.anchor,\n })\n : [undefined, undefined]\n const [focusChild] =\n focusBlock._type === schema.block.name\n ? getPointChild({\n editor,\n point: range.focus,\n })\n : [undefined, undefined]\n\n const selection: EditorSelection = {\n anchor: {\n path: [{_key: anchorBlock._key}],\n offset: range.anchor.offset,\n },\n focus: {\n path: [{_key: focusBlock._key}],\n offset: range.focus.offset,\n },\n backward: Range.isBackward(range),\n }\n\n if (anchorChild) {\n selection.anchor.path.push('children')\n selection.anchor.path.push({_key: anchorChild._key})\n }\n\n if (focusChild) {\n selection.focus.path.push('children')\n selection.focus.path.push({_key: focusChild._key})\n }\n\n return selection\n}\n","import {Editor, type BaseRange, type Node} from 'slate'\nimport {DOMEditor, isDOMNode} from 'slate-dom'\nimport type {EditorSchema, EditorSelection} from '..'\nimport type {EditorActor} from '../editor/editor-machine'\nimport {getBlockKeyFromSelectionPoint} from '../selection/selection-point'\nimport type {PortableTextSlateEditor} from '../types/editor'\nimport * as utils from '../utils'\nimport {\n getFirstBlock,\n getLastBlock,\n getNodeBlock,\n slateRangeToSelection,\n} from './slate-utils'\n\nexport type EventPosition = {\n block: 'start' | 'end'\n /**\n * Did the event origin from the editor DOM node itself or from a child node?\n */\n isEditor: boolean\n selection: NonNullable<EditorSelection>\n}\nexport type EventPositionBlock = EventPosition['block']\n\nexport function getEventPosition({\n editorActor,\n slateEditor,\n event,\n}: {\n editorActor: EditorActor\n slateEditor: PortableTextSlateEditor\n event: DragEvent | MouseEvent\n}): EventPosition | undefined {\n if (editorActor.getSnapshot().matches({setup: 'setting up'})) {\n return undefined\n }\n\n const eventNode = getEventNode({slateEditor, event})\n\n if (!eventNode) {\n return undefined\n }\n\n const eventBlock = getNodeBlock({\n editor: slateEditor,\n schema: editorActor.getSnapshot().context.schema,\n node: eventNode,\n })\n const eventPositionBlock = getEventPositionBlock({\n node: eventNode,\n slateEditor,\n event,\n })\n const eventSelection = getEventSelection({\n schema: editorActor.getSnapshot().context.schema,\n slateEditor,\n event,\n })\n\n if (\n eventBlock &&\n eventPositionBlock &&\n !eventSelection &&\n !Editor.isEditor(eventNode)\n ) {\n // If we for some reason can't find the event selection, then we default to\n // selecting the entire block that the event originates from.\n return {\n block: eventPositionBlock,\n isEditor: false,\n selection: {\n anchor: utils.getBlockStartPoint({\n context: editorActor.getSnapshot().context,\n block: {\n node: eventBlock,\n path: [{_key: eventBlock._key}],\n },\n }),\n focus: utils.getBlockEndPoint({\n context: editorActor.getSnapshot().context,\n block: {\n node: eventBlock,\n path: [{_key: eventBlock._key}],\n },\n }),\n },\n }\n }\n\n if (!eventPositionBlock || !eventSelection) {\n return undefined\n }\n\n const eventSelectionFocusBlockKey = getBlockKeyFromSelectionPoint(\n eventSelection.focus,\n )\n\n if (eventSelectionFocusBlockKey === undefined) {\n return undefined\n }\n\n if (\n utils.isSelectionCollapsed(eventSelection) &&\n eventBlock &&\n eventSelectionFocusBlockKey !== eventBlock._key\n ) {\n // If the event block and event selection somehow don't match, then the\n // event block takes precedence.\n return {\n block: eventPositionBlock,\n isEditor: false,\n selection: {\n anchor: utils.getBlockStartPoint({\n context: editorActor.getSnapshot().context,\n block: {\n node: eventBlock,\n path: [{_key: eventBlock._key}],\n },\n }),\n focus: utils.getBlockEndPoint({\n context: editorActor.getSnapshot().context,\n block: {\n node: eventBlock,\n path: [{_key: eventBlock._key}],\n },\n }),\n },\n }\n }\n\n return {\n block: eventPositionBlock,\n isEditor: Editor.isEditor(eventNode),\n selection: eventSelection,\n }\n}\n\nexport function getEventNode({\n slateEditor,\n event,\n}: {\n slateEditor: PortableTextSlateEditor\n event: DragEvent | MouseEvent\n}) {\n if (!DOMEditor.hasTarget(slateEditor, event.target)) {\n return undefined\n }\n\n const node = DOMEditor.toSlateNode(slateEditor, event.target)\n\n return node\n}\n\nfunction getEventPositionBlock({\n node,\n slateEditor,\n event,\n}: {\n node: Node\n slateEditor: PortableTextSlateEditor\n event: DragEvent | MouseEvent\n}): EventPositionBlock | undefined {\n const [firstBlock] = getFirstBlock({editor: slateEditor})\n\n if (!firstBlock) {\n return undefined\n }\n\n const firstBlockElement = DOMEditor.toDOMNode(slateEditor, firstBlock)\n const firstBlockRect = firstBlockElement.getBoundingClientRect()\n\n if (event.pageY < firstBlockRect.top) {\n return 'start'\n }\n\n const [lastBlock] = getLastBlock({editor: slateEditor})\n\n if (!lastBlock) {\n return undefined\n }\n\n const lastBlockElement = DOMEditor.toDOMNode(slateEditor, lastBlock)\n const lastBlockRef = lastBlockElement.getBoundingClientRect()\n\n if (event.pageY > lastBlockRef.bottom) {\n return 'end'\n }\n\n const element = DOMEditor.toDOMNode(slateEditor, node)\n const elementRect = element.getBoundingClientRect()\n const top = elementRect.top\n const height = elementRect.height\n const location = Math.abs(top - event.pageY)\n\n return location < height / 2 ? 'start' : 'end'\n}\n\nexport function getEventSelection({\n schema,\n slateEditor,\n event,\n}: {\n schema: EditorSchema\n slateEditor: PortableTextSlateEditor\n event: DragEvent | MouseEvent\n}): EditorSelection {\n const range = getSlateRangeFromEvent(slateEditor, event)\n\n const selection = range\n ? slateRangeToSelection({\n schema,\n editor: slateEditor,\n range,\n })\n : null\n\n return selection\n}\n\nfunction getSlateRangeFromEvent(\n editor: PortableTextSlateEditor,\n event: DragEvent | MouseEvent,\n) {\n if (!event.target) {\n return undefined\n }\n\n if (!isDOMNode(event.target)) {\n return undefined\n }\n\n const window = DOMEditor.getWindow(editor)\n\n let domRange: Range | undefined\n\n if (window.document.caretPositionFromPoint !== undefined) {\n const position = window.document.caretPositionFromPoint(\n event.clientX,\n event.clientY,\n )\n\n if (position) {\n try {\n domRange = window.document.createRange()\n domRange.setStart(position.offsetNode, position.offset)\n domRange.setEnd(position.offsetNode, position.offset)\n } catch {}\n }\n } else if (window.document.caretRangeFromPoint !== undefined) {\n // Use WebKit-proprietary fallback method\n domRange =\n window.document.caretRangeFromPoint(event.clientX, event.clientY) ??\n undefined\n } else {\n console.warn(\n 'Neither caretPositionFromPoint nor caretRangeFromPoint is supported',\n )\n return undefined\n }\n\n if (!domRange) {\n return undefined\n }\n\n let range: BaseRange | undefined\n\n try {\n range = DOMEditor.toSlateRange(editor, domRange, {\n exactMatch: false,\n // It can still throw even with this option set to true\n suppressThrow: false,\n })\n } catch {}\n\n return range\n}\n","import {isKeySegment, type Path} from '@sanity/types'\nimport {isEqual} from 'lodash'\nimport {Editor, Element, type Descendant, type Path as SlatePath} from 'slate'\n\nexport function toSlatePath(path: Path, editor: Editor): SlatePath {\n if (!editor) {\n return []\n }\n const [block, blockPath] = Array.from(\n Editor.nodes(editor, {\n at: [],\n match: (n) =>\n isKeySegment(path[0]) && (n as Descendant)._key === path[0]._key,\n }),\n )[0] || [undefined, undefined]\n\n if (!block || !Element.isElement(block)) {\n return []\n }\n\n if (editor.isVoid(block)) {\n return [blockPath[0], 0]\n }\n\n const childPath = [path[2]]\n const childIndex = block.children.findIndex((child) =>\n isEqual([{_key: child._key}], childPath),\n )\n\n if (childIndex >= 0 && block.children[childIndex]) {\n const child = block.children[childIndex]\n if (Element.isElement(child) && editor.isVoid(child)) {\n return blockPath.concat(childIndex).concat(0)\n }\n return blockPath.concat(childIndex)\n }\n\n return [blockPath[0], 0]\n}\n","import {Point, type Editor, type Operation, type Range} from 'slate'\nimport type {EditorSelection} from '../types/editor'\nimport {toSlatePath} from './paths'\n\nexport interface ObjectWithKeyAndType {\n _key: string\n _type: string\n children?: ObjectWithKeyAndType[]\n}\n\nexport function toSlateRange(\n selection: EditorSelection,\n editor: Editor,\n): Range | null {\n if (!selection || !editor) {\n return null\n }\n const anchor = {\n path: toSlatePath(selection.anchor.path, editor),\n offset: selection.anchor.offset,\n }\n const focus = {\n path: toSlatePath(selection.focus.path, editor),\n offset: selection.focus.offset,\n }\n if (focus.path.length === 0 || anchor.path.length === 0) {\n return null\n }\n const range = anchor && focus ? {anchor, focus} : null\n return range\n}\n\nexport function moveRangeByOperation(\n range: Range,\n operation: Operation,\n): Range | null {\n const anchor = Point.transform(range.anchor, operation)\n const focus = Point.transform(range.focus, operation)\n\n if (anchor === null || focus === null) {\n return null\n }\n\n if (Point.equals(anchor, range.anchor) && Point.equals(focus, range.focus)) {\n return range\n }\n\n return {anchor, focus}\n}\n","import type {Path, PortableTextBlock} from '@sanity/types'\nimport {isEqual} from 'lodash'\nimport type {EditorSelection, EditorSelectionPoint} from '../types/editor'\n\nexport function normalizePoint(\n point: EditorSelectionPoint,\n value: PortableTextBlock[],\n): EditorSelectionPoint | null {\n if (!point || !value) {\n return null\n }\n const newPath: Path = []\n let newOffset: number = point.offset || 0\n const blockKey =\n typeof point.path[0] === 'object' &&\n '_key' in point.path[0] &&\n point.path[0]._key\n const childKey =\n typeof point.path[2] === 'object' &&\n '_key' in point.path[2] &&\n point.path[2]._key\n const block: PortableTextBlock | undefined = value.find(\n (blk) => blk._key === blockKey,\n )\n if (block) {\n newPath.push({_key: block._key})\n } else {\n return null\n }\n if (block && point.path[1] === 'children') {\n if (\n !block.children ||\n (Array.isArray(block.children) && block.children.length === 0)\n ) {\n return null\n }\n const child =\n Array.isArray(block.children) &&\n block.children.find((cld) => cld._key === childKey)\n if (child) {\n newPath.push('children')\n newPath.push({_key: child._key})\n newOffset =\n child.text && child.text.length >= point.offset\n ? point.offset\n : (child.text && child.text.length) || 0\n } else {\n return null\n }\n }\n return {path: newPath, offset: newOffset}\n}\n\nexport function normalizeSelection(\n selection: EditorSelection,\n value: PortableTextBlock[] | undefined,\n): EditorSelection | null {\n if (!selection || !value || value.length === 0) {\n return null\n }\n let newAnchor: EditorSelectionPoint | null = null\n let newFocus: EditorSelectionPoint | null = null\n const {anchor, focus} = selection\n if (\n anchor &&\n value.find((blk) => isEqual({_key: blk._key}, anchor.path[0]))\n ) {\n newAnchor = normalizePoint(anchor, value)\n }\n if (focus && value.find((blk) => isEqual({_key: blk._key}, focus.path[0]))) {\n newFocus = normalizePoint(focus, value)\n }\n if (newAnchor && newFocus) {\n return {anchor: newAnchor, focus: newFocus, backward: selection.backward}\n }\n return null\n}\n","import type {Editor, Range} from 'slate'\nimport type {EditorSelection} from '..'\n\n// Is the editor currently receiving remote changes that are being applied to the content?\nexport const IS_PROCESSING_REMOTE_CHANGES: WeakMap<Editor, boolean> =\n new WeakMap()\n\nexport const KEY_TO_SLATE_ELEMENT: WeakMap<Editor, any | undefined> =\n new WeakMap()\nexport const KEY_TO_VALUE_ELEMENT: WeakMap<Editor, any | undefined> =\n new WeakMap()\n\n// Keep object relation to slate range in the portable-text-range\nexport const SLATE_TO_PORTABLE_TEXT_RANGE = new WeakMap<\n Range,\n EditorSelection\n>()\n","import {createContext} from 'react'\nimport type {EditorActor} from './editor-machine'\n\nexport const EditorActorContext = createContext<EditorActor>({} as EditorActor)\n","export function DropIndicator() {\n return (\n <div\n contentEditable={false}\n className=\"pt-drop-indicator\"\n style={{\n position: 'absolute',\n width: '100%',\n height: 1,\n borderBottom: '1px solid currentColor',\n zIndex: 5,\n }}\n >\n <span />\n </div>\n )\n}\n","import type {PortableTextChild, PortableTextObject} from '@sanity/types'\n\nexport function RenderDefaultBlockObject(props: {\n blockObject: PortableTextObject\n}) {\n return (\n <div style={{userSelect: 'none'}}>\n [{props.blockObject._type}: {props.blockObject._key}]\n </div>\n )\n}\n\nexport function RenderDefaultInlineObject(props: {\n inlineObject: PortableTextObject | PortableTextChild\n}) {\n return (\n <span style={{userSelect: 'none'}}>\n [{props.inlineObject._type}: {props.inlineObject._key}]\n </span>\n )\n}\n","import type {EditorSnapshot} from '..'\nimport * as selectors from '../selectors'\nimport * as utils from '../utils'\nimport type {EventPosition} from './event-position'\n\n/**\n * Given the current editor `snapshot` and an `eventSelection` representing\n * where the drag event origins from, this function calculates the selection\n * in the editor that should be dragged.\n */\nexport function getDragSelection({\n eventSelection,\n snapshot,\n}: {\n eventSelection: EventPosition['selection']\n snapshot: EditorSnapshot\n}) {\n let dragSelection = eventSelection\n\n const draggedInlineObject = selectors.getFocusInlineObject({\n ...snapshot,\n context: {\n ...snapshot.context,\n selection: eventSelection,\n },\n })\n\n if (draggedInlineObject) {\n return dragSelection\n }\n\n const draggingCollapsedSelection = selectors.isSelectionCollapsed({\n ...snapshot,\n context: {\n ...snapshot.context,\n selection: eventSelection,\n },\n })\n const draggedTextBlock = selectors.getFocusTextBlock({\n ...snapshot,\n context: {\n ...snapshot.context,\n selection: eventSelection,\n },\n })\n const draggedSpan = selectors.getFocusSpan({\n ...snapshot,\n context: {\n ...snapshot.context,\n selection: eventSelection,\n },\n })\n\n if (draggingCollapsedSelection && draggedTextBlock && draggedSpan) {\n // Looks like we are dragging an empty span\n // Let's drag the entire block instead\n dragSelection = {\n anchor: utils.getBlockStartPoint({\n context: snapshot.context,\n block: draggedTextBlock,\n }),\n focus: utils.getBlockEndPoint({\n context: snapshot.context,\n block: draggedTextBlock,\n }),\n }\n }\n\n const selectedBlocks = selectors.getSelectedBlocks(snapshot)\n\n if (\n snapshot.context.selection &&\n selectors.isSelectionExpanded(snapshot) &&\n selectedBlocks.length > 1\n ) {\n const selectionStartBlock = selectors.getSelectionStartBlock(snapshot)\n const selectionEndBlock = selectors.getSelectionEndBlock(snapshot)\n\n if (!selectionStartBlock || !selectionEndBlock) {\n return dragSelection\n }\n\n const selectionStartPoint = utils.getBlockStartPoint({\n context: snapshot.context,\n block: selectionStartBlock,\n })\n const selectionEndPoint = utils.getBlockEndPoint({\n context: snapshot.context,\n block: selectionEndBlock,\n })\n\n const eventSelectionInsideBlocks = selectors.isOverlappingSelection(\n eventSelection,\n )({\n ...snapshot,\n context: {\n ...snapshot.context,\n selection: {anchor: selectionStartPoint, focus: selectionEndPoint},\n },\n })\n\n if (eventSelectionInsideBlocks) {\n dragSelection = {\n anchor: selectionStartPoint,\n focus: selectionEndPoint,\n }\n }\n }\n\n return dragSelection\n}\n","import getRandomValues from 'get-random-values-esm'\n\n/**\n * @public\n */\nexport const defaultKeyGenerator = (): string => randomKey(12)\n\nconst getByteHexTable = (() => {\n let table: any[]\n return () => {\n if (table) {\n return table\n }\n\n table = []\n for (let i = 0; i < 256; ++i) {\n table[i] = (i + 0x100).toString(16).slice(1)\n }\n return table\n }\n})()\n\n// WHATWG crypto RNG - https://w3c.github.io/webcrypto/Overview.html\nfunction whatwgRNG(length = 16) {\n const rnds8 = new Uint8Array(length)\n getRandomValues(rnds8)\n return rnds8\n}\n\nfunction randomKey(length?: number): string {\n const table = getByteHexTable()\n return whatwgRNG(length)\n .reduce((str, n) => str + table[n], '')\n .slice(0, length)\n}\n","import {defaultKeyGenerator} from '../editor/key-generator'\n\nexport type EditorPriority = {\n id: string\n name?: string\n reference?: {\n priority: EditorPriority\n importance: 'higher' | 'lower'\n }\n}\n\nexport function createEditorPriority(config?: {\n name?: string\n reference?: {\n priority: EditorPriority\n importance: 'higher' | 'lower'\n }\n}): EditorPriority {\n return {\n id: defaultKeyGenerator(),\n name: config?.name,\n reference: config?.reference,\n }\n}\n","import {createEditorPriority} from './priority.types'\n\nexport const corePriority = createEditorPriority({name: 'core'})\n","import {getDragSelection} from '../internal-utils/drag-selection'\nimport type {EventPositionBlock} from '../internal-utils/event-position'\nimport {corePriority} from '../priority/priority.core'\nimport {createEditorPriority} from '../priority/priority.types'\nimport * as selectors from '../selectors'\nimport {forward} from './behavior.types.action'\nimport {defineBehavior} from './behavior.types.behavior'\n\nexport function createCoreBlockElementBehaviorsConfig({\n key,\n onSetDragPositionBlock,\n}: {\n key: string\n onSetDragPositionBlock: (\n eventPositionBlock: EventPositionBlock | undefined,\n ) => void\n}) {\n return [\n {\n behavior: defineBehavior({\n on: 'drag.dragover',\n guard: ({snapshot, event}) => {\n const dropFocusBlock = selectors.getFocusBlock({\n ...snapshot,\n context: {\n ...snapshot.context,\n selection: event.position.selection,\n },\n })\n\n if (!dropFocusBlock || dropFocusBlock.node._key !== key) {\n return false\n }\n\n const dragOrigin = event.dragOrigin\n\n if (!dragOrigin) {\n return false\n }\n\n const dragSelection = getDragSelection({\n eventSelection: dragOrigin.selection,\n snapshot,\n })\n\n const draggedBlocks = selectors.getSelectedBlocks({\n ...snapshot,\n context: {\n ...snapshot.context,\n selection: dragSelection,\n },\n })\n\n if (\n draggedBlocks.some((draggedBlock) => draggedBlock.node._key === key)\n ) {\n return false\n }\n\n const draggingEntireBlocks = selectors.isSelectingEntireBlocks({\n ...snapshot,\n context: {\n ...snapshot.context,\n selection: dragSelection,\n },\n })\n\n return draggingEntireBlocks\n },\n actions: [\n ({event}) => [\n {\n type: 'effect',\n effect: () => {\n onSetDragPositionBlock(event.position.block)\n },\n },\n ],\n ],\n }),\n priority: createEditorPriority({\n reference: {\n priority: corePriority,\n importance: 'lower',\n },\n }),\n },\n {\n behavior: defineBehavior({\n on: 'drag.*',\n guard: ({event}) => {\n return event.type !== 'drag.dragover'\n },\n actions: [\n ({event}) => [\n {\n type: 'effect',\n effect: () => {\n onSetDragPositionBlock(undefined)\n },\n },\n forward(event),\n ],\n ],\n }),\n priority: createEditorPriority({\n reference: {\n priority: corePriority,\n importance: 'lower',\n },\n }),\n },\n ]\n}\n","import {useContext, useEffect} from 'react'\nimport {createCoreBlockElementBehaviorsConfig} from '../../behaviors/behavior.core.block-element'\nimport type {EventPositionBlock} from '../../internal-utils/event-position'\nimport {EditorActorContext} from '../editor-actor-context'\n\nexport function useCoreBlockElementBehaviors({\n key,\n onSetDragPositionBlock,\n}: {\n key: string\n onSetDragPositionBlock: (\n eventPositionBlock: EventPositionBlock | undefined,\n ) => void\n}) {\n const editorActor = useContext(EditorActorContext)\n\n useEffect(() => {\n const behaviorConfigs = createCoreBlockElementBehaviorsConfig({\n key,\n onSetDragPositionBlock,\n })\n\n for (const behaviorConfig of behaviorConfigs) {\n editorActor.send({\n type: 'add behavior',\n behaviorConfig,\n })\n }\n\n return () => {\n for (const behaviorConfig of behaviorConfigs) {\n editorActor.send({\n type: 'remove behavior',\n behaviorConfig,\n })\n }\n }\n }, [editorActor, key, onSetDragPositionBlock])\n}\n","import type {PortableTextObject} from '@sanity/types'\nimport {useSelector} from '@xstate/react'\nimport {useContext, useRef, useState, type ReactElement} from 'react'\nimport {Range, type Element as SlateElement} from 'slate'\nimport {useSelected, useSlateStatic, type RenderElementProps} from 'slate-react'\nimport type {EventPositionBlock} from '../../internal-utils/event-position'\nimport type {RenderBlockFunction} from '../../types/editor'\nimport {EditorActorContext} from '../editor-actor-context'\nimport {DropIndicator} from './drop-indicator'\nimport {RenderDefaultBlockObject} from './render-default-object'\nimport {useCoreBlockElementBehaviors} from './use-core-block-element-behaviors'\n\nexport function RenderBlockObject(props: {\n attributes: RenderElementProps['attributes']\n blockObject: PortableTextObject\n children: ReactElement\n element: SlateElement\n readOnly: boolean\n renderBlock?: RenderBlockFunction\n}) {\n const [dragPositionBlock, setDragPositionBlock] =\n useState<EventPositionBlock>()\n const blockObjectRef = useRef<HTMLDivElement>(null)\n\n const slateEditor = useSlateStatic()\n const selected = useSelected()\n\n const editorActor = useContext(EditorActorContext)\n\n useCoreBlockElementBehaviors({\n key: props.element._key,\n onSetDragPositionBlock: setDragPositionBlock,\n })\n\n const legacySchemaType = useSelector(editorActor, (s) =>\n s.context\n .getLegacySchema()\n .blockObjects.find(\n (blockObject) => blockObject.name === props.element._type,\n ),\n )\n\n if (!legacySchemaType) {\n console.error(\n `Block object type ${props.element._type} not found in Schema`,\n )\n }\n\n const focused =\n selected &&\n slateEditor.selection !== null &&\n Range.isCollapsed(slateEditor.selection)\n\n return (\n <div\n {...props.attributes}\n className=\"pt-block pt-object-block\"\n data-block-key={props.element._key}\n data-block-name={props.element._type}\n data-block-type=\"object\"\n >\n {dragPositionBlock === 'start' ? <DropIndicator /> : null}\n {props.children}\n <div\n ref={blockObjectRef}\n contentEditable={false}\n draggable={!props.readOnly}\n >\n {props.renderBlock && legacySchemaType ? (\n props.renderBlock({\n children: (\n <RenderDefaultBlockObject blockObject={props.blockObject} />\n ),\n editorElementRef: blockObjectRef,\n focused,\n path: [{_key: props.element._key}],\n schemaType: legacySchemaType,\n selected,\n type: legacySchemaType,\n value: props.blockObject,\n })\n ) : (\n <RenderDefaultBlockObject blockObject={props.blockObject} />\n )}\n </div>\n {dragPositionBlock === 'end' ? <DropIndicator /> : null}\n </div>\n )\n}\n","import type {PortableTextObject} from '@sanity/types'\nimport {useSelector} from '@xstate/react'\nimport {useContext, useRef, type ReactElement} from 'react'\nimport {Range, type Element as SlateElement} from 'slate'\nimport {DOMEditor} from 'slate-dom'\nimport {useSelected, useSlateStatic, type RenderElementProps} from 'slate-react'\nimport {getPointBlock} from '../../internal-utils/slate-utils'\nimport type {RenderChildFunction} from '../../types/editor'\nimport {EditorActorContext} from '../editor-actor-context'\nimport {RenderDefaultInlineObject} from './render-default-object'\n\nexport function RenderInlineObject(props: {\n attributes: RenderElementProps['attributes']\n children: ReactElement\n element: SlateElement\n inlineObject: PortableTextObject\n readOnly: boolean\n renderChild?: RenderChildFunction\n}) {\n const inlineObjectRef = useRef<HTMLElement>(null)\n\n const slateEditor = useSlateStatic()\n const selected = useSelected()\n\n const editorActor = useContext(EditorActorContext)\n const legacySchemaType = useSelector(editorActor, (s) =>\n s.context\n .getLegacySchema()\n .inlineObjects.find(\n (inlineObject) => inlineObject.name === props.element._type,\n ),\n )\n\n if (!legacySchemaType) {\n console.error(\n `Inline object type ${props.element._type} not found in Schema`,\n )\n }\n\n const focused =\n selected &&\n slateEditor.selection !== null &&\n Range.isCollapsed(slateEditor.selection)\n const path = DOMEditor.findPath(slateEditor, props.element)\n const [block] = getPointBlock({\n editor: slateEditor,\n point: {\n path,\n offset: 0,\n },\n })\n\n if (!block) {\n console.error(\n `Unable to find parent block of inline object ${props.element._key}`,\n )\n }\n\n return (\n <span\n {...props.attributes}\n draggable={!props.readOnly}\n className=\"pt-inline-object\"\n data-child-key={props.inlineObject._key}\n data-child-name={props.inlineObject._type}\n data-child-type=\"object\"\n >\n {props.children}\n <span ref={inlineObjectRef} style={{display: 'inline-block'}}>\n {props.renderChild && block && legacySchemaType ? (\n props.renderChild({\n annotations: [],\n children: (\n <RenderDefaultInlineObject inlineObject={props.inlineObject} />\n