@wordpress/block-editor 8.0.11 → 8.0.14

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 (163) hide show
  1. package/build/components/block-inspector/index.js +12 -2
  2. package/build/components/block-inspector/index.js.map +1 -1
  3. package/build/components/block-list/block.js +1 -1
  4. package/build/components/block-list/block.js.map +1 -1
  5. package/build/components/block-list/block.native.js +2 -3
  6. package/build/components/block-list/block.native.js.map +1 -1
  7. package/build/components/block-list/use-block-props/index.js +1 -3
  8. package/build/components/block-list/use-block-props/index.js.map +1 -1
  9. package/build/components/block-list/use-in-between-inserter.js +1 -1
  10. package/build/components/block-list/use-in-between-inserter.js.map +1 -1
  11. package/build/components/block-mobile-toolbar/block-actions-menu.native.js +2 -2
  12. package/build/components/block-mobile-toolbar/block-actions-menu.native.js.map +1 -1
  13. package/build/components/block-mover/button.js +2 -2
  14. package/build/components/block-mover/button.js.map +1 -1
  15. package/build/components/block-mover/index.js +2 -2
  16. package/build/components/block-mover/index.js.map +1 -1
  17. package/build/components/block-mover/index.native.js +2 -2
  18. package/build/components/block-mover/index.native.js.map +1 -1
  19. package/build/components/block-preview/index.js +51 -0
  20. package/build/components/block-preview/index.js.map +1 -1
  21. package/build/components/block-tools/block-selection-button.js +3 -3
  22. package/build/components/block-tools/block-selection-button.js.map +1 -1
  23. package/build/components/block-tools/index.js +9 -0
  24. package/build/components/block-tools/index.js.map +1 -1
  25. package/build/components/gradients/use-gradient.js +4 -4
  26. package/build/components/gradients/use-gradient.js.map +1 -1
  27. package/build/components/index.js +8 -1
  28. package/build/components/index.js.map +1 -1
  29. package/build/components/inserter/hooks/use-insertion-point.js +2 -2
  30. package/build/components/inserter/hooks/use-insertion-point.js.map +1 -1
  31. package/build/components/inserter/index.js +2 -2
  32. package/build/components/inserter/index.js.map +1 -1
  33. package/build/components/inserter/index.native.js +2 -2
  34. package/build/components/inserter/index.native.js.map +1 -1
  35. package/build/components/inserter/quick-inserter.js +1 -1
  36. package/build/components/inserter/quick-inserter.js.map +1 -1
  37. package/build/components/inspector-controls/block-support-tools-panel.js +50 -22
  38. package/build/components/inspector-controls/block-support-tools-panel.js.map +1 -1
  39. package/build/components/list-view/use-list-view-drop-zone.js +1 -1
  40. package/build/components/list-view/use-list-view-drop-zone.js.map +1 -1
  41. package/build/components/provider/use-block-sync.js +37 -10
  42. package/build/components/provider/use-block-sync.js.map +1 -1
  43. package/build/components/use-on-block-drop/index.js +1 -1
  44. package/build/components/use-on-block-drop/index.js.map +1 -1
  45. package/build/components/use-setting/index.js +7 -0
  46. package/build/components/use-setting/index.js.map +1 -1
  47. package/build/hooks/color.js +6 -5
  48. package/build/hooks/color.js.map +1 -1
  49. package/build/hooks/layout.js +2 -1
  50. package/build/hooks/layout.js.map +1 -1
  51. package/build/hooks/style.js +2 -1
  52. package/build/hooks/style.js.map +1 -1
  53. package/build/layouts/flex.js +9 -29
  54. package/build/layouts/flex.js.map +1 -1
  55. package/build/layouts/flow.js +10 -6
  56. package/build/layouts/flow.js.map +1 -1
  57. package/build/store/actions.js +3 -3
  58. package/build/store/actions.js.map +1 -1
  59. package/build/store/reducer.js +24 -1
  60. package/build/store/reducer.js.map +1 -1
  61. package/build/store/selectors.js +27 -9
  62. package/build/store/selectors.js.map +1 -1
  63. package/build-module/components/block-inspector/index.js +12 -2
  64. package/build-module/components/block-inspector/index.js.map +1 -1
  65. package/build-module/components/block-list/block.js +1 -1
  66. package/build-module/components/block-list/block.js.map +1 -1
  67. package/build-module/components/block-list/block.native.js +2 -3
  68. package/build-module/components/block-list/block.native.js.map +1 -1
  69. package/build-module/components/block-list/use-block-props/index.js +1 -3
  70. package/build-module/components/block-list/use-block-props/index.js.map +1 -1
  71. package/build-module/components/block-list/use-in-between-inserter.js +1 -1
  72. package/build-module/components/block-list/use-in-between-inserter.js.map +1 -1
  73. package/build-module/components/block-mobile-toolbar/block-actions-menu.native.js +2 -2
  74. package/build-module/components/block-mobile-toolbar/block-actions-menu.native.js.map +1 -1
  75. package/build-module/components/block-mover/button.js +2 -2
  76. package/build-module/components/block-mover/button.js.map +1 -1
  77. package/build-module/components/block-mover/index.js +2 -2
  78. package/build-module/components/block-mover/index.js.map +1 -1
  79. package/build-module/components/block-mover/index.native.js +2 -2
  80. package/build-module/components/block-mover/index.native.js.map +1 -1
  81. package/build-module/components/block-preview/index.js +46 -0
  82. package/build-module/components/block-preview/index.js.map +1 -1
  83. package/build-module/components/block-tools/block-selection-button.js +3 -3
  84. package/build-module/components/block-tools/block-selection-button.js.map +1 -1
  85. package/build-module/components/block-tools/index.js +9 -0
  86. package/build-module/components/block-tools/index.js.map +1 -1
  87. package/build-module/components/gradients/use-gradient.js +4 -3
  88. package/build-module/components/gradients/use-gradient.js.map +1 -1
  89. package/build-module/components/index.js +1 -1
  90. package/build-module/components/index.js.map +1 -1
  91. package/build-module/components/inserter/hooks/use-insertion-point.js +2 -2
  92. package/build-module/components/inserter/hooks/use-insertion-point.js.map +1 -1
  93. package/build-module/components/inserter/index.js +2 -2
  94. package/build-module/components/inserter/index.js.map +1 -1
  95. package/build-module/components/inserter/index.native.js +2 -2
  96. package/build-module/components/inserter/index.native.js.map +1 -1
  97. package/build-module/components/inserter/quick-inserter.js +1 -1
  98. package/build-module/components/inserter/quick-inserter.js.map +1 -1
  99. package/build-module/components/inspector-controls/block-support-tools-panel.js +50 -22
  100. package/build-module/components/inspector-controls/block-support-tools-panel.js.map +1 -1
  101. package/build-module/components/list-view/use-list-view-drop-zone.js +1 -1
  102. package/build-module/components/list-view/use-list-view-drop-zone.js.map +1 -1
  103. package/build-module/components/provider/use-block-sync.js +41 -14
  104. package/build-module/components/provider/use-block-sync.js.map +1 -1
  105. package/build-module/components/use-on-block-drop/index.js +1 -1
  106. package/build-module/components/use-on-block-drop/index.js.map +1 -1
  107. package/build-module/components/use-setting/index.js +7 -0
  108. package/build-module/components/use-setting/index.js.map +1 -1
  109. package/build-module/hooks/color.js +6 -5
  110. package/build-module/hooks/color.js.map +1 -1
  111. package/build-module/hooks/layout.js +2 -1
  112. package/build-module/hooks/layout.js.map +1 -1
  113. package/build-module/hooks/style.js +2 -1
  114. package/build-module/hooks/style.js.map +1 -1
  115. package/build-module/layouts/flex.js +9 -29
  116. package/build-module/layouts/flex.js.map +1 -1
  117. package/build-module/layouts/flow.js +10 -6
  118. package/build-module/layouts/flow.js.map +1 -1
  119. package/build-module/store/actions.js +3 -3
  120. package/build-module/store/actions.js.map +1 -1
  121. package/build-module/store/reducer.js +24 -1
  122. package/build-module/store/reducer.js.map +1 -1
  123. package/build-module/store/selectors.js +26 -9
  124. package/build-module/store/selectors.js.map +1 -1
  125. package/build-style/style-rtl.css +14 -0
  126. package/build-style/style.css +14 -0
  127. package/package.json +9 -9
  128. package/src/components/block-inspector/index.js +17 -1
  129. package/src/components/block-list/block.js +1 -1
  130. package/src/components/block-list/block.native.js +2 -2
  131. package/src/components/block-list/use-block-props/index.js +1 -3
  132. package/src/components/block-list/use-in-between-inserter.js +1 -1
  133. package/src/components/block-mobile-toolbar/block-actions-menu.native.js +2 -5
  134. package/src/components/block-mover/button.js +2 -6
  135. package/src/components/block-mover/index.js +2 -5
  136. package/src/components/block-mover/index.native.js +2 -5
  137. package/src/components/block-preview/index.js +60 -0
  138. package/src/components/block-preview/style.scss +23 -0
  139. package/src/components/block-preview/test/index.js +114 -0
  140. package/src/components/block-tools/block-selection-button.js +3 -9
  141. package/src/components/block-tools/index.js +11 -0
  142. package/src/components/gradients/use-gradient.js +7 -7
  143. package/src/components/index.js +4 -1
  144. package/src/components/inserter/hooks/use-insertion-point.js +2 -9
  145. package/src/components/inserter/index.js +2 -2
  146. package/src/components/inserter/index.native.js +2 -5
  147. package/src/components/inserter/quick-inserter.js +1 -1
  148. package/src/components/inspector-controls/block-support-tools-panel.js +57 -21
  149. package/src/components/list-view/use-list-view-drop-zone.js +1 -1
  150. package/src/components/provider/use-block-sync.js +45 -11
  151. package/src/components/use-on-block-drop/index.js +1 -4
  152. package/src/components/use-setting/index.js +9 -0
  153. package/src/hooks/color.js +13 -14
  154. package/src/hooks/layout.js +1 -0
  155. package/src/hooks/style.js +2 -1
  156. package/src/hooks/test/style.js +0 -2
  157. package/src/layouts/flex.js +8 -31
  158. package/src/layouts/flow.js +8 -6
  159. package/src/store/actions.js +3 -4
  160. package/src/store/reducer.js +23 -1
  161. package/src/store/selectors.js +39 -9
  162. package/src/store/test/reducer.js +35 -0
  163. package/src/store/test/selectors.js +1 -1
