@elementor/editor-editing-panel 1.24.0 → 1.27.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.
- package/CHANGELOG.md +64 -0
- package/dist/index.js +294 -194
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +304 -204
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -7
- package/src/components/css-classes/css-class-item.tsx +2 -2
- package/src/components/css-classes/css-class-menu.tsx +55 -9
- package/src/components/css-classes/css-class-selector.tsx +6 -5
- package/src/components/editing-panel-tabs.tsx +1 -8
- package/src/components/style-indicator.tsx +23 -0
- package/src/components/style-sections/border-section/border-radius-field.tsx +4 -5
- package/src/components/style-sections/border-section/border-width-field.tsx +2 -3
- package/src/components/style-sections/layout-section/flex-size-field.tsx +28 -18
- package/src/components/style-sections/typography-section/text-alignment-field.tsx +5 -6
- package/src/contexts/style-context.tsx +1 -1
- package/src/controls-registry/control-type-container.tsx +2 -2
- package/src/dynamics/dynamic-transformer.ts +61 -0
- package/src/dynamics/errors.ts +6 -0
- package/src/dynamics/init.ts +6 -0
- package/src/dynamics/types.ts +17 -0
- package/src/styles-inheritance/create-snapshots-manager.ts +8 -8
- package/src/styles-inheritance/create-styles-inheritance.ts +8 -4
- package/src/styles-inheritance/styles-inheritance-indicator.tsx +13 -31
- package/src/styles-inheritance/types.ts +7 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elementor/editor-editing-panel",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.27.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"author": "Elementor Team",
|
|
6
6
|
"homepage": "https://elementor.com/",
|
|
@@ -40,15 +40,16 @@
|
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@elementor/editor": "0.18.5",
|
|
43
|
-
"@elementor/editor-
|
|
43
|
+
"@elementor/editor-canvas": "0.17.0",
|
|
44
|
+
"@elementor/editor-controls": "0.24.0",
|
|
44
45
|
"@elementor/editor-current-user": "0.3.0",
|
|
45
|
-
"@elementor/editor-elements": "0.
|
|
46
|
+
"@elementor/editor-elements": "0.8.0",
|
|
46
47
|
"@elementor/editor-panels": "0.14.0",
|
|
47
48
|
"@elementor/editor-props": "0.11.1",
|
|
48
|
-
"@elementor/editor-responsive": "0.13.
|
|
49
|
-
"@elementor/editor-styles": "0.6.
|
|
50
|
-
"@elementor/editor-styles-repository": "0.8.
|
|
51
|
-
"@elementor/editor-ui": "0.
|
|
49
|
+
"@elementor/editor-responsive": "0.13.4",
|
|
50
|
+
"@elementor/editor-styles": "0.6.5",
|
|
51
|
+
"@elementor/editor-styles-repository": "0.8.3",
|
|
52
|
+
"@elementor/editor-ui": "0.7.0",
|
|
52
53
|
"@elementor/editor-v1-adapters": "0.11.0",
|
|
53
54
|
"@elementor/icons": "1.37.0",
|
|
54
55
|
"@elementor/locations": "0.7.7",
|
|
@@ -154,11 +154,11 @@ export function CssClassItem( {
|
|
|
154
154
|
|
|
155
155
|
const validateLabel = ( newLabel: string ) => {
|
|
156
156
|
if ( ! stylesRepository.isLabelValid( newLabel ) ) {
|
|
157
|
-
return __( '
|
|
157
|
+
return __( 'Invalid format', 'elementor' );
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
if ( stylesRepository.isLabelExist( newLabel ) ) {
|
|
161
|
-
return __( '
|
|
161
|
+
return __( 'Name exists', 'elementor' );
|
|
162
162
|
}
|
|
163
163
|
|
|
164
164
|
return null;
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { type StyleDefinitionState } from '@elementor/editor-styles';
|
|
3
|
-
import { stylesRepository } from '@elementor/editor-styles-repository';
|
|
3
|
+
import { ELEMENTS_STYLES_PROVIDER_KEY, stylesRepository } from '@elementor/editor-styles-repository';
|
|
4
4
|
import { MenuListItem } from '@elementor/editor-ui';
|
|
5
|
-
import { bindMenu, Divider, Menu, MenuSubheader, type PopupState } from '@elementor/ui';
|
|
5
|
+
import { bindMenu, Divider, Menu, MenuSubheader, type PopupState, Stack } from '@elementor/ui';
|
|
6
6
|
import { __ } from '@wordpress/i18n';
|
|
7
7
|
|
|
8
8
|
import { useStyle } from '../../contexts/style-context';
|
|
9
9
|
import { useUnapplyClass } from '../../hooks/use-unapply-class';
|
|
10
|
+
import { type StyleDefinitionStateWithNormal } from '../../styles-inheritance/types';
|
|
11
|
+
import { StyleIndicator, type StyleIndicatorVariant } from '../style-indicator';
|
|
10
12
|
|
|
11
13
|
const STATES: NonNullable< StyleDefinitionState >[] = [ 'hover', 'focus', 'active' ];
|
|
12
14
|
|
|
@@ -19,13 +21,17 @@ type CssClassMenuProps = {
|
|
|
19
21
|
};
|
|
20
22
|
|
|
21
23
|
export function CssClassMenu( { styleId, provider, popupState, handleRename, anchorEl }: CssClassMenuProps ) {
|
|
24
|
+
const styledStates = useStyledStates( styleId );
|
|
25
|
+
|
|
26
|
+
const indicatorVariant = provider === ELEMENTS_STYLES_PROVIDER_KEY ? 'local' : 'global';
|
|
27
|
+
|
|
22
28
|
const handleKeyDown = ( e: React.KeyboardEvent< HTMLElement > ) => {
|
|
23
29
|
e.stopPropagation();
|
|
24
30
|
};
|
|
25
31
|
|
|
26
32
|
return (
|
|
27
33
|
<Menu
|
|
28
|
-
MenuListProps={ { dense: true } }
|
|
34
|
+
MenuListProps={ { dense: true, sx: { minWidth: '160px' } } }
|
|
29
35
|
{ ...bindMenu( popupState ) }
|
|
30
36
|
anchorEl={ anchorEl }
|
|
31
37
|
anchorOrigin={ {
|
|
@@ -41,18 +47,44 @@ export function CssClassMenu( { styleId, provider, popupState, handleRename, anc
|
|
|
41
47
|
{ /* It has to be an array since MUI menu doesn't accept a Fragment as a child, and wrapping the items with an HTML element disrupts keyboard navigation */ }
|
|
42
48
|
{ getMenuItemsByProvider( { provider, styleId, handleRename, closeMenu: popupState.close } ) }
|
|
43
49
|
<MenuSubheader sx={ { typography: 'caption', color: 'text.secondary', pb: 0.5, pt: 1 } }>
|
|
44
|
-
{ __( '
|
|
50
|
+
{ __( 'States', 'elementor' ) }
|
|
45
51
|
</MenuSubheader>
|
|
46
|
-
<StateMenuItem
|
|
52
|
+
<StateMenuItem
|
|
53
|
+
key="normal"
|
|
54
|
+
state={ null }
|
|
55
|
+
styleId={ styleId }
|
|
56
|
+
closeMenu={ popupState.close }
|
|
57
|
+
isStyled={ styledStates.normal }
|
|
58
|
+
indicatorVariant={ indicatorVariant }
|
|
59
|
+
/>
|
|
47
60
|
{ STATES.map( ( state ) => {
|
|
48
61
|
return (
|
|
49
|
-
<StateMenuItem
|
|
62
|
+
<StateMenuItem
|
|
63
|
+
key={ state }
|
|
64
|
+
state={ state }
|
|
65
|
+
styleId={ styleId }
|
|
66
|
+
closeMenu={ popupState.close }
|
|
67
|
+
isStyled={ styledStates[ state ] }
|
|
68
|
+
indicatorVariant={ indicatorVariant }
|
|
69
|
+
/>
|
|
50
70
|
);
|
|
51
71
|
} ) }
|
|
52
72
|
</Menu>
|
|
53
73
|
);
|
|
54
74
|
}
|
|
55
75
|
|
|
76
|
+
function useStyledStates( styleId: string | null ): Partial< Record< StyleDefinitionStateWithNormal, true > > {
|
|
77
|
+
const { meta } = useStyle();
|
|
78
|
+
|
|
79
|
+
const styleDef = stylesRepository.all().find( ( style ) => style.id === styleId );
|
|
80
|
+
|
|
81
|
+
return Object.fromEntries(
|
|
82
|
+
styleDef?.variants
|
|
83
|
+
.filter( ( variant ) => meta.breakpoint === variant.meta.breakpoint )
|
|
84
|
+
.map( ( variant ) => [ variant.meta.state ?? 'normal', true ] ) ?? []
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
56
88
|
function getMenuItemsByProvider( {
|
|
57
89
|
provider,
|
|
58
90
|
styleId,
|
|
@@ -82,7 +114,7 @@ function getMenuItemsByProvider( {
|
|
|
82
114
|
actions.unshift(
|
|
83
115
|
<MenuSubheader
|
|
84
116
|
key="provider-label"
|
|
85
|
-
sx={ { typography: 'caption', color: 'text.secondary', pb: 0.5, pt: 1 } }
|
|
117
|
+
sx={ { typography: 'caption', color: 'text.secondary', pb: 0.5, pt: 1, textTransform: 'capitalize' } }
|
|
86
118
|
>
|
|
87
119
|
{ providerInstance?.labels?.singular }
|
|
88
120
|
</MenuSubheader>
|
|
@@ -97,9 +129,18 @@ type StateMenuItemProps = {
|
|
|
97
129
|
state: StyleDefinitionState;
|
|
98
130
|
styleId: string | null;
|
|
99
131
|
closeMenu: () => void;
|
|
132
|
+
isStyled?: boolean;
|
|
133
|
+
indicatorVariant: StyleIndicatorVariant;
|
|
100
134
|
};
|
|
101
135
|
|
|
102
|
-
function StateMenuItem( {
|
|
136
|
+
function StateMenuItem( {
|
|
137
|
+
state,
|
|
138
|
+
styleId,
|
|
139
|
+
closeMenu,
|
|
140
|
+
isStyled = false,
|
|
141
|
+
indicatorVariant,
|
|
142
|
+
...props
|
|
143
|
+
}: StateMenuItemProps ) {
|
|
103
144
|
const { id: activeId, setId: setActiveId, setMetaState: setActiveMetaState, meta } = useStyle();
|
|
104
145
|
const { state: activeState } = meta;
|
|
105
146
|
|
|
@@ -121,7 +162,12 @@ function StateMenuItem( { state, styleId, closeMenu, ...props }: StateMenuItemPr
|
|
|
121
162
|
closeMenu();
|
|
122
163
|
} }
|
|
123
164
|
>
|
|
124
|
-
{
|
|
165
|
+
<Stack gap={ 0.75 } direction="row" alignItems="center">
|
|
166
|
+
{ isStyled && (
|
|
167
|
+
<StyleIndicator aria-label={ __( 'Has style', 'elementor' ) } variant={ indicatorVariant } />
|
|
168
|
+
) }
|
|
169
|
+
{ state ?? 'normal' }
|
|
170
|
+
</Stack>
|
|
125
171
|
</MenuListItem>
|
|
126
172
|
);
|
|
127
173
|
}
|
|
@@ -64,7 +64,7 @@ export function CssClassSelector() {
|
|
|
64
64
|
<Stack gap={ 1 } p={ 2 }>
|
|
65
65
|
<Stack direction="row" gap={ 1 } alignItems="center" justifyContent="space-between">
|
|
66
66
|
<Typography component="label" variant="caption" htmlFor={ ID }>
|
|
67
|
-
{ __( '
|
|
67
|
+
{ __( 'Classes', 'elementor' ) }
|
|
68
68
|
</Typography>
|
|
69
69
|
<Stack direction="row" gap={ 1 }>
|
|
70
70
|
<ClassSelectorActionsSlot />
|
|
@@ -148,7 +148,8 @@ function useOptions() {
|
|
|
148
148
|
color: isElements ? 'accent' : 'global',
|
|
149
149
|
icon: isElements ? <MapPinIcon /> : null,
|
|
150
150
|
provider: provider.key,
|
|
151
|
-
|
|
151
|
+
// translators: %s is the plural label of the provider (e.g "Existing classes").
|
|
152
|
+
group: __( 'Existing %s', 'elementor' ).replace( '%s', provider.labels?.plural ?? '' ),
|
|
152
153
|
};
|
|
153
154
|
} );
|
|
154
155
|
} );
|
|
@@ -164,9 +165,9 @@ function useCreateActions( {
|
|
|
164
165
|
return useCreateActionsByProvider().map( ( [ provider, create ] ): Action< StyleDefOption > => {
|
|
165
166
|
return {
|
|
166
167
|
// translators: %s is the label of the new class.
|
|
167
|
-
label: ( value ) => __( 'Create
|
|
168
|
-
// translators: %s is the singular label of css class provider (e.g "
|
|
169
|
-
group: __( 'Create
|
|
168
|
+
label: ( value ) => __( 'Create "%s"', 'elementor' ).replace( '%s', value ),
|
|
169
|
+
// translators: %s is the singular label of css class provider (e.g "CSS Class").
|
|
170
|
+
group: __( 'Create a new %s', 'elementor' ).replace( '%s', provider.labels?.singular ?? '' ),
|
|
170
171
|
condition: ( _, inputValue ) => isLabelValid( inputValue ) && ! hasReachedLimit( provider ),
|
|
171
172
|
apply: ( label ) => {
|
|
172
173
|
const createdId = create( label );
|
|
@@ -19,14 +19,7 @@ export const EditingPanelTabs = () => {
|
|
|
19
19
|
// Reference: https://react.dev/learn/preserving-and-resetting-state#resetting-a-form-with-a-key
|
|
20
20
|
<Fragment key={ element.id }>
|
|
21
21
|
<Stack direction="column" sx={ { width: '100%' } }>
|
|
22
|
-
<Tabs
|
|
23
|
-
variant="fullWidth"
|
|
24
|
-
indicatorColor="secondary"
|
|
25
|
-
textColor="inherit"
|
|
26
|
-
size="small"
|
|
27
|
-
sx={ { mt: 0.5 } }
|
|
28
|
-
{ ...getTabsProps() }
|
|
29
|
-
>
|
|
22
|
+
<Tabs variant="fullWidth" size="small" sx={ { mt: 0.5 } } { ...getTabsProps() }>
|
|
30
23
|
<Tab label={ __( 'General', 'elementor' ) } { ...getTabProps( 'settings' ) } />
|
|
31
24
|
<Tab label={ __( 'Style', 'elementor' ) } { ...getTabProps( 'style' ) } />
|
|
32
25
|
</Tabs>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { styled } from '@elementor/ui';
|
|
2
|
+
|
|
3
|
+
export type StyleIndicatorVariant = 'overridden' | 'local' | 'global';
|
|
4
|
+
|
|
5
|
+
export const StyleIndicator = styled( 'div', {
|
|
6
|
+
shouldForwardProp: ( prop ) => prop !== 'variant',
|
|
7
|
+
} )< { variant?: StyleIndicatorVariant } >`
|
|
8
|
+
width: 5px;
|
|
9
|
+
height: 5px;
|
|
10
|
+
border-radius: 50%;
|
|
11
|
+
background-color: ${ ( { theme, variant } ) => {
|
|
12
|
+
switch ( variant ) {
|
|
13
|
+
case 'overridden':
|
|
14
|
+
return theme.palette.warning.light;
|
|
15
|
+
case 'global':
|
|
16
|
+
return theme.palette.global.dark;
|
|
17
|
+
case 'local':
|
|
18
|
+
return theme.palette.accent.main;
|
|
19
|
+
default:
|
|
20
|
+
return theme.palette.text.disabled;
|
|
21
|
+
}
|
|
22
|
+
} };
|
|
23
|
+
`;
|
|
@@ -13,7 +13,6 @@ import { __ } from '@wordpress/i18n';
|
|
|
13
13
|
|
|
14
14
|
import { StylesField } from '../../../controls-registry/styles-field';
|
|
15
15
|
import { useDirection } from '../../../hooks/use-direction';
|
|
16
|
-
import { RotatedIcon } from '../layout-section/utils/rotated-icon';
|
|
17
16
|
|
|
18
17
|
const StartStartIcon = withDirection( RadiusTopLeftIcon );
|
|
19
18
|
const StartEndIcon = withDirection( RadiusTopRightIcon );
|
|
@@ -32,22 +31,22 @@ const getEndEndLabel = ( isSiteRtl: boolean ) =>
|
|
|
32
31
|
const getCorners = ( isSiteRtl: boolean ): EqualUnequalItems => [
|
|
33
32
|
{
|
|
34
33
|
label: getStartStartLabel( isSiteRtl ),
|
|
35
|
-
icon: <
|
|
34
|
+
icon: <StartStartIcon fontSize={ 'tiny' } />,
|
|
36
35
|
bind: 'start-start',
|
|
37
36
|
},
|
|
38
37
|
{
|
|
39
38
|
label: getStartEndLabel( isSiteRtl ),
|
|
40
|
-
icon: <
|
|
39
|
+
icon: <StartEndIcon fontSize={ 'tiny' } />,
|
|
41
40
|
bind: 'start-end',
|
|
42
41
|
},
|
|
43
42
|
{
|
|
44
43
|
label: getEndStartLabel( isSiteRtl ),
|
|
45
|
-
icon: <
|
|
44
|
+
icon: <EndStartIcon fontSize={ 'tiny' } />,
|
|
46
45
|
bind: 'end-start',
|
|
47
46
|
},
|
|
48
47
|
{
|
|
49
48
|
label: getEndEndLabel( isSiteRtl ),
|
|
50
|
-
icon: <
|
|
49
|
+
icon: <EndEndIcon fontSize={ 'tiny' } />,
|
|
51
50
|
bind: 'end-end',
|
|
52
51
|
},
|
|
53
52
|
];
|
|
@@ -7,7 +7,6 @@ import { __ } from '@wordpress/i18n';
|
|
|
7
7
|
|
|
8
8
|
import { StylesField } from '../../../controls-registry/styles-field';
|
|
9
9
|
import { useDirection } from '../../../hooks/use-direction';
|
|
10
|
-
import { RotatedIcon } from '../layout-section/utils/rotated-icon';
|
|
11
10
|
|
|
12
11
|
const InlineStartIcon = withDirection( SideRightIcon );
|
|
13
12
|
const InlineEndIcon = withDirection( SideLeftIcon );
|
|
@@ -20,7 +19,7 @@ const getEdges = ( isSiteRtl: boolean ): EqualUnequalItems => [
|
|
|
20
19
|
},
|
|
21
20
|
{
|
|
22
21
|
label: isSiteRtl ? __( 'Left', 'elementor' ) : __( 'Right', 'elementor' ),
|
|
23
|
-
icon: <
|
|
22
|
+
icon: <InlineStartIcon fontSize={ 'tiny' } />,
|
|
24
23
|
bind: 'inline-end',
|
|
25
24
|
},
|
|
26
25
|
{
|
|
@@ -30,7 +29,7 @@ const getEdges = ( isSiteRtl: boolean ): EqualUnequalItems => [
|
|
|
30
29
|
},
|
|
31
30
|
{
|
|
32
31
|
label: isSiteRtl ? __( 'Right', 'elementor' ) : __( 'Left', 'elementor' ),
|
|
33
|
-
icon: <
|
|
32
|
+
icon: <InlineEndIcon fontSize={ 'tiny' } />,
|
|
34
33
|
bind: 'inline-start',
|
|
35
34
|
},
|
|
36
35
|
];
|
|
@@ -6,14 +6,14 @@ import {
|
|
|
6
6
|
SizeControl,
|
|
7
7
|
type ToggleButtonGroupItem,
|
|
8
8
|
} from '@elementor/editor-controls';
|
|
9
|
-
import type
|
|
9
|
+
import { numberPropTypeUtil, type NumberPropValue, type SizePropValue } from '@elementor/editor-props';
|
|
10
10
|
import { ExpandIcon, PencilIcon, ShrinkIcon } from '@elementor/icons';
|
|
11
|
-
import { DirectionProvider, Grid, ThemeProvider
|
|
11
|
+
import { DirectionProvider, Grid, ThemeProvider } from '@elementor/ui';
|
|
12
12
|
import { __ } from '@wordpress/i18n';
|
|
13
13
|
|
|
14
14
|
import { StylesField } from '../../../controls-registry/styles-field';
|
|
15
15
|
import { useDirection } from '../../../hooks/use-direction';
|
|
16
|
-
import {
|
|
16
|
+
import { useStylesFields } from '../../../hooks/use-styles-fields';
|
|
17
17
|
import { ControlLabel } from '../../control-label';
|
|
18
18
|
import { SectionContent } from '../../section-content';
|
|
19
19
|
|
|
@@ -43,45 +43,55 @@ const items: ToggleButtonGroupItem< GroupItem >[] = [
|
|
|
43
43
|
];
|
|
44
44
|
|
|
45
45
|
export const FlexSizeField = () => {
|
|
46
|
-
const { isSiteRtl } = useDirection()
|
|
47
|
-
[ growField, setGrowField ] = useStylesField< NumberPropValue | null >( 'flex-grow' ),
|
|
48
|
-
[ shrinkField, setShrinkField ] = useStylesField< NumberPropValue | null >( 'flex-shrink' ),
|
|
49
|
-
[ basisField, setBasisField ] = useStylesField< SizePropValue | null >( 'flex-basis' );
|
|
46
|
+
const { isSiteRtl } = useDirection();
|
|
50
47
|
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
48
|
+
const [ fields, setFields ] = useStylesFields< {
|
|
49
|
+
'flex-grow': NumberPropValue | null;
|
|
50
|
+
'flex-shrink': NumberPropValue | null;
|
|
51
|
+
'flex-basis': SizePropValue | null;
|
|
52
|
+
} >( [ 'flex-grow', 'flex-shrink', 'flex-basis' ] );
|
|
53
|
+
|
|
54
|
+
const grow = fields?.[ 'flex-grow' ]?.value || null;
|
|
55
|
+
const shrink = fields?.[ 'flex-shrink' ]?.value || null;
|
|
56
|
+
const basis = fields?.[ 'flex-basis' ]?.value || null;
|
|
54
57
|
|
|
55
58
|
const currentGroup = useMemo( () => getActiveGroup( { grow, shrink, basis } ), [ grow, shrink, basis ] ),
|
|
56
59
|
[ activeGroup, setActiveGroup ] = useState( currentGroup );
|
|
57
60
|
|
|
58
61
|
const onChangeGroup = ( group: GroupItem | null = null ) => {
|
|
59
62
|
setActiveGroup( group );
|
|
60
|
-
setBasisField( null );
|
|
61
63
|
|
|
62
64
|
if ( ! group || group === 'custom' ) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
+
setFields( {
|
|
66
|
+
'flex-basis': null,
|
|
67
|
+
'flex-grow': null,
|
|
68
|
+
'flex-shrink': null,
|
|
69
|
+
} );
|
|
65
70
|
|
|
66
71
|
return;
|
|
67
72
|
}
|
|
68
73
|
|
|
69
74
|
if ( group === 'flex-grow' ) {
|
|
70
|
-
|
|
71
|
-
|
|
75
|
+
setFields( {
|
|
76
|
+
'flex-basis': null,
|
|
77
|
+
'flex-grow': numberPropTypeUtil.create( DEFAULT ),
|
|
78
|
+
'flex-shrink': null,
|
|
79
|
+
} );
|
|
72
80
|
|
|
73
81
|
return;
|
|
74
82
|
}
|
|
75
83
|
|
|
76
|
-
|
|
77
|
-
|
|
84
|
+
setFields( {
|
|
85
|
+
'flex-basis': null,
|
|
86
|
+
'flex-grow': null,
|
|
87
|
+
'flex-shrink': numberPropTypeUtil.create( DEFAULT ),
|
|
88
|
+
} );
|
|
78
89
|
};
|
|
79
90
|
|
|
80
91
|
return (
|
|
81
92
|
<DirectionProvider rtl={ isSiteRtl }>
|
|
82
93
|
<ThemeProvider>
|
|
83
94
|
<SectionContent>
|
|
84
|
-
<Typography fontSize="large">{ activeGroup }</Typography>
|
|
85
95
|
<Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
|
|
86
96
|
<Grid item xs={ 6 }>
|
|
87
97
|
<StylesField bind={ activeGroup ?? '' }>
|
|
@@ -6,18 +6,17 @@ import { __ } from '@wordpress/i18n';
|
|
|
6
6
|
|
|
7
7
|
import { StylesField } from '../../../controls-registry/styles-field';
|
|
8
8
|
import { ControlLabel } from '../../control-label';
|
|
9
|
-
import { RotatedIcon } from '../layout-section/utils/rotated-icon';
|
|
10
9
|
|
|
11
10
|
type Alignments = 'start' | 'center' | 'end' | 'justify';
|
|
12
11
|
|
|
13
|
-
const
|
|
14
|
-
const
|
|
12
|
+
const AlignStartIcon = withDirection( AlignLeftIcon );
|
|
13
|
+
const AlignEndIcon = withDirection( AlignRightIcon );
|
|
15
14
|
|
|
16
15
|
const options: ToggleButtonGroupItem< Alignments >[] = [
|
|
17
16
|
{
|
|
18
17
|
value: 'start',
|
|
19
18
|
label: __( 'Start', 'elementor' ),
|
|
20
|
-
renderContent: () => <
|
|
19
|
+
renderContent: ( { size } ) => <AlignStartIcon fontSize={ size } />,
|
|
21
20
|
showTooltip: true,
|
|
22
21
|
},
|
|
23
22
|
{
|
|
@@ -29,7 +28,7 @@ const options: ToggleButtonGroupItem< Alignments >[] = [
|
|
|
29
28
|
{
|
|
30
29
|
value: 'end',
|
|
31
30
|
label: __( 'End', 'elementor' ),
|
|
32
|
-
renderContent: () => <
|
|
31
|
+
renderContent: ( { size } ) => <AlignEndIcon fontSize={ size } />,
|
|
33
32
|
showTooltip: true,
|
|
34
33
|
},
|
|
35
34
|
{
|
|
@@ -45,7 +44,7 @@ export const TextAlignmentField = () => {
|
|
|
45
44
|
<StylesField bind={ 'text-align' }>
|
|
46
45
|
<Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
|
|
47
46
|
<Grid item xs={ 6 }>
|
|
48
|
-
<ControlLabel>{ __( '
|
|
47
|
+
<ControlLabel>{ __( 'Text align', 'elementor' ) }</ControlLabel>
|
|
49
48
|
</Grid>
|
|
50
49
|
<Grid item xs={ 6 } display="flex" justifyContent="end">
|
|
51
50
|
<ToggleControl options={ options } />
|
|
@@ -50,7 +50,7 @@ export function useStyle() {
|
|
|
50
50
|
return context;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
function getProviderByStyleId( styleId: StyleDefinitionID ) {
|
|
53
|
+
export function getProviderByStyleId( styleId: StyleDefinitionID ) {
|
|
54
54
|
const styleProvider = stylesRepository.getProviders().find( ( provider ) => {
|
|
55
55
|
return provider.actions.get().find( ( style ) => style.id === styleId );
|
|
56
56
|
} );
|
|
@@ -23,7 +23,7 @@ const StyledContainer = styled( Box, {
|
|
|
23
23
|
const getGridLayout = ( layout: ControlLayout ) => ( {
|
|
24
24
|
justifyContent: 'space-between',
|
|
25
25
|
gridTemplateColumns: {
|
|
26
|
-
full: '1fr',
|
|
27
|
-
'two-columns': 'repeat(2, 1fr)',
|
|
26
|
+
full: 'minmax(0, 1fr)',
|
|
27
|
+
'two-columns': 'repeat(2, minmax(0, 1fr))',
|
|
28
28
|
}[ layout ],
|
|
29
29
|
} );
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { createTransformer } from '@elementor/editor-canvas';
|
|
2
|
+
import { isTransformable, type Props } from '@elementor/editor-props';
|
|
3
|
+
|
|
4
|
+
import { DynamicTagsManagerNotFoundError } from './errors';
|
|
5
|
+
import { type ExtendedWindow } from './types';
|
|
6
|
+
|
|
7
|
+
type Dynamic = {
|
|
8
|
+
name?: string;
|
|
9
|
+
settings?: Props;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const dynamicTransformer = createTransformer( ( value: Dynamic ) => {
|
|
13
|
+
if ( ! value.name ) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return getDynamicValue( value.name, simpleTransform( value.settings ?? {} ) );
|
|
18
|
+
} );
|
|
19
|
+
|
|
20
|
+
// Temporary naive transformation until we'll have a `backendTransformer` that
|
|
21
|
+
// will replace the `dynamicTransformer` client implementation.
|
|
22
|
+
function simpleTransform( props: Props ) {
|
|
23
|
+
const transformed = Object.entries( props ).map( ( [ settingKey, settingValue ] ) => {
|
|
24
|
+
const value = isTransformable( settingValue ) ? settingValue.value : settingValue;
|
|
25
|
+
|
|
26
|
+
return [ settingKey, value ] as const;
|
|
27
|
+
} );
|
|
28
|
+
|
|
29
|
+
return Object.fromEntries( transformed );
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function getDynamicValue( name: string, settings: Record< string, unknown > ) {
|
|
33
|
+
const extendedWindow = window as unknown as ExtendedWindow;
|
|
34
|
+
const { dynamicTags } = extendedWindow.elementor ?? {};
|
|
35
|
+
|
|
36
|
+
if ( ! dynamicTags ) {
|
|
37
|
+
throw new DynamicTagsManagerNotFoundError();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const getTagValue = () => {
|
|
41
|
+
const tag = dynamicTags.createTag( 'v4-dynamic-tag', name, settings );
|
|
42
|
+
|
|
43
|
+
if ( ! tag ) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return dynamicTags.loadTagDataFromCache( tag ) ?? null;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const tagValue = getTagValue();
|
|
51
|
+
|
|
52
|
+
if ( tagValue !== null ) {
|
|
53
|
+
return tagValue;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return new Promise( ( resolve ) => {
|
|
57
|
+
dynamicTags.refreshCacheFromServer( () => {
|
|
58
|
+
resolve( getTagValue() );
|
|
59
|
+
} );
|
|
60
|
+
} );
|
|
61
|
+
}
|
package/src/dynamics/init.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import { settingsTransformersRegistry, styleTransformersRegistry } from '@elementor/editor-canvas';
|
|
2
|
+
|
|
1
3
|
import { replaceControl } from '../control-replacement';
|
|
2
4
|
import { controlActionsMenu } from '../controls-actions';
|
|
3
5
|
import { DynamicSelectionControl } from './components/dynamic-selection-control';
|
|
6
|
+
import { dynamicTransformer } from './dynamic-transformer';
|
|
4
7
|
import { usePropDynamicAction } from './hooks/use-prop-dynamic-action';
|
|
5
8
|
import { isDynamicPropValue } from './utils';
|
|
6
9
|
|
|
@@ -16,4 +19,7 @@ export const init = () => {
|
|
|
16
19
|
id: 'dynamic-tags',
|
|
17
20
|
useProps: usePropDynamicAction,
|
|
18
21
|
} );
|
|
22
|
+
|
|
23
|
+
styleTransformersRegistry.register( 'dynamic', dynamicTransformer );
|
|
24
|
+
settingsTransformersRegistry.register( 'dynamic', dynamicTransformer );
|
|
19
25
|
};
|
package/src/dynamics/types.ts
CHANGED
|
@@ -9,6 +9,7 @@ export type ExtendedWindow = Window & {
|
|
|
9
9
|
groups: Record< DynamicTag[ 'group' ], { title: string } >;
|
|
10
10
|
};
|
|
11
11
|
};
|
|
12
|
+
dynamicTags?: DynamicTagsManager;
|
|
12
13
|
};
|
|
13
14
|
};
|
|
14
15
|
|
|
@@ -34,3 +35,19 @@ export type DynamicPropValue = TransformablePropValue<
|
|
|
34
35
|
'dynamic',
|
|
35
36
|
{ name: string; settings?: Record< string, unknown > }
|
|
36
37
|
>;
|
|
38
|
+
|
|
39
|
+
export type DynamicTagsManager = {
|
|
40
|
+
createTag: ( id: string, name: string, settings: Record< string, unknown > ) => TagInstance;
|
|
41
|
+
loadTagDataFromCache: ( tag: TagInstance ) => unknown;
|
|
42
|
+
refreshCacheFromServer: ( callback: () => void ) => void;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export type TagInstance = {
|
|
46
|
+
options: {
|
|
47
|
+
id: string;
|
|
48
|
+
name: string;
|
|
49
|
+
};
|
|
50
|
+
model: {
|
|
51
|
+
toJSON: () => Record< string, unknown >;
|
|
52
|
+
};
|
|
53
|
+
};
|
|
@@ -10,12 +10,12 @@ import {
|
|
|
10
10
|
type StylesInheritanceSnapshot,
|
|
11
11
|
type StylesInheritanceSnapshotGetter,
|
|
12
12
|
type StylesInheritanceSnapshotsSlot,
|
|
13
|
-
type
|
|
13
|
+
type StyleVariantDetails,
|
|
14
14
|
} from './types';
|
|
15
15
|
import { DEFAULT_STATE, getBreakpointKey, getStateKey } from './utils';
|
|
16
16
|
|
|
17
17
|
export function createSnapshotsManager(
|
|
18
|
-
getStylesByMeta: ( meta: StyleInheritanceMetaProps ) =>
|
|
18
|
+
getStylesByMeta: ( meta: StyleInheritanceMetaProps ) => StyleVariantDetails[],
|
|
19
19
|
breakpointsRoot: BreakpointNode
|
|
20
20
|
): StylesInheritanceSnapshotGetter {
|
|
21
21
|
const breakpointsInheritancePaths = makeBreakpointsInheritancePaths( breakpointsRoot );
|
|
@@ -104,7 +104,7 @@ function makeBreakpointsInheritancePaths( root: BreakpointNode ): BreakpointsInh
|
|
|
104
104
|
|
|
105
105
|
// creates a snapshot slot for a specific breakpoint and state
|
|
106
106
|
function buildStateSnapshotSlot(
|
|
107
|
-
styles:
|
|
107
|
+
styles: StyleVariantDetails[],
|
|
108
108
|
parentBreakpoint: BreakpointStatesSlotsMapping | undefined,
|
|
109
109
|
currentBreakpoint: BreakpointStatesSlotsMapping,
|
|
110
110
|
state: StyleDefinitionState
|
|
@@ -132,13 +132,13 @@ function buildStateSnapshotSlot(
|
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
// creates an initial snapshot based on the passed style variants only
|
|
135
|
-
function buildInitialSnapshotFromStyles( styles:
|
|
135
|
+
function buildInitialSnapshotFromStyles( styles: StyleVariantDetails[] ): StylesInheritanceSnapshotsSlot {
|
|
136
136
|
const snapshot: StylesInheritanceSnapshot = {};
|
|
137
137
|
|
|
138
|
-
styles.forEach( (
|
|
138
|
+
styles.forEach( ( styleData ) => {
|
|
139
139
|
const {
|
|
140
|
-
|
|
141
|
-
} =
|
|
140
|
+
variant: { props },
|
|
141
|
+
} = styleData;
|
|
142
142
|
|
|
143
143
|
Object.entries( props ).forEach( ( [ key, value ] ) => {
|
|
144
144
|
if ( ! snapshot[ key ] ) {
|
|
@@ -146,7 +146,7 @@ function buildInitialSnapshotFromStyles( styles: StyleVariantWithId[] ): StylesI
|
|
|
146
146
|
}
|
|
147
147
|
|
|
148
148
|
const snapshotPropValue: SnapshotPropValue = {
|
|
149
|
-
...
|
|
149
|
+
...styleData,
|
|
150
150
|
value,
|
|
151
151
|
};
|
|
152
152
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type BreakpointNode } from '@elementor/editor-responsive';
|
|
2
2
|
import { type StyleDefinition } from '@elementor/editor-styles';
|
|
3
3
|
|
|
4
|
+
import { getProviderByStyleId } from '../contexts/style-context';
|
|
4
5
|
import { createSnapshotsManager } from './create-snapshots-manager';
|
|
5
6
|
import { type BreakpointsStatesStyles, type StyleInheritanceMetaProps, type StylesInheritanceSnapshot } from './types';
|
|
6
7
|
import { getBreakpointKey, getStateKey } from './utils';
|
|
@@ -21,9 +22,11 @@ function buildStyleVariantsByMetaMapping( styleDefs: StyleDefinition[] ): Breakp
|
|
|
21
22
|
const breakpointStateSlots: BreakpointsStatesStyles = {};
|
|
22
23
|
|
|
23
24
|
styleDefs.forEach( ( styleDef ) => {
|
|
25
|
+
const provider = getProviderByStyleId( styleDef.id )?.key ?? null;
|
|
26
|
+
|
|
24
27
|
// iterate over each style definition's variants and place them in the corresponding breakpoint's base or state styles
|
|
25
|
-
styleDef.variants.forEach( (
|
|
26
|
-
const { meta } =
|
|
28
|
+
styleDef.variants.forEach( ( variant ) => {
|
|
29
|
+
const { meta } = variant;
|
|
27
30
|
const { state, breakpoint } = meta;
|
|
28
31
|
|
|
29
32
|
const breakpointKey = getBreakpointKey( breakpoint );
|
|
@@ -40,8 +43,9 @@ function buildStyleVariantsByMetaMapping( styleDefs: StyleDefinition[] ): Breakp
|
|
|
40
43
|
}
|
|
41
44
|
|
|
42
45
|
breakpointNode[ stateKey ].push( {
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
style: styleDef,
|
|
47
|
+
variant,
|
|
48
|
+
provider,
|
|
45
49
|
} );
|
|
46
50
|
} );
|
|
47
51
|
} );
|