@wordpress/edit-widgets 4.5.0 → 4.8.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 (45) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/build/blocks/widget-area/edit/inner-blocks.js.map +1 -1
  3. package/build/components/header/index.js.map +1 -1
  4. package/build/components/layout/interface.js.map +1 -1
  5. package/build/components/save-button/index.js.map +1 -1
  6. package/build/components/secondary-sidebar/index.js.map +1 -1
  7. package/build/components/sidebar/index.js.map +1 -1
  8. package/build/components/widget-areas-block-editor-provider/index.js +12 -14
  9. package/build/components/widget-areas-block-editor-provider/index.js.map +1 -1
  10. package/build/filters/move-to-widget-area.js.map +1 -1
  11. package/build/hooks/use-last-selected-widget-area.js.map +1 -1
  12. package/build/hooks/use-widget-library-insertion-point.js.map +1 -1
  13. package/build/store/actions.js.map +1 -1
  14. package/build/store/resolvers.js.map +1 -1
  15. package/build/store/selectors.js.map +1 -1
  16. package/build-module/blocks/widget-area/edit/inner-blocks.js.map +1 -1
  17. package/build-module/components/header/index.js.map +1 -1
  18. package/build-module/components/layout/interface.js.map +1 -1
  19. package/build-module/components/save-button/index.js.map +1 -1
  20. package/build-module/components/secondary-sidebar/index.js.map +1 -1
  21. package/build-module/components/sidebar/index.js.map +1 -1
  22. package/build-module/components/widget-areas-block-editor-provider/index.js +12 -13
  23. package/build-module/components/widget-areas-block-editor-provider/index.js.map +1 -1
  24. package/build-module/filters/move-to-widget-area.js.map +1 -1
  25. package/build-module/hooks/use-last-selected-widget-area.js.map +1 -1
  26. package/build-module/hooks/use-widget-library-insertion-point.js.map +1 -1
  27. package/build-module/store/actions.js.map +1 -1
  28. package/build-module/store/resolvers.js.map +1 -1
  29. package/build-module/store/selectors.js.map +1 -1
  30. package/build-style/style-rtl.css +4 -6
  31. package/build-style/style.css +4 -6
  32. package/package.json +26 -26
  33. package/src/blocks/widget-area/edit/inner-blocks.js +2 -1
  34. package/src/components/header/index.js +4 -8
  35. package/src/components/layout/interface.js +2 -5
  36. package/src/components/save-button/index.js +2 -3
  37. package/src/components/secondary-sidebar/index.js +2 -3
  38. package/src/components/sidebar/index.js +2 -5
  39. package/src/components/widget-areas-block-editor-provider/index.js +2 -9
  40. package/src/filters/move-to-widget-area.js +19 -24
  41. package/src/hooks/use-last-selected-widget-area.js +4 -6
  42. package/src/hooks/use-widget-library-insertion-point.js +2 -3
  43. package/src/store/actions.js +262 -258
  44. package/src/store/resolvers.js +69 -60
  45. package/src/store/selectors.js +22 -22
@@ -30,19 +30,21 @@ import { STORE_NAME as editWidgetsStoreName } from './constants';
30
30
  * @param {Array} blocks Blocks the post should consist of.
31
31
  * @return {Object} The post object.
32
32
  */
33
- export const persistStubPost = ( id, blocks ) => ( { registry } ) => {
34
- const stubPost = createStubPost( id, blocks );
35
- registry
36
- .dispatch( coreStore )
37
- .receiveEntityRecords(
38
- KIND,
39
- POST_TYPE,
40
- stubPost,
41
- { id: stubPost.id },
42
- false
43
- );
44
- return stubPost;
45
- };
33
+ export const persistStubPost =
34
+ ( id, blocks ) =>
35
+ ( { registry } ) => {
36
+ const stubPost = createStubPost( id, blocks );
37
+ registry
38
+ .dispatch( coreStore )
39
+ .receiveEntityRecords(
40
+ KIND,
41
+ POST_TYPE,
42
+ stubPost,
43
+ { id: stubPost.id },
44
+ false
45
+ );
46
+ return stubPost;
47
+ };
46
48
 
47
49
  /**
48
50
  * Converts all the blocks from edited widget areas into widgets,
@@ -52,32 +54,30 @@ export const persistStubPost = ( id, blocks ) => ( { registry } ) => {
52
54
  *
53
55
  * @return {Function} An action creator.
54
56
  */
