@portabletext/editor 1.50.8 → 1.51.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 (93) hide show
  1. package/lib/_chunks-cjs/{util.slice-blocks.cjs → selection-point.cjs} +26 -18
  2. package/lib/_chunks-cjs/selection-point.cjs.map +1 -0
  3. package/lib/_chunks-cjs/selector.get-text-before.cjs +13 -10
  4. package/lib/_chunks-cjs/selector.get-text-before.cjs.map +1 -1
  5. package/lib/_chunks-cjs/selector.is-selecting-entire-blocks.cjs +46 -46
  6. package/lib/_chunks-cjs/selector.is-selecting-entire-blocks.cjs.map +1 -1
  7. package/lib/_chunks-cjs/selector.is-selection-expanded.cjs +21 -17
  8. package/lib/_chunks-cjs/selector.is-selection-expanded.cjs.map +1 -1
  9. package/lib/_chunks-cjs/util.child-selection-point-to-block-offset.cjs +10 -10
  10. package/lib/_chunks-cjs/util.child-selection-point-to-block-offset.cjs.map +1 -1
  11. package/lib/_chunks-cjs/util.is-equal-selection-points.cjs +5 -5
  12. package/lib/_chunks-cjs/util.is-equal-selection-points.cjs.map +1 -1
  13. package/lib/_chunks-cjs/util.merge-text-blocks.cjs +3 -3
  14. package/lib/_chunks-cjs/util.merge-text-blocks.cjs.map +1 -1
  15. package/lib/_chunks-cjs/util.selection-point-to-block-offset.cjs +7 -14
  16. package/lib/_chunks-cjs/util.selection-point-to-block-offset.cjs.map +1 -1
  17. package/lib/_chunks-es/{util.slice-blocks.js → selection-point.js} +26 -18
  18. package/lib/_chunks-es/selection-point.js.map +1 -0
  19. package/lib/_chunks-es/selector.get-text-before.js +13 -10
  20. package/lib/_chunks-es/selector.get-text-before.js.map +1 -1
  21. package/lib/_chunks-es/selector.is-selecting-entire-blocks.js +21 -21
  22. package/lib/_chunks-es/selector.is-selecting-entire-blocks.js.map +1 -1
  23. package/lib/_chunks-es/selector.is-selection-expanded.js +14 -10
  24. package/lib/_chunks-es/selector.is-selection-expanded.js.map +1 -1
  25. package/lib/_chunks-es/util.child-selection-point-to-block-offset.js +2 -2
  26. package/lib/_chunks-es/util.child-selection-point-to-block-offset.js.map +1 -1
  27. package/lib/_chunks-es/util.is-equal-selection-points.js +1 -1
  28. package/lib/_chunks-es/util.merge-text-blocks.js +1 -1
  29. package/lib/_chunks-es/util.selection-point-to-block-offset.js +4 -11
  30. package/lib/_chunks-es/util.selection-point-to-block-offset.js.map +1 -1
  31. package/lib/behaviors/index.d.cts +1 -2007
  32. package/lib/behaviors/index.d.ts +1 -2007
  33. package/lib/index.cjs +90 -93
  34. package/lib/index.cjs.map +1 -1
  35. package/lib/index.d.cts +1 -3
  36. package/lib/index.d.ts +1 -3
  37. package/lib/index.js +22 -25
  38. package/lib/index.js.map +1 -1
  39. package/lib/plugins/index.cjs +11 -11
  40. package/lib/plugins/index.cjs.map +1 -1
  41. package/lib/plugins/index.d.cts +6 -1981
  42. package/lib/plugins/index.d.ts +6 -1981
  43. package/lib/plugins/index.js +1 -1
  44. package/lib/selectors/index.cjs +11 -7
  45. package/lib/selectors/index.cjs.map +1 -1
  46. package/lib/selectors/index.d.cts +2 -2634
  47. package/lib/selectors/index.d.ts +2 -2634
  48. package/lib/selectors/index.js +7 -3
  49. package/lib/selectors/index.js.map +1 -1
  50. package/lib/utils/index.cjs +25 -14
  51. package/lib/utils/index.cjs.map +1 -1
  52. package/lib/utils/index.d.cts +0 -2633
  53. package/lib/utils/index.d.ts +0 -2633
  54. package/lib/utils/index.js +14 -3
  55. package/lib/utils/index.js.map +1 -1
  56. package/package.json +13 -13
  57. package/src/behaviors/behavior.abstract.delete.ts +0 -1
  58. package/src/behaviors/behavior.abstract.insert.ts +8 -8
  59. package/src/behaviors/behavior.types.event.ts +1 -0
  60. package/src/converters/converter.portable-text.ts +1 -1
  61. package/src/converters/converter.text-plain.ts +2 -2
  62. package/src/editor/editor-machine.ts +0 -2
  63. package/src/editor/editor-selector.ts +0 -1
  64. package/src/editor/editor-snapshot.ts +0 -5
  65. package/src/internal-utils/create-test-snapshot.ts +0 -1
  66. package/src/internal-utils/event-position.ts +3 -5
  67. package/src/internal-utils/selection-block-keys.ts +7 -7
  68. package/src/internal-utils/selection-focus-text.ts +13 -9
  69. package/src/internal-utils/selection-text.ts +9 -78
  70. package/src/internal-utils/terse-pt.test.ts +108 -26
  71. package/src/internal-utils/terse-pt.ts +132 -14
  72. package/src/operations/behavior.operation.decorator.add.ts +0 -1
  73. package/src/operations/behavior.operation.delete.ts +18 -13
  74. package/src/operations/behavior.operation.insert.block.ts +5 -1
  75. package/src/selection/selection-point.ts +22 -0
  76. package/src/selectors/selector.get-anchor-block.ts +6 -6
  77. package/src/selectors/selector.get-anchor-child.ts +6 -6
  78. package/src/selectors/selector.get-selected-spans.ts +16 -19
  79. package/src/selectors/selector.get-selected-text-blocks.ts +11 -19
  80. package/src/selectors/selector.get-selection-end-block.ts +30 -0
  81. package/src/selectors/selector.get-selection-start-block.ts +30 -0
  82. package/src/selectors/selector.get-text-before.ts +15 -16
  83. package/src/selectors/selector.get-trimmed-selection.ts +15 -21
  84. package/src/selectors/selector.is-point-after-selection.ts +11 -19
  85. package/src/selectors/selector.is-point-before-selection.ts +11 -19
  86. package/src/selectors/selectors.ts +23 -39
  87. package/src/utils/util.block-offset.ts +6 -7
  88. package/src/utils/util.child-selection-point-to-block-offset.ts +6 -7
  89. package/src/utils/util.selection-point-to-block-offset.ts +5 -6
  90. package/src/utils/util.slice-blocks.ts +11 -20
  91. package/lib/_chunks-cjs/util.slice-blocks.cjs.map +0 -1
  92. package/lib/_chunks-es/util.slice-blocks.js.map +0 -1
  93. package/src/internal-utils/inline-object-selection.ts +0 -115
