@elementor/editor-editing-panel 1.8.1 → 1.10.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 +74 -0
- package/dist/index.d.mts +10 -32
- package/dist/index.d.ts +10 -32
- package/dist/index.js +789 -824
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +772 -808
- package/dist/index.mjs.map +1 -1
- package/package.json +15 -13
- package/src/components/css-class-menu.tsx +30 -54
- package/src/components/css-class-selector.tsx +35 -22
- package/src/components/editable-field.tsx +10 -2
- package/src/components/style-sections/layout-section/flex-size-field.tsx +3 -2
- package/src/components/style-tab.tsx +2 -2
- package/src/contexts/style-context.tsx +38 -7
- package/src/controls-registry/control.tsx +3 -12
- package/src/controls-registry/settings-field.tsx +2 -2
- package/src/errors.ts +22 -0
- package/src/hooks/use-styles-fields.ts +62 -14
- package/src/hooks/use-unapply-class.ts +7 -3
- package/src/index.ts +2 -1
- package/src/init.ts +0 -2
- package/src/components/conditional-tooltip-wrapper.tsx +0 -58
- package/src/contexts/css-class-item-context.tsx +0 -31
- package/src/css-classes.ts +0 -45
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elementor/editor-editing-panel",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"author": "Elementor Team",
|
|
6
6
|
"homepage": "https://elementor.com/",
|
|
@@ -39,20 +39,22 @@
|
|
|
39
39
|
"dev": "tsup --config=../../tsup.dev.ts"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@elementor/editor": "0.17.
|
|
43
|
-
"@elementor/editor-controls": "0.
|
|
44
|
-
"@elementor/editor-elements": "0.
|
|
45
|
-
"@elementor/menus": "0.1.
|
|
46
|
-
"@elementor/editor-props": "0.
|
|
47
|
-
"@elementor/editor-panels": "0.10.
|
|
48
|
-
"@elementor/editor-responsive": "0.12.
|
|
49
|
-
"@elementor/editor-styles": "0.5.
|
|
50
|
-
"@elementor/editor-styles-repository": "0.
|
|
51
|
-
"@elementor/editor-
|
|
52
|
-
"@elementor/
|
|
42
|
+
"@elementor/editor": "0.17.5",
|
|
43
|
+
"@elementor/editor-controls": "0.8.0",
|
|
44
|
+
"@elementor/editor-elements": "0.5.1",
|
|
45
|
+
"@elementor/menus": "0.1.3",
|
|
46
|
+
"@elementor/editor-props": "0.8.0",
|
|
47
|
+
"@elementor/editor-panels": "0.10.5",
|
|
48
|
+
"@elementor/editor-responsive": "0.12.6",
|
|
49
|
+
"@elementor/editor-styles": "0.5.4",
|
|
50
|
+
"@elementor/editor-styles-repository": "0.6.0",
|
|
51
|
+
"@elementor/editor-ui": "0.1.0",
|
|
52
|
+
"@elementor/editor-v1-adapters": "0.9.1",
|
|
53
|
+
"@elementor/icons": "1.26.0",
|
|
54
|
+
"@elementor/locations": "0.7.6",
|
|
53
55
|
"@elementor/schema": "0.1.2",
|
|
54
56
|
"@elementor/session": "0.1.0",
|
|
55
|
-
"@elementor/ui": "
|
|
57
|
+
"@elementor/ui": "1.24.1",
|
|
56
58
|
"@elementor/utils": "0.3.0",
|
|
57
59
|
"@wordpress/i18n": "^5.13.0"
|
|
58
60
|
},
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { type
|
|
3
|
-
import { type StyleState } from '@elementor/editor-styles';
|
|
2
|
+
import { type StyleDefinitionState } from '@elementor/editor-styles';
|
|
4
3
|
import { CheckIcon } from '@elementor/icons';
|
|
5
|
-
import { createMenu } from '@elementor/menus';
|
|
6
4
|
import {
|
|
7
5
|
bindMenu,
|
|
8
|
-
|
|
6
|
+
Divider,
|
|
9
7
|
ListItemIcon,
|
|
10
8
|
ListItemText,
|
|
11
9
|
ListSubheader,
|
|
@@ -16,31 +14,17 @@ import {
|
|
|
16
14
|
} from '@elementor/ui';
|
|
17
15
|
import { __ } from '@wordpress/i18n';
|
|
18
16
|
|
|
19
|
-
import { useCssClassItem } from '../contexts/css-class-item-context';
|
|
20
17
|
import { useStyle } from '../contexts/style-context';
|
|
18
|
+
import { useUnapplyClass } from '../hooks/use-unapply-class';
|
|
21
19
|
|
|
22
|
-
|
|
23
|
-
components: {
|
|
24
|
-
StateMenuItem,
|
|
25
|
-
},
|
|
26
|
-
} );
|
|
20
|
+
const STATES: NonNullable< StyleDefinitionState >[] = [ 'hover', 'focus', 'active' ];
|
|
27
21
|
|
|
28
|
-
|
|
29
|
-
components: {
|
|
30
|
-
GlobalClassMenuItem,
|
|
31
|
-
},
|
|
32
|
-
} );
|
|
22
|
+
type CssClassMenuProps = { styleId: string; isFixed: boolean; popupState: PopupState };
|
|
33
23
|
|
|
34
|
-
export function CssClassMenu( {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
popupState: PopupState;
|
|
39
|
-
containerRef: RefObject< Element >;
|
|
40
|
-
} ) {
|
|
41
|
-
const { isGlobal } = useCssClassItem();
|
|
42
|
-
const { default: globalClassMenuItems } = useGlobalClassMenuItems();
|
|
43
|
-
const { default: stateMenuItems } = useStateMenuItems();
|
|
24
|
+
export function CssClassMenu( { styleId, isFixed = false, popupState }: CssClassMenuProps ) {
|
|
25
|
+
const handleKeyDown = ( e: React.KeyboardEvent< HTMLElement > ) => {
|
|
26
|
+
e.stopPropagation();
|
|
27
|
+
};
|
|
44
28
|
|
|
45
29
|
return (
|
|
46
30
|
<Menu
|
|
@@ -50,39 +34,38 @@ export function CssClassMenu( {
|
|
|
50
34
|
vertical: 'top',
|
|
51
35
|
horizontal: 'right',
|
|
52
36
|
} }
|
|
53
|
-
|
|
37
|
+
onKeyDown={ handleKeyDown }
|
|
54
38
|
>
|
|
55
|
-
{
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
</GlobalClassMenuSection>
|
|
61
|
-
) }
|
|
39
|
+
{ /* 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 */ }
|
|
40
|
+
{ ! isFixed && [
|
|
41
|
+
<UnapplyClassMenuItem key="unapply-class" styleId={ styleId } />,
|
|
42
|
+
<Divider key="global-class-items-divider" />,
|
|
43
|
+
] }
|
|
62
44
|
<ListSubheader>{ __( 'Add a pseudo selector', 'elementor' ) }</ListSubheader>
|
|
63
|
-
{
|
|
64
|
-
|
|
65
|
-
|
|
45
|
+
<StateMenuItem key="normal" state={ null } styleId={ styleId } />
|
|
46
|
+
{ STATES.map( ( state ) => {
|
|
47
|
+
return <StateMenuItem key={ state } state={ state } styleId={ styleId } />;
|
|
48
|
+
} ) }
|
|
66
49
|
</Menu>
|
|
67
50
|
);
|
|
68
51
|
}
|
|
69
52
|
|
|
70
53
|
type StateMenuItemProps = {
|
|
71
|
-
state:
|
|
72
|
-
|
|
54
|
+
state: StyleDefinitionState;
|
|
55
|
+
styleId: string;
|
|
73
56
|
};
|
|
74
57
|
|
|
75
|
-
|
|
76
|
-
const {
|
|
77
|
-
const { setId: setActiveId, setMetaState: setActiveMetaState, meta } = useStyle();
|
|
58
|
+
function StateMenuItem( { state, styleId, ...props }: StateMenuItemProps ) {
|
|
59
|
+
const { id: activeId, setId: setActiveId, setMetaState: setActiveMetaState, meta } = useStyle();
|
|
78
60
|
const { state: activeState } = meta;
|
|
79
61
|
|
|
62
|
+
const isActive = styleId === activeId;
|
|
80
63
|
const isSelected = state === activeState && isActive;
|
|
81
64
|
|
|
82
65
|
return (
|
|
83
66
|
<StyledMenuItem
|
|
84
|
-
|
|
85
|
-
|
|
67
|
+
{ ...props }
|
|
68
|
+
selected={ isSelected }
|
|
86
69
|
onClick={ () => {
|
|
87
70
|
if ( ! isActive ) {
|
|
88
71
|
setActiveId( styleId );
|
|
@@ -101,23 +84,16 @@ export function StateMenuItem( { state, disabled }: StateMenuItemProps ) {
|
|
|
101
84
|
);
|
|
102
85
|
}
|
|
103
86
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
onClick: () => void;
|
|
107
|
-
};
|
|
87
|
+
function UnapplyClassMenuItem( { styleId, ...props }: { styleId: string } ) {
|
|
88
|
+
const unapplyClass = useUnapplyClass( styleId );
|
|
108
89
|
|
|
109
|
-
export function GlobalClassMenuItem( { text, onClick }: GlobalClassMenuItemProps ) {
|
|
110
90
|
return (
|
|
111
|
-
<StyledMenuItem onClick={
|
|
112
|
-
<ListItemText primary=
|
|
91
|
+
<StyledMenuItem { ...props } onClick={ unapplyClass }>
|
|
92
|
+
<ListItemText primary="Remove" />
|
|
113
93
|
</StyledMenuItem>
|
|
114
94
|
);
|
|
115
95
|
}
|
|
116
96
|
|
|
117
|
-
const GlobalClassMenuSection = styled( Box )( ( { theme } ) => ( {
|
|
118
|
-
borderBottom: `1px solid ${ theme?.palette.divider }`,
|
|
119
|
-
} ) );
|
|
120
|
-
|
|
121
97
|
const StyledMenuItem = styled( MenuItem )( {
|
|
122
98
|
'&:hover': {
|
|
123
99
|
color: 'text.primary', // Overriding global CSS from the editor.
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { useId, useRef } from 'react';
|
|
3
|
-
import { getElementSetting,
|
|
3
|
+
import { getElementSetting, updateElementSettings, useElementSetting } from '@elementor/editor-elements';
|
|
4
4
|
import { classesPropTypeUtil, type ClassesPropValue } from '@elementor/editor-props';
|
|
5
5
|
import { type StyleDefinitionID } from '@elementor/editor-styles';
|
|
6
6
|
import {
|
|
7
7
|
ELEMENTS_STYLES_PROVIDER_KEY,
|
|
8
|
+
type StylesProvider,
|
|
8
9
|
stylesRepository,
|
|
9
10
|
type UpdateActionPayload,
|
|
10
|
-
useAllStylesByProvider,
|
|
11
11
|
useCreateActionsByProvider,
|
|
12
|
+
useProviders,
|
|
12
13
|
} from '@elementor/editor-styles-repository';
|
|
14
|
+
import { EllipsisWithTooltip } from '@elementor/editor-ui';
|
|
13
15
|
import { DotsVerticalIcon } from '@elementor/icons';
|
|
16
|
+
import { createLocation } from '@elementor/locations';
|
|
14
17
|
import {
|
|
15
18
|
type AutocompleteRenderGetTagProps,
|
|
16
19
|
bindTrigger,
|
|
@@ -24,10 +27,8 @@ import {
|
|
|
24
27
|
import { __ } from '@wordpress/i18n';
|
|
25
28
|
|
|
26
29
|
import { useClassesProp } from '../contexts/classes-prop-context';
|
|
27
|
-
import { CssClassItemProvider } from '../contexts/css-class-item-context';
|
|
28
30
|
import { useElement } from '../contexts/element-context';
|
|
29
31
|
import { useStyle } from '../contexts/style-context';
|
|
30
|
-
import { ConditionalTooltipWrapper } from './conditional-tooltip-wrapper';
|
|
31
32
|
import { CssClassMenu } from './css-class-menu';
|
|
32
33
|
import { EditableField, EditableFieldProvider, useEditableField } from './editable-field';
|
|
33
34
|
import { type Action, MultiCombobox, type Option } from './multi-combobox';
|
|
@@ -48,6 +49,8 @@ const EMPTY_OPTION = {
|
|
|
48
49
|
provider: ELEMENTS_STYLES_PROVIDER_KEY,
|
|
49
50
|
} satisfies StyleDefOption;
|
|
50
51
|
|
|
52
|
+
export const { Slot: ClassSelectorActionsSlot, inject: injectIntoClassSelectorActions } = createLocation();
|
|
53
|
+
|
|
51
54
|
/**
|
|
52
55
|
* Applied - Classes applied to an element.
|
|
53
56
|
* Active - Class that is currently on edit mode.
|
|
@@ -62,16 +65,20 @@ export function CssClassSelector() {
|
|
|
62
65
|
const actions = useCreateActions( { pushAppliedId, setActiveId } );
|
|
63
66
|
|
|
64
67
|
const handleApply = useHandleApply( appliedIds, setAppliedIds );
|
|
65
|
-
const handleActivate = ( { value }: Option ) => setActiveId( value );
|
|
66
68
|
|
|
67
69
|
const applied = useAppliedOptions( options, appliedIds );
|
|
68
70
|
const active = applied.find( ( option ) => option.value === activeId ) ?? EMPTY_OPTION;
|
|
69
71
|
|
|
70
72
|
return (
|
|
71
73
|
<Stack gap={ 1 } p={ 2 }>
|
|
72
|
-
<
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
<Stack direction="row" gap={ 1 } alignItems="center" justifyContent="space-between">
|
|
75
|
+
<Typography component="label" variant="caption" htmlFor={ ID }>
|
|
76
|
+
{ __( 'CSS Classes', 'elementor' ) }
|
|
77
|
+
</Typography>
|
|
78
|
+
<Stack direction="row" gap={ 1 }>
|
|
79
|
+
<ClassSelectorActionsSlot />
|
|
80
|
+
</Stack>
|
|
81
|
+
</Stack>
|
|
75
82
|
<MultiCombobox
|
|
76
83
|
id={ ID }
|
|
77
84
|
size="tiny"
|
|
@@ -87,6 +94,7 @@ export function CssClassSelector() {
|
|
|
87
94
|
values.map( ( value, index ) => {
|
|
88
95
|
const chipProps = getTagProps( { index } );
|
|
89
96
|
const isActive = value.value === active?.value;
|
|
97
|
+
const isElementsProvider = value.provider === ELEMENTS_STYLES_PROVIDER_KEY;
|
|
90
98
|
|
|
91
99
|
const renameLabel = ( newLabel: string ) => {
|
|
92
100
|
return updateClassByProvider( value.provider, { label: newLabel, id: value.value } );
|
|
@@ -97,7 +105,7 @@ export function CssClassSelector() {
|
|
|
97
105
|
key={ chipProps.key }
|
|
98
106
|
value={ value.label }
|
|
99
107
|
onSubmit={ renameLabel }
|
|
100
|
-
editable={ value.
|
|
108
|
+
editable={ ! value.fixed }
|
|
101
109
|
validation={ ( newLabel ) =>
|
|
102
110
|
renameValidation(
|
|
103
111
|
newLabel,
|
|
@@ -109,10 +117,12 @@ export function CssClassSelector() {
|
|
|
109
117
|
label={ value.label }
|
|
110
118
|
id={ value.value }
|
|
111
119
|
isActive={ isActive }
|
|
112
|
-
|
|
120
|
+
isFixed={ value.fixed }
|
|
113
121
|
color={ isActive && value.color ? value.color : 'default' }
|
|
114
122
|
chipProps={ chipProps }
|
|
115
|
-
|
|
123
|
+
// There is only a single local style, which might not exist, so setting it to
|
|
124
|
+
// `null` will either return the actual style or the fallback one.
|
|
125
|
+
onClickActive={ () => setActiveId( isElementsProvider ? null : value.value ) }
|
|
116
126
|
/>
|
|
117
127
|
</EditableFieldProvider>
|
|
118
128
|
);
|
|
@@ -127,7 +137,7 @@ type CssClassItemProps = {
|
|
|
127
137
|
id: string;
|
|
128
138
|
label: string;
|
|
129
139
|
isActive: boolean;
|
|
130
|
-
|
|
140
|
+
isFixed?: boolean;
|
|
131
141
|
color: ChipOwnProps[ 'color' ];
|
|
132
142
|
chipProps: ReturnType< AutocompleteRenderGetTagProps >;
|
|
133
143
|
onClickActive: ( id: string ) => void;
|
|
@@ -139,7 +149,7 @@ export function CssClassItem( {
|
|
|
139
149
|
id,
|
|
140
150
|
label,
|
|
141
151
|
isActive,
|
|
142
|
-
|
|
152
|
+
isFixed = false,
|
|
143
153
|
color: colorProp,
|
|
144
154
|
chipProps,
|
|
145
155
|
onClickActive,
|
|
@@ -155,7 +165,7 @@ export function CssClassItem( {
|
|
|
155
165
|
const color = error ? 'error' : colorProp;
|
|
156
166
|
|
|
157
167
|
return (
|
|
158
|
-
|
|
168
|
+
<>
|
|
159
169
|
<UnstableChipGroup ref={ chipRef } { ...chipGroupProps } aria-label={ `Edit ${ label }` } role="group">
|
|
160
170
|
<Chip
|
|
161
171
|
disabled={ submitting }
|
|
@@ -173,7 +183,7 @@ export function CssClassItem( {
|
|
|
173
183
|
}
|
|
174
184
|
} }
|
|
175
185
|
>
|
|
176
|
-
<
|
|
186
|
+
<EllipsisWithTooltip maxWidth="10ch" title={ label } />
|
|
177
187
|
</EditableField>
|
|
178
188
|
}
|
|
179
189
|
variant={ isActive && ! meta.state ? 'filled' : 'standard' }
|
|
@@ -203,8 +213,8 @@ export function CssClassItem( {
|
|
|
203
213
|
/>
|
|
204
214
|
) }
|
|
205
215
|
</UnstableChipGroup>
|
|
206
|
-
<CssClassMenu popupState={ popupState }
|
|
207
|
-
|
|
216
|
+
<CssClassMenu styleId={ id } popupState={ popupState } isFixed={ isFixed } />
|
|
217
|
+
</>
|
|
208
218
|
);
|
|
209
219
|
}
|
|
210
220
|
|
|
@@ -243,9 +253,13 @@ const isCharactersNotSupported = ( newLabel: string ) => ! VALID_SELECTOR_REGEX.
|
|
|
243
253
|
function useOptions() {
|
|
244
254
|
const { element } = useElement();
|
|
245
255
|
|
|
246
|
-
|
|
247
|
-
|
|
256
|
+
const isProviderEditable = ( provider: StylesProvider ) => !! provider.actions.updateProps;
|
|
257
|
+
|
|
258
|
+
return useProviders()
|
|
259
|
+
.filter( isProviderEditable )
|
|
260
|
+
.flatMap< StyleDefOption >( ( provider ) => {
|
|
248
261
|
const isElements = provider.key === ELEMENTS_STYLES_PROVIDER_KEY;
|
|
262
|
+
const styleDefs = provider.actions.get( { elementId: element.id } );
|
|
249
263
|
|
|
250
264
|
// Add empty local option for elements, as fallback.
|
|
251
265
|
if ( isElements && styleDefs.length === 0 ) {
|
|
@@ -262,8 +276,7 @@ function useOptions() {
|
|
|
262
276
|
group: provider.labels?.plural,
|
|
263
277
|
};
|
|
264
278
|
} );
|
|
265
|
-
}
|
|
266
|
-
);
|
|
279
|
+
} );
|
|
267
280
|
}
|
|
268
281
|
|
|
269
282
|
function useCreateActions( {
|
|
@@ -321,7 +334,7 @@ function useAppliedClassesIds() {
|
|
|
321
334
|
const value = useElementSetting< ClassesPropValue >( element.id, currentClassesProp )?.value || [];
|
|
322
335
|
|
|
323
336
|
const setValue = ( ids: StyleDefinitionID[] ) => {
|
|
324
|
-
|
|
337
|
+
updateElementSettings( {
|
|
325
338
|
id: element.id,
|
|
326
339
|
props: {
|
|
327
340
|
[ currentClassesProp ]: classesPropTypeUtil.create( ids ),
|
|
@@ -86,7 +86,7 @@ export const EditableFieldProvider = ( {
|
|
|
86
86
|
|
|
87
87
|
type EditableFieldProps = ComponentProps< 'div' >;
|
|
88
88
|
|
|
89
|
-
export const EditableField = ( { children, ...props }: EditableFieldProps ) => {
|
|
89
|
+
export const EditableField = ( { children, onClick, ...props }: EditableFieldProps ) => {
|
|
90
90
|
const ref = useRef< HTMLElement >( null );
|
|
91
91
|
const { isEditing, closeEditMode, value, onChange, error, submit, editable } = useEditableField();
|
|
92
92
|
|
|
@@ -110,6 +110,14 @@ export const EditableField = ( { children, ...props }: EditableFieldProps ) => {
|
|
|
110
110
|
}
|
|
111
111
|
};
|
|
112
112
|
|
|
113
|
+
const handleClick = ( event: React.MouseEvent< HTMLDivElement > ) => {
|
|
114
|
+
if ( isEditing ) {
|
|
115
|
+
event.stopPropagation();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
onClick?.( event );
|
|
119
|
+
};
|
|
120
|
+
|
|
113
121
|
const selectAll = () => {
|
|
114
122
|
const selection = getSelection();
|
|
115
123
|
|
|
@@ -131,7 +139,7 @@ export const EditableField = ( { children, ...props }: EditableFieldProps ) => {
|
|
|
131
139
|
return (
|
|
132
140
|
<Tooltip open={ !! error } title={ error } placement="top">
|
|
133
141
|
{ /* eslint-disable-next-line jsx-a11y/no-static-element-interactions */ }
|
|
134
|
-
<div onKeyDown={ handleKeyDown } { ...props }>
|
|
142
|
+
<div onKeyDown={ handleKeyDown } onClick={ handleClick } { ...props }>
|
|
135
143
|
<span
|
|
136
144
|
ref={ ref }
|
|
137
145
|
role="textbox"
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
+
import { useMemo, useState } from 'react';
|
|
2
3
|
import {
|
|
3
4
|
ControlLabel,
|
|
4
5
|
ControlToggleButtonGroup,
|
|
@@ -50,8 +51,8 @@ export const FlexSizeField = () => {
|
|
|
50
51
|
shrink = shrinkField?.value || null,
|
|
51
52
|
basis = basisField?.value || null;
|
|
52
53
|
|
|
53
|
-
const currentGroup =
|
|
54
|
-
[ activeGroup, setActiveGroup ] =
|
|
54
|
+
const currentGroup = useMemo( () => getActiveGroup( { grow, shrink, basis } ), [ grow, shrink, basis ] ),
|
|
55
|
+
[ activeGroup, setActiveGroup ] = useState( currentGroup );
|
|
55
56
|
|
|
56
57
|
const onChangeGroup = ( group: GroupItem | null = null ) => {
|
|
57
58
|
setActiveGroup( group );
|
|
@@ -3,7 +3,7 @@ import { useState } from 'react';
|
|
|
3
3
|
import { useElementSetting, useElementStyles } from '@elementor/editor-elements';
|
|
4
4
|
import { type ClassesPropValue, type PropKey } from '@elementor/editor-props';
|
|
5
5
|
import { useActiveBreakpoint } from '@elementor/editor-responsive';
|
|
6
|
-
import { type StyleDefinitionID, type
|
|
6
|
+
import { type StyleDefinitionID, type StyleDefinitionState } from '@elementor/editor-styles';
|
|
7
7
|
import { SessionStorageProvider } from '@elementor/session';
|
|
8
8
|
import { Divider } from '@elementor/ui';
|
|
9
9
|
import { __ } from '@wordpress/i18n';
|
|
@@ -28,7 +28,7 @@ const CLASSES_PROP_KEY = 'classes';
|
|
|
28
28
|
export const StyleTab = () => {
|
|
29
29
|
const currentClassesProp = useCurrentClassesProp();
|
|
30
30
|
const [ activeStyleDefId, setActiveStyleDefId ] = useActiveStyleDefId( currentClassesProp );
|
|
31
|
-
const [ activeStyleState, setActiveStyleState ] = useState<
|
|
31
|
+
const [ activeStyleState, setActiveStyleState ] = useState< StyleDefinitionState | null >( null );
|
|
32
32
|
const breakpoint = useActiveBreakpoint();
|
|
33
33
|
|
|
34
34
|
return (
|
|
@@ -1,20 +1,43 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { createContext, type Dispatch, type PropsWithChildren, useContext } from 'react';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
type StyleDefinition,
|
|
5
|
+
type StyleDefinitionID,
|
|
6
|
+
type StyleDefinitionState,
|
|
7
|
+
type StyleDefinitionVariant,
|
|
8
|
+
} from '@elementor/editor-styles';
|
|
9
|
+
import { type StylesProvider, stylesRepository } from '@elementor/editor-styles-repository';
|
|
10
|
+
|
|
11
|
+
import { StylesProviderNotFoundError } from '../errors';
|
|
4
12
|
|
|
5
13
|
type ContextValue = {
|
|
6
|
-
id: StyleDefinition[ 'id' ] | null;
|
|
7
14
|
setId: Dispatch< StyleDefinition[ 'id' ] | null >;
|
|
8
|
-
meta:
|
|
9
|
-
setMetaState: Dispatch<
|
|
15
|
+
meta: StyleDefinitionVariant[ 'meta' ];
|
|
16
|
+
setMetaState: Dispatch< StyleDefinitionState >;
|
|
17
|
+
} & ( ContextValueWithProvider | ContextValueWithoutProvider );
|
|
18
|
+
|
|
19
|
+
type ContextValueWithProvider = {
|
|
20
|
+
id: StyleDefinitionID;
|
|
21
|
+
provider: StylesProvider;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
type ContextValueWithoutProvider = {
|
|
25
|
+
id: null;
|
|
26
|
+
provider: null;
|
|
10
27
|
};
|
|
11
28
|
|
|
12
29
|
const Context = createContext< ContextValue | null >( null );
|
|
13
30
|
|
|
14
|
-
type Props = PropsWithChildren< ContextValue >;
|
|
31
|
+
type Props = PropsWithChildren< Omit< ContextValue, 'provider' > >;
|
|
32
|
+
|
|
33
|
+
export function StyleProvider( { children, ...props }: Props ) {
|
|
34
|
+
const provider = props.id === null ? null : getProviderByStyleId( props.id );
|
|
15
35
|
|
|
16
|
-
|
|
17
|
-
|
|
36
|
+
if ( props.id && ! provider ) {
|
|
37
|
+
throw new StylesProviderNotFoundError( { context: { styleId: props.id } } );
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return <Context.Provider value={ { ...props, provider } as ContextValue }>{ children }</Context.Provider>;
|
|
18
41
|
}
|
|
19
42
|
|
|
20
43
|
export function useStyle() {
|
|
@@ -26,3 +49,11 @@ export function useStyle() {
|
|
|
26
49
|
|
|
27
50
|
return context;
|
|
28
51
|
}
|
|
52
|
+
|
|
53
|
+
function getProviderByStyleId( styleId: StyleDefinitionID ) {
|
|
54
|
+
const styleProvider = stylesRepository.getProviders().find( ( provider ) => {
|
|
55
|
+
return provider.actions.get().find( ( style ) => style.id === styleId );
|
|
56
|
+
} );
|
|
57
|
+
|
|
58
|
+
return styleProvider ?? null;
|
|
59
|
+
}
|
|
@@ -1,18 +1,9 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import type { ComponentProps } from 'react';
|
|
3
|
-
import { createError } from '@elementor/utils';
|
|
4
3
|
|
|
4
|
+
import { ControlTypeNotFoundError } from '../errors';
|
|
5
5
|
import { type ControlType, type ControlTypes, getControlByType } from './controls-registry';
|
|
6
6
|
|
|
7
|
-
export type ControlTypeErrorContext = {
|
|
8
|
-
type: string;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
const ControlTypeError = createError< ControlTypeErrorContext >( {
|
|
12
|
-
code: 'CONTROL_TYPE_NOT_FOUND',
|
|
13
|
-
message: `Control type not found.`,
|
|
14
|
-
} );
|
|
15
|
-
|
|
16
7
|
type IsRequired< T, K extends keyof T > = object extends Pick< T, K > ? false : true;
|
|
17
8
|
|
|
18
9
|
type AnyPropertyRequired< T > = {
|
|
@@ -35,8 +26,8 @@ export const Control = < T extends ControlType >( { props, type }: ControlProps<
|
|
|
35
26
|
const ControlByType = getControlByType( type );
|
|
36
27
|
|
|
37
28
|
if ( ! ControlByType ) {
|
|
38
|
-
throw new
|
|
39
|
-
context: { type },
|
|
29
|
+
throw new ControlTypeNotFoundError( {
|
|
30
|
+
context: { controlType: type },
|
|
40
31
|
} );
|
|
41
32
|
}
|
|
42
33
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { PropKeyProvider, PropProvider } from '@elementor/editor-controls';
|
|
3
|
-
import {
|
|
3
|
+
import { updateElementSettings, useElementSetting } from '@elementor/editor-elements';
|
|
4
4
|
import { type PropKey, type PropValue } from '@elementor/editor-props';
|
|
5
5
|
|
|
6
6
|
import { useElement } from '../contexts/element-context';
|
|
@@ -20,7 +20,7 @@ export const SettingsField = ( { bind, children }: Props ) => {
|
|
|
20
20
|
const propType = createTopLevelOjectType( { schema: elementType.propsSchema } );
|
|
21
21
|
|
|
22
22
|
const setValue = ( newValue: Record< string, PropValue > ) => {
|
|
23
|
-
|
|
23
|
+
updateElementSettings( {
|
|
24
24
|
id: element.id,
|
|
25
25
|
props: { ...newValue },
|
|
26
26
|
} );
|
package/src/errors.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type StyleDefinitionID } from '@elementor/editor-styles';
|
|
2
|
+
import { createError } from '@elementor/utils';
|
|
3
|
+
|
|
4
|
+
export const ControlTypeNotFoundError = createError< { controlType: string } >( {
|
|
5
|
+
code: 'control_type_not_found',
|
|
6
|
+
message: 'Control type not found.',
|
|
7
|
+
} );
|
|
8
|
+
|
|
9
|
+
export const StylesProviderNotFoundError = createError< { styleId: StyleDefinitionID } >( {
|
|
10
|
+
code: 'provider_not_found',
|
|
11
|
+
message: 'Styles provider not found.',
|
|
12
|
+
} );
|
|
13
|
+
|
|
14
|
+
export const StylesProviderCannotUpdatePropsError = createError< { providerKey: string } >( {
|
|
15
|
+
code: 'provider_cannot_update_props',
|
|
16
|
+
message: "Styles provider doesn't support updating props.",
|
|
17
|
+
} );
|
|
18
|
+
|
|
19
|
+
export const StyleNotFoundUnderProviderError = createError< { styleId: StyleDefinitionID; providerKey: string } >( {
|
|
20
|
+
code: 'style_not_found_under_provider',
|
|
21
|
+
message: 'Style not found under the provider.',
|
|
22
|
+
} );
|
|
@@ -1,33 +1,81 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useEffect, useReducer } from 'react';
|
|
2
|
+
import { createElementStyle, type ElementID } from '@elementor/editor-elements';
|
|
2
3
|
import type { Props } from '@elementor/editor-props';
|
|
4
|
+
import { getVariantByMeta, type StyleDefinition, type StyleDefinitionVariant } from '@elementor/editor-styles';
|
|
5
|
+
import { type StylesProvider } from '@elementor/editor-styles-repository';
|
|
3
6
|
import { __ } from '@wordpress/i18n';
|
|
4
7
|
|
|
5
8
|
import { useClassesProp } from '../contexts/classes-prop-context';
|
|
6
9
|
import { useElement } from '../contexts/element-context';
|
|
7
10
|
import { useStyle } from '../contexts/style-context';
|
|
11
|
+
import { StyleNotFoundUnderProviderError, StylesProviderCannotUpdatePropsError } from '../errors';
|
|
8
12
|
|
|
9
13
|
export function useStylesFields< T extends Props >( propNames: ( keyof T & string )[] ) {
|
|
10
14
|
const { element } = useElement();
|
|
11
|
-
const { id, meta } = useStyle();
|
|
15
|
+
const { id, meta, provider } = useStyle();
|
|
12
16
|
const classesProp = useClassesProp();
|
|
13
17
|
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
const [ , reRender ] = useReducer( ( p ) => ! p, false );
|
|
19
|
+
|
|
20
|
+
useEffect( () => provider?.subscribe( reRender ), [ provider ] );
|
|
21
|
+
|
|
22
|
+
const value = getProps< T >( {
|
|
23
|
+
elementId: element.id,
|
|
24
|
+
styleId: id,
|
|
25
|
+
provider,
|
|
17
26
|
meta,
|
|
18
27
|
propNames,
|
|
19
28
|
} );
|
|
20
29
|
|
|
21
|
-
const setValue = (
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
+
const setValue = ( props: T ) => {
|
|
31
|
+
if ( id === null ) {
|
|
32
|
+
createElementStyle( {
|
|
33
|
+
elementId: element.id,
|
|
34
|
+
classesProp,
|
|
35
|
+
meta,
|
|
36
|
+
props,
|
|
37
|
+
label: __( 'local', 'elementor' ),
|
|
38
|
+
} );
|
|
39
|
+
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if ( ! provider.actions.updateProps ) {
|
|
44
|
+
throw new StylesProviderCannotUpdatePropsError( { context: { providerKey: provider.key } } );
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
provider.actions.updateProps( { id, meta, props }, { elementId: element.id } );
|
|
30
48
|
};
|
|
31
49
|
|
|
32
50
|
return [ value, setValue ] as const;
|
|
33
51
|
}
|
|
52
|
+
|
|
53
|
+
type GetPropsArgs = {
|
|
54
|
+
provider: StylesProvider | null;
|
|
55
|
+
styleId: StyleDefinition[ 'id' ] | null;
|
|
56
|
+
elementId: ElementID;
|
|
57
|
+
meta: StyleDefinitionVariant[ 'meta' ];
|
|
58
|
+
propNames: string[];
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
type NullableValues< T extends Props > = {
|
|
62
|
+
[ K in keyof T ]: T[ K ] | null;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
function getProps< T extends Props >( { styleId, elementId, provider, meta, propNames }: GetPropsArgs ) {
|
|
66
|
+
if ( ! provider || ! styleId ) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const style = provider.actions.getById?.( styleId, { elementId } );
|
|
71
|
+
|
|
72
|
+
if ( ! style ) {
|
|
73
|
+
throw new StyleNotFoundUnderProviderError( { context: { styleId, providerKey: provider.key } } );
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const variant = getVariantByMeta( style, meta );
|
|
77
|
+
|
|
78
|
+
return Object.fromEntries(
|
|
79
|
+
propNames.map( ( key ) => [ key, variant?.props[ key ] ?? null ] )
|
|
80
|
+
) as NullableValues< T >;
|
|
81
|
+
}
|