@wordpress/block-editor 12.19.2 → 12.20.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 (198) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/build/components/block-canvas/index.js +2 -0
  3. package/build/components/block-canvas/index.js.map +1 -1
  4. package/build/components/block-heading-level-dropdown/index.js +14 -17
  5. package/build/components/block-heading-level-dropdown/index.js.map +1 -1
  6. package/build/components/block-inspector/index.js +0 -3
  7. package/build/components/block-inspector/index.js.map +1 -1
  8. package/build/components/block-list/index.js +9 -9
  9. package/build/components/block-list/index.js.map +1 -1
  10. package/build/components/block-preview/index.js +4 -0
  11. package/build/components/block-preview/index.js.map +1 -1
  12. package/build/components/block-settings-menu/block-parent-selector-menu-item.js +54 -0
  13. package/build/components/block-settings-menu/block-parent-selector-menu-item.js.map +1 -0
  14. package/build/components/block-settings-menu/block-settings-dropdown.js +9 -37
  15. package/build/components/block-settings-menu/block-settings-dropdown.js.map +1 -1
  16. package/build/components/block-tools/insertion-point.js +4 -1
  17. package/build/components/block-tools/insertion-point.js.map +1 -1
  18. package/build/components/child-layout-control/index.js +37 -5
  19. package/build/components/child-layout-control/index.js.map +1 -1
  20. package/build/components/date-format-picker/index.js +0 -1
  21. package/build/components/date-format-picker/index.js.map +1 -1
  22. package/build/components/font-appearance-control/index.js +1 -2
  23. package/build/components/font-appearance-control/index.js.map +1 -1
  24. package/build/components/global-styles/advanced-panel.js +5 -10
  25. package/build/components/global-styles/advanced-panel.js.map +1 -1
  26. package/build/components/global-styles/dimensions-panel.js +30 -19
  27. package/build/components/global-styles/dimensions-panel.js.map +1 -1
  28. package/build/components/global-styles/shadow-panel-components.js +3 -3
  29. package/build/components/global-styles/shadow-panel-components.js.map +1 -1
  30. package/build/components/inserter/media-tab/media-preview.js +1 -1
  31. package/build/components/inserter/media-tab/media-preview.js.map +1 -1
  32. package/build/components/inspector-controls-tabs/styles-tab.js +0 -4
  33. package/build/components/inspector-controls-tabs/styles-tab.js.map +1 -1
  34. package/build/components/observe-typing/index.js +5 -10
  35. package/build/components/observe-typing/index.js.map +1 -1
  36. package/build/components/rich-text/format-toolbar-container.js +1 -31
  37. package/build/components/rich-text/format-toolbar-container.js.map +1 -1
  38. package/build/components/rich-text/index.js +8 -5
  39. package/build/components/rich-text/index.js.map +1 -1
  40. package/build/components/rich-text/use-paste-handler.js +25 -26
  41. package/build/components/rich-text/use-paste-handler.js.map +1 -1
  42. package/build/components/skip-to-selected-block/index.js +7 -14
  43. package/build/components/skip-to-selected-block/index.js.map +1 -1
  44. package/build/components/spacing-sizes-control/input-controls/spacing-input-control.js +0 -1
  45. package/build/components/spacing-sizes-control/input-controls/spacing-input-control.js.map +1 -1
  46. package/build/components/url-input/index.js +5 -8
  47. package/build/components/url-input/index.js.map +1 -1
  48. package/build/hooks/background.js +4 -2
  49. package/build/hooks/background.js.map +1 -1
  50. package/build/hooks/layout-child.js +50 -1
  51. package/build/hooks/layout-child.js.map +1 -1
  52. package/build/hooks/position.js +0 -1
  53. package/build/hooks/position.js.map +1 -1
  54. package/build/layouts/grid.js +105 -12
  55. package/build/layouts/grid.js.map +1 -1
  56. package/build/private-apis.js +3 -1
  57. package/build/private-apis.js.map +1 -1
  58. package/build/store/actions.js +2 -30
  59. package/build/store/actions.js.map +1 -1
  60. package/build/store/index.js +0 -2
  61. package/build/store/index.js.map +1 -1
  62. package/build/store/private-keys.js +8 -0
  63. package/build/store/private-keys.js.map +1 -0
  64. package/build/store/private-selectors.js +6 -9
  65. package/build/store/private-selectors.js.map +1 -1
  66. package/build/store/reducer.js +1 -9
  67. package/build/store/reducer.js.map +1 -1
  68. package/build/store/selectors.js +7 -12
  69. package/build/store/selectors.js.map +1 -1
  70. package/build/store/utils.js +7 -2
  71. package/build/store/utils.js.map +1 -1
  72. package/build-module/components/block-canvas/index.js +2 -0
  73. package/build-module/components/block-canvas/index.js.map +1 -1
  74. package/build-module/components/block-heading-level-dropdown/index.js +14 -17
  75. package/build-module/components/block-heading-level-dropdown/index.js.map +1 -1
  76. package/build-module/components/block-inspector/index.js +1 -4
  77. package/build-module/components/block-inspector/index.js.map +1 -1
  78. package/build-module/components/block-list/index.js +9 -9
  79. package/build-module/components/block-list/index.js.map +1 -1
  80. package/build-module/components/block-preview/index.js +4 -0
  81. package/build-module/components/block-preview/index.js.map +1 -1
  82. package/build-module/components/block-settings-menu/block-parent-selector-menu-item.js +46 -0
  83. package/build-module/components/block-settings-menu/block-parent-selector-menu-item.js.map +1 -0
  84. package/build-module/components/block-settings-menu/block-settings-dropdown.js +12 -40
  85. package/build-module/components/block-settings-menu/block-settings-dropdown.js.map +1 -1
  86. package/build-module/components/block-tools/insertion-point.js +4 -1
  87. package/build-module/components/block-tools/insertion-point.js.map +1 -1
  88. package/build-module/components/child-layout-control/index.js +38 -6
  89. package/build-module/components/child-layout-control/index.js.map +1 -1
  90. package/build-module/components/date-format-picker/index.js +0 -1
  91. package/build-module/components/date-format-picker/index.js.map +1 -1
  92. package/build-module/components/font-appearance-control/index.js +1 -2
  93. package/build-module/components/font-appearance-control/index.js.map +1 -1
  94. package/build-module/components/global-styles/advanced-panel.js +6 -11
  95. package/build-module/components/global-styles/advanced-panel.js.map +1 -1
  96. package/build-module/components/global-styles/dimensions-panel.js +30 -19
  97. package/build-module/components/global-styles/dimensions-panel.js.map +1 -1
  98. package/build-module/components/global-styles/shadow-panel-components.js +3 -3
  99. package/build-module/components/global-styles/shadow-panel-components.js.map +1 -1
  100. package/build-module/components/inserter/media-tab/media-preview.js +1 -1
  101. package/build-module/components/inserter/media-tab/media-preview.js.map +1 -1
  102. package/build-module/components/inspector-controls-tabs/styles-tab.js +0 -4
  103. package/build-module/components/inspector-controls-tabs/styles-tab.js.map +1 -1
  104. package/build-module/components/observe-typing/index.js +5 -10
  105. package/build-module/components/observe-typing/index.js.map +1 -1
  106. package/build-module/components/rich-text/format-toolbar-container.js +1 -31
  107. package/build-module/components/rich-text/format-toolbar-container.js.map +1 -1
  108. package/build-module/components/rich-text/index.js +8 -5
  109. package/build-module/components/rich-text/index.js.map +1 -1
  110. package/build-module/components/rich-text/use-paste-handler.js +25 -26
  111. package/build-module/components/rich-text/use-paste-handler.js.map +1 -1
  112. package/build-module/components/skip-to-selected-block/index.js +8 -14
  113. package/build-module/components/skip-to-selected-block/index.js.map +1 -1
  114. package/build-module/components/spacing-sizes-control/input-controls/spacing-input-control.js +0 -1
  115. package/build-module/components/spacing-sizes-control/input-controls/spacing-input-control.js.map +1 -1
  116. package/build-module/components/url-input/index.js +5 -8
  117. package/build-module/components/url-input/index.js.map +1 -1
  118. package/build-module/hooks/background.js +4 -2
  119. package/build-module/hooks/background.js.map +1 -1
  120. package/build-module/hooks/layout-child.js +50 -1
  121. package/build-module/hooks/layout-child.js.map +1 -1
  122. package/build-module/hooks/position.js +0 -1
  123. package/build-module/hooks/position.js.map +1 -1
  124. package/build-module/layouts/grid.js +107 -14
  125. package/build-module/layouts/grid.js.map +1 -1
  126. package/build-module/private-apis.js +3 -1
  127. package/build-module/private-apis.js.map +1 -1
  128. package/build-module/store/actions.js +2 -30
  129. package/build-module/store/actions.js.map +1 -1
  130. package/build-module/store/index.js +0 -2
  131. package/build-module/store/index.js.map +1 -1
  132. package/build-module/store/private-keys.js +2 -0
  133. package/build-module/store/private-keys.js.map +1 -0
  134. package/build-module/store/private-selectors.js +5 -6
  135. package/build-module/store/private-selectors.js.map +1 -1
  136. package/build-module/store/reducer.js +1 -9
  137. package/build-module/store/reducer.js.map +1 -1
  138. package/build-module/store/selectors.js +8 -13
  139. package/build-module/store/selectors.js.map +1 -1
  140. package/build-module/store/utils.js +6 -2
  141. package/build-module/store/utils.js.map +1 -1
  142. package/build-style/style-rtl.css +12 -27
  143. package/build-style/style.css +12 -27
  144. package/package.json +31 -32
  145. package/src/components/block-canvas/index.js +2 -0
  146. package/src/components/block-heading-level-dropdown/index.js +17 -25
  147. package/src/components/block-inspector/index.js +0 -11
  148. package/src/components/block-list/index.js +55 -55
  149. package/src/components/block-preview/index.js +6 -1
  150. package/src/components/block-settings-menu/block-parent-selector-menu-item.js +50 -0
  151. package/src/components/block-settings-menu/block-settings-dropdown.js +9 -50
  152. package/src/components/block-styles/style.scss +0 -4
  153. package/src/components/block-toolbar/style.scss +11 -6
  154. package/src/components/block-tools/insertion-point.js +6 -1
  155. package/src/components/child-layout-control/index.js +85 -44
  156. package/src/components/date-format-picker/index.js +0 -1
  157. package/src/components/font-appearance-control/index.js +0 -1
  158. package/src/components/global-styles/advanced-panel.js +6 -12
  159. package/src/components/global-styles/dimensions-panel.js +36 -24
  160. package/src/components/global-styles/shadow-panel-components.js +3 -3
  161. package/src/components/global-styles/style.scss +0 -10
  162. package/src/components/global-styles/test/use-global-styles-output.js +3 -2
  163. package/src/components/inserter/media-tab/media-preview.js +6 -1
  164. package/src/components/inspector-controls-tabs/styles-tab.js +0 -7
  165. package/src/components/observe-typing/index.js +7 -10
  166. package/src/components/rich-text/format-toolbar-container.js +1 -48
  167. package/src/components/rich-text/index.js +12 -6
  168. package/src/components/rich-text/use-paste-handler.js +26 -25
  169. package/src/components/skip-to-selected-block/index.js +10 -13
  170. package/src/components/spacing-sizes-control/input-controls/spacing-input-control.js +0 -1
  171. package/src/components/url-input/index.js +6 -15
  172. package/src/hooks/anchor.scss +1 -1
  173. package/src/hooks/background.js +5 -2
  174. package/src/hooks/layout-child.js +53 -1
  175. package/src/hooks/position.js +0 -1
  176. package/src/layouts/grid.js +131 -52
  177. package/src/layouts/test/grid.js +16 -2
  178. package/src/private-apis.js +2 -0
  179. package/src/store/actions.js +2 -38
  180. package/src/store/index.js +0 -2
  181. package/src/store/private-keys.js +1 -0
  182. package/src/store/private-selectors.js +4 -7
  183. package/src/store/reducer.js +0 -10
  184. package/src/store/selectors.js +7 -15
  185. package/src/store/test/actions.js +0 -101
  186. package/src/store/utils.js +7 -2
  187. package/src/style.scss +0 -1
  188. package/build/components/default-style-picker/index.js +0 -70
  189. package/build/components/default-style-picker/index.js.map +0 -1
  190. package/build/store/resolvers.js +0 -27
  191. package/build/store/resolvers.js.map +0 -1
  192. package/build-module/components/default-style-picker/index.js +0 -63
  193. package/build-module/components/default-style-picker/index.js.map +0 -1
  194. package/build-module/store/resolvers.js +0 -20
  195. package/build-module/store/resolvers.js.map +0 -1
  196. package/src/components/block-settings-menu/style.scss +0 -3
  197. package/src/components/default-style-picker/index.js +0 -70
  198. package/src/store/resolvers.js +0 -17