@@ -1,14 +1,25 @@
1
- import { sliceBlocks, isSpan } from "../_chunks-es/util.slice-blocks.js";
2
- import { blockOffsetToSpanSelectionPoint, getBlockStartPoint, getTextBlockText, isKeyedSegment, reverseSelection, spanSelectionPointToBlockOffset } from "../_chunks-es/util.slice-blocks.js";
1
+ import { sliceBlocks, isSpan } from "../_chunks-es/selection-point.js";
2
+ import { blockOffsetToSpanSelectionPoint, getBlockStartPoint, getSelectionEndPoint, getSelectionStartPoint, getTextBlockText, isKeyedSegment, spanSelectionPointToBlockOffset } from "../_chunks-es/selection-point.js";
3
3
  import { blockOffsetToBlockSelectionPoint, blockOffsetToSelectionPoint, blockOffsetsToSelection, childSelectionPointToBlockOffset } from "../_chunks-es/util.child-selection-point-to-block-offset.js";
4
4
  import { isEqualSelectionPoints } from "../_chunks-es/util.is-equal-selection-points.js";
5
5
  import { getBlockEndPoint, isEmptyTextBlock } from "../_chunks-es/util.is-equal-selection-points.js";
6
- import { getSelectionEndPoint, getSelectionStartPoint, isSelectionCollapsed, selectionPointToBlockOffset } from "../_chunks-es/util.selection-point-to-block-offset.js";
6
+ import { isSelectionCollapsed, selectionPointToBlockOffset } from "../_chunks-es/util.selection-point-to-block-offset.js";
7
7
  import { isTextBlock } from "../_chunks-es/util.merge-text-blocks.js";
8
8
  import { mergeTextBlocks } from "../_chunks-es/util.merge-text-blocks.js";
9
9
  function isEqualSelections(a, b) {
10
10
  return !a && !b ? !0 : !a || !b ? !1 : isEqualSelectionPoints(a.anchor, b.anchor) && isEqualSelectionPoints(a.focus, b.focus);
11
11
  }
