@portabletext/editor 2.12.2 → 2.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/lib/_chunks-cjs/selector.is-selecting-entire-blocks.cjs +64 -5
  2. package/lib/_chunks-cjs/selector.is-selecting-entire-blocks.cjs.map +1 -1
  3. package/lib/_chunks-cjs/util.slice-blocks.cjs +1 -0
  4. package/lib/_chunks-cjs/util.slice-blocks.cjs.map +1 -1
  5. package/lib/_chunks-dts/behavior.types.action.d.cts +64 -72
  6. package/lib/_chunks-dts/behavior.types.action.d.ts +9 -17
  7. package/lib/_chunks-es/selector.is-selecting-entire-blocks.js +65 -6
  8. package/lib/_chunks-es/selector.is-selecting-entire-blocks.js.map +1 -1
  9. package/lib/_chunks-es/util.slice-blocks.js +1 -0
  10. package/lib/_chunks-es/util.slice-blocks.js.map +1 -1
  11. package/lib/index.cjs +69 -60
  12. package/lib/index.cjs.map +1 -1
  13. package/lib/index.js +69 -60
  14. package/lib/index.js.map +1 -1
  15. package/lib/plugins/index.cjs +1 -1
  16. package/lib/plugins/index.d.ts +3 -3
  17. package/lib/plugins/index.js +1 -1
  18. package/lib/selectors/index.cjs +1 -0
  19. package/lib/selectors/index.cjs.map +1 -1
  20. package/lib/selectors/index.d.cts +15 -1
  21. package/lib/selectors/index.d.ts +15 -1
  22. package/lib/selectors/index.js +2 -1
  23. package/package.json +16 -16
  24. package/src/behaviors/behavior.abstract.delete.ts +1 -0
  25. package/src/behaviors/behavior.abstract.keyboard.ts +27 -0
  26. package/src/editor/Editable.tsx +1 -90
  27. package/src/internal-utils/asserters.ts +1 -1
  28. package/src/keyboard-shortcuts/default-keyboard-shortcuts.ts +22 -0
  29. package/src/operations/behavior.operation.delete.ts +6 -24
  30. package/src/selectors/index.ts +1 -0
  31. package/src/selectors/selector.get-mark-state.ts +75 -6
  32. package/src/test/vitest/step-definitions.tsx +1 -8
  33. package/src/types/paths.ts +18 -0
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: !0 });
3
- var reactCompilerRuntime = require("react-compiler-runtime"), React = require("react"), useEditor = require("../_chunks-cjs/use-editor.cjs"), react = require("@xstate/react"), isEqual = require("lodash/isEqual.js"), xstate = require("xstate"), selector_isSelectionExpanded = require("../_chunks-cjs/selector.is-selection-expanded.cjs"), util_sliceBlocks = require("../_chunks-cjs/util.slice-blocks.cjs"), util_childSelectionPointToBlockOffset = require("../_chunks-cjs/util.child-selection-point-to-block-offset.cjs"), selector_getTextBefore = require("../_chunks-cjs/selector.get-text-before.cjs"), behaviors_index = require("../behaviors/index.cjs"), jsxRuntime = require("react/jsx-runtime"), schema = require("@portabletext/schema"), util_mergeTextBlocks = require("../_chunks-cjs/util.merge-text-blocks.cjs");
3
+ var reactCompilerRuntime = require("react-compiler-runtime"), React = require("react"), useEditor = require("../_chunks-cjs/use-editor.cjs"), react = require("@xstate/react"), isEqual = require("lodash/isEqual.js"), xstate = require("xstate"), util_sliceBlocks = require("../_chunks-cjs/util.slice-blocks.cjs"), util_childSelectionPointToBlockOffset = require("../_chunks-cjs/util.child-selection-point-to-block-offset.cjs"), selector_isSelectionExpanded = require("../_chunks-cjs/selector.is-selection-expanded.cjs"), selector_getTextBefore = require("../_chunks-cjs/selector.get-text-before.cjs"), behaviors_index = require("../behaviors/index.cjs"), jsxRuntime = require("react/jsx-runtime"), schema = require("@portabletext/schema"), util_mergeTextBlocks = require("../_chunks-cjs/util.merge-text-blocks.cjs");
4
4
  function _interopDefaultCompat(e) {
5
5
  return e && typeof e == "object" && "default" in e ? e : { default: e };
6
6
  }
@@ -1,5 +1,5 @@
1
1
  import { Behavior, Editor, EditorEmittedEvent, EditorSchema } from "../_chunks-dts/behavior.types.action.js";
2
- import * as react12 from "react";
2
+ import * as react22 from "react";
3
3
  import React from "react";
4
4
  /**
5
5
  * @beta
@@ -181,7 +181,7 @@ type MarkdownPluginConfig = MarkdownBehaviorsConfig & {
181
181
  */
182
182
  declare function MarkdownPlugin(props: {
183
183
  config: MarkdownPluginConfig;
184
- }): react12.JSX.Element;
184
+ }): react22.JSX.Element;
185
185
  /**
186
186
  * @beta
187
187
  * Restrict the editor to one line. The plugin takes care of blocking
@@ -192,5 +192,5 @@ declare function MarkdownPlugin(props: {
192
192
  *
193
193
  * @deprecated Install the plugin from `@portabletext/plugin-one-line`
194
194
  */
195
- declare function OneLinePlugin(): react12.JSX.Element;
195
+ declare function OneLinePlugin(): react22.JSX.Element;
196
196
  export { BehaviorPlugin, DecoratorShortcutPlugin, EditorRefPlugin, EventListenerPlugin, MarkdownPlugin, type MarkdownPluginConfig, OneLinePlugin };
@@ -4,9 +4,9 @@ import { useEditor } from "../_chunks-es/use-editor.js";
4
4
  import { useActorRef } from "@xstate/react";
5
5
  import isEqual from "lodash/isEqual.js";
6
6
  import { setup, fromCallback, assign } from "xstate";
7
- import { getFocusTextBlock, getSelectionStartPoint, getPreviousInlineObject, isSelectionCollapsed, getFocusSpan, getFocusBlock, isSelectionExpanded } from "../_chunks-es/selector.is-selection-expanded.js";
8
7
  import { spanSelectionPointToBlockOffset, getTextBlockText } from "../_chunks-es/util.slice-blocks.js";
9
8
  import { blockOffsetsToSelection, childSelectionPointToBlockOffset } from "../_chunks-es/util.child-selection-point-to-block-offset.js";
9
+ import { getFocusTextBlock, getSelectionStartPoint, getPreviousInlineObject, isSelectionCollapsed, getFocusSpan, getFocusBlock, isSelectionExpanded } from "../_chunks-es/selector.is-selection-expanded.js";
10
10
  import { getBlockTextBefore } from "../_chunks-es/selector.get-text-before.js";
11
11
  import { defineBehavior, execute, effect, forward, raise } from "../behaviors/index.js";
12
12
  import { jsxs, Fragment, jsx } from "react/jsx-runtime";
