@wordpress/block-editor 10.1.0 → 10.2.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.
Files changed (148) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/build/components/block-controls/slot.js +2 -2
  3. package/build/components/block-controls/slot.js.map +1 -1
  4. package/build/components/block-draggable/index.js +1 -1
  5. package/build/components/block-draggable/index.js.map +1 -1
  6. package/build/components/block-inspector/index.js +4 -5
  7. package/build/components/block-inspector/index.js.map +1 -1
  8. package/build/components/block-list/use-in-between-inserter.js +4 -4
  9. package/build/components/block-list/use-in-between-inserter.js.map +1 -1
  10. package/build/components/block-parent-selector/index.js +2 -2
  11. package/build/components/block-parent-selector/index.js.map +1 -1
  12. package/build/components/block-popover/inbetween.js +3 -1
  13. package/build/components/block-popover/inbetween.js.map +1 -1
  14. package/build/components/block-popover/index.js +20 -17
  15. package/build/components/block-popover/index.js.map +1 -1
  16. package/build/components/block-settings-menu/block-settings-dropdown.js +5 -5
  17. package/build/components/block-settings-menu/block-settings-dropdown.js.map +1 -1
  18. package/build/components/block-tools/index.js +1 -1
  19. package/build/components/block-tools/index.js.map +1 -1
  20. package/build/components/block-tools/use-block-toolbar-popover-props.js +6 -3
  21. package/build/components/block-tools/use-block-toolbar-popover-props.js.map +1 -1
  22. package/build/components/image-editor/use-transform-image.js +2 -2
  23. package/build/components/image-editor/use-transform-image.js.map +1 -1
  24. package/build/components/index.js +9 -0
  25. package/build/components/index.js.map +1 -1
  26. package/build/components/inserter/block-types-tab.js +1 -1
  27. package/build/components/inserter/block-types-tab.js.map +1 -1
  28. package/build/components/inserter/search-items.js +2 -17
  29. package/build/components/inserter/search-items.js.map +1 -1
  30. package/build/components/inspector-controls/slot.js +2 -1
  31. package/build/components/inspector-controls/slot.js.map +1 -1
  32. package/build/components/rich-text/index.js +17 -0
  33. package/build/components/rich-text/index.js.map +1 -1
  34. package/build/components/spacing-sizes-control/index.js +10 -2
  35. package/build/components/spacing-sizes-control/index.js.map +1 -1
  36. package/build/components/spacing-sizes-control/spacing-input-control.js +5 -4
  37. package/build/components/spacing-sizes-control/spacing-input-control.js.map +1 -1
  38. package/build/components/url-popover/image-url-input-ui.js +1 -1
  39. package/build/components/url-popover/image-url-input-ui.js.map +1 -1
  40. package/build/components/use-block-drop-zone/index.js +19 -1
  41. package/build/components/use-block-drop-zone/index.js.map +1 -1
  42. package/build/components/use-on-block-drop/index.js +62 -20
  43. package/build/components/use-on-block-drop/index.js.map +1 -1
  44. package/build/components/writing-flow/use-arrow-nav.js +14 -7
  45. package/build/components/writing-flow/use-arrow-nav.js.map +1 -1
  46. package/build/hooks/index.js +13 -1
  47. package/build/hooks/index.js.map +1 -1
  48. package/build/hooks/layout.js +76 -23
  49. package/build/hooks/layout.js.map +1 -1
  50. package/build/index.js +14 -0
  51. package/build/index.js.map +1 -1
  52. package/build/store/reducer.js +5 -3
  53. package/build/store/reducer.js.map +1 -1
  54. package/build/store/selectors.js +1 -1
  55. package/build/store/selectors.js.map +1 -1
  56. package/build-module/components/block-controls/slot.js +3 -3
  57. package/build-module/components/block-controls/slot.js.map +1 -1
  58. package/build-module/components/block-draggable/index.js +1 -1
  59. package/build-module/components/block-draggable/index.js.map +1 -1
  60. package/build-module/components/block-inspector/index.js +5 -6
  61. package/build-module/components/block-inspector/index.js.map +1 -1
  62. package/build-module/components/block-list/use-in-between-inserter.js +4 -4
  63. package/build-module/components/block-list/use-in-between-inserter.js.map +1 -1
  64. package/build-module/components/block-parent-selector/index.js +2 -2
  65. package/build-module/components/block-parent-selector/index.js.map +1 -1
  66. package/build-module/components/block-popover/inbetween.js +3 -1
  67. package/build-module/components/block-popover/inbetween.js.map +1 -1
  68. package/build-module/components/block-popover/index.js +20 -17
  69. package/build-module/components/block-popover/index.js.map +1 -1
  70. package/build-module/components/block-settings-menu/block-settings-dropdown.js +7 -7
  71. package/build-module/components/block-settings-menu/block-settings-dropdown.js.map +1 -1
  72. package/build-module/components/block-tools/index.js +1 -1
  73. package/build-module/components/block-tools/index.js.map +1 -1
  74. package/build-module/components/block-tools/use-block-toolbar-popover-props.js +6 -3
  75. package/build-module/components/block-tools/use-block-toolbar-popover-props.js.map +1 -1
  76. package/build-module/components/image-editor/use-transform-image.js +2 -2
  77. package/build-module/components/image-editor/use-transform-image.js.map +1 -1
  78. package/build-module/components/index.js +1 -0
  79. package/build-module/components/index.js.map +1 -1
  80. package/build-module/components/inserter/block-types-tab.js +3 -3
  81. package/build-module/components/inserter/block-types-tab.js.map +1 -1
  82. package/build-module/components/inserter/search-items.js +3 -17
  83. package/build-module/components/inserter/search-items.js.map +1 -1
  84. package/build-module/components/inspector-controls/slot.js +3 -2
  85. package/build-module/components/inspector-controls/slot.js.map +1 -1
  86. package/build-module/components/rich-text/index.js +17 -0
  87. package/build-module/components/rich-text/index.js.map +1 -1
  88. package/build-module/components/spacing-sizes-control/index.js +10 -3
  89. package/build-module/components/spacing-sizes-control/index.js.map +1 -1
  90. package/build-module/components/spacing-sizes-control/spacing-input-control.js +6 -5
  91. package/build-module/components/spacing-sizes-control/spacing-input-control.js.map +1 -1
  92. package/build-module/components/url-popover/image-url-input-ui.js +1 -1
  93. package/build-module/components/url-popover/image-url-input-ui.js.map +1 -1
  94. package/build-module/components/use-block-drop-zone/index.js +19 -1
  95. package/build-module/components/use-block-drop-zone/index.js.map +1 -1
  96. package/build-module/components/use-on-block-drop/index.js +62 -21
  97. package/build-module/components/use-on-block-drop/index.js.map +1 -1
  98. package/build-module/components/writing-flow/use-arrow-nav.js +14 -7
  99. package/build-module/components/writing-flow/use-arrow-nav.js.map +1 -1
  100. package/build-module/hooks/index.js +1 -0
  101. package/build-module/hooks/index.js.map +1 -1
  102. package/build-module/hooks/layout.js +73 -23
  103. package/build-module/hooks/layout.js.map +1 -1
  104. package/build-module/index.js +1 -1
  105. package/build-module/index.js.map +1 -1
  106. package/build-module/store/reducer.js +5 -4
  107. package/build-module/store/reducer.js.map +1 -1
  108. package/build-module/store/selectors.js +1 -1
  109. package/build-module/store/selectors.js.map +1 -1
  110. package/build-style/style-rtl.css +48 -23
  111. package/build-style/style.css +48 -23
  112. package/package.json +29 -30
  113. package/src/components/block-controls/slot.js +3 -3
  114. package/src/components/block-draggable/index.js +1 -1
  115. package/src/components/block-draggable/test/index.native.js +0 -9
  116. package/src/components/block-inspector/index.js +6 -10
  117. package/src/components/block-list/use-in-between-inserter.js +5 -5
  118. package/src/components/block-parent-selector/index.js +2 -2
  119. package/src/components/block-popover/inbetween.js +1 -1
  120. package/src/components/block-popover/index.js +37 -22
  121. package/src/components/block-preview/style.scss +13 -0
  122. package/src/components/block-settings-menu/block-settings-dropdown.js +7 -7
  123. package/src/components/block-switcher/test/__snapshots__/index.js.snap +104 -33
  124. package/src/components/block-switcher/test/index.js +121 -61
  125. package/src/components/block-tools/index.js +1 -1
  126. package/src/components/block-tools/use-block-toolbar-popover-props.js +6 -0
  127. package/src/components/button-block-appender/style.scss +3 -1
  128. package/src/components/image-editor/use-transform-image.js +2 -2
  129. package/src/components/index.js +1 -0
  130. package/src/components/inserter/block-types-tab.js +3 -3
  131. package/src/components/inserter/search-items.js +3 -15
  132. package/src/components/inserter/test/search-items.js +4 -0
  133. package/src/components/inspector-controls/slot.js +6 -2
  134. package/src/components/responsive-block-control/test/index.js +73 -118
  135. package/src/components/rich-text/index.js +22 -0
  136. package/src/components/spacing-sizes-control/index.js +15 -3
  137. package/src/components/spacing-sizes-control/spacing-input-control.js +8 -7
  138. package/src/components/spacing-sizes-control/style.scss +28 -24
  139. package/src/components/url-popover/image-url-input-ui.js +1 -1
  140. package/src/components/use-block-drop-zone/index.js +26 -1
  141. package/src/components/use-on-block-drop/index.js +110 -35
  142. package/src/components/use-on-block-drop/test/index.js +33 -43
  143. package/src/components/writing-flow/use-arrow-nav.js +12 -8
  144. package/src/hooks/index.js +1 -0
  145. package/src/hooks/layout.js +64 -21
  146. package/src/index.js +2 -0
  147. package/src/store/reducer.js +4 -4
  148. package/src/store/selectors.js +1 -0