12
+ function reverseSelection(selection) {
13
+ return selection && (selection.backward ? {
14
+ anchor: selection.focus,
15
+ focus: selection.anchor,
16
+ backward: !1
17
+ } : {
18
+ anchor: selection.focus,
19
+ focus: selection.anchor,
20
+ backward: !0
21
+ });
22
+ }
12
23
  function splitTextBlock({
13
24
  context,
14
25
  block,
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../src/utils/util.is-equal-selections.ts","../../src/utils/util.split-text-block.ts"],"sourcesContent":["import type {EditorSelection} from '../types/editor'\nimport {isEqualSelectionPoints} from './util.is-equal-selection-points'\n\n/**\n * @public\n */\nexport function isEqualSelections(a: EditorSelection, b: EditorSelection) {\n if (!a && !b) {\n return true\n }\n\n if (!a || !b) {\n return false\n }\n\n return (\n isEqualSelectionPoints(a.anchor, b.anchor) &&\n isEqualSelectionPoints(a.focus, b.focus)\n )\n}\n","import type {PortableTextTextBlock} from '@sanity/types'\nimport type {EditorSelectionPoint} from '..'\nimport type {EditorContext} from '../editor/editor-snapshot'\nimport {isSpan} from './util.is-span'\nimport {isTextBlock} from './util.is-text-block'\nimport {sliceBlocks} from './util.slice-blocks'\n\n/**\n * @beta\n */\nexport function splitTextBlock({\n context,\n block,\n point,\n}: {\n context: Pick<EditorContext, 'schema'>\n block: PortableTextTextBlock\n point: EditorSelectionPoint\n}): {before: PortableTextTextBlock; after: PortableTextTextBlock} | undefined {\n const firstChild = block.children.at(0)\n const lastChild = block.children.at(block.children.length - 1)\n\n if (!firstChild || !lastChild) {\n return undefined\n }\n\n const before = sliceBlocks({\n context: {\n schema: context.schema,\n selection: {\n anchor: {\n path: [{_key: block._key}, 'children', {_key: firstChild._key}],\n offset: 0,\n },\n focus: point,\n },\n },\n blocks: [block],\n }).at(0)\n const after = sliceBlocks({\n context: {\n schema: context.schema,\n selection: {\n anchor: point,\n focus: {\n path: [{_key: block._key}, 'children', {_key: lastChild._key}],\n offset: isSpan(context, lastChild) ? lastChild.text.length : 0,\n },\n },\n },\n blocks: [block],\n }).at(0)\n\n if (!before || !after) {\n return undefined\n }\n\n if (!isTextBlock(context, before) || !isTextBlock(context, after)) {\n return undefined\n }\n\n return {before, after}\n}\n"],"names":["isEqualSelections","a","b","isEqualSelectionPoints","anchor","focus","splitTextBlock","context","block","point","firstChild","children","at","lastChild","length","before","sliceBlocks","schema","selection","path","_key","offset","blocks","after","isSpan","text","isTextBlock"],"mappings":";;;;;;;;AAMgBA,SAAAA,kBAAkBC,GAAoBC,GAAoB;AACpE,SAAA,CAACD,KAAK,CAACC,IACF,KAGL,CAACD,KAAK,CAACC,IACF,KAIPC,uBAAuBF,EAAEG,QAAQF,EAAEE,MAAM,KACzCD,uBAAuBF,EAAEI,OAAOH,EAAEG,KAAK;AAE3C;ACTO,SAASC,eAAe;AAAA,EAC7BC;AAAAA,EACAC;AAAAA,EACAC;AAKF,GAA8E;AAC5E,QAAMC,aAAaF,MAAMG,SAASC,GAAG,CAAC,GAChCC,YAAYL,MAAMG,SAASC,GAAGJ,MAAMG,SAASG,SAAS,CAAC;AAEzD,MAAA,CAACJ,cAAc,CAACG;AAClB;AAGF,QAAME,SAASC,YAAY;AAAA,IACzBT,SAAS;AAAA,MACPU,QAAQV,QAAQU;AAAAA,MAChBC,WAAW;AAAA,QACTd,QAAQ;AAAA,UACNe,MAAM,CAAC;AAAA,YAACC,MAAMZ,MAAMY;AAAAA,aAAO,YAAY;AAAA,YAACA,MAAMV,WAAWU;AAAAA,UAAAA,CAAK;AAAA,UAC9DC,QAAQ;AAAA,QACV;AAAA,QACAhB,OAAOI;AAAAA,MAAAA;AAAAA,IAEX;AAAA,IACAa,QAAQ,CAACd,KAAK;AAAA,EACf,CAAA,EAAEI,GAAG,CAAC,GACDW,QAAQP,YAAY;AAAA,IACxBT,SAAS;AAAA,MACPU,QAAQV,QAAQU;AAAAA,MAChBC,WAAW;AAAA,QACTd,QAAQK;AAAAA,QACRJ,OAAO;AAAA,UACLc,MAAM,CAAC;AAAA,YAACC,MAAMZ,MAAMY;AAAAA,aAAO,YAAY;AAAA,YAACA,MAAMP,UAAUO;AAAAA,UAAAA,CAAK;AAAA,UAC7DC,QAAQG,OAAOjB,SAASM,SAAS,IAAIA,UAAUY,KAAKX,SAAS;AAAA,QAAA;AAAA,MAC/D;AAAA,IAEJ;AAAA,IACAQ,QAAQ,CAACd,KAAK;AAAA,EAAA,CACf,EAAEI,GAAG,CAAC;AAEP,MAAI,EAACG,CAAAA,UAAU,CAACQ,UAIZ,EAACG,CAAAA,YAAYnB,SAASQ,MAAM,KAAK,CAACW,YAAYnB,SAASgB,KAAK;AAIzD,WAAA;AAAA,MAACR;AAAAA,MAAQQ;AAAAA,IAAK;AACvB;"}
1
+ {"version":3,"file":"index.js","sources":["../../src/utils/util.is-equal-selections.ts","../../src/utils/util.reverse-selection.ts","../../src/utils/util.split-text-block.ts"],"sourcesContent":["import type {EditorSelection} from '../types/editor'\nimport {isEqualSelectionPoints} from './util.is-equal-selection-points'\n\n/**\n * @public\n */\nexport function isEqualSelections(a: EditorSelection, b: EditorSelection) {\n if (!a && !b) {\n return true\n }\n\n if (!a || !b) {\n return false\n }\n\n return (\n isEqualSelectionPoints(a.anchor, b.anchor) &&\n isEqualSelectionPoints(a.focus, b.focus)\n )\n}\n","import type {EditorSelection} from '../types/editor'\n\n/**\n * @public\n */\nexport function reverseSelection<\n TEditorSelection extends NonNullable<EditorSelection> | null,\n>(selection: TEditorSelection): TEditorSelection {\n if (!selection) {\n return selection\n }\n\n if (selection.backward) {\n return {\n anchor: selection.focus,\n focus: selection.anchor,\n backward: false,\n } as TEditorSelection\n }\n\n return {\n anchor: selection.focus,\n focus: selection.anchor,\n backward: true,\n } as TEditorSelection\n}\n","import type {PortableTextTextBlock} from '@sanity/types'\nimport type {EditorSelectionPoint} from '..'\nimport type {EditorContext} from '../editor/editor-snapshot'\nimport {isSpan} from './util.is-span'\nimport {isTextBlock} from './util.is-text-block'\nimport {sliceBlocks} from './util.slice-blocks'\n\n/**\n * @beta\n */\nexport function splitTextBlock({\n context,\n block,\n point,\n}: {\n context: Pick<EditorContext, 'schema'>\n block: PortableTextTextBlock\n point: EditorSelectionPoint\n}): {before: PortableTextTextBlock; after: PortableTextTextBlock} | undefined {\n const firstChild = block.children.at(0)\n const lastChild = block.children.at(block.children.length - 1)\n\n if (!firstChild || !lastChild) {\n return undefined\n }\n\n const before = sliceBlocks({\n context: {\n schema: context.schema,\n selection: {\n anchor: {\n path: [{_key: block._key}, 'children', {_key: firstChild._key}],\n offset: 0,\n },\n focus: point,\n },\n },\n blocks: [block],\n }).at(0)\n const after = sliceBlocks({\n context: {\n schema: context.schema,\n selection: {\n anchor: point,\n focus: {\n path: [{_key: block._key}, 'children', {_key: lastChild._key}],\n offset: isSpan(context, lastChild) ? lastChild.text.length : 0,\n },\n },\n },\n blocks: [block],\n }).at(0)\n\n if (!before || !after) {\n return undefined\n }\n\n if (!isTextBlock(context, before) || !isTextBlock(context, after)) {\n return undefined\n }\n\n return {before, after}\n}\n"],"names":["isEqualSelections","a","b","isEqualSelectionPoints","anchor","focus","reverseSelection","selection","backward","splitTextBlock","context","block","point","firstChild","children","at","lastChild","length","before","sliceBlocks","schema","path","_key","offset","blocks","after","isSpan","text","isTextBlock"],"mappings":";;;;;;;;AAMgBA,SAAAA,kBAAkBC,GAAoBC,GAAoB;AACpE,SAAA,CAACD,KAAK,CAACC,IACF,KAGL,CAACD,KAAK,CAACC,IACF,KAIPC,uBAAuBF,EAAEG,QAAQF,EAAEE,MAAM,KACzCD,uBAAuBF,EAAEI,OAAOH,EAAEG,KAAK;AAE3C;ACdO,SAASC,iBAEdC,WAA+C;AAC1CA,SAAAA,cAIDA,UAAUC,WACL;AAAA,IACLJ,QAAQG,UAAUF;AAAAA,IAClBA,OAAOE,UAAUH;AAAAA,IACjBI,UAAU;AAAA,EAAA,IAIP;AAAA,IACLJ,QAAQG,UAAUF;AAAAA,IAClBA,OAAOE,UAAUH;AAAAA,IACjBI,UAAU;AAAA,EAAA;AAEd;ACfO,SAASC,eAAe;AAAA,EAC7BC;AAAAA,EACAC;AAAAA,EACAC;AAKF,GAA8E;AAC5E,QAAMC,aAAaF,MAAMG,SAASC,GAAG,CAAC,GAChCC,YAAYL,MAAMG,SAASC,GAAGJ,MAAMG,SAASG,SAAS,CAAC;AAEzD,MAAA,CAACJ,cAAc,CAACG;AAClB;AAGF,QAAME,SAASC,YAAY;AAAA,IACzBT,SAAS;AAAA,MACPU,QAAQV,QAAQU;AAAAA,MAChBb,WAAW;AAAA,QACTH,QAAQ;AAAA,UACNiB,MAAM,CAAC;AAAA,YAACC,MAAMX,MAAMW;AAAAA,aAAO,YAAY;AAAA,YAACA,MAAMT,WAAWS;AAAAA,UAAAA,CAAK;AAAA,UAC9DC,QAAQ;AAAA,QACV;AAAA,QACAlB,OAAOO;AAAAA,MAAAA;AAAAA,IAEX;AAAA,IACAY,QAAQ,CAACb,KAAK;AAAA,EACf,CAAA,EAAEI,GAAG,CAAC,GACDU,QAAQN,YAAY;AAAA,IACxBT,SAAS;AAAA,MACPU,QAAQV,QAAQU;AAAAA,MAChBb,WAAW;AAAA,QACTH,QAAQQ;AAAAA,QACRP,OAAO;AAAA,UACLgB,MAAM,CAAC;AAAA,YAACC,MAAMX,MAAMW;AAAAA,aAAO,YAAY;AAAA,YAACA,MAAMN,UAAUM;AAAAA,UAAAA,CAAK;AAAA,UAC7DC,QAAQG,OAAOhB,SAASM,SAAS,IAAIA,UAAUW,KAAKV,SAAS;AAAA,QAAA;AAAA,MAC/D;AAAA,IAEJ;AAAA,IACAO,QAAQ,CAACb,KAAK;AAAA,EAAA,CACf,EAAEI,GAAG,CAAC;AAEP,MAAI,EAACG,CAAAA,UAAU,CAACO,UAIZ,EAACG,CAAAA,YAAYlB,SAASQ,MAAM,KAAK,CAACU,YAAYlB,SAASe,KAAK;AAIzD,WAAA;AAAA,MAACP;AAAAA,MAAQO;AAAAA,IAAK;AACvB;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@portabletext/editor",
3
- "version": "1.50.8",
3
+ "version": "1.51.0",
4
4
  "description": "Portable Text Editor made in React",
5
5
  "keywords": [
6
6
  "sanity",
@@ -68,27 +68,27 @@
68
68
  ],
69
69
  "dependencies": {
70
70
  "@portabletext/to-html": "^2.0.14",
71
- "@xstate/react": "^5.0.4",
71
+ "@xstate/react": "^5.0.5",
72
72
  "debug": "^4.4.1",
73
73
  "get-random-values-esm": "^1.0.2",
74
74
  "immer": "^10.1.1",
75
75
  "lodash": "^4.17.21",
76
76
  "lodash.startcase": "^4.4.0",
77
77
  "react-compiler-runtime": "19.1.0-rc.1",
78
- "slate": "0.114.0",
78
+ "slate": "0.115.1",
79
79
  "slate-dom": "^0.114.0",
80
80
  "slate-react": "0.114.2",
81
- "use-effect-event": "^1.0.2",
82
- "xstate": "^5.19.3",
81
+ "use-effect-event": "^2.0.0",
82
+ "xstate": "^5.19.4",
83
83
  "@portabletext/patches": "1.1.4",
84
- "@portabletext/block-tools": "1.1.28"
84
+ "@portabletext/block-tools": "1.1.29"
85
85
  },
86
86
  "devDependencies": {
87
87
  "@portabletext/toolkit": "^2.0.17",
88
88
  "@sanity/diff-match-patch": "^3.2.0",
89
89
  "@sanity/pkg-utils": "^7.2.3",
90
- "@sanity/schema": "^3.90.0",
91
- "@sanity/types": "^3.90.0",
90
+ "@sanity/schema": "^3.91.0",
91
+ "@sanity/types": "^3.91.0",
92
92
  "@testing-library/jest-dom": "^6.6.3",
93
93
  "@testing-library/react": "^16.3.0",
94
94
  "@types/debug": "^4.1.12",
@@ -99,8 +99,8 @@
99
99
  "@typescript-eslint/eslint-plugin": "^8.33.0",
100
100
  "@typescript-eslint/parser": "^8.33.0",
101
101
  "@vitejs/plugin-react": "^4.4.1",
102
- "@vitest/browser": "^3.1.4",
103
- "@vitest/coverage-istanbul": "^3.1.4",
102
+ "@vitest/browser": "^3.2.1",
103
+ "@vitest/coverage-istanbul": "^3.2.1",
104
104
  "babel-plugin-react-compiler": "19.1.0-rc.1",
105
105
  "eslint": "8.57.1",
106
106
  "eslint-plugin-react-hooks": "0.0.0-experimental-f9ae0a4c-20250527",
@@ -110,13 +110,13 @@
110
110
  "rxjs": "^7.8.2",
111
111
  "typescript": "5.8.3",
112
112
  "vite": "^6.2.5",
113
- "vitest": "^3.1.4",
113
+ "vitest": "^3.2.1",
114
114
  "vitest-browser-react": "^0.2.0",
115
115
  "racejar": "1.2.5"
116
116
  },
117
117
  "peerDependencies": {
118
- "@sanity/schema": "^3.90.0",
119
- "@sanity/types": "^3.90.0",
118
+ "@sanity/schema": "^3.91.0",
119
+ "@sanity/types": "^3.91.0",
120
120
  "react": "^16.9 || ^17 || ^18 || ^19",
121
121
  "rxjs": "^7.8.2"
122
122
  },
@@ -80,7 +80,6 @@ export const abstractDeleteBehaviors = [
80
80
  beta: {
81
81
  activeAnnotations: [],
82
82
  activeDecorators: [],
83
- hasTag: () => false,
84
83
  internalDrag: undefined,
85
84
  },
86
85
  context: {
@@ -14,7 +14,7 @@ export const abstractInsertBehaviors = [
14
14
  type: 'insert.block',
15
15
  block,
16
16
  placement: index === 0 ? 'before' : 'after',
17
- select: 'end',
17
+ select: event.select ?? 'end',
18
18
  }),
19
19
  ),
20
20
  ],
@@ -29,7 +29,7 @@ export const abstractInsertBehaviors = [
29
29
  type: 'insert.block',
30
30
  block,
31
31
  placement: 'after',
32
- select: 'end',
32
+ select: event.select ?? 'end',
33
33
  }),
34
34
  ),
