@wordpress/block-editor 15.9.1-next.8b30e05b0.0 → 15.10.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 (196) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/README.md +8 -0
  3. package/build/components/block-alignment-matrix-control/index.js +1 -8
  4. package/build/components/block-alignment-matrix-control/index.js.map +2 -2
  5. package/build/components/block-bindings/attribute-control.js +172 -0
  6. package/build/components/block-bindings/attribute-control.js.map +7 -0
  7. package/build/components/block-bindings/index.js +47 -0
  8. package/build/components/block-bindings/index.js.map +7 -0
  9. package/build/components/block-bindings/source-fields-list.js +135 -0
  10. package/build/components/block-bindings/source-fields-list.js.map +7 -0
  11. package/build/components/block-bindings/use-block-bindings-utils.js +66 -0
  12. package/build/components/block-bindings/use-block-bindings-utils.js.map +7 -0
  13. package/build/components/block-edit/edit.js +1 -3
  14. package/build/components/block-edit/edit.js.map +2 -2
  15. package/build/components/block-inspector/edit-contents.js +93 -14
  16. package/build/components/block-inspector/edit-contents.js.map +3 -3
  17. package/build/components/block-inspector/index.js +44 -28
  18. package/build/components/block-inspector/index.js.map +2 -2
  19. package/build/components/block-settings-menu-controls/edit-section-menu-item.js +39 -9
  20. package/build/components/block-settings-menu-controls/edit-section-menu-item.js.map +3 -3
  21. package/build/components/block-styles/preview-panel.js +3 -5
  22. package/build/components/block-styles/preview-panel.js.map +2 -2
  23. package/build/components/block-styles/use-styles-for-block.js +2 -2
  24. package/build/components/block-styles/use-styles-for-block.js.map +2 -2
  25. package/build/components/block-toolbar/index.js +1 -8
  26. package/build/components/block-toolbar/index.js.map +3 -3
  27. package/build/components/content-only-controls/index.js +2 -25
  28. package/build/components/content-only-controls/index.js.map +2 -2
  29. package/build/components/content-only-controls/link/index.js +3 -3
  30. package/build/components/content-only-controls/link/index.js.map +2 -2
  31. package/build/components/content-only-controls/media/index.js +3 -3
  32. package/build/components/content-only-controls/media/index.js.map +2 -2
  33. package/build/components/content-only-controls/rich-text/index.js +3 -2
  34. package/build/components/content-only-controls/rich-text/index.js.map +2 -2
  35. package/build/components/dimensions-tool/width-height-tool.js +4 -16
  36. package/build/components/dimensions-tool/width-height-tool.js.map +3 -3
  37. package/build/components/grid/grid-item-resizer.js +9 -5
  38. package/build/components/grid/grid-item-resizer.js.map +2 -2
  39. package/build/components/image-editor/cropper.js +3 -34
  40. package/build/components/image-editor/cropper.js.map +3 -3
  41. package/build/components/image-editor/index.js +9 -3
  42. package/build/components/image-editor/index.js.map +2 -2
  43. package/build/components/image-editor/use-transform-image.js +62 -32
  44. package/build/components/image-editor/use-transform-image.js.map +2 -2
  45. package/build/components/image-editor/zoom-dropdown.js +2 -2
  46. package/build/components/image-editor/zoom-dropdown.js.map +2 -2
  47. package/build/components/index.js +7 -3
  48. package/build/components/index.js.map +2 -2
  49. package/build/components/inserter/hooks/use-insertion-point.js +5 -2
  50. package/build/components/inserter/hooks/use-insertion-point.js.map +2 -2
  51. package/build/components/inserter-draggable-blocks/index.js +8 -4
  52. package/build/components/inserter-draggable-blocks/index.js.map +2 -2
  53. package/build/components/inspector-controls-tabs/content-tab.js +3 -2
  54. package/build/components/inspector-controls-tabs/content-tab.js.map +2 -2
  55. package/build/components/link-control/index.js +1 -1
  56. package/build/components/link-control/index.js.map +2 -2
  57. package/build/components/link-control/search-input.js +2 -2
  58. package/build/components/link-control/search-input.js.map +2 -2
  59. package/build/hooks/block-bindings.js +22 -260
  60. package/build/hooks/block-bindings.js.map +3 -3
  61. package/build/layouts/grid.js +23 -28
  62. package/build/layouts/grid.js.map +2 -2
  63. package/build/private-apis.js +1 -0
  64. package/build/private-apis.js.map +2 -2
  65. package/build/store/private-keys.js +3 -0
  66. package/build/store/private-keys.js.map +2 -2
  67. package/build/store/private-selectors.js +2 -1
  68. package/build/store/private-selectors.js.map +2 -2
  69. package/build/store/reducer.js +3 -2
  70. package/build/store/reducer.js.map +2 -2
  71. package/build/utils/block-bindings.js +2 -44
  72. package/build/utils/block-bindings.js.map +3 -3
  73. package/build/utils/index.js +2 -5
  74. package/build/utils/index.js.map +2 -2
  75. package/build-module/components/block-alignment-matrix-control/index.js +1 -8
  76. package/build-module/components/block-alignment-matrix-control/index.js.map +2 -2
  77. package/build-module/components/block-bindings/attribute-control.js +150 -0
  78. package/build-module/components/block-bindings/attribute-control.js.map +7 -0
  79. package/build-module/components/block-bindings/index.js +10 -0
  80. package/build-module/components/block-bindings/index.js.map +7 -0
  81. package/build-module/components/block-bindings/source-fields-list.js +104 -0
  82. package/build-module/components/block-bindings/source-fields-list.js.map +7 -0
  83. package/build-module/components/block-bindings/use-block-bindings-utils.js +45 -0
  84. package/build-module/components/block-bindings/use-block-bindings-utils.js.map +7 -0
  85. package/build-module/components/block-edit/edit.js +1 -3
  86. package/build-module/components/block-edit/edit.js.map +2 -2
  87. package/build-module/components/block-inspector/edit-contents.js +93 -14
  88. package/build-module/components/block-inspector/edit-contents.js.map +2 -2
  89. package/build-module/components/block-inspector/index.js +44 -28
  90. package/build-module/components/block-inspector/index.js.map +2 -2
  91. package/build-module/components/block-settings-menu-controls/edit-section-menu-item.js +39 -9
  92. package/build-module/components/block-settings-menu-controls/edit-section-menu-item.js.map +2 -2
  93. package/build-module/components/block-styles/preview-panel.js +3 -5
  94. package/build-module/components/block-styles/preview-panel.js.map +2 -2
  95. package/build-module/components/block-styles/use-styles-for-block.js +2 -2
  96. package/build-module/components/block-styles/use-styles-for-block.js.map +2 -2
  97. package/build-module/components/block-toolbar/index.js +1 -8
  98. package/build-module/components/block-toolbar/index.js.map +2 -2
  99. package/build-module/components/content-only-controls/index.js +2 -25
  100. package/build-module/components/content-only-controls/index.js.map +2 -2
  101. package/build-module/components/content-only-controls/link/index.js +3 -3
  102. package/build-module/components/content-only-controls/link/index.js.map +2 -2
  103. package/build-module/components/content-only-controls/media/index.js +3 -3
  104. package/build-module/components/content-only-controls/media/index.js.map +2 -2
  105. package/build-module/components/content-only-controls/rich-text/index.js +3 -2
  106. package/build-module/components/content-only-controls/rich-text/index.js.map +2 -2
  107. package/build-module/components/dimensions-tool/width-height-tool.js +4 -6
  108. package/build-module/components/dimensions-tool/width-height-tool.js.map +2 -2
  109. package/build-module/components/grid/grid-item-resizer.js +9 -5
  110. package/build-module/components/grid/grid-item-resizer.js.map +2 -2
  111. package/build-module/components/image-editor/cropper.js +3 -34
  112. package/build-module/components/image-editor/cropper.js.map +2 -2
  113. package/build-module/components/image-editor/index.js +9 -3
  114. package/build-module/components/image-editor/index.js.map +2 -2
  115. package/build-module/components/image-editor/use-transform-image.js +63 -33
  116. package/build-module/components/image-editor/use-transform-image.js.map +2 -2
  117. package/build-module/components/image-editor/zoom-dropdown.js +2 -2
  118. package/build-module/components/image-editor/zoom-dropdown.js.map +2 -2
  119. package/build-module/components/index.js +74 -68
  120. package/build-module/components/index.js.map +2 -2
  121. package/build-module/components/inserter/hooks/use-insertion-point.js +5 -2
  122. package/build-module/components/inserter/hooks/use-insertion-point.js.map +2 -2
  123. package/build-module/components/inserter-draggable-blocks/index.js +8 -4
  124. package/build-module/components/inserter-draggable-blocks/index.js.map +2 -2
  125. package/build-module/components/inspector-controls-tabs/content-tab.js +3 -2
  126. package/build-module/components/inspector-controls-tabs/content-tab.js.map +2 -2
  127. package/build-module/components/link-control/index.js +1 -1
  128. package/build-module/components/link-control/index.js.map +2 -2
  129. package/build-module/components/link-control/search-input.js +2 -2
  130. package/build-module/components/link-control/search-input.js.map +2 -2
  131. package/build-module/hooks/block-bindings.js +27 -270
  132. package/build-module/hooks/block-bindings.js.map +2 -2
  133. package/build-module/layouts/grid.js +23 -28
  134. package/build-module/layouts/grid.js.map +2 -2
  135. package/build-module/private-apis.js +3 -1
  136. package/build-module/private-apis.js.map +2 -2
  137. package/build-module/store/private-keys.js +2 -0
  138. package/build-module/store/private-keys.js.map +2 -2
  139. package/build-module/store/private-selectors.js +4 -2
  140. package/build-module/store/private-selectors.js.map +2 -2
  141. package/build-module/store/reducer.js +4 -3
  142. package/build-module/store/reducer.js.map +2 -2
  143. package/build-module/utils/block-bindings.js +1 -42
  144. package/build-module/utils/block-bindings.js.map +2 -2
  145. package/build-module/utils/index.js +1 -3
  146. package/build-module/utils/index.js.map +2 -2
  147. package/build-style/style-rtl.css +6 -6
  148. package/build-style/style.css +6 -6
  149. package/package.json +39 -40
  150. package/src/components/block-alignment-matrix-control/index.js +1 -5
  151. package/src/components/block-bindings/attribute-control.js +174 -0
  152. package/src/components/block-bindings/index.js +6 -0
  153. package/src/components/block-bindings/source-fields-list.js +130 -0
  154. package/src/components/block-bindings/use-block-bindings-utils.js +156 -0
  155. package/src/components/block-edit/edit.js +1 -3
  156. package/src/components/block-inspector/edit-contents.js +108 -18
  157. package/src/components/block-inspector/index.js +53 -30
  158. package/src/components/block-settings-menu-controls/edit-section-menu-item.js +50 -6
  159. package/src/components/block-styles/preview-panel.js +3 -5
  160. package/src/components/block-styles/use-styles-for-block.js +2 -2
  161. package/src/components/block-toolbar/index.js +1 -6
  162. package/src/components/block-toolbar/style.scss +6 -6
  163. package/src/components/content-only-controls/index.js +2 -27
  164. package/src/components/content-only-controls/link/index.js +3 -3
  165. package/src/components/content-only-controls/media/index.js +3 -3
  166. package/src/components/content-only-controls/rich-text/index.js +3 -2
  167. package/src/components/dimensions-tool/width-height-tool.js +6 -13
  168. package/src/components/grid/grid-item-resizer.js +18 -5
  169. package/src/components/image-editor/cropper.js +3 -32
  170. package/src/components/image-editor/index.js +34 -29
  171. package/src/components/image-editor/use-transform-image.js +80 -34
  172. package/src/components/image-editor/zoom-dropdown.js +2 -2
  173. package/src/components/index.js +5 -1
  174. package/src/components/inserter/hooks/use-insertion-point.js +3 -0
  175. package/src/components/inserter/style.scss +1 -1
  176. package/src/components/inserter-draggable-blocks/index.js +19 -8
  177. package/src/components/inspector-controls-tabs/content-tab.js +6 -2
  178. package/src/components/link-control/index.js +1 -1
  179. package/src/components/link-control/search-input.js +8 -2
  180. package/src/components/link-control/test/index.js +146 -7
  181. package/src/hooks/block-bindings.js +27 -347
  182. package/src/layouts/grid.js +40 -72
  183. package/src/layouts/test/grid.js +14 -0
  184. package/src/private-apis.js +2 -0
  185. package/src/store/private-keys.js +1 -0
  186. package/src/store/private-selectors.js +8 -1
  187. package/src/store/reducer.js +10 -3
  188. package/src/utils/block-bindings.js +0 -157
  189. package/src/utils/index.js +0 -1
  190. package/tsconfig.json +1 -0
  191. package/build/components/block-toolbar/block-name-context.js +0 -30
  192. package/build/components/block-toolbar/block-name-context.js.map +0 -7
  193. package/build-module/components/block-toolbar/block-name-context.js +0 -9
  194. package/build-module/components/block-toolbar/block-name-context.js.map +0 -7
  195. package/src/components/block-toolbar/block-name-context.js +0 -9
  196. /package/src/{utils → components/block-bindings}/test/use-block-bindings-utils.js +0 -0
