@portabletext/editor 1.15.2 → 1.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/lib/_chunks-cjs/behavior.core.cjs +30 -28
  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 +12 -10
  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 +35 -35
  14. package/lib/behaviors/index.cjs.map +1 -1
  15. package/lib/behaviors/index.d.cts +40 -45
  16. package/lib/behaviors/index.d.ts +40 -45
  17. package/lib/behaviors/index.js +20 -20
  18. package/lib/behaviors/index.js.map +1 -1
  19. package/lib/index.cjs +868 -542
  20. package/lib/index.cjs.map +1 -1
  21. package/lib/index.d.cts +3791 -4503
  22. package/lib/index.d.ts +3791 -4503
  23. package/lib/index.js +865 -541
  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 +62 -17
  28. package/lib/selectors/index.d.ts +62 -17
  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/behavior-actions/behavior.action-utils.insert-block.ts +3 -5
  33. package/src/behavior-actions/behavior.actions.ts +6 -6
  34. package/src/behavior-actions/behavior.guards.ts +2 -6
  35. package/src/behaviors/behavior.code-editor.ts +5 -9
  36. package/src/behaviors/behavior.core.block-objects.ts +14 -20
  37. package/src/behaviors/behavior.core.lists.ts +13 -19
  38. package/src/behaviors/behavior.links.ts +6 -6
  39. package/src/behaviors/behavior.markdown.ts +27 -40
  40. package/src/behaviors/behavior.types.ts +7 -7
  41. package/src/behaviors/index.ts +1 -0
  42. package/src/editor/Editable.tsx +11 -4
  43. package/src/editor/PortableTextEditor.tsx +4 -5
  44. package/src/editor/{hooks/useSyncValue.test.tsx → __tests__/sync-value.test.tsx} +42 -23
  45. package/src/editor/components/Synchronizer.tsx +53 -80
  46. package/src/{utils/getPortableTextMemberSchemaTypes.ts → editor/create-editor-schema.ts} +3 -3
  47. package/src/editor/create-editor.ts +2 -2
  48. package/src/editor/define-schema.ts +8 -3
  49. package/src/editor/editor-machine.ts +136 -104
  50. package/src/editor/editor-provider.tsx +0 -3
  51. package/src/editor/editor-selector.ts +6 -13
  52. package/src/editor/editor-snapshot.ts +5 -6
  53. package/src/editor/get-active-decorators.ts +20 -0
  54. package/src/editor/mutation-machine.ts +100 -0
  55. package/src/editor/plugins/__tests__/withPortableTextMarkModel.test.tsx +21 -15
  56. package/src/editor/plugins/createWithMaxBlocks.ts +1 -1
  57. package/src/editor/plugins/createWithPatches.ts +0 -4
  58. package/src/editor/plugins/createWithPlaceholderBlock.ts +1 -1
  59. package/src/editor/plugins/createWithPortableTextSelections.ts +4 -1
  60. package/src/editor/plugins/createWithUndoRedo.ts +3 -3
  61. package/src/editor/sync-machine.ts +657 -0
  62. package/src/editor/withSyncRangeDecorations.ts +17 -5
  63. package/src/index.ts +3 -5
  64. package/src/selectors/_exports/index.ts +1 -0
  65. package/src/selectors/index.ts +10 -4
  66. package/src/selectors/selector.get-active-style.ts +37 -0
  67. package/src/selectors/selector.get-selected-spans.ts +136 -0
  68. package/src/selectors/selector.is-active-annotation.ts +49 -0
  69. package/src/selectors/selector.is-active-decorator.ts +21 -0
  70. package/src/selectors/selector.is-active-list-item.ts +13 -0
  71. package/src/selectors/selector.is-active-style.ts +13 -0
  72. package/src/selectors/selector.is-selection-collapsed.ts +12 -0
  73. package/src/selectors/selector.is-selection-expanded.ts +9 -0
  74. package/src/selectors/selectors.ts +0 -11
  75. package/src/utils/__tests__/operationToPatches.test.ts +2 -2
  76. package/src/utils/__tests__/patchToOperations.test.ts +2 -2
  77. package/src/utils/__tests__/values.test.ts +2 -2
  78. package/src/utils/weakMaps.ts +0 -3
  79. package/src/utils/withChanges.ts +1 -8
  80. package/lib/_chunks-cjs/selectors.cjs.map +0 -1
  81. package/lib/_chunks-es/selectors.js.map +0 -1
  82. package/src/editor/hooks/useSyncValue.ts +0 -426
