@wordpress/edit-site 4.8.0 → 4.11.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 (204) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/build/components/add-new-template/add-custom-generic-template-modal.js +84 -0
  3. package/build/components/add-new-template/add-custom-generic-template-modal.js.map +1 -0
  4. package/build/components/add-new-template/add-custom-template-modal.js +204 -0
  5. package/build/components/add-new-template/add-custom-template-modal.js.map +1 -0
  6. package/build/components/add-new-template/new-template.js +91 -33
  7. package/build/components/add-new-template/new-template.js.map +1 -1
  8. package/build/components/add-new-template/utils.js +532 -0
  9. package/build/components/add-new-template/utils.js.map +1 -0
  10. package/build/components/block-editor/index.js +1 -3
  11. package/build/components/block-editor/index.js.map +1 -1
  12. package/build/components/code-editor/index.js +17 -4
  13. package/build/components/code-editor/index.js.map +1 -1
  14. package/build/components/editor/index.js +16 -0
  15. package/build/components/editor/index.js.map +1 -1
  16. package/build/components/error-boundary/index.js +6 -0
  17. package/build/components/error-boundary/index.js.map +1 -1
  18. package/build/components/global-styles/context-menu.js +6 -3
  19. package/build/components/global-styles/context-menu.js.map +1 -1
  20. package/build/components/global-styles/dimensions-panel.js +185 -19
  21. package/build/components/global-styles/dimensions-panel.js.map +1 -1
  22. package/build/components/global-styles/global-styles-provider.js +4 -2
  23. package/build/components/global-styles/global-styles-provider.js.map +1 -1
  24. package/build/components/global-styles/hooks.js +11 -2
  25. package/build/components/global-styles/hooks.js.map +1 -1
  26. package/build/components/global-styles/palette.js +2 -1
  27. package/build/components/global-styles/palette.js.map +1 -1
  28. package/build/components/global-styles/screen-block-list.js +4 -1
  29. package/build/components/global-styles/screen-block-list.js.map +1 -1
  30. package/build/components/global-styles/screen-button-color.js +80 -0
  31. package/build/components/global-styles/screen-button-color.js.map +1 -0
  32. package/build/components/global-styles/screen-color-palette.js +13 -17
  33. package/build/components/global-styles/screen-color-palette.js.map +1 -1
  34. package/build/components/global-styles/screen-colors.js +56 -8
  35. package/build/components/global-styles/screen-colors.js.map +1 -1
  36. package/build/components/global-styles/screen-link-color.js +48 -14
  37. package/build/components/global-styles/screen-link-color.js.map +1 -1
  38. package/build/components/global-styles/screen-root.js +4 -2
  39. package/build/components/global-styles/screen-root.js.map +1 -1
  40. package/build/components/global-styles/screen-typography-element.js +4 -0
  41. package/build/components/global-styles/screen-typography-element.js.map +1 -1
  42. package/build/components/global-styles/screen-typography.js +9 -1
  43. package/build/components/global-styles/screen-typography.js.map +1 -1
  44. package/build/components/global-styles/ui.js +11 -0
  45. package/build/components/global-styles/ui.js.map +1 -1
  46. package/build/components/global-styles/use-global-styles-output.js +282 -35
  47. package/build/components/global-styles/use-global-styles-output.js.map +1 -1
  48. package/build/components/global-styles/utils.js +35 -2
  49. package/build/components/global-styles/utils.js.map +1 -1
  50. package/build/components/header/index.js +29 -10
  51. package/build/components/header/index.js.map +1 -1
  52. package/build/components/header/more-menu/site-export.js +4 -1
  53. package/build/components/header/more-menu/site-export.js.map +1 -1
  54. package/build/components/header/undo-redo/redo.js +13 -4
  55. package/build/components/header/undo-redo/redo.js.map +1 -1
  56. package/build/components/header/undo-redo/undo.js +13 -4
  57. package/build/components/header/undo-redo/undo.js.map +1 -1
  58. package/build/components/keyboard-shortcut-help-modal/config.js +17 -0
  59. package/build/components/keyboard-shortcut-help-modal/config.js.map +1 -1
  60. package/build/components/keyboard-shortcut-help-modal/index.js +1 -3
  61. package/build/components/keyboard-shortcut-help-modal/index.js.map +1 -1
  62. package/build/components/sidebar/template-card/index.js +19 -7
  63. package/build/components/sidebar/template-card/index.js.map +1 -1
  64. package/build/components/sidebar/template-card/template-actions.js +64 -0
  65. package/build/components/sidebar/template-card/template-actions.js.map +1 -0
  66. package/build/components/template-details/edit-template-title.js +11 -3
  67. package/build/components/template-details/edit-template-title.js.map +1 -1
  68. package/build/components/template-details/index.js +1 -20
  69. package/build/components/template-details/index.js.map +1 -1
  70. package/build/hooks/index.js +2 -0
  71. package/build/hooks/index.js.map +1 -1
  72. package/build/hooks/template-part-edit.js +86 -0
  73. package/build/hooks/template-part-edit.js.map +1 -0
  74. package/build/store/selectors.js +4 -1
  75. package/build/store/selectors.js.map +1 -1
  76. package/build-module/components/add-new-template/add-custom-generic-template-modal.js +77 -0
  77. package/build-module/components/add-new-template/add-custom-generic-template-modal.js.map +1 -0
  78. package/build-module/components/add-new-template/add-custom-template-modal.js +189 -0
  79. package/build-module/components/add-new-template/add-custom-template-modal.js.map +1 -0
  80. package/build-module/components/add-new-template/new-template.js +90 -34
  81. package/build-module/components/add-new-template/new-template.js.map +1 -1
  82. package/build-module/components/add-new-template/utils.js +503 -0
  83. package/build-module/components/add-new-template/utils.js.map +1 -0
  84. package/build-module/components/block-editor/index.js +1 -2
  85. package/build-module/components/block-editor/index.js.map +1 -1
  86. package/build-module/components/code-editor/index.js +18 -5
  87. package/build-module/components/code-editor/index.js.map +1 -1
  88. package/build-module/components/editor/index.js +16 -0
  89. package/build-module/components/editor/index.js.map +1 -1
  90. package/build-module/components/error-boundary/index.js +5 -0
  91. package/build-module/components/error-boundary/index.js.map +1 -1
  92. package/build-module/components/global-styles/context-menu.js +6 -3
  93. package/build-module/components/global-styles/context-menu.js.map +1 -1
  94. package/build-module/components/global-styles/dimensions-panel.js +185 -20
  95. package/build-module/components/global-styles/dimensions-panel.js.map +1 -1
  96. package/build-module/components/global-styles/global-styles-provider.js +4 -2
  97. package/build-module/components/global-styles/global-styles-provider.js.map +1 -1
  98. package/build-module/components/global-styles/hooks.js +11 -2
  99. package/build-module/components/global-styles/hooks.js.map +1 -1
  100. package/build-module/components/global-styles/palette.js +2 -1
  101. package/build-module/components/global-styles/palette.js.map +1 -1
  102. package/build-module/components/global-styles/screen-block-list.js +4 -1
  103. package/build-module/components/global-styles/screen-block-list.js.map +1 -1
  104. package/build-module/components/global-styles/screen-button-color.js +67 -0
  105. package/build-module/components/global-styles/screen-button-color.js.map +1 -0
  106. package/build-module/components/global-styles/screen-color-palette.js +14 -19
  107. package/build-module/components/global-styles/screen-color-palette.js.map +1 -1
  108. package/build-module/components/global-styles/screen-colors.js +57 -9
  109. package/build-module/components/global-styles/screen-colors.js.map +1 -1
  110. package/build-module/components/global-styles/screen-link-color.js +47 -14
  111. package/build-module/components/global-styles/screen-link-color.js.map +1 -1
  112. package/build-module/components/global-styles/screen-root.js +4 -2
  113. package/build-module/components/global-styles/screen-root.js.map +1 -1
  114. package/build-module/components/global-styles/screen-typography-element.js +4 -0
  115. package/build-module/components/global-styles/screen-typography-element.js.map +1 -1
  116. package/build-module/components/global-styles/screen-typography.js +10 -2
  117. package/build-module/components/global-styles/screen-typography.js.map +1 -1
  118. package/build-module/components/global-styles/ui.js +10 -0
  119. package/build-module/components/global-styles/ui.js.map +1 -1
  120. package/build-module/components/global-styles/use-global-styles-output.js +280 -42
  121. package/build-module/components/global-styles/use-global-styles-output.js.map +1 -1
  122. package/build-module/components/global-styles/utils.js +34 -3
  123. package/build-module/components/global-styles/utils.js.map +1 -1
  124. package/build-module/components/header/index.js +31 -11
  125. package/build-module/components/header/index.js.map +1 -1
  126. package/build-module/components/header/more-menu/site-export.js +4 -1
  127. package/build-module/components/header/more-menu/site-export.js.map +1 -1
  128. package/build-module/components/header/undo-redo/redo.js +9 -3
  129. package/build-module/components/header/undo-redo/redo.js.map +1 -1
  130. package/build-module/components/header/undo-redo/undo.js +9 -3
  131. package/build-module/components/header/undo-redo/undo.js.map +1 -1
  132. package/build-module/components/keyboard-shortcut-help-modal/config.js +17 -0
  133. package/build-module/components/keyboard-shortcut-help-modal/config.js.map +1 -1
  134. package/build-module/components/keyboard-shortcut-help-modal/index.js +1 -2
  135. package/build-module/components/keyboard-shortcut-help-modal/index.js.map +1 -1
  136. package/build-module/components/sidebar/template-card/index.js +18 -7
  137. package/build-module/components/sidebar/template-card/index.js.map +1 -1
  138. package/build-module/components/sidebar/template-card/template-actions.js +49 -0
  139. package/build-module/components/sidebar/template-card/template-actions.js.map +1 -0
  140. package/build-module/components/template-details/edit-template-title.js +12 -3
  141. package/build-module/components/template-details/edit-template-title.js.map +1 -1
  142. package/build-module/components/template-details/index.js +2 -21
  143. package/build-module/components/template-details/index.js.map +1 -1
  144. package/build-module/hooks/index.js +1 -0
  145. package/build-module/hooks/index.js.map +1 -1
  146. package/build-module/hooks/template-part-edit.js +67 -0
  147. package/build-module/hooks/template-part-edit.js.map +1 -0
  148. package/build-module/store/selectors.js +5 -2
  149. package/build-module/store/selectors.js.map +1 -1
  150. package/build-style/style-rtl.css +198 -49
  151. package/build-style/style.css +198 -49
  152. package/package.json +29 -29
  153. package/src/components/add-new-template/add-custom-generic-template-modal.js +97 -0
  154. package/src/components/add-new-template/add-custom-template-modal.js +247 -0
  155. package/src/components/add-new-template/new-template.js +158 -70
  156. package/src/components/add-new-template/style.scss +149 -0
  157. package/src/components/add-new-template/utils.js +538 -0
  158. package/src/components/block-editor/index.js +0 -2
  159. package/src/components/code-editor/index.js +15 -5
  160. package/src/components/editor/index.js +11 -0
  161. package/src/components/error-boundary/index.js +5 -0
  162. package/src/components/global-styles/context-menu.js +3 -0
  163. package/src/components/global-styles/dimensions-panel.js +209 -21
  164. package/src/components/global-styles/global-styles-provider.js +8 -9
  165. package/src/components/global-styles/hooks.js +18 -0
  166. package/src/components/global-styles/palette.js +4 -1
  167. package/src/components/global-styles/screen-block-list.js +10 -1
  168. package/src/components/global-styles/screen-button-color.js +102 -0
  169. package/src/components/global-styles/screen-color-palette.js +25 -27
  170. package/src/components/global-styles/screen-colors.js +58 -7
  171. package/src/components/global-styles/screen-link-color.js +65 -23
  172. package/src/components/global-styles/screen-root.js +8 -2
  173. package/src/components/global-styles/screen-typography-element.js +4 -0
  174. package/src/components/global-styles/screen-typography.js +17 -2
  175. package/src/components/global-styles/style.scss +14 -8
  176. package/src/components/global-styles/test/use-global-styles-output.js +313 -16
  177. package/src/components/global-styles/ui.js +13 -0
  178. package/src/components/global-styles/use-global-styles-output.js +344 -38
  179. package/src/components/global-styles/utils.js +36 -2
  180. package/src/components/header/index.js +42 -17
  181. package/src/components/header/more-menu/site-export.js +3 -0
  182. package/src/components/header/style.scss +58 -8
  183. package/src/components/header/undo-redo/redo.js +6 -1
  184. package/src/components/header/undo-redo/undo.js +6 -1
  185. package/src/components/keyboard-shortcut-help-modal/config.js +12 -0
  186. package/src/components/keyboard-shortcut-help-modal/index.js +1 -2
  187. package/src/components/keyboard-shortcut-help-modal/style.scss +0 -5
  188. package/src/components/list/style.scss +0 -8
  189. package/src/components/sidebar/style.scss +4 -0
  190. package/src/components/sidebar/template-card/index.js +15 -6
  191. package/src/components/sidebar/template-card/style.scss +49 -35
  192. package/src/components/sidebar/template-card/template-actions.js +43 -0
  193. package/src/components/template-details/edit-template-title.js +10 -2
  194. package/src/components/template-details/index.js +4 -21
  195. package/src/components/test/error-boundary.js +38 -0
  196. package/src/hooks/index.js +1 -0
  197. package/src/hooks/template-part-edit.js +82 -0
  198. package/src/store/selectors.js +11 -5
  199. package/src/style.scss +0 -1
  200. package/build/components/edit-template-part-menu-button/index.js +0 -90
  201. package/build/components/edit-template-part-menu-button/index.js.map +0 -1
  202. package/build-module/components/edit-template-part-menu-button/index.js +0 -72
  203. package/build-module/components/edit-template-part-menu-button/index.js.map +0 -1
  204. package/src/components/edit-template-part-menu-button/index.js +0 -82
