@elementor/editor-editing-panel 3.33.0-99 → 3.34.2

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 (69) hide show
  1. package/dist/index.d.mts +419 -88
  2. package/dist/index.d.ts +419 -88
  3. package/dist/index.js +3361 -2421
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +3299 -2347
  6. package/dist/index.mjs.map +1 -1
  7. package/package.json +22 -21
  8. package/src/apply-unapply-actions.ts +30 -0
  9. package/src/components/collapsible-content.tsx +1 -0
  10. package/src/components/css-classes/css-class-item.tsx +14 -4
  11. package/src/components/css-classes/css-class-menu.tsx +83 -11
  12. package/src/components/css-classes/css-class-selector.tsx +22 -1
  13. package/src/components/css-classes/use-apply-and-unapply-class.ts +4 -13
  14. package/src/components/custom-css-indicator.tsx +68 -1
  15. package/src/components/editing-panel-tabs.tsx +13 -2
  16. package/src/components/interactions-tab.tsx +15 -0
  17. package/src/components/section-content.tsx +3 -2
  18. package/src/components/section.tsx +2 -1
  19. package/src/components/settings-control.tsx +79 -0
  20. package/src/components/settings-tab.tsx +16 -46
  21. package/src/components/style-sections/border-section/border-section.tsx +6 -4
  22. package/src/components/style-sections/effects-section/blend-mode-field.tsx +44 -0
  23. package/src/components/style-sections/effects-section/effects-section.tsx +4 -1
  24. package/src/components/style-sections/layout-section/flex-size-field.tsx +10 -8
  25. package/src/components/style-sections/typography-section/font-family-field.tsx +5 -1
  26. package/src/components/style-sections/typography-section/font-size-field.tsx +1 -1
  27. package/src/components/style-sections/typography-section/letter-spacing-field.tsx +1 -1
  28. package/src/components/style-sections/typography-section/text-color-field.tsx +1 -1
  29. package/src/components/style-sections/typography-section/word-spacing-field.tsx +1 -1
  30. package/src/components/style-tab-section.tsx +2 -2
  31. package/src/components/style-tab.tsx +12 -17
  32. package/src/components/styles-field-layout.tsx +8 -1
  33. package/src/contexts/interaction-context.tsx +0 -0
  34. package/src/controls-registry/conditional-field.tsx +1 -1
  35. package/src/controls-registry/control-type-container.tsx +17 -5
  36. package/src/controls-registry/controls-registry.tsx +18 -5
  37. package/src/controls-registry/element-controls/get-element-by-type.ts +21 -0
  38. package/src/controls-registry/element-controls/registry.ts +16 -0
  39. package/src/controls-registry/element-controls/tabs-control/tabs-control.tsx +229 -0
  40. package/src/controls-registry/element-controls/tabs-control/use-actions.ts +248 -0
  41. package/src/controls-registry/settings-field.tsx +54 -10
  42. package/src/controls-registry/styles-field.tsx +2 -9
  43. package/src/dynamics/components/dynamic-conditional-control.tsx +62 -0
  44. package/src/dynamics/components/dynamic-selection-control.tsx +81 -25
  45. package/src/dynamics/components/dynamic-selection.tsx +3 -3
  46. package/src/dynamics/dynamic-control.tsx +10 -1
  47. package/src/dynamics/hooks/use-prop-dynamic-tags.ts +24 -6
  48. package/src/field-indicators-registry.ts +37 -0
  49. package/src/hooks/use-computed-style.ts +1 -4
  50. package/src/index.ts +16 -3
  51. package/src/init.ts +7 -0
  52. package/src/reset-style-props.tsx +21 -4
  53. package/src/styles-inheritance/components/infotip/value-component.tsx +2 -0
  54. package/src/styles-inheritance/components/styles-inheritance-indicator.tsx +1 -13
  55. package/src/styles-inheritance/components/styles-inheritance-infotip.tsx +5 -1
  56. package/src/styles-inheritance/hooks/use-normalized-inheritance-chain-items.tsx +18 -2
  57. package/src/styles-inheritance/init-styles-inheritance-transformers.ts +25 -4
  58. package/src/styles-inheritance/init.ts +9 -0
  59. package/src/styles-inheritance/transformers/{background-overlay-transformer.tsx → array-transformer.tsx} +2 -2
  60. package/src/styles-inheritance/transformers/background-color-overlay-transformer.tsx +0 -6
  61. package/src/styles-inheritance/transformers/box-shadow-transformer.tsx +32 -0
  62. package/src/styles-inheritance/transformers/color-transformer.tsx +32 -0
  63. package/src/styles-inheritance/transformers/repeater-to-items-transformer.tsx +27 -0
  64. package/src/utils/is-equal.ts +53 -0
  65. package/src/utils/prop-dependency-utils.ts +107 -19
  66. package/src/utils/tracking/subscribe.ts +7 -0
  67. package/src/components/custom-css-field.tsx +0 -20
  68. package/src/components/custom-css.tsx +0 -36
  69. package/src/components/style-sections/border-section/border-field.tsx +0 -54
