@elementor/editor-editing-panel 1.45.0 → 1.47.0

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 (86) hide show
  1. package/CHANGELOG.md +90 -0
  2. package/dist/index.d.mts +22 -4
  3. package/dist/index.d.ts +22 -4
  4. package/dist/index.js +1187 -1051
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +1027 -893
  7. package/dist/index.mjs.map +1 -1
  8. package/package.json +18 -17
  9. package/src/components/css-classes/css-class-menu.tsx +6 -8
  10. package/src/components/css-classes/css-class-selector.tsx +17 -11
  11. package/src/components/popover-scrollable-content.tsx +12 -0
  12. package/src/components/section-content.tsx +6 -16
  13. package/src/components/section.tsx +8 -4
  14. package/src/components/settings-tab.tsx +30 -4
  15. package/src/components/style-indicator.tsx +19 -15
  16. package/src/components/style-sections/background-section/background-section.tsx +4 -1
  17. package/src/components/style-sections/border-section/border-color-field.tsx +10 -16
  18. package/src/components/style-sections/border-section/border-field.tsx +4 -6
  19. package/src/components/style-sections/border-section/border-radius-field.tsx +4 -2
  20. package/src/components/style-sections/border-section/border-style-field.tsx +11 -16
  21. package/src/components/style-sections/border-section/border-width-field.tsx +4 -2
  22. package/src/components/style-sections/effects-section/effects-section.tsx +22 -2
  23. package/src/components/style-sections/layout-section/align-content-field.tsx +11 -12
  24. package/src/components/style-sections/layout-section/align-items-field.tsx +8 -11
  25. package/src/components/style-sections/layout-section/align-self-child-field.tsx +11 -16
  26. package/src/components/style-sections/layout-section/display-field.tsx +6 -6
  27. package/src/components/style-sections/layout-section/flex-direction-field.tsx +11 -14
  28. package/src/components/style-sections/layout-section/flex-order-field.tsx +32 -37
  29. package/src/components/style-sections/layout-section/flex-size-field.tsx +35 -58
  30. package/src/components/style-sections/layout-section/gap-control-field.tsx +5 -6
  31. package/src/components/style-sections/layout-section/justify-content-field.tsx +11 -12
  32. package/src/components/style-sections/layout-section/layout-section.tsx +2 -2
  33. package/src/components/style-sections/layout-section/opacity-control-field.tsx +21 -0
  34. package/src/components/style-sections/layout-section/utils/rotated-icon.tsx +1 -1
  35. package/src/components/style-sections/layout-section/wrap-field.tsx +10 -14
  36. package/src/components/style-sections/position-section/dimensions-field.tsx +4 -4
  37. package/src/components/style-sections/position-section/offset-field.tsx +12 -14
  38. package/src/components/style-sections/position-section/position-field.tsx +7 -11
  39. package/src/components/style-sections/position-section/position-section.tsx +6 -6
  40. package/src/components/style-sections/position-section/z-index-field.tsx +7 -11
  41. package/src/components/style-sections/size-section/object-fit-field.tsx +7 -11
  42. package/src/components/style-sections/size-section/object-position-field.tsx +4 -23
  43. package/src/components/style-sections/size-section/overflow-field.tsx +7 -11
  44. package/src/components/style-sections/size-section/size-section.tsx +10 -8
  45. package/src/components/style-sections/spacing-section/spacing-section.tsx +7 -4
  46. package/src/components/style-sections/typography-section/column-count-field.tsx +7 -11
  47. package/src/components/style-sections/typography-section/column-gap-field.tsx +9 -13
  48. package/src/components/style-sections/typography-section/font-family-field.tsx +9 -11
  49. package/src/components/style-sections/typography-section/font-size-field.tsx +9 -13
  50. package/src/components/style-sections/typography-section/font-style-field.tsx +13 -13
  51. package/src/components/style-sections/typography-section/font-weight-field.tsx +7 -11
  52. package/src/components/style-sections/typography-section/letter-spacing-field.tsx +9 -13
  53. package/src/components/style-sections/typography-section/line-height-field.tsx +9 -13
  54. package/src/components/style-sections/typography-section/text-alignment-field.tsx +11 -14
  55. package/src/components/style-sections/typography-section/text-color-field.tsx +7 -11
  56. package/src/components/style-sections/typography-section/text-decoration-field.tsx +7 -11
  57. package/src/components/style-sections/typography-section/text-direction-field.tsx +7 -11
  58. package/src/components/style-sections/typography-section/text-stroke-field.tsx +8 -8
  59. package/src/components/style-sections/typography-section/transform-field.tsx +7 -11
  60. package/src/components/style-sections/typography-section/typography-section.tsx +4 -2
  61. package/src/components/style-sections/typography-section/word-spacing-field.tsx +9 -13
  62. package/src/components/styles-field-layout.tsx +50 -0
  63. package/src/contexts/section-context.tsx +14 -0
  64. package/src/controls-registry/control-type-container.tsx +6 -2
  65. package/src/controls-registry/controls-registry.tsx +30 -10
  66. package/src/controls-registry/settings-field.tsx +65 -6
  67. package/src/controls-registry/styles-field.tsx +11 -5
  68. package/src/dynamics/components/dynamic-selection-control.tsx +20 -22
  69. package/src/dynamics/components/dynamic-selection.tsx +64 -79
  70. package/src/dynamics/hooks/use-prop-dynamic-action.tsx +1 -1
  71. package/src/hooks/use-default-panel-settings.ts +4 -0
  72. package/src/hooks/use-styles-field.ts +9 -3
  73. package/src/hooks/use-styles-fields.ts +4 -4
  74. package/src/index.ts +5 -0
  75. package/src/popover-action.tsx +11 -6
  76. package/src/provider-colors-registry.ts +20 -0
  77. package/src/styles-inheritance/components/infotip/label-chip.tsx +4 -5
  78. package/src/styles-inheritance/components/styles-inheritance-indicator.tsx +36 -41
  79. package/src/styles-inheritance/components/styles-inheritance-infotip.tsx +10 -24
  80. package/src/styles-inheritance/components/styles-inheritance-section-indicators.tsx +29 -44
  81. package/src/styles-inheritance/hooks/use-normalized-inheritance-chain-items.tsx +1 -17
  82. package/src/styles-inheritance/types.ts +0 -2
  83. package/src/styles-inheritance/utils.ts +17 -1
  84. package/src/sync/experiments-flags.ts +1 -0
  85. package/src/utils/get-styles-provider-color.ts +28 -0
  86. package/src/components/popover-content.tsx +0 -15
