@elementor/editor-editing-panel 0.15.0 → 0.17.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 (43) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/dist/index.js +751 -120
  3. package/dist/index.js.map +1 -1
  4. package/dist/index.mjs +769 -111
  5. package/dist/index.mjs.map +1 -1
  6. package/package.json +7 -6
  7. package/src/components/style-sections/position-section/position-section.tsx +15 -0
  8. package/src/components/style-sections/position-section/z-index-control.tsx +16 -0
  9. package/src/components/style-sections/size-section.tsx +9 -12
  10. package/src/components/style-sections/spacing-section/linked-dimensions-control.tsx +140 -0
  11. package/src/components/style-sections/spacing-section/spacing-section.tsx +22 -0
  12. package/src/components/style-sections/typography-section/letter-spacing-control.tsx +16 -0
  13. package/src/components/style-sections/typography-section/text-alignment-control.tsx +43 -0
  14. package/src/components/style-sections/typography-section/text-color-control.tsx +16 -0
  15. package/src/components/style-sections/typography-section/transform-control.tsx +23 -0
  16. package/src/components/style-sections/typography-section/typography-section.tsx +17 -1
  17. package/src/components/style-sections/typography-section/word-spacing-control.tsx +16 -0
  18. package/src/components/style-tab.tsx +29 -5
  19. package/src/contexts/style-context.tsx +8 -2
  20. package/src/controls/components/control-toggle-button-group.tsx +59 -0
  21. package/src/controls/components/text-field-inner-selection.tsx +79 -0
  22. package/src/controls/control-types/color-control.tsx +24 -0
  23. package/src/controls/control-types/number-control.tsx +25 -0
  24. package/src/controls/control-types/size-control.tsx +20 -34
  25. package/src/controls/control-types/toggle-control.tsx +25 -0
  26. package/src/controls/hooks/use-style-control.ts +2 -1
  27. package/src/controls/settings-control.tsx +1 -1
  28. package/src/dynamics/components/dynamic-selection-control.tsx +181 -0
  29. package/src/dynamics/components/dynamic-selection.tsx +155 -0
  30. package/src/dynamics/dynamic-control.tsx +42 -0
  31. package/src/dynamics/hooks/use-dynamic-tag.ts +10 -0
  32. package/src/{hooks/use-dynamic-tags-config.ts → dynamics/hooks/use-prop-dynamic-tags.ts} +6 -5
  33. package/src/dynamics/hooks/use-prop-value-history.ts +26 -0
  34. package/src/dynamics/init.ts +10 -0
  35. package/src/{sync → dynamics/sync}/get-atomic-dynamic-tags.ts +1 -1
  36. package/src/{sync → dynamics/sync}/get-elementor-config.ts +1 -1
  37. package/src/dynamics/types.ts +32 -0
  38. package/src/dynamics/utils.ts +9 -0
  39. package/src/init.ts +4 -0
  40. package/src/props/is-transformable.ts +14 -0
  41. package/src/sync/types.ts +0 -13
  42. package/src/sync/update-style.ts +2 -2
  43. package/src/types.ts +9 -6
@@ -0,0 +1,25 @@
1
+ import * as React from 'react';
2
+ import { useControl } from '../control-context';
3
+ import { ControlToggleButtonGroup, ToggleButtonGroupItem } from '../components/control-toggle-button-group';
4
+ import { PropValue } from '../../types';
5
+
6
+ type ToggleControlProps< T extends PropValue > = {
7
+ options: ToggleButtonGroupItem< T >[];
8
+ };
9
+
10
+ export const ToggleControl = < T extends PropValue >( { options }: ToggleControlProps< T > ) => {
11
+ const { value, setValue } = useControl< T >();
12
+
13
+ const handleToggle = ( option: T | null ) => {
14
+ setValue( option || undefined );
15
+ };
16
+
17
+ return (
18
+ <ControlToggleButtonGroup
19
+ items={ options }
20
+ value={ value || null }
21
+ onChange={ handleToggle }
22
+ exclusive={ true }
23
+ />
24
+ );
25
+ };
@@ -6,7 +6,7 @@ import { PropKey, PropValue } from '../../types';
6
6
 
