@elementor/editor-editing-panel 1.0.0 → 1.1.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 (117) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/dist/index.d.mts +10 -19
  3. package/dist/index.d.ts +10 -19
  4. package/dist/index.js +1283 -1751
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +1305 -1762
  7. package/dist/index.mjs.map +1 -1
  8. package/package.json +12 -12
  9. package/src/components/add-or-remove-content.tsx +3 -3
  10. package/src/components/collapse-icon.tsx +12 -0
  11. package/src/components/collapsible-content.tsx +5 -14
  12. package/src/components/collapsible-field.tsx +5 -3
  13. package/src/components/css-class-selector-section.tsx +76 -0
  14. package/src/components/editing-panel-hooks.tsx +2 -0
  15. package/src/components/editing-panel-tabs.tsx +23 -13
  16. package/src/components/editing-panel.tsx +9 -6
  17. package/src/components/multi-combobox/index.ts +3 -0
  18. package/src/components/multi-combobox/multi-combobox.tsx +120 -0
  19. package/src/components/multi-combobox/types.ts +26 -0
  20. package/src/components/multi-combobox/use-combobox-actions.ts +62 -0
  21. package/src/components/section.tsx +37 -0
  22. package/src/components/sections-list.tsx +6 -0
  23. package/src/components/settings-tab.tsx +11 -11
  24. package/src/components/style-sections/background-section/background-color-field.tsx +4 -4
  25. package/src/components/style-sections/background-section/background-section.tsx +9 -7
  26. package/src/components/style-sections/border-section/border-color-field.tsx +4 -4
  27. package/src/components/style-sections/border-section/border-field.tsx +4 -3
  28. package/src/components/style-sections/border-section/border-radius-field.tsx +4 -3
  29. package/src/components/style-sections/border-section/border-section.tsx +7 -10
  30. package/src/components/style-sections/border-section/border-style-field.tsx +4 -4
  31. package/src/components/style-sections/border-section/border-width-field.tsx +4 -3
  32. package/src/components/style-sections/effects-section/effects-section.tsx +7 -10
  33. package/src/components/style-sections/layout-section/display-field.tsx +32 -0
  34. package/src/components/style-sections/layout-section/justify-content-field.tsx +82 -0
  35. package/src/components/style-sections/layout-section/layout-section.tsx +17 -0
  36. package/src/components/style-sections/layout-section/utils/rotate-flex-icon.ts +12 -0
  37. package/src/components/style-sections/position-section/dimensions-field.tsx +6 -6
  38. package/src/components/style-sections/position-section/position-field.tsx +4 -4
  39. package/src/components/style-sections/position-section/position-section.tsx +45 -15
  40. package/src/components/style-sections/position-section/z-index-field.tsx +4 -4
  41. package/src/components/style-sections/size-section/overflow-field.tsx +8 -8
  42. package/src/components/style-sections/size-section/size-section.tsx +33 -26
  43. package/src/components/style-sections/spacing-section/spacing-section.tsx +11 -13
  44. package/src/components/style-sections/typography-section/font-family-field.tsx +40 -0
  45. package/src/components/style-sections/typography-section/font-size-field.tsx +4 -4
  46. package/src/components/style-sections/typography-section/font-weight-field.tsx +4 -4
  47. package/src/components/style-sections/typography-section/letter-spacing-field.tsx +4 -4
  48. package/src/components/style-sections/typography-section/text-alignment-field.tsx +9 -9
  49. package/src/components/style-sections/typography-section/text-color-field.tsx +4 -4
  50. package/src/components/style-sections/typography-section/text-direction-field.tsx +7 -7
  51. package/src/components/style-sections/typography-section/text-stroke-field.tsx +3 -3
  52. package/src/components/style-sections/typography-section/text-style-field.tsx +5 -4
  53. package/src/components/style-sections/typography-section/transform-field.tsx +23 -9
  54. package/src/components/style-sections/typography-section/typography-section.tsx +26 -27
  55. package/src/components/style-sections/typography-section/word-spacing-field.tsx +4 -4
  56. package/src/components/style-tab.tsx +67 -31
  57. package/src/contexts/classes-prop-context.tsx +1 -1
  58. package/src/contexts/element-context.tsx +2 -2
  59. package/src/contexts/style-context.tsx +6 -5
  60. package/src/control-replacement.tsx +1 -1
  61. package/src/controls-actions.ts +3 -2
  62. package/src/controls-registry/control-type-container.tsx +3 -2
  63. package/src/controls-registry/control.tsx +2 -1
  64. package/src/controls-registry/controls-registry.tsx +8 -1
  65. package/src/controls-registry/settings-field.tsx +5 -4
  66. package/src/controls-registry/styles-field.tsx +3 -2
  67. package/src/dynamics/components/dynamic-selection-control.tsx +15 -14
  68. package/src/dynamics/components/dynamic-selection.tsx +9 -8
  69. package/src/dynamics/dynamic-control.tsx +4 -4
  70. package/src/dynamics/hooks/use-dynamic-tag.ts +3 -2
  71. package/src/dynamics/hooks/use-prop-dynamic-action.tsx +6 -5
  72. package/src/dynamics/hooks/use-prop-dynamic-tags.ts +3 -2
  73. package/src/dynamics/init.ts +5 -3
  74. package/src/dynamics/sync/get-elementor-config.ts +1 -1
  75. package/src/dynamics/types.ts +2 -2
  76. package/src/dynamics/utils.ts +3 -2
  77. package/src/hooks/use-close-editor-panel.ts +23 -0
  78. package/src/hooks/use-direction.ts +13 -0
  79. package/src/hooks/use-open-editor-panel.ts +4 -3
  80. package/src/hooks/use-prop-value-history.ts +45 -0
  81. package/src/hooks/use-style-prop-history.ts +75 -0
  82. package/src/hooks/use-styles-field.ts +25 -4
  83. package/src/index.ts +1 -1
  84. package/src/init.ts +5 -4
  85. package/src/panel.ts +1 -0
  86. package/src/popover-action.tsx +1 -1
  87. package/src/sync/enqueue-font.ts +7 -0
  88. package/src/sync/get-elementor-config.ts +7 -0
  89. package/src/sync/{should-use-v2-panel.ts → is-atomic-widget-selected.ts} +1 -1
  90. package/src/sync/types.ts +20 -21
  91. package/src/components/accordion-section.tsx +0 -26
  92. package/src/components/control-label.tsx +0 -10
  93. package/src/controls/bound-prop-context.tsx +0 -30
  94. package/src/controls/components/control-toggle-button-group.tsx +0 -68
  95. package/src/controls/components/repeater.tsx +0 -197
  96. package/src/controls/components/text-field-inner-selection.tsx +0 -75
  97. package/src/controls/control-actions/control-actions-context.tsx +0 -27
  98. package/src/controls/control-actions/control-actions-menu.ts +0 -7
  99. package/src/controls/control-actions/control-actions.tsx +0 -31
  100. package/src/controls/controls/box-shadow-repeater-control.tsx +0 -210
  101. package/src/controls/controls/color-control.tsx +0 -25
  102. package/src/controls/controls/equal-unequal-sizes-control.tsx +0 -196
  103. package/src/controls/controls/image-control.tsx +0 -58
  104. package/src/controls/controls/image-media-control.tsx +0 -64
  105. package/src/controls/controls/linked-dimensions-control.tsx +0 -139
  106. package/src/controls/controls/number-control.tsx +0 -29
  107. package/src/controls/controls/select-control.tsx +0 -30
  108. package/src/controls/controls/size-control.tsx +0 -71
  109. package/src/controls/controls/stroke-control.tsx +0 -105
  110. package/src/controls/controls/text-area-control.tsx +0 -31
  111. package/src/controls/controls/text-control.tsx +0 -17
  112. package/src/controls/controls/toggle-control.tsx +0 -26
  113. package/src/controls/create-control-replacement.tsx +0 -53
  114. package/src/controls/create-control.tsx +0 -40
  115. package/src/controls/hooks/use-sync-external-state.tsx +0 -51
  116. package/src/controls/index.ts +0 -24
  117. package/src/dynamics/hooks/use-prop-value-history.ts +0 -26
