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

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,79 @@
1
+ import * as React from 'react';
2
+ import { ControlAdornmentsProvider } from '@elementor/editor-controls';
3
+ import { type Control, type ControlLayout, type ElementControl } from '@elementor/editor-elements';
4
+ import { Divider, styled } from '@elementor/ui';
5
+
6
+ import { Control as BaseControl } from '../controls-registry/control';
7
+ import { ControlTypeContainer } from '../controls-registry/control-type-container';
8
+ import { controlsRegistry, type ControlType } from '../controls-registry/controls-registry';
9
+ import { SettingsField } from '../controls-registry/settings-field';
10
+ import { getFieldIndicators } from '../field-indicators-registry';
11
+ import { ControlLabel } from './control-label';
12
+
13
+ const Wrapper = styled( 'span' )`
14
+ display: contents;
15
+ `;
16
+
17
+ export const SettingsControl = ( { control: { value, type } }: { control: Control | ElementControl } ) => {
18
+ if ( ! controlsRegistry.get( value.type as ControlType ) ) {
19
+ return null;
20
+ }
21
+
22
+ const layout = value.meta?.layout || controlsRegistry.getLayout( value.type as ControlType );
23
+ const controlProps = populateChildControlProps( value.props );
24
+
25
+ if ( layout === 'custom' ) {
26
+ controlProps.label = value.label;
27
+ }
28
+
29
+ if ( type === 'element-control' ) {
30
+ return <ControlLayout control={ value } layout={ layout } controlProps={ controlProps } />;
31
+ }
32
+
33
+ return (
34
+ <SettingsField bind={ value.bind } propDisplayName={ value.label || value.bind }>
35
+ <ControlLayout control={ value } layout={ layout } controlProps={ controlProps } />
36
+ </SettingsField>
37
+ );
38
+ };
39
+
40
+ const ControlLayout = ( {
41
+ control,
42
+ layout,
43
+ controlProps,
44
+ }: {
45
+ control: Control[ 'value' ] | ElementControl[ 'value' ];
46
+ layout: ControlLayout;
47
+ controlProps: Record< string, unknown >;
48
+ } ) => {
49
+ const controlType = control.type as ControlType;
50
+
51
+ return (
52
+ <ControlAdornmentsProvider items={ getFieldIndicators( 'settings' ) }>
53
+ { control.meta?.topDivider && <Divider /> }
54
+ <Wrapper data-type="settings-field">
55
+ <ControlTypeContainer layout={ layout }>
56
+ { control.label && layout !== 'custom' ? <ControlLabel>{ control.label }</ControlLabel> : null }
57
+ <BaseControl type={ controlType } props={ controlProps } />
58
+ </ControlTypeContainer>
59
+ </Wrapper>
60
+ </ControlAdornmentsProvider>
61
+ );
62
+ };
63
+
64
+ function populateChildControlProps( props: Record< string, unknown > ) {
65
+ if ( props.childControlType ) {
66
+ const childComponent = controlsRegistry.get( props.childControlType as ControlType );
67
+ const childPropType = controlsRegistry.getPropTypeUtil( props.childControlType as ControlType );
68
+ props = {
69
+ ...props,
70
+ childControlConfig: {
71
+ component: childComponent,
72
+ props: props.childControlProps || {},
73
+ propTypeUtil: childPropType,
74
+ },
75
+ };
76
+ }
77
+
78
+ return props;
79
+ }
@@ -1,17 +1,12 @@
1
1
  import * as React from 'react';
2
- import { ControlFormLabel } from '@elementor/editor-controls';
3
- import { type Control } from '@elementor/editor-elements';
2
+ import { type Control, type ControlItem, type Element, type ElementControl } from '@elementor/editor-elements';
4
3
  import { SessionStorageProvider } from '@elementor/session';
5
- import { Divider } from '@elementor/ui';
6
4
 