35
35
  ],
@@ -57,7 +57,7 @@ export const abstractInsertBehaviors = [
57
57
  type: 'insert.block',
58
58
  block: event.blocks[0],
59
59
  placement: 'auto',
60
- select: 'end',
60
+ select: event.select ?? 'end',
61
61
  }),
62
62
  ]
63
63
  : isEmptyTextBlock(snapshot.context, focusTextBlock.node)
@@ -66,7 +66,7 @@ export const abstractInsertBehaviors = [
66
66
  type: 'insert.block',
67
67
  block,
68
68
  placement: index === 0 ? 'auto' : 'after',
69
- select: 'end',
69
+ select: event.select ?? 'end',
70
70
  }),
71
71
  )
72
72
  : event.blocks.flatMap((block, index) =>
@@ -83,7 +83,7 @@ export const abstractInsertBehaviors = [
83
83
  type: 'insert.block',
84
84
  block,
85
85
  placement: 'auto',
86
- select: 'end',
86
+ select: event.select ?? 'end',
87
87
  }),
88
88
  ]
89
89
  : index === event.blocks.length - 1
@@ -96,7 +96,7 @@ export const abstractInsertBehaviors = [
96
96
  type: 'insert.block',
97
97
  block,
98
98
  placement: 'auto',
99
- select: 'end',
99
+ select: event.select ?? 'end',
100
100
  }),
