@portabletext/editor 1.47.10 → 1.47.12
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 +61 -3
- package/lib/_chunks-cjs/behavior.core.cjs.map +1 -1
- package/lib/_chunks-cjs/editor-provider.cjs +167 -105
- package/lib/_chunks-cjs/editor-provider.cjs.map +1 -1
- package/lib/_chunks-cjs/util.get-selection-start-point.cjs +10 -0
- package/lib/_chunks-cjs/util.get-selection-start-point.cjs.map +1 -0
- package/lib/_chunks-cjs/util.is-selection-collapsed.cjs +0 -4
- package/lib/_chunks-cjs/util.is-selection-collapsed.cjs.map +1 -1
- package/lib/_chunks-es/behavior.core.js +63 -4
- package/lib/_chunks-es/behavior.core.js.map +1 -1
- package/lib/_chunks-es/editor-provider.js +168 -105
- package/lib/_chunks-es/editor-provider.js.map +1 -1
- package/lib/_chunks-es/util.get-selection-start-point.js +11 -0
- package/lib/_chunks-es/util.get-selection-start-point.js.map +1 -0
- package/lib/_chunks-es/util.is-selection-collapsed.js +0 -4
- package/lib/_chunks-es/util.is-selection-collapsed.js.map +1 -1
- package/lib/behaviors/index.d.cts +359 -339
- package/lib/behaviors/index.d.ts +359 -339
- package/lib/behaviors/index.js +1 -1
- package/lib/index.cjs +3 -3
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +4 -4
- package/lib/index.d.ts +4 -4
- package/lib/index.js +2 -1
- package/lib/index.js.map +1 -1
- package/lib/plugins/index.cjs +0 -9
- package/lib/plugins/index.cjs.map +1 -1
- package/lib/plugins/index.d.cts +4 -4
- package/lib/plugins/index.d.ts +4 -4
- package/lib/plugins/index.js +0 -9
- package/lib/plugins/index.js.map +1 -1
- package/lib/selectors/index.d.cts +4 -4
- package/lib/selectors/index.d.ts +4 -4
- package/lib/utils/index.cjs +3 -6
- package/lib/utils/index.cjs.map +1 -1
- package/lib/utils/index.d.cts +4 -4
- package/lib/utils/index.d.ts +4 -4
- package/lib/utils/index.js +2 -4
- package/lib/utils/index.js.map +1 -1
- package/package.json +3 -3
- package/src/behavior-actions/behavior.action.insert.block.ts +136 -45
- package/src/behavior-actions/behavior.actions.ts +0 -9
- package/src/behaviors/behavior.abstract.insert.ts +2 -2
- package/src/behaviors/behavior.abstract.split.ts +118 -0
- package/src/behaviors/behavior.core.insert-break.ts +113 -0
- package/src/behaviors/behavior.core.ts +2 -0
- package/src/behaviors/behavior.default.ts +2 -0
- package/src/behaviors/behavior.types.event.ts +4 -4
- package/src/internal-utils/slate-utils.ts +50 -1
- package/src/plugins/plugin.one-line.tsx +0 -7
- package/src/behavior-actions/behavior.action.split.block.ts +0 -146
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as selectors from '../selectors'
|
|
2
|
+
import * as utils from '../utils'
|
|
2
3
|
import {raise} from './behavior.types.action'
|
|
3
4
|
import {defineBehavior} from './behavior.types.behavior'
|
|
4
5
|
|
|
@@ -112,7 +113,119 @@ const breakingAtTheStartOfTextBlock = defineBehavior({
|
|
|
112
113
|
],
|
|
113
114
|
})
|
|
114
115
|
|
|
116
|
+
const breakingEntireDocument = defineBehavior({
|
|
117
|
+
on: 'insert.break',
|
|
118
|
+
guard: ({snapshot}) => {
|
|
119
|
+
if (!snapshot.context.selection) {
|
|
120
|
+
return false
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (!selectors.isSelectionExpanded(snapshot)) {
|
|
124
|
+
return false
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const firstBlock = selectors.getFirstBlock(snapshot)
|
|
128
|
+
const lastBlock = selectors.getLastBlock(snapshot)
|
|
129
|
+
|
|
130
|
+
if (!firstBlock || !lastBlock) {
|
|
131
|
+
return false
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const firstBlockStartPoint = utils.getBlockStartPoint(firstBlock)
|
|
135
|
+
const selectionStartPoint = utils.getSelectionStartPoint(
|
|
136
|
+
snapshot.context.selection,
|
|
137
|
+
)
|
|
138
|
+
const lastBlockEndPoint = utils.getBlockEndPoint(lastBlock)
|
|
139
|
+
const selectionEndPoint = utils.getSelectionEndPoint(
|
|
140
|
+
snapshot.context.selection,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
if (
|
|
144
|
+
utils.isEqualSelectionPoints(firstBlockStartPoint, selectionStartPoint) &&
|
|
145
|
+
utils.isEqualSelectionPoints(lastBlockEndPoint, selectionEndPoint)
|
|
146
|
+
) {
|
|
147
|
+
return {selection: snapshot.context.selection}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return false
|
|
151
|
+
},
|
|
152
|
+
actions: [
|
|
153
|
+
(_, {selection}) => [
|
|
154
|
+
raise({
|
|
155
|
+
type: 'delete',
|
|
156
|
+
at: selection,
|
|
157
|
+
}),
|
|
158
|
+
],
|
|
159
|
+
],
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
const breakingEntireBlocks = defineBehavior({
|
|
163
|
+
on: 'insert.break',
|
|
164
|
+
guard: ({snapshot}) => {
|
|
165
|
+
if (!snapshot.context.selection) {
|
|
166
|
+
return false
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (!selectors.isSelectionExpanded(snapshot)) {
|
|
170
|
+
return false
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const selectedBlocks = selectors.getSelectedBlocks(snapshot)
|
|
174
|
+
const selectionStartBlock = selectors.getSelectionStartBlock(snapshot)
|
|
175
|
+
const selectionEndBlock = selectors.getSelectionEndBlock(snapshot)
|
|
176
|
+
|
|
177
|
+
if (!selectionStartBlock || !selectionEndBlock) {
|
|
178
|
+
return false
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const startBlockStartPoint = utils.getBlockStartPoint(selectionStartBlock)
|
|
182
|
+
const selectionStartPoint = utils.getSelectionStartPoint(
|
|
183
|
+
snapshot.context.selection,
|
|
184
|
+
)
|
|
185
|
+
const endBlockEndPoint = utils.getBlockEndPoint(selectionEndBlock)
|
|
186
|
+
const selectionEndPoint = utils.getSelectionEndPoint(
|
|
187
|
+
snapshot.context.selection,
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
if (
|
|
191
|
+
utils.isEqualSelectionPoints(selectionStartPoint, startBlockStartPoint) &&
|
|
192
|
+
utils.isEqualSelectionPoints(selectionEndPoint, endBlockEndPoint)
|
|
193
|
+
) {
|
|
194
|
+
return {selectedBlocks}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return false
|
|
198
|
+
},
|
|
199
|
+
actions: [
|
|
200
|
+
({snapshot}, {selectedBlocks}) => [
|
|
201
|
+
raise({
|
|
202
|
+
type: 'insert.block',
|
|
203
|
+
block: {
|
|
204
|
+
_type: snapshot.context.schema.block.name,
|
|
205
|
+
children: [
|
|
206
|
+
{
|
|
207
|
+
_type: snapshot.context.schema.span.name,
|
|
208
|
+
text: '',
|
|
209
|
+
marks: [],
|
|
210
|
+
},
|
|
211
|
+
],
|
|
212
|
+
},
|
|
213
|
+
placement: 'before',
|
|
214
|
+
select: 'start',
|
|
215
|
+
}),
|
|
216
|
+
...selectedBlocks.map((block) =>
|
|
217
|
+
raise({
|
|
218
|
+
type: 'delete.block',
|
|
219
|
+
at: block.path,
|
|
220
|
+
}),
|
|
221
|
+
),
|
|
222
|
+
],
|
|
223
|
+
],
|
|
224
|
+
})
|
|
225
|
+
|
|
115
226
|
export const coreInsertBreakBehaviors = {
|
|
116
227
|
breakingAtTheEndOfTextBlock,
|
|
117
228
|
breakingAtTheStartOfTextBlock,
|
|
229
|
+
breakingEntireDocument,
|
|
230
|
+
breakingEntireBlocks,
|
|
118
231
|
}
|
|
@@ -29,4 +29,6 @@ export const coreBehaviors = [
|
|
|
29
29
|
coreListBehaviors.unindentListOnShiftTab,
|
|
30
30
|
coreInsertBreakBehaviors.breakingAtTheEndOfTextBlock,
|
|
31
31
|
coreInsertBreakBehaviors.breakingAtTheStartOfTextBlock,
|
|
32
|
+
coreInsertBreakBehaviors.breakingEntireDocument,
|
|
33
|
+
coreInsertBreakBehaviors.breakingEntireBlocks,
|
|
32
34
|
]
|
|
@@ -10,6 +10,7 @@ import {abstractInsertBehaviors} from './behavior.abstract.insert'
|
|
|
10
10
|
import {abstractListItemBehaviors} from './behavior.abstract.list-item'
|
|
11
11
|
import {abstractMoveBehaviors} from './behavior.abstract.move'
|
|
12
12
|
import {abstractSelectBehaviors} from './behavior.abstract.select'
|
|
13
|
+
import {abstractSplitBehaviors} from './behavior.abstract.split'
|
|
13
14
|
import {abstractStyleBehaviors} from './behavior.abstract.style'
|
|
14
15
|
import {raiseInsertSoftBreak} from './behavior.default.raise-soft-break'
|
|
15
16
|
import {raise} from './behavior.types.action'
|
|
@@ -454,6 +455,7 @@ export const defaultBehaviors = [
|
|
|
454
455
|
...abstractMoveBehaviors,
|
|
455
456
|
...abstractStyleBehaviors,
|
|
456
457
|
...abstractSelectBehaviors,
|
|
458
|
+
...abstractSplitBehaviors,
|
|
457
459
|
raiseDeserializationSuccessOrFailure,
|
|
458
460
|
raiseSerializationSuccessOrFailure,
|
|
459
461
|
raiseInsertSoftBreak,
|
|
@@ -83,7 +83,6 @@ const syntheticBehaviorEventTypes = [
|
|
|
83
83
|
'move.block',
|
|
84
84
|
'move.forward',
|
|
85
85
|
'select',
|
|
86
|
-
'split.block',
|
|
87
86
|
] as const
|
|
88
87
|
|
|
89
88
|
type SyntheticBehaviorEventType = (typeof syntheticBehaviorEventTypes)[number]
|
|
@@ -195,9 +194,6 @@ export type SyntheticBehaviorEvent =
|
|
|
195
194
|
type: StrictExtract<SyntheticBehaviorEventType, 'select'>
|
|
196
195
|
at: EditorSelection
|
|
197
196
|
}
|
|
198
|
-
| {
|
|
199
|
-
type: StrictExtract<SyntheticBehaviorEventType, 'split.block'>
|
|
200
|
-
}
|
|
201
197
|
|
|
202
198
|
export type InsertPlacement = 'auto' | 'after' | 'before'
|
|
203
199
|
|
|
@@ -231,6 +227,7 @@ const abstractBehaviorEventTypes = [
|
|
|
231
227
|
'serialize',
|
|
232
228
|
'serialization.success',
|
|
233
229
|
'serialization.failure',
|
|
230
|
+
'split',
|
|
234
231
|
'style.add',
|
|
235
232
|
'style.remove',
|
|
236
233
|
'style.toggle',
|
|
@@ -362,6 +359,9 @@ export type AbstractBehaviorEvent =
|
|
|
362
359
|
type: StrictExtract<AbstractBehaviorEventType, 'select.next block'>
|
|
363
360
|
select?: 'start' | 'end'
|
|
364
361
|
}
|
|
362
|
+
| {
|
|
363
|
+
type: StrictExtract<AbstractBehaviorEventType, 'split'>
|
|
364
|
+
}
|
|
365
365
|
| {
|
|
366
366
|
type: StrictExtract<AbstractBehaviorEventType, 'style.add'>
|
|
367
367
|
style: string
|
|
@@ -3,6 +3,27 @@ import type {EditorSchema} from '../editor/editor-schema'
|
|
|
3
3
|
import type {EditorSelection, PortableTextSlateEditor} from '../types/editor'
|
|
4
4
|
import {fromSlateValue} from './values'
|
|
5
5
|
|
|
6
|
+
export function getAnchorBlock({
|
|
7
|
+
editor,
|
|
8
|
+
}: {
|
|
9
|
+
editor: PortableTextSlateEditor
|
|
10
|
+
}): [node: Node, path: Path] | [undefined, undefined] {
|
|
11
|
+
if (!editor.selection) {
|
|
12
|
+
return [undefined, undefined]
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
return (
|
|
17
|
+
Editor.node(editor, editor.selection.anchor.path.slice(0, 1)) ?? [
|
|
18
|
+
undefined,
|
|
19
|
+
undefined,
|
|
20
|
+
]
|
|
21
|
+
)
|
|
22
|
+
} catch {
|
|
23
|
+
return [undefined, undefined]
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
6
27
|
export function getFocusBlock({
|
|
7
28
|
editor,
|
|
8
29
|
}: {
|
|
@@ -24,6 +45,34 @@ export function getFocusBlock({
|
|
|
24
45
|
}
|
|
25
46
|
}
|
|
26
47
|
|
|
48
|
+
export function getSelectionStartBlock({
|
|
49
|
+
editor,
|
|
50
|
+
}: {
|
|
51
|
+
editor: PortableTextSlateEditor
|
|
52
|
+
}): [node: Node, path: Path] | [undefined, undefined] {
|
|
53
|
+
if (!editor.selection) {
|
|
54
|
+
return [undefined, undefined]
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const selectionStartPoint = Range.start(editor.selection)
|
|
58
|
+
|
|
59
|
+
return getPointBlock({editor, point: selectionStartPoint})
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function getSelectionEndBlock({
|
|
63
|
+
editor,
|
|
64
|
+
}: {
|
|
65
|
+
editor: PortableTextSlateEditor
|
|
66
|
+
}): [node: Node, path: Path] | [undefined, undefined] {
|
|
67
|
+
if (!editor.selection) {
|
|
68
|
+
return [undefined, undefined]
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const selectionEndPoint = Range.end(editor.selection)
|
|
72
|
+
|
|
73
|
+
return getPointBlock({editor, point: selectionEndPoint})
|
|
74
|
+
}
|
|
75
|
+
|
|
27
76
|
function getPointBlock({
|
|
28
77
|
editor,
|
|
29
78
|
point,
|
|
@@ -36,7 +85,7 @@ function getPointBlock({
|
|
|
36
85
|
undefined,
|
|
37
86
|
undefined,
|
|
38
87
|
]
|
|
39
|
-
return block ? [block, point.path] : [undefined, undefined]
|
|
88
|
+
return block ? [block, point.path.slice(0, 1)] : [undefined, undefined]
|
|
40
89
|
} catch {
|
|
41
90
|
return [undefined, undefined]
|
|
42
91
|
}
|
|
@@ -23,13 +23,6 @@ const oneLineBehaviors = [
|
|
|
23
23
|
on: 'insert.break',
|
|
24
24
|
actions: [() => [{type: 'noop'}]],
|
|
25
25
|
}),
|
|
26
|
-
/**
|
|
27
|
-
* `split.block`s as well.
|
|
28
|
-
*/
|
|
29
|
-
defineBehavior({
|
|
30
|
-
on: 'split.block',
|
|
31
|
-
actions: [() => [{type: 'noop'}]],
|
|
32
|
-
}),
|
|
33
26
|
/**
|
|
34
27
|
* `insert.block` `before` or `after` is not allowed in a one-line editor.
|
|
35
28
|
*/
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import {isEqual} from 'lodash'
|
|
2
|
-
import {Editor, Node, Path, Transforms} from 'slate'
|
|
3
|
-
import type {SlateTextBlock, VoidElement} from '../types/slate'
|
|
4
|
-
import type {BehaviorActionImplementation} from './behavior.actions'
|
|
5
|
-
|
|
6
|
-
export const splitBlockActionImplementation: BehaviorActionImplementation<
|
|
7
|
-
'split.block'
|
|
8
|
-
> = ({context, action}) => {
|
|
9
|
-
const keyGenerator = context.keyGenerator
|
|
10
|
-
const schema = context.schema
|
|
11
|
-
const editor = action.editor
|
|
12
|
-
|
|
13
|
-
if (!editor.selection) {
|
|
14
|
-
return
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const anchorBlockPath = editor.selection.anchor.path.slice(0, 1)
|
|
18
|
-
const focusBlockPath = editor.selection.focus.path.slice(0, 1)
|
|
19
|
-
const focusBlock = Node.descendant(editor, focusBlockPath) as
|
|
20
|
-
| SlateTextBlock
|
|
21
|
-
| VoidElement
|
|
22
|
-
|
|
23
|
-
if (editor.isTextBlock(focusBlock)) {
|
|
24
|
-
const selectionAcrossBlocks = anchorBlockPath[0] !== focusBlockPath[0]
|
|
25
|
-
|
|
26
|
-
if (!selectionAcrossBlocks) {
|
|
27
|
-
Transforms.splitNodes(editor, {
|
|
28
|
-
at: editor.selection,
|
|
29
|
-
always: true,
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
const [nextBlock, nextBlockPath] = Editor.node(
|
|
33
|
-
editor,
|
|
34
|
-
Path.next(focusBlockPath),
|
|
35
|
-
{depth: 1},
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
const nextChild = Node.child(nextBlock, 0)
|
|
39
|
-
const firstChildIsInlineObject = !editor.isTextSpan(nextChild)
|
|
40
|
-
|
|
41
|
-
if (firstChildIsInlineObject) {
|
|
42
|
-
// If the first child in the next block is an inline object then we
|
|
43
|
-
// add an empty span right before it to a place to put the cursor.
|
|
44
|
-
// This is a Slate constraint that we have to adhere to.
|
|
45
|
-
Transforms.insertNodes(
|
|
46
|
-
editor,
|
|
47
|
-
{
|
|
48
|
-
_key: context.keyGenerator(),
|
|
49
|
-
_type: 'span',
|
|
50
|
-
text: '',
|
|
51
|
-
marks: [],
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
at: [nextBlockPath[0], 0],
|
|
55
|
-
},
|
|
56
|
-
)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
Transforms.setSelection(editor, {
|
|
60
|
-
anchor: {path: [...nextBlockPath, 0], offset: 0},
|
|
61
|
-
focus: {path: [...nextBlockPath, 0], offset: 0},
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Assign new keys to markDefs that are now split across two blocks
|
|
66
|
-
*/
|
|
67
|
-
if (
|
|
68
|
-
editor.isTextBlock(nextBlock) &&
|
|
69
|
-
nextBlock.markDefs &&
|
|
70
|
-
nextBlock.markDefs.length > 0
|
|
71
|
-
) {
|
|
72
|
-
const newMarkDefKeys = new Map<string, string>()
|
|
73
|
-
|
|
74
|
-
const prevNodeSpans = Array.from(Node.children(editor, focusBlockPath))
|
|
75
|
-
.map((entry) => entry[0])
|
|
76
|
-
.filter((node) => editor.isTextSpan(node))
|
|
77
|
-
const children = Node.children(editor, nextBlockPath)
|
|
78
|
-
|
|
79
|
-
for (const [child, childPath] of children) {
|
|
80
|
-
if (!editor.isTextSpan(child)) {
|
|
81
|
-
continue
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const marks = child.marks ?? []
|
|
85
|
-
|
|
86
|
-
// Go through the marks of the span and figure out if any of
|
|
87
|
-
// them refer to annotations that are also present in the
|
|
88
|
-
// previous block
|
|
89
|
-
for (const mark of marks) {
|
|
90
|
-
if (
|
|
91
|
-
schema.decorators.some((decorator) => decorator.name === mark)
|
|
92
|
-
) {
|
|
93
|
-
continue
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
if (
|
|
97
|
-
prevNodeSpans.some((prevNodeSpan) =>
|
|
98
|
-
prevNodeSpan.marks?.includes(mark),
|
|
99
|
-
) &&
|
|
100
|
-
!newMarkDefKeys.has(mark)
|
|
101
|
-
) {
|
|
102
|
-
// This annotation is both present in the previous block
|
|
103
|
-
// and this block, so let's assign a new key to it
|
|
104
|
-
newMarkDefKeys.set(mark, keyGenerator())
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const newMarks = marks.map((mark) => newMarkDefKeys.get(mark) ?? mark)
|
|
109
|
-
|
|
110
|
-
// No need to update the marks if they are the same
|
|
111
|
-
if (!isEqual(marks, newMarks)) {
|
|
112
|
-
Transforms.setNodes(
|
|
113
|
-
editor,
|
|
114
|
-
{marks: newMarks},
|
|
115
|
-
{
|
|
116
|
-
at: childPath,
|
|
117
|
-
},
|
|
118
|
-
)
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Time to update all the markDefs that need a new key because
|
|
123
|
-
// they've been split across blocks
|
|
124
|
-
const newMarkDefs = nextBlock.markDefs.map((markDef) => ({
|
|
125
|
-
...markDef,
|
|
126
|
-
_key: newMarkDefKeys.get(markDef._key) ?? markDef._key,
|
|
127
|
-
}))
|
|
128
|
-
|
|
129
|
-
// No need to update the markDefs if they are the same
|
|
130
|
-
if (!isEqual(nextBlock.markDefs, newMarkDefs)) {
|
|
131
|
-
Transforms.setNodes(
|
|
132
|
-
editor,
|
|
133
|
-
{markDefs: newMarkDefs},
|
|
134
|
-
{
|
|
135
|
-
at: nextBlockPath,
|
|
136
|
-
match: (node) => editor.isTextBlock(node),
|
|
137
|
-
},
|
|
138
|
-
)
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
return
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
Transforms.splitNodes(editor, {always: true})
|
|
146
|
-
}
|