@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
@@ -5,50 +5,57 @@ import { setDocumentModifiedStatus } from '@elementor/editor-documents';
5
5
  import {
6
6
  type ElementID,
7
7
  getElementLabel,
8
- getElementSetting,
8
+ getElementSettings,
9
9
  updateElementSettings,
10
10
  useElementSettings,
11
11
  } from '@elementor/editor-elements';
12
- import { type PropKey, type PropType, type PropValue, shouldApplyEffect } from '@elementor/editor-props';
13
- import { isExperimentActive, undoable } from '@elementor/editor-v1-adapters';
12
+ import { isDependencyMet, type PropKey, type Props, type PropType, type PropValue } from '@elementor/editor-props';
13
+ import { undoable } from '@elementor/editor-v1-adapters';
14
14
  import { __ } from '@wordpress/i18n';
15
15
 
16
16
  import { useElement } from '../contexts/element-context';
17
- import { EXPERIMENTAL_FEATURES } from '../sync/experiments-flags';
17
+ import { extractOrderedDependencies, updateValues, type Values } from '../utils/prop-dependency-utils';
18
18
  import { createTopLevelOjectType } from './create-top-level-object-type';
19
19
 
20
- type Props = {
20
+ type SettingsFieldProps = {
21
21
  bind: PropKey;
22
22
  propDisplayName: string;
23
23
  children: React.ReactNode;
24
24
  };
25
25
 
26
- export const SettingsField = ( { bind, children, propDisplayName }: Props ) => {
27
- const { element, elementType } = useElement();
26
+ const HISTORY_DEBOUNCE_WAIT = 800;
28
27
 
29
- const elementSettingValues = useElementSettings< PropValue >( element.id, Object.keys( elementType.propsSchema ) );
28
+ export const SettingsField = ( { bind, children, propDisplayName }: SettingsFieldProps ) => {
29
+ const {
30
+ element: { id: elementId },
31
+ elementType: { propsSchema, dependenciesPerTargetMapping = {} },
32
+ } = useElement();
30
33
 
31
- const value = { [ bind ]: elementSettingValues?.[ bind ] };
34
+ const elementSettingValues = useElementSettings< PropValue >( elementId, Object.keys( propsSchema ) ) as Values;
32
35
 
33
- const propType = createTopLevelOjectType( { schema: elementType.propsSchema } );
36
+ const value = { [ bind ]: elementSettingValues?.[ bind ] ?? null };
37
+
38
+ const propType = createTopLevelOjectType( { schema: propsSchema } );
34
39
 
35
40
  const undoableUpdateElementProp = useUndoableUpdateElementProp( {
36
- propKey: bind,
37
- elementId: element.id,
41
+ elementId,
38
42
  propDisplayName,
39
43
  } );
40
44
 
41
- const setValue = ( newValue: Record< string, PropValue > ) => {
42
- const isVersion331Active = isExperimentActive( EXPERIMENTAL_FEATURES.V_3_31 );
45
+ const setValue = ( newValue: Values ) => {
46
+ const dependents = extractOrderedDependencies(
47
+ bind,
48
+ propsSchema,
49
+ elementSettingValues,
50
+ dependenciesPerTargetMapping
51
+ );
52
+
53
+ const settings = updateValues( newValue, dependents, propsSchema, elementSettingValues );
43
54
 
44
- if ( isVersion331Active ) {
45
- undoableUpdateElementProp( { newValue } );
46
- } else {
47
- updateElementSettings( { id: element.id, props: newValue } );
48
- }
55
+ undoableUpdateElementProp( settings );
49
56
  };
50
57
 
51
- const isDisabled = ( prop: PropType ) => getDisableState( prop, elementSettingValues );
58
+ const isDisabled = ( prop: PropType ) => ! isDependencyMet( prop?.dependencies, elementSettingValues );
52
59
 
53
60
  return (
54
61
  <PropProvider propType={ propType } value={ value } setValue={ setValue } isDisabled={ isDisabled }>
@@ -57,43 +64,23 @@ export const SettingsField = ( { bind, children, propDisplayName }: Props ) => {
57
64
  );
58
65
  };
59
66
 
60
- function getDisableState( propType: PropType, elementValues: PropValue ): boolean | undefined {
61
- const disablingDependencies = propType.dependencies?.filter( ( { effect } ) => effect === 'disable' ) || [];
62
-
63
- if ( ! disablingDependencies.length ) {
64
- return false;
65
- }
66
-
67
- if ( disablingDependencies.length > 1 ) {
68
- throw new Error( 'Multiple disabling dependencies are not supported.' );
69
- }
70
-
71
- return shouldApplyEffect( disablingDependencies[ 0 ], elementValues );
72
- }
73
-
74
- type UndoableUpdateElementSettingsArgs = {
75
- newValue: Record< string, PropValue >;
76
- };
77
-
78
67
  function useUndoableUpdateElementProp( {
79
- propKey,
80
68
  elementId,
81
69
  propDisplayName,
82
70
  }: {
83
- propKey: PropKey;
84
71
  elementId: ElementID;
85
72
  propDisplayName: string;
86
73
  } ) {
87
74
  return useMemo( () => {
88
75
  return undoable(
89
76
  {
90
- do: ( { newValue }: UndoableUpdateElementSettingsArgs ) => {
91
- const prevPropValue = getElementSetting( elementId, propKey ) as PropValue;
77
+ do: ( newSettings: Props ) => {
78
+ const prevPropValue = getElementSettings( elementId, Object.keys( newSettings ) ) as Props;
92
79
 
93
- updateElementSettings( { id: elementId, props: { ...newValue }, withHistory: false } );
80
+ updateElementSettings( { id: elementId, props: newSettings as Props, withHistory: false } );
94
81
  setDocumentModifiedStatus( true );
95
82
 
96
- return { [ propKey ]: prevPropValue };
83
+ return prevPropValue;
97
84
  },
98
85
 
99
86
  undo: ( {}, prevProps ) => {
@@ -104,7 +91,8 @@ function useUndoableUpdateElementProp( {
104
91
  title: getElementLabel( elementId ),
105
92
  // translators: %s is the name of the property that was edited.
106
93
  subtitle: __( '%s edited', 'elementor' ).replace( '%s', propDisplayName ),
94
+ debounce: { wait: HISTORY_DEBOUNCE_WAIT },
107
95
  }
108
96
  );
109
- }, [ propKey, elementId, propDisplayName ] );
97
+ }, [ elementId, propDisplayName ] );
110
98
  }
@@ -2,11 +2,11 @@ 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';
6
5
 
7
6
  import { useStylesInheritanceChain } from '../contexts/styles-inheritance-context';
8
7
  import { useStylesField } from '../hooks/use-styles-field';
9
8
  import { StylesInheritanceIndicator } from '../styles-inheritance/components/styles-inheritance-indicator';
9
+ import { ConditionalField } from './conditional-field';
10
10
  import { createTopLevelOjectType } from './create-top-level-object-type';
11
11
 
12
12
  export type StylesFieldProps = {
@@ -16,25 +16,23 @@ export type StylesFieldProps = {
16
16
  propDisplayName: string;
17
17
  };
18
18
 
19
- export const StylesField = ( { bind, placeholder, propDisplayName, children }: StylesFieldProps ) => {
20
- const { value, setValue, canEdit } = useStylesField( bind, {
21
- history: { propDisplayName },
22
- } );
19
+ export const StylesField = ( { bind, propDisplayName, children }: StylesFieldProps ) => {
20
+ const stylesSchema = getStylesSchema();
23
21
 
24
- const isVersion331Active = isExperimentActive( 'e_v_3_31' );
25
22
  const stylesInheritanceChain = useStylesInheritanceChain( [ bind ] );
26
23
 
27
- const stylesSchema = getStylesSchema();
24
+ const { value, canEdit, ...fields } = useStylesField( bind, { history: { propDisplayName } } );
28
25
 
29
26
  const propType = createTopLevelOjectType( { schema: stylesSchema } );
30
27
 
31
- const values = { [ bind ]: value };
32
28
  const [ actualValue ] = stylesInheritanceChain;
29
+
33
30
  const placeholderValues = {
34
- [ bind ]: isVersion331Active ? actualValue?.value : placeholder,
31
+ [ bind ]: actualValue?.value,
35
32
  };
36
- const setValues = ( newValue: Record< string, PropValue > ) => {
37
- setValue( newValue[ bind ] );
33
+
34
+ const setValue = ( newValue: Record< string, PropValue > ) => {
35
+ fields.setValue( newValue[ bind ] );
38
36
  };
39
37
 
40
38
  return (
@@ -48,12 +46,14 @@ export const StylesField = ( { bind, placeholder, propDisplayName, children }: S
48
46
  >
49
47
  <PropProvider
50
48
  propType={ propType }
51
- value={ values }
52
- setValue={ setValues }
49
+ value={ { [ bind ]: value } }
50
+ setValue={ setValue }
53
51
  placeholder={ placeholderValues }
54
52
  isDisabled={ () => ! canEdit }
55
53
  >
56
- <PropKeyProvider bind={ bind }>{ children }</PropKeyProvider>
54
+ <PropKeyProvider bind={ bind }>
55
+ <ConditionalField>{ children }</ConditionalField>
56
+ </PropKeyProvider>
57
57
  </PropProvider>
58
58
  </ControlAdornmentsProvider>
59
59
  );
@@ -23,7 +23,7 @@ import { __ } from '@wordpress/i18n';
23
23
 
24
24
  import { PopoverBody } from '../../components/popover-body';
25
25
  import { Control as BaseControl } from '../../controls-registry/control';
26
- import { type ControlType, getControl } from '../../controls-registry/controls-registry';
26
+ import { controlsRegistry, type ControlType } from '../../controls-registry/controls-registry';
27
27
  import { usePersistDynamicValue } from '../../hooks/use-persist-dynamic-value';
28
28
  import { DynamicControl } from '../dynamic-control';
29
29
  import { useDynamicTag } from '../hooks/use-dynamic-tag';
@@ -169,7 +169,7 @@ const DynamicSettings = ( { controls }: { controls: DynamicTag[ 'atomic_controls
169
169
  };
170
170
 
171
171
  const Control = ( { control }: { control: Control[ 'value' ] } ) => {
172
- if ( ! getControl( control.type as ControlType ) ) {
172
+ if ( ! controlsRegistry.get( control.type as ControlType ) ) {
173
173
  return null;
174
174
  }
175
175
 
package/src/errors.ts CHANGED
@@ -6,6 +6,16 @@ export const ControlTypeNotFoundError = createError< { controlType: string } >(
6
6
  message: 'Control type not found.',
7
7
  } );
8
8
 
9
+ export const ControlTypeAlreadyRegisteredError = createError< { controlType: string } >( {
10
+ code: 'control_type_already_registered',
11
+ message: 'Control type is already registered.',
12
+ } );
13
+
14
+ export const ControlTypeNotRegisteredError = createError< { controlType: string } >( {
15
+ code: 'control_type_not_registered',
16
+ message: 'Control type is not registered.',
17
+ } );
18
+
9
19
  export const StylesProviderNotFoundError = createError< { styleId: StyleDefinitionID } >( {
10
20
  code: 'provider_not_found',
11
21
  message: 'Styles provider not found.',
@@ -0,0 +1,184 @@
1
+ import { useMemo } from 'react';
2
+ import {
3
+ createElementStyle,
4
+ deleteElementStyle,
5
+ type ElementID,
6
+ shouldCreateNewLocalStyle,
7
+ } from '@elementor/editor-elements';
8
+ import { stringPropTypeUtil } from '@elementor/editor-props';
9
+ import {
10
+ type CustomCss,
11
+ getVariantByMeta,
12
+ type StyleDefinition,
13
+ type StyleDefinitionVariant,
14
+ } from '@elementor/editor-styles';
15
+ import { ELEMENTS_STYLES_RESERVED_LABEL, type StylesProvider } from '@elementor/editor-styles-repository';
16
+ import { undoable } from '@elementor/editor-v1-adapters';
17
+ import { decodeString, encodeString } from '@elementor/utils';
18
+
19
+ import { useClassesProp } from '../contexts/classes-prop-context';
20
+ import { useElement } from '../contexts/element-context';
21
+ import { useStyle } from '../contexts/style-context';
22
+ import { StylesProviderCannotUpdatePropsError } from '../errors';
23
+ import { getSubtitle, getTitle, HISTORY_DEBOUNCE_WAIT } from './use-styles-fields';
24
+ import { useStylesRerender } from './use-styles-rerender';
25
+
26
+ type UpdateStyleArgs = {
27
+ styleId: StyleDefinition[ 'id' ];
28
+ provider: StylesProvider;
29
+ customCss: CustomCss;
30
+ propDisplayName: string;
31
+ };
32
+
33
+ type CreateStyleArgs = {
34
+ styleId: null;
35
+ provider: null;
36
+ customCss: CustomCss | null;
37
+ propDisplayName: string;
38
+ };
39
+
40
+ type UpdateStyleReturn = {
41
+ styleId: StyleDefinition[ 'id' ];
42
+ provider: StylesProvider;
43
+ prevCustomCss: CustomCss | null;
44
+ };
45
+
46
+ type CreateStyleReturn = {
47
+ createdStyleId: StyleDefinition[ 'id' ];
48
+ };
49
+
50
+ type UndoableUpdateStylePayload = UpdateStyleArgs | CreateStyleArgs;
51
+ type UndoableUpdateStyleReturn = UpdateStyleReturn | CreateStyleReturn;
52
+
53
+ export const useCustomCss = () => {
54
+ const {
55
+ element: { id: elementId },
56
+ } = useElement();
57
+ const { id: styleId, meta, provider } = useStyle();
58
+ const style = provider?.actions.get( styleId, { elementId } );
59
+ const undoableUpdateStyle = useUndoableActions( { elementId, meta } );
60
+
61
+ const currentStyleId = styleId ? styleId : null;
62
+ const currentProvider = styleId ? provider : null;
63
+
64
+ useStylesRerender();
65
+
66
+ const variant = style ? getVariantByMeta( style, meta ) : null;
67
+
68
+ const setCustomCss = (
69
+ raw: string,
70
+ { history: { propDisplayName } }: { history: { propDisplayName: string } }
71
+ ) => {
72
+ const newValue = { raw: encodeString( sanitize( raw ) ) };
73
+
74
+ undoableUpdateStyle( {
75
+ styleId: currentStyleId,
76
+ provider: currentProvider,
77
+ customCss: newValue,
78
+ propDisplayName,
79
+ } as UndoableUpdateStylePayload );
80
+ };
81
+
82
+ const customCss = variant?.custom_css?.raw ? { raw: decodeString( variant.custom_css.raw ) } : null;
83
+
84
+ return {
85
+ customCss,
86
+ setCustomCss,
87
+ };
88
+ };
89
+
90
+ function useUndoableActions( {
91
+ elementId,
92
+ meta: { breakpoint, state },
93
+ }: {
94
+ elementId: ElementID;
95
+ meta: StyleDefinitionVariant[ 'meta' ];
96
+ } ) {
97
+ const classesProp = useClassesProp();
98
+
99
+ return useMemo( () => {
100
+ const meta = { breakpoint, state };
101
+
102
+ const createStyleArgs = { elementId, classesProp, meta, label: ELEMENTS_STYLES_RESERVED_LABEL };
103
+
104
+ return undoable(
105
+ {
106
+ do: ( payload: UndoableUpdateStylePayload ): UndoableUpdateStyleReturn => {
107
+ if ( shouldCreateNewLocalStyle< StylesProvider >( payload ) ) {
108
+ return create( payload as CreateStyleArgs );
109
+ }
110
+ return update( payload as UpdateStyleArgs );
111
+ },
112
+ undo: ( payload: UndoableUpdateStylePayload, doReturn: UndoableUpdateStyleReturn ) => {
113
+ if ( shouldCreateNewLocalStyle< StylesProvider >( payload ) ) {
114
+ return undoCreate( payload as CreateStyleArgs, doReturn as CreateStyleReturn );
115
+ }
116
+ return undoUpdate( payload as UpdateStyleArgs, doReturn as UpdateStyleReturn );
117
+ },
118
+ redo: ( payload: UndoableUpdateStylePayload, doReturn: UndoableUpdateStyleReturn ) => {
119
+ if ( shouldCreateNewLocalStyle< StylesProvider >( payload ) ) {
120
+ return create( payload as CreateStyleArgs, doReturn as CreateStyleReturn );
121
+ }
122
+ return update( payload as UpdateStyleArgs );
123
+ },
124
+ },
125
+ {
126
+ title: ( { provider, styleId } ) => getTitle( { provider, styleId, elementId } ),
127
+ subtitle: ( { provider, styleId, propDisplayName } ) =>
128
+ getSubtitle( { provider, styleId, elementId, propDisplayName } ),
129
+ debounce: { wait: HISTORY_DEBOUNCE_WAIT },
130
+ }
131
+ );
132
+
133
+ function create( { customCss }: CreateStyleArgs, redoArgs?: CreateStyleReturn ): CreateStyleReturn {
134
+ const createdStyle = createElementStyle( {
135
+ ...createStyleArgs,
136
+ props: {},
137
+ custom_css: customCss ?? null,
138
+ styleId: redoArgs?.createdStyleId,
139
+ } );
140
+
141
+ return { createdStyleId: createdStyle };
142
+ }
143
+
144
+ function undoCreate( _: UndoableUpdateStylePayload, { createdStyleId }: CreateStyleReturn ) {
145
+ deleteElementStyle( elementId, createdStyleId );
146
+ }
147
+
148
+ function update( { provider, styleId, customCss }: UpdateStyleArgs ): UpdateStyleReturn {
149
+ if ( ! provider.actions.updateCustomCss ) {
150
+ throw new StylesProviderCannotUpdatePropsError( {
151
+ context: { providerKey: provider.getKey() },
152
+ } );
153
+ }
154
+
155
+ const style = provider.actions.get( styleId, { elementId } );
156
+ const prevCustomCss = getCurrentCustomCss( style, meta );
157
+
158
+ provider.actions.updateCustomCss( { id: styleId, meta, custom_css: customCss }, { elementId } );
159
+
160
+ return { styleId, provider, prevCustomCss };
161
+ }
162
+
163
+ function undoUpdate( _: UndoableUpdateStylePayload, { styleId, provider, prevCustomCss }: UpdateStyleReturn ) {
164
+ provider.actions.updateCustomCss?.(
165
+ { id: styleId, meta, custom_css: prevCustomCss ?? { raw: '' } },
166
+ { elementId }
167
+ );
168
+ }
169
+ }, [ elementId, breakpoint, state, classesProp ] );
170
+ }
171
+
172
+ function getCurrentCustomCss( style: StyleDefinition | null, meta: StyleDefinitionVariant[ 'meta' ] ) {
173
+ if ( ! style ) {
174
+ return null;
175
+ }
176
+
177
+ const variant = getVariantByMeta( style, meta );
178
+
179
+ return variant?.custom_css ?? null;
180
+ }
181
+
182
+ function sanitize( raw: string ) {
183
+ return stringPropTypeUtil.schema.safeParse( stringPropTypeUtil.create( raw ) ).data?.value?.trim() ?? '';
184
+ }
@@ -1,15 +1,12 @@
1
1
  import { useState } from 'react';
2
- import { isExperimentActive } from '@elementor/editor-v1-adapters';
3
2
  import { getSessionStorageItem, setSessionStorageItem } from '@elementor/session';
4
3
 
5
4
  import { useElement } from '../contexts/element-context';
6
- import { EXPERIMENTAL_FEATURES } from '../sync/experiments-flags';
7
5
 
8
6
  export const useStateByElement = < T >( key: string, initialValue: T ) => {
9
7
  const { element } = useElement();
10
- const isFeatureActive = isExperimentActive( EXPERIMENTAL_FEATURES.V_3_30 );
11
8
  const lookup = `elementor/editor-state/${ element.id }/${ key }`;
12
- const storedValue = isFeatureActive ? getSessionStorageItem< T >( lookup ) : initialValue;
9
+ const storedValue = getSessionStorageItem< T >( lookup );
13
10
  const [ value, setValue ] = useState( storedValue ?? initialValue );
14
11
 
15
12
  const doUpdate = ( newValue: T ) => {