@@ -0,0 +1,32 @@
1
+ import * as React from 'react';
2
+ import { createTransformer } from '@elementor/editor-canvas';
3
+ import { Stack } from '@elementor/ui';
4
+
5
+ type Shadow = {
6
+ hOffset?: string;
7
+ vOffset?: string;
8
+ blur?: string;
9
+ spread?: string;
10
+ color?: string;
11
+ position?: string | null;
12
+ };
13
+
14
+ export const boxShadowTransformer = createTransformer( ( value: Shadow ) => {
15
+ if ( ! value ) {
16
+ return null;
17
+ }
18
+
19
+ const { color, hOffset, vOffset, blur, spread, position } = value;
20
+
21
+ const colorValue = color || '#000000';
22
+ const sizes = [ hOffset || '0px', vOffset || '0px', blur || '10px', spread || '0px' ].join( ' ' );
23
+ const positionValue = position || 'outset';
24
+
25
+ return (
26
+ <Stack direction="column" gap={ 0.5 } pb={ 1 }>
27
+ <span>
28
+ { colorValue } { positionValue }, { sizes }
29
+ </span>
30
+ </Stack>
31
+ );
32
+ } );
@@ -0,0 +1,32 @@
1
+ import * as React from 'react';
2
+ import { createTransformer } from '@elementor/editor-canvas';
3
+ import { Stack, styled, UnstableColorIndicator } from '@elementor/ui';
4
+
5
+ function isValidCSSColor( value: string ): boolean {
6
+ if ( ! value.trim() ) {
7
+ return false;
8
+ }
9
+
10
+ return CSS.supports( 'color', value.trim() );
11
+ }
12
+
13
+ const StyledColorIndicator = styled( UnstableColorIndicator )( ( { theme } ) => ( {
14
+ width: '1em',
15
+ height: '1em',
16
+ borderRadius: `${ theme.shape.borderRadius / 2 }px`,
17
+ outline: `1px solid ${ theme.palette.action.disabled }`,
18
+ flexShrink: 0,
19
+ } ) );
20
+
21
+ export const colorTransformer = createTransformer( ( value: string ) => {
22
+ if ( ! isValidCSSColor( value ) ) {
23
+ return value;
24
+ }
25
+
26
+ return (
27
+ <Stack direction="row" gap={ 1 } alignItems="center">
28
+ <StyledColorIndicator size="inherit" component="span" value={ value } />
29
+ <span>{ value }</span>
30
+ </Stack>
31
+ );
32
+ } );
@@ -0,0 +1,27 @@
1
+ import * as React from 'react';
2
+ import { type AnyTransformer, createTransformer } from '@elementor/editor-canvas';
3
+ import { Stack } from '@elementor/ui';
4
+
5
+ export const createRepeaterToItemsTransformer = ( originalTransformer: AnyTransformer, separator: string = ' ' ) => {
6
+ return createTransformer( ( value: string, options: { key: string; signal?: AbortSignal } ) => {
7
+ const stringResult = originalTransformer( value, options );
8
+
9
+ if ( ! stringResult || typeof stringResult !== 'string' ) {
10
+ return stringResult;
11
+ }
12
+
13
+ const parts = stringResult.split( separator ).filter( Boolean );
14
+
15
+ if ( parts.length <= 1 ) {
16
+ return stringResult;
17
+ }
18
+
19
+ return (
20
+ <Stack direction="column" gap={ 0.5 }>
21
+ { parts.map( ( part, index ) => (
22
+ <Stack key={ index }>{ part.trim() }</Stack>
23
+ ) ) }
24
+ </Stack>
25
+ );
26
+ } );
27
+ };
@@ -0,0 +1,53 @@
1
+ export function isEqual( a: unknown, b: unknown ): boolean {
2
+ if ( a === b ) {
3
+ return true;
4
+ }
5
+
6
+ if ( a === null || b === null ) {
7
+ return false;
8
+ }
9
+
10
+ if ( typeof a !== typeof b ) {
11
+ return false;
12
+ }
13
+
14
+ if ( Array.isArray( a ) && Array.isArray( b ) ) {
15
+ if ( a.length !== b.length ) {
16
+ return false;
17
+ }
18
+
19
+ for ( let i = 0; i < a.length; i++ ) {
20
+ if ( ! isEqual( a[ i ], b[ i ] ) ) {
21
+ return false;
22
+ }
23
+ }
24
+
25
+ return true;
26
+ }
27
+
28
+ if ( typeof a === 'object' && typeof b === 'object' ) {
29
+ const objA = a as Record< string, unknown >;
30
+ const objB = b as Record< string, unknown >;
31
+
32
+ const keysA = Object.keys( objA );
33
+ const keysB = Object.keys( objB );
34
+
35
+ if ( keysA.length !== keysB.length ) {
36
+ return false;
37
+ }
38
+
39
+ for ( const key of keysA ) {
40
+ if ( ! ( key in objB ) ) {
41
+ return false;
42
+ }
43
+
44
+ if ( ! isEqual( objA[ key ], objB[ key ] ) ) {
45
+ return false;
46
+ }
47
+ }
48
+
49
+ return true;
50
+ }
51
+
52
+ return false;
53
+ }
@@ -1,10 +1,12 @@
1
1
  import {
2
+ type DependencyTerm,
2
3
  extractValue,
3
4
  isDependencyMet,
4
5
  type PropsSchema,
5
6
  type PropType,
6
7
  type TransformablePropValue,
7
8
  } from '@elementor/editor-props';
