@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.
Files changed (51) hide show
  1. package/lib/_chunks-cjs/behavior.core.cjs +61 -3
  2. package/lib/_chunks-cjs/behavior.core.cjs.map +1 -1
  3. package/lib/_chunks-cjs/editor-provider.cjs +167 -105
  4. package/lib/_chunks-cjs/editor-provider.cjs.map +1 -1
  5. package/lib/_chunks-cjs/util.get-selection-start-point.cjs +10 -0
  6. package/lib/_chunks-cjs/util.get-selection-start-point.cjs.map +1 -0
  7. package/lib/_chunks-cjs/util.is-selection-collapsed.cjs +0 -4
  8. package/lib/_chunks-cjs/util.is-selection-collapsed.cjs.map +1 -1
  9. package/lib/_chunks-es/behavior.core.js +63 -4
  10. package/lib/_chunks-es/behavior.core.js.map +1 -1
  11. package/lib/_chunks-es/editor-provider.js +168 -105
  12. package/lib/_chunks-es/editor-provider.js.map +1 -1
  13. package/lib/_chunks-es/util.get-selection-start-point.js +11 -0
  14. package/lib/_chunks-es/util.get-selection-start-point.js.map +1 -0
  15. package/lib/_chunks-es/util.is-selection-collapsed.js +0 -4
  16. package/lib/_chunks-es/util.is-selection-collapsed.js.map +1 -1
  17. package/lib/behaviors/index.d.cts +359 -339
  18. package/lib/behaviors/index.d.ts +359 -339
  19. package/lib/behaviors/index.js +1 -1
  20. package/lib/index.cjs +3 -3
  21. package/lib/index.cjs.map +1 -1
  22. package/lib/index.d.cts +4 -4
  23. package/lib/index.d.ts +4 -4
  24. package/lib/index.js +2 -1
  25. package/lib/index.js.map +1 -1
  26. package/lib/plugins/index.cjs +0 -9
  27. package/lib/plugins/index.cjs.map +1 -1
  28. package/lib/plugins/index.d.cts +4 -4
  29. package/lib/plugins/index.d.ts +4 -4
  30. package/lib/plugins/index.js +0 -9
  31. package/lib/plugins/index.js.map +1 -1
  32. package/lib/selectors/index.d.cts +4 -4
  33. package/lib/selectors/index.d.ts +4 -4
  34. package/lib/utils/index.cjs +3 -6
  35. package/lib/utils/index.cjs.map +1 -1
  36. package/lib/utils/index.d.cts +4 -4
  37. package/lib/utils/index.d.ts +4 -4
  38. package/lib/utils/index.js +2 -4
  39. package/lib/utils/index.js.map +1 -1
  40. package/package.json +3 -3
  41. package/src/behavior-actions/behavior.action.insert.block.ts +136 -45
  42. package/src/behavior-actions/behavior.actions.ts +0 -9
  43. package/src/behaviors/behavior.abstract.insert.ts +2 -2
  44. package/src/behaviors/behavior.abstract.split.ts +118 -0
  45. package/src/behaviors/behavior.core.insert-break.ts +113 -0
  46. package/src/behaviors/behavior.core.ts +2 -0
  47. package/src/behaviors/behavior.default.ts +2 -0
  48. package/src/behaviors/behavior.types.event.ts +4 -4
  49. package/src/internal-utils/slate-utils.ts +50 -1
  50. package/src/plugins/plugin.one-line.tsx +0 -7
  51. 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
- }