@@ -1,13 +1,14 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
+ import { useCallback } from '@wordpress/element';
4
5
  import {
5
6
  cloneBlock,
6
7
  findTransform,
7
8
  getBlockTransforms,
8
9
  pasteHandler,
9
10
  } from '@wordpress/blocks';
10
- import { useDispatch, useSelect } from '@wordpress/data';
11
+ import { useDispatch, useSelect, useRegistry } from '@wordpress/data';
11
12
  import { getFilesFromDataTransfer } from '@wordpress/dom';
12
13
 
13
14
  /**
@@ -56,8 +57,8 @@ export function parseDropEvent( event ) {
56
57
  * @param {number} targetBlockIndex The index where the block(s) will be inserted.
57
58
  * @param {Function} getBlockIndex A function that gets the index of a block.
58
59
  * @param {Function} getClientIdsOfDescendants A function that gets the client ids of descendant blocks.
59
- * @param {Function} moveBlocksToPosition A function that moves blocks.
60
- * @param {Function} insertBlocks A function that inserts blocks.
60
+ * @param {Function} moveBlocks A function that moves blocks.
61
+ * @param {Function} insertOrReplaceBlocks A function that inserts or replaces blocks.
61
62
  * @param {Function} clearSelectedBlock A function that clears block selection.
62
63
  * @return {Function} The event handler for a block drop event.
63
64
  */