@@ -11,37 +11,73 @@ import { store as blockEditorStore } from '../../store';
11
11
  import { cleanEmptyObject } from '../../hooks/utils';
12
12
 
13
13
  export default function BlockSupportToolsPanel( { children, group, label } ) {
14
- const { clientId, attributes } = useSelect( ( select ) => {
15
- const { getBlockAttributes, getSelectedBlockClientId } = select(
16
- blockEditorStore
17
- );
14
+ const { attributes, clientIds, panelId } = useSelect( ( select ) => {
15
+ const {
16
+ getBlockAttributes,
17
+ getMultiSelectedBlockClientIds,
18
+ getSelectedBlockClientId,
19
+ hasMultiSelection,
20
+ } = select( blockEditorStore );
21
+
22
+ // When we currently have a multi-selection, the value returned from
23
+ // `getSelectedBlockClientId()` is `null`. When a `null` value is used
24
+ // for the `panelId`, a `ToolsPanel` will still allow panel items to
25
+ // register themselves despite their panelIds not matching.
18
26
  const selectedBlockClientId = getSelectedBlockClientId();
19
27
 
28
+ if ( hasMultiSelection() ) {
29
+ const selectedBlockClientIds = getMultiSelectedBlockClientIds();
30
+ const selectedBlockAttributes = selectedBlockClientIds.reduce(
31
+ ( blockAttributes, blockId ) => {
32
+ blockAttributes[ blockId ] = getBlockAttributes( blockId );
33
+ return blockAttributes;
34
+ },
35
+ {}
36
+ );
37
+
38
+ return {
39
+ panelId: selectedBlockClientId,
40
+ clientIds: selectedBlockClientIds,
41
+ attributes: selectedBlockAttributes,
42
+ };
43
+ }
44
+
20
45
  return {
21
- clientId: selectedBlockClientId,
22
- attributes: getBlockAttributes( selectedBlockClientId ),
46
+ panelId: selectedBlockClientId,
47
+ clientIds: [ selectedBlockClientId ],
48
+ attributes: {
49
+ [ selectedBlockClientId ]: getBlockAttributes(
50
+ selectedBlockClientId
51
+ ),
52
+ },
23
53
  };
24
54
  }, [] );
25
55
  const { updateBlockAttributes } = useDispatch( blockEditorStore );
26
56
 
27
57
  const resetAll = ( resetFilters = [] ) => {
28
- const { style } = attributes;
29
- let newAttributes = { style };
58
+ const newAttributes = {};
59
+
60
+ clientIds.forEach( ( clientId ) => {
61
+ const { style } = attributes[ clientId ];
62
+ let newBlockAttributes = { style };
30
63
 
31
- resetFilters.forEach( ( resetFilter ) => {
32
- newAttributes = {
33
- ...newAttributes,
34
- ...resetFilter( newAttributes ),
64
+ resetFilters.forEach( ( resetFilter ) => {
65
+ newBlockAttributes = {
66
+ ...newBlockAttributes,
67
+ ...resetFilter( newBlockAttributes ),
68
+ };
69
+ } );
70
+
71
+ // Enforce a cleaned style object.
72
+ newBlockAttributes = {
73
+ ...newBlockAttributes,
74
+ style: cleanEmptyObject( newBlockAttributes.style ),
35
75
  };
36
- } );
37
76
 
38
- // Enforce a cleaned style object.
39
- newAttributes = {
40
- ...newAttributes,
41
- style: cleanEmptyObject( newAttributes.style ),
42
- };
77
+ newAttributes[ clientId ] = newBlockAttributes;
78
+ } );
43
79
 
44
- updateBlockAttributes( clientId, newAttributes );
80
+ updateBlockAttributes( clientIds, newAttributes, true );
45
81
  };
46
82
 
47
83
  return (
@@ -49,8 +85,8 @@ export default function BlockSupportToolsPanel( { children, group, label } ) {
49
85
  className={ `${ group }-block-support-panel` }
50
86
  label={ label }
51
87
  resetAll={ resetAll }
52
- key={ clientId }
53
- panelId={ clientId }
88
+ key={ panelId }
89
+ panelId={ panelId }
54
90
  hasInnerWrapper={ true }
55
91
  shouldRenderPlaceholderItems={ true } // Required to maintain fills ordering.
56
92
  >
@@ -226,7 +226,7 @@ export default function useListViewDropZone() {
226
226
  return {
227
227
  clientId,
228
228
  rootClientId,
229
- blockIndex: getBlockIndex( clientId, rootClientId ),
229
+ blockIndex: getBlockIndex( clientId ),
230
230
  element: blockElement,
231
231
  isDraggedBlock: isBlockDrag
232
232
  ? draggedBlockClientIds.includes( clientId )
@@ -7,7 +7,7 @@ import { last, noop } from 'lodash';
7
7
  * WordPress dependencies
8
8
  */
9
9
  import { useEffect, useRef } from '@wordpress/element';
10
- import { useRegistry } from '@wordpress/data';
10
+ import { useRegistry, useSelect } from '@wordpress/data';
11
11
  import { cloneBlock } from '@wordpress/blocks';
12
12
 
13
13
  /**
@@ -83,6 +83,15 @@ export default function useBlockSync( {
83
83
  __unstableMarkNextChangeAsNotPersistent,
84
84
  } = registry.dispatch( blockEditorStore );
85
85
  const { getBlockName, getBlocks } = registry.select( blockEditorStore );
86
+ const isControlled = useSelect(
87
+ ( select ) => {
88
+ return (
89
+ ! clientId ||
90
+ select( blockEditorStore ).areInnerBlocksControlled( clientId )
91
+ );
92
+ },
93
+ [ clientId ]
94
+ );
86
95
 
87
96
  const pendingChanges = useRef( { incoming: null, outgoing: [] } );
88
97
  const subscribed = useRef( false );
@@ -97,15 +106,21 @@ export default function useBlockSync( {
97
106
  // and so it would already be persisted.
98
107
  __unstableMarkNextChangeAsNotPersistent();
99
108
  if ( clientId ) {
100
- setHasControlledInnerBlocks( clientId, true );
101
- __unstableMarkNextChangeAsNotPersistent();
102
- const storeBlocks = controlledBlocks.map( ( block ) =>
103
- cloneBlock( block )
104
- );
105
- if ( subscribed.current ) {
106
- pendingChanges.current.incoming = storeBlocks;
107
- }
108
- replaceInnerBlocks( clientId, storeBlocks );
109
+ // It is important to batch here because otherwise,
110
+ // as soon as `setHasControlledInnerBlocks` is called
111
+ // the effect to restore might be triggered
112
+ // before the actual blocks get set properly in state.
113
+ registry.batch( () => {
114
+ setHasControlledInnerBlocks( clientId, true );
115
+ const storeBlocks = controlledBlocks.map( ( block ) =>
116
+ cloneBlock( block )
117
+ );
118
+ if ( subscribed.current ) {
119
+ pendingChanges.current.incoming = storeBlocks;
120
+ }
121
+ __unstableMarkNextChangeAsNotPersistent();
122
+ replaceInnerBlocks( clientId, storeBlocks );
123
+ } );
109
124
  } else {
110
125
  if ( subscribed.current ) {
111
126
  pendingChanges.current.incoming = controlledBlocks;
@@ -157,6 +172,15 @@ export default function useBlockSync( {
157
172
  }
158
173
  }, [ controlledBlocks, clientId ] );
159
174
 
175
+ useEffect( () => {
176
+ // When the block becomes uncontrolled, it means its inner state has been reset
177
+ // we need to take the blocks again from the external value property.
178
+ if ( ! isControlled ) {
179
+ pendingChanges.current.outgoing = [];
180
+ setControlledBlocks();
181
+ }
182
+ }, [ isControlled ] );
183
+
160
184
  useEffect( () => {
161
185
  const {
162
186
  getSelectionStart,
@@ -164,6 +188,7 @@ export default function useBlockSync( {
164
188
  getSelectedBlocksInitialCaretPosition,
165
189
  isLastBlockChangePersistent,
166
190
  __unstableIsLastBlockChangeIgnored,
191
+ areInnerBlocksControlled,
167
192
  } = registry.select( blockEditorStore );
168
193
 
169
194
  let blocks = getBlocks( clientId );
@@ -182,8 +207,17 @@ export default function useBlockSync( {
182
207
  if ( clientId !== null && getBlockName( clientId ) === null )
183
208
  return;
184
209
 
185
- const newIsPersistent = isLastBlockChangePersistent();
210
+ // When RESET_BLOCKS on parent blocks get called, the controlled blocks
211
+ // can reset to uncontrolled, in these situations, it means we need to populate
212
+ // the blocks again from the external blocks (the value property here)
213
+ // and we should stop triggering onChange
214
+ const isStillControlled =
215
+ ! clientId || areInnerBlocksControlled( clientId );
216
+ if ( ! isStillControlled ) {
217
+ return;
218
+ }
186
219
 
220
+ const newIsPersistent = isLastBlockChangePersistent();
187
221
  const newBlocks = getBlocks( clientId );
188
222
  const areBlocksDifferent = newBlocks !== blocks;
189
223
  blocks = newBlocks;
@@ -95,10 +95,7 @@ export function onBlockDrop(
95
95
 
96
96
  // If the user is moving a block
97
97
  if ( dropType === 'block' ) {
98
- const sourceBlockIndex = getBlockIndex(
99
- sourceClientIds[ 0 ],
100
- sourceRootClientId
101
- );
98
+ const sourceBlockIndex = getBlockIndex( sourceClientIds[ 0 ] );
102
99
 
103
100
  // If the user is dropping to the same position, return early.
104
101
  if (
@@ -15,6 +15,8 @@ import { __EXPERIMENTAL_PATHS_WITH_MERGE as PATHS_WITH_MERGE } from '@wordpress/
15
15
  import { useBlockEditContext } from '../block-edit';
16
16
  import { store as blockEditorStore } from '../../store';
17
17
 
18
+ const blockedPaths = [ 'color', 'border', 'typography', 'spacing' ];
19
+
18
20
  const deprecatedFlags = {
19
21
  'color.palette': ( settings ) =>
20
22
  settings.colors === undefined ? undefined : settings.colors,
@@ -104,6 +106,13 @@ export default function useSetting( path ) {
104
106
 
105
107
  const setting = useSelect(
106
108
  ( select ) => {
109
+ if ( blockedPaths.includes( path ) ) {
110
+ // eslint-disable-next-line no-console
111
+ console.warn(
112
+ 'Top level useSetting paths are disabled. Please use a subpath to query the information needed.'
113
+ );
114
+ return undefined;
115
+ }
107
116
  const settings = select( blockEditorStore ).getSettings();
108
117
 
109
118
  // 1 - Use __experimental features, if available.
@@ -32,8 +32,6 @@ import useSetting from '../components/use-setting';
32
32
 
33
33
  export const COLOR_SUPPORT_KEY = 'color';
34
34
 
35
- const EMPTY_OBJECT = {};
36
-
37
35
  const hasColorSupport = ( blockType ) => {
38
36
  const colorSupport = getBlockSupport( blockType, COLOR_SUPPORT_KEY );
39
37
  return (
@@ -232,7 +230,17 @@ export function ColorEdit( props ) {
232
230
  ],
233
231
  [ userPalette, themePalette, defaultPalette ]
234
232
  );
235
- const gradientsPerOrigin = useSetting( 'color.gradients' ) || EMPTY_OBJECT;
233
+ const userGradientPalette = useSetting( 'color.gradients.custom' );
234
+ const themeGradientPalette = useSetting( 'color.gradients.theme' );
235
+ const defaultGradientPalette = useSetting( 'color.gradients.default' );
236
+ const allGradients = useMemo(
237
+ () => [
238
+ ...( userGradientPalette || [] ),
239
+ ...( themeGradientPalette || [] ),
240
+ ...( defaultGradientPalette || [] ),
241
+ ],
242
+ [ userGradientPalette, themeGradientPalette, defaultGradientPalette ]
243
+ );
236
244
  const areCustomSolidsEnabled = useSetting( 'color.custom' );
237
245
  const areCustomGradientsEnabled = useSetting( 'color.customGradient' );
238
246
  const isBackgroundEnabled = useSetting( 'color.background' );
@@ -244,17 +252,8 @@ export function ColorEdit( props ) {
244
252
 
245
253
  const gradientsEnabled =
246
254
  areCustomGradientsEnabled ||
247
- ! gradientsPerOrigin?.theme ||
248
- gradientsPerOrigin?.theme?.length > 0;
249
-
250
- const allGradients = useMemo(
251
- () => [
252
- ...( gradientsPerOrigin?.custom || [] ),
253
- ...( gradientsPerOrigin?.theme || [] ),
254
- ...( gradientsPerOrigin?.default || [] ),
255
- ],
256
- [ gradientsPerOrigin ]
257
- );
255
+ ! themeGradientPalette ||
256
+ themeGradientPalette?.length > 0;
258
257
 
259
258
  // Shouldn't be needed but right now the ColorGradientsPanel
260
259
  // can trigger both onChangeColor and onChangeBackground
@@ -215,6 +215,7 @@ export const withLayoutStyles = createHigherOrderComponent(
215
215
  <LayoutStyle
216
216
  selector={ `.wp-container-${ id }` }
217
217
  layout={ usedLayout }
218
+ style={ attributes?.style }
218
219
  />,
219
220
  element
220
221
  ) }
@@ -74,6 +74,7 @@ function compileStyleValue( uncompiledValue ) {
74
74
  * @return {Object} Flattened CSS variables declaration.
75
75
  */
76
76
  export function getInlineStyles( styles = {} ) {
77
+ const ignoredStyles = [ 'spacing.blockGap' ];
77
78
  const output = {};
78
79
  Object.keys( STYLE_PROPERTY ).forEach( ( propKey ) => {
79
80
  const path = STYLE_PROPERTY[ propKey ].value;
@@ -93,7 +94,7 @@ export function getInlineStyles( styles = {} ) {
93
94
  output[ name ] = compileStyleValue( value );
94
95
  }
95
96
  } );
96
- } else {
97
+ } else if ( ! ignoredStyles.includes( path.join( '.' ) ) ) {
97
98
  output[ propKey ] = compileStyleValue( get( styles, path ) );
98
99
  }
99
100
  }
@@ -30,7 +30,6 @@ describe( 'getInlineStyles', () => {
30
30
  },
31
31
  } )
32
32
  ).toEqual( {
33
- '--wp--style--block-gap': '1em',
34
33
  backgroundColor: 'black',
35
34
  borderColor: '#21759b',
36
35
  borderRadius: '10px',
@@ -104,7 +103,6 @@ describe( 'getInlineStyles', () => {
104
103
  },
105
104
  } )
106
105
  ).toEqual( {
107
- '--wp--style--block-gap': '1em',
108
106
  margin: '10px',
109
107
  padding: '20px',
110
108
  } );
@@ -84,58 +84,35 @@ export default {
84
84
  </BlockControls>
85
85
  );
86
86
  },
87
- save: function FlexLayoutStyle( { selector, layout } ) {
88
- const {
89
- orientation = 'horizontal',
90
- setCascadingProperties = false,
91
- } = layout;
87
+ save: function FlexLayoutStyle( { selector, layout, style } ) {
88
+ const { orientation = 'horizontal' } = layout;
92
89
  const blockGapSupport = useSetting( 'spacing.blockGap' );
93
90
  const hasBlockGapStylesSupport = blockGapSupport !== null;
91
+ const blockGapValue =
92
+ style?.spacing?.blockGap ?? 'var( --wp--style--block-gap, 0.5em )';
94
93
  const justifyContent =
95
94
  justifyContentMap[ layout.justifyContent ] ||
96
95
  justifyContentMap.left;
97
96
  const flexWrap = flexWrapOptions.includes( layout.flexWrap )
98
97
  ? layout.flexWrap
99
98
  : 'wrap';
100
- let rowOrientation = `
99
+ const rowOrientation = `
101
100
  flex-direction: row;
102
101
  align-items: center;
103
102
  justify-content: ${ justifyContent };
104
103
  `;
105
- if ( setCascadingProperties ) {
106
- // --layout-justification-setting allows children to inherit the value
107
- // regardless or row or column direction.
108
- rowOrientation += `
109
- --layout-justification-setting: ${ justifyContent };
110
- --layout-direction: row;
111
- --layout-wrap: ${ flexWrap };
112
- --layout-justify: ${ justifyContent };
113
- --layout-align: center;
114
- `;
115
- }
116
104
  const alignItems =
117
105
  alignItemsMap[ layout.justifyContent ] || alignItemsMap.left;
118
- let columnOrientation = `
106
+ const columnOrientation = `
119
107
  flex-direction: column;
120
108
  align-items: ${ alignItems };
121
109
  `;
122
- if ( setCascadingProperties ) {
123
- columnOrientation += `
124
- --layout-justification-setting: ${ alignItems };
125
- --layout-direction: column;
126
- --layout-justify: initial;
127
- --layout-align: ${ alignItems };
128
- `;
129
- }
110
+
130
111
  return (
131
112
  <style>{ `
132
113
  ${ appendSelectors( selector ) } {
133
114
  display: flex;
134
- gap: ${
135
- hasBlockGapStylesSupport
136
- ? 'var( --wp--style--block-gap, 0.5em )'
137
- : '0.5em'
138
- };
115
+ gap: ${ hasBlockGapStylesSupport ? blockGapValue : '0.5em' };
139
116
  flex-wrap: ${ flexWrap };
140
117
  ${ orientation === 'horizontal' ? rowOrientation : columnOrientation }
141
118
  }
@@ -105,12 +105,14 @@ export default {
105
105
  toolBarControls: function DefaultLayoutToolbarControls() {
106
106
  return null;
107
107
  },
108
- save: function DefaultLayoutStyle( { selector, layout = {} } ) {
108
+ save: function DefaultLayoutStyle( { selector, layout = {}, style } ) {
109
109
  const { contentSize, wideSize } = layout;
110
110
  const blockGapSupport = useSetting( 'spacing.blockGap' );
111
111
  const hasBlockGapStylesSupport = blockGapSupport !== null;
112
+ const blockGapValue =
113
+ style?.spacing?.blockGap ?? 'var( --wp--style--block-gap )';
112
114
 
113
- let style =
115
+ let output =
114
116
  !! contentSize || !! wideSize
115
117
  ? `
116
118
  ${ appendSelectors( selector, '> *' ) } {
@@ -129,7 +131,7 @@ export default {
129
131
  `
130
132
  : '';
131
133
 
132
- style += `
134
+ output += `
133
135
  ${ appendSelectors( selector, '> [data-align="left"]' ) } {
134
136
  float: left;
135
137
  margin-right: 2em;
@@ -143,18 +145,18 @@ export default {
143
145
  `;
144
146
 
145
147
  if ( hasBlockGapStylesSupport ) {
146
- style += `
148
+ output += `
147
149
  ${ appendSelectors( selector, '> *' ) } {
148
150
  margin-top: 0;
149
151
  margin-bottom: 0;
150
152
  }
151
153
  ${ appendSelectors( selector, '> * + *' ) } {
152
- margin-top: var( --wp--style--block-gap );
154
+ margin-top: ${ blockGapValue };
153
155
  }
154
156
  `;
155
157
  }
156
158
 
157
- return <style>{ style }</style>;
159
+ return <style>{ output }</style>;
158
160
  },
159
161
  getOrientation() {
160
162
  return 'vertical';
@@ -1170,8 +1170,7 @@ export const duplicateBlocks = ( clientIds, updateSelection = true ) => ( {
1170
1170
 
1171
1171
  const rootClientId = select.getBlockRootClientId( clientIds[ 0 ] );
1172
1172
  const lastSelectedIndex = select.getBlockIndex(
1173
- last( castArray( clientIds ) ),
1174
- rootClientId
1173
+ last( castArray( clientIds ) )
1175
1174
  );
1176
1175
  const clonedBlocks = blocks.map( ( block ) =>
1177
1176
  __experimentalCloneSanitizedBlock( block )
@@ -1206,7 +1205,7 @@ export const insertBeforeBlock = ( clientId ) => ( { select, dispatch } ) => {
1206
1205
  return;
1207
1206
  }
1208
1207
 
1209
- const firstSelectedIndex = select.getBlockIndex( clientId, rootClientId );
1208
+ const firstSelectedIndex = select.getBlockIndex( clientId );
1210
1209
  return dispatch.insertDefaultBlock( {}, rootClientId, firstSelectedIndex );
1211
1210
  };
1212
1211
 
@@ -1225,7 +1224,7 @@ export const insertAfterBlock = ( clientId ) => ( { select, dispatch } ) => {
1225
1224
  return;
1226
1225
  }
1227
1226
 
1228
- const firstSelectedIndex = select.getBlockIndex( clientId, rootClientId );
1227
+ const firstSelectedIndex = select.getBlockIndex( clientId );
1229
1228
  return dispatch.insertDefaultBlock(
1230
1229
  {},
1231
1230
  rootClientId,
@@ -737,6 +737,27 @@ const withSaveReusableBlock = ( reducer ) => ( state, action ) => {
737
737
 
738
738
  return reducer( state, action );
739
739
  };
740
+ /**
741
+ * Higher-order reducer which removes blocks from state when switching parent block controlled state.
742
+ *
743
+ * @param {Function} reducer Original reducer function.
744
+ *
745
+ * @return {Function} Enhanced reducer function.
746
+ */
747
+ const withResetControlledBlocks = ( reducer ) => ( state, action ) => {
748
+ if ( action.type === 'SET_HAS_CONTROLLED_INNER_BLOCKS' ) {
749
+ // when switching a block from controlled to uncontrolled or inverse,
750
+ // we need to remove its content first.
751
+ const tempState = reducer( state, {
752
+ type: 'REPLACE_INNER_BLOCKS',
753
+ rootClientId: action.clientId,
754
+ blocks: [],
755
+ } );
756
+ return reducer( tempState, action );
757
+ }
758
+
759
+ return reducer( state, action );
760
+ };
740
761
 
741
762
  /**
742
763
  * Reducer returning the blocks state.
@@ -754,7 +775,8 @@ export const blocks = flow(
754
775
  withReplaceInnerBlocks, // needs to be after withInnerBlocksRemoveCascade
755
776
  withBlockReset,
756
777
  withPersistentBlockChange,
757
- withIgnoredBlockChange
778
+ withIgnoredBlockChange,
779
+ withResetControlledBlocks
758
780
  )( {
759
781
  byClientId( state = {}, action ) {
760
782
  switch ( action.type ) {
@@ -30,6 +30,7 @@ import {
30
30
  parse,
31
31
  } from '@wordpress/blocks';
32
32
  import { Platform } from '@wordpress/element';
33
+ import { applyFilters } from '@wordpress/hooks';
33
34
  import { symbol } from '@wordpress/icons';
34
35
 
35
36
  /**
@@ -826,6 +827,7 @@ export const isAncestorMultiSelected = createSelector(
826
827
  state.selection.selectionEnd.clientId,
827
828
  ]
828
829
  );
830
+
829
831
  /**
830
832
  * Returns the client ID of the block which begins the multi-selection set, or
831
833
  * null if there is no multi-selection.
@@ -890,11 +892,11 @@ export function getBlockOrder( state, rootClientId ) {
890
892
  *
891
893
  * @param {Object} state Editor state.
892
894
  * @param {string} clientId Block client ID.
893
- * @param {?string} rootClientId Optional root client ID of block list.
894
895
  *
895
896
  * @return {number} Index at which block exists in order.
896
897
  */
897
- export function getBlockIndex( state, clientId, rootClientId ) {
898
+ export function getBlockIndex( state, clientId ) {
899
+ const rootClientId = getBlockRootClientId( state, clientId );
898
900
  return getBlockOrder( state, rootClientId ).indexOf( clientId );
899
901
  }
900
902
 
@@ -1259,15 +1261,43 @@ const canInsertBlockTypeUnmemoized = (
1259
1261
  parentName
1260
1262
  );
1261
1263
 
1262
- if ( hasParentAllowedBlock !== null && hasBlockAllowedParent !== null ) {
1263
- return hasParentAllowedBlock || hasBlockAllowedParent;
1264
- } else if ( hasParentAllowedBlock !== null ) {
1265
- return hasParentAllowedBlock;
1266
- } else if ( hasBlockAllowedParent !== null ) {
1267
- return hasBlockAllowedParent;
1264
+ const canInsert =
1265
+ ( hasParentAllowedBlock === null && hasBlockAllowedParent === null ) ||
1266
+ hasParentAllowedBlock === true ||
1267
+ hasBlockAllowedParent === true;
1268
+
1269
+ if ( ! canInsert ) {
1270
+ return canInsert;
1268
1271
  }
1269
1272
 
1270
- return true;
1273
+ /**
1274
+ * This filter is an ad-hoc solution to prevent adding template parts inside post content.
1275
+ * Conceptually, having a filter inside a selector is bad pattern so this code will be
1276
+ * replaced by a declarative API that doesn't the following drawbacks:
1277
+ *
1278
+ * Filters are not reactive: Upon switching between "template mode" and non "template mode",
1279
+ * the filter and selector won't necessarily be executed again. For now, it doesn't matter much
1280
+ * because you can't switch between the two modes while the inserter stays open.
1281
+ *
1282
+ * Filters are global: Once they're defined, they will affect all editor instances and all registries.
1283
+ * An ideal API would only affect specific editor instances.
1284
+ */
1285
+ return applyFilters(
1286
+ 'blockEditor.__unstableCanInsertBlockType',
1287
+ canInsert,
1288
+ blockType,
1289
+ rootClientId,
1290
+ {
1291
+ // Pass bound selectors of the current registry. If we're in a nested
1292
+ // context, the data will differ from the one selected from the root
1293
+ // registry.
1294
+ getBlock: getBlock.bind( null, state ),
1295
+ getBlockParentsByBlockName: getBlockParentsByBlockName.bind(
1296
+ null,
1297
+ state
1298
+ ),
1299
+ }
1300
+ );
1271
1301
  };
1272
1302
 
1273
1303
  /**
@@ -2050,6 +2050,41 @@ describe( 'state', () => {
2050
2050
  expect( state.isIgnoredChange ).toBe( true );
2051
2051
  } );
2052
2052
  } );
2053
+
2054
+ describe( 'controlledInnerBlocks', () => {
2055
+ it( 'should remove the content of the block if it switches from controlled to uncontrolled or opposite', () => {
2056
+ const original = blocks( undefined, {
2057
+ type: 'RESET_BLOCKS',
2058
+ blocks: [
2059
+ {
2060
+ clientId: 'chicken',
2061
+ name: 'core/test-block',
2062
+ attributes: {},
2063
+ innerBlocks: [
2064
+ {
2065
+ clientId: 'child',
2066
+ name: 'core/test-block',
2067
+ attributes: {},
2068
+ innerBlocks: [],
2069
+ },
2070
+ ],
2071
+ },
2072
+ ],
2073
+ } );
2074
+
2075
+ const state = blocks( original, {
2076
+ type: 'SET_HAS_CONTROLLED_INNER_BLOCKS',
2077
+ clientId: 'chicken',
2078
+ hasControlledInnerBlocks: true,
2079
+ } );
2080
+
2081
+ expect( state.controlledInnerBlocks.chicken ).toBe( true );
2082
+ // The previous content of the block should be removed
2083
+ expect( state.byClientId.child ).toBeUndefined();
2084
+ expect( state.tree.child ).toBeUndefined();
2085
+ expect( state.tree.chicken.innerBlocks ).toEqual( [] );
2086
+ } );
2087
+ } );
2053
2088
  } );
2054
2089
  } );
2055
2090
 
@@ -1358,7 +1358,7 @@ describe( 'selectors', () => {
1358
1358
  },
1359
1359
  };
1360
1360
 
1361
- expect( getBlockIndex( state, 56, '123' ) ).toBe( 1 );
1361
+ expect( getBlockIndex( state, 56 ) ).toBe( 1 );
1362
1362
  } );
1363
1363
  } );
1364
1364