101
101
  ]
102
102
  : [
@@ -104,7 +104,7 @@ export const abstractInsertBehaviors = [
104
104
  type: 'insert.block',
105
105
  block,
106
106
  placement: 'after',
107
- select: 'end',
107
+ select: event.select ?? 'end',
108
108
  }),
109
109
  ],
110
110
  ),
@@ -120,7 +120,7 @@ export const abstractInsertBehaviors = [
120
120
  type: 'insert.block',
121
121
  block,
122
122
  placement: index === 0 ? 'auto' : 'after',
123
- select: 'end',
123
+ select: event.select ?? 'end',
124
124
  }),
125
125
  ),
126
126
  ],
@@ -337,6 +337,7 @@ type AbstractBehaviorEvent =
337
337
  type: StrictExtract<SyntheticBehaviorEventType, 'insert.blocks'>
338
338
  blocks: Array<BlockWithOptionalKey>
339
339
  placement: InsertPlacement
340
+ select?: 'start' | 'end' | 'none'
340
341
  }
341
342
  | {
342
343
  type: StrictExtract<SyntheticBehaviorEventType, 'insert.break'>
@@ -61,7 +61,7 @@ export const converterPortableText = defineConverter({
61
61
  * If we are dragging internally then we would like to keep the
62
62
  * dropped portable text as is.
63
63
  */
64
- refreshKeys: !snapshot.beta.hasTag?.('dragging internally'),
64
+ refreshKeys: !snapshot.beta.internalDrag,
65
65
  validateFields: false,
66
66
  },
67
67
  })
@@ -41,7 +41,7 @@ export function createConverterTextPlain(
41
41
  return child.text
42
42
  }
43
43
 