7
7
  export const useStyleControl = < T extends PropValue >( propName: PropKey ) => {
8
8
  const { element } = useElementContext();
9
- const { selectedStyleDef, selectedMeta } = useStyleContext();
9
+ const { selectedStyleDef, selectedMeta, selectedClassesProp } = useStyleContext();
10
10
 
11
11
  const value = useElementStyleProp< T >( {
12
12
  elementID: element.id,
@@ -21,6 +21,7 @@ export const useStyleControl = < T extends PropValue >( propName: PropKey ) => {
21
21
  styleDefID: selectedStyleDef?.id,
22
22
  props: { [ propName ]: newValue },
23
23
  meta: selectedMeta,
24
+ bind: selectedClassesProp,
24
25
  } );
25
26
  };
26
27
 
@@ -15,7 +15,7 @@ type Props = {
15
15
  export const SettingsControlProvider = ( { bind, children }: Props ) => {
16
16
  const { element, elementType } = useElementContext();
17
17
 
18
- const defaultValue = elementType.propsSchema[ bind ]?.default;
18
+ const defaultValue = elementType.propsSchema[ bind ]?.type.default;
19
19
  const settingsValue = useWidgetSettings( { id: element.id, bind } );
20
20
  const value = settingsValue ?? defaultValue ?? null;
21
21
 
@@ -0,0 +1,181 @@
1
+ import * as React from 'react';
2
+ import { useId } from 'react';
3
+ import { useControl } from '../../controls/control-context';
4
+ import { DynamicPropValue, DynamicTag } from '../types';
5
+ import { DynamicControl } from '../dynamic-control';
6
+ import { DatabaseIcon, SettingsIcon, XIcon } from '@elementor/icons';
7
+ import type { Control, ControlsSection } from '../../types';
8
+ import { DynamicSelection } from './dynamic-selection';
9
+ import { ControlType, getControlByType } from '../../controls/controls-registry';
10
+ import { ControlLabel } from '../../components/control-label';
11
+ import { Control as BaseControl } from '../../controls/control';
12
+ import { useDynamicTag } from '../hooks/use-dynamic-tag';
13
+ import {
14
+ bindPopover,
15
+ bindTrigger,
16
+ Box,
17
+ IconButton,
18
+ Paper,
19
+ Popover,
20
+ Stack,
21
+ Typography,
22
+ UnstableTag as Tag,
23
+ usePopupState,
24
+ Tabs,
25
+ Divider,
26
+ useTabs,
27
+ Tab,
28
+ TabPanel,
29
+ } from '@elementor/ui';
30
+ import { __ } from '@wordpress/i18n';
31
+ import { usePropValueHistory } from '../hooks/use-prop-value-history';
32
+
33
+ const SIZE = 'tiny';
34
+
35
+ export const DynamicSelectionControl = () => {
36
+ const { bind, value, setValue } = useControl< DynamicPropValue | null >();
37
+ const [ propValueFromHistory ] = usePropValueHistory( bind );
38
+ const { name: tagName = '' } = value?.value || {};
39
+
40
+ const selectionPopoverId = useId();
41
+ const selectionPopoverState = usePopupState( { variant: 'popover', popupId: selectionPopoverId } );
42
+
43
+ const dynamicTag = useDynamicTag( bind, tagName );
44
+
45
+ const removeDynamicTag = () => {
46
+ setValue( propValueFromHistory ?? null );
47
+ };
48
+
49
+ if ( ! dynamicTag ) {
50
+ throw new Error( `Dynamic tag ${ tagName } not found` );
51
+ }
52
+
53
+ return (
54
+ <Box sx={ { width: '100%' } }>
55
+ <Tag
56
+ fullWidth
57
+ showActionsOnHover
58
+ label={ dynamicTag.label }
59
+ startIcon={ <DatabaseIcon fontSize={ SIZE } /> }
60
+ { ...bindTrigger( selectionPopoverState ) }
61
+ actions={
62
+ <>
63
+ <DynamicSettingsPopover dynamicTag={ dynamicTag } />
64
+ <IconButton
65
+ size={ SIZE }
66
+ onClick={ removeDynamicTag }
67
+ aria-label={ __( 'Remove dynamic value', 'elementor' ) }
68
+ >
69
+ <XIcon fontSize={ SIZE } />
70
+ </IconButton>
71
+ </>
72
+ }
73
+ />
74
+ <Popover
75
+ disablePortal
76
+ disableScrollLock
77
+ anchorOrigin={ { vertical: 'bottom', horizontal: 'left' } }
78
+ { ...bindPopover( selectionPopoverState ) }
79
+ >
80
+ <Stack>
81
+ <Stack direction="row" alignItems="center" pl={ 1.5 } pr={ 0.5 } py={ 1.5 }>
82
+ <DatabaseIcon fontSize={ SIZE } sx={ { mr: 0.5 } } />
83
+ <Typography variant="subtitle2">{ __( 'Dynamic Tags', 'elementor' ) }</Typography>
84
+ <IconButton size={ SIZE } sx={ { ml: 'auto' } } onClick={ selectionPopoverState.close }>
85
+ <XIcon fontSize={ SIZE } />
86
+ </IconButton>
87
+ </Stack>
88
+ <DynamicSelection onSelect={ selectionPopoverState.close } />
89
+ </Stack>
90
+ </Popover>
91
+ </Box>
92
+ );
93
+ };
94
+
95
+ export const DynamicSettingsPopover = ( { dynamicTag }: { dynamicTag: DynamicTag } ) => {
96
+ const popupId = useId();
97
+ const settingsPopupState = usePopupState( { variant: 'popover', popupId } );
98
+
99
+ const hasDynamicSettings = !! dynamicTag.atomic_controls.length;
100
+
101
+ if ( ! hasDynamicSettings ) {
102
+ return null;
103
+ }
104
+
105
+ return (
106
+ <>
107
+ <IconButton
108
+ size={ SIZE }
109
+ { ...bindTrigger( settingsPopupState ) }
110
+ aria-label={ __( 'Settings', 'elementor' ) }
111
+ >
112
+ <SettingsIcon fontSize={ SIZE } />
113
+ </IconButton>
114
+ <Popover
115
+ disableScrollLock
116
+ anchorOrigin={ { vertical: 'bottom', horizontal: 'center' } }
117
+ { ...bindPopover( settingsPopupState ) }
118
+ >
119
+ <Paper component={ Stack } sx={ { minHeight: '300px', width: '220px' } }>
120
+ <Stack direction="row" alignItems="center" px={ 1.5 } pt={ 2 } pb={ 1 }>
121
+ <DatabaseIcon fontSize={ SIZE } sx={ { mr: 0.5 } } />
122
+ <Typography variant="subtitle2">{ dynamicTag.label }</Typography>
123
+ <IconButton sx={ { ml: 'auto' } } size={ SIZE } onClick={ settingsPopupState.close }>
124
+ <XIcon fontSize={ SIZE } />
125
+ </IconButton>
126
+ </Stack>
127
+ <DynamicSettings controls={ dynamicTag.atomic_controls } />
128
+ </Paper>
129
+ </Popover>
130
+ </>
131
+ );
132
+ };
133
+
134
+ const DynamicSettings = ( { controls }: { controls: DynamicTag[ 'atomic_controls' ] } ) => {
135
+ const tabs = controls.filter( ( { type } ) => type === 'section' ) as ControlsSection[];
136
+ const { getTabsProps, getTabProps, getTabPanelProps } = useTabs< number >( 0 );
137
+
138
+ if ( ! tabs.length ) {
139
+ // Dynamic must have hierarchical controls.
140
+ return null;
141
+ }
142
+
143
+ return (
144
+ <>
145
+ <Tabs indicatorColor="secondary" textColor="secondary" { ...getTabsProps() }>
146
+ { tabs.map( ( { value }, index ) => (
147
+ <Tab key={ index } label={ value.label } sx={ { px: 1, py: 0.5 } } { ...getTabProps( index ) } />
148
+ ) ) }
149
+ </Tabs>
150
+ <Divider />
151
+
152
+ { tabs.map( ( { value }, index ) => {
153
+ return (
154
+ <TabPanel key={ index } sx={ { flexGrow: 1 } } { ...getTabPanelProps( index ) }>
155
+ <Stack gap={ 1 } px={ 2 }>
156
+ { value.items.map( ( item ) => {
157
+ if ( item.type === 'control' ) {
158
+ return <Control key={ item.value.bind } control={ item.value } />;
159
+ }
160
+ return null;
161
+ } ) }
162
+ </Stack>
163
+ </TabPanel>
164
+ );
165
+ } ) }
166
+ </>
167
+ );
168
+ };
169
+
170
+ const Control = ( { control }: { control: Control[ 'value' ] } ) => {
171
+ if ( ! getControlByType( control.type as ControlType ) ) {
172
+ return null;
173
+ }
174
+
175
+ return (
176
+ <DynamicControl bind={ control.bind }>
177
+ { control.label ? <ControlLabel>{ control.label }</ControlLabel> : null }
178
+ <BaseControl type={ control.type as ControlType } props={ control.props } />
179
+ </DynamicControl>
180
+ );
181
+ };
@@ -0,0 +1,155 @@
1
+ import * as React from 'react';
2
+ import { useState, Fragment } from 'react';
3
+ import { useControl } from '../../controls/control-context';
4
+ import { DynamicPropValue } from '../types';
5
+ import { usePropDynamicTags } from '../hooks/use-prop-dynamic-tags';
6
+ import { getAtomicDynamicTags } from '../sync/get-atomic-dynamic-tags';
7
+ import { SearchIcon, PhotoIcon } from '@elementor/icons';
8
+ import {
9
+ Box,
10
+ Divider,
11
+ InputAdornment,
12
+ Link,
13
+ ListSubheader,
14
+ MenuItem,
15
+ MenuList,
16
+ Stack,
17
+ TextField,
18
+ Typography,
19
+ } from '@elementor/ui';
20
+ import { __ } from '@wordpress/i18n';
21
+ import { PropKey, PropValue } from '../../types';
22
+ import { isDynamicPropValue } from '../utils';
23
+ import { usePropValueHistory } from '../hooks/use-prop-value-history';
24
+
25
+ type Option = {
26
+ label: string;
27
+ value: string;
28
+ };
29
+
30
+ type OptionEntry = [ string, Option[] ];
31
+
32
+ const SIZE = 'tiny';
33
+
34
+ export type DynamicSelectionProps = {
35
+ onSelect?: () => void;
36
+ };
37
+
38
+ export const DynamicSelection = ( { onSelect }: DynamicSelectionProps ) => {
39
+ const [ searchValue, setSearchValue ] = useState( '' );
40
+ const { groups: dynamicGroups } = getAtomicDynamicTags() || {};
41
+ const { bind, value: currentValue, setValue } = useControl< DynamicPropValue | PropValue >();
42
+ const [ , updatePropValueHistory ] = usePropValueHistory( bind );
43
+
44
+ const isCurrentValueDynamic = isDynamicPropValue( currentValue );
45
+
46
+ const options = useFilteredOptions( bind, searchValue );
47
+
48
+ const handleSearch = ( event: React.ChangeEvent< HTMLInputElement > ) => {
49
+ setSearchValue( event.target.value );
50
+ };
51
+
52
+ const handleSetDynamicTag = ( value: string ) => {
53
+ if ( ! isCurrentValueDynamic ) {
54
+ updatePropValueHistory( currentValue );
55
+ }
56
+
57
+ setValue( { $$type: 'dynamic', value: { name: value } } );
58
+
59
+ onSelect?.();
60
+ };
61
+
62
+ return (
63
+ <Stack>
64
+ <Box px={ 1.5 } pb={ 1 }>
65
+ <TextField
66
+ fullWidth
67
+ size={ SIZE }
68
+ value={ searchValue }
69
+ onChange={ handleSearch }
70
+ placeholder={ __( 'Search dynamic tag', 'elementor' ) }
71
+ InputProps={ {
72
+ startAdornment: (
73
+ <InputAdornment position="start">
74
+ <SearchIcon fontSize={ SIZE } />
75
+ </InputAdornment>
76
+ ),
77
+ } }
78
+ />
79
+ </Box>
80
+ <Divider />
81
+ <Box sx={ { overflowY: 'auto', height: 260, width: 220 } }>
82
+ { options.length > 0 ? (
83
+ <MenuList role="listbox" tabIndex={ 0 }>
84
+ { options.map( ( [ category, items ], index ) => (
85
+ <Fragment key={ index }>
86
+ <ListSubheader sx={ { typography: 'caption', color: 'text.tertiary' } }>
87
+ { dynamicGroups?.[ category ]?.title || category }
88
+ </ListSubheader>
89
+ { items.map( ( { value, label: tagLabel } ) => {
90
+ const isSelected = isCurrentValueDynamic && value === currentValue?.value?.name;
91
+
92
+ return (
93
+ <MenuItem
94
+ key={ value }
95
+ selected={ isSelected }
96
+ // eslint-disable-next-line jsx-a11y/no-autofocus
97
+ autoFocus={ isSelected }
98
+ sx={ { typography: 'caption' } }
99
+ onClick={ () => handleSetDynamicTag( value ) }
100
+ >
101
+ { tagLabel }
102
+ </MenuItem>
103
+ );
104
+ } ) }
105
+ </Fragment>
106
+ ) ) }
107
+ </MenuList>
108
+ ) : (
109
+ <Stack alignItems="center" p={ 2.5 } gap={ 1.5 }>
110
+ <PhotoIcon fontSize="large" />
111
+ <Typography align="center" variant="caption" color="text.secondary">
112
+ { __( 'Sorry, nothing matched', 'elementor' ) }
113
+ <br />
114
+ &ldquo;{ searchValue }&rdquo;.
115
+ </Typography>
116
+ <Typography align="center" variant="caption" color="text.secondary">
117
+ <Link
118
+ color="secondary"
119
+ variant="caption"
120
+ component="button"
121
+ onClick={ () => setSearchValue( '' ) }
122
+ >
123
+ { __( 'Clear the filters', 'elementor' ) }
124
+ </Link>
125
+ &nbsp;
126
+ { __( 'and try again.', 'elementor' ) }
127
+ </Typography>
128
+ </Stack>
129
+ ) }
130
+ </Box>
131
+ </Stack>
132
+ );
133
+ };
134
+
135
+ const useFilteredOptions = ( bind: PropKey, searchValue: string ): OptionEntry[] => {
136
+ const dynamicTags = usePropDynamicTags( bind );
137
+
138
+ const options = dynamicTags.reduce< Map< string, Option[] > >( ( categories, { name, label, group } ) => {
139
+ const isVisible = label.toLowerCase().includes( searchValue.trim().toLowerCase() );
140
+
141
+ if ( ! isVisible ) {
142
+ return categories;
143
+ }
144
+
145
+ if ( ! categories.has( group ) ) {
146
+ categories.set( group, [] );
147
+ }
148
+
149
+ categories.get( group )?.push( { label, value: name } );
150
+
151
+ return categories;
152
+ }, new Map() );
153
+
154
+ return [ ...options ];
155
+ };
@@ -0,0 +1,42 @@
1
+ import * as React from 'react';
2
+ import { ControlContext, useControl } from '../controls/control-context';
3
+ import { PropKey, PropValue } from '../types';
4
+ import { useDynamicTag } from './hooks/use-dynamic-tag';
5
+ import { DynamicPropValue } from './types';
6
+
7
+ export type DynamicControlProps = React.PropsWithChildren< {
8
+ bind: PropKey;
9
+ } >;
10
+
11
+ export const DynamicControl = ( { bind, children }: DynamicControlProps ) => {
12
+ const { value, setValue, bind: propName } = useControl< DynamicPropValue >();
13
+ const { name = '', settings } = value?.value ?? {};
14
+
15
+ const dynamicTag = useDynamicTag( propName, name );
16
+
17
+ if ( ! dynamicTag ) {
18
+ throw new Error( `Dynamic tag ${ name } not found` );
19
+ }
20
+
21
+ const defaultValue = dynamicTag.props_schema[ bind ]?.type.default;
22
+ const dynamicValue = settings?.[ bind ] ?? defaultValue;
23
+
24
+ const setDynamicValue = ( newValue: PropValue ) => {
25
+ setValue( {
26
+ $$type: 'dynamic',
27
+ value: {
28
+ name,
29
+ settings: {
30
+ ...settings,
31
+ [ bind ]: newValue,
32
+ },
33
+ },
34
+ } );
35
+ };
36
+
37
+ return (
38
+ <ControlContext.Provider value={ { setValue: setDynamicValue, value: dynamicValue, bind } }>
39
+ { children }
40
+ </ControlContext.Provider>
41
+ );
42
+ };
@@ -0,0 +1,10 @@
1
+ import { usePropDynamicTags } from './use-prop-dynamic-tags';
2
+ import { DynamicTag } from '../types';
3
+ import { PropKey } from '../../types';
4
+ import { useMemo } from 'react';
5
+
6
+ export const useDynamicTag = ( propName: PropKey, tagName: string ): DynamicTag | null => {
7
+ const dynamicTags = usePropDynamicTags( propName );
8
+
9
+ return useMemo( () => dynamicTags.find( ( tag ) => tag.name === tagName ) ?? null, [ dynamicTags, tagName ] );
10
+ };
@@ -1,9 +1,10 @@
1
1
  import { useMemo } from 'react';
2
- import { useElementContext } from '../contexts/element-context';
3
- import { PropKey } from '../types';
2
+ import { useElementContext } from '../../contexts/element-context';
4
3
  import { getAtomicDynamicTags } from '../sync/get-atomic-dynamic-tags';
4
+ import { PropKey } from '../../types';
5
+ import { isDynamicType } from '../utils';
5
6
 
6
- export const useDynamicTagsConfig = ( propName: PropKey ) => {
7
+ export const usePropDynamicTags = ( propName: PropKey ) => {
7
8
  let categories: string[] = [];
8
9
 
9
10
  const { elementType } = useElementContext();
@@ -11,9 +12,9 @@ export const useDynamicTagsConfig = ( propName: PropKey ) => {
11
12
  const propSchema = elementType.propsSchema?.[ propName ];
12
13
 
13
14
  if ( propSchema ) {
14
- const dynamicTags = getAtomicDynamicTags();
15
+ const propDynamicType = propSchema.additional_types.find( isDynamicType );
15
16
 
16
- categories = dynamicTags?.propTypesToDynamic?.[ propSchema.type ] || [];
17
+ categories = propDynamicType?.settings.categories || [];
17
18
  }
18
19
 
19
20
  // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -0,0 +1,26 @@
1
+ import { useElementContext } from '../../contexts/element-context';
2
+ import { PropValue } from '../../types';
3
+
4
+ export const PROPS_VALUES_HISTORY_KEY = 'elementor/dynamic/non-dynamic-values-history';
5
+
6
+ export const usePropValueHistory = ( path: string ) => {
7
+ const valuesHistory = getValues();
8
+ const { element } = useElementContext();
9
+ const key = `${ element.id }-${ path }`;
10
+
11
+ const value = valuesHistory[ key ] ?? null;
12
+
13
+ const setValue = ( newValue: PropValue ) => {
14
+ setValues( { ...valuesHistory, [ key ]: newValue } );
15
+ };
16
+
17
+ return [ value, setValue ] as const;
18
+ };
19
+
20
+ const getValues = () => {
21
+ return JSON.parse( sessionStorage.getItem( PROPS_VALUES_HISTORY_KEY ) || '{}' );
22
+ };
23
+
24
+ const setValues = ( values: Record< string, PropValue > ) => {
25
+ sessionStorage.setItem( PROPS_VALUES_HISTORY_KEY, JSON.stringify( values ) );
26
+ };
@@ -0,0 +1,10 @@
1
+ import { DynamicSelectionControl } from './components/dynamic-selection-control';
2
+ import { replaceControl } from '../controls/control-replacement';
3
+ import { isDynamicPropValue } from './utils';
4
+
5
+ export const init = () => {
6
+ replaceControl( {
7
+ component: DynamicSelectionControl,
8
+ condition: ( { value } ) => isDynamicPropValue( value ),
9
+ } );
10
+ };
@@ -9,6 +9,6 @@ export const getAtomicDynamicTags = () => {
9
9
 
10
10
  return {
11
11
  tags: atomicDynamicTags.tags,
12
- propTypesToDynamic: atomicDynamicTags.prop_types_to_dynamic,
12
+ groups: atomicDynamicTags.groups,
13
13
  };
14
14
  };
@@ -1,4 +1,4 @@
1
- import { ExtendedWindow } from './types';
1
+ import { ExtendedWindow } from '../types';
2
2
 
3
3
  export const getElementorConfig = () => {
4
4
  const extendedWindow = window as unknown as ExtendedWindow;
@@ -0,0 +1,32 @@
1
+ import { ControlItem, PropsSchema, TransformablePropValue } from '../types';
2
+
3
+ export type ExtendedWindow = Window & {
4
+ elementor?: {
5
+ config?: {
6
+ atomicDynamicTags?: {
7
+ tags: DynamicTags;
8
+ groups: Record< DynamicTag[ 'group' ], { title: string } >;
9
+ };
10
+ };
11
+ };
12
+ };
13
+
14
+ export type DynamicTags = Record< DynamicTag[ 'name' ], DynamicTag >;
15
+
16
+ export type DynamicTag = {
17
+ name: string;
18
+ label: string;
19
+ group: string;
20
+ categories: string[];
21
+ atomic_controls: ControlItem[];
22
+ props_schema: PropsSchema;
23
+ };
24
+
25
+ export type DynamicPropType = {
26
+ key: 'dynamic';
27
+ settings: {
28
+ categories: string[];
29
+ };
30
+ };
31
+
32
+ export type DynamicPropValue = TransformablePropValue< { name: string; settings?: Record< string, unknown > } >;
@@ -0,0 +1,9 @@
1
+ import { AdditionalPropType, PropValue } from '../types';
2
+ import { DynamicPropType, DynamicPropValue } from './types';
3
+ import { isTransformable } from '../props/is-transformable';
4
+
5
+ export const isDynamicType = ( prop: AdditionalPropType ): prop is DynamicPropType => prop.key === 'dynamic';
6
+
7
+ export const isDynamicPropValue = ( prop: PropValue ): prop is DynamicPropValue => {
8
+ return isTransformable( prop ) && prop.$$type === 'dynamic';
9
+ };
package/src/init.ts CHANGED
@@ -4,6 +4,7 @@ import { shouldUseV2Panel } from './sync/should-use-v2-panel';
4
4
  import { EditingPanelHooks } from './components/editing-panel-hooks';
5
5
  import { __registerPanel as registerPanel } from '@elementor/editor-panels';
6
6
  import { __privateBlockDataCommand as blockDataCommand } from '@elementor/editor-v1-adapters';
7
+ import { init as initDynamics } from './dynamics/init';
7
8
 
8
9
  export default function init() {
9
10
  registerPanel( panel );
@@ -13,6 +14,9 @@ export default function init() {
13
14
  id: 'editing-panel-hooks',
14
15
  component: EditingPanelHooks,
15
16
  } );
17
+
18
+ // TODO: Move it from here once we have dynamic package.
19
+ initDynamics();
16
20
  }
17
21
 
18
22
  const blockV1Panel = () => {
@@ -0,0 +1,14 @@
1
+ import { z } from '@elementor/schema';
2
+
3
+ // TODO: Move this file content a shared package.
4
+
5
+ export const transformableSchema = z.object( {
6
+ $$type: z.string(),
7
+ value: z.any(),
8
+ } );
9
+
10
+ export type TransformablePropValue = z.infer< typeof transformableSchema >;
11
+
12
+ export const isTransformable = ( value: unknown ): value is TransformablePropValue => {
13
+ return transformableSchema.safeParse( value ).success;
14
+ };
package/src/sync/types.ts CHANGED
@@ -16,22 +16,9 @@ export type ExtendedWindow = Window & {
16
16
  }
17
17
  >;
18
18
  getContainer?: ( id: string ) => V1Element;
19
- config?: {
20
- atomicDynamicTags?: {
21
- tags: DynamicTags;
22
- prop_types_to_dynamic: Record< string, string[] >;
23
- };
24
- };
25
19
  };
26
20
  };
27
21
 
28
- export type DynamicTags = Record< DynamicTag[ 'name' ], DynamicTag >;
29
-
30
- export type DynamicTag = {
31
- name: string;
32
- categories: string[];
33
- };
34
-
35
22
  export type V1Element = {
36
23
  model: V1Model< V1ElementModelProps >;
37
24
  settings?: V1Model< V1ElementSettingsProps >;
@@ -8,10 +8,10 @@ export type UpdateStyleProps = {
8
8
  styleDefID?: StyleDefinitionID;
9
9
  meta: StyleVariant[ 'meta' ];
10
10
  props: Props;
11
- bind?: PropKey;
11
+ bind: PropKey;
12
12
  };
13
13
 
14
- export const updateStyle = ( { elementID, styleDefID, meta, props, bind = 'classes' }: UpdateStyleProps ) => {
14
+ export const updateStyle = ( { elementID, styleDefID, meta, props, bind }: UpdateStyleProps ) => {
15
15
  const container = getContainer( elementID );
16
16
 
17
17
  runCommand( 'document/atomic-widgets/styles', {
package/src/types.ts CHANGED
@@ -34,15 +34,18 @@ export type Control = {
34
34
 
35
35
  export type ControlItem = ControlsSection | Control;
36
36
 
37
- export type Constraint = {
38
- type: string;
39
- value: unknown;
37
+ export type AdditionalPropType = {
38
+ key: string;
39
+ settings: Record< string, unknown >;
40
40
  };
41
41
 
42
42
  export type PropDefinition = {
43
- type: string;
44
- default: PropValue;
45
- constraints?: Constraint[];
43
+ type: {
44
+ key: string;
45
+ default: PropValue;
46
+ settings: Record< string, unknown >;
47
+ };
48
+ additional_types: Array< AdditionalPropType >;
46
49
  };
47
50
 
48
51
  export type PropsSchema = Record< Control[ 'value' ][ 'bind' ], PropDefinition >;