@@ -23,14 +23,15 @@ import type {OmitFromUnion, PickFromUnion} from '../type-utils'
23
23
  import type {
24
24
  EditorSelection,
25
25
  InvalidValueResolution,
26
- PortableTextMemberSchemaTypes,
27
26
  PortableTextSlateEditor,
28
27
  } from '../types/editor'
29
28
  import debug from '../utils/debug'
30
29
  import {toPortableTextRange} from '../utils/ranges'
31
30
  import {fromSlateValue} from '../utils/values'
32
31
  import {KEY_TO_VALUE_ELEMENT} from '../utils/weakMaps'
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
@@ -81,7 +83,7 @@ export type InternalEditorEvent =
81
83
  }
82
84
  | {
83
85
  type: 'update schema'
84
- schema: PortableTextMemberSchemaTypes
86
+ schema: EditorSchema
85
87
  }
86
88
  | {
87
89
  type: 'update behaviors'
@@ -98,7 +100,7 @@ export type InternalEditorEvent =
98
100
  type: 'update maxBlocks'
99
101
  maxBlocks: number | undefined
100
102
  }
101
- | OmitFromUnion<InternalEditorEmittedEvent, 'type', 'readOnly toggled'>
103
+ | OmitFromUnion<InternalEditorEmittedEvent, 'type', 'read only' | 'editable'>
102
104
 
