@portabletext/editor 1.42.0 → 1.43.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/editor-provider.cjs +522 -418
- package/lib/_chunks-cjs/editor-provider.cjs.map +1 -1
- package/lib/_chunks-cjs/selector.is-active-style.cjs +14 -0
- package/lib/_chunks-cjs/selector.is-active-style.cjs.map +1 -1
- package/lib/_chunks-cjs/selector.is-overlapping-selection.cjs +2 -2
- package/lib/_chunks-cjs/selector.is-overlapping-selection.cjs.map +1 -1
- package/lib/_chunks-cjs/util.selection-point-to-block-offset.cjs.map +1 -1
- package/lib/_chunks-es/editor-provider.js +527 -423
- package/lib/_chunks-es/editor-provider.js.map +1 -1
- package/lib/_chunks-es/selector.is-active-style.js +15 -1
- package/lib/_chunks-es/selector.is-active-style.js.map +1 -1
- package/lib/_chunks-es/selector.is-overlapping-selection.js +2 -2
- package/lib/_chunks-es/selector.is-overlapping-selection.js.map +1 -1
- package/lib/_chunks-es/util.selection-point-to-block-offset.js.map +1 -1
- package/lib/behaviors/index.d.cts +11 -11
- package/lib/behaviors/index.d.ts +11 -11
- package/lib/index.d.cts +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/plugins/index.d.cts +1 -1
- package/lib/plugins/index.d.ts +1 -1
- package/lib/selectors/index.cjs +3 -8
- package/lib/selectors/index.cjs.map +1 -1
- package/lib/selectors/index.d.cts +1 -1
- package/lib/selectors/index.d.ts +1 -1
- package/lib/selectors/index.js +5 -11
- package/lib/selectors/index.js.map +1 -1
- package/lib/utils/index.d.cts +1 -1
- package/lib/utils/index.d.ts +1 -1
- package/package.json +7 -7
- package/src/behavior-actions/behavior.actions.ts +6 -0
- package/src/behaviors/behavior.default.ts +67 -1
- package/src/behaviors/behavior.perform-event.ts +266 -0
- package/src/editor/editor-machine.ts +31 -254
- package/src/editor/with-applying-behavior-actions.ts +13 -4
- package/src/internal-utils/parse-blocks.ts +14 -0
- package/src/plugins/plugin.markdown.test.tsx +64 -0
- package/src/selectors/selector.get-active-annotations.test.ts +28 -0
- package/src/selectors/selector.get-active-annotations.ts +15 -2
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import type {Behavior, BehaviorEvent} from '.'
|
|
2
|
+
import {performAction} from '../behavior-actions/behavior.actions'
|
|
3
|
+
import type {EditorSchema} from '../editor/define-schema'
|
|
4
|
+
import type {EditorSnapshot} from '../editor/editor-snapshot'
|
|
5
|
+
import {
|
|
6
|
+
withApplyingBehaviorActions,
|
|
7
|
+
withApplyingBehaviorActionSet,
|
|
8
|
+
} from '../editor/with-applying-behavior-actions'
|
|
9
|
+
import {debugWithName} from '../internal-utils/debug'
|
|
10
|
+
import type {PortableTextSlateEditor} from '../types/editor'
|
|
11
|
+
import type {InternalBehaviorAction} from './behavior.types.action'
|
|
12
|
+
import {
|
|
13
|
+
isClipboardBehaviorEvent,
|
|
14
|
+
isCustomBehaviorEvent,
|
|
15
|
+
isDragBehaviorEvent,
|
|
16
|
+
isInputBehaviorEvent,
|
|
17
|
+
isKeyboardBehaviorEvent,
|
|
18
|
+
isMouseBehaviorEvent,
|
|
19
|
+
} from './behavior.types.event'
|
|
20
|
+
|
|
21
|
+
const debug = debugWithName('behaviors:event')
|
|
22
|
+
|
|
23
|
+
export function performEvent({
|
|
24
|
+
behaviors,
|
|
25
|
+
event,
|
|
26
|
+
editor,
|
|
27
|
+
keyGenerator,
|
|
28
|
+
schema,
|
|
29
|
+
getSnapshot,
|
|
30
|
+
nativeEvent,
|
|
31
|
+
defaultActionCallback,
|
|
32
|
+
}: {
|
|
33
|
+
behaviors: Array<Behavior>
|
|
34
|
+
event: BehaviorEvent
|
|
35
|
+
editor: PortableTextSlateEditor
|
|
36
|
+
keyGenerator: () => string
|
|
37
|
+
schema: EditorSchema
|
|
38
|
+
getSnapshot: () => EditorSnapshot
|
|
39
|
+
defaultActionCallback: (() => void) | undefined
|
|
40
|
+
nativeEvent:
|
|
41
|
+
| {
|
|
42
|
+
preventDefault: () => void
|
|
43
|
+
}
|
|
44
|
+
| undefined
|
|
45
|
+
}) {
|
|
46
|
+
debug(JSON.stringify(event, null, 2))
|
|
47
|
+
|
|
48
|
+
const defaultAction =
|
|
49
|
+
isCustomBehaviorEvent(event) ||
|
|
50
|
+
isClipboardBehaviorEvent(event) ||
|
|
51
|
+
isDragBehaviorEvent(event) ||
|
|
52
|
+
isInputBehaviorEvent(event) ||
|
|
53
|
+
isKeyboardBehaviorEvent(event) ||
|
|
54
|
+
isMouseBehaviorEvent(event) ||
|
|
55
|
+
event.type === 'deserialize' ||
|
|
56
|
+
event.type === 'serialize'
|
|
57
|
+
? undefined
|
|
58
|
+
: ({
|
|
59
|
+
...event,
|
|
60
|
+
editor,
|
|
61
|
+
} satisfies InternalBehaviorAction)
|
|
62
|
+
|
|
63
|
+
const eventBehaviors = behaviors.filter((behavior) => {
|
|
64
|
+
// Catches all events
|
|
65
|
+
if (behavior.on === '*') {
|
|
66
|
+
return true
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const [listenedNamespace] =
|
|
70
|
+
behavior.on.includes('*') && behavior.on.includes('.')
|
|
71
|
+
? behavior.on.split('.')
|
|
72
|
+
: [undefined]
|
|
73
|
+
const [eventNamespace] = event.type.includes('.')
|
|
74
|
+
? event.type.split('.')
|
|
75
|
+
: [undefined]
|
|
76
|
+
|
|
77
|
+
// Handles scenarios like a Behavior listening for `select.*` and the event
|
|
78
|
+
// `select.block` is fired.
|
|
79
|
+
if (
|
|
80
|
+
listenedNamespace !== undefined &&
|
|
81
|
+
eventNamespace !== undefined &&
|
|
82
|
+
listenedNamespace === eventNamespace
|
|
83
|
+
) {
|
|
84
|
+
return true
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Handles scenarios like a Behavior listening for `select.*` and the event
|
|
88
|
+
// `select` is fired.
|
|
89
|
+
if (
|
|
90
|
+
listenedNamespace !== undefined &&
|
|
91
|
+
eventNamespace === undefined &&
|
|
92
|
+
listenedNamespace === event.type
|
|
93
|
+
) {
|
|
94
|
+
return true
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return behavior.on === event.type
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
if (eventBehaviors.length === 0) {
|
|
101
|
+
if (defaultActionCallback) {
|
|
102
|
+
withApplyingBehaviorActions(editor, () => {
|
|
103
|
+
try {
|
|
104
|
+
defaultActionCallback()
|
|
105
|
+
} catch (error) {
|
|
106
|
+
console.error(
|
|
107
|
+
new Error(
|
|
108
|
+
`Performing action "${event.type}" failed due to: ${error.message}`,
|
|
109
|
+
),
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
})
|
|
113
|
+
return
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (!defaultAction) {
|
|
117
|
+
return
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
withApplyingBehaviorActions(editor, () => {
|
|
121
|
+
try {
|
|
122
|
+
performAction({
|
|
123
|
+
context: {
|
|
124
|
+
keyGenerator,
|
|
125
|
+
schema,
|
|
126
|
+
},
|
|
127
|
+
action: defaultAction,
|
|
128
|
+
})
|
|
129
|
+
} catch (error) {
|
|
130
|
+
console.error(
|
|
131
|
+
new Error(
|
|
132
|
+
`Performing action "${defaultAction.type}" as a result of "${event.type}" failed due to: ${error.message}`,
|
|
133
|
+
),
|
|
134
|
+
)
|
|
135
|
+
}
|
|
136
|
+
})
|
|
137
|
+
editor.onChange()
|
|
138
|
+
return
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const editorSnapshot = getSnapshot()
|
|
142
|
+
|
|
143
|
+
let behaviorOverwritten = false
|
|
144
|
+
|
|
145
|
+
for (const eventBehavior of eventBehaviors) {
|
|
146
|
+
const shouldRun =
|
|
147
|
+
eventBehavior.guard === undefined ||
|
|
148
|
+
eventBehavior.guard({
|
|
149
|
+
context: editorSnapshot.context,
|
|
150
|
+
snapshot: editorSnapshot,
|
|
151
|
+
event,
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
if (!shouldRun) {
|
|
155
|
+
continue
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const actionSets = eventBehavior.actions.map((actionSet) =>
|
|
159
|
+
actionSet(
|
|
160
|
+
{
|
|
161
|
+
context: editorSnapshot.context,
|
|
162
|
+
snapshot: editorSnapshot,
|
|
163
|
+
event,
|
|
164
|
+
},
|
|
165
|
+
shouldRun,
|
|
166
|
+
),
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
for (const actionSet of actionSets) {
|
|
170
|
+
if (actionSet.length === 0) {
|
|
171
|
+
continue
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
behaviorOverwritten =
|
|
175
|
+
behaviorOverwritten ||
|
|
176
|
+
actionSet.some((action) => action.type !== 'effect')
|
|
177
|
+
|
|
178
|
+
withApplyingBehaviorActionSet(editor, () => {
|
|
179
|
+
for (const action of actionSet) {
|
|
180
|
+
if (action.type === 'raise') {
|
|
181
|
+
performEvent({
|
|
182
|
+
behaviors,
|
|
183
|
+
event: action.event,
|
|
184
|
+
editor,
|
|
185
|
+
keyGenerator,
|
|
186
|
+
schema,
|
|
187
|
+
getSnapshot,
|
|
188
|
+
defaultActionCallback: undefined,
|
|
189
|
+
nativeEvent: undefined,
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
continue
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const internalAction = {
|
|
196
|
+
...action,
|
|
197
|
+
editor,
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
try {
|
|
201
|
+
performAction({
|
|
202
|
+
context: {
|
|
203
|
+
keyGenerator,
|
|
204
|
+
schema,
|
|
205
|
+
},
|
|
206
|
+
action: internalAction,
|
|
207
|
+
})
|
|
208
|
+
} catch (error) {
|
|
209
|
+
console.error(
|
|
210
|
+
new Error(
|
|
211
|
+
`Performing action "${internalAction.type}" as a result of "${event.type}" failed due to: ${error.message}`,
|
|
212
|
+
),
|
|
213
|
+
)
|
|
214
|
+
break
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
})
|
|
218
|
+
editor.onChange()
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (behaviorOverwritten) {
|
|
222
|
+
nativeEvent?.preventDefault()
|
|
223
|
+
break
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (!behaviorOverwritten) {
|
|
228
|
+
if (defaultActionCallback) {
|
|
229
|
+
withApplyingBehaviorActions(editor, () => {
|
|
230
|
+
try {
|
|
231
|
+
defaultActionCallback()
|
|
232
|
+
} catch (error) {
|
|
233
|
+
console.error(
|
|
234
|
+
new Error(
|
|
235
|
+
`Performing "${event.type}" failed due to: ${error.message}`,
|
|
236
|
+
),
|
|
237
|
+
)
|
|
238
|
+
}
|
|
239
|
+
})
|
|
240
|
+
return
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (!defaultAction) {
|
|
244
|
+
return
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
withApplyingBehaviorActions(editor, () => {
|
|
248
|
+
try {
|
|
249
|
+
performAction({
|
|
250
|
+
context: {
|
|
251
|
+
keyGenerator,
|
|
252
|
+
schema,
|
|
253
|
+
},
|
|
254
|
+
action: defaultAction,
|
|
255
|
+
})
|
|
256
|
+
} catch (error) {
|
|
257
|
+
console.error(
|
|
258
|
+
new Error(
|
|
259
|
+
`Performing action "${defaultAction.type}" as a result of "${event.type}" failed due to: ${error.message}`,
|
|
260
|
+
),
|
|
261
|
+
)
|
|
262
|
+
}
|
|
263
|
+
})
|
|
264
|
+
editor.onChange()
|
|
265
|
+
}
|
|
266
|
+
}
|
|
@@ -9,23 +9,16 @@ import {
|
|
|
9
9
|
setup,
|
|
10
10
|
type ActorRefFrom,
|
|
11
11
|
} from 'xstate'
|
|
12
|
-
import {performAction} from '../behavior-actions/behavior.actions'
|
|
13
12
|
import {coreBehaviors} from '../behaviors/behavior.core'
|
|
14
13
|
import {defaultBehaviors} from '../behaviors/behavior.default'
|
|
15
|
-
import
|
|
14
|
+
import {performEvent} from '../behaviors/behavior.perform-event'
|
|
16
15
|
import type {Behavior} from '../behaviors/behavior.types.behavior'
|
|
17
|
-
import {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
isMouseBehaviorEvent,
|
|
24
|
-
type CustomBehaviorEvent,
|
|
25
|
-
type ExternalSyntheticBehaviorEvent,
|
|
26
|
-
type InternalBehaviorEvent,
|
|
27
|
-
type NativeBehaviorEvent,
|
|
28
|
-
type SyntheticBehaviorEvent,
|
|
16
|
+
import type {
|
|
17
|
+
CustomBehaviorEvent,
|
|
18
|
+
ExternalSyntheticBehaviorEvent,
|
|
19
|
+
InternalBehaviorEvent,
|
|
20
|
+
NativeBehaviorEvent,
|
|
21
|
+
SyntheticBehaviorEvent,
|
|
29
22
|
} from '../behaviors/behavior.types.event'
|
|
30
23
|
import type {Converter} from '../converters/converter.types'
|
|
31
24
|
import type {EventPosition} from '../internal-utils/event-position'
|
|
@@ -37,10 +30,6 @@ import type {
|
|
|
37
30
|
} from '../types/editor'
|
|
38
31
|
import type {EditorSchema} from './define-schema'
|
|
39
32
|
import {createEditorSnapshot} from './editor-snapshot'
|
|
40
|
-
import {
|
|
41
|
-
withApplyingBehaviorActions,
|
|
42
|
-
withApplyingBehaviorActionSet,
|
|
43
|
-
} from './with-applying-behavior-actions'
|
|
44
33
|
|
|
45
34
|
export * from 'xstate/guards'
|
|
46
35
|
|
|
@@ -328,244 +317,32 @@ export const editorMachine = setup({
|
|
|
328
317
|
'clear pending events': assign({
|
|
329
318
|
pendingEvents: [],
|
|
330
319
|
}),
|
|
331
|
-
'handle behavior event':
|
|
332
|
-
(
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
: ({
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
320
|
+
'handle behavior event': ({context, event, self}) => {
|
|
321
|
+
assertEvent(event, ['behavior event', 'custom behavior event'])
|
|
322
|
+
|
|
323
|
+
performEvent({
|
|
324
|
+
behaviors: [...context.behaviors.values(), ...defaultBehaviors],
|
|
325
|
+
event: event.behaviorEvent,
|
|
326
|
+
editor: event.editor,
|
|
327
|
+
keyGenerator: context.keyGenerator,
|
|
328
|
+
schema: context.schema,
|
|
329
|
+
getSnapshot: () =>
|
|
330
|
+
createEditorSnapshot({
|
|
331
|
+
converters: [...context.converters],
|
|
332
|
+
editor: event.editor,
|
|
333
|
+
keyGenerator: context.keyGenerator,
|
|
334
|
+
readOnly: self.getSnapshot().matches({'edit mode': 'read only'}),
|
|
335
|
+
schema: context.schema,
|
|
336
|
+
hasTag: (tag) => self.getSnapshot().hasTag(tag),
|
|
337
|
+
internalDrag: context.internalDrag,
|
|
338
|
+
}),
|
|
339
|
+
nativeEvent: event.nativeEvent,
|
|
340
|
+
defaultActionCallback:
|
|
350
341
|
event.type === 'behavior event'
|
|
351
342
|
? event.defaultActionCallback
|
|
352
|
-
: undefined
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
...context.behaviors.values(),
|
|
356
|
-
...defaultBehaviors,
|
|
357
|
-
].filter((behavior) => {
|
|
358
|
-
// Catches all events
|
|
359
|
-
if (behavior.on === '*') {
|
|
360
|
-
return true
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
const [listenedNamespace] =
|
|
364
|
-
behavior.on.includes('*') && behavior.on.includes('.')
|
|
365
|
-
? behavior.on.split('.')
|
|
366
|
-
: [undefined]
|
|
367
|
-
const [eventNamespace] = event.behaviorEvent.type.includes('.')
|
|
368
|
-
? event.behaviorEvent.type.split('.')
|
|
369
|
-
: [undefined]
|
|
370
|
-
|
|
371
|
-
// Handles scenarios like a Behavior listening for `select.*` and the event
|
|
372
|
-
// `select.block` is fired.
|
|
373
|
-
if (
|
|
374
|
-
listenedNamespace !== undefined &&
|
|
375
|
-
eventNamespace !== undefined &&
|
|
376
|
-
listenedNamespace === eventNamespace
|
|
377
|
-
) {
|
|
378
|
-
return true
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
// Handles scenarios like a Behavior listening for `select.*` and the event
|
|
382
|
-
// `select` is fired.
|
|
383
|
-
if (
|
|
384
|
-
listenedNamespace !== undefined &&
|
|
385
|
-
eventNamespace === undefined &&
|
|
386
|
-
listenedNamespace === event.behaviorEvent.type
|
|
387
|
-
) {
|
|
388
|
-
return true
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
return behavior.on === event.behaviorEvent.type
|
|
392
|
-
})
|
|
393
|
-
|
|
394
|
-
if (eventBehaviors.length === 0) {
|
|
395
|
-
if (defaultActionCallback) {
|
|
396
|
-
withApplyingBehaviorActions(event.editor, () => {
|
|
397
|
-
try {
|
|
398
|
-
defaultActionCallback()
|
|
399
|
-
} catch (error) {
|
|
400
|
-
console.error(
|
|
401
|
-
new Error(
|
|
402
|
-
`Performing action "${event.behaviorEvent.type}" failed due to: ${error.message}`,
|
|
403
|
-
),
|
|
404
|
-
)
|
|
405
|
-
}
|
|
406
|
-
})
|
|
407
|
-
return
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
if (!defaultAction) {
|
|
411
|
-
return
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
withApplyingBehaviorActions(event.editor, () => {
|
|
415
|
-
try {
|
|
416
|
-
performAction({
|
|
417
|
-
context: {
|
|
418
|
-
keyGenerator: context.keyGenerator,
|
|
419
|
-
schema: context.schema,
|
|
420
|
-
},
|
|
421
|
-
action: defaultAction,
|
|
422
|
-
})
|
|
423
|
-
} catch (error) {
|
|
424
|
-
console.error(
|
|
425
|
-
new Error(
|
|
426
|
-
`Performing action "${defaultAction.type}" as a result of "${event.behaviorEvent.type}" failed due to: ${error.message}`,
|
|
427
|
-
),
|
|
428
|
-
)
|
|
429
|
-
}
|
|
430
|
-
})
|
|
431
|
-
event.editor.onChange()
|
|
432
|
-
return
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
const editorSnapshot = createEditorSnapshot({
|
|
436
|
-
converters: [...context.converters],
|
|
437
|
-
editor: event.editor,
|
|
438
|
-
keyGenerator: context.keyGenerator,
|
|
439
|
-
readOnly: self.getSnapshot().matches({'edit mode': 'read only'}),
|
|
440
|
-
schema: context.schema,
|
|
441
|
-
hasTag: (tag) => self.getSnapshot().hasTag(tag),
|
|
442
|
-
internalDrag: context.internalDrag,
|
|
443
|
-
})
|
|
444
|
-
|
|
445
|
-
let behaviorOverwritten = false
|
|
446
|
-
|
|
447
|
-
for (const eventBehavior of eventBehaviors) {
|
|
448
|
-
const shouldRun =
|
|
449
|
-
eventBehavior.guard === undefined ||
|
|
450
|
-
eventBehavior.guard({
|
|
451
|
-
context: editorSnapshot.context,
|
|
452
|
-
snapshot: editorSnapshot,
|
|
453
|
-
event: event.behaviorEvent,
|
|
454
|
-
})
|
|
455
|
-
|
|
456
|
-
if (!shouldRun) {
|
|
457
|
-
continue
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
const actionSets = eventBehavior.actions.map((actionSet) =>
|
|
461
|
-
actionSet(
|
|
462
|
-
{
|
|
463
|
-
context: editorSnapshot.context,
|
|
464
|
-
snapshot: editorSnapshot,
|
|
465
|
-
event: event.behaviorEvent,
|
|
466
|
-
},
|
|
467
|
-
shouldRun,
|
|
468
|
-
),
|
|
469
|
-
)
|
|
470
|
-
|
|
471
|
-
for (const actionSet of actionSets) {
|
|
472
|
-
behaviorOverwritten =
|
|
473
|
-
behaviorOverwritten ||
|
|
474
|
-
(actionSet.length > 0 &&
|
|
475
|
-
actionSet.some((action) => action.type !== 'effect'))
|
|
476
|
-
|
|
477
|
-
withApplyingBehaviorActionSet(event.editor, () => {
|
|
478
|
-
for (const action of actionSet) {
|
|
479
|
-
if (action.type === 'raise') {
|
|
480
|
-
if (isCustomBehaviorEvent(action.event)) {
|
|
481
|
-
enqueue.raise({
|
|
482
|
-
type: 'custom behavior event',
|
|
483
|
-
behaviorEvent: action.event as CustomBehaviorEvent,
|
|
484
|
-
editor: event.editor,
|
|
485
|
-
})
|
|
486
|
-
} else {
|
|
487
|
-
enqueue.raise({
|
|
488
|
-
type: 'behavior event',
|
|
489
|
-
behaviorEvent: action.event,
|
|
490
|
-
editor: event.editor,
|
|
491
|
-
})
|
|
492
|
-
}
|
|
493
|
-
continue
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
const internalAction = {
|
|
497
|
-
...action,
|
|
498
|
-
editor: event.editor,
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
try {
|
|
502
|
-
performAction({
|
|
503
|
-
context: {
|
|
504
|
-
keyGenerator: context.keyGenerator,
|
|
505
|
-
schema: context.schema,
|
|
506
|
-
},
|
|
507
|
-
action: internalAction,
|
|
508
|
-
})
|
|
509
|
-
} catch (error) {
|
|
510
|
-
console.error(
|
|
511
|
-
new Error(
|
|
512
|
-
`Performing action "${internalAction.type}" as a result of "${event.behaviorEvent.type}" failed due to: ${error.message}`,
|
|
513
|
-
),
|
|
514
|
-
)
|
|
515
|
-
break
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
})
|
|
519
|
-
event.editor.onChange()
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
if (behaviorOverwritten) {
|
|
523
|
-
event.nativeEvent?.preventDefault()
|
|
524
|
-
break
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
if (!behaviorOverwritten) {
|
|
529
|
-
if (defaultActionCallback) {
|
|
530
|
-
withApplyingBehaviorActions(event.editor, () => {
|
|
531
|
-
try {
|
|
532
|
-
defaultActionCallback()
|
|
533
|
-
} catch (error) {
|
|
534
|
-
console.error(
|
|
535
|
-
new Error(
|
|
536
|
-
`Performing "${event.behaviorEvent.type}" failed due to: ${error.message}`,
|
|
537
|
-
),
|
|
538
|
-
)
|
|
539
|
-
}
|
|
540
|
-
})
|
|
541
|
-
return
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
if (!defaultAction) {
|
|
545
|
-
return
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
withApplyingBehaviorActions(event.editor, () => {
|
|
549
|
-
try {
|
|
550
|
-
performAction({
|
|
551
|
-
context: {
|
|
552
|
-
keyGenerator: context.keyGenerator,
|
|
553
|
-
schema: context.schema,
|
|
554
|
-
},
|
|
555
|
-
action: defaultAction,
|
|
556
|
-
})
|
|
557
|
-
} catch (error) {
|
|
558
|
-
console.error(
|
|
559
|
-
new Error(
|
|
560
|
-
`Performing action "${defaultAction.type}" as a result of "${event.behaviorEvent.type}" failed due to: ${error.message}`,
|
|
561
|
-
),
|
|
562
|
-
)
|
|
563
|
-
}
|
|
564
|
-
})
|
|
565
|
-
event.editor.onChange()
|
|
566
|
-
}
|
|
567
|
-
},
|
|
568
|
-
),
|
|
343
|
+
: undefined,
|
|
344
|
+
})
|
|
345
|
+
},
|
|
569
346
|
},
|
|
570
347
|
}).createMachine({
|
|
571
348
|
id: 'editor',
|
|
@@ -26,11 +26,20 @@ const CURRENT_BEHAVIOR_ACTION_SET: WeakMap<
|
|
|
26
26
|
|
|
27
27
|
export function withApplyingBehaviorActionSet(editor: Editor, fn: () => void) {
|
|
28
28
|
const current = CURRENT_BEHAVIOR_ACTION_SET.get(editor)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
|
|
30
|
+
if (current) {
|
|
31
|
+
withApplyingBehaviorActions(editor, fn)
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
CURRENT_BEHAVIOR_ACTION_SET.set(
|
|
36
|
+
editor,
|
|
37
|
+
current ?? {
|
|
38
|
+
actionSetId: defaultKeyGenerator(),
|
|
39
|
+
},
|
|
40
|
+
)
|
|
32
41
|
withApplyingBehaviorActions(editor, fn)
|
|
33
|
-
CURRENT_BEHAVIOR_ACTION_SET.set(editor,
|
|
42
|
+
CURRENT_BEHAVIOR_ACTION_SET.set(editor, undefined)
|
|
34
43
|
}
|
|
35
44
|
|
|
36
45
|
export function getCurrentBehaviorActionSetId(editor: Editor) {
|
|
@@ -212,6 +212,20 @@ function parseTextBlock({
|
|
|
212
212
|
return parsedBlock
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
+
export function isSpan(
|
|
216
|
+
schema: EditorSchema,
|
|
217
|
+
child: PortableTextSpan | PortableTextObject,
|
|
218
|
+
): child is PortableTextSpan {
|
|
219
|
+
return (
|
|
220
|
+
parseSpan({
|
|
221
|
+
span: child,
|
|
222
|
+
markDefKeyMap: new Map(),
|
|
223
|
+
context: {schema, keyGenerator: () => ''},
|
|
224
|
+
options: {refreshKeys: false},
|
|
225
|
+
}) !== undefined
|
|
226
|
+
)
|
|
227
|
+
}
|
|
228
|
+
|
|
215
229
|
export function parseSpan({
|
|
216
230
|
span,
|
|
217
231
|
context,
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import {page, userEvent} from '@vitest/browser/context'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import {describe, expect, test, vi} from 'vitest'
|
|
4
|
+
import {render} from 'vitest-browser-react'
|
|
5
|
+
import {PortableTextEditable, type Editor} from '..'
|
|
6
|
+
import {defineSchema} from '../editor/define-schema'
|
|
7
|
+
import {EditorProvider} from '../editor/editor-provider'
|
|
8
|
+
import {getTersePt} from '../internal-utils/terse-pt'
|
|
9
|
+
import {createTestKeyGenerator} from '../internal-utils/test-key-generator'
|
|
10
|
+
import {getTextMarks} from '../internal-utils/text-marks'
|
|
11
|
+
import {EditorRefPlugin} from './plugin.editor-ref'
|
|
12
|
+
import {MarkdownPlugin} from './plugin.markdown'
|
|
13
|
+
|
|
14
|
+
describe(MarkdownPlugin.name, () => {
|
|
15
|
+
test('Scenario: Undoing bold shortcut', async () => {
|
|
16
|
+
const editorRef = React.createRef<Editor>()
|
|
17
|
+
|
|
18
|
+
render(
|
|
19
|
+
<EditorProvider
|
|
20
|
+
initialConfig={{
|
|
21
|
+
keyGenerator: createTestKeyGenerator(),
|
|
22
|
+
schemaDefinition: defineSchema({decorators: [{name: 'strong'}]}),
|
|
23
|
+
}}
|
|
24
|
+
>
|
|
25
|
+
<EditorRefPlugin ref={editorRef} />
|
|
26
|
+
<PortableTextEditable />
|
|
27
|
+
<MarkdownPlugin
|
|
28
|
+
config={{
|
|
29
|
+
boldDecorator: () => 'strong',
|
|
30
|
+
}}
|
|
31
|
+
/>
|
|
32
|
+
</EditorProvider>,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
const locator = page.getByRole('textbox')
|
|
36
|
+
|
|
37
|
+
await vi.waitFor(() => expect.element(locator).toBeInTheDocument())
|
|
38
|
+
|
|
39
|
+
await userEvent.type(locator, '**Hello world!**')
|
|
40
|
+
|
|
41
|
+
await vi.waitFor(() => {
|
|
42
|
+
expect(
|
|
43
|
+
getTersePt(editorRef.current?.getSnapshot().context.value),
|
|
44
|
+
).toEqual(['Hello world!'])
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
await vi.waitFor(() => {
|
|
48
|
+
expect(
|
|
49
|
+
getTextMarks(
|
|
50
|
+
editorRef.current!.getSnapshot().context.value,
|
|
51
|
+
'Hello world!',
|
|
52
|
+
),
|
|
53
|
+
).toEqual(['strong'])
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
editorRef.current?.send({type: 'history.undo'})
|
|
57
|
+
|
|
58
|
+
await vi.waitFor(() => {
|
|
59
|
+
expect(
|
|
60
|
+
getTersePt(editorRef.current?.getSnapshot().context.value),
|
|
61
|
+
).toEqual(['**Hello world!**'])
|
|
62
|
+
})
|
|
63
|
+
})
|
|
64
|
+
})
|