@portabletext/editor 1.15.3 → 1.16.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.
Files changed (71) hide show
  1. package/lib/_chunks-cjs/behavior.core.cjs +25 -25
  2. package/lib/_chunks-cjs/behavior.core.cjs.map +1 -1
  3. package/lib/_chunks-cjs/selector.get-text-before.cjs +14 -14
  4. package/lib/_chunks-cjs/selector.get-text-before.cjs.map +1 -1
  5. package/lib/_chunks-cjs/{selectors.cjs → selector.is-selection-collapsed.cjs} +8 -8
  6. package/lib/_chunks-cjs/selector.is-selection-collapsed.cjs.map +1 -0
  7. package/lib/_chunks-es/behavior.core.js +7 -7
  8. package/lib/_chunks-es/behavior.core.js.map +1 -1
  9. package/lib/_chunks-es/selector.get-text-before.js +14 -14
  10. package/lib/_chunks-es/selector.get-text-before.js.map +1 -1
  11. package/lib/_chunks-es/{selectors.js → selector.is-selection-collapsed.js} +8 -8
  12. package/lib/_chunks-es/selector.is-selection-collapsed.js.map +1 -0
  13. package/lib/behaviors/index.cjs +23 -23
  14. package/lib/behaviors/index.cjs.map +1 -1
  15. package/lib/behaviors/index.d.cts +1 -0
  16. package/lib/behaviors/index.d.ts +1 -0
  17. package/lib/behaviors/index.js +8 -8
  18. package/lib/behaviors/index.js.map +1 -1
  19. package/lib/index.cjs +863 -516
  20. package/lib/index.cjs.map +1 -1
  21. package/lib/index.d.cts +3816 -4457
  22. package/lib/index.d.ts +3816 -4457
  23. package/lib/index.js +860 -515
  24. package/lib/index.js.map +1 -1
  25. package/lib/selectors/index.cjs +166 -16
  26. package/lib/selectors/index.cjs.map +1 -1
  27. package/lib/selectors/index.d.cts +54 -5
  28. package/lib/selectors/index.d.ts +54 -5
  29. package/lib/selectors/index.js +154 -3
  30. package/lib/selectors/index.js.map +1 -1
  31. package/package.json +11 -11
  32. package/src/behaviors/behavior.code-editor.ts +5 -9
  33. package/src/behaviors/behavior.core.block-objects.ts +13 -19
  34. package/src/behaviors/behavior.core.lists.ts +11 -17
  35. package/src/behaviors/behavior.links.ts +4 -4
  36. package/src/behaviors/behavior.markdown.ts +16 -21
  37. package/src/editor/Editable.tsx +11 -4
  38. package/src/editor/PortableTextEditor.tsx +4 -4
  39. package/src/editor/{hooks/useSyncValue.test.tsx → __tests__/sync-value.test.tsx} +42 -23
  40. package/src/editor/components/Synchronizer.tsx +53 -80
  41. package/src/editor/create-editor.ts +4 -1
  42. package/src/editor/editor-machine.ts +135 -83
  43. package/src/editor/editor-provider.tsx +0 -3
  44. package/src/editor/editor-selector.ts +5 -0
  45. package/src/editor/editor-snapshot.ts +1 -0
  46. package/src/editor/get-active-decorators.ts +20 -0
  47. package/src/editor/mutation-machine.ts +100 -0
  48. package/src/editor/plugins/__tests__/withPortableTextMarkModel.test.tsx +21 -15
  49. package/src/editor/plugins/createWithMaxBlocks.ts +1 -1
  50. package/src/editor/plugins/createWithPatches.ts +0 -4
  51. package/src/editor/plugins/createWithPlaceholderBlock.ts +1 -1
  52. package/src/editor/plugins/createWithPortableTextSelections.ts +4 -1
  53. package/src/editor/plugins/createWithUndoRedo.ts +3 -3
  54. package/src/editor/sync-machine.ts +661 -0
  55. package/src/editor/withSyncRangeDecorations.ts +17 -5
  56. package/src/selectors/_exports/index.ts +1 -0
  57. package/src/selectors/index.ts +9 -1
  58. package/src/selectors/selector.get-active-style.ts +37 -0
  59. package/src/selectors/selector.get-selected-spans.ts +136 -0
  60. package/src/selectors/selector.is-active-annotation.ts +49 -0
  61. package/src/selectors/selector.is-active-decorator.ts +21 -0
  62. package/src/selectors/selector.is-active-list-item.ts +13 -0
  63. package/src/selectors/selector.is-active-style.ts +13 -0
  64. package/src/selectors/selector.is-selection-collapsed.ts +12 -0
  65. package/src/selectors/selector.is-selection-expanded.ts +9 -0
  66. package/src/selectors/selectors.ts +0 -11
  67. package/src/utils/weakMaps.ts +0 -3
  68. package/src/utils/withChanges.ts +1 -8
  69. package/lib/_chunks-cjs/selectors.cjs.map +0 -1
  70. package/lib/_chunks-es/selectors.js.map +0 -1
  71. package/src/editor/hooks/useSyncValue.ts +0 -426