103
105
  /**
104
106
  * @alpha
@@ -108,13 +110,14 @@ export type EditorEmittedEvent = PickFromUnion<
108
110
  'type',
109
111
  | 'blurred'
110
112
  | 'done loading'
113
+ | 'editable'
111
114
  | 'error'
112
115
  | 'focused'
113
116
  | 'invalid value'
114
117
  | 'loading'
115
118
  | 'mutation'
116
119
  | 'patch'
117
- | 'readOnly toggled'
120
+ | 'read only'
118
121
  | 'ready'
119
122
  | 'selection'
120
123
  | 'value changed'
@@ -152,7 +155,8 @@ export type InternalEditorEmittedEvent =
152
155
  | {type: 'focused'; event: FocusEvent<HTMLDivElement, Element>}
153
156
  | {type: 'loading'}
154
157
  | {type: 'done loading'}
155
- | {type: 'readOnly toggled'; readOnly: boolean}
158
+ | {type: 'read only'}
159
+ | {type: 'editable'}
156
160
  | PickFromUnion<
157
161
  SyntheticBehaviorEvent,
158
162
  'type',
@@ -179,10 +183,10 @@ export const editorMachine = setup({
179
183
  behaviors: Array<Behavior>
180
184
  keyGenerator: () => string
181
185
  pendingEvents: Array<PatchEvent | MutationEvent>
182
- schema: PortableTextMemberSchemaTypes
183
- readOnly: boolean
186
+ schema: EditorSchema
187
+ initialReadOnly: boolean
184
188
  maxBlocks: number | undefined
185
- selection: NonNullable<EditorSelection> | undefined
189
+ selection: EditorSelection
186
190
  value: Array<PortableTextBlock> | undefined
187
191
  },
188
192
  events: {} as InternalEditorEvent,
@@ -192,7 +196,7 @@ export const editorMachine = setup({
192
196
  keyGenerator: () => string
193
197
  maxBlocks?: number
194
198
  readOnly?: boolean
195
- schema: PortableTextMemberSchemaTypes
199
+ schema: EditorSchema
196
200
  value?: Array<PortableTextBlock>
197
201
  },
198
202
  },
@@ -217,6 +221,8 @@ export const editorMachine = setup({
217
221
  assertEvent(event, 'mutation')
218
222
  return event
219
223
  }),
224
+ 'emit read only': emit({type: 'read only'}),
225
+ 'emit editable': emit({type: 'editable'}),
220
226
  'defer event': assign({
221
227
  pendingEvents: ({context, event}) => {
222
228
  assertEvent(event, ['patch', 'mutation'])
@@ -228,6 +234,7 @@ export const editorMachine = setup({
228
234
  enqueue(emit(event))
229
235
  }
230
236
  }),
237
+ 'emit ready': emit({type: 'ready'}),
231
238
  'clear pending events': assign({
232
239
  pendingEvents: [],
233
240
  }),
@@ -275,24 +282,11 @@ export const editorMachine = setup({
275
282
  context.schema,
276
283
  )
277
284
 
278
- if (!selection) {
279
- console.warn(
280
- `Unable to handle event ${event.type} due to missing selection`,
281
- )
282
-
283
- if (!defaultAction) {
284
- return
285
- }
286
-
287
- enqueue.raise({
288
- type: 'behavior action intends',
289
- editor: event.editor,
290
- actionIntends: [defaultAction],
291
- })
292
- return
293
- }
294
-
295
285
  const editorContext = {
286
+ activeDecorators: getActiveDecorators({
287
+ schema: context.schema,
288
+ slateEditorInstance: event.editor,
289
+ }),
296
290
  keyGenerator: context.keyGenerator,
297
291
  schema: context.schema,
298
292
  selection,
@@ -361,56 +355,19 @@ export const editorMachine = setup({
361
355
  keyGenerator: input.keyGenerator,
362
356
  pendingEvents: [],
363
357
  schema: input.schema,
364
- selection: undefined,
365
- readOnly: input.readOnly ?? false,
358
+ selection: null,
359
+ initialReadOnly: input.readOnly ?? false,
366
360
  maxBlocks: input.maxBlocks,
367
361
  value: input.value,
368
362
  }),
369
363
  on: {
370
- 'annotation.add': {
371
- actions: emit(({event}) => event),
372
- guard: ({context}) => !context.readOnly,
373
- },
374
- 'annotation.remove': {
375
- actions: emit(({event}) => event),
376
- guard: ({context}) => !context.readOnly,
377
- },
378
- 'annotation.toggle': {
379
- actions: emit(({event}) => event),
380
- guard: ({context}) => !context.readOnly,
381
- },
382
- 'blur': {
383
- actions: emit(({event}) => event),
384
- guard: ({context}) => !context.readOnly,
385
- },
386
- 'decorator.*': {
387
- actions: emit(({event}) => event),
388
- guard: ({context}) => !context.readOnly,
389
- },
390
- 'focus': {
391
- actions: emit(({event}) => event),
392
- guard: ({context}) => !context.readOnly,
393
- },
394
- 'insert.*': {
395
- actions: emit(({event}) => event),
396
- guard: ({context}) => !context.readOnly,
397
- },
398
- 'list item.*': {
399
- actions: emit(({event}) => event),
400
- guard: ({context}) => !context.readOnly,
401
- },
402
- 'style.*': {
403
- actions: emit(({event}) => event),
404
- guard: ({context}) => !context.readOnly,
405
- },
406
- 'ready': {actions: emit(({event}) => event)},
407
364
  'unset': {actions: emit(({event}) => event)},
408
365
  'value changed': {actions: emit(({event}) => event)},
409
366
  'invalid value': {actions: emit(({event}) => event)},
410
367
  'error': {actions: emit(({event}) => event)},
411
368
  'selection': {
412
369
  actions: [
413
- assign({selection: ({event}) => event.selection ?? undefined}),
370
+ assign({selection: ({event}) => event.selection}),
414
371
  emit(({event}) => event),
415
372
  ],
416
373
  },
@@ -422,22 +379,9 @@ export const editorMachine = setup({
422
379
  'update behaviors': {actions: 'assign behaviors'},
423
380
  'update schema': {actions: 'assign schema'},
424
381
  'update value': {actions: assign({value: ({event}) => event.value})},
425
- 'toggle readOnly': {
426
- actions: [
427
- assign({readOnly: ({context}) => !context.readOnly}),
428
- emit(({context}) => ({
429
- type: 'readOnly toggled',
430
- readOnly: context.readOnly,
431
- })),
432
- ],
433
- },
434
382
  'update maxBlocks': {
435
383
  actions: assign({maxBlocks: ({event}) => event.maxBlocks}),
436
384
  },
437
- 'behavior event': {
438
- actions: 'handle behavior event',
439
- guard: ({context}) => !context.readOnly,
440
- },
441
385
  'behavior action intends': {
442
386
  actions: [
443
387
  ({context, event}) => {
@@ -472,49 +416,137 @@ export const editorMachine = setup({
472
416
  ],
473
417
  },
474
418
  },
475
- initial: 'pristine',
419
+ type: 'parallel',
476
420
  states: {
477
- pristine: {
478
- initial: 'idle',
421
+ 'edit mode': {
422
+ initial: 'read only',
479
423
  states: {
480
- idle: {
424
+ 'read only': {
425
+ initial: 'determine initial edit mode',
426
+ states: {
427
+ 'determine initial edit mode': {
428
+ on: {
429
+ 'done syncing': [
430
+ {
431
+ target: '#editor.edit mode.read only.read only',
432
+ guard: ({context}) => context.initialReadOnly,
433
+ },
434
+ {
435
+ target: '#editor.edit mode.editable',
436
+ },
437
+ ],
438
+ },
439
+ },
440
+ 'read only': {
441
+ on: {
442
+ 'toggle readOnly': {
443
+ target: '#editor.edit mode.editable',
444
+ actions: ['emit editable'],
445
+ },
446
+ },
447
+ },
448
+ },
449
+ },
450
+ 'editable': {
481
451
  on: {
482
- normalizing: {
483
- target: 'normalizing',
452
+ 'toggle readOnly': {
453
+ target: '#editor.edit mode.read only.read only',
454
+ actions: ['emit read only'],
484
455
  },
485
- patch: {
486
- actions: 'defer event',
487
- target: '#editor.dirty',
456
+ 'behavior event': {
457
+ actions: 'handle behavior event',
488
458
  },
489
- mutation: {
490
- actions: 'defer event',
491
- target: '#editor.dirty',
459
+ 'annotation.add': {
460
+ actions: emit(({event}) => event),
461
+ },
462
+ 'annotation.remove': {
463
+ actions: emit(({event}) => event),
464
+ },
465
+ 'annotation.toggle': {
466
+ actions: emit(({event}) => event),
467
+ },
468
+ 'blur': {
469
+ actions: emit(({event}) => event),
470
+ },
471
+ 'decorator.*': {
472
+ actions: emit(({event}) => event),
473
+ },
474
+ 'focus': {
475
+ actions: emit(({event}) => event),
476
+ },
477
+ 'insert.*': {
478
+ actions: emit(({event}) => event),
479
+ },
480
+ 'list item.*': {
481
+ actions: emit(({event}) => event),
482
+ },
483
+ 'style.*': {
484
+ actions: emit(({event}) => event),
492
485
  },
493
486
  },
494
487
  },
495
- normalizing: {
488
+ },
489
+ },
490
+ 'setup': {
491
+ initial: 'setting up',
492
+ states: {
493
+ 'setting up': {
494
+ exit: ['emit ready'],
496
495
  on: {
497
- 'done normalizing': {
498
- target: 'idle',
499
- },
500
496
  'patch': {
501
497
  actions: 'defer event',
502
498
  },
503
499
  'mutation': {
504
500
  actions: 'defer event',
505
501
  },
502
+ 'done syncing': {
503
+ target: 'pristine',
504
+ },
506
505
  },
507
506
  },
508
- },
509
- },
510
- dirty: {
511
- entry: ['emit pending events', 'clear pending events'],
512
- on: {
513
- patch: {
514
- actions: 'emit patch event',
507
+ 'pristine': {
508
+ initial: 'idle',
509
+ states: {
510
+ idle: {
511
+ on: {
512
+ normalizing: {
513
+ target: 'normalizing',
514
+ },
515
+ patch: {
516
+ actions: 'defer event',
517
+ target: '#editor.setup.dirty',
518
+ },
519
+ mutation: {
520
+ actions: 'defer event',
521
+ target: '#editor.setup.dirty',
522
+ },
523
+ },
524
+ },
525
+ normalizing: {
526
+ on: {
527
+ 'done normalizing': {
528
+ target: 'idle',
529
+ },
530
+ 'patch': {
531
+ actions: 'defer event',
532
+ },
533
+ 'mutation': {
534
+ actions: 'defer event',
535
+ },
536
+ },
537
+ },
538
+ },
515
539
  },
516
- mutation: {
517
- actions: 'emit mutation event',
540
+ 'dirty': {
541
+ entry: ['emit pending events', 'clear pending events'],
542
+ on: {
543
+ patch: {
544
+ actions: 'emit patch event',
545
+ },
546
+ mutation: {
547
+ actions: 'emit mutation event',
548
+ },
549
+ },
518
550
  },
519
551
  },
520
552
  },
@@ -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,7 +1,7 @@
1
1
  import {useSelector} from '@xstate/react'
2
- import type {EditorSelection} from '../types/editor'
3
2
  import type {Editor} from './create-editor'
4
3
  import type {EditorSnapshot} from './editor-snapshot'
4
+ import {getActiveDecorators} from './get-active-decorators'
5
5
  import {getValue} from './get-value'
6
6
 
7
7
  function defaultCompare<T>(a: T, b: T) {
@@ -11,18 +11,7 @@ function defaultCompare<T>(a: T, b: T) {
11
11
  /**
12
12
  * @alpha
13
13
  */