@@ -32,7 +32,6 @@ import { BlockGroupToolbar } from '../convert-to-group-buttons';
32
32
  import BlockEditVisuallyButton from '../block-edit-visually-button';
33
33
  import { useShowHoveredOrFocusedGestures } from './utils';
34
34
  import { store as blockEditorStore } from '../../store';
35
- import __unstableBlockNameContext from './block-name-context';
36
35
  import NavigableToolbar from '../navigable-toolbar';
37
36
  import { useHasBlockToolbar } from './use-has-block-toolbar';
38
37
  import ChangeDesign from './change-design';
@@ -265,11 +264,7 @@ export function PrivateBlockToolbar( {
265
264
  group="other"
266
265
  className="block-editor-block-toolbar__slot"
267
266
  />
268
- <__unstableBlockNameContext.Provider
269
- value={ blockType?.name }
270
- >
271
- <__unstableBlockToolbarLastItem.Slot />
272
- </__unstableBlockNameContext.Provider>
267
+ <__unstableBlockToolbarLastItem.Slot />
273
268
  </>
274
269
  ) }
275
270
  <BlockEditVisuallyButton clientIds={ blockClientIds } />
@@ -193,6 +193,12 @@
193
193
  font-size: $helptext-font-size;
194
194
  }
195
195
  }