@@ -125,6 +125,7 @@ exports.getFocusBlockObject = selector_isSelectingEntireBlocks.getFocusBlockObje
125
125
  exports.getFocusInlineObject = selector_isSelectingEntireBlocks.getFocusInlineObject;
126
126
  exports.getFocusListBlock = selector_isSelectingEntireBlocks.getFocusListBlock;
127
127
  exports.getLastBlock = selector_isSelectingEntireBlocks.getLastBlock;
128
+ exports.getMarkState = selector_isSelectingEntireBlocks.getMarkState;
128
129
  exports.getNextBlock = selector_isSelectingEntireBlocks.getNextBlock;
129
130
  exports.getNextInlineObject = selector_isSelectingEntireBlocks.getNextInlineObject;
130
131
  exports.getPreviousBlock = selector_isSelectingEntireBlocks.getPreviousBlock;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../../src/selectors/selector.get-anchor-block.ts","../../src/selectors/selector.get-anchor-text-block.ts","../../src/selectors/selector.get-anchor-child.ts","../../src/selectors/selector.get-anchor-span.ts","../../src/selectors/selector.get-block-offsets.ts","../../src/selectors/selector.get-list-state.ts","../../src/selectors/selector.get-selected-slice.ts","../../src/selectors/selector.get-selection.ts","../../src/selectors/selector.get-selection-end-child.ts","../../src/selectors/selector.get-selection-start-child.ts","../../src/selectors/selector.get-value.ts"],"sourcesContent":["import type {PortableTextBlock} from '@sanity/types'\nimport type {EditorSelector} from '../editor/editor-selector'\nimport {getBlockKeyFromSelectionPoint} from '../selection/selection-point'\nimport type {BlockPath} from '../types/paths'\n\n/**\n * @public\n */\nexport const getAnchorBlock: EditorSelector<\n {node: PortableTextBlock; path: BlockPath} | undefined\n> = (snapshot) => {\n if (!snapshot.context.selection) {\n return undefined\n }\n\n const key = getBlockKeyFromSelectionPoint(snapshot.context.selection.anchor)\n const index = key ? snapshot.blockIndexMap.get(key) : undefined\n const node =\n index !== undefined ? snapshot.context.value.at(index) : undefined\n\n return node && key ? {node, path: [{_key: key}]} : undefined\n}\n","import {isTextBlock} from '@portabletext/schema'\nimport type {PortableTextTextBlock} from '@sanity/types'\nimport type {EditorSelector} from '../editor/editor-selector'\nimport type {BlockPath} from '../types/paths'\nimport {getAnchorBlock} from './selector.get-anchor-block'\n\n/**\n * @public\n */\nexport const getAnchorTextBlock: EditorSelector<\n {node: PortableTextTextBlock; path: BlockPath} | undefined\n> = (snapshot) => {\n const anchorBlock = getAnchorBlock(snapshot)\n\n return anchorBlock && isTextBlock(snapshot.context, anchorBlock.node)\n ? {node: anchorBlock.node, path: anchorBlock.path}\n : undefined\n}\n","import type {PortableTextObject, PortableTextSpan} from '@sanity/types'\nimport type {EditorSelector} from '../editor/editor-selector'\nimport {getChildKeyFromSelectionPoint} from '../selection/selection-point'\nimport type {ChildPath} from '../types/paths'\nimport {getAnchorTextBlock} from './selector.get-anchor-text-block'\n\n/**\n * @public\n */\nexport const getAnchorChild: EditorSelector<\n | {\n node: PortableTextObject | PortableTextSpan\n path: ChildPath\n }\n | undefined\n> = (snapshot) => {\n if (!snapshot.context.selection) {\n return undefined\n }\n\n const anchorBlock = getAnchorTextBlock(snapshot)\n\n if (!anchorBlock) {\n return undefined\n }\n\n const key = getChildKeyFromSelectionPoint(snapshot.context.selection.anchor)\n\n const node = key\n ? anchorBlock.node.children.find((span) => span._key === key)\n : undefined\n\n return node && key\n ? {node, path: [...anchorBlock.path, 'children', {_key: key}]}\n : undefined\n}\n","import {isPortableTextSpan, type PortableTextSpan} from '@sanity/types'\nimport type {EditorSelector} from '../editor/editor-selector'\nimport type {ChildPath} from '../types/paths'\nimport {getAnchorChild} from './selector.get-anchor-child'\n\n/**\n * @public\n */\nexport const getAnchorSpan: EditorSelector<\n {node: PortableTextSpan; path: ChildPath} | undefined\n> = (snapshot) => {\n const anchorChild = getAnchorChild(snapshot)\n\n return anchorChild && isPortableTextSpan(anchorChild.node)\n ? {node: anchorChild.node, path: anchorChild.path}\n : undefined\n}\n","import type {EditorSelector} from '../editor/editor-selector'\nimport type {BlockOffset} from '../types/block-offset'\nimport * as utils from '../utils'\nimport {getSelectionEndPoint} from './selector.get-selection-end-point'\nimport {getSelectionStartPoint} from './selector.get-selection-start-point'\n\n/**\n * @public\n */\nexport const getBlockOffsets: EditorSelector<\n {start: BlockOffset; end: BlockOffset} | undefined\n> = (snapshot) => {\n if (!snapshot.context.selection) {\n return undefined\n }\n\n const selectionStartPoint = getSelectionStartPoint(snapshot)\n const selectionEndPoint = getSelectionEndPoint(snapshot)\n\n if (!selectionStartPoint || !selectionEndPoint) {\n return undefined\n }\n\n const start = utils.spanSelectionPointToBlockOffset({\n context: snapshot.context,\n selectionPoint: selectionStartPoint,\n })\n const end = utils.spanSelectionPointToBlockOffset({\n context: snapshot.context,\n selectionPoint: selectionEndPoint,\n })\n\n return start && end ? {start, end} : undefined\n}\n","import {isTextBlock} from '@portabletext/schema'\nimport type {EditorSelector} from '../editor/editor-selector'\nimport type {BlockPath} from '../types/paths'\nimport {getFocusTextBlock} from './selector.get-focus-text-block'\n\n/**\n * @beta\n * @deprecated Use the precomputed `data-list-index` on text blocks instead.\n * Given the `path` of a block, this selector will return the \"list index\" of\n * the block.\n */\nexport function getListIndex({\n path,\n}: {\n path: BlockPath\n}): EditorSelector<number | undefined> {\n return (snapshot) => {\n const selection = {\n anchor: {\n path,\n offset: 0,\n },\n focus: {\n path,\n offset: 0,\n },\n }\n\n const focusTextBlock = getFocusTextBlock({\n ...snapshot,\n context: {\n ...snapshot.context,\n selection,\n },\n })\n\n if (!focusTextBlock) {\n return undefined\n }\n\n if (\n focusTextBlock.node.listItem === undefined ||\n focusTextBlock.node.level === undefined\n ) {\n return undefined\n }\n\n const targetListItem = focusTextBlock.node.listItem\n const targetLevel = focusTextBlock.node.level\n const targetKey = focusTextBlock.node._key\n\n // Find the target block's index\n const targetIndex = snapshot.blockIndexMap.get(targetKey)\n\n if (targetIndex === undefined) {\n return undefined\n }\n\n // Walk backwards from the target block and count consecutive list items\n // of the same type and level\n let listIndex = 1 // Start at 1 for the target block itself\n\n for (let i = targetIndex - 1; i >= 0; i--) {\n const block = snapshot.context.value[i]\n\n if (!isTextBlock(snapshot.context, block)) {\n // Non-text block breaks the sequence\n break\n }\n\n if (block.listItem === undefined || block.level === undefined) {\n // Non-list item breaks the sequence\n break\n }\n\n if (block.listItem !== targetListItem) {\n // Different list type breaks the sequence\n break\n }\n\n if (block.level < targetLevel) {\n // Lower level breaks the sequence\n break\n }\n\n if (block.level === targetLevel) {\n // Same level - continue counting\n listIndex++\n }\n\n // Higher level items don't affect the count for the target level\n }\n\n return listIndex\n }\n}\n","import type {PortableTextBlock} from '@sanity/types'\nimport type {EditorSelector} from '../editor/editor-selector'\nimport {getSelectedValue} from './selector.get-selected-value'\n\n/**\n * @public\n * @deprecated Renamed to `getSelectedValue`.\n */\nexport const getSelectedSlice: EditorSelector<Array<PortableTextBlock>> = (\n snapshot,\n) => {\n return getSelectedValue(snapshot)\n}\n","import type {EditorSelection} from '..'\nimport type {EditorSelector} from '../editor/editor-selector'\n\n/**\n * @public\n */\nexport const getSelection: EditorSelector<EditorSelection> = (snapshot) => {\n return snapshot.context.selection\n}\n","import type {PortableTextObject, PortableTextSpan} from '@sanity/types'\nimport type {EditorSelector} from '../editor/editor-selector'\nimport type {ChildPath} from '../types/paths'\nimport {getSelectionEndPoint} from '../utils/util.get-selection-end-point'\nimport {getFocusChild} from './selector.get-focus-child'\n\n/**\n * @public\n */\nexport const getSelectionEndChild: EditorSelector<\n | {\n node: PortableTextSpan | PortableTextObject\n path: ChildPath\n }\n | undefined\n> = (snapshot) => {\n const endPoint = getSelectionEndPoint(snapshot.context.selection)\n\n if (!endPoint) {\n return undefined\n }\n\n return getFocusChild({\n ...snapshot,\n context: {\n ...snapshot.context,\n selection: {\n anchor: endPoint,\n focus: endPoint,\n },\n },\n })\n}\n","import type {PortableTextObject, PortableTextSpan} from '@sanity/types'\nimport type {EditorSelector} from '../editor/editor-selector'\nimport type {ChildPath} from '../types/paths'\nimport {getSelectionStartPoint} from '../utils/util.get-selection-start-point'\nimport {getFocusChild} from './selector.get-focus-child'\n\n/**\n * @public\n */\nexport const getSelectionStartChild: EditorSelector<\n | {\n node: PortableTextSpan | PortableTextObject\n path: ChildPath\n }\n | undefined\n> = (snapshot) => {\n const startPoint = getSelectionStartPoint(snapshot.context.selection)\n\n if (!startPoint) {\n return undefined\n }\n\n return getFocusChild({\n ...snapshot,\n context: {\n ...snapshot.context,\n selection: {\n anchor: startPoint,\n focus: startPoint,\n },\n },\n })\n}\n","import type {PortableTextBlock} from '@sanity/types'\nimport type {EditorSelector} from '../editor/editor-selector'\n\n/**\n * @public\n */\nexport const getValue: EditorSelector<Array<PortableTextBlock>> = (\n snapshot,\n) => {\n return snapshot.context.value\n}\n"],"names":["getAnchorBlock","snapshot","context","selection","key","getBlockKeyFromSelectionPoint","anchor","index","blockIndexMap","get","undefined","node","value","at","path","_key","getAnchorTextBlock","anchorBlock","isTextBlock","getAnchorChild","getChildKeyFromSelectionPoint","children","find","span","getAnchorSpan","anchorChild","isPortableTextSpan","getBlockOffsets","selectionStartPoint","getSelectionStartPoint","selectionEndPoint","getSelectionEndPoint","start","utils","selectionPoint","end","getListIndex","offset","focus","focusTextBlock","getFocusTextBlock","listItem","level","targetListItem","targetLevel","targetKey","targetIndex","listIndex","i","block","getSelectedSlice","getSelectedValue","getSelection","getSelectionEndChild","endPoint","getFocusChild","getSelectionStartChild","startPoint","getValue"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../../src/selectors/selector.get-anchor-block.ts","../../src/selectors/selector.get-anchor-text-block.ts","../../src/selectors/selector.get-anchor-child.ts","../../src/selectors/selector.get-anchor-span.ts","../../src/selectors/selector.get-block-offsets.ts","../../src/selectors/selector.get-list-state.ts","../../src/selectors/selector.get-selected-slice.ts","../../src/selectors/selector.get-selection.ts","../../src/selectors/selector.get-selection-end-child.ts","../../src/selectors/selector.get-selection-start-child.ts","../../src/selectors/selector.get-value.ts"],"sourcesContent":["import type {PortableTextBlock} from '@sanity/types'\nimport type {EditorSelector} from '../editor/editor-selector'\nimport {getBlockKeyFromSelectionPoint} from '../selection/selection-point'\nimport type {BlockPath} from '../types/paths'\n\n/**\n * @public\n */\nexport const getAnchorBlock: EditorSelector<\n {node: PortableTextBlock; path: BlockPath} | undefined\n> = (snapshot) => {\n if (!snapshot.context.selection) {\n return undefined\n }\n\n const key = getBlockKeyFromSelectionPoint(snapshot.context.selection.anchor)\n const index = key ? snapshot.blockIndexMap.get(key) : undefined\n const node =\n index !== undefined ? snapshot.context.value.at(index) : undefined\n\n return node && key ? {node, path: [{_key: key}]} : undefined\n}\n","import {isTextBlock} from '@portabletext/schema'\nimport type {PortableTextTextBlock} from '@sanity/types'\nimport type {EditorSelector} from '../editor/editor-selector'\nimport type {BlockPath} from '../types/paths'\nimport {getAnchorBlock} from './selector.get-anchor-block'\n\n/**\n * @public\n */\nexport const getAnchorTextBlock: EditorSelector<\n {node: PortableTextTextBlock; path: BlockPath} | undefined\n> = (snapshot) => {\n const anchorBlock = getAnchorBlock(snapshot)\n\n return anchorBlock && isTextBlock(snapshot.context, anchorBlock.node)\n ? {node: anchorBlock.node, path: anchorBlock.path}\n : undefined\n}\n","import type {PortableTextObject, PortableTextSpan} from '@sanity/types'\nimport type {EditorSelector} from '../editor/editor-selector'\nimport {getChildKeyFromSelectionPoint} from '../selection/selection-point'\nimport type {ChildPath} from '../types/paths'\nimport {getAnchorTextBlock} from './selector.get-anchor-text-block'\n\n/**\n * @public\n */\nexport const getAnchorChild: EditorSelector<\n | {\n node: PortableTextObject | PortableTextSpan\n path: ChildPath\n }\n | undefined\n> = (snapshot) => {\n if (!snapshot.context.selection) {\n return undefined\n }\n\n const anchorBlock = getAnchorTextBlock(snapshot)\n\n if (!anchorBlock) {\n return undefined\n }\n\n const key = getChildKeyFromSelectionPoint(snapshot.context.selection.anchor)\n\n const node = key\n ? anchorBlock.node.children.find((span) => span._key === key)\n : undefined\n\n return node && key\n ? {node, path: [...anchorBlock.path, 'children', {_key: key}]}\n : undefined\n}\n","import {isPortableTextSpan, type PortableTextSpan} from '@sanity/types'\nimport type {EditorSelector} from '../editor/editor-selector'\nimport type {ChildPath} from '../types/paths'\nimport {getAnchorChild} from './selector.get-anchor-child'\n\n/**\n * @public\n */\nexport const getAnchorSpan: EditorSelector<\n {node: PortableTextSpan; path: ChildPath} | undefined\n> = (snapshot) => {\n const anchorChild = getAnchorChild(snapshot)\n\n return anchorChild && isPortableTextSpan(anchorChild.node)\n ? {node: anchorChild.node, path: anchorChild.path}\n : undefined\n}\n","import type {EditorSelector} from '../editor/editor-selector'\nimport type {BlockOffset} from '../types/block-offset'\nimport * as utils from '../utils'\nimport {getSelectionEndPoint} from './selector.get-selection-end-point'\nimport {getSelectionStartPoint} from './selector.get-selection-start-point'\n\n/**\n * @public\n */\nexport const getBlockOffsets: EditorSelector<\n {start: BlockOffset; end: BlockOffset} | undefined\n> = (snapshot) => {\n if (!snapshot.context.selection) {\n return undefined\n }\n\n const selectionStartPoint = getSelectionStartPoint(snapshot)\n const selectionEndPoint = getSelectionEndPoint(snapshot)\n\n if (!selectionStartPoint || !selectionEndPoint) {\n return undefined\n }\n\n const start = utils.spanSelectionPointToBlockOffset({\n context: snapshot.context,\n selectionPoint: selectionStartPoint,\n })\n const end = utils.spanSelectionPointToBlockOffset({\n context: snapshot.context,\n selectionPoint: selectionEndPoint,\n })\n\n return start && end ? {start, end} : undefined\n}\n","import {isTextBlock} from '@portabletext/schema'\nimport type {EditorSelector} from '../editor/editor-selector'\nimport type {BlockPath} from '../types/paths'\nimport {getFocusTextBlock} from './selector.get-focus-text-block'\n\n/**\n * @beta\n * @deprecated Use the precomputed `data-list-index` on text blocks instead.\n * Given the `path` of a block, this selector will return the \"list index\" of\n * the block.\n */\nexport function getListIndex({\n path,\n}: {\n path: BlockPath\n}): EditorSelector<number | undefined> {\n return (snapshot) => {\n const selection = {\n anchor: {\n path,\n offset: 0,\n },\n focus: {\n path,\n offset: 0,\n },\n }\n\n const focusTextBlock = getFocusTextBlock({\n ...snapshot,\n context: {\n ...snapshot.context,\n selection,\n },\n })\n\n if (!focusTextBlock) {\n return undefined\n }\n\n if (\n focusTextBlock.node.listItem === undefined ||\n focusTextBlock.node.level === undefined\n ) {\n return undefined\n }\n\n const targetListItem = focusTextBlock.node.listItem\n const targetLevel = focusTextBlock.node.level\n const targetKey = focusTextBlock.node._key\n\n // Find the target block's index\n const targetIndex = snapshot.blockIndexMap.get(targetKey)\n\n if (targetIndex === undefined) {\n return undefined\n }\n\n // Walk backwards from the target block and count consecutive list items\n // of the same type and level\n let listIndex = 1 // Start at 1 for the target block itself\n\n for (let i = targetIndex - 1; i >= 0; i--) {\n const block = snapshot.context.value[i]\n\n if (!isTextBlock(snapshot.context, block)) {\n // Non-text block breaks the sequence\n break\n }\n\n if (block.listItem === undefined || block.level === undefined) {\n // Non-list item breaks the sequence\n break\n }\n\n if (block.listItem !== targetListItem) {\n // Different list type breaks the sequence\n break\n }\n\n if (block.level < targetLevel) {\n // Lower level breaks the sequence\n break\n }\n\n if (block.level === targetLevel) {\n // Same level - continue counting\n listIndex++\n }\n\n // Higher level items don't affect the count for the target level\n }\n\n return listIndex\n }\n}\n","import type {PortableTextBlock} from '@sanity/types'\nimport type {EditorSelector} from '../editor/editor-selector'\nimport {getSelectedValue} from './selector.get-selected-value'\n\n/**\n * @public\n * @deprecated Renamed to `getSelectedValue`.\n */\nexport const getSelectedSlice: EditorSelector<Array<PortableTextBlock>> = (\n snapshot,\n) => {\n return getSelectedValue(snapshot)\n}\n","import type {EditorSelection} from '..'\nimport type {EditorSelector} from '../editor/editor-selector'\n\n/**\n * @public\n */\nexport const getSelection: EditorSelector<EditorSelection> = (snapshot) => {\n return snapshot.context.selection\n}\n","import type {PortableTextObject, PortableTextSpan} from '@sanity/types'\nimport type {EditorSelector} from '../editor/editor-selector'\nimport type {ChildPath} from '../types/paths'\nimport {getSelectionEndPoint} from '../utils/util.get-selection-end-point'\nimport {getFocusChild} from './selector.get-focus-child'\n\n/**\n * @public\n */\nexport const getSelectionEndChild: EditorSelector<\n | {\n node: PortableTextSpan | PortableTextObject\n path: ChildPath\n }\n | undefined\n> = (snapshot) => {\n const endPoint = getSelectionEndPoint(snapshot.context.selection)\n\n if (!endPoint) {\n return undefined\n }\n\n return getFocusChild({\n ...snapshot,\n context: {\n ...snapshot.context,\n selection: {\n anchor: endPoint,\n focus: endPoint,\n },\n },\n })\n}\n","import type {PortableTextObject, PortableTextSpan} from '@sanity/types'\nimport type {EditorSelector} from '../editor/editor-selector'\nimport type {ChildPath} from '../types/paths'\nimport {getSelectionStartPoint} from '../utils/util.get-selection-start-point'\nimport {getFocusChild} from './selector.get-focus-child'\n\n/**\n * @public\n */\nexport const getSelectionStartChild: EditorSelector<\n | {\n node: PortableTextSpan | PortableTextObject\n path: ChildPath\n }\n | undefined\n> = (snapshot) => {\n const startPoint = getSelectionStartPoint(snapshot.context.selection)\n\n if (!startPoint) {\n return undefined\n }\n\n return getFocusChild({\n ...snapshot,\n context: {\n ...snapshot.context,\n selection: {\n anchor: startPoint,\n focus: startPoint,\n },\n },\n })\n}\n","import type {PortableTextBlock} from '@sanity/types'\nimport type {EditorSelector} from '../editor/editor-selector'\n\n/**\n * @public\n */\nexport const getValue: EditorSelector<Array<PortableTextBlock>> = (\n snapshot,\n) => {\n return snapshot.context.value\n}\n"],"names":["getAnchorBlock","snapshot","context","selection","key","getBlockKeyFromSelectionPoint","anchor","index","blockIndexMap","get","undefined","node","value","at","path","_key","getAnchorTextBlock","anchorBlock","isTextBlock","getAnchorChild","getChildKeyFromSelectionPoint","children","find","span","getAnchorSpan","anchorChild","isPortableTextSpan","getBlockOffsets","selectionStartPoint","getSelectionStartPoint","selectionEndPoint","getSelectionEndPoint","start","utils","selectionPoint","end","getListIndex","offset","focus","focusTextBlock","getFocusTextBlock","listItem","level","targetListItem","targetLevel","targetKey","targetIndex","listIndex","i","block","getSelectedSlice","getSelectedValue","getSelection","getSelectionEndChild","endPoint","getFocusChild","getSelectionStartChild","startPoint","getValue"],"mappings":"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"}
@@ -127,6 +127,20 @@ declare function getListIndex({
127
127
  }: {
128
128
  path: BlockPath;
129
129
  }): EditorSelector<number | undefined>;
130
+ type MarkState = {
131
+ state: 'unchanged';
132
+ marks: Array<string>;
133
+ } | {
134
+ state: 'changed';
135
+ marks: Array<string>;
136
+ previousMarks: Array<string>;
137
+ };
138
+ /**
139
+ * Given that text is inserted at the current position, what marks should
140
+ * be applied?
141
+ * @beta
142
+ */
143
+ declare const getMarkState: EditorSelector<MarkState | undefined>;
130
144
  /**
131
145
  * @public
132
146
  */
@@ -305,4 +319,4 @@ declare const isSelectionCollapsed: EditorSelector<boolean>;
305
319
  * @public
306
320
  */
307
321
  declare const isSelectionExpanded: EditorSelector<boolean>;
308
- export { getActiveAnnotations, getActiveListItem, getActiveStyle, getAnchorBlock, getAnchorChild, getAnchorSpan, getAnchorTextBlock, getBlockOffsets, getBlockTextBefore, getCaretWordSelection, getFirstBlock, getFocusBlock, getFocusBlockObject, getFocusChild, getFocusInlineObject, getFocusListBlock, getFocusSpan, getFocusTextBlock, getLastBlock, getListIndex, getNextBlock, getNextInlineObject, getPreviousBlock, getPreviousInlineObject, getSelectedBlocks, getSelectedSlice, getSelectedSpans, getSelectedTextBlocks, getSelectedValue, getSelection, getSelectionEndBlock, getSelectionEndChild, getSelectionEndPoint, getSelectionStartBlock, getSelectionStartChild, getSelectionStartPoint, getSelectionText, getTrimmedSelection, getValue, isActiveAnnotation, isActiveDecorator, isActiveListItem, isActiveStyle, isAtTheEndOfBlock, isAtTheStartOfBlock, isOverlappingSelection, isPointAfterSelection, isPointBeforeSelection, isSelectingEntireBlocks, isSelectionCollapsed, isSelectionExpanded };
322
+ export { getActiveAnnotations, getActiveListItem, getActiveStyle, getAnchorBlock, getAnchorChild, getAnchorSpan, getAnchorTextBlock, getBlockOffsets, getBlockTextBefore, getCaretWordSelection, getFirstBlock, getFocusBlock, getFocusBlockObject, getFocusChild, getFocusInlineObject, getFocusListBlock, getFocusSpan, getFocusTextBlock, getLastBlock, getListIndex, getMarkState, getNextBlock, getNextInlineObject, getPreviousBlock, getPreviousInlineObject, getSelectedBlocks, getSelectedSlice, getSelectedSpans, getSelectedTextBlocks, getSelectedValue, getSelection, getSelectionEndBlock, getSelectionEndChild, getSelectionEndPoint, getSelectionStartBlock, getSelectionStartChild, getSelectionStartPoint, getSelectionText, getTrimmedSelection, getValue, isActiveAnnotation, isActiveDecorator, isActiveListItem, isActiveStyle, isAtTheEndOfBlock, isAtTheStartOfBlock, isOverlappingSelection, isPointAfterSelection, isPointBeforeSelection, isSelectingEntireBlocks, isSelectionCollapsed, isSelectionExpanded };
@@ -127,6 +127,20 @@ declare function getListIndex({
127
127
  }: {
128
128
  path: BlockPath;
129
129
  }): EditorSelector<number | undefined>;
130
+ type MarkState = {
131
+ state: 'unchanged';
132
+ marks: Array<string>;
133
+ } | {
134
+ state: 'changed';
135
+ marks: Array<string>;
136
+ previousMarks: Array<string>;
137
+ };
138
+ /**
139
+ * Given that text is inserted at the current position, what marks should
140
+ * be applied?
141
+ * @beta
142
+ */
143
+ declare const getMarkState: EditorSelector<MarkState | undefined>;
130
144
  /**
131
145
  * @public
132
146
  */
@@ -305,4 +319,4 @@ declare const isSelectionCollapsed: EditorSelector<boolean>;
305
319
  * @public
306
320
  */
307
321
  declare const isSelectionExpanded: EditorSelector<boolean>;
308
- export { getActiveAnnotations, getActiveListItem, getActiveStyle, getAnchorBlock, getAnchorChild, getAnchorSpan, getAnchorTextBlock, getBlockOffsets, getBlockTextBefore, getCaretWordSelection, getFirstBlock, getFocusBlock, getFocusBlockObject, getFocusChild, getFocusInlineObject, getFocusListBlock, getFocusSpan, getFocusTextBlock, getLastBlock, getListIndex, getNextBlock, getNextInlineObject, getPreviousBlock, getPreviousInlineObject, getSelectedBlocks, getSelectedSlice, getSelectedSpans, getSelectedTextBlocks, getSelectedValue, getSelection, getSelectionEndBlock, getSelectionEndChild, getSelectionEndPoint, getSelectionStartBlock, getSelectionStartChild, getSelectionStartPoint, getSelectionText, getTrimmedSelection, getValue, isActiveAnnotation, isActiveDecorator, isActiveListItem, isActiveStyle, isAtTheEndOfBlock, isAtTheStartOfBlock, isOverlappingSelection, isPointAfterSelection, isPointBeforeSelection, isSelectingEntireBlocks, isSelectionCollapsed, isSelectionExpanded };
322
+ export { getActiveAnnotations, getActiveListItem, getActiveStyle, getAnchorBlock, getAnchorChild, getAnchorSpan, getAnchorTextBlock, getBlockOffsets, getBlockTextBefore, getCaretWordSelection, getFirstBlock, getFocusBlock, getFocusBlockObject, getFocusChild, getFocusInlineObject, getFocusListBlock, getFocusSpan, getFocusTextBlock, getLastBlock, getListIndex, getMarkState, getNextBlock, getNextInlineObject, getPreviousBlock, getPreviousInlineObject, getSelectedBlocks, getSelectedSlice, getSelectedSpans, getSelectedTextBlocks, getSelectedValue, getSelection, getSelectionEndBlock, getSelectionEndChild, getSelectionEndPoint, getSelectionStartBlock, getSelectionStartChild, getSelectionStartPoint, getSelectionText, getTrimmedSelection, getValue, isActiveAnnotation, isActiveDecorator, isActiveListItem, isActiveStyle, isAtTheEndOfBlock, isAtTheStartOfBlock, isOverlappingSelection, isPointAfterSelection, isPointBeforeSelection, isSelectingEntireBlocks, isSelectionCollapsed, isSelectionExpanded };
@@ -1,5 +1,5 @@
1
1
  import { getSelectionEndPoint } from "../_chunks-es/selector.is-selecting-entire-blocks.js";
2
- import { getActiveAnnotations, getActiveListItem, getActiveStyle, getCaretWordSelection, getFirstBlock, getFocusBlockObject, getFocusInlineObject, getFocusListBlock, getLastBlock, getNextBlock, getNextInlineObject, getPreviousBlock, getSelectedBlocks, getSelectedSpans, getSelectedTextBlocks, getSelectionEndBlock, getSelectionStartBlock, getTrimmedSelection, isActiveAnnotation, isActiveDecorator, isActiveListItem, isActiveStyle, isAtTheEndOfBlock, isAtTheStartOfBlock, isOverlappingSelection, isPointAfterSelection, isPointBeforeSelection, isSelectingEntireBlocks } from "../_chunks-es/selector.is-selecting-entire-blocks.js";
2
+ import { getActiveAnnotations, getActiveListItem, getActiveStyle, getCaretWordSelection, getFirstBlock, getFocusBlockObject, getFocusInlineObject, getFocusListBlock, getLastBlock, getMarkState, getNextBlock, getNextInlineObject, getPreviousBlock, getSelectedBlocks, getSelectedSpans, getSelectedTextBlocks, getSelectionEndBlock, getSelectionStartBlock, getTrimmedSelection, isActiveAnnotation, isActiveDecorator, isActiveListItem, isActiveStyle, isAtTheEndOfBlock, isAtTheStartOfBlock, isOverlappingSelection, isPointAfterSelection, isPointBeforeSelection, isSelectingEntireBlocks } from "../_chunks-es/selector.is-selecting-entire-blocks.js";
3
3
  import { getBlockKeyFromSelectionPoint, getChildKeyFromSelectionPoint, spanSelectionPointToBlockOffset, getSelectionEndPoint as getSelectionEndPoint$1, getSelectionStartPoint as getSelectionStartPoint$1 } from "../_chunks-es/util.slice-blocks.js";
4
4
  import { isTextBlock } from "@portabletext/schema";
5
5
  import { isPortableTextSpan } from "@sanity/types";
@@ -142,6 +142,7 @@ export {
142
142
  getFocusTextBlock,
143
143
  getLastBlock,
144
144
  getListIndex,
145
+ getMarkState,
145
146
  getNextBlock,
146
147
  getNextInlineObject,
147
148
  getPreviousBlock,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@portabletext/editor",
3
- "version": "2.12.2",
3
+ "version": "2.13.0",
4
4
  "description": "Portable Text Editor made in React",
5
5
  "keywords": [
6
6
  "sanity",
@@ -75,7 +75,7 @@
75
75
  "dependencies": {
76
76
  "@portabletext/to-html": "^3.0.0",
77
77
  "@xstate/react": "^6.0.0",
78
- "debug": "^4.4.1",
78
+ "debug": "^4.4.3",
79
79
  "get-random-values-esm": "^1.0.2",
80
80
  "immer": "^10.1.3",
81
81
  "lodash": "^4.17.21",
@@ -85,43 +85,43 @@
85
85
  "slate-dom": "^0.118.1",
86
86
  "slate-react": "0.117.4",
87
87
  "xstate": "^5.22.0",
88
- "@portabletext/block-tools": "^3.5.6",
89
- "@portabletext/keyboard-shortcuts": "^1.1.1",
88
+ "@portabletext/block-tools": "^3.5.7",
90
89
  "@portabletext/patches": "^1.1.8",
91
- "@portabletext/schema": "^1.2.0"
90
+ "@portabletext/schema": "^1.2.0",
91
+ "@portabletext/keyboard-shortcuts": "^1.1.1"
92
92
  },
93
93
  "devDependencies": {
94
94
  "@sanity/diff-match-patch": "^3.2.0",
95
- "@sanity/pkg-utils": "^8.1.4",
95
+ "@sanity/pkg-utils": "^8.1.14",
96
96
  "@sanity/schema": "^4.9.0",
97
97
  "@sanity/types": "^4.9.0",
98
98
  "@types/debug": "^4.1.12",
99
99
  "@types/lodash": "^4.17.20",
100
100
  "@types/lodash.startcase": "^4.4.9",
101
- "@types/react": "^19.1.11",
102
- "@types/react-dom": "^19.1.7",
101
+ "@types/react": "^19.1.13",
102
+ "@types/react-dom": "^19.1.9",
103
103
  "@vitejs/plugin-react": "^4.7.0",
104
104
  "@vitest/browser": "^3.2.4",
105
105
  "@vitest/coverage-istanbul": "^3.2.4",
106
106
  "babel-plugin-react-compiler": "19.1.0-rc.3",
107
- "eslint": "^9.34.0",
107
+ "eslint": "^9.36.0",
108
108
  "eslint-formatter-gha": "^1.6.0",
109
109
  "eslint-plugin-react-hooks": "6.0.0-rc.2",
110
- "jsdom": "^26.0.0",
110
+ "jsdom": "^27.0.0",
111
111
  "react": "^19.1.1",
112
112
  "react-dom": "^19.1.1",
113
113
  "rxjs": "^7.8.2",
114
114
  "typescript": "5.9.2",
115
- "typescript-eslint": "^8.41.0",
116
- "vite": "^7.1.3",
115
+ "typescript-eslint": "^8.44.1",
116
+ "vite": "^7.1.7",
117
117
  "vitest": "^3.2.4",
118
118
  "vitest-browser-react": "^1.0.1",
119
- "@portabletext/sanity-bridge": "1.1.10",
120
- "@portabletext/test": "^0.0.0",
121
- "racejar": "1.3.0"
119
+ "@portabletext/sanity-bridge": "1.1.11",
120
+ "racejar": "1.3.0",
121
+ "@portabletext/test": "^0.0.0"
122
122
  },
123
123
  "peerDependencies": {
124
- "@portabletext/sanity-bridge": "^1.1.10",
124
+ "@portabletext/sanity-bridge": "^1.1.11",
125
125
  "@sanity/schema": "^4.9.0",
126
126
  "@sanity/types": "^4.9.0",
127
127
  "react": "^18.3 || ^19",
@@ -204,6 +204,7 @@ export const abstractDeleteBehaviors = [
204
204
  offset: 0,
205
205
  },
206
206
  },
207
+ unit: 'block',
207
208
  }),
208
209
  ],
209
210
  ],