14
- export type EditorSelectorSnapshot = {
15
- context: Omit<EditorSnapshot['context'], 'selection'> & {
16
- selection?: NonNullable<EditorSelection>
17
- }
18
- }
19
-
20
- /**
21
- * @alpha
22
- */
23
- export type EditorSelector<TSelected> = (
24
- snapshot: EditorSelectorSnapshot,
25
- ) => TSelected
14
+ export type EditorSelector<TSelected> = (snapshot: EditorSnapshot) => TSelected
26
15
 
27
16
  /**
28
17
  * @alpha
@@ -36,6 +25,10 @@ export function useEditorSelector<TSelected>(
36
25
  editor._internal.editorActor,
37
26
  (snapshot) => {
38
27
  const context = {
28
+ activeDecorators: getActiveDecorators({
29
+ schema: snapshot.context.schema,
30
+ slateEditorInstance: editor._internal.slateEditor.instance,
31
+ }),
39
32
  keyGenerator: snapshot.context.keyGenerator,
40
33
  schema: snapshot.context.schema,
41
34
  selection: snapshot.context.selection,
@@ -1,16 +1,15 @@
1
1
  import type {PortableTextBlock} from '@sanity/types'
2
- import type {
3
- EditorSelection,
4
- PortableTextMemberSchemaTypes,
5
- } from '../types/editor'
2
+ import type {EditorSelection} from '../types/editor'
3
+ import type {EditorSchema} from './define-schema'
6
4
 
7
5
  /**
8
6
  * @alpha
9
7
  */