@@ -1,22 +1,18 @@
1
1
  import * as React from 'react';
2
2
  import { ColorControl } from '@elementor/editor-controls';
3
- import { Grid } from '@elementor/ui';
4
3
  import { __ } from '@wordpress/i18n';
5
4
 
6
5
  import { StylesField } from '../../../controls-registry/styles-field';
7
- import { ControlLabel } from '../../control-label';
6
+ import { StylesFieldLayout } from '../../styles-field-layout';
7
+
8
+ const TEXT_COLOR_LABEL = __( 'Text color', 'elementor' );
8
9
 
9
10
  export const TextColorField = () => {
10
11
  return (
11
- <StylesField bind="color">
12
- <Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
13
- <Grid item xs={ 6 }>
14
- <ControlLabel>{ __( 'Text color', 'elementor' ) }</ControlLabel>
15
- </Grid>
16
- <Grid item xs={ 6 }>
17
- <ColorControl />
18
- </Grid>
19
- </Grid>
12
+ <StylesField bind="color" propDisplayName={ TEXT_COLOR_LABEL }>
13
+ <StylesFieldLayout label={ TEXT_COLOR_LABEL }>
14
+ <ColorControl />
15
+ </StylesFieldLayout>
20
16
  </StylesField>
21
17
  );
22
18
  };
@@ -1,14 +1,15 @@
1
1
  import * as React from 'react';
2
2
  import { ToggleControl, type ToggleControlProps } from '@elementor/editor-controls';
3
3
  import { MinusIcon, OverlineIcon, StrikethroughIcon, UnderlineIcon } from '@elementor/icons';
4
- import { Grid } from '@elementor/ui';
5
4
  import { __ } from '@wordpress/i18n';
6
5
 
7
6
  import { StylesField } from '../../../controls-registry/styles-field';
8
- import { ControlLabel } from '../../control-label';
7
+ import { StylesFieldLayout } from '../../styles-field-layout';
9
8
 
10
9
  type Decoration = 'none' | 'underline' | 'line-through' | 'overline';
11
10
 
