@portabletext/editor 2.19.3 → 2.21.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.
- package/lib/_chunks-dts/index.d.ts +8 -24
- package/lib/_chunks-es/selector.is-at-the-start-of-block.js +2 -91
- package/lib/_chunks-es/selector.is-at-the-start-of-block.js.map +1 -1
- package/lib/_chunks-es/util.merge-text-blocks.js +78 -2
- package/lib/_chunks-es/util.merge-text-blocks.js.map +1 -1
- package/lib/_chunks-es/util.slice-text-block.js +1 -18
- package/lib/_chunks-es/util.slice-text-block.js.map +1 -1
- package/lib/index.js +128 -221
- package/lib/index.js.map +1 -1
- package/lib/plugins/index.js +1 -2
- package/lib/plugins/index.js.map +1 -1
- package/lib/selectors/index.d.ts +2 -0
- package/lib/selectors/index.js +92 -4
- package/lib/selectors/index.js.map +1 -1
- package/lib/utils/index.js +18 -3
- package/lib/utils/index.js.map +1 -1
- package/package.json +8 -8
- package/src/behaviors/behavior.abstract.decorator.ts +2 -10
- package/src/behaviors/behavior.abstract.delete.ts +1 -30
- package/src/behaviors/behavior.types.event.ts +4 -10
- package/src/operations/behavior.operation.decorator.add.ts +38 -131
- package/src/operations/behavior.operation.insert.block.ts +315 -307
- package/src/selectors/selector.get-trimmed-selection.ts +2 -0
- package/lib/_chunks-es/util.child-selection-point-to-block-offset.js +0 -81
- package/lib/_chunks-es/util.child-selection-point-to-block-offset.js.map +0 -1
package/lib/utils/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
+
import { getBlockKeyFromSelectionPoint } from "../_chunks-es/util.get-text-block-text.js";
|
|
1
2
|
import { blockOffsetToSpanSelectionPoint, getBlockStartPoint, getSelectionEndPoint, getSelectionStartPoint, getTextBlockText, isKeyedSegment, sliceBlocks, spanSelectionPointToBlockOffset } from "../_chunks-es/util.get-text-block-text.js";
|
|
2
|
-
import {
|
|
3
|
+
import { childSelectionPointToBlockOffset } from "../_chunks-es/util.merge-text-blocks.js";
|
|
4
|
+
import { blockOffsetToBlockSelectionPoint, blockOffsetToSelectionPoint, blockOffsetsToSelection, mergeTextBlocks } from "../_chunks-es/util.merge-text-blocks.js";
|
|
3
5
|
import { isEqualSelectionPoints } from "../_chunks-es/util.is-empty-text-block.js";
|
|
4
6
|
import { getBlockEndPoint, isEmptyTextBlock, isSelectionCollapsed } from "../_chunks-es/util.is-empty-text-block.js";
|
|
5
7
|
import { isSpan } from "@portabletext/schema";
|
|
6
8
|
import { isSpan as isSpan2, isTextBlock } from "@portabletext/schema";
|
|
7
|
-
import { mergeTextBlocks } from "../_chunks-es/util.merge-text-blocks.js";
|
|
8
9
|
import { sliceTextBlock } from "../_chunks-es/util.slice-text-block.js";
|
|
9
|
-
import { selectionPointToBlockOffset } from "../_chunks-es/util.slice-text-block.js";
|
|
10
10
|
function isEqualSelections(a, b) {
|
|
11
11
|
return !a && !b ? !0 : !a || !b ? !1 : isEqualSelectionPoints(a.anchor, b.anchor) && isEqualSelectionPoints(a.focus, b.focus);
|
|
12
12
|
}
|
|
@@ -21,6 +21,21 @@ function reverseSelection(selection) {
|
|
|
21
21
|
backward: !0
|
|
22
22
|
});
|
|
23
23
|
}
|
|
24
|
+
function selectionPointToBlockOffset({
|
|
25
|
+
context,
|
|
26
|
+
selectionPoint
|
|
27
|
+
}) {
|
|
28
|
+
const blockKey = getBlockKeyFromSelectionPoint(selectionPoint);
|
|
29
|
+
return selectionPoint.path.length === 1 && blockKey !== void 0 ? {
|
|
30
|
+
path: [{
|
|
31
|
+
_key: blockKey
|
|
32
|
+
}],
|
|
33
|
+
offset: selectionPoint.offset
|
|
34
|
+
} : childSelectionPointToBlockOffset({
|
|
35
|
+
context,
|
|
36
|
+
selectionPoint
|
|
37
|
+
});
|
|
38
|
+
}
|
|
24
39
|
function splitTextBlock({
|
|
25
40
|
context,
|
|
26
41
|
block,
|
package/lib/utils/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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 {isSpan} from '@portabletext/schema'\nimport type {PortableTextTextBlock} from '@sanity/types'\nimport type {EditorContext} from '../editor/editor-snapshot'\nimport type {EditorSelectionPoint} from '../types/editor'\nimport {sliceTextBlock} from './util.slice-text-block'\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 = sliceTextBlock({\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 block,\n })\n const after = sliceTextBlock({\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 block,\n })\n\n return {before, after}\n}\n"],"names":["isEqualSelections","a","b","isEqualSelectionPoints","anchor","focus","reverseSelection","selection","backward","
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/utils/util.is-equal-selections.ts","../../src/utils/util.reverse-selection.ts","../../src/utils/util.selection-point-to-block-offset.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 {EditorContext} from '../editor/editor-snapshot'\nimport type {BlockOffset} from '../types/block-offset'\nimport type {EditorSelectionPoint} from '../types/editor'\nimport {childSelectionPointToBlockOffset} from './util.child-selection-point-to-block-offset'\nimport {getBlockKeyFromSelectionPoint} from './util.selection-point'\n\n/**\n * @public\n */\nexport function selectionPointToBlockOffset({\n context,\n selectionPoint,\n}: {\n context: Pick<EditorContext, 'schema' | 'value'>\n selectionPoint: EditorSelectionPoint\n}): BlockOffset | undefined {\n const blockKey = getBlockKeyFromSelectionPoint(selectionPoint)\n\n if (selectionPoint.path.length === 1 && blockKey !== undefined) {\n return {\n path: [{_key: blockKey}],\n offset: selectionPoint.offset,\n }\n }\n\n return childSelectionPointToBlockOffset({\n context,\n selectionPoint,\n })\n}\n","import {isSpan} from '@portabletext/schema'\nimport type {PortableTextTextBlock} from '@sanity/types'\nimport type {EditorContext} from '../editor/editor-snapshot'\nimport type {EditorSelectionPoint} from '../types/editor'\nimport {sliceTextBlock} from './util.slice-text-block'\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 = sliceTextBlock({\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 block,\n })\n const after = sliceTextBlock({\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 block,\n })\n\n return {before, after}\n}\n"],"names":["isEqualSelections","a","b","isEqualSelectionPoints","anchor","focus","reverseSelection","selection","backward","selectionPointToBlockOffset","context","selectionPoint","blockKey","getBlockKeyFromSelectionPoint","path","length","undefined","_key","offset","childSelectionPointToBlockOffset","splitTextBlock","block","point","firstChild","children","at","lastChild","before","sliceTextBlock","schema","after","isSpan","text"],"mappings":";;;;;;;;;AAMO,SAASA,kBAAkBC,GAAoBC,GAAoB;AACxE,SAAI,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;AAC/C,SAAKA,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;AChBO,SAASC,4BAA4B;AAAA,EAC1CC;AAAAA,EACAC;AAIF,GAA4B;AAC1B,QAAMC,WAAWC,8BAA8BF,cAAc;AAE7D,SAAIA,eAAeG,KAAKC,WAAW,KAAKH,aAAaI,SAC5C;AAAA,IACLF,MAAM,CAAC;AAAA,MAACG,MAAML;AAAAA,IAAAA,CAAS;AAAA,IACvBM,QAAQP,eAAeO;AAAAA,EAAAA,IAIpBC,iCAAiC;AAAA,IACtCT;AAAAA,IACAC;AAAAA,EAAAA,CACD;AACH;ACpBO,SAASS,eAAe;AAAA,EAC7BV;AAAAA,EACAW;AAAAA,EACAC;AAKF,GAA8E;AAC5E,QAAMC,aAAaF,MAAMG,SAASC,GAAG,CAAC,GAChCC,YAAYL,MAAMG,SAASC,GAAGJ,MAAMG,SAAST,SAAS,CAAC;AAE7D,MAAI,CAACQ,cAAc,CAACG;AAClB;AAGF,QAAMC,SAASC,eAAe;AAAA,IAC5BlB,SAAS;AAAA,MACPmB,QAAQnB,QAAQmB;AAAAA,MAChBtB,WAAW;AAAA,QACTH,QAAQ;AAAA,UACNU,MAAM,CAAC;AAAA,YAACG,MAAMI,MAAMJ;AAAAA,UAAAA,GAAO,YAAY;AAAA,YAACA,MAAMM,WAAWN;AAAAA,UAAAA,CAAK;AAAA,UAC9DC,QAAQ;AAAA,QAAA;AAAA,QAEVb,OAAOiB;AAAAA,MAAAA;AAAAA,IACT;AAAA,IAEFD;AAAAA,EAAAA,CACD,GACKS,QAAQF,eAAe;AAAA,IAC3BlB,SAAS;AAAA,MACPmB,QAAQnB,QAAQmB;AAAAA,MAChBtB,WAAW;AAAA,QACTH,QAAQkB;AAAAA,QACRjB,OAAO;AAAA,UACLS,MAAM,CAAC;AAAA,YAACG,MAAMI,MAAMJ;AAAAA,UAAAA,GAAO,YAAY;AAAA,YAACA,MAAMS,UAAUT;AAAAA,UAAAA,CAAK;AAAA,UAC7DC,QAAQa,OAAOrB,SAASgB,SAAS,IAAIA,UAAUM,KAAKjB,SAAS;AAAA,QAAA;AAAA,MAC/D;AAAA,IACF;AAAA,IAEFM;AAAAA,EAAAA,CACD;AAED,SAAO;AAAA,IAACM;AAAAA,IAAQG;AAAAA,EAAAA;AAClB;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@portabletext/editor",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.21.0",
|
|
4
4
|
"description": "Portable Text Editor made in React",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sanity",
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"slate-dom": "^0.118.1",
|
|
74
74
|
"slate-react": "0.118.2",
|
|
75
75
|
"xstate": "^5.24.0",
|
|
76
|
-
"@portabletext/block-tools": "^4.0.
|
|
76
|
+
"@portabletext/block-tools": "^4.0.2",
|
|
77
77
|
"@portabletext/keyboard-shortcuts": "^2.1.0",
|
|
78
78
|
"@portabletext/patches": "^2.0.0",
|
|
79
79
|
"@portabletext/schema": "^2.0.0"
|
|
@@ -81,8 +81,8 @@
|
|
|
81
81
|
"devDependencies": {
|
|
82
82
|
"@sanity/diff-match-patch": "^3.2.0",
|
|
83
83
|
"@sanity/pkg-utils": "^9.0.3",
|
|
84
|
-
"@sanity/schema": "^4.14.
|
|
85
|
-
"@sanity/types": "^4.14.
|
|
84
|
+
"@sanity/schema": "^4.14.2",
|
|
85
|
+
"@sanity/types": "^4.14.2",
|
|
86
86
|
"@types/debug": "^4.1.12",
|
|
87
87
|
"@types/lodash": "^4.17.20",
|
|
88
88
|
"@types/lodash.startcase": "^4.4.9",
|
|
@@ -106,14 +106,14 @@
|
|
|
106
106
|
"vite": "^7.1.12",
|
|
107
107
|
"vitest": "^4.0.6",
|
|
108
108
|
"vitest-browser-react": "^2.0.2",
|
|
109
|
-
"@portabletext/sanity-bridge": "1.2.
|
|
109
|
+
"@portabletext/sanity-bridge": "1.2.2",
|
|
110
110
|
"@portabletext/test": "^1.0.0",
|
|
111
111
|
"racejar": "2.0.0"
|
|
112
112
|
},
|
|
113
113
|
"peerDependencies": {
|
|
114
|
-
"@portabletext/sanity-bridge": "^1.2.
|
|
115
|
-
"@sanity/schema": "^4.14.
|
|
116
|
-
"@sanity/types": "^4.14.
|
|
114
|
+
"@portabletext/sanity-bridge": "^1.2.2",
|
|
115
|
+
"@sanity/schema": "^4.14.2",
|
|
116
|
+
"@sanity/types": "^4.14.2",
|
|
117
117
|
"react": "^18.3 || ^19",
|
|
118
118
|
"rxjs": "^7.8.2"
|
|
119
119
|
},
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {isActiveDecorator} from '../selectors/selector.is-active-decorator'
|
|
2
|
-
import {blockOffsetsToSelection} from '../utils/util.block-offsets-to-selection'
|
|
3
2
|
import {raise} from './behavior.types.action'
|
|
4
3
|
import {defineBehavior} from './behavior.types.behavior'
|
|
5
4
|
|
|
@@ -16,19 +15,12 @@ export const abstractDecoratorBehaviors = [
|
|
|
16
15
|
defineBehavior({
|
|
17
16
|
on: 'decorator.toggle',
|
|
18
17
|
guard: ({snapshot, event}) => {
|
|
19
|
-
|
|
20
|
-
? blockOffsetsToSelection({
|
|
21
|
-
context: snapshot.context,
|
|
22
|
-
offsets: event.at,
|
|
23
|
-
})
|
|
24
|
-
: null
|
|
25
|
-
|
|
26
|
-
if (manualSelection) {
|
|
18
|
+
if (event.at) {
|
|
27
19
|
return !isActiveDecorator(event.decorator)({
|
|
28
20
|
...snapshot,
|
|
29
21
|
context: {
|
|
30
22
|
...snapshot.context,
|
|
31
|
-
selection:
|
|
23
|
+
selection: event.at,
|
|
32
24
|
},
|
|
33
25
|
})
|
|
34
26
|
}
|
|
@@ -3,10 +3,8 @@ import {getFocusChild} from '../selectors/selector.get-focus-child'
|
|
|
3
3
|
import {getFocusTextBlock} from '../selectors/selector.get-focus-text-block'
|
|
4
4
|
import {getNextBlock} from '../selectors/selector.get-next-block'
|
|
5
5
|
import {getPreviousBlock} from '../selectors/selector.get-previous-block'
|
|
6
|
-
import {getTrimmedSelection} from '../selectors/selector.get-trimmed-selection'
|
|
7
6
|
import {isAtTheEndOfBlock} from '../selectors/selector.is-at-the-end-of-block'
|
|
8
7
|
import {isAtTheStartOfBlock} from '../selectors/selector.is-at-the-start-of-block'
|
|
9
|
-
import {blockOffsetsToSelection} from '../utils/util.block-offsets-to-selection'
|
|
10
8
|
import {getBlockEndPoint} from '../utils/util.get-block-end-point'
|
|
11
9
|
import {getBlockStartPoint} from '../utils/util.get-block-start-point'
|
|
12
10
|
import {isEmptyTextBlock} from '../utils/util.is-empty-text-block'
|
|
@@ -270,33 +268,6 @@ export const abstractDeleteBehaviors = [
|
|
|
270
268
|
}),
|
|
271
269
|
defineBehavior({
|
|
272
270
|
on: 'delete.text',
|
|
273
|
-
|
|
274
|
-
const selection = blockOffsetsToSelection({
|
|
275
|
-
context: snapshot.context,
|
|
276
|
-
offsets: event.at,
|
|
277
|
-
})
|
|
278
|
-
|
|
279
|
-
if (!selection) {
|
|
280
|
-
return false
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
const trimmedSelection = getTrimmedSelection({
|
|
284
|
-
...snapshot,
|
|
285
|
-
context: {
|
|
286
|
-
...snapshot.context,
|
|
287
|
-
value: snapshot.context.value,
|
|
288
|
-
selection,
|
|
289
|
-
},
|
|
290
|
-
})
|
|
291
|
-
|
|
292
|
-
if (!trimmedSelection) {
|
|
293
|
-
return false
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
return {
|
|
297
|
-
selection: trimmedSelection,
|
|
298
|
-
}
|
|
299
|
-
},
|
|
300
|
-
actions: [(_, {selection}) => [raise({type: 'delete', at: selection})]],
|
|
271
|
+
actions: [({event}) => [raise({...event, type: 'delete'})]],
|
|
301
272
|
}),
|
|
302
273
|
]
|
|
@@ -2,7 +2,6 @@ import type {PortableTextBlock} from '@sanity/types'
|
|
|
2
2
|
import type {EventPosition} from '../internal-utils/event-position'
|
|
3
3
|
import type {MIMEType} from '../internal-utils/mime-type'
|
|
4
4
|
import type {OmitFromUnion, PickFromUnion, StrictExtract} from '../type-utils'
|
|
5
|
-
import type {BlockOffset} from '../types/block-offset'
|
|
6
5
|
import type {
|
|
7
6
|
BlockWithOptionalKey,
|
|
8
7
|
ChildWithOptionalKey,
|
|
@@ -130,10 +129,7 @@ export type SyntheticBehaviorEvent =
|
|
|
130
129
|
| {
|
|
131
130
|
type: StrictExtract<SyntheticBehaviorEventType, 'decorator.add'>
|
|
132
131
|
decorator: string
|
|
133
|
-
at?:
|
|
134
|
-
anchor: BlockOffset
|
|
135
|
-
focus: BlockOffset
|
|
136
|
-
}
|
|
132
|
+
at?: NonNullable<EditorSelection>
|
|
137
133
|
}
|
|
138
134
|
| {
|
|
139
135
|
type: StrictExtract<SyntheticBehaviorEventType, 'decorator.remove'>
|
|
@@ -162,6 +158,7 @@ export type SyntheticBehaviorEvent =
|
|
|
162
158
|
block: BlockWithOptionalKey
|
|
163
159
|
placement: InsertPlacement
|
|
164
160
|
select?: 'start' | 'end' | 'none'
|
|
161
|
+
at?: NonNullable<EditorSelection>
|
|
165
162
|
}
|
|
166
163
|
| {
|
|
167
164
|
type: StrictExtract<SyntheticBehaviorEventType, 'insert.child'>
|
|
@@ -268,7 +265,7 @@ type AbstractBehaviorEvent =
|
|
|
268
265
|
| {
|
|
269
266
|
type: StrictExtract<SyntheticBehaviorEventType, 'decorator.toggle'>
|
|
270
267
|
decorator: string
|
|
271
|
-
at?:
|
|
268
|
+
at?: NonNullable<EditorSelection>
|
|
272
269
|
}
|
|
273
270
|
| {
|
|
274
271
|
type: StrictExtract<SyntheticBehaviorEventType, 'delete.backward'>
|
|
@@ -288,10 +285,7 @@ type AbstractBehaviorEvent =
|
|
|
288
285
|
}
|
|
289
286
|
| {
|
|
290
287
|
type: StrictExtract<SyntheticBehaviorEventType, 'delete.text'>
|
|
291
|
-
at:
|
|
292
|
-
anchor: BlockOffset
|
|
293
|
-
focus: BlockOffset
|
|
294
|
-
}
|
|
288
|
+
at: NonNullable<EditorSelection>
|
|
295
289
|
}
|
|
296
290
|
| {
|
|
297
291
|
type: StrictExtract<SyntheticBehaviorEventType, 'deserialize'>
|
|
@@ -1,12 +1,5 @@
|
|
|
1
1
|
import {Editor, Range, Text, Transforms} from 'slate'
|
|
2
|
-
import {KEY_TO_VALUE_ELEMENT} from '../editor/weakMaps'
|
|
3
|
-
import {slateRangeToSelection} from '../internal-utils/slate-utils'
|
|
4
2
|
import {toSlateRange} from '../internal-utils/to-slate-range'
|
|
5
|
-
import {fromSlateValue} from '../internal-utils/values'
|
|
6
|
-
import {getTrimmedSelection} from '../selectors/selector.get-trimmed-selection'
|
|
7
|
-
import {blockOffsetToSpanSelectionPoint} from '../utils/util.block-offset'
|
|
8
|
-
import {blockOffsetsToSelection} from '../utils/util.block-offsets-to-selection'
|
|
9
|
-
import {selectionPointToBlockOffset} from '../utils/util.selection-point-to-block-offset'
|
|
10
3
|
import type {BehaviorOperationImplementation} from './behavior.operations'
|
|
11
4
|
|
|
12
5
|
export const decoratorAddOperationImplementation: BehaviorOperationImplementation<
|
|
@@ -14,147 +7,61 @@ export const decoratorAddOperationImplementation: BehaviorOperationImplementatio
|
|
|
14
7
|
> = ({context, operation}) => {
|
|
15
8
|
const editor = operation.editor
|
|
16
9
|
const mark = operation.decorator
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
KEY_TO_VALUE_ELEMENT.get(editor),
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
const manualAnchor = operation.at?.anchor
|
|
24
|
-
? blockOffsetToSpanSelectionPoint({
|
|
25
|
-
context: {
|
|
26
|
-
...context,
|
|
27
|
-
value,
|
|
28
|
-
},
|
|
29
|
-
blockOffset: operation.at.anchor,
|
|
30
|
-
direction: 'backward',
|
|
31
|
-
})
|
|
32
|
-
: undefined
|
|
33
|
-
const manualFocus = operation.at?.focus
|
|
34
|
-
? blockOffsetToSpanSelectionPoint({
|
|
35
|
-
context: {
|
|
36
|
-
...context,
|
|
37
|
-
value,
|
|
38
|
-
},
|
|
39
|
-
blockOffset: operation.at.focus,
|
|
40
|
-
direction: 'forward',
|
|
41
|
-
})
|
|
42
|
-
: undefined
|
|
43
|
-
const manualSelection =
|
|
44
|
-
manualAnchor && manualFocus
|
|
45
|
-
? {
|
|
46
|
-
anchor: manualAnchor,
|
|
47
|
-
focus: manualFocus,
|
|
48
|
-
}
|
|
49
|
-
: undefined
|
|
50
|
-
|
|
51
|
-
const selection = manualSelection
|
|
52
|
-
? (toSlateRange({
|
|
10
|
+
|
|
11
|
+
let at = operation.at
|
|
12
|
+
? toSlateRange({
|
|
53
13
|
context: {
|
|
54
14
|
schema: context.schema,
|
|
55
15
|
value: operation.editor.value,
|
|
56
|
-
selection:
|
|
16
|
+
selection: operation.at,
|
|
57
17
|
},
|
|
58
18
|
blockIndexMap: operation.editor.blockIndexMap,
|
|
59
|
-
})
|
|
60
|
-
: editor.selection
|
|
19
|
+
})
|
|
20
|
+
: operation.editor.selection
|
|
61
21
|
|
|
62
|
-
if (!
|
|
63
|
-
|
|
22
|
+
if (!at) {
|
|
23
|
+
throw new Error('Unable to add decorator without a selection')
|
|
64
24
|
}
|
|
65
25
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
range: selection,
|
|
70
|
-
})
|
|
71
|
-
const anchorOffset = editorSelection
|
|
72
|
-
? selectionPointToBlockOffset({
|
|
73
|
-
context: {
|
|
74
|
-
...context,
|
|
75
|
-
value,
|
|
76
|
-
},
|
|
77
|
-
selectionPoint: editorSelection.anchor,
|
|
78
|
-
})
|
|
79
|
-
: undefined
|
|
80
|
-
const focusOffset = editorSelection
|
|
81
|
-
? selectionPointToBlockOffset({
|
|
82
|
-
context: {
|
|
83
|
-
...context,
|
|
84
|
-
value,
|
|
85
|
-
},
|
|
86
|
-
selectionPoint: editorSelection.focus,
|
|
87
|
-
})
|
|
88
|
-
: undefined
|
|
26
|
+
if (Range.isExpanded(at)) {
|
|
27
|
+
const rangeRef = Editor.rangeRef(editor, at, {affinity: 'inward'})
|
|
28
|
+
const [start, end] = Range.edges(at)
|
|
89
29
|
|
|
90
|
-
|
|
91
|
-
throw new Error('Unable to find anchor or focus offset')
|
|
92
|
-
}
|
|
30
|
+
const endAtEndOfNode = Editor.isEnd(editor, end, end.path)
|
|
93
31
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
)
|
|
101
|
-
|
|
102
|
-
// The value might have changed after splitting
|
|
103
|
-
const newValue = fromSlateValue(
|
|
104
|
-
editor.children,
|
|
105
|
-
context.schema.block.name,
|
|
106
|
-
KEY_TO_VALUE_ELEMENT.get(editor),
|
|
107
|
-
)
|
|
108
|
-
// We need to find the new selection from the original offsets because the
|
|
109
|
-
// split operation might have changed the value.
|
|
110
|
-
const newSelection = blockOffsetsToSelection({
|
|
111
|
-
context: {
|
|
112
|
-
...context,
|
|
113
|
-
value: newValue,
|
|
114
|
-
},
|
|
115
|
-
offsets: {anchor: anchorOffset, focus: focusOffset},
|
|
116
|
-
backward: editorSelection?.backward,
|
|
32
|
+
Transforms.splitNodes(editor, {
|
|
33
|
+
at: end,
|
|
34
|
+
match: Text.isText,
|
|
35
|
+
mode: 'lowest',
|
|
36
|
+
voids: false,
|
|
37
|
+
always: !endAtEndOfNode,
|
|
117
38
|
})
|
|
118
39
|
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
value: newValue,
|
|
128
|
-
},
|
|
129
|
-
decoratorState: editor.decoratorState,
|
|
40
|
+
const startAtStartOfNode = Editor.isStart(editor, start, start.path)
|
|
41
|
+
|
|
42
|
+
Transforms.splitNodes(editor, {
|
|
43
|
+
at: start,
|
|
44
|
+
match: Text.isText,
|
|
45
|
+
mode: 'lowest',
|
|
46
|
+
voids: false,
|
|
47
|
+
always: !startAtStartOfNode,
|
|
130
48
|
})
|
|
131
49
|
|
|
132
|
-
|
|
133
|
-
throw new Error('Unable to find trimmed selection')
|
|
134
|
-
}
|
|
50
|
+
at = rangeRef.unref()
|
|
135
51
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
value: operation.editor.value,
|
|
140
|
-
selection: trimmedSelection,
|
|
141
|
-
},
|
|
142
|
-
blockIndexMap: operation.editor.blockIndexMap,
|
|
143
|
-
})
|
|
52
|
+
if (!at) {
|
|
53
|
+
throw new Error('Unable to add decorator without a selection')
|
|
54
|
+
}
|
|
144
55
|
|
|
145
|
-
if (!
|
|
146
|
-
|
|
56
|
+
if (!operation.at) {
|
|
57
|
+
Transforms.select(editor, at)
|
|
147
58
|
}
|
|
148
59
|
|
|
149
60
|
// Use new selection to find nodes to decorate
|
|
150
|
-
const splitTextNodes =
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
match: (node) => Text.isText(node),
|
|
155
|
-
}),
|
|
156
|
-
]
|
|
157
|
-
: []
|
|
61
|
+
const splitTextNodes = Editor.nodes(editor, {
|
|
62
|
+
at,
|
|
63
|
+
match: Text.isText,
|
|
64
|
+
})
|
|
158
65
|
|
|
159
66
|
for (const [node, path] of splitTextNodes) {
|
|
160
67
|
const marks = [
|
|
@@ -172,7 +79,7 @@ export const decoratorAddOperationImplementation: BehaviorOperationImplementatio
|
|
|
172
79
|
} else {
|
|
173
80
|
const selectedSpan = Array.from(
|
|
174
81
|
Editor.nodes(editor, {
|
|
175
|
-
at
|
|
82
|
+
at,
|
|
176
83
|
match: (node) => editor.isTextSpan(node),
|
|
177
84
|
}),
|
|
178
85
|
)?.at(0)
|
|
@@ -181,7 +88,7 @@ export const decoratorAddOperationImplementation: BehaviorOperationImplementatio
|
|
|
181
88
|
return
|
|
182
89
|
}
|
|
183
90
|
|
|
184
|
-
const [block, blockPath] = Editor.node(editor,
|
|
91
|
+
const [block, blockPath] = Editor.node(editor, at, {
|
|
185
92
|
depth: 1,
|
|
186
93
|
})
|
|
187
94
|
const lonelyEmptySpan =
|