@wordpress/block-editor 11.6.0 → 11.7.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 (193) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +4 -0
  3. package/build/components/block-list/block-html.js +1 -3
  4. package/build/components/block-list/block-html.js.map +1 -1
  5. package/build/components/block-preview/auto.js +6 -23
  6. package/build/components/block-preview/auto.js.map +1 -1
  7. package/build/components/editor-styles/index.js +20 -2
  8. package/build/components/editor-styles/index.js.map +1 -1
  9. package/build/components/global-styles/color-panel.js +583 -0
  10. package/build/components/global-styles/color-panel.js.map +1 -0
  11. package/build/components/global-styles/dimensions-panel.js +8 -30
  12. package/build/components/global-styles/dimensions-panel.js.map +1 -1
  13. package/build/components/global-styles/get-block-css-selector.js +129 -0
  14. package/build/components/global-styles/get-block-css-selector.js.map +1 -0
  15. package/build/components/global-styles/hooks.js +53 -1
  16. package/build/components/global-styles/hooks.js.map +1 -1
  17. package/build/components/global-styles/index.js +18 -2
  18. package/build/components/global-styles/index.js.map +1 -1
  19. package/build/components/global-styles/typography-panel.js +9 -35
  20. package/build/components/global-styles/typography-panel.js.map +1 -1
  21. package/build/components/global-styles/use-global-styles-output.js +160 -86
  22. package/build/components/global-styles/use-global-styles-output.js.map +1 -1
  23. package/build/components/global-styles/utils.js +2 -1
  24. package/build/components/global-styles/utils.js.map +1 -1
  25. package/build/components/image-size-control/index.js +8 -5
  26. package/build/components/image-size-control/index.js.map +1 -1
  27. package/build/components/inspector-controls-tabs/position-controls-panel.js +43 -7
  28. package/build/components/inspector-controls-tabs/position-controls-panel.js.map +1 -1
  29. package/build/components/line-height-control/index.js +15 -1
  30. package/build/components/line-height-control/index.js.map +1 -1
  31. package/build/components/list-view/appender.js +105 -0
  32. package/build/components/list-view/appender.js.map +1 -0
  33. package/build/components/list-view/block.js +5 -5
  34. package/build/components/list-view/block.js.map +1 -1
  35. package/build/components/list-view/branch.js +25 -5
  36. package/build/components/list-view/branch.js.map +1 -1
  37. package/build/components/list-view/index.js +37 -13
  38. package/build/components/list-view/index.js.map +1 -1
  39. package/build/components/media-replace-flow/index.js +13 -4
  40. package/build/components/media-replace-flow/index.js.map +1 -1
  41. package/build/components/rich-text/format-edit.js +2 -30
  42. package/build/components/rich-text/format-edit.js.map +1 -1
  43. package/build/components/writing-flow/use-input.js +4 -8
  44. package/build/components/writing-flow/use-input.js.map +1 -1
  45. package/build/hooks/border.js +0 -1
  46. package/build/hooks/border.js.map +1 -1
  47. package/build/hooks/color.js +92 -229
  48. package/build/hooks/color.js.map +1 -1
  49. package/build/hooks/content-lock-ui.js +4 -2
  50. package/build/hooks/content-lock-ui.js.map +1 -1
  51. package/build/hooks/{color-panel.js → contrast-checker.js} +11 -49
  52. package/build/hooks/contrast-checker.js.map +1 -0
  53. package/build/hooks/dimensions.js +0 -1
  54. package/build/hooks/dimensions.js.map +1 -1
  55. package/build/hooks/duotone.js +3 -1
  56. package/build/hooks/duotone.js.map +1 -1
  57. package/build/hooks/position.js +2 -2
  58. package/build/hooks/position.js.map +1 -1
  59. package/build/hooks/style.js +23 -26
  60. package/build/hooks/style.js.map +1 -1
  61. package/build/hooks/typography.js +0 -1
  62. package/build/hooks/typography.js.map +1 -1
  63. package/build/hooks/utils.js +25 -76
  64. package/build/hooks/utils.js.map +1 -1
  65. package/build/layouts/grid.js +165 -0
  66. package/build/layouts/grid.js.map +1 -0
  67. package/build/layouts/index.js +3 -1
  68. package/build/layouts/index.js.map +1 -1
  69. package/build/private-apis.js +4 -1
  70. package/build/private-apis.js.map +1 -1
  71. package/build/utils/object.js +76 -0
  72. package/build/utils/object.js.map +1 -0
  73. package/build-module/components/block-list/block-html.js +1 -3
  74. package/build-module/components/block-list/block-html.js.map +1 -1
  75. package/build-module/components/block-preview/auto.js +6 -22
  76. package/build-module/components/block-preview/auto.js.map +1 -1
  77. package/build-module/components/editor-styles/index.js +19 -2
  78. package/build-module/components/editor-styles/index.js.map +1 -1
  79. package/build-module/components/global-styles/color-panel.js +554 -0
  80. package/build-module/components/global-styles/color-panel.js.map +1 -0
  81. package/build-module/components/global-styles/dimensions-panel.js +7 -30
  82. package/build-module/components/global-styles/dimensions-panel.js.map +1 -1
  83. package/build-module/components/global-styles/get-block-css-selector.js +120 -0
  84. package/build-module/components/global-styles/get-block-css-selector.js.map +1 -0
  85. package/build-module/components/global-styles/hooks.js +51 -1
  86. package/build-module/components/global-styles/hooks.js.map +1 -1
  87. package/build-module/components/global-styles/index.js +3 -1
  88. package/build-module/components/global-styles/index.js.map +1 -1
  89. package/build-module/components/global-styles/typography-panel.js +8 -35
  90. package/build-module/components/global-styles/typography-panel.js.map +1 -1
  91. package/build-module/components/global-styles/use-global-styles-output.js +161 -87
  92. package/build-module/components/global-styles/use-global-styles-output.js.map +1 -1
  93. package/build-module/components/global-styles/utils.js +2 -1
  94. package/build-module/components/global-styles/utils.js.map +1 -1
  95. package/build-module/components/image-size-control/index.js +8 -5
  96. package/build-module/components/image-size-control/index.js.map +1 -1
  97. package/build-module/components/inspector-controls-tabs/position-controls-panel.js +42 -7
  98. package/build-module/components/inspector-controls-tabs/position-controls-panel.js.map +1 -1
  99. package/build-module/components/line-height-control/index.js +15 -1
  100. package/build-module/components/line-height-control/index.js.map +1 -1
  101. package/build-module/components/list-view/appender.js +88 -0
  102. package/build-module/components/list-view/appender.js.map +1 -0
  103. package/build-module/components/list-view/block.js +5 -4
  104. package/build-module/components/list-view/block.js.map +1 -1
  105. package/build-module/components/list-view/branch.js +22 -5
  106. package/build-module/components/list-view/branch.js.map +1 -1
  107. package/build-module/components/list-view/index.js +32 -12
  108. package/build-module/components/list-view/index.js.map +1 -1
  109. package/build-module/components/media-replace-flow/index.js +12 -4
  110. package/build-module/components/media-replace-flow/index.js.map +1 -1
  111. package/build-module/components/rich-text/format-edit.js +3 -31
  112. package/build-module/components/rich-text/format-edit.js.map +1 -1
  113. package/build-module/components/writing-flow/use-input.js +4 -8
  114. package/build-module/components/writing-flow/use-input.js.map +1 -1
  115. package/build-module/hooks/border.js +0 -1
  116. package/build-module/hooks/border.js.map +1 -1
  117. package/build-module/hooks/color.js +90 -232
  118. package/build-module/hooks/color.js.map +1 -1
  119. package/build-module/hooks/content-lock-ui.js +4 -2
  120. package/build-module/hooks/content-lock-ui.js.map +1 -1
  121. package/build-module/hooks/{color-panel.js → contrast-checker.js} +10 -44
  122. package/build-module/hooks/contrast-checker.js.map +1 -0
  123. package/build-module/hooks/dimensions.js +0 -1
  124. package/build-module/hooks/dimensions.js.map +1 -1
  125. package/build-module/hooks/duotone.js +4 -2
  126. package/build-module/hooks/duotone.js.map +1 -1
  127. package/build-module/hooks/position.js +3 -3
  128. package/build-module/hooks/position.js.map +1 -1
  129. package/build-module/hooks/style.js +23 -26
  130. package/build-module/hooks/style.js.map +1 -1
  131. package/build-module/hooks/typography.js +0 -1
  132. package/build-module/hooks/typography.js.map +1 -1
  133. package/build-module/hooks/utils.js +23 -73
  134. package/build-module/hooks/utils.js.map +1 -1
  135. package/build-module/layouts/grid.js +151 -0
  136. package/build-module/layouts/grid.js.map +1 -0
  137. package/build-module/layouts/index.js +2 -1
  138. package/build-module/layouts/index.js.map +1 -1
  139. package/build-module/private-apis.js +3 -1
  140. package/build-module/private-apis.js.map +1 -1
  141. package/build-module/utils/object.js +69 -0
  142. package/build-module/utils/object.js.map +1 -0
  143. package/build-style/style-rtl.css +26 -6
  144. package/build-style/style.css +26 -6
  145. package/package.json +31 -31
  146. package/src/components/block-draggable/content.scss +1 -1
  147. package/src/components/block-list/block-html.js +1 -1
  148. package/src/components/block-preview/auto.js +2 -17
  149. package/src/components/colors-gradients/style.scss +8 -8
  150. package/src/components/editor-styles/index.js +29 -1
  151. package/src/components/global-styles/color-panel.js +706 -0
  152. package/src/components/global-styles/dimensions-panel.js +13 -42
  153. package/src/components/global-styles/get-block-css-selector.js +129 -0
  154. package/src/components/global-styles/hooks.js +80 -0
  155. package/src/components/global-styles/index.js +2 -1
  156. package/src/components/global-styles/test/use-global-styles-output.js +30 -1
  157. package/src/components/global-styles/typography-panel.js +26 -51
  158. package/src/components/global-styles/use-global-styles-output.js +163 -80
  159. package/src/components/global-styles/utils.js +3 -0
  160. package/src/components/image-size-control/index.js +4 -3
  161. package/src/components/image-size-control/test/index.js +2 -2
  162. package/src/components/inner-blocks/README.md +1 -1
  163. package/src/components/inspector-controls-tabs/position-controls-panel.js +40 -9
  164. package/src/components/line-height-control/index.js +10 -1
  165. package/src/components/list-view/appender.js +101 -0
  166. package/src/components/list-view/block.js +5 -4
  167. package/src/components/list-view/branch.js +30 -1
  168. package/src/components/list-view/index.js +43 -10
  169. package/src/components/list-view/style.scss +19 -0
  170. package/src/components/media-replace-flow/index.js +36 -24
  171. package/src/components/media-replace-flow/style.scss +5 -2
  172. package/src/components/rich-text/format-edit.js +2 -32
  173. package/src/components/writing-flow/use-input.js +4 -5
  174. package/src/hooks/border.js +0 -1
  175. package/src/hooks/color.js +120 -296
  176. package/src/hooks/content-lock-ui.js +6 -2
  177. package/src/hooks/{color-panel.js → contrast-checker.js} +10 -46
  178. package/src/hooks/dimensions.js +0 -1
  179. package/src/hooks/duotone.js +8 -5
  180. package/src/hooks/position.js +3 -3
  181. package/src/hooks/style.js +29 -28
  182. package/src/hooks/test/utils.js +0 -104
  183. package/src/hooks/typography.js +0 -1
  184. package/src/hooks/utils.js +27 -70
  185. package/src/layouts/grid.js +172 -0
  186. package/src/layouts/index.js +2 -1
  187. package/src/layouts/test/grid.js +21 -0
  188. package/src/private-apis.js +2 -0
  189. package/src/utils/object.js +69 -0
  190. package/src/utils/test/object.js +107 -0
  191. package/tsconfig.tsbuildinfo +1 -1
  192. package/build/hooks/color-panel.js.map +0 -1
  193. package/build-module/hooks/color-panel.js.map +0 -1
