@elementor/editor-editing-panel 1.50.0 → 3.32.0-21

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 (55) hide show
  1. package/CHANGELOG.md +0 -27
  2. package/dist/index.d.mts +78 -47
  3. package/dist/index.d.ts +78 -47
  4. package/dist/index.js +1723 -1384
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +1510 -1143
  7. package/dist/index.mjs.map +1 -1
  8. package/package.json +22 -22
  9. package/src/components/css-classes/css-class-convert-local.tsx +77 -0
  10. package/src/components/css-classes/css-class-item.tsx +15 -1
  11. package/src/components/css-classes/css-class-menu.tsx +8 -1
  12. package/src/components/css-classes/local-class-sub-menu.tsx +23 -0
  13. package/src/components/css-classes/use-apply-and-unapply-class.ts +7 -50
  14. package/src/components/css-classes/use-can-convert-local-class-to-global.ts +22 -0
  15. package/src/components/custom-css.tsx +21 -0
  16. package/src/components/editing-panel-tabs.tsx +1 -5
  17. package/src/components/section.tsx +1 -5
  18. package/src/components/settings-tab.tsx +6 -15
  19. package/src/components/style-sections/effects-section/effects-section.tsx +30 -22
  20. package/src/components/style-sections/layout-section/display-field.tsx +11 -20
  21. package/src/components/style-sections/layout-section/flex-size-field.tsx +86 -52
  22. package/src/components/style-sections/position-section/offset-field.tsx +2 -2
  23. package/src/components/style-sections/position-section/position-section.tsx +2 -8
  24. package/src/components/style-sections/size-section/size-section.tsx +16 -31
  25. package/src/components/style-sections/typography-section/typography-section.tsx +2 -19
  26. package/src/components/style-tab-collapsible-content.tsx +1 -5
  27. package/src/components/style-tab-section.tsx +1 -5
  28. package/src/components/style-tab.tsx +15 -2
  29. package/src/controls-actions.ts +1 -1
  30. package/src/controls-registry/conditional-field.tsx +26 -0
  31. package/src/controls-registry/control.tsx +2 -2
  32. package/src/controls-registry/controls-registry.tsx +44 -3
  33. package/src/controls-registry/settings-field.tsx +33 -45
  34. package/src/controls-registry/styles-field.tsx +14 -14
  35. package/src/dynamics/components/dynamic-selection-control.tsx +2 -2
  36. package/src/errors.ts +10 -0
  37. package/src/hooks/use-custom-css.ts +184 -0
  38. package/src/hooks/use-state-by-element.ts +1 -4
  39. package/src/hooks/use-styles-fields.ts +129 -106
  40. package/src/index.ts +9 -10
  41. package/src/init.ts +2 -5
  42. package/src/popover-action.tsx +36 -15
  43. package/src/reset-style-props.tsx +2 -6
  44. package/src/styles-inheritance/components/infotip/value-component.tsx +1 -0
  45. package/src/styles-inheritance/components/styles-inheritance-indicator.tsx +6 -23
  46. package/src/styles-inheritance/components/styles-inheritance-infotip.tsx +17 -8
  47. package/src/styles-inheritance/consts.ts +0 -4
  48. package/src/styles-inheritance/init.ts +1 -4
  49. package/src/styles-inheritance/transformers/background-color-overlay-transformer.tsx +5 -1
  50. package/src/styles-inheritance/transformers/background-gradient-overlay-transformer.tsx +3 -3
  51. package/src/styles-inheritance/transformers/background-image-overlay-transformer.tsx +2 -1
  52. package/src/utils/prop-dependency-utils.ts +156 -0
  53. package/src/components/style-sections/size-section/object-position-field.tsx +0 -15
  54. package/src/sync/experiments-flags.ts +0 -5
  55. /package/src/components/style-sections/{layout-section → effects-section}/opacity-control-field.tsx +0 -0
@@ -1,11 +1,9 @@
1
1
  import * as React from 'react';
2
2
  import { type ToggleButtonGroupItem, ToggleControl } from '@elementor/editor-controls';
3
- import { isExperimentActive } from '@elementor/editor-v1-adapters';
4
3
  import { __ } from '@wordpress/i18n';
5
4
 
6
5
  import { useStylesInheritanceChain } from '../../../contexts/styles-inheritance-context';