10
8
  export type EditorContext = {
9
+ activeDecorators: Array<string>
11
10
  keyGenerator: () => string
12
- schema: PortableTextMemberSchemaTypes
13
- selection: NonNullable<EditorSelection>
11
+ schema: EditorSchema
12
+ selection: EditorSelection
14
13
  value: Array<PortableTextBlock>
15
14
  }
16
15
 
@@ -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
+ }
@@ -0,0 +1,100 @@
1
+ import type {Patch} from '@portabletext/patches'
2
+ import type {PortableTextBlock} from '@sanity/types'
3
+ import {Editor} from 'slate'
4
+ import {assign, emit, setup} from 'xstate'
5
+ import type {PortableTextSlateEditor} from '../types/editor'
6
+ import {fromSlateValue} from '../utils/values'
7
+ import {KEY_TO_VALUE_ELEMENT} from '../utils/weakMaps'
8
+ import type {EditorSchema} from './define-schema'
9
+
10
+ const FLUSH_PATCHES_THROTTLED_MS = process.env.NODE_ENV === 'test' ? 500 : 1000
11
+
12
+ /**
13
+ * Makes sure editor mutation events are debounced
14
+ */
15
+ export const mutationMachine = setup({
16
+ types: {
17
+ context: {} as {
18
+ pendingPatches: Array<Patch>
19
+ schema: EditorSchema
20
+ slateEditor: PortableTextSlateEditor
21
+ },
22
+ events: {} as {type: 'patch'; patch: Patch},
23
+ input: {} as {
24
+ schema: EditorSchema
25
+ slateEditor: PortableTextSlateEditor
26
+ },
27
+ emitted: {} as
28
+ | {
29
+ type: 'has pending patches'
30
+ }
31
+ | {
32
+ type: 'mutation'
33
+ patches: Array<Patch>
34
+ snapshot: Array<PortableTextBlock> | undefined
35
+ },
36
+ },
37
+ actions: {
38
+ 'emit has pending patches': emit({type: 'has pending patches'}),
39
+ 'emit mutation': emit(({context}) => ({
40
+ type: 'mutation' as const,
41
+ patches: context.pendingPatches,
42
+ snapshot: fromSlateValue(
43
+ context.slateEditor.children,
44
+ context.schema.block.name,
45
+ KEY_TO_VALUE_ELEMENT.get(context.slateEditor),
46
+ ),
47
+ })),
48
+ 'clear pending patches': assign({
49
+ pendingPatches: [],
50
+ }),
51
+ 'defer patch': assign({
52
+ pendingPatches: ({context, event}) => [
53
+ ...context.pendingPatches,
54
+ event.patch,
55
+ ],
56
+ }),
57
+ },
58
+ guards: {
59
+ 'slate is normalizing': ({context}) =>
60
+ Editor.isNormalizing(context.slateEditor),
61
+ },
62
+ }).createMachine({
63
+ id: 'mutation',
64
+ context: ({input}) => ({
65
+ pendingPatches: [],
66
+ schema: input.schema,
67
+ slateEditor: input.slateEditor,
68
+ }),
69
+ initial: 'idle',
70
+ states: {
71
+ 'idle': {
72
+ on: {
73
+ patch: {
74
+ actions: ['defer patch', 'emit has pending patches'],
75
+ target: 'has pending patches',
76
+ },
77
+ },
78
+ },
79
+ 'has pending patches': {
80
+ after: {
81
+ [FLUSH_PATCHES_THROTTLED_MS]: [
82
+ {
83
+ guard: 'slate is normalizing',
84
+ target: 'idle',
85
+ actions: ['emit mutation', 'clear pending patches'],
86
+ },
87
+ {
88
+ reenter: true,
89
+ },
90
+ ],
91
+ },
92
+ on: {
93
+ patch: {
94
+ actions: ['defer patch'],
95
+ reenter: true,
96
+ },
97
+ },
98
+ },
99
+ },
100
+ })
@@ -49,26 +49,22 @@ describe('plugin:withPortableTextMarksModel', () => {
49
49
  anchor: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 4},
50
50
  })