@@ -25,6 +25,33 @@ const shiftLeft = createKeyboardShortcut({
25
25
  })
26
26
 
27
27
  export const abstractKeyboardBehaviors = [
28
+ /**
29
+ * When Backspace is pressed on an inline object, Slate will raise a
30
+ * `delete.backward` event with `unit: 'block'`. This is wrong and this
31
+ * Behavior adjusts that.
32
+ */
33
+ defineBehavior({
34
+ on: 'keyboard.keydown',
35
+ guard: ({snapshot, event}) =>
36
+ defaultKeyboardShortcuts.backspace.guard(event.originEvent) &&
37
+ isSelectionCollapsed(snapshot) &&
38
+ getFocusInlineObject(snapshot),
39
+ actions: [() => [raise({type: 'delete.backward', unit: 'character'})]],
40
+ }),
41
+ /**
42
+ * When Delete is pressed on an inline object, Slate will raise a
43
+ * `delete.forward` event with `unit: 'block'`. This is wrong and this
44
+ * Behavior adjusts that.
45
+ */
46
+ defineBehavior({
47
+ on: 'keyboard.keydown',
48
+ guard: ({snapshot, event}) =>
49
+ defaultKeyboardShortcuts.delete.guard(event.originEvent) &&
50
+ isSelectionCollapsed(snapshot) &&
51
+ getFocusInlineObject(snapshot),
52
+ actions: [() => [raise({type: 'delete.forward', unit: 'character'})]],
53
+ }),
54
+
28
55
  /**
29
56
  * Allow raising an `insert.break` event when pressing Enter on an inline
30
57
  * object.
@@ -942,28 +942,8 @@ export const PortableTextEditable = forwardRef<
942
942
  } else if (forwardedRef) {
943
943
  forwardedRef.current = node
944
944
  }
945
-
946
- if (node) {
947
- // Observe mutations (child list and subtree) to this component's DOM,
948
- // and make sure the editor selection is valid when that happens.
949
- const mutationObserver = new MutationObserver(() => {
950
- validateSelection(slateEditor, node)
951
- })
952
-
953
- mutationObserver.observe(node, {
954
- attributeOldValue: false,
955
- attributes: false,
956
- characterData: false,
957
- childList: true,
958
- subtree: true,
959
- })
960
-
961
- return () => {
962
- mutationObserver.disconnect()
963
- }
964
- }
965
945
  },
966
- [forwardedRef, slateEditor],
946
+ [forwardedRef],
967
947
  )
968
948
 
969
949
  if (!portableTextEditor) {
@@ -1007,72 +987,3 @@ export const PortableTextEditable = forwardRef<
1007
987
  })
1008
988
 
1009
989
  PortableTextEditable.displayName = 'ForwardRef(PortableTextEditable)'
1010
-
1011
- // This function will handle unexpected DOM changes inside the Editable rendering,
1012
- // and make sure that we can maintain a stable slateEditor.selection when that happens.
1013
- //
1014
- // For example, if this Editable is rendered inside something that might re-render
1015
- // this component (hidden contexts) while the user is still actively changing the
1016
- // contentEditable, this could interfere with the intermediate DOM selection,
1017
- // which again could be picked up by ReactEditor's event listeners.
1018
- // If that range is invalid at that point, the slate.editorSelection could be
1019
- // set either wrong, or invalid, to which slateEditor will throw exceptions
1020
- // that are impossible to recover properly from or result in a wrong selection.
1021
- //
1022
- // Also the other way around, when the ReactEditor will try to create a DOM Range
1023
- // from the current slateEditor.selection, it may throw unrecoverable errors
1024
- // if the current editor.selection is invalid according to the DOM.
1025
- // If this is the case, default to selecting the top of the document, if the
1026
- // user already had a selection.
1027
- function validateSelection(slateEditor: Editor, activeElement: HTMLDivElement) {
1028
- if (!slateEditor.selection) {
1029
- return
1030
- }
1031
-
1032
- let root: Document | ShadowRoot | undefined
1033
-
1034
- try {
1035
- root = ReactEditor.findDocumentOrShadowRoot(slateEditor)
1036
- } catch {}
1037
-
1038
- if (!root) {
1039
- // The editor has most likely been unmounted
1040
- return
1041
- }
1042
-
1043
- // Return if the editor isn't the active element
1044
- if (activeElement !== root.activeElement) {
1045
- return
1046
- }
1047
- const window = ReactEditor.getWindow(slateEditor)
1048
- const domSelection = window.getSelection()
1049
- if (!domSelection || domSelection.rangeCount === 0) {
1050
- return
1051
- }
1052
- const existingDOMRange = domSelection.getRangeAt(0)
1053
- try {
1054
- const newDOMRange = ReactEditor.toDOMRange(
1055
- slateEditor,
1056
- slateEditor.selection,
1057
- )
1058
- if (
1059
- newDOMRange.startOffset !== existingDOMRange.startOffset ||
1060
- newDOMRange.endOffset !== existingDOMRange.endOffset
1061
- ) {
1062
- debug('DOM range out of sync, validating selection')
1063
- // Remove all ranges temporary
1064
- domSelection?.removeAllRanges()
1065
- // Set the correct range
1066
- domSelection.addRange(newDOMRange)
1067
- }
1068
- } catch {
1069
- debug(`Could not resolve selection, selecting top document`)
1070
- // Deselect the editor
1071
- Transforms.deselect(slateEditor)
1072
- // Select top document if there is a top block to select
1073
- if (slateEditor.children.length > 0) {
1074
- Transforms.select(slateEditor, [0, 0])
1075
- }
1076
- slateEditor.onChange()
1077
- }
1078
- }
@@ -4,6 +4,6 @@ export function isTypedObject(object: unknown): object is TypedObject {
4
4
  return isRecord(object) && typeof object._type === 'string'
5
5
  }
6
6
 
7
- function isRecord(value: unknown): value is Record<string, unknown> {
7
+ export function isRecord(value: unknown): value is Record<string, unknown> {
8
8
  return !!value && (typeof value === 'object' || typeof value === 'function')
9
9
  }
@@ -31,6 +31,17 @@ export const defaultKeyboardShortcuts = {
31
31
  },
32
32
  ],
33
33
  }),
34
+ backspace: createKeyboardShortcut({
35
+ default: [
36
+ {
37
+ key: 'Backspace',
38
+ alt: false,
39
+ ctrl: false,
40
+ meta: false,
41
+ shift: false,
42
+ },
43
+ ],
44
+ }),
34
45
  break: createKeyboardShortcut({
35
46
  default: [
36
47
  {
@@ -53,6 +64,17 @@ export const defaultKeyboardShortcuts = {
53
64
  underline: underline,
54
65
  code: code,
55
66
  },
67
+ delete: createKeyboardShortcut({
68
+ default: [
69
+ {
70
+ key: 'Delete',
71
+ alt: false,
72
+ ctrl: false,
73
+ meta: false,
74
+ shift: false,
75
+ },
76
+ ],
77
+ }),
56
78
  history: {
57
79
  undo,
58
80
  redo,
@@ -9,7 +9,6 @@ import {
9
9
  } from 'slate'
10
10
  import {DOMEditor} from 'slate-dom'
11
11
  import {createPlaceholderBlock} from '../internal-utils/create-placeholder-block'
12
- import {getBlockPath} from '../internal-utils/slate-utils'
13
12
  import {toSlateRange} from '../internal-utils/to-slate-range'
14
13
  import {getBlockKeyFromSelectionPoint} from '../selection/selection-point'
15
14
  import type {PortableTextSlateEditor} from '../types/editor'
@@ -59,30 +58,13 @@ export const deleteOperationImplementation: BehaviorOperationImplementation<
59
58
  throw new Error('Failed to get end block')
60
59
  }
61
60
 
62
- const anchorBlockPath =
63
- anchorBlockKey !== undefined
64
- ? getBlockPath({
65
- editor: operation.editor,
66
- _key: anchorBlockKey,
67
- })
68
- : undefined
69
- const focusBlockPath =
70
- focusBlockKey !== undefined
71
- ? getBlockPath({
72
- editor: operation.editor,
73
- _key: focusBlockKey,
74
- })
75
- : undefined
76
-
77
- if (
78
- operation.at.anchor.path.length === 1 &&
79
- operation.at.focus.path.length === 1 &&
80
- anchorBlockPath &&
81
- focusBlockPath &&
82
- anchorBlockPath[0] === focusBlockPath[0]
83
- ) {
61
+ if (operation.unit === 'block') {
84
62
  Transforms.removeNodes(operation.editor, {
85
- at: [anchorBlockPath[0]],
63
+ at: {
64
+ anchor: {path: [startBlockIndex], offset: 0},
65
+ focus: {path: [endBlockIndex], offset: 0},
66
+ },
67
+ mode: 'highest',
86
68
  })
87
69
 
88
70
  if (operation.editor.children.length === 0) {
@@ -17,6 +17,7 @@ export {getFocusSpan} from './selector.get-focus-span'
17
17
  export {getFocusTextBlock} from './selector.get-focus-text-block'
18
18
  export {getLastBlock} from './selector.get-last-block'
19
19
  export {getListIndex} from './selector.get-list-state'
20
+ export {getMarkState} from './selector.get-mark-state'
20
21
  export {getNextBlock} from './selector.get-next-block'
21
22
  export {getNextInlineObject} from './selector.get-next-inline-object'
22
23
  export {getPreviousBlock} from './selector.get-previous-block'
@@ -1,4 +1,6 @@
1
1
  import type {EditorSelector} from '../editor/editor-selector'
2
+ import {isBlockPath} from '../types/paths'
3
+ import {blockOffsetToSpanSelectionPoint} from '../utils'
2
4
  import {isSelectionExpanded} from '../utils/util.is-selection-expanded'
3
5
  import {getFocusSpan} from './selector.get-focus-span'
4
6
  import {getFocusTextBlock} from './selector.get-focus-text-block'
@@ -20,6 +22,7 @@ export type MarkState =
20
22
  /**
21
23
  * Given that text is inserted at the current position, what marks should
22
24
  * be applied?
25
+ * @beta
23
26
  */
24
27
  export const getMarkState: EditorSelector<MarkState | undefined> = (
25
28
  snapshot,
@@ -28,15 +31,69 @@ export const getMarkState: EditorSelector<MarkState | undefined> = (
28
31
  return undefined
29
32
  }
30
33
 
34
+ let selection = snapshot.context.selection
31
35
  const focusTextBlock = getFocusTextBlock(snapshot)
32
- const focusSpan = getFocusSpan(snapshot)
33
36
 
34
- if (!focusTextBlock || !focusSpan) {
37
+ if (!focusTextBlock) {
35
38
  return undefined
36
39
  }
37
40
 
38
- if (isSelectionExpanded(snapshot.context.selection)) {
39
- const selectedSpans = getSelectedSpans(snapshot)
41
+ if (isBlockPath(selection.anchor.path)) {
42
+ const spanSelectionPoint = blockOffsetToSpanSelectionPoint({
43
+ context: snapshot.context,
44
+ blockOffset: {
45
+ path: selection.anchor.path,
46
+ offset: selection.anchor.offset,
47
+ },
48
+ direction: selection.backward ? 'backward' : 'forward',
49
+ })
50
+
51
+ selection = spanSelectionPoint
52
+ ? {
53
+ ...selection,
54
+ anchor: spanSelectionPoint,
55
+ }
56
+ : selection
57
+ }
58
+
59
+ if (isBlockPath(selection.focus.path)) {
60
+ const spanSelectionPoint = blockOffsetToSpanSelectionPoint({
61
+ context: snapshot.context,
62
+ blockOffset: {
63
+ path: selection.focus.path,
64
+ offset: selection.focus.offset,
65
+ },
66
+ direction: selection.backward ? 'backward' : 'forward',
67
+ })
68
+
69
+ selection = spanSelectionPoint
70
+ ? {
71
+ ...selection,
72
+ focus: spanSelectionPoint,
73
+ }
74
+ : selection
75
+ }
76
+
77
+ const focusSpan = getFocusSpan({
78
+ ...snapshot,
79
+ context: {
80
+ ...snapshot.context,
81
+ selection,
82
+ },
83
+ })
84
+
85
+ if (!focusSpan) {
86
+ return undefined
87
+ }
88
+
89
+ if (isSelectionExpanded(selection)) {
90
+ const selectedSpans = getSelectedSpans({
91
+ ...snapshot,
92
+ context: {
93
+ ...snapshot.context,
94
+ selection,
95
+ },
96
+ })
40
97
 
41
98
  let index = 0
42
99
  let marks: Array<string> = []
@@ -80,8 +137,20 @@ export const getMarkState: EditorSelector<MarkState | undefined> = (
80
137
  const atTheEndOfSpan =
81
138
  snapshot.context.selection.anchor.offset === focusSpan.node.text.length
82
139
 
83
- const previousSpan = getPreviousSpan(snapshot)
84
- const nextSpan = getNextSpan(snapshot)
140
+ const previousSpan = getPreviousSpan({
141
+ ...snapshot,
142
+ context: {
143
+ ...snapshot.context,
144
+ selection,
145
+ },
146
+ })
147
+ const nextSpan = getNextSpan({
148
+ ...snapshot,
149
+ context: {
150
+ ...snapshot.context,
151
+ selection,
152
+ },
153
+ })
85
154
  const nextSpanAnnotations =
86
155
  nextSpan?.node?.marks?.filter((mark) => !decorators.includes(mark)) ?? []
87
156
  const spanAnnotations = marks.filter((mark) => !decorators.includes(mark))