@portabletext/editor 1.1.9 → 1.1.11
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 +295 -635
- package/lib/index.d.ts +295 -635
- package/lib/index.esm.js +360 -208
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +357 -205
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +360 -208
- package/lib/index.mjs.map +1 -1
- package/package.json +13 -13
- package/src/editor/Editable.tsx +0 -13
- package/src/editor/behavior/behavior.action.insert-break.ts +206 -0
- package/src/editor/behavior/behavior.actions.ts +91 -17
- package/src/editor/behavior/behavior.core.ts +8 -26
- package/src/editor/behavior/behavior.types.ts +41 -15
- package/src/editor/editor-machine.ts +124 -30
- package/src/editor/plugins/create-with-event-listeners.ts +55 -0
- package/src/editor/plugins/createWithUtils.ts +7 -1
- package/src/editor/plugins/index.ts +15 -18
- package/src/index.ts +1 -1
- package/src/types/editor.ts +5 -1
- package/src/editor/plugins/createWithInsertBreak.ts +0 -220
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@portabletext/editor",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.11",
|
|
4
4
|
"description": "Portable Text Editor made in React",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sanity",
|
|
@@ -57,13 +57,13 @@
|
|
|
57
57
|
"@jest/types": "^29.6.3",
|
|
58
58
|
"@playwright/test": "1.48.2",
|
|
59
59
|
"@portabletext/toolkit": "^2.0.15",
|
|
60
|
-
"@sanity/block-tools": "^3.62.
|
|
60
|
+
"@sanity/block-tools": "^3.62.3",
|
|
61
61
|
"@sanity/diff-match-patch": "^3.1.1",
|
|
62
|
-
"@sanity/pkg-utils": "^6.11.
|
|
63
|
-
"@sanity/schema": "^3.62.
|
|
64
|
-
"@sanity/types": "^3.62.
|
|
65
|
-
"@sanity/ui": "^2.8.
|
|
66
|
-
"@sanity/util": "^3.62.
|
|
62
|
+
"@sanity/pkg-utils": "^6.11.7",
|
|
63
|
+
"@sanity/schema": "^3.62.3",
|
|
64
|
+
"@sanity/types": "^3.62.3",
|
|
65
|
+
"@sanity/ui": "^2.8.13",
|
|
66
|
+
"@sanity/util": "^3.62.3",
|
|
67
67
|
"@testing-library/dom": "^10.4.0",
|
|
68
68
|
"@testing-library/jest-dom": "^6.6.2",
|
|
69
69
|
"@testing-library/react": "^16.0.1",
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"@types/react-dom": "^18.3.1",
|
|
79
79
|
"@types/ws": "~8.5.12",
|
|
80
80
|
"@vitejs/plugin-react": "^4.3.3",
|
|
81
|
-
"@vitest/browser": "^2.1.
|
|
81
|
+
"@vitest/browser": "^2.1.4",
|
|
82
82
|
"@xstate/react": "^4.1.3",
|
|
83
83
|
"dotenv": "^16.4.5",
|
|
84
84
|
"express": "^4.21.1",
|
|
@@ -96,15 +96,15 @@
|
|
|
96
96
|
"ts-node": "^10.9.2",
|
|
97
97
|
"typescript": "5.6.3",
|
|
98
98
|
"vite": "^5.4.10",
|
|
99
|
-
"vitest": "^2.1.
|
|
99
|
+
"vitest": "^2.1.4",
|
|
100
100
|
"vitest-browser-react": "^0.0.3",
|
|
101
101
|
"@sanity/gherkin-driver": "^0.0.1"
|
|
102
102
|
},
|
|
103
103
|
"peerDependencies": {
|
|
104
|
-
"@sanity/block-tools": "^3.62.
|
|
105
|
-
"@sanity/schema": "^3.62.
|
|
106
|
-
"@sanity/types": "^3.62.
|
|
107
|
-
"@sanity/util": "^3.62.
|
|
104
|
+
"@sanity/block-tools": "^3.62.3",
|
|
105
|
+
"@sanity/schema": "^3.62.3",
|
|
106
|
+
"@sanity/types": "^3.62.3",
|
|
107
|
+
"@sanity/util": "^3.62.3",
|
|
108
108
|
"react": "^16.9 || ^17 || ^18",
|
|
109
109
|
"rxjs": "^7.8.1",
|
|
110
110
|
"styled-components": "^6.1.13"
|
package/src/editor/Editable.tsx
CHANGED
|
@@ -550,14 +550,6 @@ export const PortableTextEditable = forwardRef<
|
|
|
550
550
|
if (onBeforeInput) {
|
|
551
551
|
onBeforeInput(event)
|
|
552
552
|
}
|
|
553
|
-
|
|
554
|
-
if (!event.defaultPrevented && event.inputType === 'insertText') {
|
|
555
|
-
editorActor.send({
|
|
556
|
-
type: 'before insert text',
|
|
557
|
-
nativeEvent: event,
|
|
558
|
-
editor: slateEditor,
|
|
559
|
-
})
|
|
560
|
-
}
|
|
561
553
|
},
|
|
562
554
|
[onBeforeInput],
|
|
563
555
|
)
|
|
@@ -646,11 +638,6 @@ export const PortableTextEditable = forwardRef<
|
|
|
646
638
|
props.onKeyDown(event)
|
|
647
639
|
}
|
|
648
640
|
if (!event.isDefaultPrevented()) {
|
|
649
|
-
editorActor.send({
|
|
650
|
-
type: 'key down',
|
|
651
|
-
nativeEvent: event.nativeEvent,
|
|
652
|
-
editor: slateEditor,
|
|
653
|
-
})
|
|
654
641
|
slateEditor.pteWithHotKeys(event)
|
|
655
642
|
}
|
|
656
643
|
},
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import {isEqual} from 'lodash'
|
|
2
|
+
import {Editor, Node, Path, Range, Transforms} from 'slate'
|
|
3
|
+
import type {SlateTextBlock, VoidElement} from '../../types/slate'
|
|
4
|
+
import type {BehaviourActionImplementation} from './behavior.actions'
|
|
5
|
+
import type {BehaviorAction, PickFromUnion} from './behavior.types'
|
|
6
|
+
|
|
7
|
+
export const insertBreakActionImplementation: BehaviourActionImplementation<
|
|
8
|
+
PickFromUnion<BehaviorAction, 'type', 'insert break' | 'insert soft break'>
|
|
9
|
+
> = ({context, action}) => {
|
|
10
|
+
const keyGenerator = context.keyGenerator
|
|
11
|
+
const schema = context.schema
|
|
12
|
+
const editor = action.editor
|
|
13
|
+
|
|
14
|
+
if (!editor.selection) {
|
|
15
|
+
return
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const [focusSpan] = Array.from(
|
|
19
|
+
Editor.nodes(editor, {
|
|
20
|
+
mode: 'lowest',
|
|
21
|
+
at: editor.selection.focus,
|
|
22
|
+
match: (n) => editor.isTextSpan(n),
|
|
23
|
+
voids: false,
|
|
24
|
+
}),
|
|
25
|
+
)[0] ?? [undefined]
|
|
26
|
+
const focusDecorators =
|
|
27
|
+
focusSpan.marks?.filter((mark) =>
|
|
28
|
+
schema.decorators.some((decorator) => decorator.value === mark),
|
|
29
|
+
) ?? []
|
|
30
|
+
const focusAnnotations =
|
|
31
|
+
focusSpan.marks?.filter(
|
|
32
|
+
(mark) =>
|
|
33
|
+
!schema.decorators.some((decorator) => decorator.value === mark),
|
|
34
|
+
) ?? []
|
|
35
|
+
|
|
36
|
+
const focusBlockPath = editor.selection.focus.path.slice(0, 1)
|
|
37
|
+
const focusBlock = Node.descendant(editor, focusBlockPath) as
|
|
38
|
+
| SlateTextBlock
|
|
39
|
+
| VoidElement
|
|
40
|
+
|
|
41
|
+
if (editor.isTextBlock(focusBlock)) {
|
|
42
|
+
const [start, end] = Range.edges(editor.selection)
|
|
43
|
+
const atTheStartOfBlock = isEqual(end, {
|
|
44
|
+
path: [...focusBlockPath, 0],
|
|
45
|
+
offset: 0,
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
if (atTheStartOfBlock && Range.isCollapsed(editor.selection)) {
|
|
49
|
+
Editor.insertNode(
|
|
50
|
+
editor,
|
|
51
|
+
editor.pteCreateTextBlock({
|
|
52
|
+
decorators: focusAnnotations.length === 0 ? focusDecorators : [],
|
|
53
|
+
listItem: focusBlock.listItem,
|
|
54
|
+
level: focusBlock.level,
|
|
55
|
+
}),
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
const [nextBlockPath] = Path.next(focusBlockPath)
|
|
59
|
+
|
|
60
|
+
Transforms.select(editor, {
|
|
61
|
+
anchor: {path: [nextBlockPath, 0], offset: 0},
|
|
62
|
+
focus: {path: [nextBlockPath, 0], offset: 0},
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const lastFocusBlockChild =
|
|
69
|
+
focusBlock.children[focusBlock.children.length - 1]
|
|
70
|
+
const atTheEndOfBlock = isEqual(start, {
|
|
71
|
+
path: [...focusBlockPath, focusBlock.children.length - 1],
|
|
72
|
+
offset: editor.isTextSpan(lastFocusBlockChild)
|
|
73
|
+
? lastFocusBlockChild.text.length
|
|
74
|
+
: 0,
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
if (atTheEndOfBlock && Range.isCollapsed(editor.selection)) {
|
|
78
|
+
Editor.insertNode(
|
|
79
|
+
editor,
|
|
80
|
+
editor.pteCreateTextBlock({
|
|
81
|
+
decorators: [],
|
|
82
|
+
listItem: focusBlock.listItem,
|
|
83
|
+
level: focusBlock.level,
|
|
84
|
+
}),
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
const [nextBlockPath] = Path.next(focusBlockPath)
|
|
88
|
+
|
|
89
|
+
Transforms.setSelection(editor, {
|
|
90
|
+
anchor: {path: [nextBlockPath, 0], offset: 0},
|
|
91
|
+
focus: {path: [nextBlockPath, 0], offset: 0},
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
return
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const isInTheMiddleOfNode = !atTheStartOfBlock && !atTheEndOfBlock
|
|
98
|
+
|
|
99
|
+
if (isInTheMiddleOfNode) {
|
|
100
|
+
Editor.withoutNormalizing(editor, () => {
|
|
101
|
+
if (!editor.selection) {
|
|
102
|
+
return
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
Transforms.splitNodes(editor, {
|
|
106
|
+
at: editor.selection,
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
const [nextNode, nextNodePath] = Editor.node(
|
|
110
|
+
editor,
|
|
111
|
+
Path.next(focusBlockPath),
|
|
112
|
+
{depth: 1},
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
Transforms.setSelection(editor, {
|
|
116
|
+
anchor: {path: [...nextNodePath, 0], offset: 0},
|
|
117
|
+
focus: {path: [...nextNodePath, 0], offset: 0},
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Assign new keys to markDefs that are now split across two blocks
|
|
122
|
+
*/
|
|
123
|
+
if (
|
|
124
|
+
editor.isTextBlock(nextNode) &&
|
|
125
|
+
nextNode.markDefs &&
|
|
126
|
+
nextNode.markDefs.length > 0
|
|
127
|
+
) {
|
|
128
|
+
const newMarkDefKeys = new Map<string, string>()
|
|
129
|
+
|
|
130
|
+
const prevNodeSpans = Array.from(
|
|
131
|
+
Node.children(editor, focusBlockPath),
|
|
132
|
+
)
|
|
133
|
+
.map((entry) => entry[0])
|
|
134
|
+
.filter((node) => editor.isTextSpan(node))
|
|
135
|
+
const children = Node.children(editor, nextNodePath)
|
|
136
|
+
|
|
137
|
+
for (const [child, childPath] of children) {
|
|
138
|
+
if (!editor.isTextSpan(child)) {
|
|
139
|
+
continue
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const marks = child.marks ?? []
|
|
143
|
+
|
|
144
|
+
// Go through the marks of the span and figure out if any of
|
|
145
|
+
// them refer to annotations that are also present in the
|
|
146
|
+
// previous block
|
|
147
|
+
for (const mark of marks) {
|
|
148
|
+
if (
|
|
149
|
+
schema.decorators.some((decorator) => decorator.value === mark)
|
|
150
|
+
) {
|
|
151
|
+
continue
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (
|
|
155
|
+
prevNodeSpans.some((prevNodeSpan) =>
|
|
156
|
+
prevNodeSpan.marks?.includes(mark),
|
|
157
|
+
) &&
|
|
158
|
+
!newMarkDefKeys.has(mark)
|
|
159
|
+
) {
|
|
160
|
+
// This annotation is both present in the previous block
|
|
161
|
+
// and this block, so let's assign a new key to it
|
|
162
|
+
newMarkDefKeys.set(mark, keyGenerator())
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const newMarks = marks.map(
|
|
167
|
+
(mark) => newMarkDefKeys.get(mark) ?? mark,
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
// No need to update the marks if they are the same
|
|
171
|
+
if (!isEqual(marks, newMarks)) {
|
|
172
|
+
Transforms.setNodes(
|
|
173
|
+
editor,
|
|
174
|
+
{marks: newMarks},
|
|
175
|
+
{
|
|
176
|
+
at: childPath,
|
|
177
|
+
},
|
|
178
|
+
)
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Time to update all the markDefs that need a new key because
|
|
183
|
+
// they've been split across blocks
|
|
184
|
+
const newMarkDefs = nextNode.markDefs.map((markDef) => ({
|
|
185
|
+
...markDef,
|
|
186
|
+
_key: newMarkDefKeys.get(markDef._key) ?? markDef._key,
|
|
187
|
+
}))
|
|
188
|
+
|
|
189
|
+
// No need to update the markDefs if they are the same
|
|
190
|
+
if (!isEqual(nextNode.markDefs, newMarkDefs)) {
|
|
191
|
+
Transforms.setNodes(
|
|
192
|
+
editor,
|
|
193
|
+
{markDefs: newMarkDefs},
|
|
194
|
+
{
|
|
195
|
+
at: nextNodePath,
|
|
196
|
+
match: (node) => editor.isTextBlock(node),
|
|
197
|
+
},
|
|
198
|
+
)
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
})
|
|
202
|
+
editor.onChange()
|
|
203
|
+
return
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
@@ -1,19 +1,26 @@
|
|
|
1
|
-
import {Editor, Transforms} from 'slate'
|
|
1
|
+
import {deleteBackward, Editor, insertText, Transforms} from 'slate'
|
|
2
2
|
import type {PortableTextMemberSchemaTypes} from '../../types/editor'
|
|
3
3
|
import {toSlateRange} from '../../utils/ranges'
|
|
4
|
-
import
|
|
4
|
+
import {insertBreakActionImplementation} from './behavior.action.insert-break'
|
|
5
|
+
import type {
|
|
6
|
+
BehaviorAction,
|
|
7
|
+
BehaviorEvent,
|
|
8
|
+
PickFromUnion,
|
|
9
|
+
} from './behavior.types'
|
|
5
10
|
|
|
6
|
-
type BehaviorActionContext = {
|
|
11
|
+
export type BehaviorActionContext = {
|
|
7
12
|
keyGenerator: () => string
|
|
8
13
|
schema: PortableTextMemberSchemaTypes
|
|
9
14
|
}
|
|
10
15
|
|
|
11
|
-
type BehaviourActionImplementation<
|
|
16
|
+
export type BehaviourActionImplementation<
|
|
17
|
+
TBehaviorAction extends BehaviorAction,
|
|
18
|
+
> = ({
|
|
12
19
|
context,
|
|
13
|
-
|
|
20
|
+
action,
|
|
14
21
|
}: {
|
|
15
22
|
context: BehaviorActionContext
|
|
16
|
-
|
|
23
|
+
action: TBehaviorAction
|
|
17
24
|
}) => void
|
|
18
25
|
|
|
19
26
|
type BehaviourActionImplementations = {
|
|
@@ -23,26 +30,51 @@ type BehaviourActionImplementations = {
|
|
|
23
30
|
}
|
|
24
31
|
|
|
25
32
|
export const behaviorActionImplementations: BehaviourActionImplementations = {
|
|
26
|
-
'
|
|
27
|
-
for (const path of
|
|
33
|
+
'set block': ({action}) => {
|
|
34
|
+
for (const path of action.paths) {
|
|
28
35
|
const at = toSlateRange(
|
|
29
36
|
{anchor: {path, offset: 0}, focus: {path, offset: 0}},
|
|
30
|
-
|
|
37
|
+
action.editor,
|
|
31
38
|
)!
|
|
32
39
|
|
|
33
|
-
Transforms.setNodes(
|
|
40
|
+
Transforms.setNodes(
|
|
41
|
+
action.editor,
|
|
42
|
+
{
|
|
43
|
+
...(action.style ? {style: action.style} : {}),
|
|
44
|
+
...(action.listItem ? {listItem: action.listItem} : {}),
|
|
45
|
+
...(action.level ? {level: action.level} : {}),
|
|
46
|
+
},
|
|
47
|
+
{at},
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
'unset block': ({action}) => {
|
|
52
|
+
for (const path of action.paths) {
|
|
53
|
+
const at = toSlateRange(
|
|
54
|
+
{anchor: {path, offset: 0}, focus: {path, offset: 0}},
|
|
55
|
+
action.editor,
|
|
56
|
+
)!
|
|
57
|
+
|
|
58
|
+
Transforms.unsetNodes(action.editor, action.props, {at})
|
|
34
59
|
}
|
|
35
60
|
},
|
|
36
|
-
'delete
|
|
37
|
-
|
|
38
|
-
|
|
61
|
+
'delete backward': ({action}) => {
|
|
62
|
+
deleteBackward(action.editor, action.unit)
|
|
63
|
+
},
|
|
64
|
+
'delete text': ({action}) => {
|
|
65
|
+
Transforms.delete(action.editor, {
|
|
66
|
+
at: toSlateRange(action.selection, action.editor)!,
|
|
39
67
|
})
|
|
40
68
|
},
|
|
41
|
-
'insert
|
|
42
|
-
|
|
69
|
+
'insert break': insertBreakActionImplementation,
|
|
70
|
+
// This mimics Slate's internal which also just does a regular insert break
|
|
71
|
+
// when on soft break
|
|
72
|
+
'insert soft break': insertBreakActionImplementation,
|
|
73
|
+
'insert text': ({action}) => {
|
|
74
|
+
insertText(action.editor, action.text)
|
|
43
75
|
},
|
|
44
|
-
'insert text block': ({context,
|
|
45
|
-
Editor.insertNode(
|
|
76
|
+
'insert text block': ({context, action}) => {
|
|
77
|
+
Editor.insertNode(action.editor, {
|
|
46
78
|
_key: context.keyGenerator(),
|
|
47
79
|
_type: context.schema.block.name,
|
|
48
80
|
style: context.schema.styles[0].value ?? 'normal',
|
|
@@ -56,4 +88,46 @@ export const behaviorActionImplementations: BehaviourActionImplementations = {
|
|
|
56
88
|
],
|
|
57
89
|
})
|
|
58
90
|
},
|
|
91
|
+
'effect': ({action}) => {
|
|
92
|
+
action.effect()
|
|
93
|
+
},
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function performDefaultAction({
|
|
97
|
+
context,
|
|
98
|
+
action,
|
|
99
|
+
}: {
|
|
100
|
+
context: BehaviorActionContext
|
|
101
|
+
action: PickFromUnion<BehaviorAction, 'type', BehaviorEvent['type']>
|
|
102
|
+
}) {
|
|
103
|
+
switch (action.type) {
|
|
104
|
+
case 'delete backward': {
|
|
105
|
+
behaviorActionImplementations['delete backward']({
|
|
106
|
+
context,
|
|
107
|
+
action,
|
|
108
|
+
})
|
|
109
|
+
break
|
|
110
|
+
}
|
|
111
|
+
case 'insert break': {
|
|
112
|
+
behaviorActionImplementations['insert break']({
|
|
113
|
+
context,
|
|
114
|
+
action,
|
|
115
|
+
})
|
|
116
|
+
break
|
|
117
|
+
}
|
|
118
|
+
case 'insert soft break': {
|
|
119
|
+
behaviorActionImplementations['insert soft break']({
|
|
120
|
+
context,
|
|
121
|
+
action,
|
|
122
|
+
})
|
|
123
|
+
break
|
|
124
|
+
}
|
|
125
|
+
case 'insert text': {
|
|
126
|
+
behaviorActionImplementations['insert text']({
|
|
127
|
+
context,
|
|
128
|
+
action,
|
|
129
|
+
})
|
|
130
|
+
break
|
|
131
|
+
}
|
|
132
|
+
}
|
|
59
133
|
}
|
|
@@ -1,37 +1,19 @@
|
|
|
1
|
-
import {isHotkey} from 'is-hotkey-esm'
|
|
2
1
|
import {defineBehavior} from './behavior.types'
|
|
3
2
|
import {getFocusBlockObject} from './behavior.utils'
|
|
4
3
|
|
|
5
|
-
const
|
|
6
|
-
on: '
|
|
7
|
-
|
|
8
|
-
actions: [
|
|
9
|
-
({event}) => {
|
|
10
|
-
event.nativeEvent.preventDefault()
|
|
11
|
-
return {type: 'insert text', text: '\n'}
|
|
12
|
-
},
|
|
13
|
-
],
|
|
4
|
+
const softReturn = defineBehavior({
|
|
5
|
+
on: 'insert soft break',
|
|
6
|
+
actions: [() => [{type: 'insert text', text: '\n'}]],
|
|
14
7
|
})
|
|
15
8
|
|
|
16
|
-
const
|
|
17
|
-
on: '
|
|
18
|
-
guard: ({context
|
|
19
|
-
const isEnter = isHotkey('enter', event.nativeEvent)
|
|
20
|
-
|
|
21
|
-
if (!isEnter) {
|
|
22
|
-
return false
|
|
23
|
-
}
|
|
24
|
-
|
|
9
|
+
const breakingVoidBlock = defineBehavior({
|
|
10
|
+
on: 'insert break',
|
|
11
|
+
guard: ({context}) => {
|
|
25
12
|
const focusBlockObject = getFocusBlockObject(context)
|
|
26
13
|
|
|
27
14
|
return !!focusBlockObject
|
|
28
15
|
},
|
|
29
|
-
actions: [
|
|
30
|
-
({event}) => {
|
|
31
|
-
event.nativeEvent.preventDefault()
|
|
32
|
-
return {type: 'insert text block', decorators: []}
|
|
33
|
-
},
|
|
34
|
-
],
|
|
16
|
+
actions: [() => [{type: 'insert text block', decorators: []}]],
|
|
35
17
|
})
|
|
36
18
|
|
|
37
|
-
export const coreBehaviors = [
|
|
19
|
+
export const coreBehaviors = [softReturn, breakingVoidBlock]
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type {KeyedSegment, PortableTextBlock} from '@sanity/types'
|
|
2
|
+
import type {TextUnit} from 'slate'
|
|
3
|
+
import type {TextInsertTextOptions} from 'slate/dist/interfaces/transforms/text'
|
|
2
4
|
import type {
|
|
3
5
|
EditorSelection,
|
|
4
6
|
PortableTextMemberSchemaTypes,
|
|
@@ -19,14 +21,19 @@ export type BehaviorContext = {
|
|
|
19
21
|
*/
|
|
20
22
|
export type BehaviorEvent =
|
|
21
23
|
| {
|
|
22
|
-
type: '
|
|
23
|
-
|
|
24
|
-
editor: PortableTextSlateEditor
|
|
24
|
+
type: 'delete backward'
|
|
25
|
+
unit: TextUnit
|
|
25
26
|
}
|
|
26
27
|
| {
|
|
27
|
-
type: '
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
type: 'insert soft break'
|
|
29
|
+
}
|
|
30
|
+
| {
|
|
31
|
+
type: 'insert break'
|
|
32
|
+
}
|
|
33
|
+
| {
|
|
34
|
+
type: 'insert text'
|
|
35
|
+
text: string
|
|
36
|
+
options?: TextInsertTextOptions
|
|
30
37
|
}
|
|
31
38
|
|
|
32
39
|
/**
|
|
@@ -47,23 +54,31 @@ export type BehaviorGuard<
|
|
|
47
54
|
* @alpha
|
|
48
55
|
*/
|
|
49
56
|
export type BehaviorActionIntend =
|
|
50
|
-
|
|
|
51
|
-
type: 'insert text'
|
|
52
|
-
text: string
|
|
53
|
-
}
|
|
57
|
+
| BehaviorEvent
|
|
54
58
|
| {
|
|
55
59
|
type: 'insert text block'
|
|
56
60
|
decorators: Array<string>
|
|
57
61
|
}
|
|
58
62
|
| {
|
|
59
|
-
type: '
|
|
63
|
+
type: 'set block'
|
|
60
64
|
paths: Array<[KeyedSegment]>
|
|
61
|
-
style
|
|
65
|
+
style?: string
|
|
66
|
+
listItem?: string
|
|
67
|
+
level?: number
|
|
68
|
+
}
|
|
69
|
+
| {
|
|
70
|
+
type: 'unset block'
|
|
71
|
+
paths: Array<[KeyedSegment]>
|
|
72
|
+
props: Array<'style' | 'listItem' | 'level'>
|
|
62
73
|
}
|
|
63
74
|
| {
|
|
64
75
|
type: 'delete text'
|
|
65
76
|
selection: NonNullable<EditorSelection>
|
|
66
77
|
}
|
|
78
|
+
| {
|
|
79
|
+
type: 'effect'
|
|
80
|
+
effect: () => void
|
|
81
|
+
}
|
|
67
82
|
|
|
68
83
|
/**
|
|
69
84
|
* @alpha
|
|
@@ -79,18 +94,29 @@ export type Behavior<
|
|
|
79
94
|
TBehaviorEventType extends BehaviorEvent['type'] = BehaviorEvent['type'],
|
|
80
95
|
TGuardResponse = true,
|
|
81
96
|
> = {
|
|
97
|
+
/**
|
|
98
|
+
* The internal editor event that triggers this behavior.
|
|
99
|
+
*/
|
|
82
100
|
on: TBehaviorEventType
|
|
101
|
+
/**
|
|
102
|
+
* Predicate function that determines if the behavior should be executed.
|
|
103
|
+
* Returning a non-nullable value from the guard will pass the value to the
|
|
104
|
+
* actions and execute them.
|
|
105
|
+
*/
|
|
83
106
|
guard?: BehaviorGuard<
|
|
84
107
|
PickFromUnion<BehaviorEvent, 'type', TBehaviorEventType>,
|
|
85
108
|
TGuardResponse
|
|
86
109
|
>
|
|
87
|
-
|
|
110
|
+
/**
|
|
111
|
+
* Array of behavior action sets.
|
|
112
|
+
*/
|
|
113
|
+
actions: Array<BehaviorActionIntendSet<TBehaviorEventType, TGuardResponse>>
|
|
88
114
|
}
|
|
89
115
|
|
|
90
116
|
/**
|
|
91
117
|
* @alpha
|
|
92
118
|
*/
|
|
93
|
-
export type
|
|
119
|
+
export type BehaviorActionIntendSet<
|
|
94
120
|
TBehaviorEventType extends BehaviorEvent['type'] = BehaviorEvent['type'],
|
|
95
121
|
TGuardResponse = true,
|
|
96
122
|
> = (
|
|
@@ -102,7 +128,7 @@ export type RaiseBehaviorActionIntend<
|
|
|
102
128
|
event: PickFromUnion<BehaviorEvent, 'type', TBehaviorEventType>
|
|
103
129
|
},
|
|
104
130
|
guardResponse: TGuardResponse,
|
|
105
|
-
) => BehaviorActionIntend
|
|
131
|
+
) => Array<BehaviorActionIntend>
|
|
106
132
|
|
|
107
133
|
/**
|
|
108
134
|
* @alpha
|