51
51
  PortableTextEditor.toggleMark(editor, 'strong')
52
- expect(PortableTextEditor.getValue(editor)).toMatchInlineSnapshot(`
53
- [
52
+ expect(PortableTextEditor.getValue(editor)).toEqual([
54
53
  {
55
- "_key": "a",
56
- "_type": "myTestBlockType",
57
- "children": [
54
+ _key: 'a',
55
+ _type: 'myTestBlockType',
56
+ children: [
58
57
  {
59
- "_key": "a1",
60
- "_type": "span",
61
- "marks": [
62
- "strong",
63
- ],
64
- "text": "1234",
58
+ _key: 'a1',
59
+ _type: 'span',
60
+ marks: ['strong'],
61
+ text: '1234',
65
62
  },
66
63
  ],
67
- "markDefs": [],
68
- "style": "normal",
64
+ markDefs: [],
65
+ style: 'normal',
69
66
  },
70
- ]
71
- `)
67
+ ])
72
68
  })
73
69
  await waitFor(() => {
74
70
  if (editorRef.current) {
@@ -871,6 +867,16 @@ describe('plugin:withPortableTextMarksModel', () => {
871
867
  )
872
868
  })
873
869
 
870
+ await waitFor(() => {
871
+ if (editorRef.current) {
872
+ expect(onChange).toHaveBeenCalledWith({
873
+ type: 'value',
874
+ value: initialValue,
875
+ })
876
+ expect(onChange).toHaveBeenCalledWith({type: 'ready'})
877
+ }
878
+ })
879
+
874
880
  await waitFor(() => {
875
881
  if (editorRef.current) {
876
882
  PortableTextEditor.focus(editorRef.current)
@@ -13,7 +13,7 @@ export function createWithMaxBlocks(editorActor: EditorActor) {
13
13
  ): PortableTextSlateEditor {
14
14
  const {apply} = editor
15
15
  editor.apply = (operation) => {
16
- if (editorActor.getSnapshot().context.readOnly) {
16
+ if (editorActor.getSnapshot().matches({'edit mode': 'read only'})) {
17
17
  apply(operation)
18
18
  return
19
19
  }
@@ -150,10 +150,6 @@ export function createWithPatches({
150
150
  })
151
151
 
152
152
  editor.apply = (operation: Operation): void | Editor => {
153
- if (editorActor.getSnapshot().context.readOnly) {
154
- apply(operation)
155
- return editor
156
- }
157
153
  let patches: Patch[] = []
158
154
 
159
155
  // Update previous children here before we apply
@@ -21,7 +21,7 @@ export function createWithPlaceholderBlock(
21
21
  const {apply} = editor
22
22
 
23
23
  editor.apply = (op) => {
24
- if (editorActor.getSnapshot().context.readOnly) {
24
+ if (editorActor.getSnapshot().matches({'edit mode': 'read only'})) {
25
25
  apply(op)
26
26
  return
27
27
  }
@@ -57,7 +57,10 @@ export function createWithPortableTextSelections(
57
57
  editor.onChange = () => {
58
58
  const hasChanges = editor.operations.length > 0
59
59
  onChange()
60
- if (hasChanges) {
60
+ if (
61
+ hasChanges &&
62
+ !editorActor.getSnapshot().matches({setup: 'setting up'})
63
+ ) {
61
64
  emitPortableTextSelection()
62
65
  }
63
66
  }