@@ -1,30 +1,18 @@
1
- import type {Patch} from '@portabletext/patches'
2
- import type {PortableTextBlock} from '@sanity/types'
3
- import {useSelector} from '@xstate/react'
4
- import {throttle} from 'lodash'
5
- import {useCallback, useEffect, useRef} from 'react'
6
- import {Editor} from 'slate'
1
+ import {useActorRef, useSelector} from '@xstate/react'
2
+ import {useEffect} from 'react'
7
3
  import type {PortableTextSlateEditor} from '../../types/editor'
8
4
  import {debugWithName} from '../../utils/debug'
9
- import {IS_PROCESSING_LOCAL_CHANGES} from '../../utils/weakMaps'
10
5
  import type {EditorActor} from '../editor-machine'
11
- import {useSyncValue} from '../hooks/useSyncValue'
12
- import type {PortableTextEditor} from '../PortableTextEditor'
6
+ import {mutationMachine} from '../mutation-machine'
7
+ import {syncMachine} from '../sync-machine'
13
8
 
14
9
  const debug = debugWithName('component:PortableTextEditor:Synchronizer')
15
- const debugVerbose = debug.enabled && false
16
-
17
- // The editor will commit changes in a throttled fashion in order
18
- // not to overload the network and degrade performance while typing.
19
- const FLUSH_PATCHES_THROTTLED_MS = process.env.NODE_ENV === 'test' ? 500 : 1000
20
10
 
21
11
  /**
22
12
  * @internal
23
13
  */
24
14
  export interface SynchronizerProps {
25
15
  editorActor: EditorActor
26
- getValue: () => Array<PortableTextBlock> | undefined
27
- portableTextEditor: PortableTextEditor
28
16
  slateEditor: PortableTextSlateEditor
29
17
  }
30
18
 