44
- return snapshot.beta.hasTag('dragging internally')
44
+ return snapshot.beta.internalDrag
45
45
  ? `[${
46
46
  snapshot.context.schema.inlineObjects.find(
47
47
  (inlineObjectType) =>
@@ -53,7 +53,7 @@ export function createConverterTextPlain(
53
53
  .join('')
54
54
  }
55
55
 
56
- return snapshot.beta.hasTag('dragging internally')
56
+ return snapshot.beta.internalDrag
57
57
  ? `[${
58
58
  snapshot.context.schema.blockObjects.find(
59
59
  (blockObjectType) => blockObjectType.name === block._type,
@@ -68,7 +68,6 @@ type InternalPatchEvent = NamespaceEvent<PatchEvent, 'internal'> & {
68
68
  * @internal
69
69
  */
70
70
  export type EditorActor = ActorRefFrom<typeof editorMachine>
71
- export type HasTag = ReturnType<EditorActor['getSnapshot']>['hasTag']
72
71
 
73
72
  /**
74
73
  * @internal
@@ -274,7 +273,6 @@ export const editorMachine = setup({
274
273
  keyGenerator: context.keyGenerator,
275
274
  readOnly: self.getSnapshot().matches({'edit mode': 'read only'}),
276
275
  schema: context.schema,
277
- hasTag: (tag) => self.getSnapshot().hasTag(tag),
278
276
  internalDrag: context.internalDrag,
279
277
  }),
280
278
  nativeEvent: event.nativeEvent,
@@ -86,7 +86,6 @@ export function getEditorSnapshot({
86
86
  markState: slateEditorInstance.markState,
87
87
  schema: editorActorSnapshot.context.schema,
88
88
  }),
89
- hasTag: (tag) => editorActorSnapshot.hasTag(tag),
90
89
  internalDrag: editorActorSnapshot.context.internalDrag,
91
90
  },
92
91
  }
@@ -3,7 +3,6 @@ import type {Converter} from '../converters/converter.types'
3
3
  import type {EventPosition} from '../internal-utils/event-position'
4
4
  import {slateRangeToSelection} from '../internal-utils/slate-utils'
5
5
  import type {EditorSelection, PortableTextSlateEditor} from '../types/editor'
6
- import type {HasTag} from './editor-machine'
7
6
  import type {EditorSchema} from './editor-schema'
8
7
  import {getActiveAnnotations} from './get-active-annotations'
9
8
  import {getActiveDecorators} from './get-active-decorators'
@@ -32,7 +31,6 @@ export type EditorSnapshot = {
32
31
  beta: {
33
32
  activeAnnotations: Array<string>
34
33
  activeDecorators: Array<string>
35
- hasTag: HasTag
36
34
  internalDrag:
37
35
  | {
38
36
  origin: Pick<EventPosition, 'selection'>
@@ -47,7 +45,6 @@ export function createEditorSnapshot({
47
45
  keyGenerator,
48
46
  readOnly,
49
47
  schema,
50
- hasTag,
51
48
  internalDrag,
52
49
  }: {
53
50
  converters: Array<Converter>
@@ -55,7 +52,6 @@ export function createEditorSnapshot({
55
52
  keyGenerator: () => string
56
53
  readOnly: boolean
57
54
  schema: EditorSchema
58
- hasTag: HasTag
59
55
  internalDrag:
60
56
  | {
61
57
  origin: Pick<EventPosition, 'selection'>
@@ -91,7 +87,6 @@ export function createEditorSnapshot({
91
87
  markState: editor.markState,
92
88
  schema,
93
89
  }),
94
- hasTag,
95
90
  internalDrag,
96
91
  },
97
92
  } satisfies EditorSnapshot
@@ -19,7 +19,6 @@ export function createTestSnapshot(snapshot: {
19
19
  beta: {
20
20
  activeAnnotations: snapshot.beta?.activeAnnotations ?? [],
21
21
  activeDecorators: snapshot.beta?.activeDecorators ?? [],
22
- hasTag: snapshot.beta?.hasTag ?? (() => false),
23
22
  internalDrag: undefined,
24
23
  },
25
24
  }
@@ -2,6 +2,7 @@ import {Editor, type BaseRange, type Node} from 'slate'
2
2
  import {DOMEditor, isDOMNode} from 'slate-dom'
3
3
  import type {EditorSchema, EditorSelection} from '..'
4
4
  import type {EditorActor} from '../editor/editor-machine'
5
+ import {getBlockKeyFromSelectionPoint} from '../selection/selection-point'
5
6
  import type {PortableTextSlateEditor} from '../types/editor'
6
7
  import * as utils from '../utils'
7
8
  import {
@@ -80,12 +81,9 @@ export function getEventPosition({
80
81
  return undefined
81
82
  }
82
83
 
83
- const focusBlockPath = selection.focus.path.at(0)
84
- const focusBlockKey = utils.isKeyedSegment(focusBlockPath)
85
- ? focusBlockPath._key
86
- : undefined
84
+ const focusBlockKey = getBlockKeyFromSelectionPoint(selection.focus)
87
85
 
88
- if (!focusBlockKey) {
86
+ if (focusBlockKey === undefined) {
89
87
  return undefined
90
88
  }
91
89
 
@@ -1,20 +1,20 @@
1
+ import {getBlockKeyFromSelectionPoint} from '../selection/selection-point'
1
2
  import type {EditorSelection} from '../types/editor'
2
- import {isKeyedSegment} from '../utils'
3
3
 
4
4
  export function getSelectionBlockKeys(selection: EditorSelection) {
5
5
  if (!selection) {
6
6
  return undefined
7
7
  }
8
8
 
9
- if (
10
- !isKeyedSegment(selection.anchor.path[0]) ||
11
- !isKeyedSegment(selection.focus.path[0])
12
- ) {
9
+ const anchorBlockKey = getBlockKeyFromSelectionPoint(selection.anchor)
10
+ const focusBlockKey = getBlockKeyFromSelectionPoint(selection.focus)
11
+
12
+ if (anchorBlockKey === undefined || focusBlockKey === undefined) {
13
13
  return undefined
14
14
  }
15
15
 
16
16
  return {
17
- anchor: selection.anchor.path[0]._key,
18
- focus: selection.focus.path[0]._key,
17
+ anchor: anchorBlockKey,
18
+ focus: focusBlockKey,
19
19
  }
20
20
  }
@@ -1,7 +1,10 @@
1
1
  import {isPortableTextBlock, isPortableTextSpan} from '@portabletext/toolkit'
2
2
  import type {PortableTextBlock} from '@sanity/types'
3
+ import {
4
+ getBlockKeyFromSelectionPoint,
5
+ getChildKeyFromSelectionPoint,
6
+ } from '../selection/selection-point'
3
7
  import type {EditorSelection} from '../types/editor'
4
- import {isKeyedSegment} from '../utils'
5
8
 
6
9
  export function getSelectionFocusText(
7
10
  value: Array<PortableTextBlock> | undefined,
@@ -11,20 +14,21 @@ export function getSelectionFocusText(
11
14
  return undefined
12
15
  }
13
16
 
17
+ const focusBlockKey = getBlockKeyFromSelectionPoint(selection.focus)
18
+ const focusChildKey = getChildKeyFromSelectionPoint(selection.focus)
19
+
20
+ if (focusBlockKey === undefined || focusChildKey === undefined) {
21
+ return undefined
22
+ }
23
+
14
24
  let text: string | undefined
15
25
 
16
26
  for (const block of value) {
17
27
  if (isPortableTextBlock(block)) {
18
- if (
19
- isKeyedSegment(selection.focus.path[0]) &&
20
- block._key === selection.focus.path[0]._key
21
- ) {
28
+ if (block._key === focusBlockKey) {
22
29
  for (const child of block.children) {
23
30
  if (isPortableTextSpan(child)) {
24
- if (
25
- isKeyedSegment(selection.focus.path[2]) &&
26
- child._key === selection.focus.path[2]._key
27
- ) {
31
+ if (child._key === focusChildKey) {
28
32
  text = child.text
29
33
  break
30
34
  }
@@ -1,90 +1,21 @@
1
- import {isPortableTextBlock, isPortableTextSpan} from '@portabletext/toolkit'
2
1
  import type {PortableTextBlock} from '@sanity/types'
2
+ import {compileSchemaDefinition, defineSchema} from '../editor/editor-schema'
3
3
  import type {EditorSelection} from '../types/editor'
4
- import {isKeyedSegment} from '../utils'
5
- import {reverseSelection} from '../utils/util.reverse-selection'
4
+ import {sliceBlocks} from '../utils/util.slice-blocks'
5
+ import {getTersePt} from './terse-pt'
6
6
 
7
7
  export function getSelectionText(
8
8
  value: Array<PortableTextBlock> | undefined,
9
9
  selection: EditorSelection,
10
10
  ) {
11
11
  if (!value || !selection) {
12
- return undefined
12
+ return []
13
13
  }
14
14
 
15
- const forwardSelection = selection.backward
16
- ? reverseSelection(selection)
17
- : selection
15
+ const slice = sliceBlocks({
16
+ context: {schema: compileSchemaDefinition(defineSchema({})), selection},
17
+ blocks: value,
18
+ })
18
19
 
19
- if (!forwardSelection) {
20
- return undefined
21
- }
22
-
23
- const text: Array<string> = []
24
-
25
- for (const block of value) {
26
- if (
27
- text.length === 0 &&
28
- isKeyedSegment(forwardSelection.anchor.path[0]) &&
29
- block._key !== forwardSelection.anchor.path[0]._key
30
- ) {
31
- continue
32
- }
33
-
34
- if (text.length > 0) {
35
- text.push('|')
36
- }
37
-
38
- if (isPortableTextBlock(block)) {
39
- for (const child of block.children) {
40
- if (isPortableTextSpan(child)) {
41
- if (
42
- isKeyedSegment(forwardSelection.anchor.path[2]) &&
43
- child._key === forwardSelection.anchor.path[2]._key &&
44
- isKeyedSegment(forwardSelection.focus.path[2]) &&
45
- child._key === forwardSelection.focus.path[2]._key
46
- ) {
47
- text.push(
48
- child.text.slice(
49
- forwardSelection.anchor.offset,
50
- forwardSelection.focus.offset,
51
- ),
52
- )
53
- break
54
- }
55
-
56
- if (
57
- isKeyedSegment(forwardSelection.anchor.path[2]) &&
58
- child._key === forwardSelection.anchor.path[2]._key
59
- ) {
60
- text.push(child.text.slice(forwardSelection.anchor.offset))
61
- continue
62
- }
63
-
64
- if (
65
- isKeyedSegment(forwardSelection.focus.path[2]) &&
66
- child._key === forwardSelection.focus.path[2]._key
67
- ) {
68
- text.push(child.text.slice(0, forwardSelection.focus.offset))
69
- break
70
- }
71
-
72
- if (text.length > 0) {
73
- text.push(child.text)
74
- }
75
- }
76
- }
77
- } else {
78
- text.push(`[${block._type}]`)
79
- }
80
-
81
- if (
82
- isKeyedSegment(forwardSelection.focus.path[0]) &&
83
- block._key === forwardSelection.focus.path[0]._key
84
- ) {
85
- break
86
- }
87
- }
88
-
89
- return text
20
+ return getTersePt(slice)
90
21
  }
@@ -1,5 +1,9 @@
1
1
  import {expect, test} from 'vitest'
2
- import {getTersePt, parseTersePt} from './terse-pt'
2
+ import {compileSchemaDefinition, defineSchema} from '../editor/editor-schema'
3
+ import {createTestKeyGenerator} from '../internal-utils/test-key-generator'
4
+ import {getTersePt, parseTersePt, parseTersePtString} from './terse-pt'
5
+
6
+ const keyGenerator = createTestKeyGenerator()
3
7
 
4
8
  test(getTersePt.name, () => {
5
9
  const fooBlock = {
@@ -23,38 +27,116 @@ test(getTersePt.name, () => {
23
27
  children: [{_key: 's4', _type: 'span', text: 'foo\nbar'}],
24
28
  }
25
29
 
26
- expect(getTersePt([fooBlock, barBlock])).toEqual(['foo', '|', 'bar'])
27
- expect(getTersePt([emptyBlock, barBlock])).toEqual(['', '|', 'bar'])
30
+ expect(getTersePt([fooBlock, barBlock])).toEqual(['foo', 'bar'])
31
+ expect(getTersePt([emptyBlock, barBlock])).toEqual(['', 'bar'])
28
32
  expect(getTersePt([fooBlock, emptyBlock, barBlock])).toEqual([
29
33
  'foo',
30
- '|',
31
34
  '',
32
- '|',
33
35
  'bar',
34
36
  ])
35
- expect(getTersePt([fooBlock, softReturnBlock])).toEqual([
36
- 'foo',
37
- '|',
38
- 'foo\nbar',
39
- ])
37
+ expect(getTersePt([fooBlock, softReturnBlock])).toEqual(['foo', 'foo\nbar'])
38
+
39
+ expect(
40
+ getTersePt([
41
+ {
42
+ _key: keyGenerator(),
43
+ _type: 'block',
44
+ children: [{_key: keyGenerator(), _type: 'span', text: 'foo'}],
45
+ },
46
+ ]),
47
+ ).toEqual(['foo'])
48
+ expect(
49
+ getTersePt([
50
+ {
51
+ _key: keyGenerator(),
52
+ _type: 'block',
53
+ children: [{_key: keyGenerator(), _type: 'span', text: 'foo'}],
54
+ listItem: 'number',
55
+ },
56
+ ]),
57
+ ).toEqual(['#:foo'])
58
+ expect(
59
+ getTersePt([
60
+ {
61
+ _key: keyGenerator(),
62
+ _type: 'block',
63
+ children: [{_key: keyGenerator(), _type: 'span', text: 'foo'}],
64
+ listItem: 'number',
65
+ style: 'h3',
66
+ },
67
+ ]),
68
+ ).toEqual(['#h3:foo'])
69
+ expect(
70
+ getTersePt([
71
+ {
72
+ _key: keyGenerator(),
73
+ _type: 'block',
74
+ children: [{_key: keyGenerator(), _type: 'span', text: 'foo'}],
75
+ level: 2,
76
+ listItem: 'number',
77
+ style: 'h3',
78
+ },
79
+ ]),
80
+ ).toEqual(['>>#h3:foo'])
81
+ })
82
+
83
+ test(parseTersePtString.name, () => {
84
+ expect(parseTersePtString('foo')).toEqual(['foo'])
85
+ expect(parseTersePtString('foo,bar')).toEqual(['foo,bar'])
86
+ expect(parseTersePtString('foo,bar|baz')).toEqual(['foo,bar', 'baz'])
87
+ expect(parseTersePtString('|foo')).toEqual(['', 'foo'])
88
+ expect(parseTersePtString('foo|')).toEqual(['foo', ''])
89
+ expect(parseTersePtString('foo|bar\nbaz')).toEqual(['foo', 'bar\nbaz'])
90
+ expect(parseTersePtString('f,oo||ba,r')).toEqual(['f,oo', '', 'ba,r'])
91
+ expect(parseTersePtString('|')).toEqual(['', ''])
92
+ expect(parseTersePtString('||')).toEqual(['', '', ''])
93
+ expect(parseTersePtString('>>#h3:foo')).toEqual(['>>#h3:foo'])
40
94
  })
41
95
 
42
96
  test(parseTersePt.name, () => {
43
- expect(parseTersePt('foo')).toEqual(['foo'])
44
- expect(parseTersePt('foo,bar')).toEqual(['foo', 'bar'])
45
- expect(parseTersePt('foo,bar|baz')).toEqual(['foo', 'bar', '|', 'baz'])
46
- expect(parseTersePt('|foo')).toEqual(['', '|', 'foo'])
47
- expect(parseTersePt('foo|')).toEqual(['foo', '|', ''])
48
- expect(parseTersePt('foo|bar\nbaz')).toEqual(['foo', '|', 'bar\nbaz'])
49
- expect(parseTersePt('f,oo||ba,r')).toEqual([
50
- 'f',
51
- 'oo',
52
- '|',
53
- '',
54
- '|',
55
- 'ba',
56
- 'r',
97
+ expect(
98
+ parseTersePt(
99
+ {
100
+ schema: compileSchemaDefinition(defineSchema({})),
101
+ keyGenerator: createTestKeyGenerator(),
102
+ },
103
+ parseTersePtString('[image]|foo|>>#h4:bar|-:baz,fizz|,[stock-ticker],'),
104
+ ),
105
+ ).toEqual([
106
+ {
107
+ _key: 'k0',
108
+ _type: 'image',
109
+ },
110
+ {
111
+ _key: 'k1',
112
+ _type: 'block',
113
+ children: [{_key: 'k2', _type: 'span', text: 'foo'}],
114
+ },
115
+ {
116
+ _key: 'k3',
117
+ _type: 'block',
118
+ children: [{_key: 'k4', _type: 'span', text: 'bar'}],
119
+ level: 2,
120
+ listItem: 'number',
121
+ style: 'h4',
122
+ },
123
+ {
124
+ _key: 'k5',
125
+ _type: 'block',
126
+ children: [
127
+ {_key: 'k6', _type: 'span', text: 'baz'},
128
+ {_key: 'k7', _type: 'span', text: 'fizz'},
129
+ ],
130
+ listItem: 'bullet',
131
+ },
132
+ {
133
+ _key: 'k8',
134
+ _type: 'block',
135
+ children: [
136
+ {_key: 'k9', _type: 'span', text: ''},
137
+ {_key: 'k10', _type: 'stock-ticker'},
138
+ {_key: 'k11', _type: 'span', text: ''},
139
+ ],
140
+ },
57
141
  ])
58
- expect(parseTersePt('|')).toEqual(['', '|', ''])
59
- expect(parseTersePt('||')).toEqual(['', '|', '', '|', ''])
60
142
  })