@@ -1,7 +1,8 @@
1
1
  import * as React from 'react';
2
- import { PropKey } from '@elementor/editor-props';
2
+ import { BoundPropProvider } from '@elementor/editor-controls';
3
+ import { type PropKey } from '@elementor/editor-props';
4
+
3
5
  import { useStylesField } from '../hooks/use-styles-field';
4
- import { BoundPropProvider } from '../controls';
5
6
 
6
7
  export type StylesFieldProps = {
7
8
  bind: PropKey;
@@ -1,40 +1,40 @@
1
1
  import * as React from 'react';
2
2
  import { useId } from 'react';
3
- import { __ } from '@wordpress/i18n';
3
+ import { ControlLabel, useBoundProp } from '@elementor/editor-controls';
4
4
  import type { Control, ControlsSection } from '@elementor/editor-elements';
5
5
  import { DatabaseIcon, SettingsIcon, XIcon } from '@elementor/icons';
6
6
  import {
7
7
  bindPopover,
8
8
  bindTrigger,
9
9
  Box,
10
+ Divider,
10
11
  IconButton,
11
12
  Paper,
12
13
  Popover,
13
14
  Stack,
15
+ Tab,
16
+ TabPanel,
17
+ Tabs,
14
18
  Typography,
15
19
  UnstableTag as Tag,
16
20
  usePopupState,
17
- Tabs,
18
- Divider,
19
21
  useTabs,
20
- Tab,
21
- TabPanel,
22
22
  } from '@elementor/ui';
23
- import { useBoundProp } from '../../controls';
24
- import { DynamicPropValue, DynamicTag } from '../types';
25
- import { DynamicControl } from '../dynamic-control';
26
- import { DynamicSelection } from './dynamic-selection';
27
- import { ControlLabel } from '../../components/control-label';
23
+ import { __ } from '@wordpress/i18n';
24
+
28
25
  import { Control as BaseControl } from '../../controls-registry/control';
26
+ import { type ControlType, getControlByType } from '../../controls-registry/controls-registry';
27
+ import { usePropValueHistory } from '../../hooks/use-prop-value-history';
28
+ import { DynamicControl } from '../dynamic-control';
29
29
  import { useDynamicTag } from '../hooks/use-dynamic-tag';
30
- import { usePropValueHistory } from '../hooks/use-prop-value-history';
31
- import { ControlType, getControlByType } from '../../controls-registry/controls-registry';
30
+ import { type DynamicPropValue, type DynamicTag } from '../types';
31
+ import { DynamicSelection } from './dynamic-selection';
32
32
 
33
33
  const SIZE = 'tiny';
34
34
 
35
35
  export const DynamicSelectionControl = () => {
36
36
  const { bind, value, setValue } = useBoundProp< DynamicPropValue | null >();
37
- const [ propValueFromHistory ] = usePropValueHistory( bind );
37
+ const { getPropValue: getPropValueFromHistory } = usePropValueHistory();
38
38
  const { name: tagName = '' } = value?.value || {};
39
39
 
40
40
  const selectionPopoverId = useId();
@@ -43,7 +43,8 @@ export const DynamicSelectionControl = () => {
43
43
  const dynamicTag = useDynamicTag( bind, tagName );
44
44
 
45
45
  const removeDynamicTag = () => {
46
- setValue( propValueFromHistory ?? null );
46
+ const propValue = getPropValueFromHistory< DynamicPropValue >( bind );
47
+ setValue( propValue ?? null );
47
48
  };
48
49
 
49
50
  if ( ! dynamicTag ) {
@@ -1,7 +1,8 @@
1
1
  import * as React from 'react';
2
- import { useState, Fragment } from 'react';
3
- import { PropKey, PropValue } from '@elementor/editor-props';
4
- import { SearchIcon, PhotoIcon } from '@elementor/icons';
2
+ import { Fragment, useState } from 'react';
3
+ import { useBoundProp } from '@elementor/editor-controls';
4
+ import { type PropKey, type PropValue } from '@elementor/editor-props';
5
+ import { PhotoIcon, SearchIcon } from '@elementor/icons';
5
6
  import {
6
7
  Box,
7
8
  Divider,
@@ -15,12 +16,12 @@ import {
15
16
  Typography,
16
17
  } from '@elementor/ui';
17
18
  import { __ } from '@wordpress/i18n';
18
- import { useBoundProp } from '../../controls';
19
+
20
+ import { usePropValueHistory } from '../../hooks/use-prop-value-history';
19
21
  import { usePropDynamicTags } from '../hooks/use-prop-dynamic-tags';
20
22
  import { getAtomicDynamicTags } from '../sync/get-atomic-dynamic-tags';
23
+ import { type DynamicPropValue } from '../types';
21
24
  import { isDynamicPropValue } from '../utils';
22
- import { usePropValueHistory } from '../hooks/use-prop-value-history';
23
- import { DynamicPropValue } from '../types';
24
25
 
25
26
  type Option = {
26
27
  label: string;
@@ -39,7 +40,7 @@ export const DynamicSelection = ( { onSelect }: DynamicSelectionProps ) => {
39
40
  const [ searchValue, setSearchValue ] = useState( '' );
40
41
  const { groups: dynamicGroups } = getAtomicDynamicTags() || {};
41
42
  const { bind, value: currentValue, setValue } = useBoundProp< DynamicPropValue | PropValue >();
42
- const [ , updatePropValueHistory ] = usePropValueHistory( bind );
43
+ const { setPropValue: updatePropValueHistory } = usePropValueHistory();
43
44
 
44
45
  const isCurrentValueDynamic = isDynamicPropValue( currentValue );
45
46
 
@@ -51,7 +52,7 @@ export const DynamicSelection = ( { onSelect }: DynamicSelectionProps ) => {
51
52
 
52
53
  const handleSetDynamicTag = ( value: string ) => {
53
54
  if ( ! isCurrentValueDynamic ) {
54
- updatePropValueHistory( currentValue );
55
+ updatePropValueHistory( bind, currentValue );
55
56
  }
56
57
 
57
58
  setValue( { $$type: 'dynamic', value: { name: value, settings: {} } } );
@@ -1,9 +1,9 @@
1
1
  import * as React from 'react';
2
- import { useBoundProp } from '../controls';
3
- import { PropKey, PropValue } from '@elementor/editor-props';
2
+ import { BoundPropProvider, useBoundProp } from '@elementor/editor-controls';
3
+ import { type PropKey, type PropValue } from '@elementor/editor-props';
4
+
4
5
  import { useDynamicTag } from './hooks/use-dynamic-tag';
5
- import { DynamicPropValue } from './types';
6
- import { BoundPropProvider } from '../controls/bound-prop-context';
6
+ import { type DynamicPropValue } from './types';
7
7
 
8
8
  export type DynamicControlProps = React.PropsWithChildren< {
9
9
  bind: PropKey;
@@ -1,7 +1,8 @@
1
1
  import { useMemo } from 'react';
2
- import { PropKey } from '@elementor/editor-props';
2
+ import { type PropKey } from '@elementor/editor-props';
3
+
4
+ import { type DynamicTag } from '../types';
3
5
  import { usePropDynamicTags } from './use-prop-dynamic-tags';
4
- import { DynamicTag } from '../types';
5
6
 
6
7
  export const useDynamicTag = ( propName: PropKey, tagName: string ): DynamicTag | null => {
7
8
  const dynamicTags = usePropDynamicTags( propName );
@@ -1,11 +1,12 @@
1
1
  import * as React from 'react';
2
- import { __ } from '@wordpress/i18n';
2
+ import { useBoundProp } from '@elementor/editor-controls';
3
3
  import { DatabaseIcon } from '@elementor/icons';
4
- import { supportsDynamic } from '../utils';
5
- import { DynamicSelection } from '../components/dynamic-selection';
6
- import { useBoundProp } from '../../controls';
7
- import { PopoverActionProps } from '../../popover-action';
4
+ import { __ } from '@wordpress/i18n';
5
+
8
6
  import { useElement } from '../../contexts/element-context';
7
+ import { type PopoverActionProps } from '../../popover-action';
8
+ import { DynamicSelection } from '../components/dynamic-selection';
9
+ import { supportsDynamic } from '../utils';
9
10
 
10
11
  export const usePropDynamicAction = (): PopoverActionProps => {
11
12
  const { bind } = useBoundProp();
@@ -1,7 +1,8 @@
1
1
  import { useMemo } from 'react';
2
- import { PropKey } from '@elementor/editor-props';
3
- import { getAtomicDynamicTags } from '../sync/get-atomic-dynamic-tags';
2
+ import { type PropKey } from '@elementor/editor-props';
3
+
4
4
  import { useElement } from '../../contexts/element-context';
5
+ import { getAtomicDynamicTags } from '../sync/get-atomic-dynamic-tags';
5
6
  import { getDynamicPropType } from '../utils';
6
7
 
7
8
  export const usePropDynamicTags = ( propName: PropKey ) => {
@@ -1,8 +1,10 @@
1
+ import { replaceControl } from '../control-replacement';
2
+ import { controlActionsMenu } from '../controls-actions';
1
3
  import { DynamicSelectionControl } from './components/dynamic-selection-control';
2
- import { isDynamicPropValue } from './utils';
3
4
  import { usePropDynamicAction } from './hooks/use-prop-dynamic-action';
4
- import { replaceControl } from '../control-replacement';
5
- import { registerPopoverAction } from '../controls-actions';
5
+ import { isDynamicPropValue } from './utils';
6
+
7
+ const { registerPopoverAction } = controlActionsMenu;
6
8
 
7
9
  export const init = () => {
8
10
  replaceControl( {
@@ -1,4 +1,4 @@
1
- import { ExtendedWindow } from '../types';
1
+ import { type ExtendedWindow } from '../types';
2
2
 
3
3
  export const getElementorConfig = () => {
4
4
  const extendedWindow = window as unknown as ExtendedWindow;
@@ -1,5 +1,5 @@
1
- import { TransformablePropType, TransformablePropValue } from '@elementor/editor-props';
2
- import { ControlItem, PropsSchema } from '@elementor/editor-elements';
1
+ import { type ControlItem, type PropsSchema } from '@elementor/editor-elements';
2
+ import { type TransformablePropType, type TransformablePropValue } from '@elementor/editor-props';
3
3
 
4
4
  export type ExtendedWindow = Window & {
5
5
  elementor?: {
@@ -1,5 +1,6 @@
1
- import { isTransformable, PropType, PropValue, TransformablePropType } from '@elementor/editor-props';
2
- import { DynamicPropType, DynamicPropValue } from './types';
1
+ import { isTransformable, type PropType, type PropValue, type TransformablePropType } from '@elementor/editor-props';
2
+
3
+ import { type DynamicPropType, type DynamicPropValue } from './types';
3
4
 
4
5
  const DYNAMIC_PROP_TYPE_KEY = 'dynamic';
5
6
 
@@ -0,0 +1,23 @@
1
+ import { useEffect } from 'react';
2
+ import { getSelectedElements, isElementInContainer, type V1Element } from '@elementor/editor-elements';
3
+ import { __privateListenTo as listenTo, type CommandEvent, commandStartEvent } from '@elementor/editor-v1-adapters';
4
+
5
+ import { usePanelActions } from '../panel';
6
+ import { isAtomicWidgetSelected } from '../sync/is-atomic-widget-selected';
7
+
8
+ export const useCloseEditorPanel = () => {
9
+ const { close } = usePanelActions();
10
+
11
+ return useEffect( () => {
12
+ return listenTo( commandStartEvent( 'document/elements/delete' ), ( e ) => {
13
+ const selectedElement = getSelectedElements()[ 0 ];
14
+ const { container: deletedContainer } = ( e as CommandEvent< { container: V1Element } > )?.args;
15
+ const isSelectedElementInDeletedContainer =
16
+ deletedContainer && selectedElement && isElementInContainer( selectedElement, deletedContainer );
17
+
18
+ if ( isSelectedElementInDeletedContainer && isAtomicWidgetSelected() ) {
19
+ close();
20
+ }
21
+ } );
22
+ }, [] ); // eslint-disable-line react-hooks/exhaustive-deps
23
+ };
@@ -0,0 +1,13 @@
1
+ import { useTheme } from '@elementor/ui';
2
+
3
+ import { type ExtendedWindow } from '../sync/types';
4
+
5
+ export function useDirection() {
6
+ const theme = useTheme(),
7
+ extendedWindow: ExtendedWindow = window;
8
+
9
+ const isUiRtl = 'rtl' === theme.direction,
10
+ isSiteRtl = !! extendedWindow.elementorFrontend?.config?.is_rtl;
11
+
12
+ return { isSiteRtl, isUiRtl };
13
+ }
@@ -1,14 +1,15 @@
1
1
  import { useEffect } from 'react';
2
- import { commandStartEvent, __privateListenTo as listenTo } from '@elementor/editor-v1-adapters';
2
+ import { __privateListenTo as listenTo, commandStartEvent } from '@elementor/editor-v1-adapters';
3
+
3
4
  import { usePanelActions } from '../panel';
4
- import { shouldUseV2Panel } from '../sync/should-use-v2-panel';
5
+ import { isAtomicWidgetSelected } from '../sync/is-atomic-widget-selected';
5
6
 
6
7
  export const useOpenEditorPanel = () => {
7
8
  const { open } = usePanelActions();
8
9
 
9
10
  useEffect( () => {
10
11
  return listenTo( commandStartEvent( 'panel/editor/open' ), () => {
11
- if ( shouldUseV2Panel() ) {
12
+ if ( isAtomicWidgetSelected() ) {
12
13
  open();
13
14
  }
14
15
  } );
@@ -0,0 +1,45 @@
1
+ import { useCallback, useMemo } from 'react';
2
+ import { getSessionStorageItem, setSessionStorageItem } from '@elementor/utils';
3
+
4
+ import { useElement } from '../contexts/element-context';
5
+
6
+ export const PROPS_VALUES_HISTORY_PREFIX = 'elementor/editor-editing-panel/prop-value-history';
7
+
8
+ export const usePropValueHistory = () => {
9
+ const { element } = useElement();
10
+ const elementKey = `${ PROPS_VALUES_HISTORY_PREFIX }/${ element.id }`;
11
+
12
+ const getElementPropsHistory = useCallback( () => {
13
+ return getSessionStorageItem< Record< string, unknown > >( elementKey );
14
+ }, [ elementKey ] );
15
+
16
+ const getPropValue = useCallback(
17
+ < T >( propKey: string ) => {
18
+ const elementPropValues = getElementPropsHistory();
19
+ return ( elementPropValues?.[ propKey ] ?? null ) as T | null;
20
+ },
21
+ [ getElementPropsHistory ]
22
+ );
23
+
24
+ const setPropValue = useCallback(
25
+ ( propKey: string, propValue: unknown ) => {
26
+ const elementPropValues = getElementPropsHistory();
27
+ const updatedElementPropValues = { ...elementPropValues, [ propKey ]: propValue };
28
+ setSessionStorageItem( elementKey, updatedElementPropValues );
29
+ },
30
+ [ getElementPropsHistory, elementKey ]
31
+ );
32
+
33
+ const removeProp = useCallback(
34
+ ( propKey: string ) => {
35
+ const elementPropValues = getElementPropsHistory();
36
+ const updatedElementPropValues = Object.fromEntries(
37
+ Object.entries( elementPropValues || {} ).filter( ( [ key ] ) => key !== propKey )
38
+ );
39
+ setSessionStorageItem( elementKey, updatedElementPropValues );
40
+ },
41
+ [ getElementPropsHistory, elementKey ]
42
+ );
43
+
44
+ return useMemo( () => ( { getPropValue, setPropValue, removeProp } ), [ getPropValue, removeProp, setPropValue ] );
45
+ };
@@ -0,0 +1,75 @@
1
+ import { useCallback, useMemo } from 'react';
2
+ import { getElementStyles, getVariantByMeta, updateStyle } from '@elementor/editor-elements';
3
+ import { type PropKey } from '@elementor/editor-props';
4
+
5
+ import { useElement } from '../contexts/element-context';
6
+ import { useStyle } from '../contexts/style-context';
7
+ import { usePropValueHistory } from './use-prop-value-history';
8
+
9
+ export const useStylePropsHistory = ( props: PropKey[] ) => {
10
+ const { element } = useElement();
11
+ const { id: styleDefID, meta } = useStyle();
12
+ const { getPropValue, setPropValue, removeProp } = usePropValueHistory();
13
+
14
+ const styleDef = getElementStyles( element.id )?.[ styleDefID ];
15
+ const styleVariant = styleDef ? getVariantByMeta( styleDef, meta ) : null;
16
+ const styleVariantPath = `${ styleDefID }-${ styleVariant?.meta.breakpoint }-${ styleVariant?.meta.state }`;
17
+
18
+ const saveStylePropsHistory = useCallback( () => {
19
+ props.forEach( ( propKey ) => {
20
+ const propValue = styleVariant?.props[ propKey ];
21
+
22
+ if ( propValue ) {
23
+ const propPath = `${ styleVariantPath }-${ propKey }`;
24
+ setPropValue( propPath, propValue );
25
+ }
26
+ } );
27
+ }, [ props, setPropValue, styleVariant?.props, styleVariantPath ] );
28
+
29
+ const updateStylePropsFromHistory = useCallback( () => {
30
+ const propValuesFromHistory = props.reduce( ( allProps, currentPropKey ) => {
31
+ const propPath = `${ styleVariantPath }-${ currentPropKey }`;
32
+ const propHistory = getPropValue( propPath );
33
+
34
+ if ( propHistory ) {
35
+ removeProp( propPath );
36
+ return { ...allProps, [ currentPropKey ]: propHistory };
37
+ }
38
+
39
+ return allProps;
40
+ }, {} );
41
+
42
+ if ( Object.keys( propValuesFromHistory ).length ) {
43
+ updateStyle( {
44
+ elementID: element.id,
45
+ styleDefID,
46
+ meta,
47
+ props: propValuesFromHistory,
48
+ bind: 'classes',
49
+ } );
50
+ }
51
+ }, [ element.id, getPropValue, meta, props, removeProp, styleDefID, styleVariantPath ] );
52
+
53
+ const clearCurrentStyleProps = useCallback( () => {
54
+ const resetValues = props.reduce(
55
+ ( allProps, currentPropKey ) => ( {
56
+ ...allProps,
57
+ [ currentPropKey ]: undefined,
58
+ } ),
59
+ {}
60
+ );
61
+
62
+ updateStyle( {
63
+ elementID: element.id,
64
+ styleDefID,
65
+ meta,
66
+ props: resetValues,
67
+ bind: 'classes',
68
+ } );
69
+ }, [ element.id, meta, props, styleDefID ] );
70
+
71
+ return useMemo(
72
+ () => ( { saveStylePropsHistory, updateStylePropsFromHistory, clearCurrentStyleProps } ),
73
+ [ saveStylePropsHistory, updateStylePropsFromHistory, clearCurrentStyleProps ]
74
+ );
75
+ };
@@ -1,13 +1,23 @@
1
+ import { useEffect, useRef } from 'react';
2
+ import { updateStyle, useElementStyleProp } from '@elementor/editor-elements';
1
3
  import type { PropKey, PropValue } from '@elementor/editor-props';
2
- import { useElementStyleProp, updateStyle } from '@elementor/editor-elements';
3
- import { useStyle } from '../contexts/style-context';
4
+
4
5
  import { useClassesProp } from '../contexts/classes-prop-context';
5
6
  import { useElement } from '../contexts/element-context';
7
+ import { useStyle } from '../contexts/style-context';
6
8
 
7
- export const useStylesField = < T extends PropValue >( propName: PropKey ) => {
9
+ export const useStylesField = < T extends PropValue >(
10
+ propName: PropKey
11
+ ): [
12
+ T | null,
13
+ ( newValue: T ) => void,
14
+ ( callback: ( newValue: T | null, previousValue: T | null ) => void ) => void,
15
+ ] => {
8
16
  const { element } = useElement();
9
17
  const { id, meta } = useStyle();
10
18
  const classesProp = useClassesProp();
19
+ const previousValue = useRef< T | null >( null );
20
+ const onChangeCallbacks = useRef< Set< ( newValue: T | null, previousValue: T | null ) => void > >( new Set() );
11
21
 
12
22
  const value = useElementStyleProp< T >( {
13
23
  elementID: element.id,
@@ -26,5 +36,16 @@ export const useStylesField = < T extends PropValue >( propName: PropKey ) => {
26
36
  } );
27
37
  };
28
38
 
29
- return [ value, setValue ] as const;
39
+ const registerChangeListener = ( callback: ( newValue: T | null, previousValue: T | null ) => void ) => {
40
+ onChangeCallbacks.current.add( callback );
41
+ };
42
+
43
+ useEffect( () => {
44
+ onChangeCallbacks.current.forEach( ( cb ) => {
45
+ cb( value, previousValue.current );
46
+ } );
47
+ previousValue.current = value;
48
+ }, [ value ] );
49
+
50
+ return [ value, setValue, registerChangeListener ];
30
51
  };
package/src/index.ts CHANGED
@@ -1,6 +1,6 @@
1
+ export { useBoundProp } from '@elementor/editor-controls';
1
2
  export type { PopoverActionProps } from './popover-action';
2
3
  export { replaceControl } from './control-replacement';
3
- export { useBoundProp } from './controls';
4
4
 
5
5
  import init from './init';
6
6
 
package/src/init.ts CHANGED
@@ -1,10 +1,11 @@
1
- import { panel } from './panel';
2
1
  import { injectIntoLogic } from '@elementor/editor';
3
- import { shouldUseV2Panel } from './sync/should-use-v2-panel';
4
- import { EditingPanelHooks } from './components/editing-panel-hooks';
5
2
  import { __registerPanel as registerPanel } from '@elementor/editor-panels';
6
3
  import { __privateBlockDataCommand as blockDataCommand } from '@elementor/editor-v1-adapters';
4
+
5
+ import { EditingPanelHooks } from './components/editing-panel-hooks';
7
6
  import { init as initDynamics } from './dynamics/init';
7
+ import { panel } from './panel';
8
+ import { isAtomicWidgetSelected } from './sync/is-atomic-widget-selected';
8
9
 
9
10
  export default function init() {
10
11
  registerPanel( panel );
@@ -22,6 +23,6 @@ export default function init() {
22
23
  const blockV1Panel = () => {
23
24
  blockDataCommand( {
24
25
  command: 'panel/editor/open',
25
- condition: shouldUseV2Panel,
26
+ condition: isAtomicWidgetSelected,
26
27
  } );
27
28
  };
package/src/panel.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { __createPanel as createPanel } from '@elementor/editor-panels';
2
+
2
3
  import { EditingPanel } from './components/editing-panel';
3
4
 
4
5
  export const { panel, usePanelActions, usePanelStatus } = createPanel( {
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { ComponentType, ElementType as ReactElementType, useId } from 'react';
2
+ import { type ComponentType, type ElementType as ReactElementType, useId } from 'react';
3
3
  import { XIcon } from '@elementor/icons';
4
4
  import { bindPopover, bindToggle, IconButton, Popover, Stack, Tooltip, Typography, usePopupState } from '@elementor/ui';
5
5
 
@@ -0,0 +1,7 @@
1
+ import { type EnqueueFont, type ExtendedWindow } from './types';
2
+
3
+ export const enqueueFont: EnqueueFont = ( fontFamily, context = 'editor' ) => {
4
+ const extendedWindow = window as unknown as ExtendedWindow;
5
+
6
+ return extendedWindow.elementor?.helpers?.enqueueFont( fontFamily, context ) ?? null;
7
+ };
@@ -0,0 +1,7 @@
1
+ import { type ExtendedWindow } from './types';
2
+
3
+ export const getElementorConfig = () => {
4
+ const extendedWindow = window as unknown as ExtendedWindow;
5
+
6
+ return extendedWindow.elementor?.config ?? {};
7
+ };
@@ -1,6 +1,6 @@
1
1
  import { getSelectedElements, getWidgetsCache } from '@elementor/editor-elements';
2
2
 
3
- export const shouldUseV2Panel = () => {
3
+ export const isAtomicWidgetSelected = () => {
4
4
  const selectedElements = getSelectedElements();
5
5
  const widgetCache = getWidgetsCache();
6
6
 
package/src/sync/types.ts CHANGED
@@ -1,6 +1,8 @@
1
- import { ControlItem, PropsSchema } from '@elementor/editor-elements';
2
- import { PropValue } from '@elementor/editor-props';
3
- import { StyleDefinition, StyleDefinitionID } from '@elementor/editor-styles';
1
+ import { type ControlItem, type PropsSchema, type V1Element } from '@elementor/editor-elements';
2
+
3
+ export type SupportedFonts = 'system' | 'googlefonts' | 'customfonts';
4
+
5
+ export type EnqueueFont = ( fontFamily: string, context?: 'preview' | 'editor' ) => void;
4
6
 
5
7
  export type ExtendedWindow = Window & {
6
8
  elementor?: {
@@ -17,23 +19,20 @@ export type ExtendedWindow = Window & {
17
19
  }
18
20
  >;
19
21
  getContainer?: ( id: string ) => V1Element;
22
+ helpers?: {
23
+ enqueueFont: EnqueueFont;
24
+ };
25
+ config?: {
26
+ controls?: {
27
+ font?: {
28
+ options?: Record< string, SupportedFonts >;
29
+ };
30
+ };
31
+ };
32
+ };
33
+ elementorFrontend?: {
34
+ config?: {
35
+ is_rtl?: boolean;
36
+ };
20
37
  };
21
- };
22
-
23
- export type V1Element = {
24
- model: V1Model< V1ElementModelProps >;
25
- settings?: V1Model< V1ElementSettingsProps >;
26
- };
27
-
28
- export type V1ElementModelProps = {
29
- widgetType?: string;
30
- elType: string;
31
- id: string;
32
- styles?: Record< StyleDefinitionID, StyleDefinition >;
33
- };
34
-
35
- export type V1ElementSettingsProps = Record< string, PropValue >;
36
-
37
- type V1Model< T > = {
38
- get: < K extends keyof T >( key: K ) => T[ K ];
39
38
  };
@@ -1,26 +0,0 @@
1
- import * as React from 'react';
2
- import { useId } from 'react';
3
- import { Accordion, AccordionSummary, AccordionDetails, AccordionSummaryText, Stack } from '@elementor/ui';
4
-
5
- export type AccordionSectionProps = React.PropsWithChildren< {
6
- title: React.ReactNode;
7
- defaultExpanded?: boolean;
8
- } >;
9
-
10
- export const AccordionSection = ( { title, children, defaultExpanded = false }: AccordionSectionProps ) => {
11
- const uid = useId();
12
- const labelId = `label-${ uid }`;
13
- const contentId = `content-${ uid }`;
14
-
15
- // TODO: Change to collapsible list item
16
- return (
17
- <Accordion disableGutters defaultExpanded={ defaultExpanded }>
18
- <AccordionSummary aria-controls={ contentId } id={ labelId }>
19
- <AccordionSummaryText primaryTypographyProps={ { variant: 'caption' } }>{ title }</AccordionSummaryText>
20
- </AccordionSummary>
21
- <AccordionDetails id={ contentId } aria-labelledby={ labelId }>
22
- <Stack gap={ 2.5 }>{ children }</Stack>
23
- </AccordionDetails>
24
- </Accordion>
25
- );
26
- };
@@ -1,10 +0,0 @@
1
- import * as React from 'react';
2
- import { Typography } from '@elementor/ui';
3
-
4
- export const ControlLabel = ( { children }: { children: React.ReactNode } ) => {
5
- return (
6
- <Typography component="label" variant="caption" color="text.secondary">
7
- { children }
8
- </Typography>
9
- );
10
- };
@@ -1,30 +0,0 @@
1
- import * as React from 'react';
2
- import { PropKey, PropValue } from '@elementor/editor-props';
3
- import { createContext, useContext } from 'react';
4
-
5
- export type BoundPropContext< T extends PropValue > = {
6
- bind: PropKey;
7
- setValue: ( value: T | undefined ) => void;
8
- value: T | undefined;
9
- };
10
-
11
- export type BoundPropProviderProps< T extends PropValue > = BoundPropContext< T > & {
12
- children: React.ReactNode;
13
- };
14
-
15
- const BoundPropContext = createContext< BoundPropContext< PropValue > | null >( null );
16
- export const BoundPropProvider = ( { children, value, setValue, bind }: BoundPropProviderProps< PropValue > ) => {
17
- return <BoundPropContext.Provider value={ { value, setValue, bind } }>{ children }</BoundPropContext.Provider>;
18
- };
19
-
20
- export function useBoundProp< T extends PropValue >(): BoundPropContext< T | undefined >;
21
- export function useBoundProp< T extends PropValue >( defaultValue: T ): BoundPropContext< T >;
22
- export function useBoundProp< T extends PropValue >( defaultValue?: T ) {
23
- const boundPropContext = useContext< BoundPropContext< T > >( BoundPropContext as never );
24
-
25
- if ( ! boundPropContext ) {
26
- throw new Error( 'useBoundProp must be used within a BoundPropContext' );
27
- }
28
-
29
- return { ...boundPropContext, value: boundPropContext.value ?? defaultValue };
30
- }