@@ -66,8 +67,8 @@ export function onBlockDrop(
66
67
  targetBlockIndex,
67
68
  getBlockIndex,
68
69
  getClientIdsOfDescendants,
69
- moveBlocksToPosition,
70
- insertBlocks,
70
+ moveBlocks,
71
+ insertOrReplaceBlocks,
71
72
  clearSelectedBlock
72
73
  ) {
73
74
  return ( event ) => {
@@ -84,13 +85,7 @@ export function onBlockDrop(
84
85
  const blocksToInsert = blocks.map( ( block ) =>
85
86
  cloneBlock( block )
86
87
  );
87
- insertBlocks(
88
- blocksToInsert,
89
- targetBlockIndex,
90
- targetRootClientId,
91
- true,
92
- null
93
- );
88
+ insertOrReplaceBlocks( blocksToInsert, true, null );
94
89
  }
95
90
 
96
91
  // If the user is moving a block.
@@ -128,12 +123,7 @@ export function onBlockDrop(
128
123
  ? targetBlockIndex - draggedBlockCount
129
124
  : targetBlockIndex;
130
125
 
131
- moveBlocksToPosition(
132
- sourceClientIds,
133
- sourceRootClientId,
134
- targetRootClientId,
135
- insertIndex
136
- );
126
+ moveBlocks( sourceClientIds, sourceRootClientId, insertIndex );
137
127
  }
138
128
  };
139
129
  }