11
+ const TEXT_DECORATION_LABEL = __( 'Line decoration', 'elementor' );
12
+
12
13
  const options: ToggleControlProps< Decoration >[ 'options' ] = [
13
14
  {
14
15
  value: 'none',
@@ -37,14 +38,9 @@ const options: ToggleControlProps< Decoration >[ 'options' ] = [
37
38
  },
38
39
  ];
39
40
  export const TextDecorationField = () => (
40
- <StylesField bind={ 'text-decoration' }>
41
- <Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
42
- <Grid item xs={ 6 }>
43
- <ControlLabel>{ __( 'Line decoration', 'elementor' ) }</ControlLabel>
44
- </Grid>
45
- <Grid item xs={ 6 } display="flex" justifyContent="end">
46
- <ToggleControl options={ options } exclusive={ false } />
47
- </Grid>
48
- </Grid>
41
+ <StylesField bind="text-decoration" propDisplayName={ TEXT_DECORATION_LABEL }>
42
+ <StylesFieldLayout label={ TEXT_DECORATION_LABEL }>
43
+ <ToggleControl options={ options } exclusive={ false } />
44
+ </StylesFieldLayout>
49
45
  </StylesField>
50
46
  );
@@ -1,14 +1,15 @@
1
1
  import * as React from 'react';
2
2
  import { type ToggleButtonGroupItem, ToggleControl } from '@elementor/editor-controls';
3
3
  import { TextDirectionLtrIcon, TextDirectionRtlIcon } from '@elementor/icons';
4
- import { Grid } from '@elementor/ui';
5
4
  import { __ } from '@wordpress/i18n';
6
5
 
7
6
  import { StylesField } from '../../../controls-registry/styles-field';
8
- import { ControlLabel } from '../../control-label';
7
+ import { StylesFieldLayout } from '../../styles-field-layout';
9
8
 
10
9
  type Direction = 'ltr' | 'rtl';
11
10
 
11
+ const TEXT_DIRECTION_LABEL = __( 'Direction', 'elementor' );
12
+
12
13
  const options: ToggleButtonGroupItem< Direction >[] = [
13
14
  {
14
15
  value: 'ltr',
@@ -26,15 +27,10 @@ const options: ToggleButtonGroupItem< Direction >[] = [
26
27
 
27
28
  export const TextDirectionField = () => {
28
29
  return (
29
- <StylesField bind={ 'direction' }>
30
- <Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
31
- <Grid item xs={ 6 }>
32
- <ControlLabel>{ __( 'Direction', 'elementor' ) }</ControlLabel>
33
- </Grid>
34
- <Grid item xs={ 6 } display="flex" justifyContent="end">
35
- <ToggleControl options={ options } />
36
- </Grid>
37
- </Grid>
30
+ <StylesField bind="direction" propDisplayName={ TEXT_DIRECTION_LABEL }>
31
+ <StylesFieldLayout label={ TEXT_DIRECTION_LABEL }>
32
+ <ToggleControl options={ options } />
33
+ </StylesFieldLayout>
38
34
  </StylesField>
39
35
  );
40
36
  };
@@ -2,7 +2,6 @@ import * as React from 'react';
2
2
  import { StrokeControl } from '@elementor/editor-controls';
3
3
  import { __ } from '@wordpress/i18n';
4
4
 
5
- import { useStyle } from '../../../contexts/style-context';
6
5
  import { StylesField } from '../../../controls-registry/styles-field';
7
6
  import { useStylesField } from '../../../hooks/use-styles-field';
8
7
  import { AddOrRemoveContent } from '../../add-or-remove-content';
@@ -25,28 +24,29 @@ const initTextStroke = {
25
24
  },
26
25
  };
27
26
 
27
+ const TEXT_STROKE_LABEL = __( 'Text stroke', 'elementor' );
28
+
28
29
  export const TextStrokeField = () => {
29
- const { canEdit } = useStyle();
30
- const [ textStroke, setTextStroke ] = useStylesField( 'stroke' );
30
+ const { value, setValue, canEdit } = useStylesField( 'stroke' );
31
31
 
32
32
  const addTextStroke = () => {
33
- setTextStroke( initTextStroke );
33
+ setValue( initTextStroke );
34
34
  };
35
35
 
36
36
  const removeTextStroke = () => {
37
- setTextStroke( null );
37
+ setValue( null );
38
38
  };
39
39
 
40
- const hasTextStroke = Boolean( textStroke );
40
+ const hasTextStroke = Boolean( value );
41
41
 
42
42
  return (
43
- <StylesField bind={ 'stroke' }>
43
+ <StylesField bind={ 'stroke' } propDisplayName={ TEXT_STROKE_LABEL }>
44
44
  <AddOrRemoveContent
45
45
  isAdded={ hasTextStroke }
46
46
  onAdd={ addTextStroke }
47
47
  onRemove={ removeTextStroke }
48
48
  disabled={ ! canEdit }
49
- renderLabel={ () => <ControlLabel>{ __( 'Text stroke', 'elementor' ) }</ControlLabel> }
49
+ renderLabel={ () => <ControlLabel>{ TEXT_STROKE_LABEL }</ControlLabel> }
50
50
  >
51
51
  <StrokeControl />
52
52
  </AddOrRemoveContent>
@@ -1,14 +1,15 @@
1
1
  import * as React from 'react';
2
2
  import { type ToggleButtonGroupItem, ToggleControl } from '@elementor/editor-controls';
3
3
  import { LetterCaseIcon, LetterCaseLowerIcon, LetterCaseUpperIcon, MinusIcon } from '@elementor/icons';
4
- import { Grid } from '@elementor/ui';
5
4
  import { __ } from '@wordpress/i18n';
6
5
 
7
6
  import { StylesField } from '../../../controls-registry/styles-field';
8
- import { ControlLabel } from '../../control-label';
7
+ import { StylesFieldLayout } from '../../styles-field-layout';
9
8
 
10
9
  type Transforms = 'none' | 'capitalize' | 'uppercase' | 'lowercase';
11
10
 
11
+ const TEXT_TRANSFORM_LABEL = __( 'Text transform', 'elementor' );
12
+
12
13
  const options: ToggleButtonGroupItem< Transforms >[] = [
13
14
  {
14
15
  value: 'none',
@@ -37,14 +38,9 @@ const options: ToggleButtonGroupItem< Transforms >[] = [
37
38
  ];
38
39
 
39
40
  export const TransformField = () => (
40
- <StylesField bind={ 'text-transform' }>
41
- <Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
42
- <Grid item xs={ 6 }>
43
- <ControlLabel>{ __( 'Text transform', 'elementor' ) }</ControlLabel>
44
- </Grid>
45
- <Grid item xs={ 6 } display="flex" justifyContent="end">
46
- <ToggleControl options={ options } />
47
- </Grid>
48
- </Grid>
41
+ <StylesField bind="text-transform" propDisplayName={ TEXT_TRANSFORM_LABEL }>
42
+ <StylesFieldLayout label={ TEXT_TRANSFORM_LABEL }>
43
+ <ToggleControl options={ options } />
44
+ </StylesFieldLayout>
49
45
  </StylesField>
50
46
  );
@@ -23,9 +23,11 @@ import { TransformField } from './transform-field';
23
23
  import { WordSpacingField } from './word-spacing-field';
24
24
 
25
25
  export const TypographySection = () => {
26
- const [ columnCount ] = useStylesField< NumberPropValue >( 'column-count' );
27
- const isVersion330Active = isExperimentActive( 'e_v_3_30' );
26
+ const { value: columnCount } = useStylesField< NumberPropValue >( 'column-count' );
28
27
  const hasMultiColumns = !! ( columnCount?.value && columnCount?.value > 1 );
28
+
29
+ const isVersion330Active = isExperimentActive( 'e_v_3_30' );
30
+
29
31
  return (
30
32
  <SectionContent>
31
33
  <FontFamilyField />
@@ -1,25 +1,21 @@
1
1
  import * as React from 'react';
2
- import { type MutableRefObject, useRef } from 'react';
2
+ import { useRef } from 'react';
3
3
  import { SizeControl } from '@elementor/editor-controls';
4
- import { Grid } from '@elementor/ui';
5
4
  import { __ } from '@wordpress/i18n';
6
5
 
7
6
  import { StylesField } from '../../../controls-registry/styles-field';
8
- import { ControlLabel } from '../../control-label';
7
+ import { StylesFieldLayout } from '../../styles-field-layout';
8
+
9
+ const WORD_SPACING_LABEL = __( 'Word spacing', 'elementor' );
9
10
 
10
11
  export const WordSpacingField = () => {
11
- const rowRef: MutableRefObject< HTMLElement | undefined > = useRef();
12
+ const rowRef = useRef< HTMLDivElement >( null );
12
13
 
13
14
  return (
14
- <StylesField bind="word-spacing">
15
- <Grid container gap={ 2 } alignItems="center" flexWrap="nowrap" ref={ rowRef }>
16
- <Grid item xs={ 6 }>
17
- <ControlLabel>{ __( 'Word spacing', 'elementor' ) }</ControlLabel>
18
- </Grid>
19
- <Grid item xs={ 6 }>
20
- <SizeControl anchorRef={ rowRef } />
21
- </Grid>
22
- </Grid>
15
+ <StylesField bind="word-spacing" propDisplayName={ WORD_SPACING_LABEL }>
16
+ <StylesFieldLayout label={ WORD_SPACING_LABEL } ref={ rowRef }>
17
+ <SizeControl anchorRef={ rowRef } />
18
+ </StylesFieldLayout>
23
19
  </StylesField>
24
20
  );
25
21
  };
@@ -0,0 +1,50 @@
1
+ import * as React from 'react';
2
+ import { Grid, Stack, type Theme } from '@elementor/ui';
3
+
4
+ import { ControlLabel } from './control-label';
5
+
6
+ type StylesFieldLayoutProps = {
7
+ label: string;
8
+ children: React.ReactNode;
9
+ direction?: 'row' | 'column';
10
+ };
11
+
12
+ export const StylesFieldLayout = React.forwardRef< HTMLDivElement, StylesFieldLayoutProps >( ( props, ref ) => {
13
+ const { direction = 'row', children, label } = props;
14
+
15
+ const LayoutComponent = direction === 'row' ? Row : Column;
16
+
17
+ return <LayoutComponent label={ label } ref={ ref } children={ children } />;
18
+ } );
19
+
20
+ const Row = React.forwardRef< HTMLDivElement, { label: string; children: React.ReactNode } >(
21
+ ( { label, children }, ref ) => {
22
+ return (
23
+ <Grid container gap={ 2 } alignItems="center" flexWrap="nowrap" ref={ ref }>
24
+ <Grid item xs={ 6 }>
25
+ <ControlLabel>{ label }</ControlLabel>
26
+ </Grid>
27
+ <Grid
28
+ item
29
+ xs={ 6 }
30
+ sx={ ( theme: Theme ) => ( {
31
+ width: `calc(50% - ${ theme.spacing( 2 ) })`,
32
+ } ) }
33
+ >
34
+ { children }
35
+ </Grid>
36
+ </Grid>
37
+ );
38
+ }
39
+ );
40
+
41
+ const Column = React.forwardRef< HTMLDivElement, { label: string; children: React.ReactNode } >(
42
+ ( { label, children }, ref ) => {
43
+ return (
44
+ <Stack gap={ 0.75 } ref={ ref }>
45
+ <ControlLabel>{ label }</ControlLabel>
46
+ { children }
47
+ </Stack>
48
+ );
49
+ }
50
+ );
@@ -0,0 +1,14 @@
1
+ import { createContext, useContext } from 'react';
2
+ import type { RefObject } from 'react';
3
+
4
+ const FALLBACK_SECTION_WIDTH = 320;
5
+
6
+ export const SectionRefContext = createContext< RefObject< HTMLElement > | null >( null );
7
+
8
+ const useSectionRef = () => useContext( SectionRefContext );
9
+
10
+ export const useSectionWidth = (): number => {
11
+ const sectionRef = useSectionRef();
12
+
13
+ return sectionRef?.current?.offsetWidth ?? FALLBACK_SECTION_WIDTH;
14
+ };
@@ -3,6 +3,10 @@ import { type ControlLayout } from '@elementor/editor-elements';
3
3
  import { Box, type BoxProps, styled } from '@elementor/ui';
4
4
 
5
5
  export const ControlTypeContainer = ( { children, layout }: React.PropsWithChildren< { layout?: ControlLayout } > ) => {
6
+ if ( layout === 'custom' ) {
7
+ return children;
8
+ }
9
+
6
10
  return <StyledContainer layout={ layout }>{ children }</StyledContainer>;
7
11
  };
8
12
 
@@ -14,10 +18,10 @@ const StyledContainer = styled( Box, {
14
18
  ...getGridLayout( layout ),
15
19
  } ) );
16
20
 
17
- const getGridLayout = ( layout: ControlLayout ) => ( {
21
+ const getGridLayout = ( layout: Omit< ControlLayout, 'custom' > ) => ( {
18
22
  justifyContent: 'space-between',
19
23
  gridTemplateColumns: {
20
24
  full: 'minmax(0, 1fr)',
21
25
  'two-columns': 'repeat(2, minmax(0, 1fr))',
22
- }[ layout ],
26
+ }[ layout as string ],
23
27
  } );
@@ -1,7 +1,9 @@
1
1
  import {
2
2
  type ControlComponent,
3
3
  ImageControl,
4
+ KeyValueControl,
4
5
  LinkControl,
6
+ RepeatableControl,
5
7
  SelectControl,
6
8
  SizeControl,
7
9
  SvgMediaControl,
@@ -11,19 +13,35 @@ import {
11
13
  UrlControl,
12
14
  } from '@elementor/editor-controls';
13
15
  import { type ControlLayout } from '@elementor/editor-elements';
16
+ import {
17
+ booleanPropTypeUtil,
18
+ imagePropTypeUtil,
19
+ imageSrcPropTypeUtil,
20
+ keyValuePropTypeUtil,
21
+ linkPropTypeUtil,
22
+ type PropTypeUtil,
23
+ sizePropTypeUtil,
24
+ stringPropTypeUtil,
25
+ } from '@elementor/editor-props';
14
26
 
15
- type ControlRegistry = Record< string, { component: ControlComponent; layout: ControlLayout } >;
27
+ type ControlRegistry = Record<
28
+ string,
29
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
30
+ { component: ControlComponent; layout: ControlLayout; propTypeUtil?: PropTypeUtil< string, any > }
31
+ >;
16
32
 
17
33
  const controlTypes = {
18
- image: { component: ImageControl, layout: 'full' },
19
- 'svg-media': { component: SvgMediaControl, layout: 'full' },
20
- text: { component: TextControl, layout: 'full' },
21
- textarea: { component: TextAreaControl, layout: 'full' },
22
- size: { component: SizeControl, layout: 'two-columns' },
23
- select: { component: SelectControl, layout: 'two-columns' },
24
- link: { component: LinkControl, layout: 'full' },
25
- url: { component: UrlControl, layout: 'full' },
26
- switch: { component: SwitchControl, layout: 'two-columns' },
34
+ image: { component: ImageControl, layout: 'full', propTypeUtil: imagePropTypeUtil },
35
+ 'svg-media': { component: SvgMediaControl, layout: 'full', propTypeUtil: imageSrcPropTypeUtil },
36
+ text: { component: TextControl, layout: 'full', propTypeUtil: stringPropTypeUtil },
37
+ textarea: { component: TextAreaControl, layout: 'full', propTypeUtil: stringPropTypeUtil },
38
+ size: { component: SizeControl, layout: 'two-columns', propTypeUtil: sizePropTypeUtil },
39
+ select: { component: SelectControl, layout: 'two-columns', propTypeUtil: stringPropTypeUtil },
40
+ link: { component: LinkControl, layout: 'custom', propTypeUtil: linkPropTypeUtil },
41
+ url: { component: UrlControl, layout: 'full', propTypeUtil: stringPropTypeUtil },
42
+ switch: { component: SwitchControl, layout: 'two-columns', propTypeUtil: booleanPropTypeUtil },
43
+ repeatable: { component: RepeatableControl, layout: 'full', propTypeUtil: undefined },
44
+ 'key-value': { component: KeyValueControl, layout: 'full', propTypeUtil: keyValuePropTypeUtil },
27
45
  } as const satisfies ControlRegistry;
28
46
 
29
47
  export type ControlType = keyof typeof controlTypes;
@@ -35,3 +53,5 @@ export type ControlTypes = {
35
53
  export const getControl = ( type: ControlType ) => controlTypes[ type ]?.component;
36
54
 
37
55
  export const getDefaultLayout = ( type: ControlType ) => controlTypes[ type ].layout;
56
+
57
+ export const getPropTypeUtil = ( type: ControlType ) => controlTypes[ type ]?.propTypeUtil;
@@ -1,17 +1,29 @@
1
1
  import * as React from 'react';
2
+ import { useMemo } from 'react';
2
3
  import { PropKeyProvider, PropProvider } from '@elementor/editor-controls';
3
- import { updateElementSettings, useElementSetting } from '@elementor/editor-elements';
4
+ import { setDocumentModifiedStatus } from '@elementor/editor-documents';
5
+ import {
6
+ type ElementID,
7
+ getElementLabel,
8
+ getElementSetting,
9
+ updateElementSettings,
10
+ useElementSetting,
11
+ } from '@elementor/editor-elements';
4
12
  import { type PropKey, type PropValue } from '@elementor/editor-props';
13
+ import { isExperimentActive, undoable } from '@elementor/editor-v1-adapters';
14
+ import { __ } from '@wordpress/i18n';
5
15
 
6
16
  import { useElement } from '../contexts/element-context';
17
+ import { EXPERIMENTAL_FEATURES } from '../sync/experiments-flags';
7
18
  import { createTopLevelOjectType } from './create-top-level-object-type';
8
19
 
9
20
  type Props = {
10
21
  bind: PropKey;
22
+ propDisplayName: string;
11
23
  children: React.ReactNode;
12
24
  };
13
25
 
14
- export const SettingsField = ( { bind, children }: Props ) => {
26
+ export const SettingsField = ( { bind, children, propDisplayName }: Props ) => {
15
27
  const { element, elementType } = useElement();
16
28
 
17
29
  const settingsValue = useElementSetting< PropValue >( element.id, bind );
@@ -20,11 +32,20 @@ export const SettingsField = ( { bind, children }: Props ) => {
20
32
 
21
33
  const propType = createTopLevelOjectType( { schema: elementType.propsSchema } );
22
34
 
35
+ const undoableUpdateElementProp = useUndoableUpdateElementProp( {
36
+ propKey: bind,
37
+ elementId: element.id,
38
+ propDisplayName,
39
+ } );
40
+
23
41
  const setValue = ( newValue: Record< string, PropValue > ) => {
24
- updateElementSettings( {
25
- id: element.id,
26
- props: { ...newValue },
27
- } );
42
+ const isVersion331Active = isExperimentActive( EXPERIMENTAL_FEATURES.V_3_31 );
43
+
44
+ if ( isVersion331Active ) {
45
+ undoableUpdateElementProp( { newValue } );
46
+ } else {
47
+ updateElementSettings( { id: element.id, props: newValue } );
48
+ }
28
49
  };
29
50
 
30
51
  return (
@@ -33,3 +54,41 @@ export const SettingsField = ( { bind, children }: Props ) => {
33
54
  </PropProvider>
34
55
  );
35
56
  };
57
+
58
+ type UndoableUpdateElementSettingsArgs = {
59
+ newValue: Record< string, PropValue >;
60
+ };
61
+
62
+ function useUndoableUpdateElementProp( {
63
+ propKey,
64
+ elementId,
65
+ propDisplayName,
66
+ }: {
67
+ propKey: PropKey;
68
+ elementId: ElementID;
69
+ propDisplayName: string;
70
+ } ) {
71
+ return useMemo( () => {
72
+ return undoable(
73
+ {
74
+ do: ( { newValue }: UndoableUpdateElementSettingsArgs ) => {
75
+ const prevPropValue = getElementSetting( elementId, propKey ) as PropValue;
76
+
77
+ updateElementSettings( { id: elementId, props: { ...newValue }, withHistory: false } );
78
+ setDocumentModifiedStatus( true );
79
+
80
+ return { [ propKey ]: prevPropValue };
81
+ },
82
+
83
+ undo: ( {}, prevProps ) => {
84
+ updateElementSettings( { id: elementId, props: prevProps, withHistory: false } );
85
+ },
86
+ },
87
+ {
88
+ title: getElementLabel( elementId ),
89
+ // translators: %s is the name of the property that was edited.
90
+ subtitle: __( '%s edited', 'elementor' ).replace( '%s', propDisplayName ),
91
+ }
92
+ );
93
+ }, [ propKey, elementId, propDisplayName ] );
94
+ }
@@ -2,8 +2,9 @@ import * as React from 'react';
2
2
  import { ControlAdornmentsProvider, PropKeyProvider, PropProvider } from '@elementor/editor-controls';
3
3
  import { type PropKey, type PropValue } from '@elementor/editor-props';
4
4
  import { getStylesSchema } from '@elementor/editor-styles';
5
+ import { isExperimentActive } from '@elementor/editor-v1-adapters';
5
6
 
6
- import { useStyle } from '../contexts/style-context';
7
+ import { useStylesInheritanceChain } from '../contexts/styles-inheritance-context';
7
8
  import { useStylesField } from '../hooks/use-styles-field';
8
9
  import { StylesInheritanceIndicator } from '../styles-inheritance/components/styles-inheritance-indicator';
9
10
  import { createTopLevelOjectType } from './create-top-level-object-type';
@@ -12,19 +13,24 @@ export type StylesFieldProps = {
12
13
  bind: PropKey;
13
14
  placeholder?: PropValue;
14
15
  children: React.ReactNode;
16
+ propDisplayName: string;
15
17
  };
16
18
 
17
19
  export const StylesField = ( { bind, placeholder, children }: StylesFieldProps ) => {
18
- const [ value, setValue ] = useStylesField( bind );
19
- const { canEdit } = useStyle();
20
+ const { value, setValue, canEdit } = useStylesField( bind );
21
+
22
+ const isVersion331Active = isExperimentActive( 'e_v_3_31' );
23
+ const stylesInheritanceChain = useStylesInheritanceChain( [ bind ] );
20
24
 
21
25
  const stylesSchema = getStylesSchema();
22
26
 
23
27
  const propType = createTopLevelOjectType( { schema: stylesSchema } );
24
28
 
25
29
  const values = { [ bind ]: value };
26
- const placeholderValues = { [ bind ]: placeholder };
27
-
30
+ const [ actualValue ] = stylesInheritanceChain;
31
+ const placeholderValues = {
32
+ [ bind ]: isVersion331Active ? actualValue?.value : placeholder,
33
+ };
28
34
  const setValues = ( newValue: Record< string, PropValue > ) => {
29
35
  setValue( newValue[ bind ] );
30
36
  };
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import { ControlFormLabel, useBoundProp } from '@elementor/editor-controls';
3
3
  import type { Control, ControlsSection } from '@elementor/editor-elements';
4
- import { PopoverHeader } from '@elementor/editor-ui';
4
+ import { PopoverHeader, PopoverScrollableContent } from '@elementor/editor-ui';
5
5
  import { DatabaseIcon, SettingsIcon, XIcon } from '@elementor/icons';
6
6
  import {
7
7
  bindPopover,
@@ -10,7 +10,6 @@ import {
10
10
  Divider,
11
11
  Grid,
12
12
  IconButton,
13
- Paper,
14
13
  Popover,
15
14
  Stack,
16
15
  Tab,
@@ -22,7 +21,6 @@ import {
22
21
  } from '@elementor/ui';
23
22
  import { __ } from '@wordpress/i18n';
24
23
 
25
- import { PopoverContent } from '../../components/popover-content';
26
24
  import { Control as BaseControl } from '../../controls-registry/control';
27
25
  import { type ControlType, getControl } from '../../controls-registry/controls-registry';
28
26
  import { usePersistDynamicValue } from '../../hooks/use-persist-dynamic-value';
@@ -77,16 +75,15 @@ export const DynamicSelectionControl = () => {
77
75
  <Popover
78
76
  disablePortal
79
77
  disableScrollLock
80
- anchorOrigin={ { vertical: 'bottom', horizontal: 'left' } }
78
+ anchorOrigin={ { vertical: 'bottom', horizontal: 'right' } }
79
+ transformOrigin={ { vertical: 'top', horizontal: 'right' } }
80
+ PaperProps={ {
81
+ sx: { my: 1 },
82
+ } }
81
83
  { ...bindPopover( selectionPopoverState ) }
82
84
  >
83
85
  <Stack>
84
- <PopoverHeader
85
- title={ __( 'Dynamic tags', 'elementor' ) }
86
- onClose={ selectionPopoverState.close }
87
- icon={ <DatabaseIcon fontSize={ SIZE } /> }
88
- />
89
- <DynamicSelection onSelect={ selectionPopoverState.close } />
86
+ <DynamicSelection close={ selectionPopoverState.close } />
90
87
  </Stack>
91
88
  </Popover>
92
89
  </Box>
@@ -111,16 +108,17 @@ export const DynamicSettingsPopover = ( { dynamicTag }: { dynamicTag: DynamicTag
111
108
  disablePortal
112
109
  disableScrollLock
113
110
  anchorOrigin={ { vertical: 'bottom', horizontal: 'center' } }
111
+ PaperProps={ {
112
+ sx: { my: 0.5 },
113
+ } }
114
114
  { ...bindPopover( popupState ) }
115
115
  >
116
- <Paper component={ Stack } sx={ { minHeight: '300px', width: '220px' } }>
117
- <PopoverHeader
118
- title={ dynamicTag.label }
119
- onClose={ popupState.close }
120
- icon={ <DatabaseIcon fontSize={ SIZE } /> }
121
- />
122
- <DynamicSettings controls={ dynamicTag.atomic_controls } />
123
- </Paper>
116
+ <PopoverHeader
117
+ title={ dynamicTag.label }
118
+ onClose={ popupState.close }
119
+ icon={ <DatabaseIcon fontSize={ SIZE } /> }
120
+ />
121
+ <DynamicSettings controls={ dynamicTag.atomic_controls } />
124
122
  </Popover>
125
123
  </>
126
124
  );
@@ -136,7 +134,7 @@ const DynamicSettings = ( { controls }: { controls: DynamicTag[ 'atomic_controls
136
134
  }
137
135
 
138
136
  return (
139
- <>
137
+ <PopoverScrollableContent>
140
138
  <Tabs size="small" variant="fullWidth" { ...getTabsProps() }>
141
139
  { tabs.map( ( { value }, index ) => (
142
140
  <Tab key={ index } label={ value.label } sx={ { px: 1, py: 0.5 } } { ...getTabProps( index ) } />
@@ -147,18 +145,18 @@ const DynamicSettings = ( { controls }: { controls: DynamicTag[ 'atomic_controls
147
145
  { tabs.map( ( { value }, index ) => {
148
146
  return (
149
147
  <TabPanel key={ index } sx={ { flexGrow: 1, py: 0 } } { ...getTabPanelProps( index ) }>
150
- <PopoverContent p={ 2 } gap={ 2 }>
148
+ <Stack p={ 2 } gap={ 2 }>
151
149
  { value.items.map( ( item ) => {
152
150
  if ( item.type === 'control' ) {
153
151
  return <Control key={ item.value.bind } control={ item.value } />;
154
152
  }
155
153
  return null;
156
154
  } ) }
157
- </PopoverContent>
155
+ </Stack>
158
156
  </TabPanel>
159
157
  );
160
158
  } ) }
161
- </>
159
+ </PopoverScrollableContent>
162
160
  );
163
161
  };
164
162