@wordpress/block-editor 13.0.3 → 13.0.5

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 (85) hide show
  1. package/build/components/global-styles/background-panel.js +2 -1
  2. package/build/components/global-styles/background-panel.js.map +1 -1
  3. package/build/components/global-styles/border-panel.js +2 -1
  4. package/build/components/global-styles/border-panel.js.map +1 -1
  5. package/build/components/global-styles/color-panel.js +2 -1
  6. package/build/components/global-styles/color-panel.js.map +1 -1
  7. package/build/components/global-styles/dimensions-panel.js +2 -1
  8. package/build/components/global-styles/dimensions-panel.js.map +1 -1
  9. package/build/components/global-styles/filters-panel.js +2 -1
  10. package/build/components/global-styles/filters-panel.js.map +1 -1
  11. package/build/components/global-styles/image-settings-panel.js +2 -1
  12. package/build/components/global-styles/image-settings-panel.js.map +1 -1
  13. package/build/components/global-styles/typography-panel.js +2 -1
  14. package/build/components/global-styles/typography-panel.js.map +1 -1
  15. package/build/components/global-styles/use-global-styles-output.js +8 -8
  16. package/build/components/global-styles/use-global-styles-output.js.map +1 -1
  17. package/build/components/global-styles/utils.js +17 -7
  18. package/build/components/global-styles/utils.js.map +1 -1
  19. package/build/components/inspector-controls/block-support-tools-panel.js +2 -1
  20. package/build/components/inspector-controls/block-support-tools-panel.js.map +1 -1
  21. package/build/hooks/block-style-variation.js +171 -6
  22. package/build/hooks/block-style-variation.js.map +1 -1
  23. package/build/hooks/duotone.js +16 -11
  24. package/build/hooks/duotone.js.map +1 -1
  25. package/build/hooks/index.js +7 -1
  26. package/build/hooks/index.js.map +1 -1
  27. package/build/hooks/use-bindings-attributes.js +11 -6
  28. package/build/hooks/use-bindings-attributes.js.map +1 -1
  29. package/build/hooks/utils.js +2 -0
  30. package/build/hooks/utils.js.map +1 -1
  31. package/build/private-apis.js +2 -1
  32. package/build/private-apis.js.map +1 -1
  33. package/build-module/components/global-styles/background-panel.js +3 -2
  34. package/build-module/components/global-styles/background-panel.js.map +1 -1
  35. package/build-module/components/global-styles/border-panel.js +3 -2
  36. package/build-module/components/global-styles/border-panel.js.map +1 -1
  37. package/build-module/components/global-styles/color-panel.js +3 -2
  38. package/build-module/components/global-styles/color-panel.js.map +1 -1
  39. package/build-module/components/global-styles/dimensions-panel.js +3 -2
  40. package/build-module/components/global-styles/dimensions-panel.js.map +1 -1
  41. package/build-module/components/global-styles/filters-panel.js +3 -2
  42. package/build-module/components/global-styles/filters-panel.js.map +1 -1
  43. package/build-module/components/global-styles/image-settings-panel.js +3 -2
  44. package/build-module/components/global-styles/image-settings-panel.js.map +1 -1
  45. package/build-module/components/global-styles/typography-panel.js +3 -2
  46. package/build-module/components/global-styles/typography-panel.js.map +1 -1
  47. package/build-module/components/global-styles/use-global-styles-output.js +8 -8
  48. package/build-module/components/global-styles/use-global-styles-output.js.map +1 -1
  49. package/build-module/components/global-styles/utils.js +15 -6
  50. package/build-module/components/global-styles/utils.js.map +1 -1
  51. package/build-module/components/inspector-controls/block-support-tools-panel.js +3 -2
  52. package/build-module/components/inspector-controls/block-support-tools-panel.js.map +1 -1
  53. package/build-module/hooks/block-style-variation.js +170 -6
  54. package/build-module/hooks/block-style-variation.js.map +1 -1
  55. package/build-module/hooks/duotone.js +16 -11
  56. package/build-module/hooks/duotone.js.map +1 -1
  57. package/build-module/hooks/index.js +1 -0
  58. package/build-module/hooks/index.js.map +1 -1
  59. package/build-module/hooks/use-bindings-attributes.js +11 -6
  60. package/build-module/hooks/use-bindings-attributes.js.map +1 -1
  61. package/build-module/hooks/utils.js +2 -0
  62. package/build-module/hooks/utils.js.map +1 -1
  63. package/build-module/private-apis.js +3 -2
  64. package/build-module/private-apis.js.map +1 -1
  65. package/build-style/content-rtl.css +1 -0
  66. package/build-style/content.css +1 -0
  67. package/package.json +2 -2
  68. package/src/components/global-styles/background-panel.js +3 -2
  69. package/src/components/global-styles/border-panel.js +3 -2
  70. package/src/components/global-styles/color-panel.js +3 -2
  71. package/src/components/global-styles/dimensions-panel.js +3 -2
  72. package/src/components/global-styles/filters-panel.js +3 -2
  73. package/src/components/global-styles/image-settings-panel.js +3 -2
  74. package/src/components/global-styles/typography-panel.js +3 -2
  75. package/src/components/global-styles/use-global-styles-output.js +8 -8
  76. package/src/components/global-styles/utils.js +17 -6
  77. package/src/components/iframe/content.scss +1 -0
  78. package/src/components/inspector-controls/block-support-tools-panel.js +3 -3
  79. package/src/hooks/block-style-variation.js +204 -5
  80. package/src/hooks/duotone.js +16 -12
  81. package/src/hooks/index.js +1 -0
  82. package/src/hooks/test/get-variation-styles-with-ref-values.js +91 -0
  83. package/src/hooks/use-bindings-attributes.js +13 -4
  84. package/src/hooks/utils.js +2 -0
  85. package/src/private-apis.js +6 -1
