@portabletext/editor 1.1.4 → 1.1.6
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/README.md +4 -0
- package/lib/index.d.mts +631 -30
- package/lib/index.d.ts +631 -30
- package/lib/index.esm.js +303 -200
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +295 -192
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +303 -200
- package/lib/index.mjs.map +1 -1
- package/package.json +30 -25
- package/src/editor/Editable.tsx +11 -11
- package/src/editor/PortableTextEditor.tsx +37 -32
- package/src/editor/__tests__/self-solving.test.tsx +1 -1
- package/src/editor/behavior/behavior.actions.ts +39 -0
- package/src/editor/behavior/behavior.core.ts +37 -0
- package/src/editor/behavior/behavior.types.ts +106 -0
- package/src/editor/behavior/behavior.utils.ts +34 -0
- package/src/editor/components/SlateContainer.tsx +2 -13
- package/src/editor/components/Synchronizer.tsx +0 -3
- package/src/editor/editor-machine.ts +120 -3
- package/src/editor/hooks/useSyncValue.ts +3 -5
- package/src/editor/key-generator.ts +6 -0
- package/src/editor/plugins/createWithEditableAPI.ts +8 -5
- package/src/editor/plugins/createWithHotKeys.ts +1 -32
- package/src/editor/plugins/createWithInsertBreak.ts +6 -2
- package/src/editor/plugins/createWithInsertData.ts +7 -4
- package/src/editor/plugins/createWithObjectKeys.ts +12 -5
- package/src/editor/plugins/createWithPatches.ts +0 -1
- package/src/editor/plugins/createWithPortableTextMarkModel.ts +85 -114
- package/src/editor/plugins/createWithSchemaTypes.ts +3 -4
- package/src/editor/plugins/createWithUtils.ts +5 -4
- package/src/editor/plugins/index.ts +5 -13
- package/src/index.ts +11 -2
- package/src/types/options.ts +0 -1
- package/src/utils/__tests__/operationToPatches.test.ts +0 -2
- package/src/utils/__tests__/patchToOperations.test.ts +1 -2
- package/src/utils/sibling-utils.ts +55 -0
- package/src/editor/hooks/usePortableTextEditorKeyGenerator.ts +0 -27
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
*
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {isPortableTextBlock
|
|
8
|
-
import type {PortableTextObject
|
|
7
|
+
import {isPortableTextBlock} from '@portabletext/toolkit'
|
|
8
|
+
import type {PortableTextObject} from '@sanity/types'
|
|
9
9
|
import {isEqual, uniq} from 'lodash'
|
|
10
10
|
import {Editor, Element, Node, Path, Range, Text, Transforms} from 'slate'
|
|
11
11
|
import type {
|
|
@@ -14,6 +14,7 @@ import type {
|
|
|
14
14
|
} from '../../types/editor'
|
|
15
15
|
import {debugWithName} from '../../utils/debug'
|
|
16
16
|
import {toPortableTextRange} from '../../utils/ranges'
|
|
17
|
+
import {getNextSpan, getPreviousSpan} from '../../utils/sibling-utils'
|
|
17
18
|
import {isChangingRemotely} from '../../utils/withChanges'
|
|
18
19
|
import {isRedoing, isUndoing} from '../../utils/withUndoRedo'
|
|
19
20
|
import type {EditorActor} from '../editor-machine'
|
|
@@ -23,7 +24,6 @@ const debug = debugWithName('plugin:withPortableTextMarkModel')
|
|
|
23
24
|
export function createWithPortableTextMarkModel(
|
|
24
25
|
editorActor: EditorActor,
|
|
25
26
|
types: PortableTextMemberSchemaTypes,
|
|
26
|
-
keyGenerator: () => string,
|
|
27
27
|
): (editor: PortableTextSlateEditor) => PortableTextSlateEditor {
|
|
28
28
|
return function withPortableTextMarkModel(editor: PortableTextSlateEditor) {
|
|
29
29
|
const {apply, normalizeNode} = editor
|
|
@@ -314,32 +314,13 @@ export function createWithPortableTextMarkModel(
|
|
|
314
314
|
const atTheBeginningOfSpan = selection.anchor.offset === 0
|
|
315
315
|
const atTheEndOfSpan = selection.anchor.offset === span.text.length
|
|
316
316
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
continue
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
if (Path.isBefore(childPath, spanPath)) {
|
|
328
|
-
previousSpan = child
|
|
329
|
-
break
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
for (const [child, childPath] of Node.children(editor, blockPath)) {
|
|
334
|
-
if (!editor.isTextSpan(child)) {
|
|
335
|
-
continue
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
if (Path.isAfter(childPath, spanPath)) {
|
|
339
|
-
nextSpan = child
|
|
340
|
-
break
|
|
341
|
-
}
|
|
342
|
-
}
|
|
317
|
+
const previousSpan = getPreviousSpan({editor, blockPath, spanPath})
|
|
318
|
+
const nextSpan = getNextSpan({editor, blockPath, spanPath})
|
|
319
|
+
const nextSpanAnnotations =
|
|
320
|
+
nextSpan?.marks?.filter((mark) => !decorators.includes(mark)) ?? []
|
|
321
|
+
const spanAnnotations = marks.filter(
|
|
322
|
+
(mark) => !decorators.includes(mark),
|
|
323
|
+
)
|
|
343
324
|
|
|
344
325
|
const previousSpanHasSameAnnotation = previousSpan
|
|
345
326
|
? previousSpan.marks?.some(
|
|
@@ -349,21 +330,16 @@ export function createWithPortableTextMarkModel(
|
|
|
349
330
|
const previousSpanHasSameMarks = previousSpan
|
|
350
331
|
? previousSpan.marks?.every((mark) => marks.includes(mark))
|
|
351
332
|
: false
|
|
352
|
-
const
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
)
|
|
356
|
-
: false
|
|
357
|
-
const nextSpanHasSameMarks = nextSpan
|
|
358
|
-
? nextSpan.marks?.every((mark) => marks.includes(mark))
|
|
359
|
-
: false
|
|
333
|
+
const nextSpanSharesSomeAnnotations = spanAnnotations.some((mark) =>
|
|
334
|
+
nextSpanAnnotations?.includes(mark),
|
|
335
|
+
)
|
|
360
336
|
|
|
361
337
|
if (spanHasAnnotations && !spanIsEmpty) {
|
|
362
338
|
if (atTheBeginningOfSpan) {
|
|
363
339
|
if (previousSpanHasSameMarks) {
|
|
364
340
|
Transforms.insertNodes(editor, {
|
|
365
341
|
_type: 'span',
|
|
366
|
-
_key: keyGenerator(),
|
|
342
|
+
_key: editorActor.getSnapshot().context.keyGenerator(),
|
|
367
343
|
text: op.text,
|
|
368
344
|
marks: previousSpan?.marks ?? [],
|
|
369
345
|
})
|
|
@@ -372,7 +348,7 @@ export function createWithPortableTextMarkModel(
|
|
|
372
348
|
} else {
|
|
373
349
|
Transforms.insertNodes(editor, {
|
|
374
350
|
_type: 'span',
|
|
375
|
-
_key: keyGenerator(),
|
|
351
|
+
_key: editorActor.getSnapshot().context.keyGenerator(),
|
|
376
352
|
text: op.text,
|
|
377
353
|
marks: [],
|
|
378
354
|
})
|
|
@@ -381,105 +357,100 @@ export function createWithPortableTextMarkModel(
|
|
|
381
357
|
}
|
|
382
358
|
|
|
383
359
|
if (atTheEndOfSpan) {
|
|
384
|
-
if (
|
|
360
|
+
if (
|
|
361
|
+
(nextSpan &&
|
|
362
|
+
nextSpanSharesSomeAnnotations &&
|
|
363
|
+
nextSpanAnnotations.length < spanAnnotations.length) ||
|
|
364
|
+
!nextSpanSharesSomeAnnotations
|
|
365
|
+
) {
|
|
385
366
|
Transforms.insertNodes(editor, {
|
|
386
367
|
_type: 'span',
|
|
387
|
-
_key: keyGenerator(),
|
|
368
|
+
_key: editorActor.getSnapshot().context.keyGenerator(),
|
|
388
369
|
text: op.text,
|
|
389
370
|
marks: nextSpan?.marks ?? [],
|
|
390
371
|
})
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
372
|
+
return
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if (!nextSpan) {
|
|
394
376
|
Transforms.insertNodes(editor, {
|
|
395
377
|
_type: 'span',
|
|
396
|
-
_key: keyGenerator(),
|
|
378
|
+
_key: editorActor.getSnapshot().context.keyGenerator(),
|
|
397
379
|
text: op.text,
|
|
398
380
|
marks: [],
|
|
399
381
|
})
|
|
382
|
+
return
|
|
400
383
|
}
|
|
401
|
-
return
|
|
402
384
|
}
|
|
403
385
|
}
|
|
404
386
|
}
|
|
405
387
|
}
|
|
406
388
|
|
|
407
389
|
if (op.type === 'remove_text') {
|
|
408
|
-
const
|
|
409
|
-
Editor.nodes(editor, {
|
|
410
|
-
mode: 'lowest',
|
|
411
|
-
at: {path: op.path, offset: op.offset},
|
|
412
|
-
match: (n) => n._type === types.span.name,
|
|
413
|
-
voids: false,
|
|
414
|
-
}),
|
|
415
|
-
)[0]
|
|
416
|
-
const node = nodeEntry[0]
|
|
417
|
-
const blockEntry = Editor.node(editor, Path.parent(op.path))
|
|
418
|
-
const block = blockEntry[0]
|
|
419
|
-
|
|
420
|
-
if (
|
|
421
|
-
node &&
|
|
422
|
-
isPortableTextSpan(node) &&
|
|
423
|
-
block &&
|
|
424
|
-
isPortableTextBlock(block)
|
|
425
|
-
) {
|
|
426
|
-
const markDefs = block.markDefs ?? []
|
|
427
|
-
const nodeHasAnnotations = (node.marks ?? []).some((mark) =>
|
|
428
|
-
markDefs.find((markDef) => markDef._key === mark),
|
|
429
|
-
)
|
|
430
|
-
const deletingPartOfTheNode = op.offset !== 0
|
|
431
|
-
const deletingFromTheEnd =
|
|
432
|
-
op.offset + op.text.length === node.text.length
|
|
390
|
+
const {selection} = editor
|
|
433
391
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
392
|
+
if (selection && Range.isExpanded(selection)) {
|
|
393
|
+
const [block, blockPath] = Editor.node(editor, selection, {
|
|
394
|
+
depth: 1,
|
|
395
|
+
})
|
|
396
|
+
const [span, spanPath] =
|
|
397
|
+
Array.from(
|
|
398
|
+
Editor.nodes(editor, {
|
|
399
|
+
mode: 'lowest',
|
|
442
400
|
at: {path: op.path, offset: op.offset},
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
editor.onChange()
|
|
448
|
-
return
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
const deletingAllText = op.offset === 0 && deletingFromTheEnd
|
|
452
|
-
|
|
453
|
-
if (nodeHasAnnotations && deletingAllText) {
|
|
454
|
-
const marksWithoutAnnotationMarks: string[] = (
|
|
455
|
-
{
|
|
456
|
-
...(Editor.marks(editor) || {}),
|
|
457
|
-
}.marks || []
|
|
458
|
-
).filter((mark) => decorators.includes(mark))
|
|
401
|
+
match: (n) => editor.isTextSpan(n),
|
|
402
|
+
voids: false,
|
|
403
|
+
}),
|
|
404
|
+
)[0] ?? ([undefined, undefined] as const)
|
|
459
405
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
406
|
+
if (span && block && isPortableTextBlock(block)) {
|
|
407
|
+
const markDefs = block.markDefs ?? []
|
|
408
|
+
const marks = span.marks ?? []
|
|
409
|
+
const spanHasAnnotations = marks.some((mark) =>
|
|
410
|
+
markDefs.find((markDef) => markDef._key === mark),
|
|
411
|
+
)
|
|
412
|
+
const deletingFromTheEnd =
|
|
413
|
+
op.offset + op.text.length === span.text.length
|
|
414
|
+
const deletingAllText = op.offset === 0 && deletingFromTheEnd
|
|
468
415
|
|
|
469
|
-
editor
|
|
470
|
-
|
|
471
|
-
}
|
|
416
|
+
const previousSpan = getPreviousSpan({editor, blockPath, spanPath})
|
|
417
|
+
const nextSpan = getNextSpan({editor, blockPath, spanPath})
|
|
472
418
|
|
|
473
|
-
|
|
419
|
+
const previousSpanHasSameAnnotation = previousSpan
|
|
420
|
+
? previousSpan.marks?.some(
|
|
421
|
+
(mark) => !decorators.includes(mark) && marks.includes(mark),
|
|
422
|
+
)
|
|
423
|
+
: false
|
|
424
|
+
const nextSpanHasSameAnnotation = nextSpan
|
|
425
|
+
? nextSpan.marks?.some(
|
|
426
|
+
(mark) => !decorators.includes(mark) && marks.includes(mark),
|
|
427
|
+
)
|
|
428
|
+
: false
|
|
429
|
+
|
|
430
|
+
if (
|
|
431
|
+
spanHasAnnotations &&
|
|
432
|
+
deletingAllText &&
|
|
433
|
+
!previousSpanHasSameAnnotation &&
|
|
434
|
+
!nextSpanHasSameAnnotation
|
|
435
|
+
) {
|
|
436
|
+
const marksWithoutAnnotationMarks: string[] = (
|
|
437
|
+
{
|
|
438
|
+
...(Editor.marks(editor) || {}),
|
|
439
|
+
}.marks || []
|
|
440
|
+
).filter((mark) => decorators.includes(mark))
|
|
474
441
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
442
|
+
Editor.withoutNormalizing(editor, () => {
|
|
443
|
+
apply(op)
|
|
444
|
+
Transforms.setNodes(
|
|
445
|
+
editor,
|
|
446
|
+
{marks: marksWithoutAnnotationMarks},
|
|
447
|
+
{at: op.path},
|
|
448
|
+
)
|
|
449
|
+
})
|
|
480
450
|
|
|
481
|
-
|
|
482
|
-
|
|
451
|
+
editor.onChange()
|
|
452
|
+
return
|
|
453
|
+
}
|
|
483
454
|
}
|
|
484
455
|
}
|
|
485
456
|
}
|
|
@@ -22,11 +22,9 @@ const debug = debugWithName('plugin:withSchemaTypes')
|
|
|
22
22
|
export function createWithSchemaTypes({
|
|
23
23
|
editorActor,
|
|
24
24
|
schemaTypes,
|
|
25
|
-
keyGenerator,
|
|
26
25
|
}: {
|
|
27
26
|
editorActor: EditorActor
|
|
28
27
|
schemaTypes: PortableTextMemberSchemaTypes
|
|
29
|
-
keyGenerator: () => string
|
|
30
28
|
}) {
|
|
31
29
|
return function withSchemaTypes(
|
|
32
30
|
editor: PortableTextSlateEditor,
|
|
@@ -73,7 +71,8 @@ export function createWithSchemaTypes({
|
|
|
73
71
|
if (node._type === undefined && path.length === 2) {
|
|
74
72
|
debug('Setting span type on text node without a type')
|
|
75
73
|
const span = node as PortableTextSpan
|
|
76
|
-
const key =
|
|
74
|
+
const key =
|
|
75
|
+
span._key || editorActor.getSnapshot().context.keyGenerator()
|
|
77
76
|
editorActor.send({type: 'normalizing'})
|
|
78
77
|
Transforms.setNodes(
|
|
79
78
|
editor,
|
|
@@ -87,7 +86,7 @@ export function createWithSchemaTypes({
|
|
|
87
86
|
// catches cases when the children are missing keys but excludes it when the normalize is running the node as the editor object
|
|
88
87
|
if (node._key === undefined && (path.length === 1 || path.length === 2)) {
|
|
89
88
|
debug('Setting missing key on child node without a key')
|
|
90
|
-
const key = keyGenerator()
|
|
89
|
+
const key = editorActor.getSnapshot().context.keyGenerator()
|
|
91
90
|
editorActor.send({type: 'normalizing'})
|
|
92
91
|
Transforms.setNodes(editor, {_key: key}, {at: path})
|
|
93
92
|
editorActor.send({type: 'done normalizing'})
|
|
@@ -5,13 +5,14 @@ import type {
|
|
|
5
5
|
} from '../../types/editor'
|
|
6
6
|
import {debugWithName} from '../../utils/debug'
|
|
7
7
|
import {toSlateValue} from '../../utils/values'
|
|
8
|
+
import type {EditorActor} from '../editor-machine'
|
|
8
9
|
import type {PortableTextEditor} from '../PortableTextEditor'
|
|
9
10
|
|
|
10
11
|
const debug = debugWithName('plugin:withUtils')
|
|
11
12
|
|
|
12
13
|
interface Options {
|
|
14
|
+
editorActor: EditorActor
|
|
13
15
|
schemaTypes: PortableTextMemberSchemaTypes
|
|
14
|
-
keyGenerator: () => string
|
|
15
16
|
portableTextEditor: PortableTextEditor
|
|
16
17
|
}
|
|
17
18
|
/**
|
|
@@ -19,8 +20,8 @@ interface Options {
|
|
|
19
20
|
*
|
|
20
21
|
*/
|
|
21
22
|
export function createWithUtils({
|
|
23
|
+
editorActor,
|
|
22
24
|
schemaTypes,
|
|
23
|
-
keyGenerator,
|
|
24
25
|
portableTextEditor,
|
|
25
26
|
}: Options) {
|
|
26
27
|
return function withUtils(
|
|
@@ -79,13 +80,13 @@ export function createWithUtils({
|
|
|
79
80
|
[
|
|
80
81
|
{
|
|
81
82
|
_type: schemaTypes.block.name,
|
|
82
|
-
_key: keyGenerator(),
|
|
83
|
+
_key: editorActor.getSnapshot().context.keyGenerator(),
|
|
83
84
|
style: schemaTypes.styles[0].value || 'normal',
|
|
84
85
|
markDefs: [],
|
|
85
86
|
children: [
|
|
86
87
|
{
|
|
87
88
|
_type: 'span',
|
|
88
|
-
_key: keyGenerator(),
|
|
89
|
+
_key: editorActor.getSnapshot().context.keyGenerator(),
|
|
89
90
|
text: '',
|
|
90
91
|
marks: options.decorators.filter((decorator) =>
|
|
91
92
|
schemaTypes.decorators.find(({value}) => value === decorator),
|
|
@@ -47,8 +47,7 @@ export const withPlugins = <T extends Editor>(
|
|
|
47
47
|
options: createEditorOptions,
|
|
48
48
|
): {editor: PortableTextSlateEditor; subscribe: () => () => void} => {
|
|
49
49
|
const e = editor as T & PortableTextSlateEditor
|
|
50
|
-
const {
|
|
51
|
-
options
|
|
50
|
+
const {portableTextEditor, patches$, readOnly, maxBlocks} = options
|
|
52
51
|
const {editorActor, schemaTypes} = portableTextEditor
|
|
53
52
|
e.subscriptions = []
|
|
54
53
|
if (e.destroy) {
|
|
@@ -63,24 +62,18 @@ export const withPlugins = <T extends Editor>(
|
|
|
63
62
|
})
|
|
64
63
|
}
|
|
65
64
|
const operationToPatches = createOperationToPatches(schemaTypes)
|
|
66
|
-
const withObjectKeys = createWithObjectKeys(
|
|
67
|
-
editorActor,
|
|
68
|
-
schemaTypes,
|
|
69
|
-
keyGenerator,
|
|
70
|
-
)
|
|
65
|
+
const withObjectKeys = createWithObjectKeys(editorActor, schemaTypes)
|
|
71
66
|
const withSchemaTypes = createWithSchemaTypes({
|
|
72
67
|
editorActor,
|
|
73
68
|
schemaTypes,
|
|
74
|
-
keyGenerator,
|
|
75
69
|
})
|
|
76
70
|
const withEditableAPI = createWithEditableAPI(
|
|
71
|
+
editorActor,
|
|
77
72
|
portableTextEditor,
|
|
78
73
|
schemaTypes,
|
|
79
|
-
keyGenerator,
|
|
80
74
|
)
|
|
81
75
|
const withPatches = createWithPatches({
|
|
82
76
|
editorActor,
|
|
83
|
-
keyGenerator,
|
|
84
77
|
patches$,
|
|
85
78
|
patchFunctions: operationToPatches,
|
|
86
79
|
readOnly,
|
|
@@ -96,7 +89,6 @@ export const withPlugins = <T extends Editor>(
|
|
|
96
89
|
const withPortableTextMarkModel = createWithPortableTextMarkModel(
|
|
97
90
|
editorActor,
|
|
98
91
|
schemaTypes,
|
|
99
|
-
keyGenerator,
|
|
100
92
|
)
|
|
101
93
|
const withPortableTextBlockStyle = createWithPortableTextBlockStyle(
|
|
102
94
|
editorActor,
|
|
@@ -105,10 +97,10 @@ export const withPlugins = <T extends Editor>(
|
|
|
105
97
|
|
|
106
98
|
const withPlaceholderBlock = createWithPlaceholderBlock()
|
|
107
99
|
|
|
108
|
-
const withInsertBreak = createWithInsertBreak(
|
|
100
|
+
const withInsertBreak = createWithInsertBreak(editorActor, schemaTypes)
|
|
109
101
|
|
|
110
102
|
const withUtils = createWithUtils({
|
|
111
|
-
|
|
103
|
+
editorActor,
|
|
112
104
|
schemaTypes,
|
|
113
105
|
portableTextEditor,
|
|
114
106
|
})
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
export
|
|
1
|
+
export type {Patch} from '@portabletext/patches'
|
|
2
|
+
export type {
|
|
3
|
+
Behavior,
|
|
4
|
+
BehaviorActionIntend,
|
|
5
|
+
BehaviorContext,
|
|
6
|
+
BehaviorEvent,
|
|
7
|
+
BehaviorGuard,
|
|
8
|
+
RaiseBehaviorActionIntend,
|
|
9
|
+
PickFromUnion,
|
|
10
|
+
} from './editor/behavior/behavior.types'
|
|
2
11
|
export {PortableTextEditable} from './editor/Editable'
|
|
3
12
|
export type {PortableTextEditableProps} from './editor/Editable'
|
|
4
13
|
export {
|
|
@@ -8,8 +17,8 @@ export {
|
|
|
8
17
|
type PatchEvent,
|
|
9
18
|
} from './editor/editor-machine'
|
|
10
19
|
export {usePortableTextEditor} from './editor/hooks/usePortableTextEditor'
|
|
11
|
-
export {defaultKeyGenerator as keyGenerator} from './editor/hooks/usePortableTextEditorKeyGenerator'
|
|
12
20
|
export {usePortableTextEditorSelection} from './editor/hooks/usePortableTextEditorSelection'
|
|
21
|
+
export {defaultKeyGenerator as keyGenerator} from './editor/key-generator'
|
|
13
22
|
export {PortableTextEditor} from './editor/PortableTextEditor'
|
|
14
23
|
export type {PortableTextEditorProps} from './editor/PortableTextEditor'
|
|
15
24
|
export * from './types/editor'
|
package/src/types/options.ts
CHANGED
|
@@ -3,7 +3,6 @@ import {createEditor, type Descendant} from 'slate'
|
|
|
3
3
|
import {beforeEach, describe, expect, it} from 'vitest'
|
|
4
4
|
import {PortableTextEditor, type PortableTextEditorProps} from '../..'
|
|
5
5
|
import {schemaType} from '../../editor/__tests__/PortableTextEditorTester'
|
|
6
|
-
import {defaultKeyGenerator} from '../../editor/hooks/usePortableTextEditorKeyGenerator'
|
|
7
6
|
import {withPlugins} from '../../editor/plugins'
|
|
8
7
|
import {getPortableTextMemberSchemaTypes} from '../getPortableTextMemberSchemaTypes'
|
|
9
8
|
import {createOperationToPatches} from '../operationToPatches'
|
|
@@ -16,7 +15,6 @@ const {editor} = withPlugins(createEditor(), {
|
|
|
16
15
|
portableTextEditor: new PortableTextEditor({
|
|
17
16
|
schemaType,
|
|
18
17
|
} as PortableTextEditorProps),
|
|
19
|
-
keyGenerator: defaultKeyGenerator,
|
|
20
18
|
readOnly: false,
|
|
21
19
|
})
|
|
22
20
|
|
|
@@ -2,7 +2,7 @@ import type {Patch} from '@portabletext/patches'
|
|
|
2
2
|
import {noop} from 'lodash'
|
|
3
3
|
import {createEditor, type Descendant} from 'slate'
|
|
4
4
|
import {beforeEach, describe, expect, it} from 'vitest'
|
|
5
|
-
import {
|
|
5
|
+
import {PortableTextEditor} from '../..'
|
|
6
6
|
import {schemaType} from '../../editor/__tests__/PortableTextEditorTester'
|
|
7
7
|
import {withPlugins} from '../../editor/plugins'
|
|
8
8
|
import {createApplyPatch} from '../applyPatch'
|
|
@@ -16,7 +16,6 @@ const portableTextEditor = new PortableTextEditor({schemaType, onChange: noop})
|
|
|
16
16
|
|
|
17
17
|
const {editor} = withPlugins(createEditor(), {
|
|
18
18
|
portableTextEditor,
|
|
19
|
-
keyGenerator,
|
|
20
19
|
readOnly: false,
|
|
21
20
|
})
|
|
22
21
|
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type {PortableTextSpan} from '@sanity/types'
|
|
2
|
+
import {Node, Path} from 'slate'
|
|
3
|
+
import type {PortableTextSlateEditor} from '../types/editor'
|
|
4
|
+
|
|
5
|
+
export function getPreviousSpan({
|
|
6
|
+
editor,
|
|
7
|
+
blockPath,
|
|
8
|
+
spanPath,
|
|
9
|
+
}: {
|
|
10
|
+
editor: PortableTextSlateEditor
|
|
11
|
+
blockPath: Path
|
|
12
|
+
spanPath: Path
|
|
13
|
+
}): PortableTextSpan | undefined {
|
|
14
|
+
let previousSpan: PortableTextSpan | undefined
|
|
15
|
+
|
|
16
|
+
for (const [child, childPath] of Node.children(editor, blockPath, {
|
|
17
|
+
reverse: true,
|
|
18
|
+
})) {
|
|
19
|
+
if (!editor.isTextSpan(child)) {
|
|
20
|
+
continue
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (Path.isBefore(childPath, spanPath)) {
|
|
24
|
+
previousSpan = child
|
|
25
|
+
break
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return previousSpan
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function getNextSpan({
|
|
33
|
+
editor,
|
|
34
|
+
blockPath,
|
|
35
|
+
spanPath,
|
|
36
|
+
}: {
|
|
37
|
+
editor: PortableTextSlateEditor
|
|
38
|
+
blockPath: Path
|
|
39
|
+
spanPath: Path
|
|
40
|
+
}): PortableTextSpan | undefined {
|
|
41
|
+
let nextSpan: PortableTextSpan | undefined
|
|
42
|
+
|
|
43
|
+
for (const [child, childPath] of Node.children(editor, blockPath)) {
|
|
44
|
+
if (!editor.isTextSpan(child)) {
|
|
45
|
+
continue
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (Path.isAfter(childPath, spanPath)) {
|
|
49
|
+
nextSpan = child
|
|
50
|
+
break
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return nextSpan
|
|
55
|
+
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import {randomKey} from '@sanity/util/content'
|
|
2
|
-
import {createContext, useContext} from 'react'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* @public
|
|
6
|
-
*/
|
|
7
|
-
export const defaultKeyGenerator = (): string => randomKey(12)
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* A React context for sharing the editor's keyGenerator.
|
|
11
|
-
*/
|
|
12
|
-
export const PortableTextEditorKeyGeneratorContext =
|
|
13
|
-
createContext<() => string>(defaultKeyGenerator)
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Get the current editor selection from the React context.
|
|
17
|
-
*/
|
|
18
|
-
export const usePortableTextEditorKeyGenerator = (): (() => string) => {
|
|
19
|
-
const keyGenerator = useContext(PortableTextEditorKeyGeneratorContext)
|
|
20
|
-
|
|
21
|
-
if (keyGenerator === undefined) {
|
|
22
|
-
throw new Error(
|
|
23
|
-
`The \`usePortableTextEditorKeyGenerator\` hook must be used inside the <PortableTextEditor> component's context.`,
|
|
24
|
-
)
|
|
25
|
-
}
|
|
26
|
-
return keyGenerator
|
|
27
|
-
}
|