@portabletext/editor 1.5.6 → 1.6.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/index.d.mts +452 -28
- package/lib/index.d.ts +452 -28
- package/lib/index.esm.js +2533 -2398
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +2533 -2398
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +2533 -2398
- package/lib/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/editor/Editable.tsx +2 -2
- package/src/editor/__tests__/self-solving.test.tsx +9 -9
- package/src/editor/behavior/behavior.actions.ts +51 -4
- package/src/editor/behavior/behavior.core.decorators.ts +52 -0
- package/src/editor/behavior/behavior.core.ts +5 -0
- package/src/editor/behavior/behavior.markdown.ts +7 -6
- package/src/editor/behavior/behavior.types.ts +15 -0
- package/src/editor/editor-machine.ts +32 -4
- package/src/editor/plugins/create-with-event-listeners.ts +24 -0
- package/src/editor/plugins/createWithEditableAPI.ts +10 -2
- package/src/editor/plugins/createWithHotKeys.ts +10 -1
- package/src/editor/plugins/createWithPortableTextMarkModel.ts +215 -220
- package/src/types/editor.ts +0 -14
package/package.json
CHANGED
package/src/editor/Editable.tsx
CHANGED
|
@@ -169,8 +169,8 @@ export const PortableTextEditable = forwardRef<
|
|
|
169
169
|
[editorActor, schemaTypes],
|
|
170
170
|
)
|
|
171
171
|
const withHotKeys = useMemo(
|
|
172
|
-
() => createWithHotkeys(portableTextEditor, hotkeys),
|
|
173
|
-
[hotkeys, portableTextEditor],
|
|
172
|
+
() => createWithHotkeys(editorActor, portableTextEditor, hotkeys),
|
|
173
|
+
[editorActor, hotkeys, portableTextEditor],
|
|
174
174
|
)
|
|
175
175
|
|
|
176
176
|
// Output a minimal React editor inside Editable when in readOnly mode.
|
|
@@ -127,23 +127,23 @@ describe('Feature: Self-solving', () => {
|
|
|
127
127
|
},
|
|
128
128
|
})
|
|
129
129
|
expect(onChange).toHaveBeenNthCalledWith(4, {
|
|
130
|
-
type: '
|
|
131
|
-
|
|
130
|
+
type: 'selection',
|
|
131
|
+
selection: {
|
|
132
|
+
...getTextSelection(initialValue, 'foo'),
|
|
133
|
+
backward: false,
|
|
134
|
+
},
|
|
132
135
|
})
|
|
133
136
|
expect(onChange).toHaveBeenNthCalledWith(5, {
|
|
134
137
|
type: 'patch',
|
|
135
|
-
patch:
|
|
138
|
+
patch: spanPatch,
|
|
136
139
|
})
|
|
137
140
|
expect(onChange).toHaveBeenNthCalledWith(6, {
|
|
138
141
|
type: 'patch',
|
|
139
|
-
patch:
|
|
142
|
+
patch: blockPatch,
|
|
140
143
|
})
|
|
141
144
|
expect(onChange).toHaveBeenNthCalledWith(7, {
|
|
142
|
-
type: '
|
|
143
|
-
|
|
144
|
-
...getTextSelection(initialValue, 'foo'),
|
|
145
|
-
backward: false,
|
|
146
|
-
},
|
|
145
|
+
type: 'patch',
|
|
146
|
+
patch: strongPatch,
|
|
147
147
|
})
|
|
148
148
|
expect(onChange).toHaveBeenNthCalledWith(8, {
|
|
149
149
|
type: 'selection',
|
|
@@ -7,6 +7,11 @@ import {
|
|
|
7
7
|
} from 'slate'
|
|
8
8
|
import type {PortableTextMemberSchemaTypes} from '../../types/editor'
|
|
9
9
|
import {toSlateRange} from '../../utils/ranges'
|
|
10
|
+
import {
|
|
11
|
+
addDecoratorActionImplementation,
|
|
12
|
+
removeDecoratorActionImplementation,
|
|
13
|
+
toggleDecoratorActionImplementation,
|
|
14
|
+
} from '../plugins/createWithPortableTextMarkModel'
|
|
10
15
|
import {insertBreakActionImplementation} from './behavior.action.insert-break'
|
|
11
16
|
import type {
|
|
12
17
|
BehaviorAction,
|
|
@@ -36,6 +41,9 @@ type BehaviourActionImplementations = {
|
|
|
36
41
|
}
|
|
37
42
|
|
|
38
43
|
const behaviorActionImplementations: BehaviourActionImplementations = {
|
|
44
|
+
'decorator.add': addDecoratorActionImplementation,
|
|
45
|
+
'decorator.remove': removeDecoratorActionImplementation,
|
|
46
|
+
'decorator.toggle': toggleDecoratorActionImplementation,
|
|
39
47
|
'set block': ({action}) => {
|
|
40
48
|
for (const path of action.paths) {
|
|
41
49
|
const at = toSlateRange(
|
|
@@ -116,10 +124,21 @@ const behaviorActionImplementations: BehaviourActionImplementations = {
|
|
|
116
124
|
action.effect()
|
|
117
125
|
},
|
|
118
126
|
'select': ({action}) => {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
127
|
+
const newSelection = toSlateRange(action.selection, action.editor)
|
|
128
|
+
|
|
129
|
+
if (newSelection) {
|
|
130
|
+
Transforms.select(action.editor, newSelection)
|
|
131
|
+
} else {
|
|
132
|
+
Transforms.deselect(action.editor)
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
'reselect': ({action}) => {
|
|
136
|
+
const selection = action.editor.selection
|
|
137
|
+
|
|
138
|
+
if (selection) {
|
|
139
|
+
Transforms.select(action.editor, {...selection})
|
|
140
|
+
action.editor.selection = {...selection}
|
|
141
|
+
}
|
|
123
142
|
},
|
|
124
143
|
}
|
|
125
144
|
|
|
@@ -173,6 +192,13 @@ export function performAction({
|
|
|
173
192
|
})
|
|
174
193
|
break
|
|
175
194
|
}
|
|
195
|
+
case 'reselect': {
|
|
196
|
+
behaviorActionImplementations.reselect({
|
|
197
|
+
context,
|
|
198
|
+
action,
|
|
199
|
+
})
|
|
200
|
+
break
|
|
201
|
+
}
|
|
176
202
|
default: {
|
|
177
203
|
performDefaultAction({context, action})
|
|
178
204
|
}
|
|
@@ -187,6 +213,27 @@ export function performDefaultAction({
|
|
|
187
213
|
action: PickFromUnion<BehaviorAction, 'type', BehaviorEvent['type']>
|
|
188
214
|
}) {
|
|
189
215
|
switch (action.type) {
|
|
216
|
+
case 'decorator.add': {
|
|
217
|
+
behaviorActionImplementations['decorator.add']({
|
|
218
|
+
context,
|
|
219
|
+
action,
|
|
220
|
+
})
|
|
221
|
+
break
|
|
222
|
+
}
|
|
223
|
+
case 'decorator.remove': {
|
|
224
|
+
behaviorActionImplementations['decorator.remove']({
|
|
225
|
+
context,
|
|
226
|
+
action,
|
|
227
|
+
})
|
|
228
|
+
break
|
|
229
|
+
}
|
|
230
|
+
case 'decorator.toggle': {
|
|
231
|
+
behaviorActionImplementations['decorator.toggle']({
|
|
232
|
+
context,
|
|
233
|
+
action,
|
|
234
|
+
})
|
|
235
|
+
break
|
|
236
|
+
}
|
|
190
237
|
case 'delete backward': {
|
|
191
238
|
behaviorActionImplementations['delete backward']({
|
|
192
239
|
context,
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import {defineBehavior} from './behavior.types'
|
|
2
|
+
|
|
3
|
+
const decoratorAdd = defineBehavior({
|
|
4
|
+
on: 'decorator.add',
|
|
5
|
+
actions: [
|
|
6
|
+
({event}) => [
|
|
7
|
+
{
|
|
8
|
+
type: 'decorator.add',
|
|
9
|
+
decorator: event.decorator,
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
type: 'reselect',
|
|
13
|
+
},
|
|
14
|
+
],
|
|
15
|
+
],
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const decoratorRemove = defineBehavior({
|
|
19
|
+
on: 'decorator.remove',
|
|
20
|
+
actions: [
|
|
21
|
+
({event}) => [
|
|
22
|
+
{
|
|
23
|
+
type: 'decorator.remove',
|
|
24
|
+
decorator: event.decorator,
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
type: 'reselect',
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
],
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
const decoratorToggle = defineBehavior({
|
|
34
|
+
on: 'decorator.toggle',
|
|
35
|
+
actions: [
|
|
36
|
+
({event}) => [
|
|
37
|
+
{
|
|
38
|
+
type: 'decorator.toggle',
|
|
39
|
+
decorator: event.decorator,
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
type: 'reselect',
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
],
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
export const coreDecoratorBehaviors = {
|
|
49
|
+
decoratorAdd,
|
|
50
|
+
decoratorRemove,
|
|
51
|
+
decoratorToggle,
|
|
52
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {coreBlockObjectBehaviors} from './behavior.core.block-objects'
|
|
2
|
+
import {coreDecoratorBehaviors} from './behavior.core.decorators'
|
|
2
3
|
import {coreListBehaviors} from './behavior.core.lists'
|
|
3
4
|
import {defineBehavior} from './behavior.types'
|
|
4
5
|
|
|
@@ -12,6 +13,9 @@ const softReturn = defineBehavior({
|
|
|
12
13
|
*/
|
|
13
14
|
export const coreBehaviors = [
|
|
14
15
|
softReturn,
|
|
16
|
+
coreDecoratorBehaviors.decoratorAdd,
|
|
17
|
+
coreDecoratorBehaviors.decoratorRemove,
|
|
18
|
+
coreDecoratorBehaviors.decoratorToggle,
|
|
15
19
|
coreBlockObjectBehaviors.breakingBlockObject,
|
|
16
20
|
coreBlockObjectBehaviors.deletingEmptyTextBlockAfterBlockObject,
|
|
17
21
|
coreBlockObjectBehaviors.deletingEmptyTextBlockBeforeBlockObject,
|
|
@@ -24,6 +28,7 @@ export const coreBehaviors = [
|
|
|
24
28
|
*/
|
|
25
29
|
export const coreBehavior = {
|
|
26
30
|
softReturn,
|
|
31
|
+
decorators: coreDecoratorBehaviors,
|
|
27
32
|
blockObjects: coreBlockObjectBehaviors,
|
|
28
33
|
lists: coreListBehaviors,
|
|
29
34
|
}
|
|
@@ -227,11 +227,13 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
|
|
|
227
227
|
return false
|
|
228
228
|
}
|
|
229
229
|
|
|
230
|
+
const defaultStyle = config.mapDefaultStyle(context.schema)
|
|
230
231
|
const looksLikeUnorderedList = /^(-|\*)/.test(focusSpan.node.text)
|
|
231
232
|
const unorderedListStyle = config.mapUnorderedListStyle(context.schema)
|
|
232
233
|
const caretAtTheEndOfUnorderedList = context.selection.focus.offset === 1
|
|
233
234
|
|
|
234
235
|
if (
|
|
236
|
+
defaultStyle &&
|
|
235
237
|
caretAtTheEndOfUnorderedList &&
|
|
236
238
|
looksLikeUnorderedList &&
|
|
237
239
|
unorderedListStyle !== undefined
|
|
@@ -241,6 +243,7 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
|
|
|
241
243
|
focusSpan,
|
|
242
244
|
listItem: unorderedListStyle,
|
|
243
245
|
listItemLength: 1,
|
|
246
|
+
style: defaultStyle,
|
|
244
247
|
}
|
|
245
248
|
}
|
|
246
249
|
|
|
@@ -249,6 +252,7 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
|
|
|
249
252
|
const caretAtTheEndOfOrderedList = context.selection.focus.offset === 2
|
|
250
253
|
|
|
251
254
|
if (
|
|
255
|
+
defaultStyle &&
|
|
252
256
|
caretAtTheEndOfOrderedList &&
|
|
253
257
|
looksLikeOrderedList &&
|
|
254
258
|
orderedListStyle !== undefined
|
|
@@ -258,6 +262,7 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
|
|
|
258
262
|
focusSpan,
|
|
259
263
|
listItem: orderedListStyle,
|
|
260
264
|
listItemLength: 2,
|
|
265
|
+
style: defaultStyle,
|
|
261
266
|
}
|
|
262
267
|
}
|
|
263
268
|
|
|
@@ -270,16 +275,12 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
|
|
|
270
275
|
text: ' ',
|
|
271
276
|
},
|
|
272
277
|
],
|
|
273
|
-
(_, {focusTextBlock, focusSpan, listItem, listItemLength}) => [
|
|
274
|
-
{
|
|
275
|
-
type: 'unset block',
|
|
276
|
-
props: ['style'],
|
|
277
|
-
paths: [focusTextBlock.path],
|
|
278
|
-
},
|
|
278
|
+
(_, {focusTextBlock, focusSpan, style, listItem, listItemLength}) => [
|
|
279
279
|
{
|
|
280
280
|
type: 'set block',
|
|
281
281
|
listItem,
|
|
282
282
|
level: 1,
|
|
283
|
+
style,
|
|
283
284
|
paths: [focusTextBlock.path],
|
|
284
285
|
},
|
|
285
286
|
{
|
|
@@ -20,6 +20,18 @@ export type BehaviorContext = {
|
|
|
20
20
|
* @alpha
|
|
21
21
|
*/
|
|
22
22
|
export type BehaviorEvent =
|
|
23
|
+
| {
|
|
24
|
+
type: 'decorator.add'
|
|
25
|
+
decorator: string
|
|
26
|
+
}
|
|
27
|
+
| {
|
|
28
|
+
type: 'decorator.remove'
|
|
29
|
+
decorator: string
|
|
30
|
+
}
|
|
31
|
+
| {
|
|
32
|
+
type: 'decorator.toggle'
|
|
33
|
+
decorator: string
|
|
34
|
+
}
|
|
23
35
|
| {
|
|
24
36
|
type: 'delete backward'
|
|
25
37
|
unit: TextUnit
|
|
@@ -87,6 +99,9 @@ export type BehaviorActionIntend =
|
|
|
87
99
|
type: 'select'
|
|
88
100
|
selection: EditorSelection
|
|
89
101
|
}
|
|
102
|
+
| {
|
|
103
|
+
type: 'reselect'
|
|
104
|
+
}
|
|
90
105
|
|
|
91
106
|
/**
|
|
92
107
|
* @alpha
|
|
@@ -20,7 +20,7 @@ import type {
|
|
|
20
20
|
import {toPortableTextRange} from '../utils/ranges'
|
|
21
21
|
import {fromSlateValue} from '../utils/values'
|
|
22
22
|
import {KEY_TO_VALUE_ELEMENT} from '../utils/weakMaps'
|
|
23
|
-
import {performAction
|
|
23
|
+
import {performAction} from './behavior/behavior.actions'
|
|
24
24
|
import {coreBehaviors} from './behavior/behavior.core'
|
|
25
25
|
import type {
|
|
26
26
|
Behavior,
|
|
@@ -197,7 +197,11 @@ export const editorMachine = setup({
|
|
|
197
197
|
)
|
|
198
198
|
|
|
199
199
|
if (eventBehaviors.length === 0) {
|
|
200
|
-
|
|
200
|
+
enqueue.raise({
|
|
201
|
+
type: 'behavior action intends',
|
|
202
|
+
editor: event.editor,
|
|
203
|
+
actionIntends: [defaultAction],
|
|
204
|
+
})
|
|
201
205
|
return
|
|
202
206
|
}
|
|
203
207
|
|
|
@@ -216,7 +220,11 @@ export const editorMachine = setup({
|
|
|
216
220
|
console.warn(
|
|
217
221
|
`Unable to handle event ${event.type} due to missing selection`,
|
|
218
222
|
)
|
|
219
|
-
|
|
223
|
+
enqueue.raise({
|
|
224
|
+
type: 'behavior action intends',
|
|
225
|
+
editor: event.editor,
|
|
226
|
+
actionIntends: [defaultAction],
|
|
227
|
+
})
|
|
220
228
|
return
|
|
221
229
|
}
|
|
222
230
|
|
|
@@ -264,7 +272,11 @@ export const editorMachine = setup({
|
|
|
264
272
|
}
|
|
265
273
|
|
|
266
274
|
if (!behaviorOverwritten) {
|
|
267
|
-
|
|
275
|
+
enqueue.raise({
|
|
276
|
+
type: 'behavior action intends',
|
|
277
|
+
editor: event.editor,
|
|
278
|
+
actionIntends: [defaultAction],
|
|
279
|
+
})
|
|
268
280
|
}
|
|
269
281
|
}),
|
|
270
282
|
},
|
|
@@ -315,6 +327,22 @@ export const editorMachine = setup({
|
|
|
315
327
|
})
|
|
316
328
|
event.editor.onChange()
|
|
317
329
|
},
|
|
330
|
+
enqueueActions(({context, event, enqueue}) => {
|
|
331
|
+
if (
|
|
332
|
+
event.actionIntends.some(
|
|
333
|
+
(actionIntend) => actionIntend.type === 'reselect',
|
|
334
|
+
)
|
|
335
|
+
) {
|
|
336
|
+
enqueue.raise({
|
|
337
|
+
type: 'selection',
|
|
338
|
+
selection: toPortableTextRange(
|
|
339
|
+
event.editor.children,
|
|
340
|
+
event.editor.selection,
|
|
341
|
+
context.schema,
|
|
342
|
+
),
|
|
343
|
+
})
|
|
344
|
+
}
|
|
345
|
+
}),
|
|
318
346
|
],
|
|
319
347
|
},
|
|
320
348
|
},
|
|
@@ -3,6 +3,30 @@ import type {EditorActor} from '../editor-machine'
|
|
|
3
3
|
|
|
4
4
|
export function createWithEventListeners(editorActor: EditorActor) {
|
|
5
5
|
return function withEventListeners(editor: Editor) {
|
|
6
|
+
editor.addMark = (mark) => {
|
|
7
|
+
editorActor.send({
|
|
8
|
+
type: 'behavior event',
|
|
9
|
+
behaviorEvent: {
|
|
10
|
+
type: 'decorator.add',
|
|
11
|
+
decorator: mark,
|
|
12
|
+
},
|
|
13
|
+
editor,
|
|
14
|
+
})
|
|
15
|
+
return
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
editor.removeMark = (mark) => {
|
|
19
|
+
editorActor.send({
|
|
20
|
+
type: 'behavior event',
|
|
21
|
+
behaviorEvent: {
|
|
22
|
+
type: 'decorator.remove',
|
|
23
|
+
decorator: mark,
|
|
24
|
+
},
|
|
25
|
+
editor,
|
|
26
|
+
})
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
|
|
6
30
|
editor.deleteBackward = (unit) => {
|
|
7
31
|
editorActor.send({
|
|
8
32
|
type: 'behavior event',
|
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
} from '../../utils/weakMaps'
|
|
39
39
|
import type {EditorActor} from '../editor-machine'
|
|
40
40
|
import type {PortableTextEditor} from '../PortableTextEditor'
|
|
41
|
+
import {isDecoratorActive} from './createWithPortableTextMarkModel'
|
|
41
42
|
|
|
42
43
|
const debug = debugWithName('API:editable')
|
|
43
44
|
|
|
@@ -57,7 +58,14 @@ export function createWithEditableAPI(
|
|
|
57
58
|
ReactEditor.blur(editor)
|
|
58
59
|
},
|
|
59
60
|
toggleMark: (mark: string): void => {
|
|
60
|
-
|
|
61
|
+
editorActor.send({
|
|
62
|
+
type: 'behavior event',
|
|
63
|
+
behaviorEvent: {
|
|
64
|
+
type: 'decorator.toggle',
|
|
65
|
+
decorator: mark,
|
|
66
|
+
},
|
|
67
|
+
editor,
|
|
68
|
+
})
|
|
61
69
|
},
|
|
62
70
|
toggleList: (listStyle: string): void => {
|
|
63
71
|
editor.pteToggleListItem(listStyle)
|
|
@@ -69,7 +77,7 @@ export function createWithEditableAPI(
|
|
|
69
77
|
// Try/catch this, as Slate may error because the selection is currently wrong
|
|
70
78
|
// TODO: catch only relevant error from Slate
|
|
71
79
|
try {
|
|
72
|
-
return editor
|
|
80
|
+
return isDecoratorActive({editor, decorator: mark})
|
|
73
81
|
} catch (err) {
|
|
74
82
|
console.warn(err)
|
|
75
83
|
return false
|
|
@@ -7,6 +7,7 @@ import type {PortableTextSlateEditor} from '../../types/editor'
|
|
|
7
7
|
import type {HotkeyOptions} from '../../types/options'
|
|
8
8
|
import type {SlateTextBlock, VoidElement} from '../../types/slate'
|
|
9
9
|
import {debugWithName} from '../../utils/debug'
|
|
10
|
+
import type {EditorActor} from '../editor-machine'
|
|
10
11
|
import type {PortableTextEditor} from '../PortableTextEditor'
|
|
11
12
|
|
|
12
13
|
const debug = debugWithName('plugin:withHotKeys')
|
|
@@ -26,6 +27,7 @@ const DEFAULT_HOTKEYS: HotkeyOptions = {
|
|
|
26
27
|
*
|
|
27
28
|
*/
|
|
28
29
|
export function createWithHotkeys(
|
|
30
|
+
editorActor: EditorActor,
|
|
29
31
|
portableTextEditor: PortableTextEditor,
|
|
30
32
|
hotkeysFromOptions?: HotkeyOptions,
|
|
31
33
|
): (editor: PortableTextSlateEditor & ReactEditor) => any {
|
|
@@ -46,7 +48,14 @@ export function createWithHotkeys(
|
|
|
46
48
|
if (possibleMark) {
|
|
47
49
|
const mark = possibleMark[hotkey]
|
|
48
50
|
debug(`HotKey ${hotkey} to toggle ${mark}`)
|
|
49
|
-
|
|
51
|
+
editorActor.send({
|
|
52
|
+
type: 'behavior event',
|
|
53
|
+
behaviorEvent: {
|
|
54
|
+
type: 'decorator.toggle',
|
|
55
|
+
decorator: mark,
|
|
56
|
+
},
|
|
57
|
+
editor,
|
|
58
|
+
})
|
|
50
59
|
}
|
|
51
60
|
}
|
|
52
61
|
}
|