@@ -384,39 +384,40 @@ const withElementsStyles = createHigherOrderComponent(
384
384
  );
385
385
 
386
386
  const styles = useMemo( () => {
387
- const rawElementsStyles = props.attributes.style?.elements;
387
+ // The .editor-styles-wrapper selector is required on elements styles. As it is
388
+ // added to all other editor styles, not providing it causes reset and global
389
+ // styles to override element styles because of higher specificity.
390
+ const elements = [
391
+ {
392
+ styles: ! skipLinkColorSerialization
393
+ ? props.attributes.style?.elements?.link
394
+ : undefined,
395
+ selector: `.editor-styles-wrapper .${ blockElementsContainerIdentifier } ${ ELEMENTS.link }`,
396
+ },
397
+ {
398
+ styles: ! skipLinkColorSerialization
399
+ ? props.attributes.style?.elements?.link?.[ ':hover' ]
400
+ : undefined,
401
+ selector: `.editor-styles-wrapper .${ blockElementsContainerIdentifier } ${ ELEMENTS.link }:hover`,
402
+ },
403
+ ];
388
404
  const elementCssRules = [];
389
- if (
390
- rawElementsStyles &&
391
- Object.keys( rawElementsStyles ).length > 0
392
- ) {
393
- // Remove values based on whether serialization has been skipped for a specific style.
394
- const filteredElementsStyles = {
395
- ...rawElementsStyles,
396
- link: {
397
- ...rawElementsStyles.link,
398
- color: ! skipLinkColorSerialization
399
- ? rawElementsStyles.link?.color
400
- : undefined,
401
- },
402
- };
403
-
404
- for ( const [ elementName, elementStyles ] of Object.entries(
405
- filteredElementsStyles
406
- ) ) {
405
+ for ( const { styles: elementStyles, selector } of elements ) {
406
+ if ( elementStyles ) {
407
407
  const cssRule = compileCSS( elementStyles, {
408
- // The .editor-styles-wrapper selector is required on elements styles. As it is
409
- // added to all other editor styles, not providing it causes reset and global
410
- // styles to override element styles because of higher specificity.
411
- selector: `.editor-styles-wrapper .${ blockElementsContainerIdentifier } ${ ELEMENTS[ elementName ] }`,
408
+ selector,
412
409
  } );
413
- if ( !! cssRule ) {
414
- elementCssRules.push( cssRule );
415
- }
410
+ elementCssRules.push( cssRule );
416
411
  }
417
412
  }
418
- return elementCssRules.length > 0 ? elementCssRules : undefined;
419
- }, [ props.attributes.style?.elements ] );
413
+ return elementCssRules.length > 0
414
+ ? elementCssRules.join( '' )
415
+ : undefined;
416
+ }, [
417
+ props.attributes.style?.elements,
418
+ blockElementsContainerIdentifier,
419
+ skipLinkColorSerialization,
420
+ ] );
420
421
 