@@ -146,7 +136,7 @@ export function onBlockDrop(
146
136
  * @param {boolean} hasUploadPermissions Whether the user has upload permissions.
147
137
  * @param {Function} updateBlockAttributes A function that updates a block's attributes.
148
138
  * @param {Function} canInsertBlockType A function that returns checks whether a block type can be inserted.
149
- * @param {Function} insertBlocks A function that inserts blocks.
139
+ * @param {Function} insertOrReplaceBlocks A function that inserts or replaces blocks.
150
140
  *
151
141
  * @return {Function} The event handler for a block-related file drop event.
152
142
  */
@@ -156,7 +146,7 @@ export function onFilesDrop(
156
146
  hasUploadPermissions,
157
147
  updateBlockAttributes,
158
148
  canInsertBlockType,
159
- insertBlocks
149
+ insertOrReplaceBlocks
160
150
  ) {
161
151
  return ( files ) => {
162
152
  if ( ! hasUploadPermissions ) {
@@ -176,7 +166,7 @@ export function onFilesDrop(
176
166
  files,
177
167
  updateBlockAttributes
178
168
  );
179
- insertBlocks( blocks, targetBlockIndex, targetRootClientId );
169
+ insertOrReplaceBlocks( blocks );
180
170
  }
181
171
  };
182
172
  }
@@ -184,22 +174,22 @@ export function onFilesDrop(
184
174
  /**
185
175
  * A function that returns an event handler function for block-related HTML drop events.
186
176
  *
187
- * @param {string} targetRootClientId The root client id where the block(s) will be inserted.
188
- * @param {number} targetBlockIndex The index where the block(s) will be inserted.
189
- * @param {Function} insertBlocks A function that inserts blocks.
177
+ * @param {string} targetRootClientId The root client id where the block(s) will be inserted.
178
+ * @param {number} targetBlockIndex The index where the block(s) will be inserted.
179
+ * @param {Function} insertOrReplaceBlocks A function that inserts or replaces blocks.
190
180
  *
191
181
  * @return {Function} The event handler for a block-related HTML drop event.
192
182
  */
193
183
  export function onHTMLDrop(
194
184
  targetRootClientId,
195
185
  targetBlockIndex,
196
- insertBlocks
186
+ insertOrReplaceBlocks
197
187
  ) {
198
188
  return ( HTML ) => {
199
189
  const blocks = pasteHandler( { HTML, mode: 'BLOCKS' } );
200
190
 
201
191
  if ( blocks.length ) {
202
- insertBlocks( blocks, targetBlockIndex, targetRootClientId );
192
+ insertOrReplaceBlocks( blocks );
203
193
  }
204
194
  };
205
195
  }
@@ -207,32 +197,117 @@ export function onHTMLDrop(
207
197
  /**
208
198
  * A React hook for handling block drop events.
209
199
  *
210
- * @param {string} targetRootClientId The root client id where the block(s) will be inserted.
211
- * @param {number} targetBlockIndex The index where the block(s) will be inserted.
200
+ * @typedef {'insert'|'replace'} DropAction The type of action to perform on drop.
201
+ *
202
+ * @param {string} targetRootClientId The root client id where the block(s) will be inserted.
203
+ * @param {number} targetBlockIndex The index where the block(s) will be inserted.
204
+ * @param {Object} options The optional options.
205
+ * @param {DropAction} options.action The type of action to perform on drop. Could be `insert` or `replace` for now.
212
206
  *
213
207
  * @return {Object} An object that contains the event handlers `onDrop`, `onFilesDrop` and `onHTMLDrop`.
214
208
  */
215
- export default function useOnBlockDrop( targetRootClientId, targetBlockIndex ) {
209
+ export default function useOnBlockDrop(
210
+ targetRootClientId,
211
+ targetBlockIndex,
212
+ options = {}
213
+ ) {
214
+ const { action = 'insert' } = options;
216
215
  const hasUploadPermissions = useSelect(
217
216
  ( select ) => select( blockEditorStore ).getSettings().mediaUpload,
218
217
  []
219
218
  );
220
- const { canInsertBlockType, getBlockIndex, getClientIdsOfDescendants } =
221
- useSelect( blockEditorStore );
219
+ const {
220
+ canInsertBlockType,
221
+ getBlockIndex,
222
+ getClientIdsOfDescendants,
223
+ getBlockOrder,
224
+ getBlocksByClientId,
225
+ } = useSelect( blockEditorStore );
222
226
  const {
223
227
  insertBlocks,
224
228
  moveBlocksToPosition,
225
229
  updateBlockAttributes,
226
230
  clearSelectedBlock,
231
+ replaceBlocks,
232
+ removeBlocks,
227
233
  } = useDispatch( blockEditorStore );
234
+ const registry = useRegistry();
235
+
236
+ const insertOrReplaceBlocks = useCallback(
237
+ ( blocks, updateSelection = true, initialPosition = 0 ) => {
238
+ if ( action === 'replace' ) {
239
+ const clientIds = getBlockOrder( targetRootClientId );
240
+ const clientId = clientIds[ targetBlockIndex ];
241
+
242
+ replaceBlocks( clientId, blocks, undefined, initialPosition );
243
+ } else {
244
+ insertBlocks(
245
+ blocks,
246
+ targetBlockIndex,
247
+ targetRootClientId,
248
+ updateSelection,
249
+ initialPosition
250
+ );
251
+ }
252
+ },
253
+ [
254
+ action,
255
+ getBlockOrder,
256
+ insertBlocks,
257
+ replaceBlocks,
258
+ targetBlockIndex,
259
+ targetRootClientId,
260
+ ]
261
+ );
262
+
263
+ const moveBlocks = useCallback(
264
+ ( sourceClientIds, sourceRootClientId, insertIndex ) => {
265
+ if ( action === 'replace' ) {
266
+ const sourceBlocks = getBlocksByClientId( sourceClientIds );
267
+ const targetBlockClientIds =
268
+ getBlockOrder( targetRootClientId );
269
+ const targetBlockClientId =
270
+ targetBlockClientIds[ targetBlockIndex ];
271
+
272
+ registry.batch( () => {
273
+ // Remove the source blocks.
274
+ removeBlocks( sourceClientIds, false );
275
+ // Replace the target block with the source blocks.
276
+ replaceBlocks(
277
+ targetBlockClientId,
278
+ sourceBlocks,
279
+ undefined,
280
+ 0
281
+ );
282
+ } );
283
+ } else {
284
+ moveBlocksToPosition(
285
+ sourceClientIds,
286
+ sourceRootClientId,
287
+ targetRootClientId,
288
+ insertIndex
289
+ );
290
+ }
291
+ },
292
+ [
293
+ action,
294
+ getBlockOrder,
295
+ getBlocksByClientId,
296
+ insertBlocks,
297
+ moveBlocksToPosition,
298
+ removeBlocks,
299
+ targetBlockIndex,
300
+ targetRootClientId,
301
+ ]
302
+ );
228
303
 
229
304
  const _onDrop = onBlockDrop(
230
305
  targetRootClientId,
231
306
  targetBlockIndex,
232
307
  getBlockIndex,
233
308
  getClientIdsOfDescendants,
234
- moveBlocksToPosition,
235
- insertBlocks,
309
+ moveBlocks,
310
+ insertOrReplaceBlocks,
236
311
  clearSelectedBlock
237
312
  );
238
313
  const _onFilesDrop = onFilesDrop(
@@ -241,12 +316,12 @@ export default function useOnBlockDrop( targetRootClientId, targetBlockIndex ) {
241
316
  hasUploadPermissions,
242
317
  updateBlockAttributes,
243
318
  canInsertBlockType,
244
- insertBlocks
319
+ insertOrReplaceBlocks
245
320
  );
246
321
  const _onHTMLDrop = onHTMLDrop(
247
322
  targetRootClientId,
248
323
  targetBlockIndex,
249
- insertBlocks
324
+ insertOrReplaceBlocks
250
325
  );
251
326
 
252
327
  return ( event ) => {
@@ -98,7 +98,7 @@ describe( 'onBlockDrop', () => {
98
98
  const targetBlockIndex = 0;
99
99
  const getBlockIndex = noop;
100
100
  const getClientIdsOfDescendants = noop;
101
- const moveBlocksToPosition = jest.fn();
101
+ const moveBlocks = jest.fn();
102
102
 
103
103
  const event = {
104
104
  dataTransfer: {
@@ -115,11 +115,11 @@ describe( 'onBlockDrop', () => {
115
115
  targetBlockIndex,
116
116
  getBlockIndex,
117
117
  getClientIdsOfDescendants,
118
- moveBlocksToPosition
118
+ moveBlocks
119
119
  );
120
120
  eventHandler( event );
121
121
 
122
- expect( moveBlocksToPosition ).not.toHaveBeenCalled();
122
+ expect( moveBlocks ).not.toHaveBeenCalled();
123
123
  } );
124
124
 
125
125
  it( 'does nothing if the block is dropped to the same place it was dragged from', () => {
@@ -128,7 +128,7 @@ describe( 'onBlockDrop', () => {
128
128
  // Target and source block index is the same.
129
129
  const getBlockIndex = jest.fn( () => targetBlockIndex );
130
130
  const getClientIdsOfDescendants = noop;
131
- const moveBlocksToPosition = jest.fn();
131
+ const moveBlocks = jest.fn();
132
132
 
133
133
  const event = {
134
134
  dataTransfer: {
@@ -148,11 +148,11 @@ describe( 'onBlockDrop', () => {
148
148
  targetBlockIndex,
149
149
  getBlockIndex,
150
150
  getClientIdsOfDescendants,
151
- moveBlocksToPosition
151
+ moveBlocks
152
152
  );
153
153
  eventHandler( event );
154
154
 
155
- expect( moveBlocksToPosition ).not.toHaveBeenCalled();
155
+ expect( moveBlocks ).not.toHaveBeenCalled();
156
156
  } );
157
157
 
158
158
  it( 'does nothing if the block is dropped as a child of itself', () => {
@@ -160,7 +160,7 @@ describe( 'onBlockDrop', () => {
160
160
  const targetBlockIndex = 0;
161
161
  const getBlockIndex = jest.fn( () => 6 );
162
162
  const getClientIdsOfDescendants = noop;
163
- const moveBlocksToPosition = jest.fn();
163
+ const moveBlocks = jest.fn();
164
164
 
165
165
  const event = {
166
166
  dataTransfer: {
@@ -180,11 +180,11 @@ describe( 'onBlockDrop', () => {
180
180
  targetBlockIndex,
181
181
  getBlockIndex,
182
182
  getClientIdsOfDescendants,
183
- moveBlocksToPosition
183
+ moveBlocks
184
184
  );
185
185
  eventHandler( event );
186
186
 
187
- expect( moveBlocksToPosition ).not.toHaveBeenCalled();
187
+ expect( moveBlocks ).not.toHaveBeenCalled();
188
188
  } );
189
189
 
190
190
  it( 'does nothing if the block is dropped as a descendant of itself', () => {
@@ -195,7 +195,7 @@ describe( 'onBlockDrop', () => {
195
195
  const getClientIdsOfDescendants = jest.fn( () => [
196
196
  targetRootClientId,
197
197
  ] );
198
- const moveBlocksToPosition = jest.fn();
198
+ const moveBlocks = jest.fn();
199
199
 
200
200
  const event = {
201
201
  dataTransfer: {
@@ -214,11 +214,11 @@ describe( 'onBlockDrop', () => {
214
214
  targetBlockIndex,
215
215
  getBlockIndex,
216
216
  getClientIdsOfDescendants,
217
- moveBlocksToPosition
217
+ moveBlocks
218
218
  );
219
219
  eventHandler( event );
220
220
 
221
- expect( moveBlocksToPosition ).not.toHaveBeenCalled();
221
+ expect( moveBlocks ).not.toHaveBeenCalled();
222
222
  } );
223
223
 
224
224
  it( 'inserts blocks if the drop is valid', () => {
@@ -228,7 +228,7 @@ describe( 'onBlockDrop', () => {
228
228
  const targetBlockIndex = 0;
229
229
  const getBlockIndex = jest.fn( () => 1 );
230
230
  const getClientIdsOfDescendants = () => [];
231
- const moveBlocksToPosition = jest.fn();
231
+ const moveBlocks = jest.fn();
232
232
 
233
233
  const event = {
234
234
  dataTransfer: {
@@ -247,14 +247,13 @@ describe( 'onBlockDrop', () => {
247
247
  targetBlockIndex,
248
248
  getBlockIndex,
249
249
  getClientIdsOfDescendants,
250
- moveBlocksToPosition
250
+ moveBlocks
251
251
  );
252
252
  eventHandler( event );
253
253
 
254
- expect( moveBlocksToPosition ).toHaveBeenCalledWith(
254
+ expect( moveBlocks ).toHaveBeenCalledWith(
255
255
  sourceClientIds,
256
256
  sourceRootClientId,
257
- targetRootClientId,
258
257
  targetBlockIndex
259
258
  );
260
259
  } );
@@ -267,7 +266,7 @@ describe( 'onBlockDrop', () => {
267
266
  const getBlockIndex = jest.fn( () => 1 );
268
267
  // Dragged block is being dropped as a descendant of itself.
269
268
  const getClientIdsOfDescendants = () => [];
270
- const moveBlocksToPosition = jest.fn();
269
+ const moveBlocks = jest.fn();
271
270
 
272
271
  const event = {
273
272
  dataTransfer: {
@@ -289,14 +288,13 @@ describe( 'onBlockDrop', () => {
289
288
  targetBlockIndex,
290
289
  getBlockIndex,
291
290
  getClientIdsOfDescendants,
292
- moveBlocksToPosition
291
+ moveBlocks
293
292
  );
294
293
  eventHandler( event );
295
294
 
296
- expect( moveBlocksToPosition ).toHaveBeenCalledWith(
295
+ expect( moveBlocks ).toHaveBeenCalledWith(
297
296
  sourceClientIds,
298
297
  sourceRootClientId,
299
- targetRootClientId,
300
298
  insertIndex
301
299
  );
302
300
  } );
@@ -306,7 +304,7 @@ describe( 'onFilesDrop', () => {
306
304
  it( 'does nothing if hasUploadPermissions is false', () => {
307
305
  const updateBlockAttributes = jest.fn();
308
306
  const canInsertBlockType = noop;
309
- const insertBlocks = jest.fn();
307
+ const insertOrReplaceBlocks = jest.fn();
310
308
  const targetRootClientId = '1';
311
309
  const targetBlockIndex = 0;
312
310
  const uploadPermissions = false;
@@ -317,12 +315,12 @@ describe( 'onFilesDrop', () => {
317
315
  uploadPermissions,
318
316
  updateBlockAttributes,
319
317
  canInsertBlockType,
320
- insertBlocks
318
+ insertOrReplaceBlocks
321
319
  );
322
320
  onFileDropHandler();
323
321
 
324
322
  expect( findTransform ).not.toHaveBeenCalled();
325
- expect( insertBlocks ).not.toHaveBeenCalled();
323
+ expect( insertOrReplaceBlocks ).not.toHaveBeenCalled();
326
324
  } );
327
325
 
328
326
  it( 'does nothing if the block has no matching file transforms', () => {
@@ -330,7 +328,7 @@ describe( 'onFilesDrop', () => {
330
328
  // to have no return value.
331
329
  findTransform.mockImplementation( noop );
332
330
  const updateBlockAttributes = noop;
333
- const insertBlocks = jest.fn();
331
+ const insertOrReplaceBlocks = jest.fn();
334
332
  const canInsertBlockType = noop;
335
333
  const targetRootClientId = '1';
336
334
  const targetBlockIndex = 0;
@@ -342,12 +340,12 @@ describe( 'onFilesDrop', () => {
342
340
  uploadPermissions,
343
341
  updateBlockAttributes,
344
342
  canInsertBlockType,
345
- insertBlocks
343
+ insertOrReplaceBlocks
346
344
  );
347
345
  onFileDropHandler();
348
346
 
349
347
  expect( findTransform ).toHaveBeenCalled();
350
- expect( insertBlocks ).not.toHaveBeenCalled();
348
+ expect( insertOrReplaceBlocks ).not.toHaveBeenCalled();
351
349
  } );
352
350
 
353
351
  it( 'inserts blocks if a valid transform can be found', () => {
@@ -359,7 +357,7 @@ describe( 'onFilesDrop', () => {
359
357
  findTransform.mockImplementation( () => transformation );
360
358
  const updateBlockAttributes = noop;
361
359
  const canInsertBlockType = noop;
362
- const insertBlocks = jest.fn();
360
+ const insertOrReplaceBlocks = jest.fn();
363
361
  const targetRootClientId = '1';
364
362
  const targetBlockIndex = 0;
365
363
  const uploadPermissions = true;
@@ -370,7 +368,7 @@ describe( 'onFilesDrop', () => {
370
368
  uploadPermissions,
371
369
  updateBlockAttributes,
372
370
  canInsertBlockType,
373
- insertBlocks
371
+ insertOrReplaceBlocks
374
372
  );
375
373
  const files = 'test';
376
374
  onFileDropHandler( files );
@@ -380,11 +378,7 @@ describe( 'onFilesDrop', () => {
380
378
  files,
381
379
  updateBlockAttributes
382
380
  );
383
- expect( insertBlocks ).toHaveBeenCalledWith(
384
- blocks,
385
- targetBlockIndex,
386
- targetRootClientId
387
- );
381
+ expect( insertOrReplaceBlocks ).toHaveBeenCalledWith( blocks );
388
382
  } );
389
383
  } );
390
384
 
@@ -393,16 +387,16 @@ describe( 'onHTMLDrop', () => {
393
387
  pasteHandler.mockImplementation( () => [] );
394
388
  const targetRootClientId = '1';
395
389
  const targetBlockIndex = 0;
396
- const insertBlocks = jest.fn();
390
+ const insertOrReplaceBlocks = jest.fn();
397
391
 
398
392
  const eventHandler = onHTMLDrop(
399
393
  targetRootClientId,
400
394
  targetBlockIndex,
401
- insertBlocks
395
+ insertOrReplaceBlocks
402
396
  );
403
397
  eventHandler();
404
398
 
405
- expect( insertBlocks ).not.toHaveBeenCalled();
399
+ expect( insertOrReplaceBlocks ).not.toHaveBeenCalled();
406
400
  } );
407
401
 
408
402
  it( 'inserts blocks if the HTML can be converted into blocks', () => {
@@ -410,19 +404,15 @@ describe( 'onHTMLDrop', () => {
410
404
  pasteHandler.mockImplementation( () => blocks );
411
405
  const targetRootClientId = '1';
412
406
  const targetBlockIndex = 0;
413
- const insertBlocks = jest.fn();
407
+ const insertOrReplaceBlocks = jest.fn();
414
408
 
415
409
  const eventHandler = onHTMLDrop(
416
410
  targetRootClientId,
417
411
  targetBlockIndex,
418
- insertBlocks
412
+ insertOrReplaceBlocks
419
413
  );
420
414
  eventHandler();
421
415
 
422
- expect( insertBlocks ).toHaveBeenCalledWith(
423
- blocks,
424
- targetBlockIndex,
425
- targetRootClientId
426
- );
416
+ expect( insertOrReplaceBlocks ).toHaveBeenCalledWith( blocks );
427
417
  } );
428
418
  } );
@@ -167,7 +167,8 @@ export default function useArrowNav() {
167
167
  }
168
168
 
169
169
  function onKeyDown( event ) {
170
- const { keyCode, target } = event;
170
+ const { keyCode, target, shiftKey, ctrlKey, altKey, metaKey } =
171
+ event;
171
172
  const isUp = keyCode === UP;
172
173
  const isDown = keyCode === DOWN;
173
174
  const isLeft = keyCode === LEFT;
@@ -176,9 +177,7 @@ export default function useArrowNav() {
176
177
  const isHorizontal = isLeft || isRight;
177
178
  const isVertical = isUp || isDown;
178
179
  const isNav = isHorizontal || isVertical;
179
- const isShift = event.shiftKey;
180
- const hasModifier =
181
- isShift || event.ctrlKey || event.altKey || event.metaKey;
180
+ const hasModifier = shiftKey || ctrlKey || altKey || metaKey;
182
181
  const isNavEdge = isVertical ? isVerticalEdge : isHorizontalEdge;
183
182
  const { ownerDocument } = node;
184
183
  const { defaultView } = ownerDocument;
@@ -200,7 +199,7 @@ export default function useArrowNav() {
200
199
  return;
201
200
  }
202
201
 
203
- if ( isShift ) {
202
+ if ( shiftKey ) {
204
203
  return;
205
204
  }
206
205
 
@@ -249,7 +248,7 @@ export default function useArrowNav() {
249
248
  const isReverseDir = isRTL( target ) ? ! isReverse : isReverse;
250
249
  const { keepCaretInsideBlock } = getSettings();
251
250
 
252
- if ( isShift ) {
251
+ if ( shiftKey ) {
253
252
  if (
254
253
  isClosestTabbableABlock( target, isReverse ) &&
255
254
  isNavEdge( target, isReverse )
@@ -261,6 +260,9 @@ export default function useArrowNav() {
261
260
  } else if (
262
261
  isVertical &&
263
262
  isVerticalEdge( target, isReverse ) &&
263
+ // When Alt is pressed, only intercept if the caret is also at
264
+ // the horizontal edge.
265
+ ( altKey ? isHorizontalEdge( target, isReverseDir ) : true ) &&
264
266
  ! keepCaretInsideBlock
265
267
  ) {
266
268
  const closestTabbable = getClosestTabbable(
@@ -273,8 +275,10 @@ export default function useArrowNav() {
273
275
  if ( closestTabbable ) {
274
276
  placeCaretAtVerticalEdge(
275
277
  closestTabbable,
276
- isReverse,
277
- verticalRect
278
+ // When Alt is pressed, place the caret at the furthest
279
+ // horizontal edge and the furthest vertical edge.
280
+ altKey ? ! isReverse : isReverse,
281
+ altKey ? undefined : verticalRect
278
282
  );
279
283
  event.preventDefault();
280
284
  }
@@ -20,6 +20,7 @@ import './metadata';
20
20
  import './metadata-name';
21
21
 
22
22
  export { useCustomSides } from './dimensions';
23
+ export { useLayoutClasses, useLayoutStyles } from './layout';
23
24
  export { getBorderClassesAndStyles, useBorderProps } from './use-border-props';
24
25
  export { getColorClassesAndStyles, useColorProps } from './use-color-props';
25
26
  export { getSpacingClassesAndStyles } from './use-spacing-props';