@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.
- package/lib/_chunks-cjs/behavior.core.cjs +30 -28
- package/lib/_chunks-cjs/behavior.core.cjs.map +1 -1
- package/lib/_chunks-cjs/selector.get-text-before.cjs +14 -14
- package/lib/_chunks-cjs/selector.get-text-before.cjs.map +1 -1
- package/lib/_chunks-cjs/{selectors.cjs → selector.is-selection-collapsed.cjs} +8 -8
- package/lib/_chunks-cjs/selector.is-selection-collapsed.cjs.map +1 -0
- package/lib/_chunks-es/behavior.core.js +12 -10
- package/lib/_chunks-es/behavior.core.js.map +1 -1
- package/lib/_chunks-es/selector.get-text-before.js +14 -14
- package/lib/_chunks-es/selector.get-text-before.js.map +1 -1
- package/lib/_chunks-es/{selectors.js → selector.is-selection-collapsed.js} +8 -8
- package/lib/_chunks-es/selector.is-selection-collapsed.js.map +1 -0
- package/lib/behaviors/index.cjs +35 -35
- package/lib/behaviors/index.cjs.map +1 -1
- package/lib/behaviors/index.d.cts +40 -45
- package/lib/behaviors/index.d.ts +40 -45
- package/lib/behaviors/index.js +20 -20
- package/lib/behaviors/index.js.map +1 -1
- package/lib/index.cjs +868 -542
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +3791 -4503
- package/lib/index.d.ts +3791 -4503
- package/lib/index.js +865 -541
- package/lib/index.js.map +1 -1
- package/lib/selectors/index.cjs +166 -16
- package/lib/selectors/index.cjs.map +1 -1
- package/lib/selectors/index.d.cts +62 -17
- package/lib/selectors/index.d.ts +62 -17
- package/lib/selectors/index.js +154 -3
- package/lib/selectors/index.js.map +1 -1
- package/package.json +11 -11
- package/src/behavior-actions/behavior.action-utils.insert-block.ts +3 -5
- package/src/behavior-actions/behavior.actions.ts +6 -6
- package/src/behavior-actions/behavior.guards.ts +2 -6
- package/src/behaviors/behavior.code-editor.ts +5 -9
- package/src/behaviors/behavior.core.block-objects.ts +14 -20
- package/src/behaviors/behavior.core.lists.ts +13 -19
- package/src/behaviors/behavior.links.ts +6 -6
- package/src/behaviors/behavior.markdown.ts +27 -40
- package/src/behaviors/behavior.types.ts +7 -7
- package/src/behaviors/index.ts +1 -0
- package/src/editor/Editable.tsx +11 -4
- package/src/editor/PortableTextEditor.tsx +4 -5
- package/src/editor/{hooks/useSyncValue.test.tsx → __tests__/sync-value.test.tsx} +42 -23
- package/src/editor/components/Synchronizer.tsx +53 -80
- package/src/{utils/getPortableTextMemberSchemaTypes.ts → editor/create-editor-schema.ts} +3 -3
- package/src/editor/create-editor.ts +2 -2
- package/src/editor/define-schema.ts +8 -3
- package/src/editor/editor-machine.ts +136 -104
- package/src/editor/editor-provider.tsx +0 -3
- package/src/editor/editor-selector.ts +6 -13
- package/src/editor/editor-snapshot.ts +5 -6
- package/src/editor/get-active-decorators.ts +20 -0
- package/src/editor/mutation-machine.ts +100 -0
- package/src/editor/plugins/__tests__/withPortableTextMarkModel.test.tsx +21 -15
- package/src/editor/plugins/createWithMaxBlocks.ts +1 -1
- package/src/editor/plugins/createWithPatches.ts +0 -4
- package/src/editor/plugins/createWithPlaceholderBlock.ts +1 -1
- package/src/editor/plugins/createWithPortableTextSelections.ts +4 -1
- package/src/editor/plugins/createWithUndoRedo.ts +3 -3
- package/src/editor/sync-machine.ts +657 -0
- package/src/editor/withSyncRangeDecorations.ts +17 -5
- package/src/index.ts +3 -5
- package/src/selectors/_exports/index.ts +1 -0
- package/src/selectors/index.ts +10 -4
- package/src/selectors/selector.get-active-style.ts +37 -0
- package/src/selectors/selector.get-selected-spans.ts +136 -0
- package/src/selectors/selector.is-active-annotation.ts +49 -0
- package/src/selectors/selector.is-active-decorator.ts +21 -0
- package/src/selectors/selector.is-active-list-item.ts +13 -0
- package/src/selectors/selector.is-active-style.ts +13 -0
- package/src/selectors/selector.is-selection-collapsed.ts +12 -0
- package/src/selectors/selector.is-selection-expanded.ts +9 -0
- package/src/selectors/selectors.ts +0 -11
- package/src/utils/__tests__/operationToPatches.test.ts +2 -2
- package/src/utils/__tests__/patchToOperations.test.ts +2 -2
- package/src/utils/__tests__/values.test.ts +2 -2
- package/src/utils/weakMaps.ts +0 -3
- package/src/utils/withChanges.ts +1 -8
- package/lib/_chunks-cjs/selectors.cjs.map +0 -1
- package/lib/_chunks-es/selectors.js.map +0 -1
- 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:
|
|
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', '
|
|
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
|
-
| '
|
|
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: '
|
|
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:
|
|
183
|
-
|
|
186
|
+
schema: EditorSchema
|
|
187
|
+
initialReadOnly: boolean
|
|
184
188
|
maxBlocks: number | undefined
|
|
185
|
-
selection:
|
|
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:
|
|
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:
|
|
365
|
-
|
|
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
|
|
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
|
-
|
|
419
|
+
type: 'parallel',
|
|
476
420
|
states: {
|
|
477
|
-
|
|
478
|
-
initial: '
|
|
421
|
+
'edit mode': {
|
|
422
|
+
initial: 'read only',
|
|
479
423
|
states: {
|
|
480
|
-
|
|
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
|
-
|
|
483
|
-
target: '
|
|
452
|
+
'toggle readOnly': {
|
|
453
|
+
target: '#editor.edit mode.read only.read only',
|
|
454
|
+
actions: ['emit read only'],
|
|
484
455
|
},
|
|
485
|
-
|
|
486
|
-
actions: '
|
|
487
|
-
target: '#editor.dirty',
|
|
456
|
+
'behavior event': {
|
|
457
|
+
actions: 'handle behavior event',
|
|
488
458
|
},
|
|
489
|
-
|
|
490
|
-
actions:
|
|
491
|
-
|
|
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
|
-
|
|
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
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
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
|
-
|
|
517
|
-
|
|
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
|
|
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
|
-
|
|
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:
|
|
13
|
-
selection:
|
|
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)).
|
|
53
|
-
[
|
|
52
|
+
expect(PortableTextEditor.getValue(editor)).toEqual([
|
|
54
53
|
{
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
_key: 'a',
|
|
55
|
+
_type: 'myTestBlockType',
|
|
56
|
+
children: [
|
|
58
57
|
{
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
],
|
|
64
|
-
"text": "1234",
|
|
58
|
+
_key: 'a1',
|
|
59
|
+
_type: 'span',
|
|
60
|
+
marks: ['strong'],
|
|
61
|
+
text: '1234',
|
|
65
62
|
},
|
|
66
63
|
],
|
|
67
|
-
|
|
68
|
-
|
|
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().
|
|
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
|
|
@@ -57,7 +57,10 @@ export function createWithPortableTextSelections(
|
|
|
57
57
|
editor.onChange = () => {
|
|
58
58
|
const hasChanges = editor.operations.length > 0
|
|
59
59
|
onChange()
|
|
60
|
-
if (
|
|
60
|
+
if (
|
|
61
|
+
hasChanges &&
|
|
62
|
+
!editorActor.getSnapshot().matches({setup: 'setting up'})
|
|
63
|
+
) {
|
|
61
64
|
emitPortableTextSelection()
|
|
62
65
|
}
|
|
63
66
|
}
|