@portabletext/editor 1.13.0 → 1.14.1

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 (74) hide show
  1. package/README.md +1 -1
  2. package/lib/_chunks-cjs/selector.get-text-before.cjs +320 -0
  3. package/lib/_chunks-cjs/selector.get-text-before.cjs.map +1 -0
  4. package/lib/_chunks-es/selector.get-text-before.js +321 -0
  5. package/lib/_chunks-es/selector.get-text-before.js.map +1 -0
  6. package/lib/{index.esm.js → index.cjs} +1703 -1431
  7. package/lib/index.cjs.map +1 -0
  8. package/lib/{index.d.mts → index.d.cts} +4038 -313
  9. package/lib/index.d.ts +4038 -313
  10. package/lib/index.js +1724 -1407
  11. package/lib/index.js.map +1 -1
  12. package/lib/selectors/index.cjs +35 -0
  13. package/lib/selectors/index.cjs.map +1 -0
  14. package/lib/selectors/index.d.cts +243 -0
  15. package/lib/selectors/index.d.ts +243 -0
  16. package/lib/selectors/index.js +36 -0
  17. package/lib/selectors/index.js.map +1 -0
  18. package/package.json +21 -13
  19. package/src/editor/Editable.tsx +1 -1
  20. package/src/editor/PortableTextEditor.tsx +19 -4
  21. package/src/editor/__tests__/handleClick.test.tsx +4 -4
  22. package/src/editor/behavior/behavior.action.insert-block-object.ts +1 -1
  23. package/src/editor/behavior/behavior.action.insert-break.ts +3 -3
  24. package/src/editor/behavior/behavior.action.insert-inline-object.ts +58 -0
  25. package/src/editor/behavior/behavior.action.insert-span.ts +1 -1
  26. package/src/editor/behavior/behavior.action.list-item.ts +100 -0
  27. package/src/editor/behavior/behavior.action.style.ts +108 -0
  28. package/src/editor/behavior/behavior.action.text-block.set.ts +25 -0
  29. package/src/editor/behavior/behavior.action.text-block.unset.ts +17 -0
  30. package/src/editor/behavior/behavior.actions.ts +178 -109
  31. package/src/editor/behavior/behavior.code-editor.ts +30 -40
  32. package/src/editor/behavior/behavior.core.block-objects.ts +26 -26
  33. package/src/editor/behavior/behavior.core.decorators.ts +9 -6
  34. package/src/editor/behavior/behavior.core.lists.ts +139 -17
  35. package/src/editor/behavior/behavior.core.ts +5 -2
  36. package/src/editor/behavior/behavior.guards.ts +28 -0
  37. package/src/editor/behavior/behavior.links.ts +7 -7
  38. package/src/editor/behavior/behavior.markdown.ts +68 -79
  39. package/src/editor/behavior/behavior.types.ts +86 -60
  40. package/src/editor/{use-editor.ts → create-editor.ts} +13 -8
  41. package/src/editor/editor-event-listener.tsx +2 -2
  42. package/src/editor/editor-machine.ts +54 -15
  43. package/src/editor/editor-provider.tsx +5 -5
  44. package/src/editor/editor-selector.ts +49 -0
  45. package/src/editor/editor-snapshot.ts +22 -0
  46. package/src/editor/get-value.ts +11 -0
  47. package/src/editor/plugins/create-with-event-listeners.ts +93 -5
  48. package/src/editor/plugins/createWithEditableAPI.ts +69 -20
  49. package/src/editor/plugins/createWithHotKeys.ts +0 -54
  50. package/src/editor/plugins/createWithPortableTextBlockStyle.ts +1 -55
  51. package/src/editor/plugins/with-plugins.ts +4 -8
  52. package/src/editor/{behavior/behavior.utils.block-offset.test.ts → utils/utils.block-offset.test.ts} +1 -1
  53. package/src/editor/{behavior/behavior.utils.block-offset.ts → utils/utils.block-offset.ts} +1 -8
  54. package/src/editor/{behavior/behavior.utils.reverse-selection.ts → utils/utils.reverse-selection.ts} +3 -5
  55. package/src/editor/utils/utils.ts +21 -0
  56. package/src/index.ts +13 -13
  57. package/src/selectors/index.ts +15 -0
  58. package/src/selectors/selector.get-active-list-item.ts +37 -0
  59. package/src/{editor/behavior/behavior.utils.get-selection-text.ts → selectors/selector.get-selection-text.ts} +10 -15
  60. package/src/selectors/selector.get-text-before.ts +41 -0
  61. package/src/selectors/selectors.ts +329 -0
  62. package/src/types/editor.ts +0 -60
  63. package/src/utils/is-hotkey.test.ts +2 -0
  64. package/src/utils/operationToPatches.ts +5 -0
  65. package/src/utils/paths.ts +4 -11
  66. package/src/utils/ranges.ts +3 -3
  67. package/lib/index.esm.js.map +0 -1
  68. package/lib/index.mjs +0 -7541
  69. package/lib/index.mjs.map +0 -1
  70. package/src/editor/behavior/behavior.utils.ts +0 -218
  71. package/src/editor/behavior/behavior.utilts.get-text-before.ts +0 -31
  72. package/src/editor/plugins/createWithPortableTextLists.ts +0 -172
  73. /package/src/editor/{behavior/behavior.utils.get-start-point.ts → utils/utils.get-start-point.ts} +0 -0
  74. /package/src/editor/{behavior/behavior.utils.is-keyed-segment.ts → utils/utils.is-keyed-segment.ts} +0 -0