421
422
  const element = useContext( BlockList.__unstableElementContext );
422
423
 
@@ -7,113 +7,9 @@ import { applyFilters } from '@wordpress/hooks';
7
7
  * Internal dependencies
8
8
  */
9
9
  import '../anchor';
10
- import { immutableSet } from '../utils';
11
10
 
12
11
  const noop = () => {};
13
12
 
14
- describe( 'immutableSet', () => {
15
- describe( 'handling falsy values properly', () => {
16
- it( 'should create a new object if `undefined` is passed', () => {
17
- const result = immutableSet( undefined, 'test', 1 );
18
-
19
- expect( result ).toEqual( { test: 1 } );
20
- } );
21
-
22
- it( 'should create a new object if `null` is passed', () => {
23
- const result = immutableSet( null, 'test', 1 );
24
-
25
- expect( result ).toEqual( { test: 1 } );
26
- } );
27
-
28
- it( 'should create a new object if `false` is passed', () => {
29
- const result = immutableSet( false, 'test', 1 );
30
-
31
- expect( result ).toEqual( { test: 1 } );
32
- } );
33
-
34
- it( 'should create a new object if `0` is passed', () => {
35
- const result = immutableSet( 0, 'test', 1 );
36
-
37
- expect( result ).toEqual( { test: 1 } );
38
- } );
39
-
40
- it( 'should create a new object if an empty string is passed', () => {
41
- const result = immutableSet( '', 'test', 1 );
42
-
43
- expect( result ).toEqual( { test: 1 } );
44
- } );
45
-
46
- it( 'should create a new object if a NaN is passed', () => {
47
- const result = immutableSet( NaN, 'test', 1 );
48
-
49
- expect( result ).toEqual( { test: 1 } );
50
- } );
51
- } );
52
-
53
- describe( 'manages data assignment properly', () => {
54
- it( 'assigns value properly when it does not exist', () => {
55
- const result = immutableSet( {}, 'test', 1 );
56
-
57
- expect( result ).toEqual( { test: 1 } );
58
- } );
59
-
60
- it( 'overrides existing values', () => {
61
- const result = immutableSet( { test: 1 }, 'test', 2 );
62
-
63
- expect( result ).toEqual( { test: 2 } );
64
- } );
65
-
66
- describe( 'with array notation access', () => {
67
- it( 'assigns values at deeper levels', () => {
68
- const result = immutableSet( {}, [ 'foo', 'bar', 'baz' ], 5 );
69
-
70
- expect( result ).toEqual( { foo: { bar: { baz: 5 } } } );
71
- } );
72
-
73
- it( 'overrides existing values at deeper levels', () => {
74
- const result = immutableSet(
75
- { foo: { bar: { baz: 1 } } },
76
- [ 'foo', 'bar', 'baz' ],
77
- 5
78
- );
79
-
80
- expect( result ).toEqual( { foo: { bar: { baz: 5 } } } );
81
- } );
82
-
83
- it( 'keeps other properties intact', () => {
84
- const result = immutableSet(
85
- { foo: { bar: { baz: 1 } } },
86
- [ 'foo', 'bar', 'test' ],
87
- 5
88
- );
89
-
90
- expect( result ).toEqual( {
91
- foo: { bar: { baz: 1, test: 5 } },
92
- } );
93
- } );
94
- } );
95
- } );
96
-
97
- describe( 'does not mutate the original object', () => {
98
- it( 'clones the object at the first level', () => {
99
- const input = {};
100
- const result = immutableSet( input, 'test', 1 );
101
-
102
- expect( result ).not.toBe( input );
103
- } );
104
-
105
- it( 'clones the object at deeper levels', () => {
106
- const input = { foo: { bar: { baz: 1 } } };
107
- const result = immutableSet( input, [ 'foo', 'bar', 'baz' ], 2 );
108
-
109
- expect( result ).not.toBe( input );
110
- expect( result.foo ).not.toBe( input.foo );
111
- expect( result.foo.bar ).not.toBe( input.foo.bar );
112
- expect( result.foo.bar.baz ).not.toBe( input.foo.bar.baz );
113
- } );
114
- } );
115
- } );
116
-
117
13
  describe( 'anchor', () => {
118
14
  const blockSettings = {
119
15
  save: noop,
@@ -138,7 +138,6 @@ export function TypographyPanel( {
138
138
  <StylesTypographyPanel
139
139
  as={ TypographyInspectorControl }
140
140
  panelId={ clientId }
141
- name={ name }
142
141
  settings={ settings }
143
142
  value={ value }
144
143
  onChange={ onChange }
@@ -14,6 +14,7 @@ import { useMemo } from '@wordpress/element';
14
14
  */
15
15
  import { useSetting } from '../components';
16
16
  import { useSettingsForBlockElement } from '../components/global-styles/hooks';
17
+ import { immutableSet } from '../utils/object';
17
18
 
18
19
  /**
19
20
  * Removed falsy values from nested object.
@@ -37,76 +38,6 @@ export const cleanEmptyObject = ( object ) => {
37
38
  return isEmpty( cleanedNestedObjects ) ? undefined : cleanedNestedObjects;
38
39
  };
39
40
 
40
- /**
41
- * Converts a path to an array of its fragments.
42
- * Supports strings, numbers and arrays:
43
- *
44
- * 'foo' => [ 'foo' ]
45
- * 2 => [ '2' ]
46
- * [ 'foo', 'bar' ] => [ 'foo', 'bar' ]
47
- *
48
- * @param {string|number|Array} path Path
49
- * @return {Array} Normalized path.
50
- */
51
- function normalizePath( path ) {
52
- if ( Array.isArray( path ) ) {
53
- return path;
54
- } else if ( typeof path === 'number' ) {
55
- return [ path.toString() ];
56
- }
57
-
58
- return [ path ];
59
- }
60
-
61
- /**
62
- * Clones an object.
63
- * Non-object values are returned unchanged.
64
- *
65
- * @param {*} object Object to clone.
66
- * @return {*} Cloned object, or original literal non-object value.
67
- */
68
- function cloneObject( object ) {
69
- if ( typeof object === 'object' ) {
70
- return {
71
- ...Object.fromEntries(
72
- Object.entries( object ).map( ( [ key, value ] ) => [
73
- key,
74
- cloneObject( value ),
75
- ] )
76
- ),
77
- };
78
- }
79
-
80
- return object;
81
- }
82
-
83
- /**
84
- * Perform an immutable set.
85
- * Handles nullish initial values.
86
- * Clones all nested objects in the specified object.
87
- *
88
- * @param {Object} object Object to set a value in.
89
- * @param {number|string|Array} path Path in the object to modify.
90
- * @param {*} value New value to set.
91
- * @return {Object} Cloned object with the new value set.
92
- */
93
- export function immutableSet( object, path, value ) {
94
- const normalizedPath = normalizePath( path );
95
- const newObject = object ? cloneObject( object ) : {};
96
-
97
- normalizedPath.reduce( ( acc, key, i ) => {
98
- if ( acc[ key ] === undefined ) {
99
- acc[ key ] = {};
100
- }
101
- if ( i === normalizedPath.length - 1 ) {
102
- acc[ key ] = value;
103
- }
104
- return acc[ key ];
105
- }, newObject );
106
-
107
- return newObject;
108
- }
109
-
110
41
  export function transformStyles(
111
42
  activeSupports,
112
43
  migrationPaths,
@@ -222,6 +153,14 @@ export function useBlockSettings( name, parentLayout ) {
222
153
  const themeColors = useSetting( 'color.palette.theme' );
223
154
  const defaultColors = useSetting( 'color.palette.default' );
224
155
  const defaultPalette = useSetting( 'color.defaultPalette' );
156
+ const userGradientPalette = useSetting( 'color.gradients.custom' );
157
+ const themeGradientPalette = useSetting( 'color.gradients.theme' );
158
+ const defaultGradientPalette = useSetting( 'color.gradients.default' );
159
+ const defaultGradients = useSetting( 'color.defaultGradients' );
160
+ const areCustomGradientsEnabled = useSetting( 'color.customGradient' );
161
+ const isBackgroundEnabled = useSetting( 'color.background' );
162
+ const isLinkEnabled = useSetting( 'color.link' );
163
+ const isTextEnabled = useSetting( 'color.text' );
225
164
 
226
165
  const rawSettings = useMemo( () => {
227
166
  return {
@@ -231,8 +170,18 @@ export function useBlockSettings( name, parentLayout ) {
231
170
  theme: themeColors,
232
171
  default: defaultColors,
233
172
  },
173
+ gradients: {
174
+ custom: userGradientPalette,
175
+ theme: themeGradientPalette,
176
+ default: defaultGradientPalette,
177
+ },
178
+ defaultGradients,
234
179
  defaultPalette,
235
180
  custom: customColorsEnabled,
181
+ customGradient: areCustomGradientsEnabled,
182
+ background: isBackgroundEnabled,
183
+ link: isLinkEnabled,
184
+ text: isTextEnabled,
236
185
  },
237
186
  typography: {
238
187
  fontFamilies: {
@@ -299,6 +248,14 @@ export function useBlockSettings( name, parentLayout ) {
299
248
  themeColors,
300
249
  defaultColors,
301
250
  defaultPalette,
251
+ userGradientPalette,
252
+ themeGradientPalette,
253
+ defaultGradientPalette,
254
+ defaultGradients,
255
+ areCustomGradientsEnabled,
256
+ isBackgroundEnabled,
257
+ isLinkEnabled,
258
+ isTextEnabled,
302
259
  ] );
303
260
 
304
261
  return useSettingsForBlockElement( rawSettings, name );
@@ -0,0 +1,172 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { __ } from '@wordpress/i18n';
5
+
6
+ import {
7
+ BaseControl,
8
+ Flex,
9
+ FlexItem,
10
+ RangeControl,
11
+ __experimentalUnitControl as UnitControl,
12
+ __experimentalParseQuantityAndUnitFromRawValue as parseQuantityAndUnitFromRawValue,
13
+ } from '@wordpress/components';
14
+
15
+ /**
16
+ * Internal dependencies
17
+ */
18
+ import { appendSelectors, getBlockGapCSS } from './utils';
19
+ import { getGapCSSValue } from '../hooks/gap';
20
+ import { shouldSkipSerialization } from '../hooks/utils';
21
+
22
+ const RANGE_CONTROL_MAX_VALUES = {
23
+ px: 600,
24
+ '%': 100,
25
+ vw: 100,
26
+ vh: 100,
27
+ em: 38,
28
+ rem: 38,
29
+ };
30
+
31
+ export default {
32
+ name: 'grid',
33
+ label: __( 'Grid' ),
34
+ inspectorControls: function GridLayoutInspectorControls( {
35
+ layout = {},
36
+ onChange,
37
+ } ) {
38
+ return (
39
+ <GridLayoutMinimumWidthControl
40
+ layout={ layout }
41
+ onChange={ onChange }
42
+ />
43
+ );
44
+ },
45
+ toolBarControls: function DefaultLayoutToolbarControls() {
46
+ return null;
47
+ },
48
+ getLayoutStyle: function getLayoutStyle( {
49
+ selector,
50
+ layout,
51
+ style,
52
+ blockName,
53
+ hasBlockGapSupport,
54
+ layoutDefinitions,
55
+ } ) {
56
+ const { minimumColumnWidth = '12rem' } = layout;
57
+
58
+ // If a block's block.json skips serialization for spacing or spacing.blockGap,
59
+ // don't apply the user-defined value to the styles.
60
+ const blockGapValue =
61
+ style?.spacing?.blockGap &&
62
+ ! shouldSkipSerialization( blockName, 'spacing', 'blockGap' )
63
+ ? getGapCSSValue( style?.spacing?.blockGap, '0.5em' )
64
+ : undefined;
65
+
66
+ let output = '';
67
+ const rules = [];
68
+
69
+ if ( minimumColumnWidth ) {
70
+ rules.push(
71
+ `grid-template-columns: repeat(auto-fill, minmax(min(${ minimumColumnWidth }, 100%), 1fr))`
72
+ );
73
+ }
74
+
75
+ if ( rules.length ) {
76
+ // Reason to disable: the extra line breaks added by prettier mess with the unit tests.
77
+ // eslint-disable-next-line prettier/prettier
78
+ output = `${ appendSelectors( selector ) } { ${ rules.join(
79
+ '; '
80
+ ) }; }`;
81
+ }
82
+
83
+ // Output blockGap styles based on rules contained in layout definitions in theme.json.
84
+ if ( hasBlockGapSupport && blockGapValue ) {
85
+ output += getBlockGapCSS(
86
+ selector,
87
+ layoutDefinitions,
88
+ 'grid',
89
+ blockGapValue
90
+ );
91
+ }
92
+ return output;
93
+ },
94
+ getOrientation() {
95
+ return 'horizontal';
96
+ },
97
+ getAlignments() {
98
+ return [];
99
+ },
100
+ };
101
+
102
+ // Enables setting minimum width of grid items.
103
+ function GridLayoutMinimumWidthControl( { layout, onChange } ) {
104
+ const { minimumColumnWidth: value = '12rem' } = layout;
105
+ const [ quantity, unit ] = parseQuantityAndUnitFromRawValue( value );
106
+
107
+ const handleSliderChange = ( next ) => {
108
+ onChange( {
109
+ ...layout,
110
+ minimumColumnWidth: [ next, unit ].join( '' ),
111
+ } );
112
+ };
113
+
114
+ // Mostly copied from HeightControl.
115
+ const handleUnitChange = ( newUnit ) => {
116
+ // Attempt to smooth over differences between currentUnit and newUnit.
117
+ // This should slightly improve the experience of switching between unit types.
118
+ let newValue;
119
+
120
+ if ( [ 'em', 'rem' ].includes( newUnit ) && unit === 'px' ) {
121
+ // Convert pixel value to an approximate of the new unit, assuming a root size of 16px.
122
+ newValue = ( quantity / 16 ).toFixed( 2 ) + newUnit;
123
+ } else if ( [ 'em', 'rem' ].includes( unit ) && newUnit === 'px' ) {
124
+ // Convert to pixel value assuming a root size of 16px.
125
+ newValue = Math.round( quantity * 16 ) + newUnit;
126
+ } else if (
127
+ [ 'vh', 'vw', '%' ].includes( newUnit ) &&
128
+ quantity > 100
129
+ ) {
130
+ // When converting to `vh`, `vw`, or `%` units, cap the new value at 100.
131
+ newValue = 100 + newUnit;
132
+ }
133
+
134
+ onChange( {
135
+ ...layout,
136
+ minimumColumnWidth: newValue,
137
+ } );
138
+ };
139
+
140
+ return (
141
+ <fieldset>
142
+ <BaseControl.VisualLabel as="legend">
143
+ { __( 'Minimum column width' ) }
144
+ </BaseControl.VisualLabel>
145
+ <Flex gap={ 4 }>
146
+ <FlexItem isBlock>
147
+ <UnitControl
148
+ size={ '__unstable-large' }
149
+ onChange={ ( newValue ) => {
150
+ onChange( {
151
+ ...layout,
152
+ minimumColumnWidth: newValue,
153
+ } );
154
+ } }
155
+ onUnitChange={ handleUnitChange }
156
+ value={ value }
157
+ min={ 0 }
158
+ />
159
+ </FlexItem>
160
+ <FlexItem isBlock>
161
+ <RangeControl
162
+ onChange={ handleSliderChange }
163
+ value={ quantity }
164
+ min={ 0 }
165
+ max={ RANGE_CONTROL_MAX_VALUES[ unit ] || 600 }
166
+ withInputField={ false }
167
+ />
168
+ </FlexItem>
169
+ </Flex>
170
+ </fieldset>
171
+ );
172
+ }
@@ -4,8 +4,9 @@
4
4
  import flex from './flex';
5
5
  import flow from './flow';
6
6
  import constrained from './constrained';
7
+ import grid from './grid';
7
8
 
8
- const layoutTypes = [ flow, flex, constrained ];
9
+ const layoutTypes = [ flow, flex, constrained, grid ];
9
10
 
10
11
  /**
11
12
  * Retrieves a layout type by name.
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import grid from '../grid';
5
+
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)); }`;
9
+
10
+ const result = grid.getLayoutStyle( {
11
+ selector: '.my-container',
12
+ layout: {},
13
+ style: {},
14
+ blockName: 'test-block',
15
+ hasBlockGapSupport: false,
16
+ layoutDefinitions: undefined,
17
+ } );
18
+
19
+ expect( result ).toBe( expected );
20
+ } );
21
+ } );
@@ -7,6 +7,7 @@ import { lock } from './lock-unlock';
7
7
  import OffCanvasEditor from './components/off-canvas-editor';
8
8
  import LeafMoreMenu from './components/off-canvas-editor/leaf-more-menu';
9
9
  import { ComposedPrivateInserter as PrivateInserter } from './components/inserter';
10
+ import { PrivateListView } from './components/list-view';
10
11
 
11
12
  /**
12
13
  * Private @wordpress/block-editor APIs.
@@ -18,4 +19,5 @@ lock( privateApis, {
18
19
  LeafMoreMenu,
19
20
  OffCanvasEditor,
20
21
  PrivateInserter,
22
+ PrivateListView,
21
23
  } );
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Converts a path to an array of its fragments.
3
+ * Supports strings, numbers and arrays:
4
+ *
5
+ * 'foo' => [ 'foo' ]
6
+ * 2 => [ '2' ]
7
+ * [ 'foo', 'bar' ] => [ 'foo', 'bar' ]
8
+ *
9
+ * @param {string|number|Array} path Path
10
+ * @return {Array} Normalized path.
11
+ */
12
+ function normalizePath( path ) {
13
+ if ( Array.isArray( path ) ) {
14
+ return path;
15
+ } else if ( typeof path === 'number' ) {
16
+ return [ path.toString() ];
17
+ }
18
+
19
+ return [ path ];
20
+ }
21
+
22
+ /**
23
+ * Clones an object.
24
+ * Non-object values are returned unchanged.
25
+ *
26
+ * @param {*} object Object to clone.
27
+ * @return {*} Cloned object, or original literal non-object value.
28
+ */
29
+ function cloneObject( object ) {
30
+ if ( typeof object === 'object' ) {
31
+ return {
32
+ ...Object.fromEntries(
33
+ Object.entries( object ).map( ( [ key, value ] ) => [
34
+ key,
35
+ cloneObject( value ),
36
+ ] )
37
+ ),
38
+ };
39
+ }
40
+
41
+ return object;
42
+ }
43
+
44
+ /**
45
+ * Perform an immutable set.
46
+ * Handles nullish initial values.
47
+ * Clones all nested objects in the specified object.
48
+ *
49
+ * @param {Object} object Object to set a value in.
50
+ * @param {number|string|Array} path Path in the object to modify.
51
+ * @param {*} value New value to set.
52
+ * @return {Object} Cloned object with the new value set.
53
+ */
54
+ export function immutableSet( object, path, value ) {
55
+ const normalizedPath = normalizePath( path );
56
+ const newObject = object ? cloneObject( object ) : {};
57
+
58
+ normalizedPath.reduce( ( acc, key, i ) => {
59
+ if ( acc[ key ] === undefined ) {
60
+ acc[ key ] = {};
61
+ }
62
+ if ( i === normalizedPath.length - 1 ) {
63
+ acc[ key ] = value;
64
+ }
65
+ return acc[ key ];
66
+ }, newObject );
67
+
68
+ return newObject;
69
+ }