9
+ import { getSessionStorageItem, removeSessionStorageItem, setSessionStorageItem } from '@elementor/session';
8
10
 
9
11
  type Value = TransformablePropValue< string > | null;
10
12
 
@@ -61,11 +63,12 @@ function extractPropOrderedDependencies(
61
63
  );
62
64
  }
63
65
 
64
- export function updateValues(
66
+ export function getUpdatedValues(
65
67
  values: Values,
66
68
  dependencies: string[],
67
69
  propsSchema: PropsSchema,
68
- elementValues: Values
70
+ elementValues: Values,
71
+ elementId: string
69
72
  ): Values {
70
73
  if ( ! dependencies.length ) {
71
74
  return values;
@@ -81,10 +84,34 @@ export function updateValues(
81
84
  return newValues;
82
85
  }
83
86
 
84
- if ( ! isDependencyMet( propType?.dependencies, combinedValues ) ) {
87
+ const testDependencies = {
88
+ previousValues: isDependencyMet( propType.dependencies, elementValues ),
89
+ newValues: isDependencyMet( propType.dependencies, combinedValues ),
90
+ };
91
+
92
+ if ( ! testDependencies.newValues.isMet ) {
93
+ const newValue = handleUnmetCondition( {
94
+ failingDependencies: testDependencies.newValues.failingDependencies,
95
+ dependency,
96
+ elementValues: combinedValues,
97
+ defaultValue: propType.default as Value,
98
+ elementId,
99
+ } );
100
+
101
+ return {
102
+ ...newValues,
103
+ ...updateValue( path, newValue, combinedValues ),
104
+ };
105
+ }
106
+
107
+ if ( ! testDependencies.previousValues.isMet ) {
108
+ const savedValue = retrievePreviousValueFromStorage< Value >( { path: dependency, elementId } );
109
+
110
+ removePreviousValueFromStorage( { path: dependency, elementId } );
111
+
85
112
  return {
86
113
  ...newValues,
87
- ...updateValue( path, null, combinedValues ),
114
+ ...updateValue( path, savedValue ?? ( propType.default as Value ), combinedValues ),
88
115
  };
89
116
  }
90
117
 
@@ -106,12 +133,31 @@ function getPropType( schema: PropsSchema, elementValues: Values, path: string[]
106
133
  return null;
107
134
  }
108
135
 
109
- return keys.reduce( ( prop: PropType | null, key, index ) => {
110
- if ( ! prop?.kind ) {
111
- return null;
112
- }
136
+ return keys.reduce(
137
+ ( prop: PropType | null, key, index ) =>
138
+ evaluatePropType( { prop, key, index, path, elementValues, basePropKey } ),
139
+ baseProp
140
+ );
141
+ }
113
142
 
114
- if ( 'union' === prop.kind ) {
143
+ function evaluatePropType( props: {
144
+ prop: PropType | null;
145
+ key: string;
146
+ index: number;
147
+ path: string[];
148
+ elementValues: Values;
149
+ basePropKey: string;
150
+ } ) {
151
+ const { prop } = props;
152
+
153
+ if ( ! prop?.kind ) {
154
+ return null;
155
+ }
156
+
157
+ const { key, index, path, elementValues, basePropKey } = props;
158
+
159
+ switch ( prop.kind ) {
160
+ case 'union':
115
161
  const value = extractValue( path.slice( 0, index + 1 ), elementValues );
116
162
  const type = ( value?.$$type as string ) ?? null;
117
163
 
@@ -120,18 +166,13 @@ function getPropType( schema: PropsSchema, elementValues: Values, path: string[]
120
166
  elementValues,
121
167
  path.slice( 0, index + 2 )
122
168
  );
123
- }
124
-
125
- if ( 'array' === prop.kind ) {
169
+ case 'array':
126
170
  return prop.item_prop_type;
127
- }
128
-
129
- if ( 'object' === prop.kind ) {
171
+ case 'object':
130
172
  return prop.shape[ key ];
131
- }
173
+ }
132
174
 
133
- return prop[ key as keyof typeof prop ] as PropType;
134
- }, baseProp );
175
+ return prop[ key as keyof typeof prop ] as PropType;
135
176
  }
136
177
 
137
178
  function updateValue( path: string[], value: Value, values: Values ) {
@@ -144,7 +185,7 @@ function updateValue( path: string[], value: Value, values: Values ) {
144
185
  }
145
186
 
146
187
  if ( index === path.length - 1 ) {
147
- carry[ key ] = value !== null ? ( { ...( carry[ key ] ?? {} ), value } as Value ) : null;
188
+ carry[ key ] = value ?? null;
148
189
 
149
190
  return ( carry[ key ]?.value as Values ) ?? carry.value;
150
191
  }
@@ -154,3 +195,50 @@ function updateValue( path: string[], value: Value, values: Values ) {
154
195
 
155
196
  return { [ topPropKey ]: newValue[ topPropKey ] ?? null };
156
197
  }
198
+
199
+ function handleUnmetCondition( props: {
200
+ failingDependencies: DependencyTerm[];
201
+ dependency: string;
202
+ elementValues: Values;
203
+ defaultValue: Value;
204
+ elementId: string;
205
+ } ) {
206
+ const { failingDependencies, dependency, elementValues, defaultValue, elementId } = props;
207
+ const newValue = failingDependencies.find( ( term ) => term.newValue )?.newValue ?? null;
208
+ const currentValue = extractValue( dependency.split( '.' ), elementValues ) ?? defaultValue;
209
+
210
+ savePreviousValueToStorage( {
211
+ path: dependency,
212
+ elementId,
213
+ value: currentValue,
214
+ } );
215
+
216
+ return newValue;
217
+ }
218
+
219
+ function savePreviousValueToStorage( { path, elementId, value }: { path: string; elementId: string; value: unknown } ) {
220
+ const prefix = `elementor/${ elementId }`;
221
+ const savedValue = retrievePreviousValueFromStorage( { path, elementId } );
222
+
223
+ if ( savedValue ) {
224
+ return;
225
+ }
226
+
227
+ const key = `${ prefix }:${ path }`;
228
+
229
+ setSessionStorageItem( key, value );
230
+ }
231
+
232
+ function retrievePreviousValueFromStorage< T >( { path, elementId }: { path: string; elementId: string } ) {
233
+ const prefix = `elementor/${ elementId }`;
234
+ const key = `${ prefix }:${ path }`;
235
+
236
+ return getSessionStorageItem< T >( key ) ?? null;
237
+ }
238
+
239
+ function removePreviousValueFromStorage( { path, elementId }: { path: string; elementId: string } ) {
240
+ const prefix = `elementor/${ elementId }`;
241
+ const key = `${ prefix }:${ path }`;
242
+
243
+ removeSessionStorageItem( key );
244
+ }
@@ -0,0 +1,7 @@
1
+ import { stylesRepository } from '@elementor/editor-styles-repository';
2
+
3
+ export const trackStyles = ( provider: string, event: string, data: Record< string, unknown > ) => {
4
+ const providerInstance = stylesRepository.getProviderByKey( provider );
5
+
6
+ providerInstance?.actions.tracking?.( { event, ...data } );
7
+ };
@@ -1,20 +0,0 @@
1
- import * as React from 'react';
2
- import { type PropsWithChildren } from 'react';
3
- import { ControlAdornmentsProvider } from '@elementor/editor-controls';
4
-
5
- import { CustomCssIndicator } from './custom-css-indicator';
6
-
7
- export const CustomCssField = ( { children }: PropsWithChildren< object > ) => {
8
- return (
9
- <ControlAdornmentsProvider
10
- items={ [
11
- {
12
- id: 'custom-css-indicator',
13
- Adornment: CustomCssIndicator,
14
- },
15
- ] }
16
- >
17
- { children }
18
- </ControlAdornmentsProvider>
19
- );
20
- };
@@ -1,36 +0,0 @@
1
- import * as React from 'react';
2
- import { ControlAdornments, ControlFormLabel, CssEditor } from '@elementor/editor-controls';
3
- import { Stack } from '@elementor/ui';
4
- import { __ } from '@wordpress/i18n';
5
-
6
- import { useCustomCss } from '../hooks/use-custom-css';
7
- import { CustomCssField } from './custom-css-field';
8
- import { SectionContent } from './section-content';
9
-
10
- export const CustomCss = () => {
11
- const { customCss, setCustomCss } = useCustomCss();
12
- const [ localState, setLocalState ] = React.useState( {
13
- value: customCss?.raw || '',
14
- isValid: true,
15
- } );
16
-
17
- const handleChange = ( value: string, isValid: boolean ) => {
18
- setLocalState( { value, isValid } );
19
-
20
- if ( isValid ) {
21
- setCustomCss( value, { history: { propDisplayName: 'Custom CSS' } } );
22
- }
23
- };
24
-
25
- return (
26
- <SectionContent>
27
- <CustomCssField>
28
- <Stack direction="row" alignItems="center" gap={ 1 }>
29
- <ControlFormLabel>{ __( 'CSS code', 'elementor' ) }</ControlFormLabel>
30
- <ControlAdornments />
31
- </Stack>
32
- </CustomCssField>
33
- <CssEditor value={ localState.value } onChange={ handleChange } />
34
- </SectionContent>
35
- );
36
- };
@@ -1,54 +0,0 @@
1
- import * as React from 'react';
2
- import { ControlFormLabel } from '@elementor/editor-controls';
3
- import { __ } from '@wordpress/i18n';
4
-
5
- import { useStylesFields } from '../../../hooks/use-styles-fields';
6
- import { AddOrRemoveContent } from '../../add-or-remove-content';
7
- import { BorderColorField } from './border-color-field';
8
- import { BorderStyleField } from './border-style-field';
9
- import { BorderWidthField } from './border-width-field';
10
-
11
- const BORDER_LABEL = __( 'Border', 'elementor' );
12
-
13
- const initialBorder = {
14
- 'border-width': { $$type: 'size', value: { size: 1, unit: 'px' } },
15
- 'border-color': { $$type: 'color', value: '#000000' },
16
- 'border-style': { $$type: 'string', value: 'solid' },
17
- };
18
-
19
- export const BorderField = () => {
20
- const { values, setValues, canEdit } = useStylesFields( Object.keys( initialBorder ) );
21
-
22
- const meta = { history: { propDisplayName: BORDER_LABEL } };
23
-
24
- const addBorder = () => {
25
- setValues( initialBorder, meta );
26
- };
27
-
28
- const removeBorder = () => {
29
- setValues(
30
- {
31
- 'border-width': null,
32
- 'border-color': null,
33
- 'border-style': null,
34
- },
35
- meta
36
- );
37
- };
38
-
39
- const hasBorder = Object.values( values ?? {} ).some( Boolean );
40
-
41
- return (
42
- <AddOrRemoveContent
43
- isAdded={ hasBorder }
44
- onAdd={ addBorder }
45
- onRemove={ removeBorder }
46
- disabled={ ! canEdit }
47
- renderLabel={ () => <ControlFormLabel>{ BORDER_LABEL }</ControlFormLabel> }
48
- >
49
- <BorderWidthField />
50
- <BorderColorField />
51
- <BorderStyleField />
52
- </AddOrRemoveContent>
53
- );
54
- };