@wordpress/block-editor 10.0.2 → 10.1.1-next.4d3b314fd5.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 (153) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/build/components/block-draggable/index.js +1 -1
  3. package/build/components/block-draggable/index.js.map +1 -1
  4. package/build/components/block-list/index.js +19 -5
  5. package/build/components/block-list/index.js.map +1 -1
  6. package/build/components/block-list/use-block-props/index.js +7 -1
  7. package/build/components/block-list/use-block-props/index.js.map +1 -1
  8. package/build/components/block-list/use-in-between-inserter.js +14 -5
  9. package/build/components/block-list/use-in-between-inserter.js.map +1 -1
  10. package/build/components/block-popover/inbetween.js +24 -9
  11. package/build/components/block-popover/inbetween.js.map +1 -1
  12. package/build/components/block-popover/index.js +29 -3
  13. package/build/components/block-popover/index.js.map +1 -1
  14. package/build/components/block-preview/auto.js +23 -11
  15. package/build/components/block-preview/auto.js.map +1 -1
  16. package/build/components/block-styles/index.js +1 -3
  17. package/build/components/block-styles/index.js.map +1 -1
  18. package/build/components/block-tools/index.js +18 -13
  19. package/build/components/block-tools/index.js.map +1 -1
  20. package/build/components/block-tools/insertion-point.js +5 -3
  21. package/build/components/block-tools/insertion-point.js.map +1 -1
  22. package/build/components/block-tools/use-block-toolbar-popover-props.js +6 -3
  23. package/build/components/block-tools/use-block-toolbar-popover-props.js.map +1 -1
  24. package/build/components/block-variation-picker/index.native.js +2 -1
  25. package/build/components/block-variation-picker/index.native.js.map +1 -1
  26. package/build/components/button-block-appender/index.native.js +1 -0
  27. package/build/components/button-block-appender/index.native.js.map +1 -1
  28. package/build/components/index.js +9 -0
  29. package/build/components/index.js.map +1 -1
  30. package/build/components/list-view/index.js +2 -1
  31. package/build/components/list-view/index.js.map +1 -1
  32. package/build/components/rich-text/format-toolbar-container.js +8 -1
  33. package/build/components/rich-text/format-toolbar-container.js.map +1 -1
  34. package/build/components/rich-text/index.js +12 -0
  35. package/build/components/rich-text/index.js.map +1 -1
  36. package/build/components/url-input/index.js +1 -3
  37. package/build/components/url-input/index.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/use-setting/index.js +16 -12
  45. package/build/components/use-setting/index.js.map +1 -1
  46. package/build/components/writing-flow/use-arrow-nav.js +21 -8
  47. package/build/components/writing-flow/use-arrow-nav.js.map +1 -1
  48. package/build/hooks/index.js +13 -1
  49. package/build/hooks/index.js.map +1 -1
  50. package/build/hooks/layout.js +76 -23
  51. package/build/hooks/layout.js.map +1 -1
  52. package/build/index.js +14 -0
  53. package/build/index.js.map +1 -1
  54. package/build/store/reducer.js +33 -19
  55. package/build/store/reducer.js.map +1 -1
  56. package/build/store/selectors.js +5 -5
  57. package/build/store/selectors.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-list/index.js +22 -8
  61. package/build-module/components/block-list/index.js.map +1 -1
  62. package/build-module/components/block-list/use-block-props/index.js +8 -2
  63. package/build-module/components/block-list/use-block-props/index.js.map +1 -1
  64. package/build-module/components/block-list/use-in-between-inserter.js +15 -6
  65. package/build-module/components/block-list/use-in-between-inserter.js.map +1 -1
  66. package/build-module/components/block-popover/inbetween.js +24 -9
  67. package/build-module/components/block-popover/inbetween.js.map +1 -1
  68. package/build-module/components/block-popover/index.js +29 -4
  69. package/build-module/components/block-popover/index.js.map +1 -1
  70. package/build-module/components/block-preview/auto.js +22 -10
  71. package/build-module/components/block-preview/auto.js.map +1 -1
  72. package/build-module/components/block-styles/index.js +1 -2
  73. package/build-module/components/block-styles/index.js.map +1 -1
  74. package/build-module/components/block-tools/index.js +18 -12
  75. package/build-module/components/block-tools/index.js.map +1 -1
  76. package/build-module/components/block-tools/insertion-point.js +5 -3
  77. package/build-module/components/block-tools/insertion-point.js.map +1 -1
  78. package/build-module/components/block-tools/use-block-toolbar-popover-props.js +6 -3
  79. package/build-module/components/block-tools/use-block-toolbar-popover-props.js.map +1 -1
  80. package/build-module/components/block-variation-picker/index.native.js +2 -1
  81. package/build-module/components/block-variation-picker/index.native.js.map +1 -1
  82. package/build-module/components/button-block-appender/index.native.js +1 -0
  83. package/build-module/components/button-block-appender/index.native.js.map +1 -1
  84. package/build-module/components/index.js +1 -0
  85. package/build-module/components/index.js.map +1 -1
  86. package/build-module/components/list-view/index.js +2 -1
  87. package/build-module/components/list-view/index.js.map +1 -1
  88. package/build-module/components/rich-text/format-toolbar-container.js +6 -1
  89. package/build-module/components/rich-text/format-toolbar-container.js.map +1 -1
  90. package/build-module/components/rich-text/index.js +12 -0
  91. package/build-module/components/rich-text/index.js.map +1 -1
  92. package/build-module/components/url-input/index.js +1 -2
  93. package/build-module/components/url-input/index.js.map +1 -1
  94. package/build-module/components/url-popover/image-url-input-ui.js +1 -1
  95. package/build-module/components/url-popover/image-url-input-ui.js.map +1 -1
  96. package/build-module/components/use-block-drop-zone/index.js +19 -1
  97. package/build-module/components/use-block-drop-zone/index.js.map +1 -1
  98. package/build-module/components/use-on-block-drop/index.js +62 -21
  99. package/build-module/components/use-on-block-drop/index.js.map +1 -1
  100. package/build-module/components/use-setting/index.js +16 -12
  101. package/build-module/components/use-setting/index.js.map +1 -1
  102. package/build-module/components/writing-flow/use-arrow-nav.js +22 -9
  103. package/build-module/components/writing-flow/use-arrow-nav.js.map +1 -1
  104. package/build-module/hooks/index.js +1 -0
  105. package/build-module/hooks/index.js.map +1 -1
  106. package/build-module/hooks/layout.js +73 -23
  107. package/build-module/hooks/layout.js.map +1 -1
  108. package/build-module/index.js +1 -1
  109. package/build-module/index.js.map +1 -1
  110. package/build-module/store/reducer.js +29 -18
  111. package/build-module/store/reducer.js.map +1 -1
  112. package/build-module/store/selectors.js +5 -5
  113. package/build-module/store/selectors.js.map +1 -1
  114. package/build-style/style-rtl.css +24 -6
  115. package/build-style/style.css +24 -6
  116. package/package.json +29 -29
  117. package/src/components/block-controls/test/index.js +84 -8
  118. package/src/components/block-draggable/index.js +1 -1
  119. package/src/components/block-draggable/test/index.native.js +0 -9
  120. package/src/components/block-list/index.js +42 -6
  121. package/src/components/block-list/use-block-props/index.js +6 -1
  122. package/src/components/block-list/use-in-between-inserter.js +14 -8
  123. package/src/components/block-mover/style.scss +2 -7
  124. package/src/components/block-popover/inbetween.js +34 -10
  125. package/src/components/block-popover/index.js +47 -3
  126. package/src/components/block-preview/auto.js +77 -65
  127. package/src/components/block-preview/style.scss +13 -0
  128. package/src/components/block-styles/index.js +1 -2
  129. package/src/components/block-tools/index.js +16 -10
  130. package/src/components/block-tools/insertion-point.js +3 -3
  131. package/src/components/block-tools/use-block-toolbar-popover-props.js +6 -0
  132. package/src/components/block-variation-picker/index.native.js +1 -0
  133. package/src/components/button-block-appender/index.native.js +1 -0
  134. package/src/components/index.js +1 -0
  135. package/src/components/link-control/test/index.js +1 -2
  136. package/src/components/list-view/index.js +1 -0
  137. package/src/components/rich-text/format-toolbar-container.js +8 -2
  138. package/src/components/rich-text/index.js +14 -0
  139. package/src/components/url-input/index.js +6 -2
  140. package/src/components/url-popover/image-url-input-ui.js +1 -1
  141. package/src/components/use-block-drop-zone/index.js +26 -1
  142. package/src/components/use-on-block-drop/index.js +110 -35
  143. package/src/components/use-on-block-drop/test/index.js +33 -43
  144. package/src/components/use-setting/index.js +18 -16
  145. package/src/components/warning/style.scss +1 -0
  146. package/src/components/writing-flow/use-arrow-nav.js +23 -9
  147. package/src/hooks/index.js +1 -0
  148. package/src/hooks/layout.js +64 -21
  149. package/src/index.js +2 -0
  150. package/src/store/reducer.js +22 -14
  151. package/src/store/selectors.js +5 -4
  152. package/src/store/test/reducer.js +0 -17
  153. package/src/components/block-controls/test/__snapshots__/index.js.snap +0 -64