@@ -6,7 +6,6 @@ import {
6
6
  forEach,
7
7
  get,
8
8
  isEmpty,
9
- isString,
10
9
  kebabCase,
11
10
  pickBy,
12
11
  reduce,
@@ -24,15 +23,27 @@ import {
24
23
  } from '@wordpress/blocks';
25
24
  import { useEffect, useState, useContext } from '@wordpress/element';
26
25
  import { getCSSRules } from '@wordpress/style-engine';
27
- import { __unstablePresetDuotoneFilter as PresetDuotoneFilter } from '@wordpress/block-editor';
26
+ import {
27
+ __unstablePresetDuotoneFilter as PresetDuotoneFilter,
28
+ __experimentalGetGapCSSValue as getGapCSSValue,
29
+ } from '@wordpress/block-editor';
28
30
 
29
31
  /**
30
32
  * Internal dependencies
31
33
  */
32
- import { PRESET_METADATA, ROOT_BLOCK_SELECTOR } from './utils';
34
+ import { PRESET_METADATA, ROOT_BLOCK_SELECTOR, scopeSelector } from './utils';
33
35
  import { GlobalStylesContext } from './context';
34
36
  import { useSetting } from './hooks';
35
37
 
38
+ // List of block support features that can have their related styles
39
+ // generated under their own feature level selector rather than the block's.
40
+ const BLOCK_SUPPORT_FEATURE_LEVEL_SELECTORS = {
41
+ __experimentalBorder: 'border',
42
+ color: 'color',
43
+ spacing: 'spacing',
44
+ typography: 'typography',
45
+ };
46
+
36
47
  function compileStyleValue( uncompiledValue ) {
37
48
  const VARIABLE_REFERENCE_PREFIX = 'var:';
38
49
  const VARIABLE_PATH_SEPARATOR_TOKEN_ATTRIBUTE = '|';
@@ -170,14 +181,26 @@ function flattenTree( input = {}, prefix, token ) {
170
181
  /**
171
182
  * Transform given style tree into a set of style declarations.
172
183
  *
173
- * @param {Object} blockStyles Block styles.
184
+ * @param {Object} blockStyles Block styles.
185
+ *
186
+ * @param {string} selector The selector these declarations should attach to.
187
+ *
188
+ * @param {boolean} useRootPaddingAlign Whether to use CSS custom properties in root selector.
174
189
  *
175
190
  * @return {Array} An array of style declarations.
176
191
  */
177
- function getStylesDeclarations( blockStyles = {} ) {
192
+ function getStylesDeclarations(
193
+ blockStyles = {},
194
+ selector = '',
195
+ useRootPaddingAlign
196
+ ) {
197
+ const isRoot = ROOT_BLOCK_SELECTOR === selector;
178
198
  const output = reduce(
179
199
  STYLE_PROPERTY,
180
- ( declarations, { value, properties, useEngine }, key ) => {
200
+ ( declarations, { value, properties, useEngine, rootOnly }, key ) => {
201
+ if ( rootOnly && ! isRoot ) {
202
+ return declarations;
203
+ }
181
204
  const pathToValue = value;
182
205
  if ( first( pathToValue ) === 'elements' || useEngine ) {
183
206
  return declarations;
@@ -185,7 +208,7 @@ function getStylesDeclarations( blockStyles = {} ) {
185
208
 
186
209
  const styleValue = get( blockStyles, pathToValue );
187
210
 
188
- if ( !! properties && ! isString( styleValue ) ) {
211
+ if ( !! properties && typeof styleValue !== 'string' ) {
189
212
  Object.entries( properties ).forEach( ( entry ) => {
190
213
  const [ name, prop ] = entry;
191
214
 
@@ -195,13 +218,22 @@ function getStylesDeclarations( blockStyles = {} ) {
195
218
  return;
196
219
  }
197
220
 
198
- const cssProperty = kebabCase( name );
221
+ const cssProperty = name.startsWith( '--' )
222
+ ? name
223
+ : kebabCase( name );
199
224
  declarations.push(
200
225
  `${ cssProperty }: ${ compileStyleValue(
201
226
  get( styleValue, [ prop ] )
202
227
  ) }`
203
228
  );
204
229
  } );
230
+ } else if (
231
+ key === '--wp--style--root--padding' &&
232
+ typeof styleValue === 'string'
233
+ ) {
234
+ // Root-level padding styles don't currently support strings with CSS shorthand values.
235
+ // This may change: https://github.com/WordPress/gutenberg/issues/40132.
236
+ return declarations;
205
237
  } else if ( get( blockStyles, pathToValue, false ) ) {
206
238
  const cssProperty = key.startsWith( '--' )
207
239
  ? key
@@ -218,6 +250,10 @@ function getStylesDeclarations( blockStyles = {} ) {
218
250
  []
219
251
  );
220
252
 
253
+ if ( isRoot && useRootPaddingAlign ) {
254
+ return output;
255
+ }
256
+
221
257
  // The goal is to move everything to server side generated engine styles
222
258
  // This is temporary as we absorb more and more styles into the engine.
223
259
  const extraRules = getCSSRules( blockStyles );
@@ -231,6 +267,135 @@ function getStylesDeclarations( blockStyles = {} ) {
231
267
  return output;
232
268
  }
233
269
 
270
+ /**
271
+ * Get generated CSS for layout styles by looking up layout definitions provided
272
+ * in theme.json, and outputting common layout styles, and specific blockGap values.
273
+ *
274
+ * @param {Object} props
275
+ * @param {Object} props.tree A theme.json tree containing layout definitions.
276
+ * @param {Object} props.style A style object containing spacing values.
277
+ * @param {string} props.selector Selector used to group together layout styling rules.
278
+ * @param {boolean} props.hasBlockGapSupport Whether or not the theme opts-in to blockGap support.
279
+ * @param {boolean} props.hasFallbackGapSupport Whether or not the theme allows fallback gap styles.
280
+ * @param {?string} props.fallbackGapValue An optional fallback gap value if no real gap value is available.
281
+ * @return {string} Generated CSS rules for the layout styles.
282
+ */
283
+ export function getLayoutStyles( {
284
+ tree,
285
+ style,
286
+ selector,
287
+ hasBlockGapSupport,
288
+ hasFallbackGapSupport,
289
+ fallbackGapValue,
290
+ } ) {
291
+ let ruleset = '';
292
+ let gapValue = hasBlockGapSupport
293
+ ? getGapCSSValue( style?.spacing?.blockGap )
294
+ : '';
295
+
296
+ // Ensure a fallback gap value for the root layout definitions,
297
+ // and use a fallback value if one is provided for the current block.
298
+ if ( hasFallbackGapSupport ) {
299
+ if ( selector === ROOT_BLOCK_SELECTOR ) {
300
+ gapValue = ! gapValue ? '0.5em' : gapValue;
301
+ } else if ( ! hasBlockGapSupport && fallbackGapValue ) {
302
+ gapValue = fallbackGapValue;
303
+ }
304
+ }
305
+
306
+ if ( gapValue && tree?.settings?.layout?.definitions ) {
307
+ Object.values( tree.settings.layout.definitions ).forEach(
308
+ ( { className, name, spacingStyles } ) => {
309
+ // Allow skipping default layout for themes that opt-in to block styles, but opt-out of blockGap.
310
+ if ( ! hasBlockGapSupport && 'default' === name ) {
311
+ return;
312
+ }
313
+
314
+ if ( spacingStyles?.length ) {
315
+ spacingStyles.forEach( ( spacingStyle ) => {
316
+ const declarations = [];
317
+
318
+ if ( spacingStyle.rules ) {
319
+ Object.entries( spacingStyle.rules ).forEach(
320
+ ( [ cssProperty, cssValue ] ) => {
321
+ declarations.push(
322
+ `${ cssProperty }: ${
323
+ cssValue ? cssValue : gapValue
324
+ }`
325
+ );
326
+ }
327
+ );
328
+ }
329
+
330
+ if ( declarations.length ) {
331
+ const combinedSelector =
332
+ selector === ROOT_BLOCK_SELECTOR
333
+ ? `${ selector } .${ className }${
334
+ spacingStyle?.selector || ''
335
+ }`
336
+ : `${ selector }.${ className }${
337
+ spacingStyle?.selector || ''
338
+ }`;
339
+ ruleset += `${ combinedSelector } { ${ declarations.join(
340
+ '; '
341
+ ) }; }`;
342
+ }
343
+ } );
344
+ }
345
+ }
346
+ );
347
+ // For backwards compatibility, ensure the legacy block gap CSS variable is still available.
348
+ if ( selector === ROOT_BLOCK_SELECTOR && hasBlockGapSupport ) {
349
+ ruleset += `${ selector } { --wp--style--block-gap: ${ gapValue }; }`;
350
+ }
351
+ }
352
+
353
+ // Output base styles
354
+ if (
355
+ selector === ROOT_BLOCK_SELECTOR &&
356
+ tree?.settings?.layout?.definitions
357
+ ) {
358
+ const validDisplayModes = [ 'block', 'flex', 'grid' ];
359
+ Object.values( tree.settings.layout.definitions ).forEach(
360
+ ( { className, displayMode, baseStyles } ) => {
361
+ if (
362
+ displayMode &&
363
+ validDisplayModes.includes( displayMode )
364
+ ) {
365
+ ruleset += `${ selector } .${ className } { display:${ displayMode }; }`;
366
+ }
367
+
368
+ if ( baseStyles?.length ) {
369
+ baseStyles.forEach( ( baseStyle ) => {
370
+ const declarations = [];
371
+
372
+ if ( baseStyle.rules ) {
373
+ Object.entries( baseStyle.rules ).forEach(
374
+ ( [ cssProperty, cssValue ] ) => {
375
+ declarations.push(
376
+ `${ cssProperty }: ${ cssValue }`
377
+ );
378
+ }
379
+ );
380
+ }
381
+
382
+ if ( declarations.length ) {
383
+ const combinedSelector = `${ selector } .${ className }${
384
+ baseStyle?.selector || ''
385
+ }`;
386
+ ruleset += `${ combinedSelector } { ${ declarations.join(
387
+ '; '
388
+ ) }; }`;
389
+ }
390
+ } );
391
+ }
392
+ }
393
+ );
394
+ }
395
+
396
+ return ruleset;
397
+ }
398
+
234
399
  export const getNodesWithStyles = ( tree, blockSelectors ) => {
235
400
  const nodes = [];
236
401
 
@@ -267,9 +432,12 @@ export const getNodesWithStyles = ( tree, blockSelectors ) => {
267
432
  const blockStyles = pickStyleKeys( node );
268
433
  if ( !! blockStyles && !! blockSelectors?.[ blockName ]?.selector ) {
269
434
  nodes.push( {
270
- styles: blockStyles,
271
- selector: blockSelectors[ blockName ].selector,
272
435
  duotoneSelector: blockSelectors[ blockName ].duotoneSelector,
436
+ fallbackGapValue: blockSelectors[ blockName ].fallbackGapValue,
437
+ hasLayoutSupport: blockSelectors[ blockName ].hasLayoutSupport,
438
+ selector: blockSelectors[ blockName ].selector,
439
+ styles: blockStyles,
440
+ featureSelectors: blockSelectors[ blockName ].featureSelectors,
273
441
  } );
274
442
  }
275
443
 
@@ -283,7 +451,14 @@ export const getNodesWithStyles = ( tree, blockSelectors ) => {
283
451
  styles: value,
284
452
  selector: blockSelectors[ blockName ].selector
285
453
  .split( ',' )
286
- .map( ( sel ) => sel + ' ' + ELEMENTS[ elementName ] )
454
+ .map( ( sel ) => {
455
+ const elementSelectors =
456
+ ELEMENTS[ elementName ].split( ',' );
457
+ return elementSelectors.map(
458
+ ( elementSelector ) =>
459
+ sel + ' ' + elementSelector
460
+ );
461
+ } )
287
462
  .join( ',' ),
288
463
  } );
289
464
  }
@@ -357,9 +532,15 @@ export const toCustomProperties = ( tree, blockSelectors ) => {
357
532
  return ruleset;
358
533
  };
359
534
 
360
- export const toStyles = ( tree, blockSelectors, hasBlockGapSupport ) => {
535
+ export const toStyles = (
536
+ tree,
537
+ blockSelectors,
538
+ hasBlockGapSupport,
539
+ hasFallbackGapSupport
540
+ ) => {
361
541
  const nodesWithStyles = getNodesWithStyles( tree, blockSelectors );
362
542
  const nodesWithSettings = getNodesWithSettings( tree, blockSelectors );
543
+ const useRootPaddingAlign = tree?.settings?.useRootPaddingAwareAlignments;
363
544
 
364
545
  /*
365
546
  * Reset default browser margin on the root body element.
@@ -370,31 +551,126 @@ export const toStyles = ( tree, blockSelectors, hasBlockGapSupport ) => {
370
551
  * @link https://github.com/WordPress/gutenberg/issues/36147.
371
552
  */
372
553
  let ruleset = 'body {margin: 0;}';
373
- nodesWithStyles.forEach( ( { selector, duotoneSelector, styles } ) => {
374
- const duotoneStyles = {};
375
- if ( styles?.filter ) {
376
- duotoneStyles.filter = styles.filter;
377
- delete styles.filter;
378
- }
379
554
 
380
- // Process duotone styles (they use color.__experimentalDuotone selector).
381
- if ( duotoneSelector ) {
382
- const duotoneDeclarations = getStylesDeclarations( duotoneStyles );
383
- if ( duotoneDeclarations.length === 0 ) {
384
- return;
555
+ if ( useRootPaddingAlign ) {
556
+ ruleset =
557
+ 'body { margin: 0; padding-right: 0; padding-left: 0; padding-top: var(--wp--style--root--padding-top); padding-bottom: var(--wp--style--root--padding-bottom) } .has-global-padding { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); } .has-global-padding > .alignfull { margin-right: calc(var(--wp--style--root--padding-right) * -1); margin-left: calc(var(--wp--style--root--padding-left) * -1); } .has-global-padding > .alignfull > :where([class*="wp-block-"]:not(.alignfull):not([class*="__"]),p,h1,h2,h3,h4,h5,h6,ul,ol) { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }';
558
+ }
559
+
560
+ nodesWithStyles.forEach(
561
+ ( {
562
+ selector,
563
+ duotoneSelector,
564
+ styles,
565
+ fallbackGapValue,
566
+ hasLayoutSupport,
567
+ featureSelectors,
568
+ } ) => {
569
+ // Process styles for block support features with custom feature level
570
+ // CSS selectors set.
571
+ if ( featureSelectors ) {
572
+ Object.entries( featureSelectors ).forEach(
573
+ ( [ featureName, featureSelector ] ) => {
574
+ if ( styles?.[ featureName ] ) {
575
+ const featureStyles = {
576
+ [ featureName ]: styles[ featureName ],
577
+ };
578
+ const featureDeclarations =
579
+ getStylesDeclarations( featureStyles );
580
+ delete styles[ featureName ];
581
+
582
+ if ( !! featureDeclarations.length ) {
583
+ ruleset =
584
+ ruleset +
585
+ `${ featureSelector }{${ featureDeclarations.join(
586
+ ';'
587
+ ) } }`;
588
+ }
589
+ }
590
+ }
591
+ );
385
592
  }
386
- ruleset =
387
- ruleset +
388
- `${ duotoneSelector }{${ duotoneDeclarations.join( ';' ) };}`;
389
- }
390
593
 
391
- // Process the remaning block styles (they use either normal block class or __experimentalSelector).
392
- const declarations = getStylesDeclarations( styles );
393
- if ( declarations.length === 0 ) {
394
- return;
594
+ const duotoneStyles = {};
595
+ if ( styles?.filter ) {
596
+ duotoneStyles.filter = styles.filter;
597
+ delete styles.filter;
598
+ }
599
+
600
+ // Process duotone styles (they use color.__experimentalDuotone selector).
601
+ if ( duotoneSelector ) {
602
+ const duotoneDeclarations =
603
+ getStylesDeclarations( duotoneStyles );
604
+ if ( duotoneDeclarations.length === 0 ) {
605
+ return;
606
+ }
607
+ ruleset =
608
+ ruleset +
609
+ `${ duotoneSelector }{${ duotoneDeclarations.join(
610
+ ';'
611
+ ) };}`;
612
+ }
613
+
614
+ // Process blockGap and layout styles.
615
+ if ( ROOT_BLOCK_SELECTOR === selector || hasLayoutSupport ) {
616
+ ruleset += getLayoutStyles( {
617
+ tree,
618
+ style: styles,
619
+ selector,
620
+ hasBlockGapSupport,
621
+ hasFallbackGapSupport,
622
+ fallbackGapValue,
623
+ } );
624
+ }
625
+
626
+ // Process the remaining block styles (they use either normal block class or __experimentalSelector).
627
+ const declarations = getStylesDeclarations(
628
+ styles,
629
+ selector,
630
+ useRootPaddingAlign
631
+ );
632
+ if ( declarations?.length ) {
633
+ ruleset =
634
+ ruleset + `${ selector }{${ declarations.join( ';' ) };}`;
635
+ }
636
+
637
+ // Check for pseudo selector in `styles` and handle separately.
638
+ const pseudoSelectorStyles = Object.entries( styles ).filter(
639
+ ( [ key ] ) => key.startsWith( ':' )
640
+ );
641
+
642
+ if ( pseudoSelectorStyles?.length ) {
643
+ pseudoSelectorStyles.forEach(
644
+ ( [ pseudoKey, pseudoStyle ] ) => {
645
+ const pseudoDeclarations =
646
+ getStylesDeclarations( pseudoStyle );
647
+
648
+ if ( ! pseudoDeclarations?.length ) {
649
+ return;
650
+ }
651
+
652
+ // `selector` maybe provided in a form
653
+ // where block level selectors have sub element
654
+ // selectors appended to them as a comma separated
655
+ // string.
656
+ // e.g. `h1 a,h2 a,h3 a,h4 a,h5 a,h6 a`;
657
+ // Split and append pseudo selector to create
658
+ // the proper rules to target the elements.
659
+ const _selector = selector
660
+ .split( ',' )
661
+ .map( ( sel ) => sel + pseudoKey )
662
+ .join( ',' );
663
+
664
+ const pseudoRule = `${ _selector }{${ pseudoDeclarations.join(
665
+ ';'
666
+ ) };}`;
667
+
668
+ ruleset = ruleset + pseudoRule;
669
+ }
670
+ );
671
+ }
395
672
  }
396
- ruleset = ruleset + `${ selector }{${ declarations.join( ';' ) };}`;
397
- } );
673
+ );
398
674
 
399
675
  /* Add alignment / layout styles */
400
676
  ruleset =
@@ -408,12 +684,15 @@ export const toStyles = ( tree, blockSelectors, hasBlockGapSupport ) => {
408
684
  '.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }';
409
685
 
410
686
  if ( hasBlockGapSupport ) {
687
+ // Use fallback of `0.5em` just in case, however if there is blockGap support, there should nearly always be a real value.
688
+ const gapValue =
689
+ getGapCSSValue( tree?.styles?.spacing?.blockGap ) || '0.5em';
411
690
  ruleset =
412
691
  ruleset +
413
692
  '.wp-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }';
414
693
  ruleset =
415
694
  ruleset +
416
- '.wp-site-blocks > * + * { margin-block-start: var( --wp--style--block-gap ); }';
695
+ `.wp-site-blocks > * + * { margin-block-start: ${ gapValue }; }`;
417
696
  }
418
697
 
419
698
  nodesWithSettings.forEach( ( { selector, presets } ) => {
@@ -438,7 +717,7 @@ export function toSvgFilters( tree, blockSelectors ) {
438
717
  } );
439
718
  }
440
719
 
441
- const getBlockSelectors = ( blockTypes ) => {
720
+ export const getBlockSelectors = ( blockTypes ) => {
442
721
  const result = {};
443
722
  blockTypes.forEach( ( blockType ) => {
444
723
  const name = blockType.name;
@@ -447,10 +726,35 @@ const getBlockSelectors = ( blockTypes ) => {
447
726
  '.wp-block-' + name.replace( 'core/', '' ).replace( '/', '-' );
448
727
  const duotoneSelector =
449
728
  blockType?.supports?.color?.__experimentalDuotone ?? null;
729
+ const hasLayoutSupport = !! blockType?.supports?.__experimentalLayout;
730
+ const fallbackGapValue =
731
+ blockType?.supports?.spacing?.blockGap?.__experimentalDefault;
732
+
733
+ // For each block support feature add any custom selectors.
734
+ const featureSelectors = {};
735
+ Object.entries( BLOCK_SUPPORT_FEATURE_LEVEL_SELECTORS ).forEach(
736
+ ( [ featureKey, featureName ] ) => {
737
+ const featureSelector =
738
+ blockType?.supports?.[ featureKey ]?.__experimentalSelector;
739
+
740
+ if ( featureSelector ) {
741
+ featureSelectors[ featureName ] = scopeSelector(
742
+ selector,
743
+ featureSelector
744
+ );
745
+ }
746
+ }
747
+ );
748
+
450
749
  result[ name ] = {
750
+ duotoneSelector,
751
+ fallbackGapValue,
752
+ featureSelectors: Object.keys( featureSelectors ).length
753
+ ? featureSelectors
754
+ : undefined,
755
+ hasLayoutSupport,
451
756
  name,
452
757
  selector,
453
- duotoneSelector,
454
758
  };
455
759
  } );
456
760
 
@@ -464,6 +768,7 @@ export function useGlobalStylesOutput() {
464
768
  const { merged: mergedConfig } = useContext( GlobalStylesContext );
465
769
  const [ blockGap ] = useSetting( 'spacing.blockGap' );
466
770
  const hasBlockGapSupport = blockGap !== null;
771
+ const hasFallbackGapSupport = ! hasBlockGapSupport; // This setting isn't useful yet: it exists as a placeholder for a future explicit fallback styles support.
467
772
 
468
773
  useEffect( () => {
469
774
  if ( ! mergedConfig?.styles || ! mergedConfig?.settings ) {
@@ -478,7 +783,8 @@ export function useGlobalStylesOutput() {
478
783
  const globalStyles = toStyles(
479
784
  mergedConfig,
480
785
  blockSelectors,
481
- hasBlockGapSupport
786
+ hasBlockGapSupport,
787
+ hasFallbackGapSupport
482
788
  );
483
789
  const filters = toSvgFilters( mergedConfig, blockSelectors );
484
790
  setStylesheets( [
@@ -493,7 +799,7 @@ export function useGlobalStylesOutput() {
493
799
  ] );
494
800
  setSettings( mergedConfig.settings );
495
801
  setSvgFilters( filters );
496
- }, [ mergedConfig ] );
802
+ }, [ hasBlockGapSupport, hasFallbackGapSupport, mergedConfig ] );
497
803
 
498
804
  return [ stylesheets, settings, svgFilters, hasBlockGapSupport ];
499
805
  }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * External dependencies
3
3
  */
4
- import { get, find, isString } from 'lodash';
4
+ import { get, find } from 'lodash';
5
5
 
6
6
  /* Supporting data. */
7
7
  export const ROOT_BLOCK_NAME = 'root';
@@ -11,6 +11,7 @@ export const ROOT_BLOCK_SUPPORTS = [
11
11
  'backgroundColor',
12
12
  'color',
13
13
  'linkColor',
14
+ 'buttonColor',
14
15
  'fontFamily',
15
16
  'fontSize',
16
17
  'fontStyle',
@@ -75,6 +76,8 @@ const STYLE_PATH_TO_CSS_VAR_INFIX = {
75
76
  'color.background': 'color',
76
77
  'color.text': 'color',
77
78
  'elements.link.color.text': 'color',
79
+ 'elements.button.color.text': 'color',
80
+ 'elements.button.backgroundColor': 'background-color',
78
81
  'color.gradient': 'gradient',
79
82
  'typography.fontSize': 'font-size',
80
83
  'typography.fontFamily': 'font-family',
@@ -210,7 +213,7 @@ function getValueFromCustomVariable( features, blockName, variable, path ) {
210
213
  }
211
214
 
212
215
  export function getValueFromVariable( features, blockName, variable ) {
213
- if ( ! variable || ! isString( variable ) ) {
216
+ if ( ! variable || typeof variable !== 'string' ) {
214
217
  return variable;
215
218
  }
216
219
  const USER_VALUE_PREFIX = 'var:';
@@ -252,3 +255,34 @@ export function getValueFromVariable( features, blockName, variable ) {
252
255
  }
253
256
  return variable;
254
257
  }
258
+
259
+ /**
260
+ * Function that scopes a selector with another one. This works a bit like
261
+ * SCSS nesting except the `&` operator isn't supported.
262
+ *
263
+ * @example
264
+ * ```js
265
+ * const scope = '.a, .b .c';
266
+ * const selector = '> .x, .y';
267
+ * const merged = scopeSelector( scope, selector );
268
+ * // merged is '.a > .x, .a .y, .b .c > .x, .b .c .y'
269
+ * ```
270
+ *
271
+ * @param {string} scope Selector to scope to.
272
+ * @param {string} selector Original selector.
273
+ *
274
+ * @return {string} Scoped selector.
275
+ */
276
+ export function scopeSelector( scope, selector ) {
277
+ const scopes = scope.split( ',' );
278
+ const selectors = selector.split( ',' );
279
+
280
+ const selectorsScoped = [];
281
+ scopes.forEach( ( outer ) => {
282
+ selectors.forEach( ( inner ) => {
283
+ selectorsScoped.push( `${ outer.trim() } ${ inner.trim() }` );
284
+ } );
285
+ } );
286
+
287
+ return selectorsScoped.join( ', ' );
288
+ }
@@ -6,6 +6,7 @@ import { useViewportMatch } from '@wordpress/compose';
6
6
  import {
7
7
  ToolSelector,
8
8
  __experimentalPreviewOptions as PreviewOptions,
9
+ NavigableToolbar,
9
10
  } from '@wordpress/block-editor';
10
11
  import { useSelect, useDispatch } from '@wordpress/data';
11
12
  import { PinnedItems } from '@wordpress/interface';
@@ -105,49 +106,73 @@ export default function Header( {
105
106
 
106
107
  const isFocusMode = templateType === 'wp_template_part';
107
108
 
109
+ /* translators: button label text should, if possible, be under 16 characters. */
110
+ const longLabel = _x(
111
+ 'Toggle block inserter',
112
+ 'Generic label for block inserter button'
113
+ );
114
+ const shortLabel = ! isInserterOpen ? __( 'Add' ) : __( 'Close' );
115
+
108
116
  return (
109
117
  <div className="edit-site-header">
110
- <div className="edit-site-header_start">
118
+ <NavigableToolbar
119
+ className="edit-site-header_start"
120
+ aria-label={ __( 'Document tools' ) }
121
+ >
111
122
  <div className="edit-site-header__toolbar">
112
- <Button
123
+ <ToolbarItem
113
124
  ref={ inserterButton }
125
+ as={ Button }
126
+ className="edit-site-header-toolbar__inserter-toggle"
114
127
  variant="primary"
115
128
  isPressed={ isInserterOpen }
116
- className="edit-site-header-toolbar__inserter-toggle"
117
- disabled={ ! isVisualMode }
118
129
  onMouseDown={ preventDefault }
119
130
  onClick={ openInserter }
131
+ disabled={ ! isVisualMode }
120
132
  icon={ plus }
121
- label={ _x(
122
- 'Toggle block inserter',
123
- 'Generic label for block inserter button'
124
- ) }
125
- >
126
- { showIconLabels &&
127
- ( ! isInserterOpen ? __( 'Add' ) : __( 'Close' ) ) }
128
- </Button>
133
+ label={ showIconLabels ? shortLabel : longLabel }
134
+ showTooltip={ ! showIconLabels }
135
+ />
129
136
  { isLargeViewport && (
130
137
  <>
131
138
  <ToolbarItem
132
139
  as={ ToolSelector }
140
+ showTooltip={ ! showIconLabels }
141
+ variant={
142
+ showIconLabels ? 'tertiary' : undefined
143
+ }
133
144
  disabled={ ! isVisualMode }
134
145
  />
135
- <UndoButton />
136
- <RedoButton />
137
- <Button
146
+ <ToolbarItem
147
+ as={ UndoButton }
148
+ showTooltip={ ! showIconLabels }
149
+ variant={
150
+ showIconLabels ? 'tertiary' : undefined
151
+ }
152
+ />
153
+ <ToolbarItem
154
+ as={ RedoButton }
155
+ showTooltip={ ! showIconLabels }
156
+ variant={
157
+ showIconLabels ? 'tertiary' : undefined
158
+ }
159
+ />
160
+ <ToolbarItem
161
+ as={ Button }
138
162
  className="edit-site-header-toolbar__list-view-toggle"
139
- disabled={ ! isVisualMode }
140
163
  icon={ listView }
164
+ disabled={ ! isVisualMode }
141
165
  isPressed={ isListViewOpen }
142
166
  /* translators: button label text should, if possible, be under 16 characters. */
143
167
  label={ __( 'List View' ) }
144
168
  onClick={ toggleListView }
145
169
  shortcut={ listViewShortcut }
170
+ showTooltip={ ! showIconLabels }
146
171
  />
147
172
  </>
148
173
  ) }
149
174
  </div>
150
- </div>
175
+ </NavigableToolbar>
151
176
 
152
177
  <div className="edit-site-header_center">
153
178
  <DocumentActions