@portabletext/editor 1.53.1 → 1.54.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-cjs/selection-point.cjs.map +1 -1
- package/lib/_chunks-cjs/selector.is-selecting-entire-blocks.cjs.map +1 -1
- package/lib/_chunks-cjs/selector.is-selection-expanded.cjs.map +1 -1
- package/lib/_chunks-cjs/util.is-equal-selection-points.cjs.map +1 -1
- package/lib/_chunks-es/selection-point.js.map +1 -1
- package/lib/_chunks-es/selector.is-selecting-entire-blocks.js.map +1 -1
- package/lib/_chunks-es/selector.is-selection-expanded.js.map +1 -1
- package/lib/_chunks-es/util.is-equal-selection-points.js.map +1 -1
- package/lib/behaviors/index.cjs.map +1 -1
- package/lib/behaviors/index.d.cts +68 -9
- package/lib/behaviors/index.d.ts +68 -9
- package/lib/behaviors/index.js.map +1 -1
- package/lib/index.cjs +529 -289
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +69 -13
- package/lib/index.d.ts +69 -13
- package/lib/index.js +533 -293
- package/lib/index.js.map +1 -1
- package/lib/plugins/index.d.cts +69 -9
- package/lib/plugins/index.d.ts +69 -9
- package/lib/selectors/index.cjs.map +1 -1
- package/lib/selectors/index.d.cts +31 -16
- package/lib/selectors/index.d.ts +31 -16
- package/lib/selectors/index.js.map +1 -1
- package/lib/utils/index.d.cts +27 -5
- package/lib/utils/index.d.ts +27 -5
- package/package.json +3 -3
- package/src/behaviors/behavior.abstract.annotation.ts +51 -0
- package/src/behaviors/behavior.abstract.delete.ts +55 -0
- package/src/behaviors/behavior.perform-event.ts +6 -15
- package/src/behaviors/behavior.types.action.ts +1 -1
- package/src/behaviors/behavior.types.event.ts +32 -8
- package/src/behaviors/behavior.types.guard.ts +1 -1
- package/src/editor/Editable.tsx +3 -0
- package/src/editor/PortableTextEditor.tsx +1 -6
- package/src/editor/create-editor.ts +5 -0
- package/src/{internal-utils/selection-elements.ts → editor/editor-dom.ts} +29 -21
- package/src/editor/editor-provider.tsx +1 -6
- package/src/editor/hooks/usePortableTextEditorSelection.tsx +6 -46
- package/src/editor/plugins/createWithPatches.ts +1 -5
- package/src/editor.ts +2 -0
- package/src/index.ts +2 -0
- package/src/operations/behavior.operation.child.set.ts +103 -0
- package/src/operations/behavior.operation.child.unset.ts +89 -0
- package/src/operations/behavior.operations.ts +18 -0
- package/src/selectors/selector.get-anchor-block.ts +3 -2
- package/src/selectors/selector.get-anchor-child.ts +2 -2
- package/src/selectors/selector.get-anchor-span.ts +2 -3
- package/src/selectors/selector.get-anchor-text-block.ts +3 -2
- package/src/selectors/selector.get-focus-child.ts +3 -6
- package/src/selectors/selector.get-focus-inline-object.ts +3 -7
- package/src/selectors/selector.get-focus-span.ts +3 -3
- package/src/selectors/selector.get-next-inline-object.ts +4 -7
- package/src/selectors/selector.get-previous-block.ts +2 -2
- package/src/selectors/selector.get-previous-inline-object.ts +4 -7
- package/src/selectors/selector.get-selected-blocks.ts +2 -3
- package/src/selectors/selector.get-selected-spans.test.ts +96 -0
- package/src/selectors/selector.get-selected-spans.ts +4 -3
- package/src/selectors/selector.get-selected-text-blocks.ts +4 -3
- package/src/selectors/selector.is-at-the-end-of-block.ts +3 -2
- package/src/selectors/selector.is-at-the-start-of-block.ts +3 -2
- package/src/types/block-offset.ts +2 -2
- package/src/types/paths.ts +13 -0
- package/src/utils/util.block-offset.ts +2 -4
- package/src/utils/util.get-block-end-point.ts +3 -2
- package/src/utils/util.get-block-start-point.ts +3 -2
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
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
5
|
import type {BlockOffset} from '../types/block-offset'
|
|
6
6
|
import type {BlockWithOptionalKey} from '../types/block-with-optional-key'
|
|
7
7
|
import type {EditorSelection} from '../types/editor'
|
|
8
|
+
import type {AnnotationPath, BlockPath, ChildPath} from '../types/paths'
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* @beta
|
|
@@ -63,6 +64,8 @@ const syntheticBehaviorEventTypes = [
|
|
|
63
64
|
'annotation.remove',
|
|
64
65
|
'block.set',
|
|
65
66
|
'block.unset',
|
|
67
|
+
'child.set',
|
|
68
|
+
'child.unset',
|
|
66
69
|
'decorator.add',
|
|
67
70
|
'decorator.remove',
|
|
68
71
|
'delete',
|
|
@@ -104,12 +107,22 @@ export type SyntheticBehaviorEvent =
|
|
|
104
107
|
}
|
|
105
108
|
| {
|
|
106
109
|
type: StrictExtract<SyntheticBehaviorEventType, 'block.set'>
|
|
107
|
-
at:
|
|
110
|
+
at: BlockPath
|
|
108
111
|
props: Record<string, unknown>
|
|
109
112
|
}
|
|
110
113
|
| {
|
|
111
114
|
type: StrictExtract<SyntheticBehaviorEventType, 'block.unset'>
|
|
112
|
-
at:
|
|
115
|
+
at: BlockPath
|
|
116
|
+
props: Array<string>
|
|
117
|
+
}
|
|
118
|
+
| {
|
|
119
|
+
type: StrictExtract<SyntheticBehaviorEventType, 'child.set'>
|
|
120
|
+
at: ChildPath
|
|
121
|
+
props: {[prop: string]: unknown}
|
|
122
|
+
}
|
|
123
|
+
| {
|
|
124
|
+
type: StrictExtract<SyntheticBehaviorEventType, 'child.unset'>
|
|
125
|
+
at: ChildPath
|
|
113
126
|
props: Array<string>
|
|
114
127
|
}
|
|
115
128
|
| {
|
|
@@ -174,8 +187,8 @@ export type SyntheticBehaviorEvent =
|
|
|
174
187
|
}
|
|
175
188
|
| {
|
|
176
189
|
type: StrictExtract<SyntheticBehaviorEventType, 'move.block'>
|
|
177
|
-
at:
|
|
178
|
-
to:
|
|
190
|
+
at: BlockPath
|
|
191
|
+
to: BlockPath
|
|
179
192
|
}
|
|
180
193
|
| {
|
|
181
194
|
type: StrictExtract<SyntheticBehaviorEventType, 'move.forward'>
|
|
@@ -208,10 +221,12 @@ export function isSyntheticBehaviorEvent(
|
|
|
208
221
|
**************************************/
|
|
209
222
|
|
|
210
223
|
const abstractBehaviorEventTypes = [
|
|
224
|
+
'annotation.set',
|
|
211
225
|
'annotation.toggle',
|
|
212
226
|
'decorator.toggle',
|
|
213
227
|
'delete.backward',
|
|
214
228
|
'delete.block',
|
|
229
|
+
'delete.child',
|
|
215
230
|
'delete.forward',
|
|
216
231
|
'delete.text',
|
|
217
232
|
'deserialize',
|
|
@@ -240,6 +255,11 @@ export type AbstractBehaviorEventType =
|
|
|
240
255
|
(typeof abstractBehaviorEventTypes)[number]
|
|
241
256
|
|
|
242
257
|
type AbstractBehaviorEvent =
|
|
258
|
+
| {
|
|
259
|
+
type: StrictExtract<SyntheticBehaviorEventType, 'annotation.set'>
|
|
260
|
+
at: AnnotationPath
|
|
261
|
+
props: Record<string, unknown>
|
|
262
|
+
}
|
|
243
263
|
| {
|
|
244
264
|
type: StrictExtract<SyntheticBehaviorEventType, 'annotation.toggle'>
|
|
245
265
|
annotation: {
|
|
@@ -258,7 +278,11 @@ type AbstractBehaviorEvent =
|
|
|
258
278
|
}
|
|
259
279
|
| {
|
|
260
280
|
type: StrictExtract<SyntheticBehaviorEventType, 'delete.block'>
|
|
261
|
-
at:
|
|
281
|
+
at: BlockPath
|
|
282
|
+
}
|
|
283
|
+
| {
|
|
284
|
+
type: StrictExtract<SyntheticBehaviorEventType, 'delete.child'>
|
|
285
|
+
at: ChildPath
|
|
262
286
|
}
|
|
263
287
|
| {
|
|
264
288
|
type: StrictExtract<SyntheticBehaviorEventType, 'delete.forward'>
|
|
@@ -359,11 +383,11 @@ type AbstractBehaviorEvent =
|
|
|
359
383
|
}
|
|
360
384
|
| {
|
|
361
385
|
type: StrictExtract<SyntheticBehaviorEventType, 'move.block down'>
|
|
362
|
-
at:
|
|
386
|
+
at: BlockPath
|
|
363
387
|
}
|
|
364
388
|
| {
|
|
365
389
|
type: StrictExtract<SyntheticBehaviorEventType, 'move.block up'>
|
|
366
|
-
at:
|
|
390
|
+
at: BlockPath
|
|
367
391
|
}
|
|
368
392
|
| {
|
|
369
393
|
type: StrictExtract<SyntheticBehaviorEventType, 'select.previous block'>
|
package/src/editor/Editable.tsx
CHANGED
|
@@ -30,7 +30,6 @@ import {createInternalEditor, type InternalEditor} from './create-editor'
|
|
|
30
30
|
import {EditorActorContext} from './editor-actor-context'
|
|
31
31
|
import type {EditorActor} from './editor-machine'
|
|
32
32
|
import {PortableTextEditorContext} from './hooks/usePortableTextEditor'
|
|
33
|
-
import {PortableTextEditorSelectionProvider} from './hooks/usePortableTextEditorSelection'
|
|
34
33
|
import type {MutationActor} from './mutation-machine'
|
|
35
34
|
import {RelayActorContext} from './relay-actor-context'
|
|
36
35
|
import type {RelayActor} from './relay-machine'
|
|
@@ -288,11 +287,7 @@ export class PortableTextEditor extends Component<
|
|
|
288
287
|
initialValue={this.editor._internal.slateEditor.initialValue}
|
|
289
288
|
>
|
|
290
289
|
<PortableTextEditorContext.Provider value={this}>
|
|
291
|
-
|
|
292
|
-
editorActor={this.editor._internal.editorActor}
|
|
293
|
-
>
|
|
294
|
-
{this.props.children}
|
|
295
|
-
</PortableTextEditorSelectionProvider>
|
|
290
|
+
{this.props.children}
|
|
296
291
|
</PortableTextEditorContext.Provider>
|
|
297
292
|
</Slate>
|
|
298
293
|
</RelayActorContext.Provider>
|
|
@@ -9,6 +9,7 @@ import {corePriority} from '../priority/priority.core'
|
|
|
9
9
|
import {createEditorPriority} from '../priority/priority.types'
|
|
10
10
|
import type {EditableAPI, PortableTextSlateEditor} from '../types/editor'
|
|
11
11
|
import {createSlateEditor, type SlateEditor} from './create-slate-editor'
|
|
12
|
+
import {createEditorDom} from './editor-dom'
|
|
12
13
|
import type {EditorActor} from './editor-machine'
|
|
13
14
|
import {editorMachine} from './editor-machine'
|
|
14
15
|
import {
|
|
@@ -64,6 +65,10 @@ export function createInternalEditor(config: EditorConfig): {
|
|
|
64
65
|
})
|
|
65
66
|
|
|
66
67
|
const editor = {
|
|
68
|
+
dom: createEditorDom(
|
|
69
|
+
(event) => editorActor.send(event),
|
|
70
|
+
slateEditor.instance,
|
|
71
|
+
),
|
|
67
72
|
getSnapshot: () =>
|
|
68
73
|
getEditorSnapshot({
|
|
69
74
|
editorActorSnapshot: editorActor.getSnapshot(),
|
|
@@ -2,9 +2,9 @@ import {Editor} from 'slate'
|
|
|
2
2
|
import {DOMEditor} from 'slate-dom'
|
|
3
3
|
import type {EditorSnapshot} from '..'
|
|
4
4
|
import type {BehaviorEvent} from '../behaviors'
|
|
5
|
+
import {toSlateRange} from '../internal-utils/ranges'
|
|
5
6
|
import type {PickFromUnion} from '../type-utils'
|
|
6
7
|
import type {PortableTextSlateEditor} from '../types/editor'
|
|
7
|
-
import {toSlateRange} from './ranges'
|
|
8
8
|
|
|
9
9
|
export type EditorDom = {
|
|
10
10
|
getBlockNodes: (snapshot: EditorSnapshot) => Array<Node>
|
|
@@ -51,17 +51,21 @@ function getBlockNodes(
|
|
|
51
51
|
return []
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
54
|
+
try {
|
|
55
|
+
const blockEntries = Array.from(
|
|
56
|
+
Editor.nodes(slateEditor, {
|
|
57
|
+
at: range,
|
|
58
|
+
mode: 'highest',
|
|
59
|
+
match: (n) => !Editor.isEditor(n),
|
|
60
|
+
}),
|
|
61
|
+
)
|
|
61
62
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
return blockEntries.map(([blockNode]) =>
|
|
64
|
+
DOMEditor.toDOMNode(slateEditor, blockNode),
|
|
65
|
+
)
|
|
66
|
+
} catch {
|
|
67
|
+
return []
|
|
68
|
+
}
|
|
65
69
|
}
|
|
66
70
|
|
|
67
71
|
function getChildNodes(
|
|
@@ -78,17 +82,21 @@ function getChildNodes(
|
|
|
78
82
|
return []
|
|
79
83
|
}
|
|
80
84
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
85
|
+
try {
|
|
86
|
+
const childEntries = Array.from(
|
|
87
|
+
Editor.nodes(slateEditor, {
|
|
88
|
+
at: range,
|
|
89
|
+
mode: 'lowest',
|
|
90
|
+
match: (n) => !Editor.isEditor(n),
|
|
91
|
+
}),
|
|
92
|
+
)
|
|
88
93
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
94
|
+
return childEntries.map(([childNode]) =>
|
|
95
|
+
DOMEditor.toDOMNode(slateEditor, childNode),
|
|
96
|
+
)
|
|
97
|
+
} catch {
|
|
98
|
+
return []
|
|
99
|
+
}
|
|
92
100
|
}
|
|
93
101
|
|
|
94
102
|
export type SelectionDomNodes = {
|
|
@@ -8,7 +8,6 @@ import {createInternalEditor} from './create-editor'
|
|
|
8
8
|
import {EditorActorContext} from './editor-actor-context'
|
|
9
9
|
import {EditorContext} from './editor-context'
|
|
10
10
|
import {PortableTextEditorContext} from './hooks/usePortableTextEditor'
|
|
11
|
-
import {PortableTextEditorSelectionProvider} from './hooks/usePortableTextEditorSelection'
|
|
12
11
|
import {
|
|
13
12
|
PortableTextEditor,
|
|
14
13
|
type PortableTextEditorProps,
|
|
@@ -93,11 +92,7 @@ export function EditorProvider(props: EditorProviderProps) {
|
|
|
93
92
|
}
|
|
94
93
|
>
|
|
95
94
|
<PortableTextEditorContext.Provider value={portableTextEditor}>
|
|
96
|
-
|
|
97
|
-
editorActor={internalEditor.actors.editorActor}
|
|
98
|
-
>
|
|
99
|
-
{props.children}
|
|
100
|
-
</PortableTextEditorSelectionProvider>
|
|
95
|
+
{props.children}
|
|
101
96
|
</PortableTextEditorContext.Provider>
|
|
102
97
|
</Slate>
|
|
103
98
|
</RelayActorContext.Provider>
|
|
@@ -1,19 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createContext,
|
|
3
|
-
startTransition,
|
|
4
|
-
useContext,
|
|
5
|
-
useEffect,
|
|
6
|
-
useState,
|
|
7
|
-
} from 'react'
|
|
8
|
-
import {debugWithName} from '../../internal-utils/debug'
|
|
1
|
+
import {startTransition, useContext, useEffect, useState} from 'react'
|
|
9
2
|
import type {EditorSelection} from '../../types/editor'
|
|
10
|
-
import
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* A React context for sharing the editor selection.
|
|
14
|
-
*/
|
|
15
|
-
const PortableTextEditorSelectionContext =
|
|
16
|
-
createContext<EditorSelection | null>(null)
|
|
3
|
+
import {EditorActorContext} from '../editor-actor-context'
|
|
17
4
|
|
|
18
5
|
/**
|
|
19
6
|
* @deprecated Use `useEditorSelector` to get the current editor selection.
|
|
@@ -21,48 +8,21 @@ const PortableTextEditorSelectionContext =
|
|
|
21
8
|
* Get the current editor selection from the React context.
|
|
22
9
|
*/
|
|
23
10
|
export const usePortableTextEditorSelection = (): EditorSelection => {
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
if (selection === undefined) {
|
|
27
|
-
throw new Error(
|
|
28
|
-
`The \`usePortableTextEditorSelection\` hook must be used inside the <PortableTextEditor> component's context.`,
|
|
29
|
-
)
|
|
30
|
-
}
|
|
31
|
-
return selection
|
|
32
|
-
}
|
|
33
|
-
const debug = debugWithName('component:PortableTextEditor:SelectionProvider')
|
|
34
|
-
const debugVerbose = debug.enabled && false
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* @internal
|
|
38
|
-
*/
|
|
39
|
-
export function PortableTextEditorSelectionProvider(
|
|
40
|
-
props: React.PropsWithChildren<{
|
|
41
|
-
editorActor: EditorActor
|
|
42
|
-
}>,
|
|
43
|
-
) {
|
|
11
|
+
const editorActor = useContext(EditorActorContext)
|
|
44
12
|
const [selection, setSelection] = useState<EditorSelection>(null)
|
|
45
13
|
|
|
46
|
-
// Subscribe to, and handle changes from the editor
|
|
47
14
|
useEffect(() => {
|
|
48
|
-
|
|
49
|
-
const subscription = props.editorActor.on('selection', (event) => {
|
|
15
|
+
const subscription = editorActor.on('selection', (event) => {
|
|
50
16
|
// Set the selection state in a transition, we don't need the state immediately.
|
|
51
17
|
startTransition(() => {
|
|
52
|
-
if (debugVerbose) debug('Setting selection')
|
|
53
18
|
setSelection(event.selection)
|
|
54
19
|
})
|
|
55
20
|
})
|
|
56
21
|
|
|
57
22
|
return () => {
|
|
58
|
-
debug('Unsubscribing to selection changes')
|
|
59
23
|
subscription.unsubscribe()
|
|
60
24
|
}
|
|
61
|
-
}, [
|
|
25
|
+
}, [editorActor])
|
|
62
26
|
|
|
63
|
-
return
|
|
64
|
-
<PortableTextEditorSelectionContext.Provider value={selection}>
|
|
65
|
-
{props.children}
|
|
66
|
-
</PortableTextEditorSelectionContext.Provider>
|
|
67
|
-
)
|
|
27
|
+
return selection
|
|
68
28
|
}
|
|
@@ -267,11 +267,7 @@ export function createWithPatches({
|
|
|
267
267
|
type: 'internal.patch',
|
|
268
268
|
patch: {...patch, origin: 'local'},
|
|
269
269
|
operationId: getCurrentOperationId(editor),
|
|
270
|
-
value:
|
|
271
|
-
editor.children,
|
|
272
|
-
editorActor.getSnapshot().context.schema.block.name,
|
|
273
|
-
KEY_TO_VALUE_ELEMENT.get(editor),
|
|
274
|
-
),
|
|
270
|
+
value: editor.value,
|
|
275
271
|
})
|
|
276
272
|
}
|
|
277
273
|
}
|
package/src/editor.ts
CHANGED
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
import type {ActorRef, EventObject, Snapshot} from 'xstate'
|
|
7
7
|
import type {Behavior} from './behaviors/behavior.types.behavior'
|
|
8
8
|
import type {ExternalBehaviorEvent} from './behaviors/behavior.types.event'
|
|
9
|
+
import type {EditorDom} from './editor/editor-dom'
|
|
9
10
|
import type {ExternalEditorEvent} from './editor/editor-machine'
|
|
10
11
|
import type {SchemaDefinition} from './editor/editor-schema'
|
|
11
12
|
import type {EditorSnapshot} from './editor/editor-snapshot'
|
|
@@ -51,6 +52,7 @@ export type EditorEvent =
|
|
|
51
52
|
* @public
|
|
52
53
|
*/
|
|
53
54
|
export type Editor = {
|
|
55
|
+
dom: EditorDom
|
|
54
56
|
getSnapshot: () => EditorSnapshot
|
|
55
57
|
/**
|
|
56
58
|
* @beta
|
package/src/index.ts
CHANGED
|
@@ -2,6 +2,7 @@ export type {Patch} from '@portabletext/patches'
|
|
|
2
2
|
export type {
|
|
3
3
|
PortableTextBlock,
|
|
4
4
|
PortableTextChild,
|
|
5
|
+
PortableTextObject,
|
|
5
6
|
PortableTextSpan,
|
|
6
7
|
} from '@sanity/types'
|
|
7
8
|
export type {Editor, EditorConfig, EditorEvent} from './editor'
|
|
@@ -79,3 +80,4 @@ export type {
|
|
|
79
80
|
ValueChange,
|
|
80
81
|
} from './types/editor'
|
|
81
82
|
export type {HotkeyOptions} from './types/options'
|
|
83
|
+
export type {AnnotationPath, BlockPath, ChildPath} from './types/paths'
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import {Editor, Element, Transforms} from 'slate'
|
|
2
|
+
import {toSlateRange} from '../internal-utils/ranges'
|
|
3
|
+
import type {BehaviorOperationImplementation} from './behavior.operations'
|
|
4
|
+
|
|
5
|
+
export const childSetOperationImplementation: BehaviorOperationImplementation<
|
|
6
|
+
'child.set'
|
|
7
|
+
> = ({context, operation}) => {
|
|
8
|
+
const location = toSlateRange(
|
|
9
|
+
{
|
|
10
|
+
anchor: {path: operation.at, offset: 0},
|
|
11
|
+
focus: {path: operation.at, offset: 0},
|
|
12
|
+
},
|
|
13
|
+
operation.editor,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
if (!location) {
|
|
17
|
+
throw new Error(
|
|
18
|
+
`Unable to convert ${JSON.stringify(operation.at)} into a Slate Range`,
|
|
19
|
+
)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const childEntry = Editor.node(operation.editor, location, {depth: 2})
|
|
23
|
+
const child = childEntry?.[0]
|
|
24
|
+
const childPath = childEntry?.[1]
|
|
25
|
+
|
|
26
|
+
if (!child || !childPath) {
|
|
27
|
+
throw new Error(`Unable to find child at ${JSON.stringify(operation.at)}`)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (operation.editor.isTextSpan(child)) {
|
|
31
|
+
const {_type, text, ...rest} = operation.props
|
|
32
|
+
|
|
33
|
+
Transforms.setNodes(
|
|
34
|
+
operation.editor,
|
|
35
|
+
{
|
|
36
|
+
...child,
|
|
37
|
+
...rest,
|
|
38
|
+
},
|
|
39
|
+
{at: childPath},
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
if (typeof text === 'string') {
|
|
43
|
+
if (child.text !== text) {
|
|
44
|
+
operation.editor.apply({
|
|
45
|
+
type: 'remove_text',
|
|
46
|
+
path: childPath,
|
|
47
|
+
offset: 0,
|
|
48
|
+
text: child.text,
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
operation.editor.apply({
|
|
52
|
+
type: 'insert_text',
|
|
53
|
+
path: childPath,
|
|
54
|
+
offset: 0,
|
|
55
|
+
text,
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (Element.isElement(child)) {
|
|
64
|
+
const definition = context.schema.inlineObjects.find(
|
|
65
|
+
(definition) => definition.name === child._type,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
if (!definition) {
|
|
69
|
+
throw new Error(
|
|
70
|
+
`Unable to find schema definition for Inline Object type ${child._type}`,
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const value =
|
|
75
|
+
'value' in child && typeof child.value === 'object' ? child.value : {}
|
|
76
|
+
const {_type, _key, ...rest} = operation.props
|
|
77
|
+
|
|
78
|
+
for (const prop in rest) {
|
|
79
|
+
if (!definition.fields.some((field) => field.name === prop)) {
|
|
80
|
+
delete rest[prop]
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
Transforms.setNodes(
|
|
85
|
+
operation.editor,
|
|
86
|
+
{
|
|
87
|
+
...child,
|
|
88
|
+
_key: typeof _key === 'string' ? _key : child._key,
|
|
89
|
+
value: {
|
|
90
|
+
...value,
|
|
91
|
+
...rest,
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
{at: childPath},
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
throw new Error(
|
|
101
|
+
`Unable to determine the type of child at ${JSON.stringify(operation.at)}`,
|
|
102
|
+
)
|
|
103
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import {applyAll} from '@portabletext/patches'
|
|
2
|
+
import {Editor, Element, Transforms} from 'slate'
|
|
3
|
+
import {toSlateRange} from '../internal-utils/ranges'
|
|
4
|
+
import type {BehaviorOperationImplementation} from './behavior.operations'
|
|
5
|
+
|
|
6
|
+
export const childUnsetOperationImplementation: BehaviorOperationImplementation<
|
|
7
|
+
'child.unset'
|
|
8
|
+
> = ({context, operation}) => {
|
|
9
|
+
const location = toSlateRange(
|
|
10
|
+
{
|
|
11
|
+
anchor: {path: operation.at, offset: 0},
|
|
12
|
+
focus: {path: operation.at, offset: 0},
|
|
13
|
+
},
|
|
14
|
+
operation.editor,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
if (!location) {
|
|
18
|
+
throw new Error(
|
|
19
|
+
`Unable to convert ${JSON.stringify(operation.at)} into a Slate Range`,
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const childEntry = Editor.node(operation.editor, location, {depth: 2})
|
|
24
|
+
const child = childEntry?.[0]
|
|
25
|
+
const childPath = childEntry?.[1]
|
|
26
|
+
|
|
27
|
+
if (!child || !childPath) {
|
|
28
|
+
throw new Error(`Unable to find child at ${JSON.stringify(operation.at)}`)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (operation.editor.isTextSpan(child)) {
|
|
32
|
+
if (operation.props.includes('text')) {
|
|
33
|
+
operation.editor.apply({
|
|
34
|
+
type: 'remove_text',
|
|
35
|
+
path: childPath,
|
|
36
|
+
offset: 0,
|
|
37
|
+
text: child.text,
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const newNode: Record<string, unknown> = {}
|
|
42
|
+
|
|
43
|
+
for (const prop of operation.props) {
|
|
44
|
+
if (prop === '_type') {
|
|
45
|
+
// It's not allowed to unset the _type of a span
|
|
46
|
+
continue
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (prop === '_key') {
|
|
50
|
+
newNode._key = context.keyGenerator()
|
|
51
|
+
continue
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
newNode[prop] = null
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
Transforms.setNodes(operation.editor, newNode, {at: childPath})
|
|
58
|
+
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (Element.isElement(child)) {
|
|
63
|
+
const value =
|
|
64
|
+
'value' in child && typeof child.value === 'object' ? child.value : {}
|
|
65
|
+
const patches = operation.props.map((prop) => ({
|
|
66
|
+
type: 'unset' as const,
|
|
67
|
+
path: [prop],
|
|
68
|
+
}))
|
|
69
|
+
const newValue = applyAll(value, patches)
|
|
70
|
+
|
|
71
|
+
Transforms.setNodes(
|
|
72
|
+
operation.editor,
|
|
73
|
+
{
|
|
74
|
+
...child,
|
|
75
|
+
_key: operation.props.includes('_key')
|
|
76
|
+
? context.keyGenerator()
|
|
77
|
+
: child._key,
|
|
78
|
+
value: newValue,
|
|
79
|
+
},
|
|
80
|
+
{at: childPath},
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
return
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
throw new Error(
|
|
87
|
+
`Unable to determine the type of child at ${JSON.stringify(operation.at)}`,
|
|
88
|
+
)
|
|
89
|
+
}
|
|
@@ -14,6 +14,8 @@ import {addAnnotationOperationImplementation} from './behavior.operation.annotat
|
|
|
14
14
|
import {removeAnnotationOperationImplementation} from './behavior.operation.annotation.remove'
|
|
15
15
|
import {blockSetOperationImplementation} from './behavior.operation.block.set'
|
|
16
16
|
import {blockUnsetOperationImplementation} from './behavior.operation.block.unset'
|
|
17
|
+
import {childSetOperationImplementation} from './behavior.operation.child.set'
|
|
18
|
+
import {childUnsetOperationImplementation} from './behavior.operation.child.unset'
|
|
17
19
|
import {decoratorAddOperationImplementation} from './behavior.operation.decorator.add'
|
|
18
20
|
import {deleteOperationImplementation} from './behavior.operation.delete'
|
|
19
21
|
import {insertInlineObjectOperationImplementation} from './behavior.operation.insert-inline-object'
|
|
@@ -58,6 +60,8 @@ const behaviorOperationImplementations: BehaviorOperationImplementations = {
|
|
|
58
60
|
'annotation.remove': removeAnnotationOperationImplementation,
|
|
59
61
|
'block.set': blockSetOperationImplementation,
|
|
60
62
|
'block.unset': blockUnsetOperationImplementation,
|
|
63
|
+
'child.set': childSetOperationImplementation,
|
|
64
|
+
'child.unset': childUnsetOperationImplementation,
|
|
61
65
|
'decorator.add': decoratorAddOperationImplementation,
|
|
62
66
|
'decorator.remove': removeDecoratorOperationImplementation,
|
|
63
67
|
'delete': deleteOperationImplementation,
|
|
@@ -110,6 +114,20 @@ export function performOperation({
|
|
|
110
114
|
})
|
|
111
115
|
break
|
|
112
116
|
}
|
|
117
|
+
case 'child.set': {
|
|
118
|
+
behaviorOperationImplementations['child.set']({
|
|
119
|
+
context,
|
|
120
|
+
operation: operation,
|
|
121
|
+
})
|
|
122
|
+
break
|
|
123
|
+
}
|
|
124
|
+
case 'child.unset': {
|
|
125
|
+
behaviorOperationImplementations['child.unset']({
|
|
126
|
+
context,
|
|
127
|
+
operation: operation,
|
|
128
|
+
})
|
|
129
|
+
break
|
|
130
|
+
}
|
|
113
131
|
case 'decorator.add': {
|
|
114
132
|
behaviorOperationImplementations['decorator.add']({
|
|
115
133
|
context,
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {PortableTextBlock} from '@sanity/types'
|
|
2
2
|
import type {EditorSelector} from '../editor/editor-selector'
|
|
3
3
|
import {getBlockKeyFromSelectionPoint} from '../selection/selection-point'
|
|
4
|
+
import type {BlockPath} from '../types/paths'
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* @public
|
|
7
8
|
*/
|
|
8
9
|
export const getAnchorBlock: EditorSelector<
|
|
9
|
-
{node: PortableTextBlock; path:
|
|
10
|
+
{node: PortableTextBlock; path: BlockPath} | undefined
|
|
10
11
|
> = (snapshot) => {
|
|
11
12
|
if (!snapshot.context.selection) {
|
|
12
13
|
return undefined
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type {KeyedSegment} from '@portabletext/patches'
|
|
2
1
|
import type {PortableTextObject, PortableTextSpan} from '@sanity/types'
|
|
3
2
|
import type {EditorSelector} from '../editor/editor-selector'
|
|
4
3
|
import {getChildKeyFromSelectionPoint} from '../selection/selection-point'
|
|
4
|
+
import type {ChildPath} from '../types/paths'
|
|
5
5
|
import {getAnchorTextBlock} from './selector.get-anchor-text-block'
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -10,7 +10,7 @@ import {getAnchorTextBlock} from './selector.get-anchor-text-block'
|
|
|
10
10
|
export const getAnchorChild: EditorSelector<
|
|
11
11
|
| {
|
|
12
12
|
node: PortableTextObject | PortableTextSpan
|
|
13
|
-
path:
|
|
13
|
+
path: ChildPath
|
|
14
14
|
}
|
|
15
15
|
| undefined
|
|
16
16
|
> = (snapshot) => {
|
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import type {KeyedSegment} from '@portabletext/patches'
|
|
2
1
|
import {isPortableTextSpan, type PortableTextSpan} from '@sanity/types'
|
|
3
2
|
import type {EditorSelector} from '../editor/editor-selector'
|
|
3
|
+
import type {ChildPath} from '../types/paths'
|
|
4
4
|
import {getAnchorChild} from './selector.get-anchor-child'
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* @public
|
|
8
8
|
*/
|
|
9
9
|
export const getAnchorSpan: EditorSelector<
|
|
10
|
-
|
|
11
|
-
| undefined
|
|
10
|
+
{node: PortableTextSpan; path: ChildPath} | undefined
|
|
12
11
|
> = (snapshot) => {
|
|
13
12
|
const anchorChild = getAnchorChild(snapshot)
|
|
14
13
|
|