55
- export const saveEditedWidgetAreas = () => async ( {
56
- select,
57
- dispatch,
58
- registry,
59
- } ) => {
60
- const editedWidgetAreas = select.getEditedWidgetAreas();
61
- if ( ! editedWidgetAreas?.length ) {
62
- return;
63
- }
64
- try {
65
- await dispatch.saveWidgetAreas( editedWidgetAreas );
66
- registry
67
- .dispatch( noticesStore )
68
- .createSuccessNotice( __( 'Widgets saved.' ), {
69
- type: 'snackbar',
70
- } );
71
- } catch ( e ) {
72
- registry.dispatch( noticesStore ).createErrorNotice(
73
- /* translators: %s: The error message. */
74
- sprintf( __( 'There was an error. %s' ), e.message ),
75
- {
76
- type: 'snackbar',
77
- }
78
- );
79
- }
80
- };
57
+ export const saveEditedWidgetAreas =
58
+ () =>
59
+ async ( { select, dispatch, registry } ) => {
60
+ const editedWidgetAreas = select.getEditedWidgetAreas();
61
+ if ( ! editedWidgetAreas?.length ) {
62
+ return;
63
+ }
64
+ try {
65
+ await dispatch.saveWidgetAreas( editedWidgetAreas );
66
+ registry
67
+ .dispatch( noticesStore )
68
+ .createSuccessNotice( __( 'Widgets saved.' ), {
69
+ type: 'snackbar',
70
+ } );
71
+ } catch ( e ) {
72
+ registry.dispatch( noticesStore ).createErrorNotice(
73
+ /* translators: %s: The error message. */
74
+ sprintf( __( 'There was an error. %s' ), e.message ),
75
+ {
76
+ type: 'snackbar',
77
+ }
78
+ );
79
+ }
80
+ };
81
81
 
82
82
  /**
83
83
  * Converts all the blocks from specified widget areas into widgets,
@@ -86,26 +86,25 @@ export const saveEditedWidgetAreas = () => async ( {
86
86
  * @param {Object[]} widgetAreas Widget areas to save.
87
87
  * @return {Function} An action creator.
88
88
  */