@@ -33,90 +21,75 @@ export interface SynchronizerProps {
33
21
  * @internal
34
22
  */
35
23
  export function Synchronizer(props: SynchronizerProps) {
36
- const readOnly = useSelector(props.editorActor, (s) => s.context.readOnly)
37
- const value = useSelector(props.editorActor, (s) => s.context.value)
38
- const {editorActor, getValue, portableTextEditor, slateEditor} = props
39
- const pendingPatches = useRef<Patch[]>([])
24
+ const {editorActor, slateEditor} = props
40
25
 
41
- const syncValue = useSyncValue({
42
- editorActor,
43
- portableTextEditor,
44
- readOnly,
45
- slateEditor,
26
+ const value = useSelector(props.editorActor, (s) => s.context.value)
27
+ const readOnly = useSelector(props.editorActor, (s) =>
28
+ s.matches({'edit mode': 'read only'}),
29
+ )
30
+ const syncActorRef = useActorRef(syncMachine, {
31
+ input: {
32
+ keyGenerator: props.editorActor.getSnapshot().context.keyGenerator,
33
+ readOnly: props.editorActor
34
+ .getSnapshot()
35
+ .matches({'edit mode': 'read only'}),
36
+ schema: props.editorActor.getSnapshot().context.schema,
37
+ slateEditor,
38
+ },
39
+ })
40
+ const mutationActorRef = useActorRef(mutationMachine, {
41
+ input: {
42
+ schema: editorActor.getSnapshot().context.schema,
43
+ slateEditor,
44
+ },
46
45
  })
47
46
 
48
47
  useEffect(() => {
49
- IS_PROCESSING_LOCAL_CHANGES.set(slateEditor, false)
50
- }, [slateEditor])
51
-
52
- const onFlushPendingPatches = useCallback(() => {
53
- if (pendingPatches.current.length > 0) {
54
- debug('Flushing pending patches')
55
- if (debugVerbose) {
56
- debug(`Patches:\n${JSON.stringify(pendingPatches.current, null, 2)}`)
48
+ const subscription = mutationActorRef.on('*', (event) => {
49
+ if (event.type === 'has pending patches') {
50
+ syncActorRef.send({type: 'has pending patches'})
51
+ }
52
+ if (event.type === 'mutation') {
53
+ syncActorRef.send({type: 'mutation'})
54
+ editorActor.send(event)
57
55
  }
58
- const snapshot = getValue()
59
- editorActor.send({
60
- type: 'mutation',
61
- patches: pendingPatches.current,
62
- snapshot,
63
- })
64
- pendingPatches.current = []
56
+ })
57
+
58
+ return () => {
59
+ subscription.unsubscribe()
65
60
  }
66
- IS_PROCESSING_LOCAL_CHANGES.set(slateEditor, false)
67
- }, [editorActor, slateEditor, getValue])
61
+ }, [mutationActorRef, syncActorRef, editorActor])
68
62
 
69
- // Flush pending patches immediately on unmount
70
63
  useEffect(() => {
64
+ const subscription = syncActorRef.on('*', (event) => {
65
+ props.editorActor.send(event)
66
+ })
67
+
71
68
  return () => {
72
- onFlushPendingPatches()
69
+ subscription.unsubscribe()
73
70
  }
74
- }, [onFlushPendingPatches])
71
+ }, [props.editorActor, syncActorRef])
75
72
 
76
- // Subscribe to, and handle changes from the editor
77
73
  useEffect(() => {
78
- const onFlushPendingPatchesThrottled = throttle(
79
- () => {
80
- // If the editor is normalizing (each operation) it means that it's not in the middle of a bigger transform,
81
- // and we can flush these changes immediately.
82
- if (Editor.isNormalizing(slateEditor)) {
83
- onFlushPendingPatches()
84
- return
85
- }
86
- // If it's in the middle of something, try again.
87
- onFlushPendingPatchesThrottled()
88
- },
89
- FLUSH_PATCHES_THROTTLED_MS,
90
- {
91
- leading: false,
92
- trailing: true,
93
- },
94
- )
74
+ syncActorRef.send({type: 'update readOnly', readOnly})
75
+ }, [syncActorRef, readOnly])
95
76
 
77
+ useEffect(() => {
78
+ debug('Value from props changed, syncing new value')
79
+ syncActorRef.send({type: 'update value', value})
80
+ }, [syncActorRef, value])
81
+
82
+ // Subscribe to, and handle changes from the editor
83
+ useEffect(() => {
96
84
  debug('Subscribing to patch events')
97
85
  const sub = editorActor.on('patch', (event) => {
98
- IS_PROCESSING_LOCAL_CHANGES.set(slateEditor, true)
99
- pendingPatches.current.push(event.patch)
100
- onFlushPendingPatchesThrottled()
86
+ mutationActorRef.send(event)
101
87
  })
102
88
  return () => {
103
89
  debug('Unsubscribing to patch events')
104
90
  sub.unsubscribe()
105
91
  }
106
- }, [editorActor, onFlushPendingPatches, slateEditor])
107
-
108
- // This hook must be set up after setting up the subscription above, or it will not pick up validation errors from the useSyncValue hook.
109
- // This will cause the editor to not be able to signal a validation error and offer invalid value resolution of the initial value.
110
- const isInitialValueFromProps = useRef(true)
111
- useEffect(() => {
112
- debug('Value from props changed, syncing new value')
113
- syncValue(value)
114
- // Signal that we have our first value, and are ready to roll.
115
- if (isInitialValueFromProps.current) {
116
- editorActor.send({type: 'ready'})
117
- isInitialValueFromProps.current = false
118
- }
119
- }, [editorActor, syncValue, value])
92
+ }, [editorActor, mutationActorRef, slateEditor])
120
93
 