@@ -3,13 +3,6 @@
3
3
  */
4
4
  import { __ } from '@wordpress/i18n';
5
5
  import { Popover, ToolbarGroup } from '@wordpress/components';
6
- import { useSelect } from '@wordpress/data';
7
- import {
8
- isCollapsed,
9
- getActiveFormats,
10
- useAnchor,
11
- store as richTextStore,
12
- } from '@wordpress/rich-text';
13
6
 
14
7
  /**
15
8
  * Internal dependencies
@@ -17,22 +10,6 @@ import {
17
10
  import BlockControls from '../block-controls';
18
11
  import FormatToolbar from './format-toolbar';
19
12
  import NavigableToolbar from '../navigable-toolbar';
20
- import { store as blockEditorStore } from '../../store';
21
-
22
- function InlineSelectionToolbar( { editableContentElement, activeFormats } ) {
23
- const lastFormat = activeFormats[ activeFormats.length - 1 ];
24
- const lastFormatType = lastFormat?.type;
25
- const settings = useSelect(
26
- ( select ) => select( richTextStore ).getFormatType( lastFormatType ),
27
- [ lastFormatType ]
28
- );
29
- const popoverAnchor = useAnchor( {
30
- editableContentElement,
31
- settings,
32
- } );
33
-
34
- return <InlineToolbar popoverAnchor={ popoverAnchor } />;
35
- }
36
13
 
37
14
  function InlineToolbar( { popoverAnchor } ) {
38
15
  return (
@@ -56,35 +33,11 @@ function InlineToolbar( { popoverAnchor } ) {
56
33
  );
57
34
  }
58
35
 
59
- const FormatToolbarContainer = ( {
60
- inline,
61
- editableContentElement,
62
- value,
63
- } ) => {
64
- const hasInlineToolbar = useSelect(
65
- ( select ) => select( blockEditorStore ).getSettings().hasInlineToolbar,
66
- []
67
- );
68
-
36
+ const FormatToolbarContainer = ( { inline, editableContentElement } ) => {
69
37
  if ( inline ) {
70
38
  return <InlineToolbar popoverAnchor={ editableContentElement } />;
71
39
  }
72
40
 
73
- if ( hasInlineToolbar ) {
74
- const activeFormats = getActiveFormats( value );
75
-
76
- if ( isCollapsed( value ) && ! activeFormats.length ) {
77
- return null;
78
- }
79
-
80
- return (
81
- <InlineSelectionToolbar
82
- editableContentElement={ editableContentElement }
83
- activeFormats={ activeFormats }
84
- />
85
- );
86
- }
87
-
88
41
  // Render regular toolbar.
89
42
  return (
90
43
  <BlockControls group="inline">
@@ -156,12 +156,19 @@ export function RichTextWrapper(
156
156
  for ( const [ attribute, args ] of Object.entries(
157
157
  blockBindings
158
158
  ) ) {
159
- // If any of the attributes with source "rich-text" is part of the bindings,
160
- // has a source with `lockAttributesEditing`, disable it.
161
159
  if (
162
- blockTypeAttributes?.[ attribute ]?.source ===
163
- 'rich-text' &&
164
- getBlockBindingsSource( args.source )?.lockAttributesEditing
160
+ blockTypeAttributes?.[ attribute ]?.source !== 'rich-text'
161
+ ) {
162
+ break;
163
+ }
164
+
165
+ // If the source is not defined, or if its value of `lockAttributesEditing` is `true`, disable it.
166
+ const blockBindingsSource = getBlockBindingsSource(
167
+ args.source
168
+ );
169
+ if (
170
+ ! blockBindingsSource ||
171
+ blockBindingsSource.lockAttributesEditing
165
172
  ) {
166
173
  shouldDisableEditing = true;
167
174
  break;
@@ -348,7 +355,6 @@ export function RichTextWrapper(
348
355
  <FormatToolbarContainer
349
356
  inline={ inlineToolbar }
350
357
  editableContentElement={ anchorRef.current }
351
- value={ value }
352
358
  />
353
359
  ) }
354
360
  <TagName
@@ -58,13 +58,35 @@ export function usePasteHandler( props ) {
58
58
  const isInternal =
59
59
  event.clipboardData.getData( 'rich-text' ) === 'true';
60
60
 
61
+ function pasteInline( content ) {
62
+ const transformed = formatTypes.reduce(
63
+ ( accumulator, { __unstablePasteRule } ) => {
64
+ // Only allow one transform.
65
+ if ( __unstablePasteRule && accumulator === value ) {
66
+ accumulator = __unstablePasteRule( value, {
67
+ html,
68
+ plainText,
69
+ } );
70
+ }
71
+
72
+ return accumulator;
73
+ },
74
+ value
75
+ );
76
+ if ( transformed !== value ) {
77
+ onChange( transformed );
78
+ } else {
79
+ const valueToInsert = create( { html: content } );
80
+ addActiveFormats( valueToInsert, value.activeFormats );
81
+ onChange( insert( value, valueToInsert ) );
82
+ }
83
+ }
84
+
61
85
  // If the data comes from a rich text instance, we can directly use it
62
86
  // without filtering the data. The filters are only meant for externally
63
87
  // pasted content and remove inline styles.
64
88
  if ( isInternal ) {
65
- const pastedValue = create( { html } );
66
- addActiveFormats( pastedValue, value.activeFormats );
67
- onChange( insert( value, pastedValue ) );
89
+ pasteInline( html );
68
90
  return;
69
91
  }
70
92
 
@@ -135,28 +157,7 @@ export function usePasteHandler( props ) {
135
157
  } );
136
158
 
137
159
  if ( typeof content === 'string' ) {
138
- const transformed = formatTypes.reduce(
139
- ( accumlator, { __unstablePasteRule } ) => {
140
- // Only allow one transform.
141
- if ( __unstablePasteRule && accumlator === value ) {
142
- accumlator = __unstablePasteRule( value, {
143
- html,
144
- plainText,
145
- } );
146
- }
147
-
148
- return accumlator;
149
- },
150
- value
151
- );
152
-
153
- if ( transformed !== value ) {
154
- onChange( transformed );
155
- } else {
156
- const valueToInsert = create( { html: content } );
157
- addActiveFormats( valueToInsert, value.activeFormats );
158
- onChange( insert( value, valueToInsert ) );
159
- }
160
+ pasteInline( content );
160
161
  } else if ( content.length > 0 ) {
161
162
  if ( onReplace && isEmpty( value ) ) {
162
163
  onReplace( content, content.length - 1, -1 );
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- import { withSelect } from '@wordpress/data';
4
+ import { useSelect } from '@wordpress/data';
5
5
  import { __ } from '@wordpress/i18n';
6
6
  import { Button } from '@wordpress/components';
7
7
 
@@ -11,7 +11,14 @@ import { Button } from '@wordpress/components';
11
11
  import { store as blockEditorStore } from '../../store';
12
12
  import { __unstableUseBlockRef as useBlockRef } from '../block-list/use-block-props/use-block-refs';
13
13
 
14
- const SkipToSelectedBlock = ( { selectedBlockClientId } ) => {
14
+ /**
15
+ * @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/skip-to-selected-block/README.md
16
+ */
17
+ export default function SkipToSelectedBlock() {
18
+ const selectedBlockClientId = useSelect(
19
+ ( select ) => select( blockEditorStore ).getBlockSelectionStart(),
20
+ []
21
+ );
15
22
  const ref = useBlockRef( selectedBlockClientId );
16
23
  const onClick = () => {
17
24
  ref.current.focus();
@@ -26,14 +33,4 @@ const SkipToSelectedBlock = ( { selectedBlockClientId } ) => {
26
33
  { __( 'Skip to the selected block' ) }
27
34
  </Button>
28
35
  ) : null;
29
- };
30
-
31
- /**
32
- * @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/skip-to-selected-block/README.md
33
- */
34
- export default withSelect( ( select ) => {
35
- return {
36
- selectedBlockClientId:
37
- select( blockEditorStore ).getBlockSelectionStart(),
38
- };
39
- } )( SkipToSelectedBlock );
36
+ }
@@ -297,7 +297,6 @@ export default function SpacingInputControl( {
297
297
  options={ options }
298
298
  label={ ariaLabel }
299
299
  hideLabelFromVision={ true }
300
- __nextUnconstrainedWidth={ true }
301
300
  size={ '__unstable-large' }
302
301
  onMouseOver={ onMouseOver }
303
302
  onMouseOut={ onMouseOut }
@@ -2,7 +2,6 @@
2
2
  * External dependencies
3
3
  */
4
4
  import classnames from 'classnames';
5
- import scrollIntoView from 'dom-scroll-into-view';
6
5
 
7
6
  /**
8
7
  * WordPress dependencies
@@ -83,21 +82,13 @@ class URLInput extends Component {
83
82
  if (
84
83
  showSuggestions &&
85
84
  selectedSuggestion !== null &&
86
- this.suggestionNodes[ selectedSuggestion ] &&
87
- ! this.scrollingIntoView
85
+ this.suggestionNodes[ selectedSuggestion ]
88
86
  ) {
89
- this.scrollingIntoView = true;
90
- scrollIntoView(
91
- this.suggestionNodes[ selectedSuggestion ],
92
- this.autocompleteRef.current,
93
- {
94
- onlyScrollIfNeeded: true,
95
- }
96
- );
97
-
98
- this.props.setTimeout( () => {
99
- this.scrollingIntoView = false;
100
- }, 100 );
87
+ this.suggestionNodes[ selectedSuggestion ].scrollIntoView( {
88
+ behavior: 'instant',
89
+ block: 'nearest',
90
+ inline: 'nearest',
91
+ } );
101
92
  }
102
93
 
103
94
  // Update suggestions when the value changes.
@@ -1,4 +1,4 @@
1
1
  .html-anchor-control .components-external-link {
2
- display: block;
2
+ display: inline-block;
3
3
  margin-top: $grid-unit-10;
4
4
  }
@@ -374,11 +374,14 @@ function backgroundSizeHelpText( value ) {
374
374
  }
375
375
 
376
376
  export const coordsToBackgroundPosition = ( value ) => {
377
- if ( ! value || isNaN( value.x ) || isNaN( value.y ) ) {
377
+ if ( ! value || ( isNaN( value.x ) && isNaN( value.y ) ) ) {
378
378
  return undefined;
379
379
  }
380
380
 
381
- return `${ value.x * 100 }% ${ value.y * 100 }%`;
381
+ const x = isNaN( value.x ) ? 0.5 : value.x;
382
+ const y = isNaN( value.y ) ? 0.5 : value.y;
383
+
384
+ return `${ x * 100 }% ${ y * 100 }%`;
382
385
  };
383
386
 
384
387
  export const backgroundPositionToCoords = ( value ) => {
@@ -9,13 +9,16 @@ import { useSelect } from '@wordpress/data';
9
9
  */
10
10
  import { store as blockEditorStore } from '../store';
11
11
  import { useStyleOverride } from './utils';
12
+ import { useLayout } from '../components/block-list/layout';
12
13
 
13
14
  function useBlockPropsChildLayoutStyles( { style } ) {
14
15
  const shouldRenderChildLayoutStyles = useSelect( ( select ) => {
15
16
  return ! select( blockEditorStore ).getSettings().disableLayoutStyles;
16
17
  } );
17
18
  const layout = style?.layout ?? {};
18
- const { selfStretch, flexSize } = layout;
19
+ const { selfStretch, flexSize, columnSpan, rowSpan } = layout;
20
+ const parentLayout = useLayout() || {};
21
+ const { columnCount, minimumColumnWidth } = parentLayout;
19
22
  const id = useInstanceId( useBlockPropsChildLayoutStyles );
20
23
  const selector = `.wp-container-content-${ id }`;
21
24
 
@@ -30,6 +33,55 @@ function useBlockPropsChildLayoutStyles( { style } ) {
30
33
  css = `${ selector } {
31
34
  flex-grow: 1;
32
35
  }`;
36
+ } else if ( columnSpan ) {
37
+ css = `${ selector } {
38
+ grid-column: span ${ columnSpan };
39
+ }`;
40
+ }
41
+ /**
42
+ * If minimumColumnWidth is set on the parent, or if no
43
+ * columnCount is set, the grid is responsive so a
44
+ * container query is needed for the span to resize.
45
+ */
46
+ if ( columnSpan && ( minimumColumnWidth || ! columnCount ) ) {
47
+ // Calculate the container query value.
48
+ const columnSpanNumber = parseInt( columnSpan );
49
+ let parentColumnValue = parseFloat( minimumColumnWidth );
50
+ /**
51
+ * 12rem is the default minimumColumnWidth value.
52
+ * If parentColumnValue is not a number, default to 12.
53
+ */
54
+ if ( isNaN( parentColumnValue ) ) {
55
+ parentColumnValue = 12;
56
+ }
57
+
58
+ let parentColumnUnit = minimumColumnWidth?.replace(
59
+ parentColumnValue,
60
+ ''
61
+ );
62
+ /**
63
+ * Check that parent column unit is either 'px', 'rem' or 'em'.
64
+ * If not, default to 'rem'.
65
+ */
66
+ if ( ! [ 'px', 'rem', 'em' ].includes( parentColumnUnit ) ) {
67
+ parentColumnUnit = 'rem';
68
+ }
69
+
70
+ const defaultGapValue = parentColumnUnit === 'px' ? 24 : 1.5;
71
+ const containerQueryValue =
72
+ columnSpanNumber * parentColumnValue +
73
+ ( columnSpanNumber - 1 ) * defaultGapValue;
74
+
75
+ css += `@container (max-width: ${ containerQueryValue }${ parentColumnUnit }) {
76
+ ${ selector } {
77
+ grid-column: 1 / -1;
78
+ }
79
+ }`;
80
+ }
81
+ if ( rowSpan ) {
82
+ css += `${ selector } {
83
+ grid-row: span ${ rowSpan };
84
+ }`;
33
85
  }
34
86
  }
35
87
 
@@ -288,7 +288,6 @@ export function PositionPanelPure( {
288
288
  help={ stickyHelpText }
289
289
  >
290
290
  <CustomSelectControl
291
- __nextUnconstrainedWidth
292
291
  __next40pxDefaultSize
293
292
  className="block-editor-hooks__position-selection__select-control"
294
293
  label={ __( 'Position' ) }
@@ -8,9 +8,13 @@ import {
8
8
  Flex,
9
9
  FlexItem,
10
10
  RangeControl,
11
+ __experimentalNumberControl as NumberControl,
12
+ __experimentalToggleGroupControl as ToggleGroupControl,
13
+ __experimentalToggleGroupControlOption as ToggleGroupControlOption,
11
14
  __experimentalUnitControl as UnitControl,
12
15
  __experimentalParseQuantityAndUnitFromRawValue as parseQuantityAndUnitFromRawValue,
13
16
  } from '@wordpress/components';
17
+ import { useState } from '@wordpress/element';
14
18
 
15
19
  /**
16
20
  * Internal dependencies
@@ -51,6 +55,12 @@ const RANGE_CONTROL_MAX_VALUES = {
51
55
  dvmax: 100,
52
56
  };
53
57
 
58
+ const units = [
59
+ { value: 'px', label: 'px', default: 0 },
60
+ { value: 'rem', label: 'rem', default: 0 },
61
+ { value: 'em', label: 'em', default: 0 },
62
+ ];
63
+
54
64
  export default {
55
65
  name: 'grid',
56
66
  label: __( 'Grid' ),
@@ -58,13 +68,24 @@ export default {
58
68
  layout = {},
59
69
  onChange,
60
70
  } ) {
61
- return layout?.columnCount ? (
62
- <GridLayoutColumnsControl layout={ layout } onChange={ onChange } />
63
- ) : (
64
- <GridLayoutMinimumWidthControl
65
- layout={ layout }
66
- onChange={ onChange }
67
- />
71
+ return (
72
+ <>
73
+ <GridLayoutTypeControl
74
+ layout={ layout }
75
+ onChange={ onChange }
76
+ />
77
+ { layout?.columnCount ? (
78
+ <GridLayoutColumnsControl
79
+ layout={ layout }
80
+ onChange={ onChange }
81
+ />
82
+ ) : (
83
+ <GridLayoutMinimumWidthControl
84
+ layout={ layout }
85
+ onChange={ onChange }
86
+ />
87
+ ) }
88
+ </>
68
89
  );
69
90
  },
70
91
  toolBarControls: function DefaultLayoutToolbarControls() {
@@ -97,7 +118,8 @@ export default {
97
118
  );
98
119
  } else if ( minimumColumnWidth ) {
99
120
  rules.push(
100
- `grid-template-columns: repeat(auto-fill, minmax(min(${ minimumColumnWidth }, 100%), 1fr))`
121
+ `grid-template-columns: repeat(auto-fill, minmax(min(${ minimumColumnWidth }, 100%), 1fr))`,
122
+ `container-type: inline-size`
101
123
  );
102
124
  }
103
125
 
@@ -152,38 +174,6 @@ function GridLayoutMinimumWidthControl( { layout, onChange } ) {
152
174
  } else if ( [ 'em', 'rem' ].includes( unit ) && newUnit === 'px' ) {
153
175
  // Convert to pixel value assuming a root size of 16px.
154
176
  newValue = Math.round( quantity * 16 ) + newUnit;
155
- } else if (
156
- [
157
- 'vh',
158
- 'vw',
159
- '%',
160
- 'svw',
161
- 'lvw',
162
- 'dvw',
163
- 'svh',
164
- 'lvh',
165
- 'dvh',
166
- 'vi',
167
- 'svi',
168
- 'lvi',
169
- 'dvi',
170
- 'vb',
171
- 'svb',
172
- 'lvb',
173
- 'dvb',
174
- 'vmin',
175
- 'svmin',
176
- 'lvmin',
177
- 'dvmin',
178
- 'vmax',
179
- 'svmax',
180
- 'lvmax',
181
- 'dvmax',
182
- ].includes( newUnit ) &&
183
- quantity > 100
184
- ) {
185
- // When converting to `%` or viewport-relative units, cap the new value at 100.
186
- newValue = 100 + newUnit;
187
177
  }
188
178
 
189
179
  onChange( {
@@ -209,7 +199,10 @@ function GridLayoutMinimumWidthControl( { layout, onChange } ) {
209
199
  } }
210
200
  onUnitChange={ handleUnitChange }
211
201
  value={ value }
202
+ units={ units }
212
203
  min={ 0 }
204
+ label={ __( 'Minimum column width' ) }
205
+ hideLabelFromVision
213
206
  />
214
207
  </FlexItem>
215
208
  <FlexItem isBlock>
@@ -219,6 +212,8 @@ function GridLayoutMinimumWidthControl( { layout, onChange } ) {
219
212
  min={ 0 }
220
213
  max={ RANGE_CONTROL_MAX_VALUES[ unit ] || 600 }
221
214
  withInputField={ false }
215
+ label={ __( 'Minimum column width' ) }
216
+ hideLabelFromVision
222
217
  />
223
218
  </FlexItem>
224
219
  </Flex>
@@ -231,17 +226,101 @@ function GridLayoutColumnsControl( { layout, onChange } ) {
231
226
  const { columnCount = 3 } = layout;
232
227
 
233
228
  return (
234
- <RangeControl
235
- label={ __( 'Columns' ) }
236
- value={ columnCount }
237
- onChange={ ( value ) =>
238
- onChange( {
239
- ...layout,
240
- columnCount: value,
241
- } )
242
- }
243
- min={ 1 }
244
- max={ 6 }
245
- />
229
+ <fieldset>
230
+ <BaseControl.VisualLabel as="legend">
231
+ { __( 'Columns' ) }
232
+ </BaseControl.VisualLabel>
233
+ <Flex gap={ 4 }>
234
+ <FlexItem isBlock>
235
+ <NumberControl
236
+ size={ '__unstable-large' }
237
+ onChange={ ( value ) => {
238
+ /**
239
+ * If the input is cleared, avoid switching
240
+ * back to "Auto" by setting a value of "1".
241
+ */
242
+ const validValue = value !== '' ? value : '1';
243
+ onChange( {
244
+ ...layout,
245
+ columnCount: validValue,
246
+ } );
247
+ } }
248
+ value={ columnCount }
249
+ min={ 1 }
250
+ label={ __( 'Columns' ) }
251
+ hideLabelFromVision
252
+ />
253
+ </FlexItem>
254
+ <FlexItem isBlock>
255
+ <RangeControl
256
+ value={ parseInt( columnCount, 10 ) } // RangeControl can't deal with strings.
257
+ onChange={ ( value ) =>
258
+ onChange( {
259
+ ...layout,
260
+ columnCount: value,
261
+ } )
262
+ }
263
+ min={ 1 }
264
+ max={ 16 }
265
+ withInputField={ false }
266
+ label={ __( 'Columns' ) }
267
+ hideLabelFromVision
268
+ />
269
+ </FlexItem>
270
+ </Flex>
271
+ </fieldset>
272
+ );
273
+ }
274
+
275
+ // Enables switching between grid types
276
+ function GridLayoutTypeControl( { layout, onChange } ) {
277
+ const { columnCount, minimumColumnWidth } = layout;
278
+
279
+ /**
280
+ * When switching, temporarily save any custom values set on the
281
+ * previous type so we can switch back without loss.
282
+ */
283
+ const [ tempColumnCount, setTempColumnCount ] = useState(
284
+ columnCount || 3
285
+ );
286
+ const [ tempMinimumColumnWidth, setTempMinimumColumnWidth ] = useState(
287
+ minimumColumnWidth || '12rem'
288
+ );
289
+
290
+ const isManual = !! columnCount ? 'manual' : 'auto';
291
+
292
+ const onChangeType = ( value ) => {
293
+ if ( value === 'manual' ) {
294
+ setTempMinimumColumnWidth( minimumColumnWidth || '12rem' );
295
+ } else {
296
+ setTempColumnCount( columnCount || 3 );
297
+ }
298
+ onChange( {
299
+ ...layout,
300
+ columnCount: value === 'manual' ? tempColumnCount : null,
301
+ minimumColumnWidth:
302
+ value === 'auto' ? tempMinimumColumnWidth : null,
303
+ } );
304
+ };
305
+
306
+ return (
307
+ <ToggleGroupControl
308
+ __nextHasNoMarginBottom={ true }
309
+ label={ __( 'Type' ) }
310
+ value={ isManual }
311
+ onChange={ onChangeType }
312
+ isBlock={ true }
313
+ >
314
+ <ToggleGroupControlOption
315
+ key={ 'auto' }
316
+ value="auto"
317
+ label={ __( 'Auto' ) }
318
+ />
319
+ <ToggleGroupControlOption
320
+ key={ 'manual' }
321
+ value="manual"
322
+ label={ __( 'Manual' ) }
323
+ />
324
+ </ToggleGroupControl>
246
325
  );
247
326
  }