89
- export const saveWidgetAreas = ( widgetAreas ) => async ( {
90
- dispatch,
91
- registry,
92
- } ) => {
93
- try {
94
- for ( const widgetArea of widgetAreas ) {
95
- await dispatch.saveWidgetArea( widgetArea.id );
89
+ export const saveWidgetAreas =
90
+ ( widgetAreas ) =>
91
+ async ( { dispatch, registry } ) => {
92
+ try {
93
+ for ( const widgetArea of widgetAreas ) {
94
+ await dispatch.saveWidgetArea( widgetArea.id );
95
+ }
96
+ } finally {
97
+ // saveEditedEntityRecord resets the resolution status, let's fix it manually.
98
+ await registry
99
+ .dispatch( coreStore )
100
+ .finishResolution(
101
+ 'getEntityRecord',
102
+ KIND,
103
+ WIDGET_AREA_ENTITY_TYPE,
104
+ buildWidgetAreasQuery()
105
+ );
96
106
  }
97
- } finally {
98
- // saveEditedEntityRecord resets the resolution status, let's fix it manually.
99
- await registry
100
- .dispatch( coreStore )
101
- .finishResolution(
102
- 'getEntityRecord',
103
- KIND,
104
- WIDGET_AREA_ENTITY_TYPE,
105
- buildWidgetAreasQuery()
106
- );
107
- }
108
- };
107
+ };
109
108
 
110
109
  /**
111
110
  * Converts all the blocks from a widget area specified by ID into widgets,
@@ -114,180 +113,185 @@ export const saveWidgetAreas = ( widgetAreas ) => async ( {
114
113
  * @param {string} widgetAreaId ID of the widget area to process.
115
114
  * @return {Function} An action creator.
116
115
  */
117
- export const saveWidgetArea = ( widgetAreaId ) => async ( {
118
- dispatch,
119
- select,
120
- registry,
121
- } ) => {
122
- const widgets = select.getWidgets();
123
-
124
- const post = registry
125
- .select( coreStore )
126
- .getEditedEntityRecord(
127
- KIND,
128
- POST_TYPE,
129
- buildWidgetAreaPostId( widgetAreaId )
116
+ export const saveWidgetArea =
117
+ ( widgetAreaId ) =>
118
+ async ( { dispatch, select, registry } ) => {
119
+ const widgets = select.getWidgets();
120
+
121
+ const post = registry
122
+ .select( coreStore )
123
+ .getEditedEntityRecord(
124
+ KIND,
125
+ POST_TYPE,
126
+ buildWidgetAreaPostId( widgetAreaId )
127
+ );
128
+
129
+ // Get all widgets from this area
130
+ const areaWidgets = Object.values( widgets ).filter(
131
+ ( { sidebar } ) => sidebar === widgetAreaId
130
132
  );
131
133
 
132
- // Get all widgets from this area
133
- const areaWidgets = Object.values( widgets ).filter(
134
- ( { sidebar } ) => sidebar === widgetAreaId
135
- );
136
-
137
- // Remove all duplicate reference widget instances for legacy widgets.
138
- // Why? We filter out the widgets with duplicate IDs to prevent adding more than one instance of a widget
139
- // implemented using a function. WordPress doesn't support having more than one instance of these, if you try to
140
- // save multiple instances of these in different sidebars you will run into undefined behaviors.
141
- const usedReferenceWidgets = [];
142
- const widgetsBlocks = post.blocks.filter( ( block ) => {
143
- const { id } = block.attributes;
144
-
145
- if ( block.name === 'core/legacy-widget' && id ) {
146
- if ( usedReferenceWidgets.includes( id ) ) {
147
- return false;
134
+ // Remove all duplicate reference widget instances for legacy widgets.
135
+ // Why? We filter out the widgets with duplicate IDs to prevent adding more than one instance of a widget
136
+ // implemented using a function. WordPress doesn't support having more than one instance of these, if you try to
137
+ // save multiple instances of these in different sidebars you will run into undefined behaviors.
138
+ const usedReferenceWidgets = [];
139
+ const widgetsBlocks = post.blocks.filter( ( block ) => {
140
+ const { id } = block.attributes;
141
+
142
+ if ( block.name === 'core/legacy-widget' && id ) {
143
+ if ( usedReferenceWidgets.includes( id ) ) {
144
+ return false;
145
+ }
146
+ usedReferenceWidgets.push( id );
148
147
  }
149
- usedReferenceWidgets.push( id );
150
- }
151
- return true;
152
- } );
153
-
154
- // Determine which widgets have been deleted. We can tell if a widget is
155
- // deleted and not just moved to a different area by looking to see if
156
- // getWidgetAreaForWidgetId() finds something.
157
- const deletedWidgets = [];
158
- for ( const widget of areaWidgets ) {
159
- const widgetsNewArea = select.getWidgetAreaForWidgetId( widget.id );
160
- if ( ! widgetsNewArea ) {
161
- deletedWidgets.push( widget );
162
- }
163
- }
164
-
165
- const batchMeta = [];
166
- const batchTasks = [];
167
- const sidebarWidgetsIds = [];
168
- for ( let i = 0; i < widgetsBlocks.length; i++ ) {
169
- const block = widgetsBlocks[ i ];
170
- const widgetId = getWidgetIdFromBlock( block );
171
- const oldWidget = widgets[ widgetId ];
172
- const widget = transformBlockToWidget( block, oldWidget );
173
-
174
- // We'll replace the null widgetId after save, but we track it here
175
- // since order is important.
176
- sidebarWidgetsIds.push( widgetId );
177
-
178
- // Check oldWidget as widgetId might refer to an ID which has been
179
- // deleted, e.g. if a deleted block is restored via undo after saving.
180
- if ( oldWidget ) {
181
- // Update an existing widget.
182
- registry.dispatch( coreStore ).editEntityRecord(
183
- 'root',
184
- 'widget',
185
- widgetId,
186
- {
187
- ...widget,
188
- sidebar: widgetAreaId,
189
- },
190
- { undoIgnore: true }
191
- );
148
+ return true;
149
+ } );
192
150
 
193
- const hasEdits = registry
194
- .select( coreStore )
195
- .hasEditsForEntityRecord( 'root', 'widget', widgetId );
151
+ // Determine which widgets have been deleted. We can tell if a widget is
152
+ // deleted and not just moved to a different area by looking to see if
153
+ // getWidgetAreaForWidgetId() finds something.
154
+ const deletedWidgets = [];
155
+ for ( const widget of areaWidgets ) {
156
+ const widgetsNewArea = select.getWidgetAreaForWidgetId( widget.id );
157
+ if ( ! widgetsNewArea ) {
158
+ deletedWidgets.push( widget );
159
+ }
160
+ }
196
161
 
197
- if ( ! hasEdits ) {
198
- continue;
162
+ const batchMeta = [];
163
+ const batchTasks = [];
164
+ const sidebarWidgetsIds = [];
165
+ for ( let i = 0; i < widgetsBlocks.length; i++ ) {
166
+ const block = widgetsBlocks[ i ];
167
+ const widgetId = getWidgetIdFromBlock( block );
168
+ const oldWidget = widgets[ widgetId ];
169
+ const widget = transformBlockToWidget( block, oldWidget );
170
+
171
+ // We'll replace the null widgetId after save, but we track it here
172
+ // since order is important.
173
+ sidebarWidgetsIds.push( widgetId );
174
+
175
+ // Check oldWidget as widgetId might refer to an ID which has been
176
+ // deleted, e.g. if a deleted block is restored via undo after saving.
177
+ if ( oldWidget ) {
178
+ // Update an existing widget.
179
+ registry.dispatch( coreStore ).editEntityRecord(
180
+ 'root',
181
+ 'widget',
182
+ widgetId,
183
+ {
184
+ ...widget,
185
+ sidebar: widgetAreaId,
186
+ },
187
+ { undoIgnore: true }
188
+ );
189
+
190
+ const hasEdits = registry
191
+ .select( coreStore )
192
+ .hasEditsForEntityRecord( 'root', 'widget', widgetId );
193
+
194
+ if ( ! hasEdits ) {
195
+ continue;
196
+ }
197
+
198
+ batchTasks.push( ( { saveEditedEntityRecord } ) =>
199
+ saveEditedEntityRecord( 'root', 'widget', widgetId )
200
+ );
201
+ } else {
202
+ // Create a new widget.
203
+ batchTasks.push( ( { saveEntityRecord } ) =>
204
+ saveEntityRecord( 'root', 'widget', {
205
+ ...widget,
206
+ sidebar: widgetAreaId,
207
+ } )
208
+ );
199
209
  }
200
210
 
201
- batchTasks.push( ( { saveEditedEntityRecord } ) =>
202
- saveEditedEntityRecord( 'root', 'widget', widgetId )
203
- );
204
- } else {
205
- // Create a new widget.
206
- batchTasks.push( ( { saveEntityRecord } ) =>
207
- saveEntityRecord( 'root', 'widget', {
208
- ...widget,
209
- sidebar: widgetAreaId,
211
+ batchMeta.push( {
212
+ block,
213
+ position: i,
214
+ clientId: block.clientId,
215
+ } );
216
+ }
217
+ for ( const widget of deletedWidgets ) {
218
+ batchTasks.push( ( { deleteEntityRecord } ) =>
219
+ deleteEntityRecord( 'root', 'widget', widget.id, {
220
+ force: true,
210
221
  } )
211
222
  );
212
223
  }
213
224
 
214
- batchMeta.push( {
215
- block,
216
- position: i,
217
- clientId: block.clientId,
218
- } );
219
- }
220
- for ( const widget of deletedWidgets ) {
221
- batchTasks.push( ( { deleteEntityRecord } ) =>
222
- deleteEntityRecord( 'root', 'widget', widget.id, {
223
- force: true,
224
- } )
225
+ const records = await registry
226
+ .dispatch( coreStore )
227
+ .__experimentalBatch( batchTasks );
228
+ const preservedRecords = records.filter(
229
+ ( record ) => ! record.hasOwnProperty( 'deleted' )
225
230
  );
226
- }
227
231
 
228
- const records = await registry
229
- .dispatch( coreStore )
230
- .__experimentalBatch( batchTasks );
231
- const preservedRecords = records.filter(
232
- ( record ) => ! record.hasOwnProperty( 'deleted' )
233
- );
232
+ const failedWidgetNames = [];
234
233
 
235
- const failedWidgetNames = [];
234
+ for ( let i = 0; i < preservedRecords.length; i++ ) {
235
+ const widget = preservedRecords[ i ];
236
+ const { block, position } = batchMeta[ i ];
236
237
 
237
- for ( let i = 0; i < preservedRecords.length; i++ ) {
238
- const widget = preservedRecords[ i ];
239
- const { block, position } = batchMeta[ i ];
238
+ // Set __internalWidgetId on the block. This will be persisted to the
239
+ // store when we dispatch receiveEntityRecords( post ) below.
240
+ post.blocks[ position ].attributes.__internalWidgetId = widget.id;
240
241
 
241
- // Set __internalWidgetId on the block. This will be persisted to the
242
- // store when we dispatch receiveEntityRecords( post ) below.
243
- post.blocks[ position ].attributes.__internalWidgetId = widget.id;
242
+ const error = registry
243
+ .select( coreStore )
244
+ .getLastEntitySaveError( 'root', 'widget', widget.id );
245
+ if ( error ) {
246
+ failedWidgetNames.push( block.attributes?.name || block?.name );
247
+ }
244
248
 
245
- const error = registry
246
- .select( coreStore )
247
- .getLastEntitySaveError( 'root', 'widget', widget.id );
248
- if ( error ) {
249
- failedWidgetNames.push( block.attributes?.name || block?.name );
249
+ if ( ! sidebarWidgetsIds[ position ] ) {
250
+ sidebarWidgetsIds[ position ] = widget.id;
251
+ }
250
252
  }
251
253
 
252
- if ( ! sidebarWidgetsIds[ position ] ) {
253
- sidebarWidgetsIds[ position ] = widget.id;
254
+ if ( failedWidgetNames.length ) {
255
+ throw new Error(
256
+ sprintf(
257
+ /* translators: %s: List of widget names */
258
+ __( 'Could not save the following widgets: %s.' ),
259
+ failedWidgetNames.join( ', ' )
260
+ )
261
+ );
254
262
  }
255
- }
256
-
257
- if ( failedWidgetNames.length ) {
258
- throw new Error(
259
- sprintf(
260
- /* translators: %s: List of widget names */
261
- __( 'Could not save the following widgets: %s.' ),
262
- failedWidgetNames.join( ', ' )
263
- )
263
+
264
+ registry.dispatch( coreStore ).editEntityRecord(
265
+ KIND,
266
+ WIDGET_AREA_ENTITY_TYPE,
267
+ widgetAreaId,
268
+ {
269
+ widgets: sidebarWidgetsIds,
270
+ },
271
+ { undoIgnore: true }
264
272
  );
265
- }
266
-
267
- registry.dispatch( coreStore ).editEntityRecord(
268
- KIND,
269
- WIDGET_AREA_ENTITY_TYPE,
270
- widgetAreaId,
271
- {
272
- widgets: sidebarWidgetsIds,
273
- },
274
- { undoIgnore: true }
275
- );
276
-
277
- dispatch( trySaveWidgetArea( widgetAreaId ) );
278
-
279
- registry
280
- .dispatch( coreStore )
281
- .receiveEntityRecords( KIND, POST_TYPE, post, undefined );
282
- };
283
-
284
- const trySaveWidgetArea = ( widgetAreaId ) => ( { registry } ) => {
285
- registry
286
- .dispatch( coreStore )
287
- .saveEditedEntityRecord( KIND, WIDGET_AREA_ENTITY_TYPE, widgetAreaId, {
288
- throwOnError: true,
289
- } );
290
- };
273
+
274
+ dispatch( trySaveWidgetArea( widgetAreaId ) );
275
+
276
+ registry
277
+ .dispatch( coreStore )
278
+ .receiveEntityRecords( KIND, POST_TYPE, post, undefined );
279
+ };
280
+
281
+ const trySaveWidgetArea =
282
+ ( widgetAreaId ) =>
283
+ ( { registry } ) => {
284
+ registry
285
+ .dispatch( coreStore )
286
+ .saveEditedEntityRecord(
287
+ KIND,
288
+ WIDGET_AREA_ENTITY_TYPE,
289
+ widgetAreaId,
290
+ {
291
+ throwOnError: true,
292
+ }
293
+ );
294
+ };
291
295
 
292
296
  /**
293
297
  * Sets the clientId stored for a particular widgetId.
@@ -372,11 +376,13 @@ export function setIsListViewOpened( isOpen ) {
372
376
  *
373
377
  * @return {Object} Action creator.
374
378
  */
375
- export const closeGeneralSidebar = () => ( { registry } ) => {
376
- registry
377
- .dispatch( interfaceStore )
378
- .disableComplementaryArea( editWidgetsStoreName );
379
- };
379
+ export const closeGeneralSidebar =
380
+ () =>
381
+ ( { registry } ) => {
382
+ registry
383
+ .dispatch( interfaceStore )
384
+ .disableComplementaryArea( editWidgetsStoreName );
385
+ };
380
386
 
381
387
  /**
382
388
  * Action that handles moving a block between widget areas
@@ -384,46 +390,44 @@ export const closeGeneralSidebar = () => ( { registry } ) => {
384
390
  * @param {string} clientId The clientId of the block to move.
385
391
  * @param {string} widgetAreaId The id of the widget area to move the block to.
386
392
  */
387
- export const moveBlockToWidgetArea = ( clientId, widgetAreaId ) => async ( {
388
- dispatch,
389
- select,
390
- registry,
391
- } ) => {
392
- const sourceRootClientId = registry
393
- .select( blockEditorStore )
394
- .getBlockRootClientId( [ clientId ] );
395
-
396
- // Search the top level blocks (widget areas) for the one with the matching
397
- // id attribute. Makes the assumption that all top-level blocks are widget
398
- // areas.
399
- const widgetAreas = registry.select( blockEditorStore ).getBlocks();
400
- const destinationWidgetAreaBlock = widgetAreas.find(
401
- ( { attributes } ) => attributes.id === widgetAreaId
402
- );
403
- const destinationRootClientId = destinationWidgetAreaBlock.clientId;
404
-
405
- // Get the index for moving to the end of the the destination widget area.
406
- const destinationInnerBlocksClientIds = registry
407
- .select( blockEditorStore )
408
- .getBlockOrder( destinationRootClientId );
409
- const destinationIndex = destinationInnerBlocksClientIds.length;
410
-
411
- // Reveal the widget area, if it's not open.
412
- const isDestinationWidgetAreaOpen = select.getIsWidgetAreaOpen(
413
- destinationRootClientId
414
- );
415
-
416
- if ( ! isDestinationWidgetAreaOpen ) {
417
- dispatch.setIsWidgetAreaOpen( destinationRootClientId, true );
418
- }
419
-
420
- // Move the block.
421
- registry
422
- .dispatch( blockEditorStore )
423
- .moveBlocksToPosition(
424
- [ clientId ],
425
- sourceRootClientId,
426
- destinationRootClientId,
427
- destinationIndex
393
+ export const moveBlockToWidgetArea =
394
+ ( clientId, widgetAreaId ) =>
395
+ async ( { dispatch, select, registry } ) => {
396
+ const sourceRootClientId = registry
397
+ .select( blockEditorStore )
398
+ .getBlockRootClientId( [ clientId ] );
399
+
400
+ // Search the top level blocks (widget areas) for the one with the matching
401
+ // id attribute. Makes the assumption that all top-level blocks are widget
402
+ // areas.
403
+ const widgetAreas = registry.select( blockEditorStore ).getBlocks();
404
+ const destinationWidgetAreaBlock = widgetAreas.find(
405
+ ( { attributes } ) => attributes.id === widgetAreaId
428
406
  );
429
- };
407
+ const destinationRootClientId = destinationWidgetAreaBlock.clientId;
408
+
409
+ // Get the index for moving to the end of the the destination widget area.
410
+ const destinationInnerBlocksClientIds = registry
411
+ .select( blockEditorStore )
412
+ .getBlockOrder( destinationRootClientId );
413
+ const destinationIndex = destinationInnerBlocksClientIds.length;
414
+
415
+ // Reveal the widget area, if it's not open.
416
+ const isDestinationWidgetAreaOpen = select.getIsWidgetAreaOpen(
417
+ destinationRootClientId
418
+ );
419
+
420
+ if ( ! isDestinationWidgetAreaOpen ) {
421
+ dispatch.setIsWidgetAreaOpen( destinationRootClientId, true );
422
+ }
423
+
424
+ // Move the block.
425
+ registry
426
+ .dispatch( blockEditorStore )
427
+ .moveBlocksToPosition(
428
+ [ clientId ],
429
+ sourceRootClientId,
430
+ destinationRootClientId,
431
+ destinationIndex
432
+ );
433
+ };