@elementor/editor-components 4.1.0-741 → 4.1.0-742

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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@elementor/editor-components",
3
3
  "description": "Elementor editor components",
4
- "version": "4.1.0-741",
4
+ "version": "4.1.0-742",
5
5
  "private": false,
6
6
  "author": "Elementor Team",
7
7
  "homepage": "https://elementor.com/",
@@ -40,31 +40,31 @@
40
40
  "dev": "tsup --config=../../tsup.dev.ts"
41
41
  },
42
42
  "dependencies": {
43
- "@elementor/editor": "4.1.0-741",
44
- "@elementor/editor-canvas": "4.1.0-741",
45
- "@elementor/editor-controls": "4.1.0-741",
46
- "@elementor/editor-documents": "4.1.0-741",
47
- "@elementor/editor-editing-panel": "4.1.0-741",
48
- "@elementor/editor-elements": "4.1.0-741",
49
- "@elementor/editor-elements-panel": "4.1.0-741",
50
- "@elementor/editor-mcp": "4.1.0-741",
51
- "@elementor/editor-templates": "4.1.0-741",
52
- "@elementor/editor-panels": "4.1.0-741",
53
- "@elementor/editor-props": "4.1.0-741",
54
- "@elementor/editor-styles-repository": "4.1.0-741",
55
- "@elementor/editor-ui": "4.1.0-741",
56
- "@elementor/editor-v1-adapters": "4.1.0-741",
57
- "@elementor/http-client": "4.1.0-741",
43
+ "@elementor/editor": "4.1.0-742",
44
+ "@elementor/editor-canvas": "4.1.0-742",
45
+ "@elementor/editor-controls": "4.1.0-742",
46
+ "@elementor/editor-documents": "4.1.0-742",
47
+ "@elementor/editor-editing-panel": "4.1.0-742",
48
+ "@elementor/editor-elements": "4.1.0-742",
49
+ "@elementor/editor-elements-panel": "4.1.0-742",
50
+ "@elementor/editor-mcp": "4.1.0-742",
51
+ "@elementor/editor-templates": "4.1.0-742",
52
+ "@elementor/editor-panels": "4.1.0-742",
53
+ "@elementor/editor-props": "4.1.0-742",
54
+ "@elementor/editor-styles-repository": "4.1.0-742",
55
+ "@elementor/editor-ui": "4.1.0-742",
56
+ "@elementor/editor-v1-adapters": "4.1.0-742",
57
+ "@elementor/http-client": "4.1.0-742",
58
58
  "@elementor/icons": "^1.68.0",
59
- "@elementor/events": "4.1.0-741",
60
- "@elementor/query": "4.1.0-741",
61
- "@elementor/schema": "4.1.0-741",
62
- "@elementor/store": "4.1.0-741",
59
+ "@elementor/events": "4.1.0-742",
60
+ "@elementor/query": "4.1.0-742",
61
+ "@elementor/schema": "4.1.0-742",
62
+ "@elementor/store": "4.1.0-742",
63
63
  "@elementor/ui": "1.36.17",
64
- "@elementor/utils": "4.1.0-741",
64
+ "@elementor/utils": "4.1.0-742",
65
65
  "@wordpress/i18n": "^5.13.0",
66
- "@elementor/editor-notifications": "4.1.0-741",
67
- "@elementor/editor-current-user": "4.1.0-741"
66
+ "@elementor/editor-notifications": "4.1.0-742",
67
+ "@elementor/editor-current-user": "4.1.0-742"
68
68
  },