@@ -4,8 +4,8 @@
4
4
  import grid from '../grid';
5
5
 
6
6
  describe( 'getLayoutStyle', () => {
7
- it( 'should return a single `grid-template-columns` property if no non-default params are provided', () => {
8
- const expected = `.editor-styles-wrapper .my-container { grid-template-columns: repeat(auto-fill, minmax(min(12rem, 100%), 1fr)); }`;
7
+ it( 'should return only `grid-template-columns` and `container-type` properties if no non-default params are provided', () => {
8
+ const expected = `.editor-styles-wrapper .my-container { grid-template-columns: repeat(auto-fill, minmax(min(12rem, 100%), 1fr)); container-type: inline-size; }`;
9
9
 
10
10
  const result = grid.getLayoutStyle( {
11
11
  selector: '.my-container',
@@ -16,6 +16,20 @@ describe( 'getLayoutStyle', () => {
16
16
  layoutDefinitions: undefined,
17
17
  } );
18
18
 
19
+ expect( result ).toBe( expected );
20
+ } );
21
+ it( 'should return only `grid-template-columns` if columnCount property is provided', () => {
22
+ const expected = `.editor-styles-wrapper .my-container { grid-template-columns: repeat(3, minmax(0, 1fr)); }`;
23
+
24
+ const result = grid.getLayoutStyle( {
25
+ selector: '.my-container',
26
+ layout: { columnCount: 3 },
27
+ style: {},
28
+ blockName: 'test-block',
29
+ hasBlockGapSupport: false,
30
+ layoutDefinitions: undefined,
31
+ } );
32
+
19
33
  expect( result ).toBe( expected );
20
34
  } );
21
35
  } );
@@ -26,6 +26,7 @@ import { usesContextKey } from './components/rich-text/format-edit';
26
26
  import { ExperimentalBlockCanvas } from './components/block-canvas';
27
27
  import { getDuotoneFilter } from './components/duotone/utils';
28
28
  import { useFlashEditableBlocks } from './components/use-flash-editable-blocks';
29
+ import { selectBlockPatternsKey } from './store/private-keys';
29
30
 
30
31
  /**
31
32
  * Private @wordpress/block-editor APIs.
@@ -56,4 +57,5 @@ lock( privateApis, {
56
57
  useReusableBlocksRenameHint,
57
58
  usesContextKey,
58
59
  useFlashEditableBlocks,
60
+ selectBlockPatternsKey,
59
61
  } );