196
+
197
+ .block-editor-block-icon {
198
+ width: 0 !important;
199
+ height: 0 !important;
200
+ min-width: 0 !important;
201
+ }
196
202
  }
197
203
 
198
204
  // Padding overrides.
@@ -201,12 +207,6 @@
201
207
  padding-right: 6px;
202
208
  }
203
209
 
204
- .block-editor-block-icon {
205
- width: 0 !important;
206
- height: 0 !important;
207
- min-width: 0 !important;
208
- }
209
-
210
210
  // Parent selector overrides
211
211
  .block-editor-block-parent-selector .block-editor-block-parent-selector__button {
212
212
  border-top-right-radius: 0;
@@ -295,19 +295,13 @@ function BlockFields( { clientId } ) {
295
295
  field.Edit = createConfiguredControl( {
296
296
  control: fieldDef.type,
297
297
  clientId,
298
- updateBlockAttributes,
299
298
  fieldDef,
300
299
  } );
301
300
  }
302
301
 
303
302
  return field;
304
303
  } );
305
- }, [
306
- blockTypeFields,
307
- blockType?.attributes,
308
- clientId,
309
- updateBlockAttributes,
310
- ] );
304
+ }, [ blockTypeFields, blockType?.attributes, clientId ] );
311
305
 
312
306
  const handleToggleField = ( fieldId ) => {
313
307
  setForm( ( prev ) => {
@@ -351,26 +345,7 @@ function BlockFields( { clientId } ) {
351
345
  fields={ dataFormFields }
352
346
  form={ form }
353
347
  onChange={ ( changes ) => {
354
- // Map field values to block attributes using field.setValue
355
- const mappedChanges = {};
356
- Object.entries( changes ).forEach(
357
- ( [ fieldId, fieldValue ] ) => {
358
- const field = dataFormFields.find(
359
- ( f ) => f.id === fieldId
360
- );
361
- if ( field && field.setValue ) {
362
- const updates = field.setValue( {
363
- item: attributes,
364
- value: fieldValue,
365
- } );
366
- Object.assign( mappedChanges, updates );
367
- } else {
368
- // For fields without setValue, use the value directly
369
- mappedChanges[ fieldId ] = fieldValue;
370
- }
371
- }
372
- );
373
- updateBlockAttributes( clientId, mappedChanges );
348
+ updateBlockAttributes( clientId, changes );
374
349
  } }
375
350
  />
376
351
  </div>
@@ -67,15 +67,15 @@ export function getUpdatedLinkAttributes( {
67
67
  };
68
68
  }
69
69
 
70
- export default function Link( { data, field, config = {} } ) {
70
+ export default function Link( { data, field, onChange, config = {} } ) {
71
71
  const [ isLinkControlOpen, setIsLinkControlOpen ] = useState( false );
72
72
  const { popoverProps } = useInspectorPopoverPlacement( {
73
73
  isControl: true,
74
74
  } );
75
- const { clientId, updateBlockAttributes, fieldDef } = config;
75
+ const { fieldDef } = config;
76
76
  const updateAttributes = ( newValue ) => {
77
77
  const mappedChanges = field.setValue( { item: data, value: newValue } );
78
- updateBlockAttributes( clientId, mappedChanges );
78
+ onChange( mappedChanges );
79
79
  };
80
80
 
81
81
  const value = field.getValue( { item: data } );
@@ -83,19 +83,19 @@ function MediaThumbnail( { data, field, attachment } ) {
83
83
  return <Icon icon={ mediaIcon } size={ 24 } />;
84
84
  }
85
85
 
86
- export default function Media( { data, field, config = {} } ) {
86
+ export default function Media( { data, field, onChange, config = {} } ) {
87
87
  const { popoverProps } = useInspectorPopoverPlacement( {
88
88
  isControl: true,
89
89
  } );
90
90
  const value = field.getValue( { item: data } );
91
91
  const { allowedTypes = [], multiple = false } = field.config || {};
92
- const { clientId, updateBlockAttributes, fieldDef } = config;
92
+ const { fieldDef } = config;
93
93
  const updateAttributes = ( newFieldValue ) => {
94
94
  const mappedChanges = field.setValue( {
95
95
  item: data,
96
96
  value: newFieldValue,
97
97
  } );
98
- updateBlockAttributes( clientId, mappedChanges );
98
+ onChange( mappedChanges );
99
99
  };
100
100
 
101
101
  // Check if featured image is supported by checking if it's in the mapping
@@ -23,15 +23,16 @@ export default function RichTextControl( {
23
23
  data,
24
24
  field,
25
25
  hideLabelFromVision,
26
+ onChange,
26
27
  config = {},
27
28
  } ) {
28
29
  const registry = useRegistry();
29
30
  const attrValue = field.getValue( { item: data } );
30
31
  const fieldConfig = field.config || {};
31
- const { clientId, updateBlockAttributes } = config;
32
+ const { clientId } = config;
32
33
  const updateAttributes = ( html ) => {
33
34
  const mappedChanges = field.setValue( { item: data, value: html } );
34
- updateBlockAttributes( clientId, mappedChanges );
35
+ onChange( mappedChanges );
35
36
  };
36
37
  const [ selection, setSelection ] = useState( {
37
38
  start: undefined,
@@ -1,8 +1,3 @@
1
- /**
2
- * External dependencies
3
- */
4
- import styled from '@emotion/styled';
5
-
6
1
  /**
7
2
  * WordPress dependencies
8
3
  */
@@ -12,10 +7,6 @@ import {
12
7
  } from '@wordpress/components';
13
8
  import { __ } from '@wordpress/i18n';
14
9
 
15
- const SingleColumnToolsPanelItem = styled( ToolsPanelItem )`
16
- grid-column: span 1;
17
- `;
18
-
19
10
  /**
20
11
  * @typedef {import('@wordpress/components/build-types/unit-control/types').WPUnitControlUnit} WPUnitControlUnit
21
12
  */
@@ -72,7 +63,8 @@ export default function WidthHeightTool( {
72
63
 
73
64
  return (
74
65
  <>
75
- <SingleColumnToolsPanelItem
66
+ <ToolsPanelItem
67
+ style={ { gridColumn: 'span 1' } }
76
68
  label={ __( 'Width' ) }
77
69
  isShownByDefault={ isShownByDefault }
78
70
  hasValue={ () => width !== '' }
@@ -89,8 +81,9 @@ export default function WidthHeightTool( {
89
81
  onChange={ onDimensionChange( 'width' ) }
90
82
  size="__unstable-large"
91
83
  />
92
- </SingleColumnToolsPanelItem>
93
- <SingleColumnToolsPanelItem
84
+ </ToolsPanelItem>
85
+ <ToolsPanelItem
86
+ style={ { gridColumn: 'span 1' } }
94
87
  label={ __( 'Height' ) }
95
88
  isShownByDefault={ isShownByDefault }
96
89
  hasValue={ () => height !== '' }
@@ -107,7 +100,7 @@ export default function WidthHeightTool( {
107
100
  onChange={ onDimensionChange( 'height' ) }
108
101
  size="__unstable-large"
109
102
  />
110
- </SingleColumnToolsPanelItem>
103
+ </ToolsPanelItem>
111
104
  </>
112
105
  );
113
106
  }
@@ -61,16 +61,29 @@ function GridItemResizerInner( {
61
61
  const blockClientRect = blockElement.getBoundingClientRect();
62
62
  const rootBlockClientRect =
63
63
  rootBlockElement.getBoundingClientRect();
64
+
65
+ const topAvailable = blockClientRect.top > rootBlockClientRect.top;
66
+ const bottomAvailable =
67
+ blockClientRect.bottom < rootBlockClientRect.bottom;
68
+ const leftAvailable =
69
+ blockClientRect.left > rootBlockClientRect.left;
70
+ const rightAvailable =
71
+ blockClientRect.right < rootBlockClientRect.right;
72
+
64
73
  setEnableSide( {
65
- top: blockClientRect.top > rootBlockClientRect.top,
66
- bottom: blockClientRect.bottom < rootBlockClientRect.bottom,
67
- left: blockClientRect.left > rootBlockClientRect.left,
68
- right: blockClientRect.right < rootBlockClientRect.right,
74
+ top: !! isManualGrid
75
+ ? topAvailable
76
+ : ! bottomAvailable && topAvailable,
77
+ bottom: bottomAvailable,
78
+ left: !! isManualGrid
79
+ ? leftAvailable
80
+ : ! rightAvailable && leftAvailable,
81
+ right: rightAvailable,
69
82
  } );
70
83
  } );
71
84
  observer.observe( blockElement );
72
85
  return () => observer.disconnect();
73
- }, [ blockElement, rootBlockElement ] );
86
+ }, [ blockElement, rootBlockElement, isManualGrid ] );
74
87
 
75
88
  const justification = {
76
89
  right: 'left',
@@ -1,7 +1,6 @@
1
1
  /**
2
2
  * External dependencies
3
3
  */
4
- import Cropper from 'react-easy-crop';
5
4
  import clsx from 'clsx';
6
5
 
7
6
  /**
@@ -9,12 +8,11 @@ import clsx from 'clsx';
9
8
  */
10
9
  import { Spinner } from '@wordpress/components';
11
10
  import { useResizeObserver } from '@wordpress/compose';
11
+ import { ImageCropper as ImageCropperComponent } from '@wordpress/image-cropper';
12
12
 
13
13
  /**
14
14
  * Internal dependencies
15
15
  */
16
- import { MIN_ZOOM, MAX_ZOOM } from './constants';
17
-
18
16
  import { useImageEditingContext } from './context';
19
17
 
20
18
  export default function ImageCropper( {
@@ -25,17 +23,7 @@ export default function ImageCropper( {
25
23
  naturalWidth,
26
24
  borderProps,
27
25
  } ) {
28
- const {
29
- isInProgress,
30
- editedUrl,
31
- position,
32
- zoom,
33
- aspect,
34
- setPosition,
35
- setCrop,
36
- setZoom,
37
- rotation,
38
- } = useImageEditingContext();
26
+ const { isInProgress, editedUrl, rotation } = useImageEditingContext();
39
27
  const [ contentResizeListener, { width: clientWidth } ] =
40
28
  useResizeObserver();
41
29
 
@@ -60,24 +48,7 @@ export default function ImageCropper( {
60
48
  height: editedHeight,
61
49
  } }
62
50
  >
63
- <Cropper
64
- image={ editedUrl || url }
65
- disabled={ isInProgress }
66
- minZoom={ MIN_ZOOM / 100 }
67
- maxZoom={ MAX_ZOOM / 100 }
68
- crop={ position }
69
- zoom={ zoom / 100 }
70
- aspect={ aspect }
71
- onCropChange={ ( pos ) => {
72
- setPosition( pos );
73
- } }
74
- onCropComplete={ ( newCropPercent ) => {
75
- setCrop( newCropPercent );
76
- } }
77
- onZoomChange={ ( newZoom ) => {
78
- setZoom( newZoom * 100 );
79
- } }
80
- />
51
+ <ImageCropperComponent src={ editedUrl || url } />
81
52
  { isInProgress && <Spinner /> }
82
53
  </div>
83
54
  );
@@ -2,6 +2,7 @@
2
2
  * WordPress dependencies
3
3
  */
4
4
  import { ToolbarGroup, ToolbarItem } from '@wordpress/components';
5
+ import { ImageCropperProvider } from '@wordpress/image-cropper';
5
6
 
6
7
  /**
7
8
  * Internal dependencies
@@ -26,36 +27,40 @@ export default function ImageEditor( {
26
27
  borderProps,
27
28
  } ) {
28
29
  return (
29
- <ImageEditingProvider
30
- id={ id }
31
- url={ url }
32
- naturalWidth={ naturalWidth }
33
- naturalHeight={ naturalHeight }
34
- onSaveImage={ onSaveImage }
35
- onFinishEditing={ onFinishEditing }
36
- >
37
- <Cropper
38
- borderProps={ borderProps }
30
+ <ImageCropperProvider>
31
+ <ImageEditingProvider
32
+ id={ id }
39
33
  url={ url }
40
- width={ width }
41
- height={ height }
42
- naturalHeight={ naturalHeight }
43
34
  naturalWidth={ naturalWidth }
44
- />
45
- <BlockControls>
46
- <ToolbarGroup>
47
- <ZoomDropdown />
48
- <ToolbarItem>
49
- { ( toggleProps ) => (
50
- <AspectRatioDropdown toggleProps={ toggleProps } />
51
- ) }
52
- </ToolbarItem>
53
- <RotationButton />
54
- </ToolbarGroup>
55
- <ToolbarGroup>
56
- <FormControls />
57
- </ToolbarGroup>
58
- </BlockControls>
59
- </ImageEditingProvider>
35
+ naturalHeight={ naturalHeight }
36
+ onSaveImage={ onSaveImage }
37
+ onFinishEditing={ onFinishEditing }
38
+ >
39
+ <Cropper
40
+ borderProps={ borderProps }
41
+ url={ url }
42
+ width={ width }
43
+ height={ height }
44
+ naturalHeight={ naturalHeight }
45
+ naturalWidth={ naturalWidth }
46
+ />
47
+ <BlockControls>
48
+ <ToolbarGroup>
49
+ <ZoomDropdown />
50
+ <ToolbarItem>
51
+ { ( toggleProps ) => (
52
+ <AspectRatioDropdown
53
+ toggleProps={ toggleProps }
54
+ />
55
+ ) }
56
+ </ToolbarItem>
57
+ <RotationButton />
58
+ </ToolbarGroup>
59
+ <ToolbarGroup>
60
+ <FormControls />
61
+ </ToolbarGroup>
62
+ </BlockControls>
63
+ </ImageEditingProvider>
64
+ </ImageCropperProvider>
60
65
  );
61
66
  }
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- import { useCallback, useMemo, useState } from '@wordpress/element';
4
+ import { useCallback, useEffect, useMemo, useState } from '@wordpress/element';
5
5
  import { applyFilters } from '@wordpress/hooks';
6
+ import { useImageCropper } from '@wordpress/image-cropper';
6
7
 
7
8
  export default function useTransformImage( {
8
9
  url,
@@ -10,30 +11,63 @@ export default function useTransformImage( {
10
11
  naturalHeight,
11
12
  } ) {
12
13
  const [ editedUrl, setEditedUrl ] = useState();
13
- const [ crop, setCrop ] = useState();
14
- const [ position, setPosition ] = useState( { x: 0, y: 0 } );
15
- const [ zoom, setZoom ] = useState( 100 );
16
- const [ rotation, setRotation ] = useState( 0 );
17
- const defaultAspect = naturalWidth / naturalHeight;
18
- const [ aspect, setAspect ] = useState( defaultAspect );
14
+ const { cropperState, setCropperState } = useImageCropper();
15
+ const { zoom, aspectRatio, crop, croppedArea } = cropperState;
16
+
17
+ const setZoom = useCallback(
18
+ ( newZoom ) => {
19
+ setCropperState( { zoom: newZoom } );
20
+ },
21
+ [ setCropperState ]
22
+ );
23
+
24
+ const setAspectRatio = useCallback(
25
+ ( newAspect ) => {
26
+ setCropperState( { aspectRatio: newAspect } );
27
+ },
28
+ [ setCropperState ]
29
+ );
19
30
 
31
+ const defaultAspect = naturalWidth / naturalHeight;
32
+ const rotatedAspect = naturalHeight / naturalWidth;
33
+
34
+ // Initialize aspect ratio on mount or when defaultAspect changes
35
+ useEffect( () => {
36
+ setAspectRatio( defaultAspect );
37
+ }, [] ); // eslint-disable-line react-hooks/exhaustive-deps
38
+
39
+ /**
40
+ * rotateClockwise rotates the image by 90° clockwise by drawing the original image onto a canvas with rotation applied,
41
+ * then saves it as a new blob URL (editedUrl).
42
+ * This creates a new rotated image file, bypassing the image-cropper’s CSS transform rotation.
43
+ * It's a bespoke solution to ensure that the rotated image fills the content width.
44
+ */
45
+ const [ internalRotation, setInternalRotation ] = useState( 0 );
20
46
  const rotateClockwise = useCallback( () => {
21
- const angle = ( rotation + 90 ) % 360;
47
+ const angle = ( internalRotation + 90 ) % 360;
22
48
 
23
49
  let naturalAspectRatio = defaultAspect;
50
+ const isDefaultAspect =
51
+ defaultAspect === aspectRatio || rotatedAspect === aspectRatio;
52
+ const shouldResetAspect = zoom !== 1 || ! isDefaultAspect;
24
53
 
25
- if ( rotation % 180 === 90 ) {
54
+ if ( internalRotation % 180 === 90 ) {
26
55
  naturalAspectRatio = 1 / defaultAspect;
27
56
  }
28
57
 
29
58
  if ( angle === 0 ) {
30
59
  setEditedUrl();
31
- setRotation( angle );
32
- setAspect( defaultAspect );
33
- setPosition( ( prevPosition ) => ( {
34
- x: -( prevPosition.y * naturalAspectRatio ),
35
- y: prevPosition.x * naturalAspectRatio,
36
- } ) );
60
+ setInternalRotation( angle );
61
+ const newAspectRatio = shouldResetAspect
62
+ ? aspectRatio
63
+ : defaultAspect;
64
+ setCropperState( {
65
+ aspectRatio: newAspectRatio,
66
+ crop: {
67
+ x: -( crop.y * naturalAspectRatio ),
68
+ y: crop.x * naturalAspectRatio,
69
+ },
70
+ } );
37
71
  return;
38
72
  }
39
73
 
@@ -67,12 +101,17 @@ export default function useTransformImage( {
67
101
 
68
102
  canvas.toBlob( ( blob ) => {
69
103
  setEditedUrl( URL.createObjectURL( blob ) );
70
- setRotation( angle );
71
- setAspect( canvas.width / canvas.height );
72
- setPosition( ( prevPosition ) => ( {
73
- x: -( prevPosition.y * naturalAspectRatio ),
74
- y: prevPosition.x * naturalAspectRatio,
75
- } ) );
104
+ setInternalRotation( angle );
105
+ const newAspectRatio = shouldResetAspect
106
+ ? aspectRatio
107
+ : canvas.width / canvas.height;
108
+ setCropperState( {
109
+ aspectRatio: newAspectRatio,
110
+ crop: {
111
+ x: -( crop.y * naturalAspectRatio ),
112
+ y: crop.x * naturalAspectRatio,
113
+ },
114
+ } );
76
115
  } );
77
116
  }
78
117
 
@@ -88,33 +127,40 @@ export default function useTransformImage( {
88
127
  if ( typeof imgCrossOrigin === 'string' ) {
89
128
  el.crossOrigin = imgCrossOrigin;
90
129
  }
91
- }, [ rotation, defaultAspect, url ] );
130
+ }, [
131
+ internalRotation,
132
+ defaultAspect,
133
+ url,
134
+ setCropperState,
135
+ crop,
136
+ zoom,
137
+ aspectRatio,
138
+ rotatedAspect,
139
+ setInternalRotation,
140
+ ] );
92
141
 
93
142
  return useMemo(
94
143
  () => ( {
95
144
  editedUrl,
96
145
  setEditedUrl,
97
- crop,
98
- setCrop,
99
- position,
100
- setPosition,
146
+ crop: croppedArea,
101
147
  zoom,
102
148
  setZoom,
103
- rotation,
104
- setRotation,
149
+ rotation: internalRotation,
105
150
  rotateClockwise,
106
- aspect,
107
- setAspect,
151
+ aspect: aspectRatio,
152
+ setAspect: setAspectRatio,
108
153
  defaultAspect,
109
154
  } ),
110
155
  [
111
156
  editedUrl,
112
- crop,
113
- position,
157
+ croppedArea,
114
158
  zoom,
115
- rotation,
159
+ setZoom,
160
+ internalRotation,
116
161
  rotateClockwise,
117
- aspect,
162
+ aspectRatio,
163
+ setAspectRatio,
118
164
  defaultAspect,
119
165
  ]
120
166
  );
@@ -39,8 +39,8 @@ export default function ZoomDropdown() {
39
39
  label={ __( 'Zoom' ) }
40
40
  min={ MIN_ZOOM }
41
41
  max={ MAX_ZOOM }
42
- value={ Math.round( zoom ) }
43
- onChange={ setZoom }
42
+ value={ Math.round( zoom * 100 ) }
43
+ onChange={ ( newZoom ) => setZoom( newZoom / 100 ) }
44
44
  />
45
45
  </DropdownContentWrapper>
46
46
  ) }
@@ -11,6 +11,11 @@ export {
11
11
  BlockAlignmentControl,
12
12
  BlockAlignmentToolbar,
13
13
  } from './block-alignment-control';
14
+ export {
15
+ BlockBindingsAttributeControl,
16
+ BlockBindingsSourceFieldsList,
17
+ useBlockBindingsUtils,
18
+ } from './block-bindings';
14
19
  export { default as __experimentalBlockFullHeightAligmentControl } from './block-full-height-alignment-control';
15
20
  export { default as __experimentalBlockAlignmentMatrixControl } from './block-alignment-matrix-control';
16
21
  export { default as BlockBreadcrumb } from './block-breadcrumb';
@@ -107,7 +112,6 @@ export {
107
112
 
108
113
  export { default as __unstableBlockSettingsMenuFirstItem } from './block-settings-menu/block-settings-menu-first-item';
109
114
  export { default as __unstableBlockToolbarLastItem } from './block-toolbar/block-toolbar-last-item';
110
- export { default as __unstableBlockNameContext } from './block-toolbar/block-name-context';
111
115
  export { default as __unstableInserterMenuExtension } from './inserter-menu-extension';
112
116
  export { default as __experimentalPreviewOptions } from './preview-options';
113
117
  export { default as __experimentalUseResizeCanvas } from './use-resize-canvas';
@@ -201,6 +201,8 @@ function useInsertionPoint( {
201
201
  onSelect,
202
202
  shouldFocusBlock,
203
203
  selectBlockOnInsert,
204
+ setLastFocus,
205
+ registry,
204
206
  ]
205
207
  );
206
208
 
@@ -234,6 +236,7 @@ function useInsertionPoint( {
234
236
  hideInsertionPoint,
235
237
  destinationRootClientId,
236
238
  destinationIndex,
239
+ registry,
237
240
  ]
238
241
  );
239
242
 
@@ -383,7 +383,7 @@ $block-inserter-tabs-height: 44px;
383
383
  left: 0;
384
384
  bottom: 0;
385
385
  width: $sidebar-width;
386
- padding: $grid-unit-30 $grid-unit-40 $grid-unit-40;
386
+ padding: $grid-unit-30 $grid-unit-30 $grid-unit-30;
387
387
  overflow-x: visible;
388
388
  overflow-y: auto;
389
389
 
@@ -21,14 +21,15 @@ const InserterDraggableBlocks = ( {
21
21
  children,
22
22
  pattern,
23
23
  } ) => {
24
+ const blockName = blocks.length === 1 ? blocks[ 0 ].name : undefined;
24
25
  const blockTypeIcon = useSelect(
25
26
  ( select ) => {
26
- const { getBlockType } = select( blocksStore );
27
27
  return (
28
- blocks.length === 1 && getBlockType( blocks[ 0 ].name )?.icon
28
+ blockName &&
29
+ select( blocksStore ).getBlockType( blockName )?.icon
29
30
  );
30
31
  },
31
- [ blocks ]
32
+ [ blockName ]
32
33
  );
33
34
 
34
35
  const { startDragging, stopDragging } = unlock(
@@ -57,13 +58,23 @@ const InserterDraggableBlocks = ( {
57
58
  transferData={ { type: 'inserter', blocks: draggableBlocks } }
58
59
  onDragStart={ ( event ) => {
59
60
  startDragging();
61
+ const addedTypes = new Set();
60
62
  for ( const block of draggableBlocks ) {
61
63
  const type = `wp-block:${ block.name }`;
62
- // This will fill in the dataTransfer.types array so that
63
- // the drop zone can check if the draggable is eligible.
64
- // Unfortuantely, on drag start, we don't have access to the
65
- // actual data, only the data keys/types.
66
- event.dataTransfer.items.add( '', type );
64
+ /*
65
+ * Only add each block type once to avoid DataTransferItemList::add `NotSupportedError`
66
+ * when patterns contain multiple blocks of the same type.
67
+ */
68
+ if ( ! addedTypes.has( type ) ) {
69
+ /*
70
+ * This will fill in the dataTransfer.types array so that
71
+ * the drop zone can check if the draggable is eligible.
72
+ * Unfortuantely, on drag start, we don't have access to the
73
+ * actual data, only the data keys/types.
74
+ */
75
+ event.dataTransfer.items.add( '', type );
76
+ addedTypes.add( type );
77
+ }
67
78
  }
68
79
  } }
69
80
  onDragEnd={ () => {