@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.
Files changed (38) hide show
  1. package/README.md +4 -0
  2. package/lib/index.d.mts +631 -30
  3. package/lib/index.d.ts +631 -30
  4. package/lib/index.esm.js +303 -200
  5. package/lib/index.esm.js.map +1 -1
  6. package/lib/index.js +295 -192
  7. package/lib/index.js.map +1 -1
  8. package/lib/index.mjs +303 -200
  9. package/lib/index.mjs.map +1 -1
  10. package/package.json +30 -25
  11. package/src/editor/Editable.tsx +11 -11
  12. package/src/editor/PortableTextEditor.tsx +37 -32
  13. package/src/editor/__tests__/self-solving.test.tsx +1 -1
  14. package/src/editor/behavior/behavior.actions.ts +39 -0
  15. package/src/editor/behavior/behavior.core.ts +37 -0
  16. package/src/editor/behavior/behavior.types.ts +106 -0
  17. package/src/editor/behavior/behavior.utils.ts +34 -0
  18. package/src/editor/components/SlateContainer.tsx +2 -13
  19. package/src/editor/components/Synchronizer.tsx +0 -3
  20. package/src/editor/editor-machine.ts +120 -3
  21. package/src/editor/hooks/useSyncValue.ts +3 -5
  22. package/src/editor/key-generator.ts +6 -0
  23. package/src/editor/plugins/createWithEditableAPI.ts +8 -5
  24. package/src/editor/plugins/createWithHotKeys.ts +1 -32
  25. package/src/editor/plugins/createWithInsertBreak.ts +6 -2
  26. package/src/editor/plugins/createWithInsertData.ts +7 -4
  27. package/src/editor/plugins/createWithObjectKeys.ts +12 -5
  28. package/src/editor/plugins/createWithPatches.ts +0 -1
  29. package/src/editor/plugins/createWithPortableTextMarkModel.ts +85 -114
  30. package/src/editor/plugins/createWithSchemaTypes.ts +3 -4
  31. package/src/editor/plugins/createWithUtils.ts +5 -4
  32. package/src/editor/plugins/index.ts +5 -13
  33. package/src/index.ts +11 -2
  34. package/src/types/options.ts +0 -1
  35. package/src/utils/__tests__/operationToPatches.test.ts +0 -2
  36. package/src/utils/__tests__/patchToOperations.test.ts +1 -2
  37. package/src/utils/sibling-utils.ts +55 -0
  38. package/src/editor/hooks/usePortableTextEditorKeyGenerator.ts +0 -27
@@ -4,8 +4,8 @@
4
4
  *
5
5
  */
6
6
 
7
- import {isPortableTextBlock, isPortableTextSpan} from '@portabletext/toolkit'
8
- import type {PortableTextObject, PortableTextSpan} from '@sanity/types'
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
- let previousSpan: PortableTextSpan | undefined
318
- let nextSpan: PortableTextSpan | undefined
319
-
320
- for (const [child, childPath] of Node.children(editor, blockPath, {
321
- reverse: true,
322
- })) {
323
- if (!editor.isTextSpan(child)) {
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 nextSpanHasSameAnnotation = nextSpan
353
- ? nextSpan.marks?.some(
354
- (mark) => !decorators.includes(mark) && marks.includes(mark),
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 (nextSpanHasSameMarks) {
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
- } else if (nextSpanHasSameAnnotation) {
392
- apply(op)
393
- } else {
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 nodeEntry = Array.from(
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
- if (
435
- nodeHasAnnotations &&
436
- deletingPartOfTheNode &&
437
- deletingFromTheEnd
438
- ) {
439
- Editor.withoutNormalizing(editor, () => {
440
- Transforms.splitNodes(editor, {
441
- match: Text.isText,
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
- Transforms.removeNodes(editor, {at: Path.next(op.path)})
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
- Editor.withoutNormalizing(editor, () => {
461
- apply(op)
462
- Transforms.setNodes(
463
- editor,
464
- {marks: marksWithoutAnnotationMarks},
465
- {at: op.path},
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.onChange()
470
- return
471
- }
416
+ const previousSpan = getPreviousSpan({editor, blockPath, spanPath})
417
+ const nextSpan = getNextSpan({editor, blockPath, spanPath})
472
418
 
473
- const nodeHasMarks = node.marks !== undefined && node.marks.length > 0
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
- if (nodeHasMarks && deletingAllText) {
476
- Editor.withoutNormalizing(editor, () => {
477
- apply(op)
478
- Transforms.setNodes(editor, {marks: []}, {at: op.path})
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
- editor.onChange()
482
- return
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 = span._key || keyGenerator()
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 {keyGenerator, portableTextEditor, patches$, readOnly, maxBlocks} =
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(schemaTypes, keyGenerator)
100
+ const withInsertBreak = createWithInsertBreak(editorActor, schemaTypes)
109
101
 
110
102
  const withUtils = createWithUtils({
111
- keyGenerator,
103
+ editorActor,
112
104
  schemaTypes,
113
105
  portableTextEditor,
114
106
  })
package/src/index.ts CHANGED
@@ -1,4 +1,13 @@
1
- export {type Patch} from '@portabletext/patches'
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'
@@ -6,7 +6,6 @@ import type {PatchObservable} from './editor'
6
6
  * @internal
7
7
  */
8
8
  export type createEditorOptions = {
9
- keyGenerator: () => string
10
9
  patches$?: PatchObservable
11
10
  portableTextEditor: PortableTextEditor
12
11
  readOnly: boolean
@@ -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 {keyGenerator, PortableTextEditor} from '../..'
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
- }