@@ -6,10 +6,12 @@ import {
6
6
  Transforms,
7
7
  } from 'slate'
8
8
  import {ReactEditor} from 'slate-react'
9
- import type {PortableTextMemberSchemaTypes} from '../../types/editor'
9
+ import debug from '../../utils/debug'
10
+ import {toSlatePath} from '../../utils/paths'
10
11
  import {toSlateRange} from '../../utils/ranges'
11
12
  import {fromSlateValue, toSlateValue} from '../../utils/values'
12
13
  import {KEY_TO_VALUE_ELEMENT} from '../../utils/weakMaps'
14
+ import type {EditorContext} from '../editor-snapshot'
13
15
  import {
14
16
  addAnnotationActionImplementation,
15
17
  removeAnnotationActionImplementation,
@@ -20,24 +22,37 @@ import {
20
22
  removeDecoratorActionImplementation,
21
23
  toggleDecoratorActionImplementation,
22
24
  } from '../plugins/createWithPortableTextMarkModel'
25
+ import {blockOffsetToSpanSelectionPoint} from '../utils/utils.block-offset'
23
26
  import {insertBlock} from './behavior.action-utils.insert-block'
24
27
  import {insertBlockObjectActionImplementation} from './behavior.action.insert-block-object'
25
28
  import {
26
29
  insertBreakActionImplementation,
27
30
  insertSoftBreakActionImplementation,
28
31
  } from './behavior.action.insert-break'
32
+ import {insertInlineObjectActionImplementation} from './behavior.action.insert-inline-object'
29
33
  import {insertSpanActionImplementation} from './behavior.action.insert-span'
34
+ import {
35
+ addListItemActionImplementation,
36
+ removeListItemActionImplementation,
37
+ toggleListItemActionImplementation,
38
+ } from './behavior.action.list-item'
39
+ import {
40
+ addStyleActionImplementation,
41
+ removeStyleActionImplementation,
42
+ toggleStyleActionImplementation,
43
+ } from './behavior.action.style'
44
+ import {textBlockSetActionImplementation} from './behavior.action.text-block.set'
45
+ import {textBlockUnsetActionImplementation} from './behavior.action.text-block.unset'
30
46
  import type {
31
47
  BehaviorAction,
32
48
  BehaviorEvent,
33
49
  PickFromUnion,
34
50
  } from './behavior.types'
35
- import {blockOffsetToSpanSelectionPoint} from './behavior.utils.block-offset'
36
51
 
37
- export type BehaviorActionContext = {
38
- keyGenerator: () => string
39
- schema: PortableTextMemberSchemaTypes
40
- }
52
+ export type BehaviorActionImplementationContext = Pick<
53
+ EditorContext,
54
+ 'keyGenerator' | 'schema'
55
+ >
41
56
 
42
57
  export type BehaviorActionImplementation<
43
58
  TBehaviorActionType extends BehaviorAction['type'],
@@ -46,7 +61,7 @@ export type BehaviorActionImplementation<
46
61
  context,
47
62
  action,
48
63
  }: {
49
- context: BehaviorActionContext
64
+ context: BehaviorActionImplementationContext
50
65
  action: PickFromUnion<BehaviorAction, 'type', TBehaviorActionType>
51
66
  }) => TReturnType