@@ -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
  } );
@@ -21,10 +21,8 @@ import { store as blockEditorStore } from '../../store';
21
21
  const blockedPaths = [ 'color', 'border', 'typography', 'spacing' ];
22
22
 
23
23
  const deprecatedFlags = {
24
- 'color.palette': ( settings ) =>
25
- settings.colors === undefined ? undefined : settings.colors,
26
- 'color.gradients': ( settings ) =>
27
- settings.gradients === undefined ? undefined : settings.gradients,
24
+ 'color.palette': ( settings ) => settings.colors,
25
+ 'color.gradients': ( settings ) => settings.gradients,
28
26
  'color.custom': ( settings ) =>
29
27
  settings.disableCustomColors === undefined
30
28
  ? undefined
@@ -33,8 +31,7 @@ const deprecatedFlags = {
33
31
  settings.disableCustomGradients === undefined
34
32
  ? undefined
35
33
  : ! settings.disableCustomGradients,
36
- 'typography.fontSizes': ( settings ) =>
37
- settings.fontSizes === undefined ? undefined : settings.fontSizes,
34
+ 'typography.fontSizes': ( settings ) => settings.fontSizes,
38
35
  'typography.customFontSize': ( settings ) =>
39
36
  settings.disableCustomFontSizes === undefined
40
37
  ? undefined
@@ -109,7 +106,7 @@ const removeCustomPrefixes = ( path ) => {
109
106
  export default function useSetting( path ) {
110
107
  const { name: blockName, clientId } = useBlockEditContext();
111
108
 
112
- const setting = useSelect(
109
+ return useSelect(
113
110
  ( select ) => {
114
111
  if ( blockedPaths.includes( path ) ) {
115
112
  // eslint-disable-next-line no-console
@@ -120,14 +117,20 @@ export default function useSetting( path ) {
120
117
  }
121
118
 
122
119
  let result;
120
+
123
121
  const normalizedPath = removeCustomPrefixes( path );
124
122
 
125
123
  // 1. Take settings from the block instance or its ancestors.
124
+ // Start from the current block and work our way up the ancestors.
126
125
  const candidates = [
127
- ...select( blockEditorStore ).getBlockParents( clientId ),
128
- clientId, // The current block is added last, so it overwrites any ancestor.
126
+ clientId,
127
+ ...select( blockEditorStore ).getBlockParents(
128
+ clientId,
129
+ /* ascending */ true
130
+ ),
129
131
  ];
130
- candidates.forEach( ( candidateClientId ) => {
132
+
133
+ for ( const candidateClientId of candidates ) {
131
134
  const candidateBlockName =
132
135
  select( blockEditorStore ).getBlockName(
133
136
  candidateClientId
@@ -143,17 +146,18 @@ export default function useSetting( path ) {
143
146
  select( blockEditorStore ).getBlockAttributes(
144
147
  candidateClientId
145
148
  );
146
- const candidateResult =
149
+ result =
147
150
  get(
148
151
  candidateAtts,
149
152
  `settings.blocks.${ blockName }.${ normalizedPath }`
150
153
  ) ??
151
154
  get( candidateAtts, `settings.${ normalizedPath }` );
152
- if ( candidateResult !== undefined ) {
153
- result = candidateResult;
155
+ if ( result !== undefined ) {
156
+ // Stop the search for more distant ancestors and move on.
157
+ break;
154
158
  }
155
159
  }
156
- } );
160
+ }
157
161
 
158
162
  // 2. Fall back to the settings from the block editor store (__experimentalFeatures).
159
163
  const settings = select( blockEditorStore ).getSettings();
@@ -188,6 +192,4 @@ export default function useSetting( path ) {
188
192
  },
189
193
  [ blockName, clientId, path ]
190
194
  );
191
-
192
- return setting;
193
195
  }
@@ -33,6 +33,7 @@
33
33
  }
34
34
 
35
35
  .block-editor-warning__actions {
36
+ align-items: center;
36
37
  display: flex;
37
38
  margin-top: 1em;
38
39
  }
@@ -17,7 +17,7 @@ import { useRefEffect } from '@wordpress/compose';
17
17
  /**
18
18
  * Internal dependencies
19
19
  */
20
- import { getBlockClientId } from '../../utils/dom';
20
+ import { getBlockClientId, isInSameBlock } from '../../utils/dom';
21
21
  import { store as blockEditorStore } from '../../store';
22
22
 
23
23
  /**
@@ -101,6 +101,16 @@ export function getClosestTabbable(
101
101
  }
102
102
 
103
103
  function isTabCandidate( node ) {
104
+ // Skip if there's only one child that is content editable (and thus a
105
+ // better candidate).
106
+ if (
107
+ node.children.length === 1 &&
108
+ isInSameBlock( node, node.firstElementChild ) &&
109
+ node.firstElementChild.getAttribute( 'contenteditable' ) === 'true'
110
+ ) {
111
+ return;
112
+ }
113
+
104
114
  // Not a candidate if the node is not tabbable.
105
115
  if ( ! focus.tabbable.isTabbableIndex( node ) ) {
106
116
  return false;
@@ -157,7 +167,8 @@ export default function useArrowNav() {
157
167
  }
158
168
 
159
169
  function onKeyDown( event ) {
160
- const { keyCode, target } = event;
170
+ const { keyCode, target, shiftKey, ctrlKey, altKey, metaKey } =
171
+ event;
161
172
  const isUp = keyCode === UP;
162
173
  const isDown = keyCode === DOWN;
163
174
  const isLeft = keyCode === LEFT;
@@ -166,9 +177,7 @@ export default function useArrowNav() {
166
177
  const isHorizontal = isLeft || isRight;
167
178
  const isVertical = isUp || isDown;
168
179
  const isNav = isHorizontal || isVertical;
169
- const isShift = event.shiftKey;
170
- const hasModifier =
171
- isShift || event.ctrlKey || event.altKey || event.metaKey;
180
+ const hasModifier = shiftKey || ctrlKey || altKey || metaKey;
172
181
  const isNavEdge = isVertical ? isVerticalEdge : isHorizontalEdge;
173
182
  const { ownerDocument } = node;
174
183
  const { defaultView } = ownerDocument;
@@ -190,7 +199,7 @@ export default function useArrowNav() {
190
199
  return;
191
200
  }
192
201
 
193
- if ( isShift ) {
202
+ if ( shiftKey ) {
194
203
  return;
195
204
  }
196
205
 
@@ -239,7 +248,7 @@ export default function useArrowNav() {
239
248
  const isReverseDir = isRTL( target ) ? ! isReverse : isReverse;
240
249
  const { keepCaretInsideBlock } = getSettings();
241
250
 
242
- if ( isShift ) {
251
+ if ( shiftKey ) {
243
252
  if (
244
253
  isClosestTabbableABlock( target, isReverse ) &&
245
254
  isNavEdge( target, isReverse )
@@ -251,6 +260,9 @@ export default function useArrowNav() {
251
260
  } else if (
252
261
  isVertical &&
253
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 ) &&
254
266
  ! keepCaretInsideBlock
255
267
  ) {
256
268
  const closestTabbable = getClosestTabbable(
@@ -263,8 +275,10 @@ export default function useArrowNav() {
263
275
  if ( closestTabbable ) {
264
276
  placeCaretAtVerticalEdge(
265
277
  closestTabbable,
266
- isReverse,
267
- 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
268
282
  );
269
283
  event.preventDefault();
270
284
  }