7
5
  import { useElement } from '../contexts/element-context';
8
- import { Control as BaseControl } from '../controls-registry/control';
9
- import { ControlTypeContainer } from '../controls-registry/control-type-container';
10
- import { controlsRegistry, type ControlType } from '../controls-registry/controls-registry';
11
- import { SettingsField } from '../controls-registry/settings-field';
12
6
  import { useDefaultPanelSettings } from '../hooks/use-default-panel-settings';
13
7
  import { Section } from './section';
14
8
  import { SectionsList } from './sections-list';
9
+ import { SettingsControl } from './settings-control';
15
10
 
16
11
  export const SettingsTab = () => {
17
12
  const { elementType, element } = useElement();
@@ -23,11 +18,13 @@ export const SettingsTab = () => {
23
18
  return (
24
19
  <SessionStorageProvider prefix={ element.id }>
25
20
  <SectionsList>
26
- { elementType.controls.map( ( { type, value }, index ) => {
27
- if ( type === 'control' ) {
28
- return <Control key={ value.bind } control={ value } />;
21
+ { elementType.controls.map( ( control, index ) => {
22
+ if ( isControl( control ) ) {
23
+ return <SettingsControl key={ getKey( control, element ) } control={ control } />;
29
24
  }
30
25
 
26
+ const { type, value } = control;
27
+
31
28
  if ( type === 'section' ) {
32
29
  return (
33
30
  <Section
@@ -36,8 +33,8 @@ export const SettingsTab = () => {
36
33
  defaultExpanded={ isDefaultExpanded( value.label ) }
37
34
  >
38
35
  { value.items?.map( ( item ) => {
39
- if ( item.type === 'control' ) {
40
- return <Control key={ item.value.bind } control={ item.value } />;
36
+ if ( isControl( item ) ) {
37
+ return <SettingsControl key={ getKey( item, element ) } control={ item } />;
41
38
  }
42
39
 
43
40
  // TODO: Handle 2nd level sections
@@ -54,41 +51,14 @@ export const SettingsTab = () => {
54
51
  );
55
52
  };
56
53
 
57
- const Control = ( { control }: { control: Control[ 'value' ] } ) => {
58
- if ( ! controlsRegistry.get( control.type as ControlType ) ) {
59
- return null;
60
- }
61
-
62
- const layout = control.meta?.layout || controlsRegistry.getLayout( control.type as ControlType );
63
- const controlProps = populateChildControlProps( control.props );
64
- if ( layout === 'custom' ) {
65
- controlProps.label = control.label;
54
+ function getKey( control: Control | ElementControl, element: Element ) {
55
+ if ( control.type === 'control' ) {
56
+ return control.value.bind + '.' + element.id;
66
57
  }
67
58
 
68
- return (
69
- <SettingsField bind={ control.bind } propDisplayName={ control.label || control.bind }>
70
- { control.meta?.topDivider && <Divider /> }
71
- <ControlTypeContainer layout={ layout }>
72
- { control.label && layout !== 'custom' ? <ControlFormLabel>{ control.label }</ControlFormLabel> : null }
73
- <BaseControl type={ control.type as ControlType } props={ controlProps } />
74
- </ControlTypeContainer>
75
- </SettingsField>
76
- );
77
- };
78
-
79
- function populateChildControlProps( props: Record< string, unknown > ) {
80
- if ( props.childControlType ) {
81
- const childComponent = controlsRegistry.get( props.childControlType as ControlType );
82
- const childPropType = controlsRegistry.getPropTypeUtil( props.childControlType as ControlType );
83
- props = {
84
- ...props,
85
- childControlConfig: {
86
- component: childComponent,
87
- props: props.childControlProps || {},
88
- propTypeUtil: childPropType,
89
- },
90
- };
91
- }
59
+ return control.value.type + '.' + element.id;
60
+ }
92
61
 
93
- return props;
62
+ function isControl( control: ControlItem ): control is Control | ElementControl {
63
+ return control.type === 'control' || control.type === 'element-control';
94
64
  }
@@ -1,14 +1,16 @@
1
1
  import * as React from 'react';
2
2
 
3
- import { PanelDivider } from '../../panel-divider';
4
3
  import { SectionContent } from '../../section-content';
5
- import { BorderField } from './border-field';
4
+ import { BorderColorField } from './border-color-field';
6
5
  import { BorderRadiusField } from './border-radius-field';
6
+ import { BorderStyleField } from './border-style-field';
7
+ import { BorderWidthField } from './border-width-field';
7
8
 
8
9
  export const BorderSection = () => (
9
10
  <SectionContent>
11
+ <BorderWidthField />
12
+ <BorderColorField />
13
+ <BorderStyleField />
10
14
  <BorderRadiusField />
11
- <PanelDivider />
12
- <BorderField />
13
15
  </SectionContent>
14
16
  );
@@ -0,0 +1,44 @@
1
+ import * as React from 'react';
2
+ import { SelectControl } from '@elementor/editor-controls';
3
+ import { __ } from '@wordpress/i18n';
4
+
5
+ import { StylesField } from '../../../controls-registry/styles-field';
6
+ import { StylesFieldLayout } from '../../styles-field-layout';
7
+
8
+ const BLEND_MODE_LABEL = __( 'Blend mode', 'elementor' );
9
+
10
+ const blendModeOptions = [
11
+ { label: __( 'Normal', 'elementor' ), value: 'normal' },
12
+ { label: __( 'Multiply', 'elementor' ), value: 'multiply' },
13
+ { label: __( 'Screen', 'elementor' ), value: 'screen' },
14
+ { label: __( 'Overlay', 'elementor' ), value: 'overlay' },
15
+ { label: __( 'Darken', 'elementor' ), value: 'darken' },
16
+ { label: __( 'Lighten', 'elementor' ), value: 'lighten' },
17
+ { label: __( 'Color dodge', 'elementor' ), value: 'color-dodge' },
18
+ { label: __( 'Color burn', 'elementor' ), value: 'color-burn' },
19
+ { label: __( 'Saturation', 'elementor' ), value: 'saturation' },
20
+ { label: __( 'Color', 'elementor' ), value: 'color' },
21
+ { label: __( 'Difference', 'elementor' ), value: 'difference' },
22
+ { label: __( 'Exclusion', 'elementor' ), value: 'exclusion' },
23
+ { label: __( 'Hue', 'elementor' ), value: 'hue' },
24
+ { label: __( 'Luminosity', 'elementor' ), value: 'luminosity' },
25
+ { label: __( 'Soft light', 'elementor' ), value: 'soft-light' },
26
+ { label: __( 'Hard light', 'elementor' ), value: 'hard-light' },
27
+ ];
28
+
29
+ export const BlendModeField = () => {
30
+ return (
31
+ <StylesField bind="mix-blend-mode" propDisplayName={ BLEND_MODE_LABEL }>
32
+ <StylesFieldLayout label={ BLEND_MODE_LABEL }>
33
+ <SelectControl
34
+ options={ blendModeOptions }
35
+ MenuProps={ {
36
+ sx: {
37
+ maxHeight: '256px',
38
+ },
39
+ } }
40
+ />
41
+ </StylesFieldLayout>
42
+ </StylesField>
43
+ );
44
+ };
@@ -13,6 +13,7 @@ import { StylesField } from '../../../controls-registry/styles-field';
13
13
  import { getRecentlyUsedList } from '../../../utils/get-recently-used-styles';
14
14
  import { PanelDivider } from '../../panel-divider';
15
15
  import { SectionContent } from '../../section-content';
16
+ import { BlendModeField } from './blend-mode-field';
16
17
  import { OpacityControlField } from './opacity-control-field';
17
18
 
18
19
  const BOX_SHADOW_LABEL = __( 'Box shadow', 'elementor' );
@@ -26,7 +27,9 @@ export const EffectsSection = () => {
26
27
  const { meta } = useStyle();
27
28
 
28
29
  return (
29
- <SectionContent>
30
+ <SectionContent gap={ 1 }>
31
+ <BlendModeField />
32
+ <PanelDivider />
30
33
  <OpacityControlField />
31
34
  <PanelDivider />
32
35
  <StylesField bind="box-shadow" propDisplayName={ BOX_SHADOW_LABEL }>
@@ -9,7 +9,7 @@ import {
9
9
  type ToggleButtonGroupItem,
10
10
  useBoundProp,
11
11
  } from '@elementor/editor-controls';
12
- import { flexPropTypeUtil, type FlexPropValue, numberPropTypeUtil } from '@elementor/editor-props';
12
+ import { flexPropTypeUtil, type FlexPropValue, numberPropTypeUtil, sizePropTypeUtil } from '@elementor/editor-props';
13
13
  import { ExpandIcon, PencilIcon, ShrinkIcon } from '@elementor/icons';
14
14
  import { __ } from '@wordpress/i18n';
15
15
 
@@ -130,16 +130,16 @@ const createFlexValueForGroup = ( group: GroupItem | null, flexValue: FlexPropVa
130
130
  if ( group === 'flex-grow' ) {
131
131
  return flexPropTypeUtil.create( {
132
132
  flexGrow: numberPropTypeUtil.create( DEFAULT ),
133
- flexShrink: null,
134
- flexBasis: null,
133
+ flexShrink: numberPropTypeUtil.create( 0 ),
134
+ flexBasis: sizePropTypeUtil.create( { unit: 'auto', size: '' } ),
135
135
  } );
136
136
  }
137
137
 
138
138
  if ( group === 'flex-shrink' ) {
139
139
  return flexPropTypeUtil.create( {
140
- flexGrow: null,
140
+ flexGrow: numberPropTypeUtil.create( 0 ),
141
141
  flexShrink: numberPropTypeUtil.create( DEFAULT ),
142
- flexBasis: null,
142
+ flexBasis: sizePropTypeUtil.create( { unit: 'auto', size: '' } ),
143
143
  } );
144
144
  }
145
145
 
@@ -197,15 +197,17 @@ const getActiveGroup = ( {
197
197
  return null;
198
198
  }
199
199
 
200
- if ( ( shrink && grow ) || basis ) {
200
+ const isAutoBasis = basis === null || ( typeof basis === 'object' && basis.unit === 'auto' );
201
+
202
+ if ( basis && ! isAutoBasis ) {
201
203
  return 'custom';
202
204
  }
203
205
 
204
- if ( grow === DEFAULT ) {
206
+ if ( grow === DEFAULT && ( shrink === null || shrink === 0 ) && isAutoBasis ) {
205
207
  return 'flex-grow';
206
208
  }
207
209
 
208
- if ( shrink === DEFAULT ) {
210
+ if ( shrink === DEFAULT && ( grow === null || grow === 0 ) && isAutoBasis ) {
209
211
  return 'flex-shrink';
210
212
  }
211
213
 
@@ -20,7 +20,11 @@ export const FontFamilyField = () => {
20
20
  return (
21
21
  <StylesField bind="font-family" propDisplayName={ FONT_FAMILY_LABEL }>
22
22
  <StylesFieldLayout label={ FONT_FAMILY_LABEL }>
23
- <FontFamilyControl fontFamilies={ fontFamilies } sectionWidth={ sectionWidth } />
23
+ <FontFamilyControl
24
+ fontFamilies={ fontFamilies }
25
+ sectionWidth={ sectionWidth }
26
+ ariaLabel={ FONT_FAMILY_LABEL }
27
+ />
24
28
  </StylesFieldLayout>
25
29
  </StylesField>
26
30
  );
@@ -14,7 +14,7 @@ export const FontSizeField = () => {
14
14
  return (
15
15
  <StylesField bind="font-size" propDisplayName={ FONT_SIZE_LABEL }>
16
16
  <StylesFieldLayout label={ FONT_SIZE_LABEL } ref={ rowRef }>
17
- <SizeControl anchorRef={ rowRef } />
17
+ <SizeControl anchorRef={ rowRef } ariaLabel={ FONT_SIZE_LABEL } />
18
18
  </StylesFieldLayout>
19
19
  </StylesField>
20
20
  );
@@ -14,7 +14,7 @@ export const LetterSpacingField = () => {
14
14
  return (
15
15
  <StylesField bind="letter-spacing" propDisplayName={ LETTER_SPACING_LABEL }>
16
16
  <StylesFieldLayout label={ LETTER_SPACING_LABEL } ref={ rowRef }>
17
- <SizeControl anchorRef={ rowRef } />
17
+ <SizeControl anchorRef={ rowRef } min={ -Number.MAX_SAFE_INTEGER } />
18
18
  </StylesFieldLayout>
19
19
  </StylesField>
20
20
  );
@@ -11,7 +11,7 @@ export const TextColorField = () => {
11
11
  return (
12
12
  <StylesField bind="color" propDisplayName={ TEXT_COLOR_LABEL }>
13
13
  <StylesFieldLayout label={ TEXT_COLOR_LABEL }>
14
- <ColorControl />
14
+ <ColorControl id="text-color-control" />
15
15
  </StylesFieldLayout>
16
16
  </StylesField>
17
17
  );
@@ -14,7 +14,7 @@ export const WordSpacingField = () => {
14
14
  return (
15
15
  <StylesField bind="word-spacing" propDisplayName={ WORD_SPACING_LABEL }>
16
16
  <StylesFieldLayout label={ WORD_SPACING_LABEL } ref={ rowRef }>
17
- <SizeControl anchorRef={ rowRef } />
17
+ <SizeControl anchorRef={ rowRef } min={ -Number.MAX_SAFE_INTEGER } />
18
18
  </StylesFieldLayout>
19
19
  </StylesField>
20
20
  );
@@ -4,13 +4,13 @@ import { useDefaultPanelSettings } from '../hooks/use-default-panel-settings';
4
4
  import { Section } from './section';
5
5
  import { getStylesInheritanceIndicators } from './style-tab-collapsible-content';
6
6
 
7
- type Section = {
7
+ type SectionType = {
8
8
  component: () => React.JSX.Element;
9
9
  name: string;
10
10
  title: string;
11
11
  };
12
12
 
13
- type Props = { section: Section; fields?: string[]; unmountOnExit?: boolean };
13
+ type Props = { section: SectionType; fields?: string[]; unmountOnExit?: boolean };
14
14
 
15
15
  export const StyleTabSection = ( { section, fields = [], unmountOnExit = true }: Props ) => {
16
16
  const { component, name, title } = section;
@@ -3,7 +3,7 @@ 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
+ import { createLocation } from '@elementor/locations';
7
7
  import { SessionStorageProvider } from '@elementor/session';
8
8
  import { Box, Divider, Stack } from '@elementor/ui';
9
9
  import { __ } from '@wordpress/i18n';
@@ -15,7 +15,6 @@ import { StyleProvider } from '../contexts/style-context';
15
15
  import { StyleInheritanceProvider } from '../contexts/styles-inheritance-context';
16
16
  import { useActiveStyleDefId } from '../hooks/use-active-style-def-id';
17
17
  import { CssClassSelector } from './css-classes/css-class-selector';
18
- import { CustomCss } from './custom-css';
19
18
  import { SectionsList } from './sections-list';
20
19
  import { BackgroundSection } from './style-sections/background-section/background-section';
21
20
  import { BorderSection } from './style-sections/border-section/border-section';
@@ -29,6 +28,8 @@ import { StyleTabSection } from './style-tab-section';
29
28
 
30
29
  const TABS_HEADER_HEIGHT = '37px';
31
30
 
31
+ export const { Slot: StyleTabSlot, inject: injectIntoStyleTab } = createLocation();
32
+
32
33
  export const stickyHeaderStyles = {
33
34
  position: 'sticky',
34
35
  zIndex: 1100,
@@ -39,10 +40,13 @@ export const stickyHeaderStyles = {
39
40
 
40
41
  export const StyleTab = () => {
41
42
  const currentClassesProp = useCurrentClassesProp();
42
- const [ activeStyleDefId, setActiveStyleDefId ] = useActiveStyleDefId( currentClassesProp );
43
+ const [ activeStyleDefId, setActiveStyleDefId ] = useActiveStyleDefId( currentClassesProp ?? '' );
43
44
  const [ activeStyleState, setActiveStyleState ] = useState< StyleDefinitionState | null >( null );
44
45
  const breakpoint = useActiveBreakpoint();
45
- const shouldRenderCustomCss = isExperimentActive( EXPERIMENTAL_FEATURES.CUSTOM_CSS );
46
+
47
+ if ( ! currentClassesProp ) {
48
+ return null;
49
+ }
46
50
 
47
51
  return (
48
52
  <ClassesPropProvider prop={ currentClassesProp }>
@@ -159,6 +163,7 @@ export const StyleTab = () => {
159
163
  title: __( 'Effects', 'elementor' ),
160
164
  } }
161
165
  fields={ [
166
+ 'mix-blend-mode',
162
167
  'box-shadow',
163
168
  'opacity',
164
169
  'transform',
@@ -168,17 +173,7 @@ export const StyleTab = () => {
168
173
  'transition',
169
174
  ] }
170
175
  />
171
- { shouldRenderCustomCss && (
172
- <StyleTabSection
173
- section={ {
174
- component: CustomCss,
175
- name: 'Custom CSS',
176
- title: __( 'Custom CSS', 'elementor' ),
177
- } }
178
- fields={ [ 'custom_css' ] }
179
- unmountOnExit={ false }
180
- />
181
- ) }
176
+ <StyleTabSlot />
182
177
  </SectionsList>
183
178
  <Box sx={ { height: '150px' } } />
184
179
  </StyleInheritanceProvider>
@@ -198,7 +193,7 @@ function ClassesHeader( { children }: { children: React.ReactNode } ) {
198
193
  );
199
194
  }
200
195
 
201
- function useCurrentClassesProp(): string {
196
+ function useCurrentClassesProp(): string | null {
202
197
  const { elementType } = useElement();
203
198
 
204
199
  const prop = Object.entries( elementType.propsSchema ).find(
@@ -206,7 +201,7 @@ function useCurrentClassesProp(): string {
206
201
  );
207
202
 
208
203
  if ( ! prop ) {
209
- throw new Error( 'Element does not have a classes prop' );
204
+ return null;
210
205
  }
211
206
 
212
207
  return prop[ 0 ];
@@ -20,7 +20,14 @@ export const StylesFieldLayout = React.forwardRef< HTMLDivElement, StylesFieldLa
20
20
  const Row = React.forwardRef< HTMLDivElement, { label: string; children: React.ReactNode } >(
21
21
  ( { label, children }, ref ) => {
22
22
  return (
23
- <Grid container gap={ 2 } alignItems="center" flexWrap="nowrap" ref={ ref }>
23
+ <Grid
24
+ container
25
+ gap={ 2 }
26
+ alignItems="center"
27
+ flexWrap="nowrap"
28
+ ref={ ref }
29
+ aria-label={ `${ label } control` }
30
+ >
24
31
  <Grid item xs={ 6 }>
25
32
  <ControlLabel>{ label }</ControlLabel>
26
33
  </Grid>
File without changes
@@ -12,7 +12,7 @@ export const ConditionalField: React.FC< {
12
12
 
13
13
  const { values: depValues } = useStylesFields( depList );
14
14
 
15
- const isHidden = ! isDependencyMet( propType?.dependencies, depValues );
15
+ const isHidden = ! isDependencyMet( propType?.dependencies, depValues ).isMet;
16
16
 
17
17
  return isHidden ? null : children;
18
18
  };
@@ -18,10 +18,22 @@ const StyledContainer = styled( Box, {
18
18
  ...getGridLayout( layout ),
19
19
  } ) );
20
20
 
21
- const getGridLayout = ( layout: Omit< ControlLayout, 'custom' > ) => ( {
21
+ const getGridLayout = ( layout: ControlLayout ) => ( {
22
22
  justifyContent: 'space-between',
23
- gridTemplateColumns: {
24
- full: 'minmax(0, 1fr)',
25
- 'two-columns': 'repeat(2, minmax(0, 1fr))',
26
- }[ layout as string ],
23
+ ...getStyleByLayout( layout ),
27
24
  } );
25
+
26
+ const getStyleByLayout = ( layout: ControlLayout ) => {
27
+ if ( layout === 'full' ) {
28
+ return {
29
+ gridTemplateColumns: 'minmax(0, 1fr)',
30
+ };
31
+ }
32
+
33
+ if ( layout === 'two-columns' ) {
34
+ return {
35
+ alignItems: 'center',
36
+ gridTemplateColumns: 'repeat(2, minmax(0, 1fr))',
37
+ };
38
+ }
39
+ };
@@ -1,34 +1,42 @@
1
1
  import {
2
2
  type ControlComponent,
3
+ DateTimeControl,
4
+ HtmlTagControl,
3
5
  ImageControl,
6
+ InlineEditingControl,
4
7
  KeyValueControl,
5
8
  LinkControl,
6
9
  NumberControl,
10
+ QueryControl,
7
11
  RepeatableControl,
8
- SelectControl,
12
+ SelectControlWrapper,
9
13
  SizeControl,
10
14
  SvgMediaControl,
11
15
  SwitchControl,
12
16
  TextAreaControl,
13
17
  TextControl,
18
+ ToggleControl,
14
19
  UrlControl,
15
20
  } from '@elementor/editor-controls';
16
21
  import { type ControlLayout } from '@elementor/editor-elements';
17
22
  import {
18
23
  booleanPropTypeUtil,
24
+ DateTimePropTypeUtil,
25
+ htmlPropTypeUtil,
19
26
  imagePropTypeUtil,
20
27
  imageSrcPropTypeUtil,
21
28
  keyValuePropTypeUtil,
22
29
  linkPropTypeUtil,
23
30
  numberPropTypeUtil,
24
31
  type PropTypeUtil,
32
+ queryPropTypeUtil,
25
33
  sizePropTypeUtil,
26
34
  stringPropTypeUtil,
27
35
  } from '@elementor/editor-props';
28
36
 
29
37
  import { ControlTypeAlreadyRegisteredError, ControlTypeNotRegisteredError } from '../errors';
30
38
 
31
- type ControlRegistry = Record<
39
+ export type ControlRegistry = Record<
32
40
  string,
33
41
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
34
42
  { component: ControlComponent; layout: ControlLayout; propTypeUtil?: PropTypeUtil< string, any > }
@@ -40,13 +48,18 @@ const controlTypes = {
40
48
  text: { component: TextControl, layout: 'full', propTypeUtil: stringPropTypeUtil },
41
49
  textarea: { component: TextAreaControl, layout: 'full', propTypeUtil: stringPropTypeUtil },
42
50
  size: { component: SizeControl, layout: 'two-columns', propTypeUtil: sizePropTypeUtil },
43
- select: { component: SelectControl, layout: 'two-columns', propTypeUtil: stringPropTypeUtil },
51
+ select: { component: SelectControlWrapper, layout: 'two-columns', propTypeUtil: stringPropTypeUtil },
44
52
  link: { component: LinkControl, layout: 'custom', propTypeUtil: linkPropTypeUtil },
53
+ query: { component: QueryControl, layout: 'full', propTypeUtil: queryPropTypeUtil },
45
54
  url: { component: UrlControl, layout: 'full', propTypeUtil: stringPropTypeUtil },
46
55
  switch: { component: SwitchControl, layout: 'two-columns', propTypeUtil: booleanPropTypeUtil },
47
56
  number: { component: NumberControl, layout: 'two-columns', propTypeUtil: numberPropTypeUtil },
48
57
  repeatable: { component: RepeatableControl, layout: 'full', propTypeUtil: undefined },
49
58
  'key-value': { component: KeyValueControl, layout: 'full', propTypeUtil: keyValuePropTypeUtil },
59
+ 'html-tag': { component: HtmlTagControl, layout: 'two-columns', propTypeUtil: stringPropTypeUtil },
60
+ toggle: { component: ToggleControl, layout: 'full', propTypeUtil: stringPropTypeUtil },
61
+ 'date-time': { component: DateTimeControl, layout: 'full', propTypeUtil: DateTimePropTypeUtil },
62
+ 'inline-editing': { component: InlineEditingControl, layout: 'full', propTypeUtil: htmlPropTypeUtil },
50
63
  } as const satisfies ControlRegistry;
51
64
 
52
65
  export type ControlType = keyof typeof controlTypes;
@@ -56,7 +69,7 @@ export type ControlTypes = {
56
69
  };
57
70
 
58
71
  class ControlsRegistry {
59
- constructor( private readonly controlsRegistry: ControlRegistry = controlTypes ) {
72
+ constructor( private readonly controlsRegistry: ControlRegistry ) {
60
73
  this.controlsRegistry = controlsRegistry;
61
74
  }
62
75
 
@@ -98,4 +111,4 @@ class ControlsRegistry {
98
111
  }
99
112
  }
100
113
 
101
- export const controlsRegistry = new ControlsRegistry();
114
+ export const controlsRegistry = new ControlsRegistry( controlTypes );
@@ -0,0 +1,21 @@
1
+ import { getContainer } from '@elementor/editor-elements';
2
+
3
+ export const getElementByType = ( elementId: string, type: string ) => {
4
+ const currentElement = getContainer( elementId );
5
+
6
+ if ( ! currentElement ) {
7
+ throw new Error( `Current element not found, elementId: ${ elementId }` );
8
+ }
9
+
10
+ if ( currentElement.model.get( 'elType' ) === type ) {
11
+ return currentElement;
12
+ }
13
+
14
+ const element = currentElement.children?.findRecursive?.( ( child ) => child.model.get( 'elType' ) === type );
15
+
16
+ if ( ! element ) {
17
+ throw new Error( `Child element ${ type } not found` );
18
+ }
19
+
20
+ return element;
21
+ };
@@ -0,0 +1,16 @@
1
+ import { type ControlComponent } from '@elementor/editor-controls';
2
+
3
+ import { type ControlRegistry, controlsRegistry } from '../controls-registry';
4
+ import { TabsControl } from './tabs-control/tabs-control';
5
+
6
+ const controlTypes = {
7
+ tabs: { component: TabsControl as ControlComponent, layout: 'full' },
8
+ } as const satisfies ControlRegistry;
9
+
10
+ export const registerElementControls = () => {
11
+ Object.entries< ( typeof controlTypes )[ keyof typeof controlTypes ] >( controlTypes ).forEach(
12
+ ( [ type, { component, layout } ] ) => {
13
+ controlsRegistry.register( type, component, layout );
14
+ }
15
+ );
16
+ };