@portabletext/editor 3.0.9 → 3.1.1
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 +16 -10
- package/lib/index.js +176 -262
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/behaviors/behavior.types.event.ts +1 -1
- package/src/editor/PortableTextEditor.tsx +14 -0
- package/src/editor/create-slate-editor.tsx +1 -8
- package/src/editor/plugins/createWithEditableAPI.ts +18 -83
- package/src/editor/plugins/createWithPatches.ts +3 -1
- package/src/editor/plugins/createWithPortableTextMarkModel.ts +14 -0
- package/src/editor/plugins/with-plugins.ts +8 -19
- package/src/editor/range-decorations-machine.ts +13 -3
- package/src/editor/sync-machine.ts +0 -18
- package/src/internal-utils/applyPatch.ts +31 -41
- package/src/internal-utils/values.ts +22 -0
- package/src/operations/behavior.operation.delete.ts +17 -4
- package/src/operations/behavior.operation.insert.block.ts +5 -0
- package/src/types/editor.ts +0 -9
- package/src/editor/plugins/createWithPlaceholderBlock.ts +0 -67
- package/src/editor/plugins/createWithUtils.ts +0 -52
package/package.json
CHANGED
|
@@ -145,7 +145,7 @@ export type SyntheticBehaviorEvent =
|
|
|
145
145
|
/**
|
|
146
146
|
* Defaults to character deletion.
|
|
147
147
|
*/
|
|
148
|
-
unit?: 'character' | 'word' | 'line' | 'block'
|
|
148
|
+
unit?: 'character' | 'word' | 'line' | 'block' | 'child'
|
|
149
149
|
}
|
|
150
150
|
| {
|
|
151
151
|
type: StrictExtract<SyntheticBehaviorEventType, 'history.redo'>
|
|
@@ -350,6 +350,20 @@ export class PortableTextEditor extends Component<
|
|
|
350
350
|
editor.editable?.blur()
|
|
351
351
|
}
|
|
352
352
|
|
|
353
|
+
/**
|
|
354
|
+
* @deprecated
|
|
355
|
+
* Use `editor.send(...)` instead
|
|
356
|
+
*
|
|
357
|
+
* ```
|
|
358
|
+
* const editor = useEditor()
|
|
359
|
+
* editor.send({
|
|
360
|
+
* type: 'delete',
|
|
361
|
+
* at: {...},
|
|
362
|
+
* direction: '...',
|
|
363
|
+
* unit: '...',
|
|
364
|
+
* })
|
|
365
|
+
* ```
|
|
366
|
+
*/
|
|
353
367
|
static delete = (
|
|
354
368
|
editor: PortableTextEditor,
|
|
355
369
|
selection: EditorSelection,
|
|
@@ -3,7 +3,6 @@ import {withReact} from 'slate-react'
|
|
|
3
3
|
import {buildIndexMaps} from '../internal-utils/build-index-maps'
|
|
4
4
|
import {createPlaceholderBlock} from '../internal-utils/create-placeholder-block'
|
|
5
5
|
import {debugWithName} from '../internal-utils/debug'
|
|
6
|
-
import {toSlateBlock} from '../internal-utils/values'
|
|
7
6
|
import type {PortableTextSlateEditor} from '../types/editor'
|
|
8
7
|
import type {EditorActor} from './editor-machine'
|
|
9
8
|
import {withPlugins} from './plugins/with-plugins'
|
|
@@ -56,15 +55,9 @@ export function createSlateEditor(config: SlateEditorConfig): SlateEditor {
|
|
|
56
55
|
},
|
|
57
56
|
)
|
|
58
57
|
|
|
59
|
-
const initialValue = [
|
|
60
|
-
toSlateBlock(placeholderBlock, {
|
|
61
|
-
schemaTypes: config.editorActor.getSnapshot().context.schema,
|
|
62
|
-
}),
|
|
63
|
-
]
|
|
64
|
-
|
|
65
58
|
const slateEditor: SlateEditor = {
|
|
66
59
|
instance,
|
|
67
|
-
initialValue,
|
|
60
|
+
initialValue: [placeholderBlock],
|
|
68
61
|
}
|
|
69
62
|
|
|
70
63
|
return slateEditor
|
|
@@ -5,12 +5,9 @@ import type {
|
|
|
5
5
|
PortableTextChild,
|
|
6
6
|
PortableTextObject,
|
|
7
7
|
} from '@sanity/types'
|
|
8
|
-
import {Editor, Range,
|
|
8
|
+
import {Editor, Range, Text, Transforms} from 'slate'
|
|
9
9
|
import type {DOMNode} from 'slate-dom'
|
|
10
10
|
import {ReactEditor} from 'slate-react'
|
|
11
|
-
import {buildIndexMaps} from '../../internal-utils/build-index-maps'
|
|
12
|
-
import {createPlaceholderBlock} from '../../internal-utils/create-placeholder-block'
|
|
13
|
-
import {debugWithName} from '../../internal-utils/debug'
|
|
14
11
|
import {
|
|
15
12
|
isListItemActive,
|
|
16
13
|
isStyleActive,
|
|
@@ -37,8 +34,6 @@ import type {EditorActor} from '../editor-machine'
|
|
|
37
34
|
import {getEditorSnapshot} from '../editor-selector'
|
|
38
35
|
import {SLATE_TO_PORTABLE_TEXT_RANGE} from '../weakMaps'
|
|
39
36
|
|
|
40
|
-
const debug = debugWithName('API:editable')
|
|
41
|
-
|
|
42
37
|
export function createEditableAPI(
|
|
43
38
|
editor: PortableTextSlateEditor,
|
|
44
39
|
editorActor: EditorActor,
|
|
@@ -431,84 +426,24 @@ export function createEditableAPI(
|
|
|
431
426
|
selection: EditorSelection,
|
|
432
427
|
options?: EditableAPIDeleteOptions,
|
|
433
428
|
): void => {
|
|
434
|
-
if (selection) {
|
|
435
|
-
|
|
436
|
-
context: {
|
|
437
|
-
schema: editorActor.getSnapshot().context.schema,
|
|
438
|
-
value: editor.value,
|
|
439
|
-
selection,
|
|
440
|
-
},
|
|
441
|
-
blockIndexMap: editor.blockIndexMap,
|
|
442
|
-
})
|
|
443
|
-
const hasRange =
|
|
444
|
-
range && range.anchor.path.length > 0 && range.focus.path.length > 0
|
|
445
|
-
if (!hasRange) {
|
|
446
|
-
throw new Error('Invalid range')
|
|
447
|
-
}
|
|
448
|
-
if (range) {
|
|
449
|
-
if (!options?.mode || options?.mode === 'selected') {
|
|
450
|
-
debug(`Deleting content in selection`)
|
|
451
|
-
Transforms.delete(editor, {
|
|
452
|
-
at: range,
|
|
453
|
-
hanging: true,
|
|
454
|
-
voids: true,
|
|
455
|
-
})
|
|
456
|
-
editor.onChange()
|
|
457
|
-
return
|
|
458
|
-
}
|
|
459
|
-
if (options?.mode === 'blocks') {
|
|
460
|
-
debug(`Deleting blocks touched by selection`)
|
|
461
|
-
Transforms.removeNodes(editor, {
|
|
462
|
-
at: range,
|
|
463
|
-
voids: true,
|
|
464
|
-
match: (node) => {
|
|
465
|
-
return (
|
|
466
|
-
editor.isTextBlock(node) ||
|
|
467
|
-
(!editor.isTextBlock(node) && SlateElement.isElement(node))
|
|
468
|
-
)
|
|
469
|
-
},
|
|
470
|
-
})
|
|
471
|
-
}
|
|
472
|
-
if (options?.mode === 'children') {
|
|
473
|
-
debug(`Deleting children touched by selection`)
|
|
474
|
-
Transforms.removeNodes(editor, {
|
|
475
|
-
at: range,
|
|
476
|
-
voids: true,
|
|
477
|
-
match: (node) => {
|
|
478
|
-
return (
|
|
479
|
-
node._type === types.span.name || // Text children
|
|
480
|
-
(!editor.isTextBlock(node) && SlateElement.isElement(node)) // inline blocks
|
|
481
|
-
)
|
|
482
|
-
},
|
|
483
|
-
})
|
|
484
|
-
}
|
|
485
|
-
// If the editor was emptied, insert a placeholder block
|
|
486
|
-
// directly into the editor's children. We don't want to do this
|
|
487
|
-
// through a Transform (because that would trigger a change event
|
|
488
|
-
// that would insert the placeholder into the actual value
|
|
489
|
-
// which should remain empty)
|
|
490
|
-
if (editor.children.length === 0) {
|
|
491
|
-
const placeholderBlock = createPlaceholderBlock(
|
|
492
|
-
editorActor.getSnapshot().context,
|
|
493
|
-
)
|
|
494
|
-
editor.children = [placeholderBlock]
|
|
495
|
-
editor.value = [placeholderBlock]
|
|
496
|
-
|
|
497
|
-
buildIndexMaps(
|
|
498
|
-
{
|
|
499
|
-
schema: editorActor.getSnapshot().context.schema,
|
|
500
|
-
value: editor.value,
|
|
501
|
-
},
|
|
502
|
-
{
|
|
503
|
-
blockIndexMap: editor.blockIndexMap,
|
|
504
|
-
listIndexMap: editor.listIndexMap,
|
|
505
|
-
},
|
|
506
|
-
)
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
editor.onChange()
|
|
510
|
-
}
|
|
429
|
+
if (!selection) {
|
|
430
|
+
return
|
|
511
431
|
}
|
|
432
|
+
|
|
433
|
+
editorActor.send({
|
|
434
|
+
type: 'behavior event',
|
|
435
|
+
behaviorEvent: {
|
|
436
|
+
type: 'delete',
|
|
437
|
+
at: selection,
|
|
438
|
+
unit:
|
|
439
|
+
options?.mode === 'blocks'
|
|
440
|
+
? 'block'
|
|
441
|
+
: options?.mode === 'children'
|
|
442
|
+
? 'child'
|
|
443
|
+
: undefined,
|
|
444
|
+
},
|
|
445
|
+
editor,
|
|
446
|
+
})
|
|
512
447
|
},
|
|
513
448
|
removeAnnotation: <TSchemaType extends {name: string}>(
|
|
514
449
|
type: TSchemaType,
|
|
@@ -41,7 +41,7 @@ export function createWithPatches({
|
|
|
41
41
|
// The editor.value would no longer contain that information if the node is already deleted.
|
|
42
42
|
let previousValue: PortableTextBlock[]
|
|
43
43
|
|
|
44
|
-
const applyPatch = createApplyPatch(editorActor.getSnapshot().context
|
|
44
|
+
const applyPatch = createApplyPatch(editorActor.getSnapshot().context)
|
|
45
45
|
|
|
46
46
|
return function withPatches(editor: PortableTextSlateEditor) {
|
|
47
47
|
IS_PROCESSING_REMOTE_CHANGES.set(editor, false)
|
|
@@ -110,6 +110,7 @@ export function createWithPatches({
|
|
|
110
110
|
previousValue = editor.value
|
|
111
111
|
|
|
112
112
|
const editorWasEmpty = isEqualToEmptyEditor(
|
|
113
|
+
editorActor.getSnapshot().context.initialValue,
|
|
113
114
|
previousValue,
|
|
114
115
|
editorActor.getSnapshot().context.schema,
|
|
115
116
|
)
|
|
@@ -118,6 +119,7 @@ export function createWithPatches({
|
|
|
118
119
|
apply(operation)
|
|
119
120
|
|
|
120
121
|
const editorIsEmpty = isEqualToEmptyEditor(
|
|
122
|
+
editorActor.getSnapshot().context.initialValue,
|
|
121
123
|
editor.value,
|
|
122
124
|
editorActor.getSnapshot().context.schema,
|
|
123
125
|
)
|
|
@@ -10,6 +10,7 @@ import {isEqual, uniq} from 'lodash'
|
|
|
10
10
|
import {Editor, Element, Node, Path, Range, Text, Transforms} from 'slate'
|
|
11
11
|
import {isRedoing} from '../../history/slate-plugin.redoing'
|
|
12
12
|
import {isUndoing} from '../../history/slate-plugin.undoing'
|
|
13
|
+
import {createPlaceholderBlock} from '../../internal-utils/create-placeholder-block'
|
|
13
14
|
import {debugWithName} from '../../internal-utils/debug'
|
|
14
15
|
import {getNextSpan, getPreviousSpan} from '../../internal-utils/sibling-utils'
|
|
15
16
|
import type {BehaviorOperationImplementation} from '../../operations/behavior.operations'
|
|
@@ -19,6 +20,7 @@ import type {EditorActor} from '../editor-machine'
|
|
|
19
20
|
import {getEditorSnapshot} from '../editor-selector'
|
|
20
21
|
import {withNormalizeNode} from '../with-normalizing-node'
|
|
21
22
|
import {isChangingRemotely} from '../withChanges'
|
|
23
|
+
import {withoutPatching} from '../withoutPatching'
|
|
22
24
|
|
|
23
25
|
const debug = debugWithName('plugin:withPortableTextMarkModel')
|
|
24
26
|
|
|
@@ -38,6 +40,18 @@ export function createWithPortableTextMarkModel(
|
|
|
38
40
|
editor.normalizeNode = (nodeEntry) => {
|
|
39
41
|
const [node, path] = nodeEntry
|
|
40
42
|
|
|
43
|
+
if (Editor.isEditor(node) && node.children.length === 0) {
|
|
44
|
+
withoutPatching(editor, () => {
|
|
45
|
+
withNormalizeNode(editor, () => {
|
|
46
|
+
Transforms.insertNodes(
|
|
47
|
+
editor,
|
|
48
|
+
createPlaceholderBlock(editorActor.getSnapshot().context),
|
|
49
|
+
{at: [0], select: true},
|
|
50
|
+
)
|
|
51
|
+
})
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
|
|
41
55
|
if (editor.isTextBlock(node)) {
|
|
42
56
|
const children = Node.children(editor, path)
|
|
43
57
|
|
|
@@ -6,10 +6,8 @@ import type {RelayActor} from '../relay-machine'
|
|
|
6
6
|
import {createWithEventListeners} from './create-with-event-listeners'
|
|
7
7
|
import {createWithObjectKeys} from './createWithObjectKeys'
|
|
8
8
|
import {createWithPatches} from './createWithPatches'
|
|
9
|
-
import {createWithPlaceholderBlock} from './createWithPlaceholderBlock'
|
|
10
9
|
import {createWithPortableTextMarkModel} from './createWithPortableTextMarkModel'
|
|
11
10
|
import {createWithSchemaTypes} from './createWithSchemaTypes'
|
|
12
|
-
import {createWithUtils} from './createWithUtils'
|
|
13
11
|
import {pluginUpdateSelection} from './slate-plugin.update-selection'
|
|
14
12
|
import {pluginUpdateValue} from './slate-plugin.update-value'
|
|
15
13
|
|
|
@@ -46,11 +44,6 @@ export const withPlugins = <T extends Editor>(
|
|
|
46
44
|
})
|
|
47
45
|
const withPortableTextMarkModel = createWithPortableTextMarkModel(editorActor)
|
|
48
46
|
|
|
49
|
-
const withPlaceholderBlock = createWithPlaceholderBlock(editorActor)
|
|
50
|
-
|
|
51
|
-
const withUtils = createWithUtils({
|
|
52
|
-
editorActor,
|
|
53
|
-
})
|
|
54
47
|
const withEventListeners = createWithEventListeners(editorActor)
|
|
55
48
|
|
|
56
49
|
// Ordering is important here, selection dealing last, data manipulation in the middle and core model stuff first.
|
|
@@ -58,18 +51,14 @@ export const withPlugins = <T extends Editor>(
|
|
|
58
51
|
withSchemaTypes(
|
|
59
52
|
withObjectKeys(
|
|
60
53
|
withPortableTextMarkModel(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
editor: e,
|
|
70
|
-
}),
|
|
71
|
-
),
|
|
72
|
-
),
|
|
54
|
+
withUndoRedo(
|
|
55
|
+
withPatches(
|
|
56
|
+
pluginUpdateValue(
|
|
57
|
+
editorActor.getSnapshot().context,
|
|
58
|
+
pluginUpdateSelection({
|
|
59
|
+
editorActor,
|
|
60
|
+
editor: e,
|
|
61
|
+
}),
|
|
73
62
|
),
|
|
74
63
|
),
|
|
75
64
|
),
|
|
@@ -18,8 +18,8 @@ import {
|
|
|
18
18
|
import {moveRangeByOperation} from '../internal-utils/move-range-by-operation'
|
|
19
19
|
import {slateRangeToSelection} from '../internal-utils/slate-utils'
|
|
20
20
|
import {toSlateRange} from '../internal-utils/to-slate-range'
|
|
21
|
-
import {isEqualToEmptyEditor} from '../internal-utils/values'
|
|
22
21
|
import type {PortableTextSlateEditor, RangeDecoration} from '../types/editor'
|
|
22
|
+
import {isEmptyTextBlock} from '../utils'
|
|
23
23
|
import type {EditorSchema} from './editor-schema'
|
|
24
24
|
|
|
25
25
|
const slateOperationCallback: CallbackLogicFunction<
|
|
@@ -282,7 +282,9 @@ export const rangeDecorationsMachine = setup({
|
|
|
282
282
|
skipSetup: input.skipSetup,
|
|
283
283
|
schema: input.schema,
|
|
284
284
|
slateEditor: input.slateEditor,
|
|
285
|
-
decorate: {
|
|
285
|
+
decorate: {
|
|
286
|
+
fn: createDecorate(input.schema, input.slateEditor),
|
|
287
|
+
},
|
|
286
288
|
}),
|
|
287
289
|
invoke: {
|
|
288
290
|
src: 'slate operation listener',
|
|
@@ -357,7 +359,15 @@ function createDecorate(
|
|
|
357
359
|
slateEditor: PortableTextSlateEditor,
|
|
358
360
|
) {
|
|
359
361
|
return function decorate([node, path]: NodeEntry): Array<BaseRange> {
|
|
360
|
-
|
|
362
|
+
const defaultStyle = schema.styles.at(0)?.name
|
|
363
|
+
const editorOnlyContainsEmptyParagraph =
|
|
364
|
+
slateEditor.value.length === 1 &&
|
|
365
|
+
isEmptyTextBlock({schema}, slateEditor.value[0]) &&
|
|
366
|
+
(!slateEditor.value[0].style ||
|
|
367
|
+
slateEditor.value[0].style === defaultStyle) &&
|
|
368
|
+
!slateEditor.value[0].listItem
|
|
369
|
+
|
|
370
|
+
if (editorOnlyContainsEmptyParagraph) {
|
|
361
371
|
return [
|
|
362
372
|
{
|
|
363
373
|
anchor: {
|
|
@@ -426,7 +426,6 @@ async function updateValue({
|
|
|
426
426
|
clearEditor({
|
|
427
427
|
slateEditor,
|
|
428
428
|
doneSyncing,
|
|
429
|
-
hadSelection,
|
|
430
429
|
})
|
|
431
430
|
|
|
432
431
|
isChanged = true
|
|
@@ -575,11 +574,9 @@ async function* getStreamedBlocks({value}: {value: Array<PortableTextBlock>}) {
|
|
|
575
574
|
function clearEditor({
|
|
576
575
|
slateEditor,
|
|
577
576
|
doneSyncing,
|
|
578
|
-
hadSelection,
|
|
579
577
|
}: {
|
|
580
578
|
slateEditor: PortableTextSlateEditor
|
|
581
579
|
doneSyncing: boolean
|
|
582
|
-
hadSelection: boolean
|
|
583
580
|
}) {
|
|
584
581
|
Editor.withoutNormalizing(slateEditor, () => {
|
|
585
582
|
pluginWithoutHistory(slateEditor, () => {
|
|
@@ -589,10 +586,6 @@ function clearEditor({
|
|
|
589
586
|
return
|
|
590
587
|
}
|
|
591
588
|
|
|
592
|
-
if (hadSelection) {
|
|
593
|
-
Transforms.deselect(slateEditor)
|
|
594
|
-
}
|
|
595
|
-
|
|
596
589
|
const childrenLength = slateEditor.children.length
|
|
597
590
|
|
|
598
591
|
slateEditor.children.forEach((_, index) => {
|
|
@@ -600,17 +593,6 @@ function clearEditor({
|
|
|
600
593
|
at: [childrenLength - 1 - index],
|
|
601
594
|
})
|
|
602
595
|
})
|
|
603
|
-
|
|
604
|
-
Transforms.insertNodes(
|
|
605
|
-
slateEditor,
|
|
606
|
-
slateEditor.pteCreateTextBlock({decorators: []}),
|
|
607
|
-
{at: [0]},
|
|
608
|
-
)
|
|
609
|
-
|
|
610
|
-
// Add a new selection in the top of the document
|
|
611
|
-
if (hadSelection) {
|
|
612
|
-
Transforms.select(slateEditor, [0, 0])
|
|
613
|
-
}
|
|
614
596
|
})
|
|
615
597
|
})
|
|
616
598
|
})
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
} from '@sanity/diff-match-patch'
|
|
18
18
|
import type {Path, PortableTextBlock, PortableTextChild} from '@sanity/types'
|
|
19
19
|
import {Editor, Element, Node, Text, Transforms, type Descendant} from 'slate'
|
|
20
|
-
import type {
|
|
20
|
+
import type {EditorContext} from '../editor/editor-snapshot'
|
|
21
21
|
import {KEY_TO_SLATE_ELEMENT} from '../editor/weakMaps'
|
|
22
22
|
import type {PortableTextSlateEditor} from '../types/editor'
|
|
23
23
|
import {isKeyedSegment} from '../utils/util.is-keyed-segment'
|
|
@@ -27,7 +27,9 @@ import {isEqualToEmptyEditor, toSlateBlock} from './values'
|
|
|
27
27
|
* Creates a function that can apply a patch onto a PortableTextSlateEditor.
|
|
28
28
|
*/
|
|
29
29
|
export function createApplyPatch(
|
|
30
|
-
|
|
30
|
+
context: Pick<EditorContext, 'schema' | 'keyGenerator'> & {
|
|
31
|
+
initialValue: Array<PortableTextBlock> | undefined
|
|
32
|
+
},
|
|
31
33
|
): (editor: PortableTextSlateEditor, patch: Patch) => boolean {
|
|
32
34
|
return (editor: PortableTextSlateEditor, patch: Patch): boolean => {
|
|
33
35
|
let changed = false
|
|
@@ -35,7 +37,7 @@ export function createApplyPatch(
|
|
|
35
37
|
try {
|
|
36
38
|
switch (patch.type) {
|
|
37
39
|
case 'insert':
|
|
38
|
-
changed = insertPatch(editor, patch
|
|
40
|
+
changed = insertPatch(context, editor, patch)
|
|
39
41
|
break
|
|
40
42
|
case 'unset':
|
|
41
43
|
changed = unsetPatch(editor, patch)
|
|
@@ -117,13 +119,31 @@ function diffMatchPatch(
|
|
|
117
119
|
}
|
|
118
120
|
|
|
119
121
|
function insertPatch(
|
|
122
|
+
context: Pick<EditorContext, 'schema' | 'keyGenerator'> & {
|
|
123
|
+
initialValue: Array<PortableTextBlock> | undefined
|
|
124
|
+
},
|
|
120
125
|
editor: PortableTextSlateEditor,
|
|
121
126
|
patch: InsertPatch,
|
|
122
|
-
schema: EditorSchema,
|
|
123
127
|
) {
|
|
124
128
|
const block = findBlock(editor.children, patch.path)
|
|
125
129
|
|
|
126
130
|
if (!block) {
|
|
131
|
+
if (patch.path.length === 1 && patch.path[0] === 0) {
|
|
132
|
+
const blocksToInsert = patch.items.map((item) =>
|
|
133
|
+
toSlateBlock(
|
|
134
|
+
item as PortableTextBlock,
|
|
135
|
+
{schemaTypes: context.schema},
|
|
136
|
+
KEY_TO_SLATE_ELEMENT.get(editor),
|
|
137
|
+
),
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
Transforms.insertNodes(editor, blocksToInsert, {
|
|
141
|
+
at: [0],
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
return true
|
|
145
|
+
}
|
|
146
|
+
|
|
127
147
|
return false
|
|
128
148
|
}
|
|
129
149
|
|
|
@@ -137,7 +157,7 @@ function insertPatch(
|
|
|
137
157
|
const blocksToInsert = items.map((item) =>
|
|
138
158
|
toSlateBlock(
|
|
139
159
|
item as PortableTextBlock,
|
|
140
|
-
{schemaTypes: schema},
|
|
160
|
+
{schemaTypes: context.schema},
|
|
141
161
|
KEY_TO_SLATE_ELEMENT.get(editor),
|
|
142
162
|
),
|
|
143
163
|
)
|
|
@@ -145,7 +165,11 @@ function insertPatch(
|
|
|
145
165
|
const normalizedIdx =
|
|
146
166
|
position === 'after' ? targetBlockIndex + 1 : targetBlockIndex
|
|
147
167
|
|
|
148
|
-
const editorWasEmptyBefore = isEqualToEmptyEditor(
|
|
168
|
+
const editorWasEmptyBefore = isEqualToEmptyEditor(
|
|
169
|
+
context.initialValue,
|
|
170
|
+
editor.value,
|
|
171
|
+
context.schema,
|
|
172
|
+
)
|
|
149
173
|
|
|
150
174
|
Transforms.insertNodes(editor, blocksToInsert, {at: [normalizedIdx]})
|
|
151
175
|
|
|
@@ -177,7 +201,7 @@ function insertPatch(
|
|
|
177
201
|
|
|
178
202
|
const childrenToInsert = toSlateBlock(
|
|
179
203
|
{...block.node, children: items as PortableTextChild[]},
|
|
180
|
-
{schemaTypes: schema},
|
|
204
|
+
{schemaTypes: context.schema},
|
|
181
205
|
KEY_TO_SLATE_ELEMENT.get(editor),
|
|
182
206
|
)
|
|
183
207
|
const normalizedIdx =
|
|
@@ -417,7 +441,6 @@ function setPatch(editor: PortableTextSlateEditor, patch: SetPatch) {
|
|
|
417
441
|
function unsetPatch(editor: PortableTextSlateEditor, patch: UnsetPatch) {
|
|
418
442
|
// Value
|
|
419
443
|
if (patch.path.length === 0) {
|
|
420
|
-
const previousSelection = editor.selection
|
|
421
444
|
Transforms.deselect(editor)
|
|
422
445
|
|
|
423
446
|
const children = Node.children(editor, [], {
|
|
@@ -428,15 +451,6 @@ function unsetPatch(editor: PortableTextSlateEditor, patch: UnsetPatch) {
|
|
|
428
451
|
Transforms.removeNodes(editor, {at: path})
|
|
429
452
|
}
|
|
430
453
|
|
|
431
|
-
Transforms.insertNodes(editor, editor.pteCreateTextBlock({decorators: []}))
|
|
432
|
-
if (previousSelection) {
|
|
433
|
-
Transforms.select(editor, {
|
|
434
|
-
anchor: {path: [0, 0], offset: 0},
|
|
435
|
-
focus: {path: [0, 0], offset: 0},
|
|
436
|
-
})
|
|
437
|
-
}
|
|
438
|
-
// call OnChange here to emit the new selection
|
|
439
|
-
editor.onChange()
|
|
440
454
|
return true
|
|
441
455
|
}
|
|
442
456
|
|
|
@@ -448,30 +462,6 @@ function unsetPatch(editor: PortableTextSlateEditor, patch: UnsetPatch) {
|
|
|
448
462
|
|
|
449
463
|
// Single blocks
|
|
450
464
|
if (patch.path.length === 1) {
|
|
451
|
-
if (editor.children.length === 1) {
|
|
452
|
-
// `unset`ing the last block should be treated similar to `unset`ing the
|
|
453
|
-
// entire editor value
|
|
454
|
-
const previousSelection = editor.selection
|
|
455
|
-
|
|
456
|
-
Transforms.deselect(editor)
|
|
457
|
-
Transforms.removeNodes(editor, {at: [block.index]})
|
|
458
|
-
Transforms.insertNodes(
|
|
459
|
-
editor,
|
|
460
|
-
editor.pteCreateTextBlock({decorators: []}),
|
|
461
|
-
)
|
|
462
|
-
|
|
463
|
-
if (previousSelection) {
|
|
464
|
-
Transforms.select(editor, {
|
|
465
|
-
anchor: {path: [0, 0], offset: 0},
|
|
466
|
-
focus: {path: [0, 0], offset: 0},
|
|
467
|
-
})
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
editor.onChange()
|
|
471
|
-
|
|
472
|
-
return true
|
|
473
|
-
}
|
|
474
|
-
|
|
475
465
|
Transforms.removeNodes(editor, {at: [block.index]})
|
|
476
466
|
|
|
477
467
|
return true
|
|
@@ -169,9 +169,14 @@ export function fromSlateBlock(
|
|
|
169
169
|
}
|
|
170
170
|
|
|
171
171
|
export function isEqualToEmptyEditor(
|
|
172
|
+
initialValue: Array<PortableTextBlock> | undefined,
|
|
172
173
|
blocks: Array<Descendant> | Array<PortableTextBlock>,
|
|
173
174
|
schemaTypes: EditorSchema,
|
|
174
175
|
): boolean {
|
|
176
|
+
if (!blocks) {
|
|
177
|
+
return false
|
|
178
|
+
}
|
|
179
|
+
|
|
175
180
|
// Must have exactly one block
|
|
176
181
|
if (blocks.length !== 1) {
|
|
177
182
|
return false
|
|
@@ -240,5 +245,22 @@ export function isEqualToEmptyEditor(
|
|
|
240
245
|
return false
|
|
241
246
|
}
|
|
242
247
|
|
|
248
|
+
if (
|
|
249
|
+
Object.keys(firstBlock).some(
|
|
250
|
+
(key) =>
|
|
251
|
+
key !== '_type' &&
|
|
252
|
+
key !== '_key' &&
|
|
253
|
+
key !== 'children' &&
|
|
254
|
+
key !== 'markDefs' &&
|
|
255
|
+
key !== 'style',
|
|
256
|
+
)
|
|
257
|
+
) {
|
|
258
|
+
return false
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (isEqual(initialValue, [firstBlock])) {
|
|
262
|
+
return false
|
|
263
|
+
}
|
|
264
|
+
|
|
243
265
|
return true
|
|
244
266
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {isTextBlock} from '@portabletext/schema'
|
|
1
|
+
import {isSpan, isTextBlock} from '@portabletext/schema'
|
|
2
2
|
import {deleteText, Editor, Element, Range, Transforms} from 'slate'
|
|
3
3
|
import {DOMEditor} from 'slate-dom'
|
|
4
|
-
import {createPlaceholderBlock} from '../internal-utils/create-placeholder-block'
|
|
5
4
|
import {slateRangeToSelection} from '../internal-utils/slate-utils'
|
|
6
5
|
import {toSlateRange} from '../internal-utils/to-slate-range'
|
|
6
|
+
import {VOID_CHILD_KEY} from '../internal-utils/values'
|
|
7
7
|
import type {PortableTextSlateEditor} from '../types/editor'
|
|
8
8
|
import {getBlockKeyFromSelectionPoint} from '../utils/util.selection-point'
|
|
9
9
|
import type {BehaviorOperationImplementation} from './behavior.operations'
|
|
@@ -67,10 +67,23 @@ export const deleteOperationImplementation: BehaviorOperationImplementation<
|
|
|
67
67
|
mode: 'highest',
|
|
68
68
|
})
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
return
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (operation.unit === 'child') {
|
|
74
|
+
const range = at ?? operation.editor.selection ?? undefined
|
|
75
|
+
|
|
76
|
+
if (!range) {
|
|
77
|
+
throw new Error('Unable to delete children without a selection')
|
|
72
78
|
}
|
|
73
79
|
|
|
80
|
+
Transforms.removeNodes(operation.editor, {
|
|
81
|
+
at: range,
|
|
82
|
+
match: (node) =>
|
|
83
|
+
(isSpan(context, node) && node._key !== VOID_CHILD_KEY) ||
|
|
84
|
+
('__inline' in node && node.__inline === true),
|
|
85
|
+
})
|
|
86
|
+
|
|
74
87
|
return
|
|
75
88
|
}
|
|
76
89
|
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
type Descendant,
|
|
11
11
|
} from 'slate'
|
|
12
12
|
import {DOMEditor} from 'slate-dom'
|
|
13
|
+
import {createPlaceholderBlock} from '../internal-utils/create-placeholder-block'
|
|
13
14
|
import {getFocusBlock, getFocusChild} from '../internal-utils/slate-utils'
|
|
14
15
|
import {toSlateRange} from '../internal-utils/to-slate-range'
|
|
15
16
|
import {toSlateBlock} from '../internal-utils/values'
|
|
@@ -70,6 +71,10 @@ export function insertBlock(options: {
|
|
|
70
71
|
})
|
|
71
72
|
: editor.selection
|
|
72
73
|
|
|
74
|
+
if (editor.children.length === 0) {
|
|
75
|
+
Transforms.insertNodes(editor, createPlaceholderBlock(context), {at: [0]})
|
|
76
|
+
}
|
|
77
|
+
|
|
73
78
|
// Fall back to the start and end of the editor if neither an editor
|
|
74
79
|
// selection nor an `at` range is provided
|
|
75
80
|
const start = at ? Range.start(at) : Editor.start(editor, [])
|
package/src/types/editor.ts
CHANGED
|
@@ -152,15 +152,6 @@ export interface PortableTextSlateEditor extends ReactEditor {
|
|
|
152
152
|
*/
|
|
153
153
|
pteWithHotKeys: (event: KeyboardEvent<HTMLDivElement>) => void
|
|
154
154
|
|
|
155
|
-
/**
|
|
156
|
-
* Helper function that creates a text block
|
|
157
|
-
*/
|
|
158
|
-
pteCreateTextBlock: (options: {
|
|
159
|
-
decorators: Array<string>
|
|
160
|
-
listItem?: string
|
|
161
|
-
level?: number
|
|
162
|
-
}) => Descendant
|
|
163
|
-
|
|
164
155
|
/**
|
|
165
156
|
* Undo
|
|
166
157
|
*/
|