121
94
  return null
122
95
  }
@@ -33,6 +33,9 @@ import {createEditableAPI} from './plugins/createWithEditableAPI'
33
33
  export type EditorConfig = {
34
34
  behaviors?: Array<Behavior>
35
35
  keyGenerator?: () => string
36
+ /**
37
+ * @deprecated Will be removed in the next major version
38
+ */
36
39
  maxBlocks?: number
37
40
  readOnly?: boolean
38
41
  initialValue?: Array<PortableTextBlock>
@@ -64,8 +67,8 @@ export type EditorEvent = PickFromUnion<
64
67
  | 'list item.toggle'
65
68
  | 'style.toggle'
66
69
  | 'patches'
67
- | 'toggle readOnly'
68
70
  | 'update behaviors'
71
+ | 'update readOnly'
69
72
  | 'update value'
70
73
  >
71
74
 
@@ -31,6 +31,7 @@ import {fromSlateValue} from '../utils/values'
31
31
  import {KEY_TO_VALUE_ELEMENT} from '../utils/weakMaps'
32
32
  import type {EditorSchema} from './define-schema'
33
33
  import type {EditorContext} from './editor-snapshot'
34
+ import {getActiveDecorators} from './get-active-decorators'
34
35
 
35
36
  export * from 'xstate/guards'
36
37
 
@@ -68,6 +69,7 @@ export type MutationEvent = {
68
69
  export type InternalEditorEvent =
69
70
  | {type: 'normalizing'}
70
71
  | {type: 'done normalizing'}
72
+ | {type: 'done syncing'}
71
73
  | {
72
74
  type: 'behavior event'
73
75
  behaviorEvent: SyntheticBehaviorEvent | NativeBehaviorEvent
@@ -79,6 +81,10 @@ export type InternalEditorEvent =
79
81
  editor: PortableTextSlateEditor
80
82
  actionIntends: Array<BehaviorActionIntend>
81
83
  }
84
+ | {
85
+ type: 'update readOnly'
86
+ readOnly: boolean
87
+ }
82
88
  | {
83
89
  type: 'update schema'
84
90
  schema: EditorSchema
@@ -91,14 +97,11 @@ export type InternalEditorEvent =
91
97
  type: 'update value'
92
98
  value: Array<PortableTextBlock> | undefined
93
99
  }
94
- | {
95
- type: 'toggle readOnly'
96
- }
97
100
  | {
98
101
  type: 'update maxBlocks'
99
102
  maxBlocks: number | undefined
100
103
  }
101
- | OmitFromUnion<InternalEditorEmittedEvent, 'type', 'readOnly toggled'>
104
+ | OmitFromUnion<InternalEditorEmittedEvent, 'type', 'read only' | 'editable'>
102
105
 
103
106
  /**
104
107
  * @alpha
@@ -108,13 +111,14 @@ export type EditorEmittedEvent = PickFromUnion<
108
111
  'type',
109
112
  | 'blurred'
110
113
  | 'done loading'
114
+ | 'editable'
111
115
  | 'error'
112
116
  | 'focused'
113
117
  | 'invalid value'
114
118
  | 'loading'
115
119
  | 'mutation'
116
120
  | 'patch'
117
- | 'readOnly toggled'
121
+ | 'read only'
118
122
  | 'ready'
119
123
  | 'selection'
120
124
  | 'value changed'
@@ -152,7 +156,8 @@ export type InternalEditorEmittedEvent =
152
156
  | {type: 'focused'; event: FocusEvent<HTMLDivElement, Element>}
153
157
  | {type: 'loading'}
154
158
  | {type: 'done loading'}
155
- | {type: 'readOnly toggled'; readOnly: boolean}
159
+ | {type: 'read only'}
160
+ | {type: 'editable'}
156
161
  | PickFromUnion<
157
162
  SyntheticBehaviorEvent,
158
163
  'type',
@@ -180,7 +185,7 @@ export const editorMachine = setup({
180
185
  keyGenerator: () => string
181
186
  pendingEvents: Array<PatchEvent | MutationEvent>
182
187
  schema: EditorSchema
183
- readOnly: boolean
188
+ initialReadOnly: boolean
184
189
  maxBlocks: number | undefined
185
190
  selection: EditorSelection
186
191
  value: Array<PortableTextBlock> | undefined
@@ -217,6 +222,8 @@ export const editorMachine = setup({
217
222
  assertEvent(event, 'mutation')
218
223
  return event
219
224
  }),
225
+ 'emit read only': emit({type: 'read only'}),
226
+ 'emit editable': emit({type: 'editable'}),
220
227
  'defer event': assign({
221
228
  pendingEvents: ({context, event}) => {
222
229
  assertEvent(event, ['patch', 'mutation'])
@@ -228,6 +235,7 @@ export const editorMachine = setup({
228
235
  enqueue(emit(event))
229
236
  }
230
237
  }),
238
+ 'emit ready': emit({type: 'ready'}),
231
239
  'clear pending events': assign({
232
240
  pendingEvents: [],
233
241
  }),
@@ -276,6 +284,10 @@ export const editorMachine = setup({
276
284
  )
277
285
 
278
286
  const editorContext = {
287
+ activeDecorators: getActiveDecorators({
288
+ schema: context.schema,
289
+ slateEditorInstance: event.editor,
290
+ }),
279
291
  keyGenerator: context.keyGenerator,
280
292
  schema: context.schema,
281
293
  selection,
@@ -345,48 +357,11 @@ export const editorMachine = setup({
345
357
  pendingEvents: [],
346
358
  schema: input.schema,
347
359
  selection: null,
348
- readOnly: input.readOnly ?? false,
360
+ initialReadOnly: input.readOnly ?? false,
349
361
  maxBlocks: input.maxBlocks,
350
362
  value: input.value,
351
363
  }),
352
364
  on: {
353
- 'annotation.add': {
354
- actions: emit(({event}) => event),
355
- guard: ({context}) => !context.readOnly,
356
- },
357
- 'annotation.remove': {
358
- actions: emit(({event}) => event),
359
- guard: ({context}) => !context.readOnly,
360
- },
361
- 'annotation.toggle': {
362
- actions: emit(({event}) => event),
363
- guard: ({context}) => !context.readOnly,
364
- },
365
- 'blur': {
366
- actions: emit(({event}) => event),
367
- guard: ({context}) => !context.readOnly,
368
- },
369
- 'decorator.*': {
370
- actions: emit(({event}) => event),
371
- guard: ({context}) => !context.readOnly,
372
- },
373
- 'focus': {
374
- actions: emit(({event}) => event),
375
- guard: ({context}) => !context.readOnly,
376
- },
377
- 'insert.*': {
378
- actions: emit(({event}) => event),
379
- guard: ({context}) => !context.readOnly,
380
- },
381
- 'list item.*': {
382
- actions: emit(({event}) => event),
383
- guard: ({context}) => !context.readOnly,
384
- },
385
- 'style.*': {
386
- actions: emit(({event}) => event),
387
- guard: ({context}) => !context.readOnly,
388
- },
389
- 'ready': {actions: emit(({event}) => event)},
390
365
  'unset': {actions: emit(({event}) => event)},
391
366
  'value changed': {actions: emit(({event}) => event)},
392
367
  'invalid value': {actions: emit(({event}) => event)},
@@ -405,22 +380,9 @@ export const editorMachine = setup({
405
380
  'update behaviors': {actions: 'assign behaviors'},
406
381
  'update schema': {actions: 'assign schema'},
407
382
  'update value': {actions: assign({value: ({event}) => event.value})},
408
- 'toggle readOnly': {
409
- actions: [
410
- assign({readOnly: ({context}) => !context.readOnly}),
411
- emit(({context}) => ({
412
- type: 'readOnly toggled',
413
- readOnly: context.readOnly,
414
- })),
415
- ],
416
- },
417
383
  'update maxBlocks': {
418
384
  actions: assign({maxBlocks: ({event}) => event.maxBlocks}),
419
385
  },
420
- 'behavior event': {
421
- actions: 'handle behavior event',
422
- guard: ({context}) => !context.readOnly,
423
- },
424
386
  'behavior action intends': {
425
387
  actions: [
426
388
  ({context, event}) => {
@@ -455,49 +417,139 @@ export const editorMachine = setup({
455
417
  ],
456
418
  },
457
419
  },
458
- initial: 'pristine',
420
+ type: 'parallel',
459
421
  states: {
460
- pristine: {
461
- initial: 'idle',
422
+ 'edit mode': {
423
+ initial: 'read only',
462
424
  states: {
463
- idle: {
425
+ 'read only': {
426
+ initial: 'determine initial edit mode',
427
+ states: {
428
+ 'determine initial edit mode': {
429
+ on: {
430
+ 'done syncing': [
431
+ {
432
+ target: '#editor.edit mode.read only.read only',
433
+ guard: ({context}) => context.initialReadOnly,
434
+ },
435
+ {
436
+ target: '#editor.edit mode.editable',
437
+ },
438
+ ],
439
+ },
440
+ },
441
+ 'read only': {
442
+ on: {
443
+ 'update readOnly': {
444
+ guard: ({event}) => !event.readOnly,
445
+ target: '#editor.edit mode.editable',
446
+ actions: ['emit editable'],
447
+ },
448
+ },
449
+ },
450
+ },
451
+ },
452
+ 'editable': {
464
453
  on: {
465
- normalizing: {
466
- target: 'normalizing',
454
+ 'update readOnly': {
455
+ guard: ({event}) => event.readOnly,
456
+ target: '#editor.edit mode.read only.read only',
457
+ actions: ['emit read only'],
467
458
  },
468
- patch: {
469
- actions: 'defer event',
470
- target: '#editor.dirty',
459
+ 'behavior event': {
460
+ actions: 'handle behavior event',
471
461
  },
472
- mutation: {
473
- actions: 'defer event',
474
- target: '#editor.dirty',
462
+ 'annotation.add': {
463
+ actions: emit(({event}) => event),
464
+ },
465
+ 'annotation.remove': {
466
+ actions: emit(({event}) => event),
467
+ },
468
+ 'annotation.toggle': {
469
+ actions: emit(({event}) => event),
470
+ },
471
+ 'blur': {
472
+ actions: emit(({event}) => event),
473
+ },
474
+ 'decorator.*': {
475
+ actions: emit(({event}) => event),
476
+ },
477
+ 'focus': {
478
+ actions: emit(({event}) => event),
479
+ },
480
+ 'insert.*': {
481
+ actions: emit(({event}) => event),
482
+ },
483
+ 'list item.*': {
484
+ actions: emit(({event}) => event),
485
+ },
486
+ 'style.*': {
487
+ actions: emit(({event}) => event),
475
488
  },
476
489
  },
477
490
  },
478
- normalizing: {
491
+ },
492
+ },
493
+ 'setup': {
494
+ initial: 'setting up',
495
+ states: {
496
+ 'setting up': {
497
+ exit: ['emit ready'],
479
498
  on: {
480
- 'done normalizing': {
481
- target: 'idle',
482
- },
483
499
  'patch': {
484
500
  actions: 'defer event',
485
501
  },
486
502
  'mutation': {
487
503
  actions: 'defer event',
488
504
  },
505
+ 'done syncing': {
506
+ target: 'pristine',
507
+ },
489
508
  },
490
509
  },
491
- },
492
- },
493
- dirty: {
494
- entry: ['emit pending events', 'clear pending events'],
495
- on: {
496
- patch: {
497
- actions: 'emit patch event',
510
+ 'pristine': {
511
+ initial: 'idle',
512
+ states: {
513
+ idle: {
514
+ on: {
515
+ normalizing: {
516
+ target: 'normalizing',
517
+ },
518
+ patch: {
519
+ actions: 'defer event',
520
+ target: '#editor.setup.dirty',
521
+ },
522
+ mutation: {
523
+ actions: 'defer event',
524
+ target: '#editor.setup.dirty',
525
+ },
526
+ },
527
+ },
528
+ normalizing: {
529
+ on: {
530
+ 'done normalizing': {
531
+ target: 'idle',
532
+ },
533
+ 'patch': {
534
+ actions: 'defer event',
535
+ },
536
+ 'mutation': {
537
+ actions: 'defer event',
538
+ },
539
+ },
540
+ },
541
+ },
498
542
  },
499
- mutation: {
500
- actions: 'emit mutation event',
543
+ 'dirty': {
544
+ entry: ['emit pending events', 'clear pending events'],
545
+ on: {
546
+ patch: {
547
+ actions: 'emit patch event',
548
+ },
549
+ mutation: {
550
+ actions: 'emit mutation event',
551
+ },
552
+ },
501
553
  },
502
554
  },
503
555
  },
@@ -28,7 +28,6 @@ export function EditorProvider(props: EditorProviderProps) {
28
28
  const editor = useCreateEditor(props.initialConfig)
29
29
  const editorActor = editor._internal.editorActor
30
30
  const slateEditor = editor._internal.slateEditor
31
- const editable = editor._internal.editable
32
31
  const portableTextEditor = useMemo(
33
32
  () =>
34
33
  new PortableTextEditor({
@@ -47,8 +46,6 @@ export function EditorProvider(props: EditorProviderProps) {
47
46
  />
48
47
  <Synchronizer
49
48
  editorActor={editorActor}
50
- getValue={editable.getValue}
51
- portableTextEditor={portableTextEditor}
52
49
  slateEditor={slateEditor.instance}
53
50
  />
54
51
  <EditorActorContext.Provider value={editorActor}>
@@ -1,6 +1,7 @@
1
1
  import {useSelector} from '@xstate/react'
2
2
  import type {Editor} from './create-editor'
3
3
  import type {EditorSnapshot} from './editor-snapshot'
4
+ import {getActiveDecorators} from './get-active-decorators'
4
5
  import {getValue} from './get-value'
5
6
 
6
7
  function defaultCompare<T>(a: T, b: T) {
@@ -24,6 +25,10 @@ export function useEditorSelector<TSelected>(
24
25
  editor._internal.editorActor,
25
26
  (snapshot) => {
26
27
  const context = {
28
+ activeDecorators: getActiveDecorators({
29
+ schema: snapshot.context.schema,
30
+ slateEditorInstance: editor._internal.slateEditor.instance,
31
+ }),
27
32
  keyGenerator: snapshot.context.keyGenerator,
28
33
  schema: snapshot.context.schema,
29
34
  selection: snapshot.context.selection,
@@ -6,6 +6,7 @@ import type {EditorSchema} from './define-schema'
6
6
  * @alpha
7
7
  */
8
8
  export type EditorContext = {
9
+ activeDecorators: Array<string>
9
10
  keyGenerator: () => string
10
11
  schema: EditorSchema
11
12
  selection: EditorSelection
@@ -0,0 +1,20 @@
1
+ import {Editor} from 'slate'
2
+ import type {PortableTextSlateEditor} from '../types/editor'
3
+ import type {EditorSchema} from './define-schema'
4
+
5
+ export function getActiveDecorators({
6
+ schema,
7
+ slateEditorInstance,
8
+ }: {
9
+ schema: EditorSchema
10
+ slateEditorInstance: PortableTextSlateEditor
11
+ }) {
12
+ const decorators = schema.decorators.map((decorator) => decorator.value)
13
+
14
+ const marks =
15
+ {
16
+ ...(Editor.marks(slateEditorInstance) ?? {}),
17
+ }.marks ?? []
18
+
19
+ return marks.filter((mark) => decorators.includes(mark))
20
+ }