7
6
  import { StylesField } from '../../../controls-registry/styles-field';
8
- import { EXPERIMENTAL_FEATURES } from '../../../sync/experiments-flags';
9
7
  import { StylesFieldLayout } from '../../styles-field-layout';
10
8
 
11
9
  type Displays = 'block' | 'flex' | 'inline-block' | 'inline-flex' | 'none';
@@ -31,34 +29,27 @@ const displayFieldItems: ToggleButtonGroupItem< Displays >[] = [
31
29
  label: __( 'Inline-block', 'elementor' ),
32
30
  showTooltip: true,
33
31
  },
34
- ];
35
-
36
- export const DisplayField = () => {
37
- const isDisplayNoneFeatureActive = isExperimentActive( EXPERIMENTAL_FEATURES.V_3_30 );
38
- const items = [ ...displayFieldItems ];
39
-
40
- if ( isDisplayNoneFeatureActive ) {
41
- items.push( {
42
- value: 'none',
43
- renderContent: () => __( 'None', 'elementor' ),
44
- label: __( 'None', 'elementor' ),
45
- showTooltip: true,
46
- } );
47
- }
48
-
49
- items.push( {
32
+ {
33
+ value: 'none',
34
+ renderContent: () => __( 'None', 'elementor' ),
35
+ label: __( 'None', 'elementor' ),
36
+ showTooltip: true,
37
+ },
38
+ {
50
39
  value: 'inline-flex',
51
40
  renderContent: () => __( 'In-flx', 'elementor' ),
52
41
  label: __( 'Inline-flex', 'elementor' ),
53
42
  showTooltip: true,
54
- } );
43
+ },
44
+ ];
55
45
 