69
69
  "peerDependencies": {
70
70
  "react": "^18.3.1",
@@ -1,4 +1,5 @@
1
1
  import * as React from 'react';
2
+ import { useMemo } from 'react';
2
3
  import {
3
4
  ControlReplacementsProvider,
4
5
  getControlReplacements,
@@ -29,6 +30,7 @@ import {
29
30
  import {
30
31
  type ComponentInstanceOverride,
31
32
  componentInstanceOverridesPropTypeUtil,
33
+ type ComponentInstanceOverridesPropValue,
32
34
  } from '../../prop-types/component-instance-overrides-prop-type';
33
35
  import { componentInstancePropTypeUtil } from '../../prop-types/component-instance-prop-type';
34
36
  import {
@@ -44,14 +46,18 @@ import { OverridablePropProvider } from '../../provider/overridable-prop-context
44
46
  import { updateOverridableProp } from '../../store/actions/update-overridable-prop';
45
47
  import { useCurrentComponentId } from '../../store/store';
46
48
  import { type OriginPropFields, type OverridableProp, type OverridableProps } from '../../types';
47
- import { getContainerByOriginId } from '../../utils/get-container-by-origin-id';
48
49
  import { getPropTypeForComponentOverride } from '../../utils/get-prop-type-for-component-override';
49
50
  import { getMatchingOverride } from '../../utils/overridable-props-utils';
50
51
  import { resolveOverridePropValue } from '../../utils/resolve-override-prop-value';
52
+ import { resolveOverridesChain } from '../../utils/resolve-overrides-chain';
51
53
  import { ControlLabel } from '../control-label';
52
54
  import { OverrideControlInnerElementNotFoundError } from '../errors';
53
- import { useResolvedOriginValue } from './use-resolved-origin-value';
54
55
  import { correctExposedEmptyOverride } from './utils/correct-exposed-empty-override';
56
+ import {
57
+ applyOverridesToSettings,
58
+ type OverridesMapping,
59
+ unwrapOverridableSettings,
60
+ } from './utils/resolve-element-settings';
55
61
 
56
62
  type Props = {
57
63
  overrideKey: string;
@@ -92,8 +98,6 @@ function OverrideControl( { overridableProp }: InternalProps ) {
92
98
 
93
99
  const matchingOverride = getMatchingOverride( overrides, overridableProp.overrideKey );
94
100
 
95
- const recursiveOriginValue = useResolvedOriginValue( matchingOverride, overridableProp );
96
-
97
101
  if ( ! componentId ) {
98
102
  throw new Error( 'Component ID is required' );
99
103
  }
@@ -102,14 +106,59 @@ function OverrideControl( { overridableProp }: InternalProps ) {
102
106
  throw new Error( 'Component has no overridable props' );
103
107
  }
104
108
 
109
+ const {
110
+ elementId: originElementId,
111
+ widgetType,
112
+ elType,
113
+ propKey,
114
+ } = overridableProp.originPropFields ?? overridableProp;
115
+ const type = elType === 'widget' ? widgetType : elType;
116
+ const elementType = getElementType( type );
117
+
118
+ const { elementId, overridesMapping } = useMemo( () => {
119
+ const overridesChainResult = resolveOverridesChain( {
120
+ outerOverridableProp: overridableProp,
121
+ outerInstanceId: componentInstanceElement.element.id,
122
+ } );
123
+
124
+ if ( overridesChainResult.isChainBroken ) {
125
+ throw new OverrideControlInnerElementNotFoundError( {
126
+ context: { componentId, elementId: originElementId },
127
+ } );
128
+ }
129
+
130
+ return {
131
+ elementId: overridesChainResult.innerElement.id,
132
+ overridesMapping: overridesChainResult.overridesMapping,
133
+ };
134
+ }, [ overridableProp, componentInstanceElement.element.id, componentId, originElementId ] );
135
+
136
+ // Not reactive to inner element store changes — intentional.
137
+ // Inner element settings can only change in component edit mode, which unmounts this component.
138
+ const settingsWithInnerOverrides = useMemo( () => {
139
+ const settings = getElementSettings< AnyTransformable >(
140
+ elementId,
141
+ Object.keys( elementType?.propsSchema ?? {} )
142
+ );
143
+
144
+ return applyOverridesToSettings( settings, overridesMapping );
145
+ }, [ elementId, elementType?.propsSchema, overridesMapping ] );
146
+
147
+ const resolvedElementSettings = useMemo( () => {
148
+ const withAllOverrides = applyOverridesToSettings(
149
+ settingsWithInnerOverrides,
150
+ formatOverridesToApply( overrides )
151
+ );
152
+ return unwrapOverridableSettings( withAllOverrides );
153
+ }, [ settingsWithInnerOverrides, overrides ] );
154
+
105
155
  const propType = getPropTypeForComponentOverride( overridableProp );
106
156
 
107
- if ( ! propType ) {
157
+ if ( ! propType || ! elementType ) {
108
158
  return null;
109
159
  }
110
160
 
111
- const resolvedOverrideValue = matchingOverride ? resolveOverridePropValue( matchingOverride ) : null;
112
- const propValue = resolvedOverrideValue ?? recursiveOriginValue ?? overridableProp.originValue;
161
+ const propValue = resolvedElementSettings[ propKey ];
113
162
 
114
163
  const value = {
115
164
  [ overridableProp.overrideKey ]: propValue,
@@ -161,8 +210,13 @@ function OverrideControl( { overridableProp }: InternalProps ) {
161
210
  return;
162
211
  }
163
212
 
164
- const { elType, widgetType, propKey, elementId } = overridableProp;
165
- updateOverridableProp( wrappingComponentId, overridableValue, { elType, widgetType, propKey, elementId } );
213
+ const originPropFields = {
214
+ elType: overridableProp.elType,
215
+ widgetType: overridableProp.widgetType,
216
+ propKey: overridableProp.propKey,
217
+ elementId: overridableProp.elementId,
218
+ };
219
+ updateOverridableProp( wrappingComponentId, overridableValue, originPropFields );
166
220
  }
167
221
  };
168
222
 
@@ -172,29 +226,6 @@ function OverrideControl( { overridableProp }: InternalProps ) {
172
226
  overridableProp.label
173
227
  );
174
228
 
175
- const {
176
- elementId: originElementId,
177
- widgetType,
178
- elType,
179
- propKey,
180
- } = overridableProp.originPropFields ?? overridableProp;
181
-
182
- const element = getContainerByOriginId( originElementId, componentInstanceElement.element.id );
183
-
184
- if ( ! element ) {
185
- throw new OverrideControlInnerElementNotFoundError( { context: { componentId, elementId: originElementId } } );
186
- }
187
- const elementId = element.id;
188
-
189
- const type = elType === 'widget' ? widgetType : elType;
190
- const elementType = getElementType( type );
191
-
192
- if ( ! elementType ) {
193
- return null;
194
- }
195
-
196
- const settings = getElementSettings< AnyTransformable >( elementId, Object.keys( elementType.propsSchema ) );
197
-
198
229
  const propTypeSchema = createTopLevelObjectType( {
199
230
  schema: {
200
231
  [ overridableProp.overrideKey ]: propType,
@@ -206,7 +237,11 @@ function OverrideControl( { overridableProp }: InternalProps ) {
206
237
  value={ componentOverridablePropTypeUtil.extract( matchingOverride ) ?? undefined }
207
238
  componentInstanceElement={ componentInstanceElement }
208
239
  >
209
- <ElementProvider element={ { id: elementId, type } } elementType={ elementType } settings={ settings }>
240
+ <ElementProvider
241
+ element={ { id: elementId, type } }
242
+ elementType={ elementType }
243
+ settings={ resolvedElementSettings }
244
+ >
210
245
  <SettingsField bind={ propKey } propDisplayName={ overridableProp.label }>
211
246
  <PropProvider
212
247
  propType={ propTypeSchema }
@@ -344,3 +379,31 @@ function isValidOverride( overridableProps: OverridableProps, override: Componen
344
379
 
345
380
  return !! overridableProps.props[ overridableKey ];
346
381
  }
382
+
383
+ function formatOverridesToApply( overrides: ComponentInstanceOverridesPropValue ): OverridesMapping {
384
+ if ( ! overrides ) {
385
+ return {};
386
+ }
387
+
388
+ const result: OverridesMapping = {};
389
+
390
+ for ( const item of overrides ) {
391
+ const overridable = componentOverridablePropTypeUtil.extract( item );
392
+ let override: PropValue = item;
393
+
394
+ if ( overridable ) {
395
+ override = overridable.origin_value;
396
+ }
397
+
398
+ const extractedOverride = componentInstanceOverridePropTypeUtil.extract( override );
399
+ if ( ! extractedOverride ) {
400
+ continue;
401
+ }
402
+
403
+ result[ extractedOverride.override_key ] = {
404
+ value: extractedOverride.override_value as AnyTransformable | null,
405
+ };
406
+ }
407
+
408
+ return result;
409
+ }
@@ -0,0 +1,65 @@
1
+ import { type AnyTransformable } from '@elementor/editor-props';
2
+
3
+ import { componentOverridablePropTypeUtil } from '../../../prop-types/component-overridable-prop-type';
4
+
5
+ type ElementSettings = Record< string, AnyTransformable | null >;
6
+
7
+ export type OverridesMapping = {
8
+ [ key: string ]: {
9
+ value: AnyTransformable | null;
10
+ outermostKey?: string;
11
+ };
12
+ };
13
+
14
+ export function applyOverridesToSettings(
15
+ elementSettings: ElementSettings,
16
+ overrides: OverridesMapping
17
+ ): ElementSettings {
18
+ const result: ElementSettings = {};
19
+
20
+ for ( const [ propKey, propValue ] of Object.entries( elementSettings ) ) {
21
+ const overridable = componentOverridablePropTypeUtil.extract( propValue );
22
+
23
+ if ( ! overridable ) {
24
+ result[ propKey ] = propValue;
25
+ continue;
26
+ }
27
+
28
+ const override = overrides[ overridable.override_key ];
29
+
30
+ if ( ! override ) {
31
+ result[ propKey ] = propValue;
32
+ continue;
33
+ }
34
+
35
+ if ( override.outermostKey && override.outermostKey !== overridable.override_key ) {
36
+ const originValue = overridable.origin_value as AnyTransformable | null;
37
+
38
+ result[ propKey ] = componentOverridablePropTypeUtil.create( {
39
+ override_key: override.outermostKey,
40
+ origin_value: override.value ?? originValue,
41
+ } );
42
+ } else {
43
+ result[ propKey ] = override.value ?? propValue;
44
+ }
45
+ }
46
+
47
+ return result;
48
+ }
49
+
50
+ export function unwrapOverridableSettings( elementSettings: ElementSettings ): ElementSettings {
51
+ const result: ElementSettings = {};
52
+
53
+ for ( const [ propKey, propValue ] of Object.entries( elementSettings ) ) {
54
+ const overridable = componentOverridablePropTypeUtil.extract( propValue );
55
+
56
+ if ( ! overridable ) {
57
+ result[ propKey ] = propValue;
58
+ continue;
59
+ }
60
+
61
+ result[ propKey ] = overridable.origin_value as AnyTransformable | null;
62
+ }
63
+
64
+ return result;
65
+ }
@@ -1,11 +1,5 @@
1
- import { type ComponentInstanceOverride } from '../prop-types/component-instance-overrides-prop-type';
2
- import { componentInstanceOverridesPropTypeUtil } from '../prop-types/component-instance-overrides-prop-type';
3
- import { componentInstancePropTypeUtil } from '../prop-types/component-instance-prop-type';
4
- import { componentOverridablePropTypeUtil } from '../prop-types/component-overridable-prop-type';
5
1
  import { type OverridableProp, type OverridableProps } from '../types';
6
- import { getContainerByOriginId } from './get-container-by-origin-id';
7
- import { getOverridableProp } from './get-overridable-prop';
8
- import { extractInnerOverrideInfo } from './overridable-props-utils';
2
+ import { resolveOverridesChain } from './resolve-overrides-chain';
9
3
 
10
4
  export function filterValidOverridableProps(
11
5
  overridableProps: OverridableProps,
@@ -36,59 +30,10 @@ export function filterValidOverridableProps(
36
30
  }
37
31
 
38
32
  export function isExposedPropValid( prop: OverridableProp, instanceElementId?: string ): boolean {
39
- if ( ! prop.originPropFields ) {
40
- // if no originPropFields - the prop is on the widget level itself, therefore no need to lookup for a corresponding component's overridables
41
- return true;
42
- }
43
-
44
- const innerComponentInstanceElement = getContainerByOriginId( prop.elementId, instanceElementId );
45
-
46
- if ( ! innerComponentInstanceElement ) {
47
- return false;
48
- }
49
-
50
- const setting = innerComponentInstanceElement.settings?.get( 'component_instance' ) ?? null;
51
- const componentInstance = componentInstancePropTypeUtil.extract( setting );
52
-
53
- if ( ! componentInstance?.component_id?.value ) {
54
- return false;
55
- }
56
-
57
- const overrides = componentInstanceOverridesPropTypeUtil.extract( componentInstance.overrides ) ?? undefined;
58
- const matchingOverride = findOverrideByOuterKey( overrides, prop.overrideKey );
59
- const innerOverrideInfo = extractInnerOverrideInfo( matchingOverride );
60
-
61
- if ( ! innerOverrideInfo ) {
62
- return false;
63
- }
64
-
65
- const { componentId, innerOverrideKey } = innerOverrideInfo;
66
- const innerOverridableProp = getOverridableProp( { componentId, overrideKey: innerOverrideKey } );
67
-
68
- if ( ! innerOverridableProp ) {
69
- return false;
70
- }
71
-
72
- return isExposedPropValid( innerOverridableProp, innerComponentInstanceElement.id );
73
- }
74
-
75
- function findOverrideByOuterKey(
76
- overrides: ComponentInstanceOverride[] | undefined,
77
- outerKey: string
78
- ): ComponentInstanceOverride | null {
79
- if ( ! overrides ) {
80
- return null;
81
- }
82
-
83
- return (
84
- overrides.find( ( override ) => {
85
- const overridableValue = componentOverridablePropTypeUtil.extract( override );
86
-
87
- if ( overridableValue ) {
88
- return overridableValue.override_key === outerKey;
89
- }
33
+ const { isChainBroken } = resolveOverridesChain( {
34
+ outerOverridableProp: prop,
35
+ outerInstanceId: instanceElementId,
36
+ } );
90
37
 
91
- return override.value.override_key === outerKey;
92
- } ) ?? null
93
- );
38
+ return ! isChainBroken;
94
39
  }
@@ -5,12 +5,6 @@ import {
5
5
  } from '../prop-types/component-instance-overrides-prop-type';
6
6
  import { componentOverridablePropTypeUtil } from '../prop-types/component-overridable-prop-type';
7
7
 
8
- export type InnerOverrideInfo = {
9
- componentId: number;
10
- innerOverrideKey: string;
11
- overrideValue: unknown;
12
- };
13
-
14
8
  export function getMatchingOverride(
15
9
  overrides: ComponentInstanceOverridesPropValue,
16
10
  overrideKey: string
@@ -28,31 +22,3 @@ export function getMatchingOverride(
28
22
  } ) ?? null
29
23
  );
30
24
  }
31
-
32
- export function extractInnerOverrideInfo( override: ComponentInstanceOverride | null ): InnerOverrideInfo | null {
33
- if ( ! override ) {
34
- return null;
35
- }
36
-
37
- const overridableValue = componentOverridablePropTypeUtil.extract( override );
38
- const innerOverride = overridableValue
39
- ? componentInstanceOverridePropTypeUtil.extract( overridableValue.origin_value )
40
- : componentInstanceOverridePropTypeUtil.extract( override );
41
-
42
- if ( ! innerOverride ) {
43
- return null;
44
- }
45
-
46
- const {
47
- schema_source: schemaSource,
48
- override_key: innerOverrideKey,
49
- override_value: overrideValue,
50
- } = innerOverride;
51
- const componentId = schemaSource?.id;
52
-
53
- if ( ! componentId || ! innerOverrideKey ) {
54
- return null;
55
- }
56
-
57
- return { componentId, innerOverrideKey, overrideValue };
58
- }
@@ -0,0 +1,197 @@
1
+ import { type V1Element } from '@elementor/editor-elements';
2
+ import { type AnyTransformable } from '@elementor/editor-props';
3
+
4
+ import { type OverridesMapping } from '../components/instance-editing-panel/utils/resolve-element-settings';
5
+ import {
6
+ type ComponentInstanceOverrideProp,
7
+ componentInstanceOverridePropTypeUtil,
8
+ } from '../prop-types/component-instance-override-prop-type';
9
+ import {
10
+ type ComponentInstanceOverride,
11
+ componentInstanceOverridesPropTypeUtil,
12
+ } from '../prop-types/component-instance-overrides-prop-type';
13
+ import { componentInstancePropTypeUtil } from '../prop-types/component-instance-prop-type';
14
+ import { componentOverridablePropTypeUtil } from '../prop-types/component-overridable-prop-type';
15
+ import { type OverridableProp } from '../types';
16
+ import { getContainerByOriginId } from './get-container-by-origin-id';
17
+ import { getOverridableProp } from './get-overridable-prop';
18
+
19
+ type OverridesChainResult =
20
+ | {
21
+ isChainBroken: false;
22
+ innerElement: V1Element;
23
+ overridesMapping: OverridesMapping;
24
+ }
25
+ | {
26
+ isChainBroken: true;
27
+ };
28
+
29
+ // Recursively walks down a chain of nested component instances to find the innermost element
30
+ // and collect the overrides mapping for it.
31
+ // Returns the resolved inner element with its overrides mapping,
32
+ // or { isChainBroken: true } if any level in the chain is no longer overridable.
33
+ export function resolveOverridesChain( {
34
+ outerOverridableProp,
35
+ outerInstanceId,
36
+ overridesMapping = {},
37
+ }: {
38
+ outerOverridableProp: OverridableProp;
39
+ outerInstanceId?: string;
40
+ overridesMapping?: OverridesMapping;
41
+ } ): OverridesChainResult {
42
+ // Stop condition: no originPropFields means we've reached the most inner component instance
43
+ if ( ! outerOverridableProp.originPropFields ) {
44
+ const innerElement = getContainerByOriginId( outerOverridableProp.elementId, outerInstanceId );
45
+
46
+ if ( ! innerElement ) {
47
+ throw new Error(
48
+ `Inner element not found inside instance. elementId: ${ outerOverridableProp.elementId }, instanceId: ${ outerInstanceId }`
49
+ );
50
+ }
51
+
52
+ return { isChainBroken: false, innerElement, overridesMapping };
53
+ }
54
+
55
+ // Step 1: Find the intermediate component instance and read its settings.
56
+ const currentInstance = getContainerByOriginId( outerOverridableProp.elementId, outerInstanceId );
57
+ if ( ! currentInstance ) {
58
+ // One of the instances in the chain was deleted.
59
+ return { isChainBroken: true };
60
+ }
61
+ const { componentId, overrides } = extractComponentInstanceSettings( currentInstance );
62
+
63
+ if ( ! componentId ) {
64
+ throw new Error(
65
+ `Component ID not found for current instance. currentInstanceId: ${ currentInstance.id }. outerInstanceId: ${ outerInstanceId }`
66
+ );
67
+ }
68
+
69
+ // Collect overrides from this level, translating keys for exposed-further props.
70
+ const mergedOverrides = buildOverridesMap( overridesMapping, overrides ?? [] );
71
+
72
+ // Find the overridable-override that matches the outer overridable prop's key,
73
+ // to get the next level's overridable prop.
74
+ const override = findOverrideByOuterKey( overrides, outerOverridableProp.overrideKey );
75
+ const overrideKey = componentInstanceOverridePropTypeUtil.extract( override )?.override_key;
76
+
77
+ if ( ! override || ! overrideKey ) {
78
+ // No matching override found for the current level - it means it's no longer overridable.
79
+ return { isChainBroken: true };
80
+ }
81
+
82
+ const overridableProp = getOverridableProp( { componentId, overrideKey } );
83
+
84
+ if ( ! overridableProp ) {
85
+ throw new Error( `Overridable prop not found. componentId: ${ componentId }, overrideKey: ${ overrideKey }` );
86
+ }
87
+
88
+ // Step 4: Recurse into the next nesting level.
89
+ return resolveOverridesChain( {
90
+ outerOverridableProp: overridableProp,
91
+ outerInstanceId: currentInstance.id,
92
+ overridesMapping: mergedOverrides,
93
+ } );
94
+ }
95
+
96
+ /**
97
+ * Builds overrides map from instances chain:
98
+ * At each level, we collect overrides from the current instance and merge them with the overrides from the outer levels.
99
+ *
100
+ * For exposed-further overrides (overridable wrapping an override), we have outer key (overridable's) and inner key (override's).
101
+ * If a higher level already set a value for the outer key, that value is carried forward to the inner key
102
+ * — same logic as the componentOverridableTransformer in the render pipeline.
103
+ *
104
+ * For simple overrides, that are not exposed further, we just use the override's key and value.
105
+ *
106
+ * @param existing - Previously accumulated overrides from outer levels.
107
+ * @param levelOverrides - The overrides array from the current level instance.
108
+ */
109
+ export function buildOverridesMap(
110
+ existing: OverridesMapping,
111
+ levelOverrides: ComponentInstanceOverride[]
112
+ ): OverridesMapping {
113
+ const result: OverridesMapping = { ...existing };
114
+
115
+ for ( const item of levelOverrides ) {
116
+ const overridableValue = componentOverridablePropTypeUtil.extract( item );
117
+
118
+ if ( overridableValue ) {
119
+ // Exposed-further: overridable wraps an inner override with a different key.
120
+ const override = componentInstanceOverridePropTypeUtil.extract( overridableValue.origin_value );
121
+
122
+ if ( ! override ) {
123
+ continue;
124
+ }
125
+
126
+ const outerKey = overridableValue.override_key;
127
+ const innerKey = override.override_key;
128
+ const innerValue = override.override_value as AnyTransformable | null;
129
+
130
+ // If an upper level already set a value for the outer key, carry it forward to the inner key.
131
+ const higherLevelOverride = existing[ outerKey ];
132
+
133
+ if ( higherLevelOverride ) {
134
+ const outerValue = higherLevelOverride.value;
135
+ result[ innerKey ] = {
136
+ value: outerValue ?? innerValue,
137
+ outermostKey: higherLevelOverride.outermostKey ?? outerKey,
138
+ };
139
+ continue;
140
+ }
141
+
142
+ result[ innerKey ] = {
143
+ value: innerValue,
144
+ outermostKey: outerKey,
145
+ };
146
+ } else {
147
+ // Simple override: not exposed further, we just store the override's key and value.
148
+ const override = componentInstanceOverridePropTypeUtil.extract( item );
149
+
150
+ if ( ! override ) {
151
+ continue;
152
+ }
153
+
154
+ const key = override.override_key;
155
+ const value = override.override_value as AnyTransformable | null;
156
+
157
+ result[ key ] = { value };
158
+ }
159
+ }
160
+
161
+ return result;
162
+ }
163
+
164
+ function extractComponentInstanceSettings( element: V1Element ) {
165
+ const instanceSetting = element.settings?.get( 'component_instance' );
166
+ const instanceValue = componentInstancePropTypeUtil.extract( instanceSetting );
167
+ const componentId = instanceValue?.component_id?.value;
168
+ const overrides = componentInstanceOverridesPropTypeUtil.extract( instanceValue?.overrides );
169
+
170
+ return { componentId, overrides };
171
+ }
172
+
173
+ // Finds the inner override prop whose wrapping overridable matches the given outer key.
174
+ function findOverrideByOuterKey(
175
+ overrides: ComponentInstanceOverride[] | null | undefined,
176
+ outerKey: string
177
+ ): ComponentInstanceOverrideProp | null {
178
+ if ( ! overrides ) {
179
+ return null;
180
+ }
181
+
182
+ const overridableOverride: ComponentInstanceOverride | undefined = overrides.find( ( item ) => {
183
+ const overridableValue = componentOverridablePropTypeUtil.extract( item );
184
+ if ( ! overridableValue ) {
185
+ return false;
186
+ }
187
+ return overridableValue.override_key === outerKey;
188
+ } );
189
+
190
+ const override = componentOverridablePropTypeUtil.extract( overridableOverride )?.origin_value;
191
+
192
+ if ( ! override || ! componentInstanceOverridePropTypeUtil.isValid( override ) ) {
193
+ return null;
194
+ }
195
+
196
+ return override;
197
+ }