@@ -18,7 +18,7 @@ import { __ } from '@wordpress/i18n';
18
18
  */
19
19
  import BorderRadiusControl from '../border-radius-control';
20
20
  import { useColorsPerOrigin } from './hooks';
21
- import { getValueFromVariable, TOOLSPANEL_DROPDOWNMENU_PROPS } from './utils';
21
+ import { getValueFromVariable, useToolsPanelDropdownMenuProps } from './utils';
22
22
  import { setImmutably } from '../../utils/object';
23
23
  import { useBorderPanelLabel } from '../../hooks/border';
24
24
  import { ShadowPopover, useShadowPresets } from './shadow-panel-components';
@@ -69,6 +69,7 @@ function BorderToolsPanel( {
69
69
  children,
70
70
  label,
71
71
  } ) {
72
+ const dropdownMenuProps = useToolsPanelDropdownMenuProps();
72
73
  const resetAll = () => {
73
74
  const updatedValue = resetAllFilter( value );
74
75
  onChange( updatedValue );
@@ -79,7 +80,7 @@ function BorderToolsPanel( {
79
80
  label={ label }
80
81
  resetAll={ resetAll }
81
82
  panelId={ panelId }
82
- dropdownMenuProps={ TOOLSPANEL_DROPDOWNMENU_PROPS }
83
+ dropdownMenuProps={ dropdownMenuProps }
83
84
  >
84
85
  { children }
85
86
  </ToolsPanel>
@@ -27,7 +27,7 @@ import { __, sprintf } from '@wordpress/i18n';
27
27
  */
28
28
  import ColorGradientControl from '../colors-gradients/control';
29
29
  import { useColorsPerOrigin, useGradientsPerOrigin } from './hooks';
30
- import { getValueFromVariable, TOOLSPANEL_DROPDOWNMENU_PROPS } from './utils';
30
+ import { getValueFromVariable, useToolsPanelDropdownMenuProps } from './utils';
31
31
  import { setImmutably } from '../../utils/object';
32
32
  import { unlock } from '../../lock-unlock';
33
33
 
@@ -116,6 +116,7 @@ function ColorToolsPanel( {
116
116
  panelId,
117
117
  children,
118
118
  } ) {
119
+ const dropdownMenuProps = useToolsPanelDropdownMenuProps();
119
120
  const resetAll = () => {
120
121
  const updatedValue = resetAllFilter( value );
121
122
  onChange( updatedValue );
@@ -131,7 +132,7 @@ function ColorToolsPanel( {
131
132
  className="color-block-support-panel"
132
133
  __experimentalFirstVisibleItemClass="first"
133
134
  __experimentalLastVisibleItemClass="last"
134
- dropdownMenuProps={ TOOLSPANEL_DROPDOWNMENU_PROPS }
135
+ dropdownMenuProps={ dropdownMenuProps }
135
136
  >
136
137
  <div className="color-block-support-panel__inner-wrapper">
137
138
  { children }
@@ -22,7 +22,7 @@ import { useCallback, useState, Platform } from '@wordpress/element';
22
22
  /**
23
23
  * Internal dependencies
24
24
  */
25
- import { getValueFromVariable, TOOLSPANEL_DROPDOWNMENU_PROPS } from './utils';
25
+ import { getValueFromVariable, useToolsPanelDropdownMenuProps } from './utils';
26
26
  import SpacingSizesControl from '../spacing-sizes-control';
27
27
  import HeightControl from '../height-control';
28
28
  import ChildLayoutControl from '../child-layout-control';
@@ -175,6 +175,7 @@ function DimensionsToolsPanel( {
175
175
  panelId,
176
176
  children,
177
177
  } ) {
178
+ const dropdownMenuProps = useToolsPanelDropdownMenuProps();
178
179
  const resetAll = () => {
179
180
  const updatedValue = resetAllFilter( value );
180
181
  onChange( updatedValue );
@@ -185,7 +186,7 @@ function DimensionsToolsPanel( {
185
186
  label={ __( 'Dimensions' ) }
186
187
  resetAll={ resetAll }
187
188
  panelId={ panelId }
188
- dropdownMenuProps={ TOOLSPANEL_DROPDOWNMENU_PROPS }
189
+ dropdownMenuProps={ dropdownMenuProps }
189
190
  >
190
191
  { children }
191
192
  </ToolsPanel>
@@ -28,7 +28,7 @@ import { useCallback, useMemo } from '@wordpress/element';
28
28
  /**
29
29
  * Internal dependencies
30
30
  */
31
- import { getValueFromVariable, TOOLSPANEL_DROPDOWNMENU_PROPS } from './utils';
31
+ import { getValueFromVariable, useToolsPanelDropdownMenuProps } from './utils';
32
32
  import { setImmutably } from '../../utils/object';
33
33
 
34
34
  const EMPTY_ARRAY = [];
@@ -72,6 +72,7 @@ function FiltersToolsPanel( {
72
72
  panelId,
73
73
  children,
74
74
  } ) {
75
+ const dropdownMenuProps = useToolsPanelDropdownMenuProps();
75
76
  const resetAll = () => {
76
77
  const updatedValue = resetAllFilter( value );
77
78
  onChange( updatedValue );
@@ -82,7 +83,7 @@ function FiltersToolsPanel( {
82
83
  label={ _x( 'Filters', 'Name for applying graphical effects' ) }
83
84
  resetAll={ resetAll }
84
85
  panelId={ panelId }
85
- dropdownMenuProps={ TOOLSPANEL_DROPDOWNMENU_PROPS }
86
+ dropdownMenuProps={ dropdownMenuProps }
86
87
  >
87
88
  { children }
88
89
  </ToolsPanel>
@@ -11,7 +11,7 @@ import { __, _x } from '@wordpress/i18n';
11
11
  /**
12
12
  * Internal dependencies
13
13
  */
14
- import { TOOLSPANEL_DROPDOWNMENU_PROPS } from './utils';
14
+ import { useToolsPanelDropdownMenuProps } from './utils';
15
15
 
16
16
  export function useHasImageSettingsPanel( name, value, inheritedValue ) {
17
17
  // Note: If lightbox `value` exists, that means it was
@@ -30,6 +30,7 @@ export default function ImageSettingsPanel( {
30
30
  inheritedValue,
31
31
  panelId,
32
32
  } ) {
33
+ const dropdownMenuProps = useToolsPanelDropdownMenuProps();
33
34
  const resetLightbox = () => {
34
35
  onChange( undefined );
35
36
  };
@@ -52,7 +53,7 @@ export default function ImageSettingsPanel( {
52
53
  label={ _x( 'Settings', 'Image settings' ) }
53
54
  resetAll={ resetLightbox }
54
55
  panelId={ panelId }
55
- dropdownMenuProps={ TOOLSPANEL_DROPDOWNMENU_PROPS }
56
+ dropdownMenuProps={ dropdownMenuProps }
56
57
  >
57
58
  <ToolsPanelItem
58
59
  // We use the `userSettings` prop instead of `settings`, because `settings`
@@ -21,7 +21,7 @@ import TextAlignmentControl from '../text-alignment-control';
21
21
  import TextTransformControl from '../text-transform-control';
22
22
  import TextDecorationControl from '../text-decoration-control';
23
23
  import WritingModeControl from '../writing-mode-control';
24
- import { getValueFromVariable, TOOLSPANEL_DROPDOWNMENU_PROPS } from './utils';
24
+ import { getValueFromVariable, useToolsPanelDropdownMenuProps } from './utils';
25
25
  import { setImmutably } from '../../utils/object';
26
26
 
27
27
  const MIN_TEXT_COLUMNS = 1;
@@ -135,6 +135,7 @@ function TypographyToolsPanel( {
135
135
  panelId,
136
136
  children,
137
137
  } ) {
138
+ const dropdownMenuProps = useToolsPanelDropdownMenuProps();
138
139
  const resetAll = () => {
139
140
  const updatedValue = resetAllFilter( value );
140
141
  onChange( updatedValue );
@@ -145,7 +146,7 @@ function TypographyToolsPanel( {
145
146
  label={ __( 'Typography' ) }
146
147
  resetAll={ resetAll }
147
148
  panelId={ panelId }
148
- dropdownMenuProps={ TOOLSPANEL_DROPDOWNMENU_PROPS }
149
+ dropdownMenuProps={ dropdownMenuProps }
149
150
  >
150
151
  { children }
151
152
  </ToolsPanel>
@@ -656,7 +656,7 @@ export const getNodesWithStyles = ( tree, blockSelectors ) => {
656
656
  }
657
657
  const variationSelector =
658
658
  blockSelectors[ blockName ]
659
- .styleVariationSelectors?.[ variationName ];
659
+ ?.styleVariationSelectors?.[ variationName ];
660
660
 
661
661
  // Process the variation's inner element styles.
662
662
  // This comes before the inner block styles so the
@@ -685,18 +685,18 @@ export const getNodesWithStyles = ( tree, blockSelectors ) => {
685
685
  const variationBlockSelector = scopeSelector(
686
686
  variationSelector,
687
687
  blockSelectors[ variationBlockName ]
688
- .selector
688
+ ?.selector
689
689
  );
690
690
  const variationDuotoneSelector = scopeSelector(
691
691
  variationSelector,
692
692
  blockSelectors[ variationBlockName ]
693
- .duotoneSelector
693
+ ?.duotoneSelector
694
694
  );
695
695
  const variationFeatureSelectors =
696
696
  scopeFeatureSelectors(
697
697
  variationSelector,
698
698
  blockSelectors[ variationBlockName ]
699
- .featureSelectors
699
+ ?.featureSelectors
700
700
  );
701
701
 
702
702
  const variationBlockStyleNodes =
@@ -713,10 +713,10 @@ export const getNodesWithStyles = ( tree, blockSelectors ) => {
713
713
  featureSelectors: variationFeatureSelectors,
714
714
  fallbackGapValue:
715
715
  blockSelectors[ variationBlockName ]
716
- .fallbackGapValue,
716
+ ?.fallbackGapValue,
717
717
  hasLayoutSupport:
718
718
  blockSelectors[ variationBlockName ]
719
- .hasLayoutSupport,
719
+ ?.hasLayoutSupport,
720
720
  styles: variationBlockStyleNodes,
721
721
  } );
722
722
 
@@ -924,8 +924,8 @@ export const toStyles = (
924
924
  ruleset += `padding-right: 0; padding-left: 0; padding-top: var(--wp--style--root--padding-top); padding-bottom: var(--wp--style--root--padding-bottom) }
925
925
  .has-global-padding { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }
926
926
  .has-global-padding > .alignfull { margin-right: calc(var(--wp--style--root--padding-right) * -1); margin-left: calc(var(--wp--style--root--padding-left) * -1); }
927
- .has-global-padding :where(:not(.alignfull.is-layout-flow) > .has-global-padding:not(.wp-block-block, .alignfull, .alignwide)) { padding-right: 0; padding-left: 0; }
928
- .has-global-padding :where(:not(.alignfull.is-layout-flow) > .has-global-padding:not(.wp-block-block, .alignfull, .alignwide)) > .alignfull { margin-left: 0; margin-right: 0;
927
+ .has-global-padding :where(:not(.alignfull.is-layout-flow) > .has-global-padding:not(.wp-block-block, .alignfull)) { padding-right: 0; padding-left: 0; }
928
+ .has-global-padding :where(:not(.alignfull.is-layout-flow) > .has-global-padding:not(.wp-block-block, .alignfull)) > .alignfull { margin-left: 0; margin-right: 0;
929
929
  `;
930
930
  }
931
931
 
@@ -3,6 +3,11 @@
3
3
  */
4
4
  import fastDeepEqual from 'fast-deep-equal/es6';
5
5
 
6
+ /**
7
+ * WordPress dependencies
8
+ */
9
+ import { useViewportMatch } from '@wordpress/compose';
10
+
6
11
  /**
7
12
  * Internal dependencies
8
13
  */
@@ -136,12 +141,18 @@ export const STYLE_PATH_TO_PRESET_BLOCK_ATTRIBUTE = {
136
141
  'typography.fontFamily': 'fontFamily',
137
142
  };
138
143
 
139
- export const TOOLSPANEL_DROPDOWNMENU_PROPS = {
140
- popoverProps: {
141
- placement: 'left-start',
142
- offset: 259, // Inner sidebar width (248px) - button width (24px) - border (1px) + padding (16px) + spacing (20px)
143
- },
144
- };
144
+ export function useToolsPanelDropdownMenuProps() {
145
+ const isMobile = useViewportMatch( 'medium', '<' );
146
+ return ! isMobile
147
+ ? {
148
+ popoverProps: {
149
+ placement: 'left-start',
150
+ // For non-mobile, inner sidebar width (248px) - button width (24px) - border (1px) + padding (16px) + spacing (20px)
151
+ offset: 259,
152
+ },
153
+ }
154
+ : {};
155
+ }
145
156
 
146
157
  function findInPresetsBy(
147
158
  features,
@@ -11,6 +11,7 @@
11
11
  .block-editor-iframe__scale-container {
12
12
  width: 100%;
13
13
  height: 100%;
14
+ display: flex;
14
15
  }
15
16
 
16
17
  .block-editor-iframe__scale-container.is-zoomed-out {
@@ -10,7 +10,7 @@ import { useCallback } from '@wordpress/element';
10
10
  */
11
11
  import { store as blockEditorStore } from '../../store';
12
12
  import { cleanEmptyObject } from '../../hooks/utils';
13
- import { TOOLSPANEL_DROPDOWNMENU_PROPS } from '../global-styles/utils';
13
+ import { useToolsPanelDropdownMenuProps } from '../global-styles/utils';
14
14
 
15
15
  export default function BlockSupportToolsPanel( { children, group, label } ) {
16
16
  const { updateBlockAttributes } = useDispatch( blockEditorStore );
@@ -20,7 +20,7 @@ export default function BlockSupportToolsPanel( { children, group, label } ) {
20
20
  getSelectedBlockClientId,
21
21
  hasMultiSelection,
22
22
  } = useSelect( blockEditorStore );
23
-
23
+ const dropdownMenuProps = useToolsPanelDropdownMenuProps();
24
24
  const panelId = getSelectedBlockClientId();
25
25
  const resetAll = useCallback(
26
26
  ( resetFilters = [] ) => {
@@ -72,7 +72,7 @@ export default function BlockSupportToolsPanel( { children, group, label } ) {
72
72
  shouldRenderPlaceholderItems // Required to maintain fills ordering.
73
73
  __experimentalFirstVisibleItemClass="first"
74
74
  __experimentalLastVisibleItemClass="last"
75
- dropdownMenuProps={ TOOLSPANEL_DROPDOWNMENU_PROPS }
75
+ dropdownMenuProps={ dropdownMenuProps }
76
76
  >
77
77
  { children }
78
78
  </ToolsPanel>
@@ -14,8 +14,10 @@ import {
14
14
  getBlockSelectors,
15
15
  } from '../components/global-styles';
16
16
  import { useStyleOverride } from './utils';
17
+ import { getValueFromObjectPath } from '../utils/object';
17
18
  import { store as blockEditorStore } from '../store';
18
19
  import { globalStylesDataKey } from '../store/private-keys';
20
+ import { unlock } from '../lock-unlock';
19
21
 
20
22
  const VARIATION_PREFIX = 'is-style-';
21
23
 
@@ -59,7 +61,198 @@ function getVariationNameFromClass( className, registeredStyles = [] ) {
59
61
  return null;
60
62
  }
61
63
 
62
- function useBlockSyleVariation( name, variation, clientId ) {
64
+ // A helper component to apply a style override using the useStyleOverride hook.
65
+ function OverrideStyles( { override } ) {
66
+ useStyleOverride( override );
67
+ }
68
+
69
+ /**
70
+ * This component is used to generate new block style variation overrides
71
+ * based on an incoming theme config. If a matching style is found in the config,
72
+ * a new override is created and returned. The overrides can be used in conjunction with
73
+ * useStyleOverride to apply the new styles to the editor. Its use is
74
+ * subject to change.
75
+ *
76
+ * @param {Object} props Props.
77
+ * @param {Object} props.config A global styles object, containing settings and styles.
78
+ * @return {JSX.Element|undefined} An array of new block variation overrides.
79
+ */
80
+ export function __unstableBlockStyleVariationOverridesWithConfig( { config } ) {
81
+ const { getBlockStyles, overrides } = useSelect(
82
+ ( select ) => ( {
83
+ getBlockStyles: select( blocksStore ).getBlockStyles,
84
+ overrides: unlock( select( blockEditorStore ) ).getStyleOverrides(),
85
+ } ),
86
+ []
87
+ );
88
+ const { getBlockName } = useSelect( blockEditorStore );
89
+
90
+ const overridesWithConfig = useMemo( () => {
91
+ if ( ! overrides?.length ) {
92
+ return;
93
+ }
94
+ const newOverrides = [];
95
+ const overriddenClientIds = [];
96
+ for ( const [ , override ] of overrides ) {
97
+ if (
98
+ override?.variation &&
99
+ override?.clientId &&
100
+ /*
101
+ * Because this component overwrites existing style overrides,
102
+ * filter out any overrides that are already present in the store.
103
+ */
104
+ ! overriddenClientIds.includes( override.clientId )
105
+ ) {
106
+ const blockName = getBlockName( override.clientId );
107
+ const configStyles =
108
+ config?.styles?.blocks?.[ blockName ]?.variations?.[
109
+ override.variation
110
+ ];
111
+ if ( configStyles ) {
112
+ const variationConfig = {
113
+ settings: config?.settings,
114
+ // The variation style data is all that is needed to generate
115
+ // the styles for the current application to a block. The variation
116
+ // name is updated to match the instance specific class name.
117
+ styles: {
118
+ blocks: {
119
+ [ blockName ]: {
120
+ variations: {
121
+ [ `${ override.variation }-${ override.clientId }` ]:
122
+ configStyles,
123
+ },
124
+ },
125
+ },
126
+ },
127
+ };
128
+ const blockSelectors = getBlockSelectors(
129
+ getBlockTypes(),
130
+ getBlockStyles,
131
+ override.clientId
132
+ );
133
+ const hasBlockGapSupport = false;
134
+ const hasFallbackGapSupport = true;
135
+ const disableLayoutStyles = true;
136
+ const disableRootPadding = true;
137
+ const variationStyles = toStyles(
138
+ variationConfig,
139
+ blockSelectors,
140
+ hasBlockGapSupport,
141
+ hasFallbackGapSupport,
142
+ disableLayoutStyles,
143
+ disableRootPadding,
144
+ {
145
+ blockGap: false,
146
+ blockStyles: true,
147
+ layoutStyles: false,
148
+ marginReset: false,
149
+ presets: false,
150
+ rootPadding: false,
151
+ variationStyles: true,
152
+ }
153
+ );
154
+ newOverrides.push( {
155
+ id: `${ override.variation }-${ override.clientId }`,
156
+ css: variationStyles,
157
+ __unstableType: 'variation',
158
+ variation: override.variation,
159
+ // The clientId will be stored with the override and used to ensure
160
+ // the order of overrides matches the order of blocks so that the
161
+ // correct CSS cascade is maintained.
162
+ clientId: override.clientId,
163
+ } );
164
+ overriddenClientIds.push( override.clientId );
165
+ }
166
+ }
167
+ }
168
+ return newOverrides;
169
+ }, [ config, overrides, getBlockStyles, getBlockName ] );
170
+
171
+ if ( ! overridesWithConfig || ! overridesWithConfig.length ) {
172
+ return;
173
+ }
174
+
175
+ return (
176
+ <>
177
+ { overridesWithConfig.map( ( override ) => (
178
+ <OverrideStyles key={ override.id } override={ override } />
179
+ ) ) }
180
+ </>
181
+ );
182
+ }
183
+
184
+ /**
185
+ * Retrieves any variation styles data and resolves any referenced values.
186
+ *
187
+ * @param {Object} globalStyles A complete global styles object, containing settings and styles.
188
+ * @param {string} name The name of the desired block type.
189
+ * @param {variation} variation The of the block style variation to retrieve data for.
190
+ *
191
+ * @return {Object|undefined} The global styles data for the specified variation.
192
+ */
193
+ export function getVariationStylesWithRefValues(
194
+ globalStyles,
195
+ name,
196
+ variation
197
+ ) {
198
+ if ( ! globalStyles?.styles?.blocks?.[ name ]?.variations?.[ variation ] ) {
199
+ return;
200
+ }
201
+
202
+ // Helper to recursively look for `ref` values to resolve.
203
+ const replaceRefs = ( variationStyles ) => {
204
+ Object.keys( variationStyles ).forEach( ( key ) => {
205
+ const value = variationStyles[ key ];
206
+
207
+ // Only process objects.
208
+ if ( typeof value === 'object' && value !== null ) {
209
+ // Process `ref` value if present.
210
+ if ( value.ref !== undefined ) {
211
+ if (
212
+ typeof value.ref !== 'string' ||
213
+ value.ref.trim() === ''
214
+ ) {
215
+ // Remove invalid ref.
216
+ delete variationStyles[ key ];
217
+ } else {
218
+ // Resolve `ref` value.
219
+ const refValue = getValueFromObjectPath(
220
+ globalStyles,
221
+ value.ref
222
+ );
223
+
224
+ if ( refValue ) {
225
+ variationStyles[ key ] = refValue;
226
+ } else {
227
+ delete variationStyles[ key ];
228
+ }
229
+ }
230
+ } else {
231
+ // Recursively resolve `ref` values in nested objects.
232
+ replaceRefs( value );
233
+
234
+ // After recursion, if value is empty due to explicitly
235
+ // `undefined` ref value, remove it.
236
+ if ( Object.keys( value ).length === 0 ) {
237
+ delete variationStyles[ key ];
238
+ }
239
+ }
240
+ }
241
+ } );
242
+ };
243
+
244
+ // Deep clone variation node to avoid mutating it within global styles and losing refs.
245
+ const styles = JSON.parse(
246
+ JSON.stringify(
247
+ globalStyles.styles.blocks[ name ].variations[ variation ]
248
+ )
249
+ );
250
+ replaceRefs( styles );
251
+
252
+ return styles;
253
+ }
254
+
255
+ function useBlockStyleVariation( name, variation, clientId ) {
63
256
  // Prefer global styles data in GlobalStylesContext, which are available
64
257
  // if in the site editor. Otherwise fall back to whatever is in the
65
258
  // editor settings and available in the post editor.
@@ -73,9 +266,14 @@ function useBlockSyleVariation( name, variation, clientId ) {
73
266
  }, [] );
74
267
 
75
268
  return useMemo( () => {
76
- const styles = mergedConfig?.styles ?? globalStyles;
77
- const variationStyles =
78
- styles?.blocks?.[ name ]?.variations?.[ variation ];
269
+ const variationStyles = getVariationStylesWithRefValues(
270
+ {
271
+ settings: mergedConfig?.settings ?? globalSettings,
272
+ styles: mergedConfig?.styles ?? globalStyles,
273
+ },
274
+ name,
275
+ variation
276
+ );
79
277
 
80
278
  return {
81
279
  settings: mergedConfig?.settings ?? globalSettings,
@@ -112,7 +310,7 @@ function useBlockProps( { name, className, clientId } ) {
112
310
  const variation = getVariationNameFromClass( className, registeredStyles );
113
311
  const variationClass = `${ VARIATION_PREFIX }${ variation }-${ clientId }`;
114
312
 
115
- const { settings, styles } = useBlockSyleVariation(
313
+ const { settings, styles } = useBlockStyleVariation(
116
314
  name,
117
315
  variation,
118
316
  clientId
@@ -157,6 +355,7 @@ function useBlockProps( { name, className, clientId } ) {
157
355
  id: `variation-${ clientId }`,
158
356
  css: variationStyles,
159
357
  __unstableType: 'variation',
358
+ variation,
160
359
  // The clientId will be stored with the override and used to ensure
161
360
  // the order of overrides matches the order of blocks so that the
162
361
  // correct CSS cascade is maintained.
@@ -295,26 +295,30 @@ function useDuotoneStyles( {
295
295
  return;
296
296
  }
297
297
 
298
- // Safari does not always update the duotone filter when the duotone colors
299
- // are changed. When using Safari, force the block element to be repainted by
300
- // the browser to ensure any changes are reflected visually. This logic matches
301
- // that used on the site frontend in `block-supports/duotone.php`.
298
+ // Safari does not always update the duotone filter when the duotone
299
+ // colors are changed. When using Safari, force the block element to be
300
+ // repainted by the browser to ensure any changes are reflected
301
+ // visually. This logic matches that used on the site frontend in
302
+ // `block-supports/duotone.php`.
302
303
  if ( blockElement && isSafari ) {
303
304
  const display = blockElement.style.display;
304
- // Switch to `inline-block` to force a repaint. In the editor, `inline-block`
305
- // is used instead of `none` to ensure that scroll position is not affected,
306
- // as `none` results in the editor scrolling to the top of the block.
305
+ // Switch to `inline-block` to force a repaint. In the editor,
306
+ // `inline-block` is used instead of `none` to ensure that scroll
307
+ // position is not affected, as `none` results in the editor
308
+ // scrolling to the top of the block.
307
309
  blockElement.style.display = 'inline-block';
308
- // Simply accessing el.offsetHeight flushes layout and style
309
- // changes in WebKit without having to wait for setTimeout.
310
+ // Simply accessing el.offsetHeight flushes layout and style changes
311
+ // in WebKit without having to wait for setTimeout.
310
312
  // eslint-disable-next-line no-unused-expressions
311
313
  blockElement.offsetHeight;
312
314
  blockElement.style.display = display;
313
315
  }
314
- }, [ isValidFilter, blockElement ] );
316
+ // `colors` must be a dependency so this effect runs when the colors
317
+ // change in Safari.
318
+ }, [ isValidFilter, blockElement, colors ] );
315
319
  }
316
320
 
317
- function useBlockProps( { name, style } ) {
321
+ function useBlockProps( { clientId, name, style } ) {
318
322
  const id = useInstanceId( useBlockProps );
319
323
  const selector = useMemo( () => {
320
324
  const blockType = getBlockType( name );
@@ -362,7 +366,7 @@ function useBlockProps( { name, style } ) {
362
366
  const shouldRender = selector && attribute;
363
367
 
364
368
  useDuotoneStyles( {
365
- clientId: id,
369
+ clientId,
366
370
  id: filterClass,
367
371
  selector,
368
372
  attribute,
@@ -88,3 +88,4 @@ export { getTypographyClassesAndStyles } from './use-typography-props';
88
88
  export { getGapCSSValue } from './gap';
89
89
  export { useCachedTruthy } from './use-cached-truthy';
90
90
  export { useZoomOut } from './use-zoom-out';
91
+ export { __unstableBlockStyleVariationOverridesWithConfig } from './block-style-variation';
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import { getVariationStylesWithRefValues } from '../block-style-variation';
5
+
6
+ describe( 'getVariationStylesWithRefValues', () => {
7
+ it( 'should resolve ref values correctly', () => {
8
+ const globalStyles = {
9
+ styles: {
10
+ color: { background: 'red' },
11
+ blocks: {
12
+ 'core/heading': {
13
+ color: { text: 'blue' },
14
+ },
15
+ 'core/group': {
16
+ variations: {
17
+ custom: {
18
+ color: {
19
+ text: { ref: 'styles.does-not-exist' },
20
+ background: {
21
+ ref: 'styles.color.background',
22
+ },
23
+ },
24
+ blocks: {
25
+ 'core/heading': {
26
+ color: {
27
+ text: {
28
+ ref: 'styles.blocks.core/heading.color.text',
29
+ },
30
+ background: { ref: '' },
31
+ },
32
+ },
33
+ },
34
+ elements: {
35
+ link: {
36
+ color: {
37
+ text: {
38
+ ref: 'styles.elements.link.color.text',
39
+ },
40
+ background: { ref: undefined },
41
+ },
42
+ ':hover': {
43
+ color: {
44
+ text: {
45
+ ref: 'styles.elements.link.:hover.color.text',
46
+ },
47
+ },
48
+ },
49
+ },
50
+ },
51
+ },
52
+ },
53
+ },
54
+ },
55
+ elements: {
56
+ link: {
57
+ color: { text: 'green' },
58
+ ':hover': {
59
+ color: { text: 'yellow' },
60
+ },
61
+ },
62
+ },
63
+ },
64
+ };
65
+
66
+ expect(
67
+ getVariationStylesWithRefValues(
68
+ globalStyles,
69
+ 'core/group',
70
+ 'custom'
71
+ )
72
+ ).toEqual( {
73
+ color: { background: 'red' },
74
+ blocks: {
75
+ 'core/heading': {
76
+ color: { text: 'blue' },
77
+ },
78
+ },
79
+ elements: {
80
+ link: {
81
+ color: {
82
+ text: 'green',
83
+ },
84
+ ':hover': {
85
+ color: { text: 'yellow' },
86
+ },
87
+ },
88
+ },
89
+ } );
90
+ } );
91
+ } );