46
+ export const DisplayField = () => {
56
47
  const placeholder = useDisplayPlaceholderValue();
57
48
 
58
49
  return (
59
50
  <StylesField bind="display" propDisplayName={ DISPLAY_LABEL } placeholder={ placeholder }>
60
51
  <StylesFieldLayout label={ DISPLAY_LABEL } direction="column">
61
- <ToggleControl options={ items } maxItems={ 4 } fullWidth={ true } />
52
+ <ToggleControl options={ displayFieldItems } maxItems={ 4 } fullWidth={ true } />
62
53
  </StylesFieldLayout>
63
54
  </StylesField>
64
55
  );
@@ -1,17 +1,20 @@
1
1
  import * as React from 'react';
2
- import { useMemo, useRef, useState } from 'react';
2
+ import { useEffect, useMemo, useRef, useState } from 'react';
3
3
  import {
4
4
  ControlToggleButtonGroup,
5
5
  NumberControl,
6
+ PropKeyProvider,
7
+ PropProvider,
6
8
  SizeControl,
7
9
  type ToggleButtonGroupItem,
10
+ useBoundProp,
8
11
  } from '@elementor/editor-controls';
9
- import { numberPropTypeUtil, type NumberPropValue, type SizePropValue } from '@elementor/editor-props';
12
+ import { flexPropTypeUtil, type FlexPropValue, numberPropTypeUtil } from '@elementor/editor-props';
10
13
  import { ExpandIcon, PencilIcon, ShrinkIcon } from '@elementor/icons';
11
14
  import { __ } from '@wordpress/i18n';
12
15
 
13
16
  import { StylesField } from '../../../controls-registry/styles-field';
14
- import { useStylesFields } from '../../../hooks/use-styles-fields';
17
+ import { useStylesField } from '../../../hooks/use-styles-field';
15
18
  import { UiProviders } from '../../../styles-inheritance/components/ui-providers';
16
19
  import { SectionContent } from '../../section-content';
17
20
  import { StylesFieldLayout } from '../../styles-field-layout';
@@ -44,51 +47,44 @@ const items: ToggleButtonGroupItem< GroupItem >[] = [
44
47
  ];
45
48
 
46
49
  export const FlexSizeField = () => {
47
- const { values, setValues, canEdit } = useStylesFields< {
48
- 'flex-grow': NumberPropValue | null;
49
- 'flex-shrink': NumberPropValue | null;
50
- 'flex-basis': SizePropValue | null;
51
- } >( [ 'flex-grow', 'flex-shrink', 'flex-basis' ] );
52
-
53
- const grow = values?.[ 'flex-grow' ]?.value || null;
54
- const shrink = values?.[ 'flex-shrink' ]?.value || null;
55
- const basis = values?.[ 'flex-basis' ]?.value || null;
50
+ const { value, setValue, canEdit } = useStylesField< FlexPropValue >( 'flex', {
51
+ history: { propDisplayName: FLEX_SIZE_LABEL },
52
+ } );
53
+
54
+ const flexValue = value as FlexPropValue | null;
55
+ const grow = flexValue?.value?.flexGrow?.value || null;
56
+ const shrink = flexValue?.value?.flexShrink?.value || null;
57
+ const basis = flexValue?.value?.flexBasis?.value || null;
58
+
59
+ const currentGroup = useMemo( () => getActiveGroup( { grow, shrink, basis } ), [ grow, shrink, basis ] );
60
+ const [ activeGroup, setActiveGroup ] = useState( currentGroup );
61
+ const [ customLocked, setCustomLocked ] = useState( false );
62
+
63
+ useEffect( () => {
64
+ if ( ! customLocked ) {
65
+ setActiveGroup( currentGroup );
66
+ }
67
+ }, [ currentGroup, customLocked ] );
56
68
 
57
- const currentGroup = useMemo( () => getActiveGroup( { grow, shrink, basis } ), [ grow, shrink, basis ] ),
58
- [ activeGroup, setActiveGroup ] = useState( currentGroup );
69
+ useEffect( () => {
70
+ if ( value === null ) {
71
+ setCustomLocked( false );
72
+ }
73
+ }, [ value ] );
59
74
 
60
75
  const onChangeGroup = ( group: GroupItem | null = null ) => {
61
76
  setActiveGroup( group );
77
+ setCustomLocked( group === 'custom' );
62
78
 
63
- let props;
64
-
65
- if ( ! group || group === 'custom' ) {
66
- props = {
67
- 'flex-basis': null,
68
- 'flex-grow': null,
69
- 'flex-shrink': null,
70
- };
71
- } else if ( group === 'flex-grow' ) {
72
- props = {
73
- 'flex-basis': null,
74
- 'flex-grow': numberPropTypeUtil.create( DEFAULT ),
75
- 'flex-shrink': null,
76
- };
77
- } else {
78
- props = {
79
- 'flex-basis': null,
80
- 'flex-grow': null,
81
- 'flex-shrink': numberPropTypeUtil.create( DEFAULT ),
82
- };
83
- }
79
+ const newFlexValue = createFlexValueForGroup( group, flexValue );
84
80
 
85
- setValues( props, { history: { propDisplayName: FLEX_SIZE_LABEL } } );
81
+ setValue( newFlexValue as FlexPropValue );
86
82
  };
87
83
 
88
84
  return (
89
85
  <UiProviders>
90
86
  <SectionContent>
91
- <StylesField bind={ activeGroup ?? '' } propDisplayName={ FLEX_SIZE_LABEL }>
87
+ <StylesField bind="flex" propDisplayName={ FLEX_SIZE_LABEL }>
92
88
  <StylesFieldLayout label={ FLEX_SIZE_LABEL }>
93
89
  <ControlToggleButtonGroup
94
90
  value={ activeGroup }
@@ -98,34 +94,72 @@ export const FlexSizeField = () => {
98
94
  exclusive={ true }
99
95
  />
100
96
  </StylesFieldLayout>
97
+ { 'custom' === activeGroup && <FlexCustomField /> }
101
98
  </StylesField>
102
- { 'custom' === activeGroup && <FlexCustomField /> }
103
99
  </SectionContent>
104
100
  </UiProviders>
105
101
  );
106
102
  };
107
103
 
104
+ const createFlexValueForGroup = ( group: GroupItem | null, flexValue: FlexPropValue | null ): FlexPropValue | null => {
105
+ if ( ! group ) {
106
+ return null;
107
+ }
108
+
109
+ if ( group === 'flex-grow' ) {
110
+ return flexPropTypeUtil.create( {
111
+ flexGrow: numberPropTypeUtil.create( DEFAULT ),
112
+ flexShrink: null,
113
+ flexBasis: null,
114
+ } );
115
+ }
116
+
117
+ if ( group === 'flex-shrink' ) {
118
+ return flexPropTypeUtil.create( {
119
+ flexGrow: null,
120
+ flexShrink: numberPropTypeUtil.create( DEFAULT ),
121
+ flexBasis: null,
122
+ } );
123
+ }
124
+
125
+ if ( group === 'custom' ) {
126
+ if ( flexValue ) {
127
+ return flexValue;
128
+ }
129
+ return flexPropTypeUtil.create( {
130
+ flexGrow: null,
131
+ flexShrink: null,
132
+ flexBasis: null,
133
+ } );
134
+ }
135
+
136
+ return null;
137
+ };
138
+
108
139
  const FlexCustomField = () => {
109
140
  const flexBasisRowRef = useRef< HTMLDivElement >( null );
141
+ const context = useBoundProp( flexPropTypeUtil );
110
142
 
111
143
  return (
112
- <>
113
- <StylesField bind="flex-grow" propDisplayName={ FLEX_SIZE_LABEL }>
144
+ <PropProvider { ...context }>
145
+ <>
114
146
  <StylesFieldLayout label={ __( 'Grow', 'elementor' ) }>
115
- <NumberControl min={ 0 } shouldForceInt={ true } />
147
+ <PropKeyProvider bind="flexGrow">
148
+ <NumberControl min={ 0 } shouldForceInt={ true } />
149
+ </PropKeyProvider>
116
150
  </StylesFieldLayout>
117
- </StylesField>
118
- <StylesField bind="flex-shrink" propDisplayName={ FLEX_SIZE_LABEL }>
119
151
  <StylesFieldLayout label={ __( 'Shrink', 'elementor' ) }>
120
- <NumberControl min={ 0 } shouldForceInt={ true } />
152
+ <PropKeyProvider bind="flexShrink">
153
+ <NumberControl min={ 0 } shouldForceInt={ true } />
154
+ </PropKeyProvider>
121
155
  </StylesFieldLayout>
122
- </StylesField>
123
- <StylesField bind="flex-basis" propDisplayName={ FLEX_SIZE_LABEL }>
124
156
  <StylesFieldLayout label={ __( 'Basis', 'elementor' ) } ref={ flexBasisRowRef }>
125
- <SizeControl extendedOptions={ [ 'auto' ] } anchorRef={ flexBasisRowRef } />
157
+ <PropKeyProvider bind="flexBasis">
158
+ <SizeControl extendedOptions={ [ 'auto' ] } anchorRef={ flexBasisRowRef } />
159
+ </PropKeyProvider>
126
160
  </StylesFieldLayout>
127
- </StylesField>
128
- </>
161
+ </>
162
+ </PropProvider>
129
163
  );
130
164
  };
131
165
 
@@ -134,9 +168,9 @@ const getActiveGroup = ( {
134
168
  shrink,
135
169
  basis,
136
170
  }: {
137
- grow: NumberPropValue[ 'value' ] | null;
138
- shrink: NumberPropValue[ 'value' ] | null;
139
- basis: SizePropValue[ 'value' ] | null;
171
+ grow: number | null;
172
+ shrink: number | null;
173
+ basis: { size: number; unit: string } | string | null;
140
174
  } ): GroupItem | null => {
141
175
  if ( null === grow && null === shrink && ! basis ) {
142
176
  return null;
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import { useRef } from 'react';
3
- import { SizeControl, type Unit } from '@elementor/editor-controls';
3
+ import { type LengthUnit, SizeControl } from '@elementor/editor-controls';
4
4
  import { __ } from '@wordpress/i18n';
5
5
 
6
6
  import { StylesField } from '../../../controls-registry/styles-field';
@@ -8,7 +8,7 @@ import { StylesFieldLayout } from '../../styles-field-layout';
8
8
 
9
9
  const OFFSET_LABEL = __( 'Anchor offset', 'elementor' );
10
10
 
11
- const UNITS: Unit[] = [ 'px', 'em', 'rem', 'vw', 'vh' ];
11
+ const UNITS: LengthUnit[] = [ 'px', 'em', 'rem', 'vw', 'vh' ];
12
12
 
13
13
  export const OffsetField = () => {
14
14
  const rowRef = useRef< HTMLDivElement >( null );
@@ -1,6 +1,5 @@
1
1
  import * as React from 'react';
2
2
  import { type StringPropValue } from '@elementor/editor-props';
3
- import { isExperimentActive } from '@elementor/editor-v1-adapters';
4
3
  import { useSessionStorage } from '@elementor/session';
5
4
  import { __ } from '@wordpress/i18n';
6
5
 
@@ -44,7 +43,6 @@ export const PositionSection = () => {
44
43
  ] );
45
44
 
46
45
  const [ dimensionsValuesFromHistory, updateDimensionsHistory, clearDimensionsHistory ] = usePersistDimensions();
47
- const isCssIdFeatureActive = isExperimentActive( 'e_v_3_30' );
48
46
 
49
47
  const onPositionChange = ( newPosition: string | null, previousPosition: string | null | undefined ) => {
50
48
  const meta = { history: { propDisplayName: DIMENSIONS_LABEL } };
@@ -82,12 +80,8 @@ export const PositionSection = () => {
82
80
  </>
83
81
  ) : null }
84
82
 
85
- { isCssIdFeatureActive && (
86
- <>
87
- <PanelDivider />
88
- <OffsetField />
89
- </>
90
- ) }
83
+ <PanelDivider />
84
+ <OffsetField />
91
85
  </SectionContent>
92
86
  );
93
87
  };
@@ -1,23 +1,17 @@
1
1
  import * as React from 'react';
2
2
  import { type RefObject, useRef } from 'react';
3
- import { AspectRatioControl, type ExtendedOption, SizeControl } from '@elementor/editor-controls';
4
- import type { StringPropValue } from '@elementor/editor-props';
5
- import { isExperimentActive } from '@elementor/editor-v1-adapters';
3
+ import { AspectRatioControl, type ExtendedOption, PositionControl, SizeControl } from '@elementor/editor-controls';
6
4
  import { Grid, Stack } from '@elementor/ui';
7
5
  import { __ } from '@wordpress/i18n';
8
6
 
9
7
  import { StylesField, type StylesFieldProps } from '../../../controls-registry/styles-field';
10
- import { useStylesField } from '../../../hooks/use-styles-field';
11
8
  import { ControlLabel } from '../../control-label';
12
9
  import { PanelDivider } from '../../panel-divider';
13
10
  import { SectionContent } from '../../section-content';
14
11
  import { StyleTabCollapsibleContent } from '../../style-tab-collapsible-content';
15
12
  import { ObjectFitField } from './object-fit-field';
16
- import { ObjectPositionField } from './object-position-field';
17
13
  import { OverflowField } from './overflow-field';
18
14
 
19
- const EXPERIMENT_ID = 'e_v_3_30';
20
-
21
15
  const CssSizeProps = [
22
16
  [
23
17
  {
@@ -52,17 +46,9 @@ const CssSizeProps = [
52
46
  ];
53
47
 
54
48
  const ASPECT_RATIO_LABEL = __( 'Aspect Ratio', 'elementor' );
55
- const OBJECT_FIT_LABEL = __( 'Object fit', 'elementor' );
56
49
 
57
50
  export const SizeSection = () => {
58
- const { value: fitValue } = useStylesField< StringPropValue >( 'object-fit', {
59
- history: { propDisplayName: OBJECT_FIT_LABEL },
60
- } );
61
-
62
- const isNotFill = fitValue && fitValue?.value !== 'fill';
63
-
64
51
  const gridRowRefs: RefObject< HTMLDivElement >[] = [ useRef( null ), useRef( null ), useRef( null ) ];
65
- const isVersion330Active = isExperimentActive( EXPERIMENT_ID );
66
52
 
67
53
  return (
68
54
  <SectionContent>
@@ -79,22 +65,21 @@ export const SizeSection = () => {
79
65
  <Stack>
80
66
  <OverflowField />
81
67
  </Stack>
82
- { isVersion330Active && (
83
- <StyleTabCollapsibleContent fields={ [ 'aspect-ratio', 'object-fit' ] }>
84
- <Stack gap={ 2 } pt={ 2 }>
85
- <StylesField bind="aspect-ratio" propDisplayName={ ASPECT_RATIO_LABEL }>
86
- <AspectRatioControl label={ ASPECT_RATIO_LABEL } />
87
- </StylesField>
88
- <PanelDivider />
89
- <ObjectFitField />
90
- { isNotFill && (
91
- <Grid item xs={ 6 }>
92
- <ObjectPositionField />
93
- </Grid>
94
- ) }
95
- </Stack>
96
- </StyleTabCollapsibleContent>
97
- ) }
68
+
69
+ <StyleTabCollapsibleContent fields={ [ 'aspect-ratio', 'object-fit' ] }>
70
+ <Stack gap={ 2 } pt={ 2 }>
71
+ <StylesField bind="aspect-ratio" propDisplayName={ ASPECT_RATIO_LABEL }>
72
+ <AspectRatioControl label={ ASPECT_RATIO_LABEL } />
73
+ </StylesField>
74
+ <PanelDivider />
75
+ <ObjectFitField />
76
+ <StylesField bind="object-position" propDisplayName={ __( 'Object position', 'elementor' ) }>
77
+ <Grid item xs={ 6 }>
78
+ <PositionControl />
79
+ </Grid>
80
+ </StylesField>
81
+ </Stack>
82
+ </StyleTabCollapsibleContent>
98
83
  </SectionContent>
99
84
  );
100
85
  };
@@ -1,9 +1,5 @@
1
1
  import * as React from 'react';
2
- import type { NumberPropValue } from '@elementor/editor-props';
3
- import { isExperimentActive } from '@elementor/editor-v1-adapters';
4
- import { __ } from '@wordpress/i18n';
5
2
 
6
- import { useStylesField } from '../../../hooks/use-styles-field';
7
3
  import { PanelDivider } from '../../panel-divider';
8
4
  import { SectionContent } from '../../section-content';
9
5
  import { StyleTabCollapsibleContent } from '../../style-tab-collapsible-content';
@@ -23,16 +19,7 @@ import { TextStrokeField } from './text-stroke-field';
23
19
  import { TransformField } from './transform-field';
24
20
  import { WordSpacingField } from './word-spacing-field';
25
21
 
26
- const COLUMN_COUNT_LABEL = __( 'Column count', 'elementor' );
27
-
28
22
  export const TypographySection = () => {
29
- const { value: columnCount } = useStylesField< NumberPropValue >( 'column-count', {
30
- history: { propDisplayName: COLUMN_COUNT_LABEL },
31
- } );
32
- const hasMultiColumns = !! ( columnCount?.value && columnCount?.value > 1 );
33
-
34
- const isVersion330Active = isExperimentActive( 'e_v_3_30' );
35
-
36
23
  return (
37
24
  <SectionContent>
38
25
  <FontFamilyField />
@@ -58,12 +45,8 @@ export const TypographySection = () => {
58
45
  <LineHeightField />
59
46
  <LetterSpacingField />
60
47
  <WordSpacingField />
61
- { isVersion330Active && (
62
- <>
63
- <ColumnCountField />
64
- { hasMultiColumns && <ColumnGapField /> }
65
- </>
66
- ) }
48
+ <ColumnCountField />
49
+ <ColumnGapField />
67
50
  <PanelDivider />
68
51
  <TextDecorationField />
69
52
  <TransformField />
@@ -1,9 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import { type PropsWithChildren } from 'react';
3
- import { isExperimentActive } from '@elementor/editor-v1-adapters';
4
3
 
5
4
  import { StylesInheritanceSectionIndicators } from '../styles-inheritance/components/styles-inheritance-section-indicators';
6
- import { EXPERIMENTAL_FEATURES } from '../sync/experiments-flags';
7
5
  import { CollapsibleContent } from './collapsible-content';
8
6
  type Props = PropsWithChildren< { fields?: string[] } >;
9
7
 
@@ -12,9 +10,7 @@ export const StyleTabCollapsibleContent = ( { fields = [], children }: Props ) =
12
10
  };
13
11
 
14
12
  export function getStylesInheritanceIndicators( fields: string[] ) {
15
- const isUsingFieldsIndicators = isExperimentActive( EXPERIMENTAL_FEATURES.V_3_30 );
16
-
17
- if ( fields.length === 0 || ! isUsingFieldsIndicators ) {
13
+ if ( fields.length === 0 ) {
18
14
  return null;
19
15
  }
20
16
 
@@ -1,8 +1,6 @@
1
1
  import * as React from 'react';
2
- import { isExperimentActive } from '@elementor/editor-v1-adapters';
3
2
 
4
3
  import { useDefaultPanelSettings } from '../hooks/use-default-panel-settings';
5
- import { EXPERIMENTAL_FEATURES } from '../sync/experiments-flags';
6
4
  import { Section } from './section';
7
5
  import { getStylesInheritanceIndicators } from './style-tab-collapsible-content';
8
6
 
@@ -18,9 +16,7 @@ export const StyleTabSection = ( { section, fields = [] }: Props ) => {
18
16
  const { component, name, title } = section;
19
17
  const tabDefaults = useDefaultPanelSettings();
20
18
  const SectionComponent = component;
21
- const isExpanded = isExperimentActive( EXPERIMENTAL_FEATURES.V_3_30 )
22
- ? tabDefaults.defaultSectionsExpanded.style?.includes( name )
23
- : false;
19
+ const isExpanded = tabDefaults.defaultSectionsExpanded.style?.includes( name );
24
20
 
25
21
  return (
26
22
  <Section title={ title } defaultExpanded={ isExpanded } titleEnd={ getStylesInheritanceIndicators( fields ) }>
@@ -3,8 +3,9 @@ import { useState } from 'react';
3
3
  import { CLASSES_PROP_KEY } from '@elementor/editor-props';
4
4
  import { useActiveBreakpoint } from '@elementor/editor-responsive';
5
5
  import { type StyleDefinitionID, type StyleDefinitionState } from '@elementor/editor-styles';
6
+ import { EXPERIMENTAL_FEATURES, isExperimentActive } from '@elementor/editor-v1-adapters';
6
7
  import { SessionStorageProvider } from '@elementor/session';
7
- import { Divider, Stack } from '@elementor/ui';
8
+ import { Box, Divider, Stack } from '@elementor/ui';
8
9
  import { __ } from '@wordpress/i18n';
9
10
 
10
11
  import { ClassesPropProvider } from '../contexts/classes-prop-context';
@@ -14,6 +15,7 @@ import { StyleProvider } from '../contexts/style-context';
14
15
  import { StyleInheritanceProvider } from '../contexts/styles-inheritance-context';
15
16
  import { useActiveStyleDefId } from '../hooks/use-active-style-def-id';
16
17
  import { CssClassSelector } from './css-classes/css-class-selector';
18
+ import { CustomCss } from './custom-css';
17
19
  import { SectionsList } from './sections-list';
18
20
  import { BackgroundSection } from './style-sections/background-section/background-section';
19
21
  import { BorderSection } from './style-sections/border-section/border-section';
@@ -40,6 +42,7 @@ export const StyleTab = () => {
40
42
  const [ activeStyleDefId, setActiveStyleDefId ] = useActiveStyleDefId( currentClassesProp );
41
43
  const [ activeStyleState, setActiveStyleState ] = useState< StyleDefinitionState | null >( null );
42
44
  const breakpoint = useActiveBreakpoint();
45
+ const shouldRenderCustomCss = isExperimentActive( EXPERIMENTAL_FEATURES.CUSTOM_CSS );
43
46
 
44
47
  return (
45
48
  <ClassesPropProvider prop={ currentClassesProp }>
@@ -155,9 +158,19 @@ export const StyleTab = () => {
155
158
  name: 'Effects',
156
159
  title: __( 'Effects', 'elementor' ),
157
160
  } }
158
- fields={ [ 'box-shadow' ] }
161
+ fields={ [ 'box-shadow', 'opacity', 'transform', 'filter', 'backdrop-filter' ] }
159
162
  />
163
+ { shouldRenderCustomCss && (
164
+ <StyleTabSection
165
+ section={ {
166
+ component: CustomCss,
167
+ name: 'Custom CSS',
168
+ title: __( 'Custom CSS', 'elementor' ),
169
+ } }
170
+ />
171
+ ) }
160
172
  </SectionsList>
173
+ <Box sx={ { height: '150px' } } />
161
174
  </StyleInheritanceProvider>
162
175
  </SessionStorageProvider>
163
176
  </StyleProvider>
@@ -1,7 +1,7 @@
1
1
  import { createMenu } from '@elementor/menus';
2
2
 
3
3
  import Action from './action';
4
- import PopoverAction from './popover-action';
4
+ import { PopoverAction } from './popover-action';
5
5
 
6
6
  export const controlActionsMenu = createMenu( {
7
7
  components: {
@@ -0,0 +1,26 @@
1
+ import { useBoundProp } from '@elementor/editor-controls';
2
+ import { isDependency, isDependencyMet, type PropKey, type PropType } from '@elementor/editor-props';
3
+
4
+ import { useStylesFields } from '../hooks/use-styles-fields';
5
+
6
+ export const ConditionalField: React.FC< {
7
+ children: React.ReactNode;
8
+ } > = ( { children } ) => {
9
+ const { propType } = useBoundProp();
10
+
11
+ const depList = getDependencies( propType );
12
+
13
+ const { values: depValues } = useStylesFields( depList );
14
+
15
+ const isHidden = ! isDependencyMet( propType?.dependencies, depValues );
16
+
17
+ return isHidden ? null : children;
18
+ };
19
+
20
+ export function getDependencies( propType?: PropType ): PropKey[] {
21
+ if ( ! propType?.dependencies?.terms.length ) {
22
+ return [];
23
+ }
24
+
25
+ return propType.dependencies.terms.flatMap( ( term ) => ( ! isDependency( term ) ? term.path : [] ) );
26
+ }
@@ -3,7 +3,7 @@ import type { ComponentProps } from 'react';
3
3
 
4
4
  import { useElement } from '../contexts/element-context';
5
5
  import { ControlTypeNotFoundError } from '../errors';
6
- import { type ControlType, type ControlTypes, getControl } from './controls-registry';
6
+ import { controlsRegistry, type ControlType, type ControlTypes } from './controls-registry';
7
7
 
8
8
  type IsRequired< T, K extends keyof T > = object extends Pick< T, K > ? false : true;
9
9
 
@@ -24,7 +24,7 @@ type ControlProps< T extends ControlType > = AnyPropertyRequired< ComponentProps
24
24
  };
25
25
 
26
26
  export const Control = < T extends ControlType >( { props, type }: ControlProps< T > ) => {
27
- const ControlByType = getControl( type );
27
+ const ControlByType = controlsRegistry.get( type );
28
28
  const { element } = useElement();
29
29
 
30
30
  if ( ! ControlByType ) {
@@ -24,6 +24,8 @@ import {
24
24
  stringPropTypeUtil,
25
25
  } from '@elementor/editor-props';
26
26
 
27
+ import { ControlTypeAlreadyRegisteredError, ControlTypeNotRegisteredError } from '../errors';
28
+
27
29
  type ControlRegistry = Record<
28
30
  string,
29
31
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -50,8 +52,47 @@ export type ControlTypes = {
50
52
  [ key in ControlType ]: ( typeof controlTypes )[ key ][ 'component' ];
51
53
  };
52
54
 
53
- export const getControl = ( type: ControlType ) => controlTypes[ type ]?.component;
55
+ class ControlsRegistry {
56
+ constructor( private readonly controlsRegistry: ControlRegistry = controlTypes ) {
57
+ this.controlsRegistry = controlsRegistry;
58
+ }
59
+
60
+ get( type: ControlType ): ControlComponent {
61
+ return this.controlsRegistry[ type ]?.component;
62
+ }
63
+
64
+ getLayout( type: ControlType ) {
65
+ return this.controlsRegistry[ type ]?.layout;
66
+ }
67
+
68
+ getPropTypeUtil( type: ControlType ) {
69
+ return this.controlsRegistry[ type ]?.propTypeUtil;
70
+ }
71
+
72
+ registry(): ControlRegistry {
73
+ return this.controlsRegistry;
74
+ }
75
+
76
+ register(
77
+ type: string,
78
+ component: ControlComponent,
79
+ layout: ControlLayout,
80
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
81
+ propTypeUtil?: PropTypeUtil< string, any >
82
+ ) {
83
+ if ( this.controlsRegistry[ type ] ) {
84
+ throw new ControlTypeAlreadyRegisteredError( { context: { controlType: type } } );
85
+ }
86
+ this.controlsRegistry[ type ] = { component, layout, propTypeUtil };
87
+ }
54
88
 
55
- export const getDefaultLayout = ( type: ControlType ) => controlTypes[ type ].layout;
89
+ unregister( type: string ) {
90
+ if ( ! this.controlsRegistry[ type ] ) {
91
+ throw new ControlTypeNotRegisteredError( { context: { controlType: type } } );
92
+ }
93
+ // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
94
+ delete this.controlsRegistry[ type ];
95
+ }
96
+ }
56
97
 
57
- export const getPropTypeUtil = ( type: ControlType ) => controlTypes[ type ]?.propTypeUtil;
98
+ export const controlsRegistry = new ControlsRegistry();