52
67
 
@@ -58,48 +73,23 @@ const behaviorActionImplementations: BehaviorActionImplementations = {
58
73
  'annotation.add': addAnnotationActionImplementation,
59
74
  'annotation.remove': removeAnnotationActionImplementation,
60
75
  'annotation.toggle': toggleAnnotationActionImplementation,
76
+ 'blur': ({action}) => {
77
+ ReactEditor.blur(action.editor)
78
+ },
61
79
  'decorator.add': addDecoratorActionImplementation,
62
80
  'decorator.remove': removeDecoratorActionImplementation,
63
81
  'decorator.toggle': toggleDecoratorActionImplementation,
64
82
  'focus': ({action}) => {
65
83
  ReactEditor.focus(action.editor)
66
84
  },
67
- 'set block': ({action}) => {
68
- for (const path of action.paths) {
69
- const at = toSlateRange(
70
- {anchor: {path, offset: 0}, focus: {path, offset: 0}},
71
- action.editor,
72
- )!
73
-
74
- Transforms.setNodes(
75
- action.editor,
76
- {
77
- ...(action.style ? {style: action.style} : {}),
78
- ...(action.listItem ? {listItem: action.listItem} : {}),
79
- ...(action.level ? {level: action.level} : {}),
80
- },
81
- {at},
82
- )
83
- }
84
- },
85
- 'unset block': ({action}) => {
86
- for (const path of action.paths) {
87
- const at = toSlateRange(
88
- {anchor: {path, offset: 0}, focus: {path, offset: 0}},
89
- action.editor,
90
- )!
91
-
92
- Transforms.unsetNodes(action.editor, action.props, {at})
93
- }
94
- },
95
85
  'copy': () => {},
96
- 'delete backward': ({action}) => {
86
+ 'delete.backward': ({action}) => {
97
87
  deleteBackward(action.editor, action.unit)
98
88
  },
99
- 'delete forward': ({action}) => {
89
+ 'delete.forward': ({action}) => {
100
90
  deleteForward(action.editor, action.unit)
101
91
  },
102
- 'delete block': ({action}) => {
92
+ 'delete.block': ({action}) => {
103
93
  const range = toSlateRange(
104
94
  {
105
95
  anchor: {path: action.blockPath, offset: 0},
@@ -117,7 +107,7 @@ const behaviorActionImplementations: BehaviorActionImplementations = {
117
107
  at: range,
118
108
  })
119
109
  },
120
- 'delete text': ({context, action}) => {
110
+ 'delete.text': ({context, action}) => {
121
111
  const value = fromSlateValue(
122
112
  action.editor.children,
123
113
  context.schema.block.name,
@@ -155,14 +145,15 @@ const behaviorActionImplementations: BehaviorActionImplementations = {
155
145
  at: range,
156
146
  })
157
147
  },
158
- 'insert block object': insertBlockObjectActionImplementation,
159
- 'insert break': insertBreakActionImplementation,
160
- 'insert soft break': insertSoftBreakActionImplementation,
161
- 'insert span': insertSpanActionImplementation,
162
- 'insert text': ({action}) => {
148
+ 'insert.block object': insertBlockObjectActionImplementation,
149
+ 'insert.break': insertBreakActionImplementation,
150
+ 'insert.inline object': insertInlineObjectActionImplementation,
151
+ 'insert.soft break': insertSoftBreakActionImplementation,
152
+ 'insert.span': insertSpanActionImplementation,
153
+ 'insert.text': ({action}) => {
163
154
  insertText(action.editor, action.text)
164
155
  },
165
- 'insert text block': ({context, action}) => {
156
+ 'insert.text block': ({context, action}) => {
166
157
  const block = toSlateValue(
167
158
  [
168
159
  {
@@ -197,51 +188,45 @@ const behaviorActionImplementations: BehaviorActionImplementations = {
197
188
  },
198
189
  'key.down': () => {},
199
190
  'key.up': () => {},
200
- 'move block': ({action}) => {
201
- const location = toSlateRange(
202
- {
203
- anchor: {
204
- path: action.blockPath,
205
- offset: 0,
206
- },
207
- focus: {
208
- path: action.blockPath,
209
- offset: 0,
210
- },
211
- },
212
- action.editor,
213
- )
191
+ 'list item.add': addListItemActionImplementation,
192
+ 'list item.remove': removeListItemActionImplementation,
193
+ 'list item.toggle': toggleListItemActionImplementation,
194
+ 'move.block': ({action}) => {
195
+ const at = [toSlatePath(action.at, action.editor)[0]]
196
+ const to = [toSlatePath(action.to, action.editor)[0]]
214
197
 
215
- if (!location) {
216
- console.error('Unable to find Slate range from selection points')
217
- return
218
- }
198
+ Transforms.moveNodes(action.editor, {
199
+ at,
200
+ to,
201
+ mode: 'highest',
202
+ })
203
+ },
204
+ 'move.block down': ({action}) => {
205
+ const at = [toSlatePath(action.at, action.editor)[0]]
206
+ const to = [Path.next(at)[0]]
219
207
 
220
- const newLocation = toSlateRange(
221
- {
222
- anchor: {
223
- path: action.to,
224
- offset: 0,
225
- },
226
- focus: {
227
- path: action.to,
228
- offset: 0,
229
- },
230
- },
231
- action.editor,
232
- )
208
+ Transforms.moveNodes(action.editor, {
209
+ at,
210
+ to,
211
+ mode: 'highest',
212
+ })
213
+ },
214
+ 'move.block up': ({action}) => {
215
+ const at = [toSlatePath(action.at, action.editor)[0]]
233
216
 
234
- if (!newLocation) {
235
- console.error('Unable to find Slate range from selection points')
217
+ if (!Path.hasPrevious(at)) {
236
218
  return
237
219
  }
238
220
 
221
+ const to = [Path.previous(at)[0]]
222
+
239
223
  Transforms.moveNodes(action.editor, {
240
- at: location,
241
- to: newLocation.anchor.path.slice(0, 1),
224
+ at,
225
+ to,
242
226
  mode: 'highest',
243
227
  })
244
228
  },
229
+ 'noop': () => {},
245
230
  'paste': () => {},
246
231
  'select': ({action}) => {
247
232
  const newSelection = toSlateRange(action.selection, action.editor)
@@ -288,67 +273,88 @@ const behaviorActionImplementations: BehaviorActionImplementations = {
288
273
  action.editor.selection = {...selection}
289
274
  }
290
275
  },
276
+ 'style.toggle': toggleStyleActionImplementation,
277
+ 'style.add': addStyleActionImplementation,
278
+ 'style.remove': removeStyleActionImplementation,
279
+ 'text block.set': textBlockSetActionImplementation,
280
+ 'text block.unset': textBlockUnsetActionImplementation,
291
281
  }
292
282
 
293
283
  export function performAction({
294
284
  context,
295
285
  action,
296
286
  }: {
297
- context: BehaviorActionContext
287
+ context: BehaviorActionImplementationContext
298
288
  action: BehaviorAction
299
289
  }) {
290
+ debug('Behavior action', action)
291
+
300
292
  switch (action.type) {
301
- case 'delete block': {
302
- behaviorActionImplementations['delete block']({
293
+ case 'delete.block': {
294
+ behaviorActionImplementations['delete.block']({
295
+ context,
296
+ action,
297
+ })
298
+ break
299
+ }
300
+ case 'delete.text': {
301
+ behaviorActionImplementations['delete.text']({
302
+ context,
303
+ action,
304
+ })
305
+ break
306
+ }
307
+ case 'insert.span': {
308
+ behaviorActionImplementations['insert.span']({
303
309
  context,
304
310
  action,
305
311
  })
306
312
  break
307
313
  }
308
- case 'delete text': {
309
- behaviorActionImplementations['delete text']({
314
+ case 'insert.text block': {
315
+ behaviorActionImplementations['insert.text block']({
310
316
  context,
311
317
  action,
312
318
  })
313
319
  break
314
320
  }
315
- case 'insert block object': {
316
- behaviorActionImplementations['insert block object']({
321
+ case 'list item.add': {
322
+ behaviorActionImplementations['list item.add']({
317
323
  context,
318
324
  action,
319
325
  })
320
326
  break
321
327
  }
322
- case 'insert span': {
323
- behaviorActionImplementations['insert span']({
328
+ case 'list item.remove': {
329
+ behaviorActionImplementations['list item.remove']({
324
330
  context,
325
331
  action,
326
332
  })
327
333
  break
328
334
  }
329
- case 'insert text block': {
330
- behaviorActionImplementations['insert text block']({
335
+ case 'move.block': {
336
+ behaviorActionImplementations['move.block']({
331
337
  context,
332
338
  action,
333
339
  })
334
340
  break
335
341
  }
336
- case 'move block': {
337
- behaviorActionImplementations['move block']({
342
+ case 'move.block down': {
343
+ behaviorActionImplementations['move.block down']({
338
344
  context,
339
345
  action,
340
346
  })
341
347
  break
342
348
  }
343
- case 'set block': {
344
- behaviorActionImplementations['set block']({
349
+ case 'move.block up': {
350
+ behaviorActionImplementations['move.block up']({
345
351
  context,
346
352
  action,
347
353
  })
348
354
  break
349
355
  }
350
- case 'unset block': {
351
- behaviorActionImplementations['unset block']({
356
+ case 'noop': {
357
+ behaviorActionImplementations.noop({
352
358
  context,
353
359
  action,
354
360
  })
@@ -389,6 +395,34 @@ export function performAction({
389
395
  })
390
396
  break
391
397
  }
398
+ case 'style.add': {
399
+ behaviorActionImplementations['style.add']({
400
+ context,
401
+ action,
402
+ })
403
+ break
404
+ }
405
+ case 'style.remove': {
406
+ behaviorActionImplementations['style.remove']({
407
+ context,
408
+ action,
409
+ })
410
+ break
411
+ }
412
+ case 'text block.set': {
413
+ behaviorActionImplementations['text block.set']({
414
+ context,
415
+ action,
416
+ })
417
+ break
418
+ }
419
+ case 'text block.unset': {
420
+ behaviorActionImplementations['text block.unset']({
421
+ context,
422
+ action,
423
+ })
424
+ break
425
+ }
392
426
  default: {
393
427
  performDefaultAction({context, action})
394
428
  }
@@ -399,7 +433,7 @@ function performDefaultAction({
399
433
  context,
400
434
  action,
401
435
  }: {
402
- context: BehaviorActionContext
436
+ context: BehaviorActionImplementationContext
403
437
  action: PickFromUnion<BehaviorAction, 'type', BehaviorEvent['type']>
404
438
  }) {
405
439
  switch (action.type) {
@@ -424,6 +458,13 @@ function performDefaultAction({
424
458
  })
425
459
  break
426
460
  }
461
+ case 'blur': {
462
+ behaviorActionImplementations.blur({
463
+ context,
464
+ action,
465
+ })
466
+ break
467
+ }
427
468
  case 'copy': {
428
469
  behaviorActionImplementations.copy({
429
470
  context,
@@ -452,15 +493,15 @@ function performDefaultAction({
452
493
  })
453
494
  break
454
495
  }
455
- case 'delete backward': {
456
- behaviorActionImplementations['delete backward']({
496
+ case 'delete.backward': {
497
+ behaviorActionImplementations['delete.backward']({
457
498
  context,
458
499
  action,
459
500
  })
460
501
  break
461
502
  }
462
- case 'delete forward': {
463
- behaviorActionImplementations['delete forward']({
503
+ case 'delete.forward': {
504
+ behaviorActionImplementations['delete.forward']({
464
505
  context,
465
506
  action,
466
507
  })
@@ -473,22 +514,36 @@ function performDefaultAction({
473
514
  })
474
515
  break
475
516
  }
476
- case 'insert break': {
477
- behaviorActionImplementations['insert break']({
517
+ case 'insert.block object': {
518
+ behaviorActionImplementations['insert.block object']({
519
+ context,
520
+ action,
521
+ })
522
+ break
523
+ }
524
+ case 'insert.inline object': {
525
+ behaviorActionImplementations['insert.inline object']({
478
526
  context,
479
527
  action,
480
528
  })
481
529
  break
482
530
  }
483
- case 'insert soft break': {
484
- behaviorActionImplementations['insert soft break']({
531
+ case 'insert.break': {
532
+ behaviorActionImplementations['insert.break']({
485
533
  context,
486
534
  action,
487
535
  })
488
536
  break
489
537
  }
490
- case 'insert text': {
491
- behaviorActionImplementations['insert text']({
538
+ case 'insert.soft break': {
539
+ behaviorActionImplementations['insert.soft break']({
540
+ context,
541
+ action,
542
+ })
543
+ break
544
+ }
545
+ case 'insert.text': {
546
+ behaviorActionImplementations['insert.text']({
492
547
  context,
493
548
  action,
494
549
  })
@@ -508,11 +563,25 @@ function performDefaultAction({
508
563
  })
509
564
  break
510
565
  }
511
- default: {
566
+ case 'list item.toggle': {
567
+ behaviorActionImplementations['list item.toggle']({
568
+ context,
569
+ action,
570
+ })
571
+ break
572
+ }
573
+ case 'paste': {
512
574
  behaviorActionImplementations.paste({
513
575
  context,
514
576
  action,
515
577
  })
578
+ break
579
+ }
580
+ default: {
581
+ behaviorActionImplementations['style.toggle']({
582
+ context,
583
+ action,
584
+ })
516
585
  }
517
586
  }
518
587
  }
@@ -1,11 +1,10 @@
1
+ import {
2
+ getFirstBlock,
3
+ getLastBlock,
4
+ getSelectedBlocks,
5
+ } from '../../selectors/selectors'
1
6
  import {isHotkey} from '../../utils/is-hotkey'
2
7
  import {defineBehavior} from './behavior.types'
3
- import {
4
- getFocusBlock,
5
- getNextBlock,
6
- getPreviousBlock,
7
- selectionIsCollapsed,
8
- } from './behavior.utils'
9
8
 
10
9
  /**
11
10
  * @alpha
@@ -23,63 +22,54 @@ export function createCodeEditorBehaviors(config: CodeEditorBehaviorsConfig) {
23
22
  defineBehavior({
24
23
  on: 'key.down',
25
24
  guard: ({context, event}) => {
26
- const isAltArrowUp = isHotkey(
25
+ const isMoveUpShortcut = isHotkey(
27
26
  config.moveBlockUpShortcut,
28
27
  event.keyboardEvent,
29
28
  )
29
+ const firstBlock = getFirstBlock({context})
30
+ const selectedBlocks = getSelectedBlocks({context})
31
+ const blocksAbove =
32
+ firstBlock?.node._key !== selectedBlocks[0]?.node._key
30
33
 
31
- if (!isAltArrowUp || !selectionIsCollapsed(context)) {
34
+ if (!isMoveUpShortcut || !blocksAbove) {
32
35
  return false
33
36
  }
34
37
 
35
- const focusBlock = getFocusBlock(context)
36
- const previousBlock = getPreviousBlock(context)
37
-
38
- if (focusBlock && previousBlock) {
39
- return {focusBlock, previousBlock}
40
- }
41
-
42
- return false
38
+ return {paths: selectedBlocks.map((block) => block.path)}
43
39
  },
44
40
  actions: [
45
- (_, {focusBlock, previousBlock}) => [
46
- {
47
- type: 'move block',
48
- blockPath: focusBlock.path,
49
- to: previousBlock.path,
50
- },
51
- ],
41
+ ({paths}) =>
42
+ paths.map((at) => ({
43
+ type: 'move.block up',
44
+ at,
45
+ })),
52
46
  ],
53
47
  }),
54
48
  defineBehavior({
55
49
  on: 'key.down',
56
50
  guard: ({context, event}) => {
57
- const isAltArrowDown = isHotkey(
51
+ const isMoveDownShortcut = isHotkey(
58
52
  config.moveBlockDownShortcut,
59
53
  event.keyboardEvent,
60
54
  )
55
+ const lastBlock = getLastBlock({context})
56
+ const selectedBlocks = getSelectedBlocks({context})
57
+ const blocksBelow =
58
+ lastBlock?.node._key !==
59
+ selectedBlocks[selectedBlocks.length - 1]?.node._key
61
60
 
62
- if (!isAltArrowDown || !selectionIsCollapsed(context)) {
61
+ if (!isMoveDownShortcut || !blocksBelow) {
63
62
  return false
64
63
  }
65
64
 
66
- const focusBlock = getFocusBlock(context)
67
- const nextBlock = getNextBlock(context)
68
-
69
- if (focusBlock && nextBlock) {
70
- return {focusBlock, nextBlock}
71
- }
72
-
73
- return false
65
+ return {paths: selectedBlocks.map((block) => block.path).reverse()}
74
66
  },
75
67
  actions: [
76
- (_, {focusBlock, nextBlock}) => [
77
- {
78
- type: 'move block',
79
- blockPath: focusBlock.path,
80
- to: nextBlock.path,
81
- },
82
- ],
68
+ ({paths}) =>
69
+ paths.map((at) => ({
70
+ type: 'move.block down',
71
+ at,
72
+ })),
83
73
  ],
84
74
  }),
85
75
  ]