@elementor/editor-editing-panel 1.9.0 → 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 +32 -0
- package/dist/index.d.mts +1 -35
- package/dist/index.d.ts +1 -35
- package/dist/index.js +577 -635
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +490 -546
- package/dist/index.mjs.map +1 -1
- package/package.json +12 -11
- package/src/components/css-class-menu.tsx +28 -52
- package/src/components/css-class-selector.tsx +23 -18
- package/src/contexts/style-context.tsx +36 -5
- package/src/controls-registry/control.tsx +3 -12
- package/src/errors.ts +22 -0
- package/src/hooks/use-styles-fields.ts +49 -11
- package/src/hooks/use-unapply-class.ts +4 -0
- package/src/index.ts +0 -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,21 +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.5.
|
|
42
|
+
"@elementor/editor": "0.17.5",
|
|
43
|
+
"@elementor/editor-controls": "0.8.0",
|
|
44
|
+
"@elementor/editor-elements": "0.5.1",
|
|
45
45
|
"@elementor/menus": "0.1.3",
|
|
46
46
|
"@elementor/editor-props": "0.8.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/
|
|
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",
|
|
53
54
|
"@elementor/locations": "0.7.6",
|
|
54
55
|
"@elementor/schema": "0.1.2",
|
|
55
56
|
"@elementor/session": "0.1.0",
|
|
56
|
-
"@elementor/ui": "1.
|
|
57
|
+
"@elementor/ui": "1.24.1",
|
|
57
58
|
"@elementor/utils": "0.3.0",
|
|
58
59
|
"@wordpress/i18n": "^5.13.0"
|
|
59
60
|
},
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { type RefObject } from 'react';
|
|
3
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
54
|
state: StyleDefinitionState;
|
|
72
|
-
|
|
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.
|
|
@@ -5,11 +5,13 @@ import { classesPropTypeUtil, type ClassesPropValue } from '@elementor/editor-pr
|
|
|
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';
|
|
14
16
|
import { createLocation } from '@elementor/locations';
|
|
15
17
|
import {
|
|
@@ -25,10 +27,8 @@ import {
|
|
|
25
27
|
import { __ } from '@wordpress/i18n';
|
|
26
28
|
|
|
27
29
|
import { useClassesProp } from '../contexts/classes-prop-context';
|
|
28
|
-
import { CssClassItemProvider } from '../contexts/css-class-item-context';
|
|
29
30
|
import { useElement } from '../contexts/element-context';
|
|
30
31
|
import { useStyle } from '../contexts/style-context';
|
|
31
|
-
import { ConditionalTooltipWrapper } from './conditional-tooltip-wrapper';
|
|
32
32
|
import { CssClassMenu } from './css-class-menu';
|
|
33
33
|
import { EditableField, EditableFieldProvider, useEditableField } from './editable-field';
|
|
34
34
|
import { type Action, MultiCombobox, type Option } from './multi-combobox';
|
|
@@ -65,14 +65,13 @@ export function CssClassSelector() {
|
|
|
65
65
|
const actions = useCreateActions( { pushAppliedId, setActiveId } );
|
|
66
66
|
|
|
67
67
|
const handleApply = useHandleApply( appliedIds, setAppliedIds );
|
|
68
|
-
const handleActivate = ( { value }: Option ) => setActiveId( value );
|
|
69
68
|
|
|
70
69
|
const applied = useAppliedOptions( options, appliedIds );
|
|
71
70
|
const active = applied.find( ( option ) => option.value === activeId ) ?? EMPTY_OPTION;
|
|
72
71
|
|
|
73
72
|
return (
|
|
74
73
|
<Stack gap={ 1 } p={ 2 }>
|
|
75
|
-
<Stack direction="row" gap={ 1 } alignItems="
|
|
74
|
+
<Stack direction="row" gap={ 1 } alignItems="center" justifyContent="space-between">
|
|
76
75
|
<Typography component="label" variant="caption" htmlFor={ ID }>
|
|
77
76
|
{ __( 'CSS Classes', 'elementor' ) }
|
|
78
77
|
</Typography>
|
|
@@ -95,6 +94,7 @@ export function CssClassSelector() {
|
|
|
95
94
|
values.map( ( value, index ) => {
|
|
96
95
|
const chipProps = getTagProps( { index } );
|
|
97
96
|
const isActive = value.value === active?.value;
|
|
97
|
+
const isElementsProvider = value.provider === ELEMENTS_STYLES_PROVIDER_KEY;
|
|
98
98
|
|
|
99
99
|
const renameLabel = ( newLabel: string ) => {
|
|
100
100
|
return updateClassByProvider( value.provider, { label: newLabel, id: value.value } );
|
|
@@ -105,7 +105,7 @@ export function CssClassSelector() {
|
|
|
105
105
|
key={ chipProps.key }
|
|
106
106
|
value={ value.label }
|
|
107
107
|
onSubmit={ renameLabel }
|
|
108
|
-
editable={ value.
|
|
108
|
+
editable={ ! value.fixed }
|
|
109
109
|
validation={ ( newLabel ) =>
|
|
110
110
|
renameValidation(
|
|
111
111
|
newLabel,
|
|
@@ -117,10 +117,12 @@ export function CssClassSelector() {
|
|
|
117
117
|
label={ value.label }
|
|
118
118
|
id={ value.value }
|
|
119
119
|
isActive={ isActive }
|
|
120
|
-
|
|
120
|
+
isFixed={ value.fixed }
|
|
121
121
|
color={ isActive && value.color ? value.color : 'default' }
|
|
122
122
|
chipProps={ chipProps }
|
|
123
|
-
|
|
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 ) }
|
|
124
126
|
/>
|
|
125
127
|
</EditableFieldProvider>
|
|
126
128
|
);
|
|
@@ -135,7 +137,7 @@ type CssClassItemProps = {
|
|
|
135
137
|
id: string;
|
|
136
138
|
label: string;
|
|
137
139
|
isActive: boolean;
|
|
138
|
-
|
|
140
|
+
isFixed?: boolean;
|
|
139
141
|
color: ChipOwnProps[ 'color' ];
|
|
140
142
|
chipProps: ReturnType< AutocompleteRenderGetTagProps >;
|
|
141
143
|
onClickActive: ( id: string ) => void;
|
|
@@ -147,7 +149,7 @@ export function CssClassItem( {
|
|
|
147
149
|
id,
|
|
148
150
|
label,
|
|
149
151
|
isActive,
|
|
150
|
-
|
|
152
|
+
isFixed = false,
|
|
151
153
|
color: colorProp,
|
|
152
154
|
chipProps,
|
|
153
155
|
onClickActive,
|
|
@@ -163,7 +165,7 @@ export function CssClassItem( {
|
|
|
163
165
|
const color = error ? 'error' : colorProp;
|
|
164
166
|
|
|
165
167
|
return (
|
|
166
|
-
|
|
168
|
+
<>
|
|
167
169
|
<UnstableChipGroup ref={ chipRef } { ...chipGroupProps } aria-label={ `Edit ${ label }` } role="group">
|
|
168
170
|
<Chip
|
|
169
171
|
disabled={ submitting }
|
|
@@ -181,7 +183,7 @@ export function CssClassItem( {
|
|
|
181
183
|
}
|
|
182
184
|
} }
|
|
183
185
|
>
|
|
184
|
-
<
|
|
186
|
+
<EllipsisWithTooltip maxWidth="10ch" title={ label } />
|
|
185
187
|
</EditableField>
|
|
186
188
|
}
|
|
187
189
|
variant={ isActive && ! meta.state ? 'filled' : 'standard' }
|
|
@@ -211,8 +213,8 @@ export function CssClassItem( {
|
|
|
211
213
|
/>
|
|
212
214
|
) }
|
|
213
215
|
</UnstableChipGroup>
|
|
214
|
-
<CssClassMenu popupState={ popupState }
|
|
215
|
-
|
|
216
|
+
<CssClassMenu styleId={ id } popupState={ popupState } isFixed={ isFixed } />
|
|
217
|
+
</>
|
|
216
218
|
);
|
|
217
219
|
}
|
|
218
220
|
|
|
@@ -251,9 +253,13 @@ const isCharactersNotSupported = ( newLabel: string ) => ! VALID_SELECTOR_REGEX.
|
|
|
251
253
|
function useOptions() {
|
|
252
254
|
const { element } = useElement();
|
|
253
255
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
+
const isProviderEditable = ( provider: StylesProvider ) => !! provider.actions.updateProps;
|
|
257
|
+
|
|
258
|
+
return useProviders()
|
|
259
|
+
.filter( isProviderEditable )
|
|
260
|
+
.flatMap< StyleDefOption >( ( provider ) => {
|
|
256
261
|
const isElements = provider.key === ELEMENTS_STYLES_PROVIDER_KEY;
|
|
262
|
+
const styleDefs = provider.actions.get( { elementId: element.id } );
|
|
257
263
|
|
|
258
264
|
// Add empty local option for elements, as fallback.
|
|
259
265
|
if ( isElements && styleDefs.length === 0 ) {
|
|
@@ -270,8 +276,7 @@ function useOptions() {
|
|
|
270
276
|
group: provider.labels?.plural,
|
|
271
277
|
};
|
|
272
278
|
} );
|
|
273
|
-
}
|
|
274
|
-
);
|
|
279
|
+
} );
|
|
275
280
|
}
|
|
276
281
|
|
|
277
282
|
function useCreateActions( {
|
|
@@ -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
15
|
meta: StyleDefinitionVariant[ 'meta' ];
|
|
9
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
|
|
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,19 +1,28 @@
|
|
|
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
|
} );
|
|
@@ -31,13 +40,42 @@ export function useStylesFields< T extends Props >( propNames: ( keyof T & strin
|
|
|
31
40
|
return;
|
|
32
41
|
}
|
|
33
42
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
} );
|
|
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 } );
|
|
40
48
|
};
|
|
41
49
|
|
|
42
50
|
return [ value, setValue ] as const;
|
|
43
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
|
+
}
|
|
@@ -3,9 +3,11 @@ import { type ClassesPropValue } from '@elementor/editor-props';
|
|
|
3
3
|
|
|
4
4
|
import { useClassesProp } from '../contexts/classes-prop-context';
|
|
5
5
|
import { useElement } from '../contexts/element-context';
|
|
6
|
+
import { useStyle } from '../contexts/style-context';
|
|
6
7
|
|
|
7
8
|
export const useUnapplyClass = ( classId: string ) => {
|
|
8
9
|
const { element } = useElement();
|
|
10
|
+
const { setId: setStyleId } = useStyle();
|
|
9
11
|
const classesProp = useClassesProp();
|
|
10
12
|
|
|
11
13
|
const classes = useElementSetting< ClassesPropValue >( element.id, classesProp );
|
|
@@ -21,5 +23,7 @@ export const useUnapplyClass = ( classId: string ) => {
|
|
|
21
23
|
},
|
|
22
24
|
},
|
|
23
25
|
} );
|
|
26
|
+
|
|
27
|
+
setStyleId( null );
|
|
24
28
|
};
|
|
25
29
|
};
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
export { useBoundProp } from '@elementor/editor-controls';
|
|
2
2
|
export type { PopoverActionProps } from './popover-action';
|
|
3
3
|
export { replaceControl } from './control-replacement';
|
|
4
|
-
export { registerGlobalClassMenuItem, registerStateMenuItem } from './components/css-class-menu';
|
|
5
4
|
export { injectIntoClassSelectorActions } from './components/css-class-selector';
|
|
6
5
|
export { usePanelActions, usePanelStatus } from './panel';
|
|
7
6
|
|
package/src/init.ts
CHANGED
|
@@ -3,7 +3,6 @@ import { __registerPanel as registerPanel } from '@elementor/editor-panels';
|
|
|
3
3
|
import { __privateBlockDataCommand as blockDataCommand } from '@elementor/editor-v1-adapters';
|
|
4
4
|
|
|
5
5
|
import { EditingPanelHooks } from './components/editing-panel-hooks';
|
|
6
|
-
import { initCssClasses } from './css-classes';
|
|
7
6
|
import { init as initDynamics } from './dynamics/init';
|
|
8
7
|
import { panel } from './panel';
|
|
9
8
|
import { isAtomicWidgetSelected } from './sync/is-atomic-widget-selected';
|
|
@@ -19,7 +18,6 @@ export default function init() {
|
|
|
19
18
|
|
|
20
19
|
// TODO: Move it from here once we have dynamic package.
|
|
21
20
|
initDynamics();
|
|
22
|
-
initCssClasses();
|
|
23
21
|
}
|
|
24
22
|
|
|
25
23
|
const blockV1Panel = () => {
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { useEffect, useRef, useState } from 'react';
|
|
2
|
-
import * as React from 'react';
|
|
3
|
-
import { Box, Tooltip } from '@elementor/ui';
|
|
4
|
-
|
|
5
|
-
type ConditionalTooltipWrapperProps = {
|
|
6
|
-
maxWidth: React.CSSProperties[ 'maxWidth' ];
|
|
7
|
-
title: string;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export const ConditionalTooltipWrapper = ( { maxWidth, title }: ConditionalTooltipWrapperProps ) => {
|
|
11
|
-
const elRef = useRef< HTMLElement >( null );
|
|
12
|
-
const [ isOverflown, setIsOverflown ] = useState( false );
|
|
13
|
-
|
|
14
|
-
useEffect( () => {
|
|
15
|
-
const onResize = () => {
|
|
16
|
-
const element = elRef.current;
|
|
17
|
-
|
|
18
|
-
if ( element ) {
|
|
19
|
-
setIsOverflown( element.scrollWidth > element.clientWidth );
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
onResize();
|
|
24
|
-
|
|
25
|
-
window.addEventListener( 'resize', onResize );
|
|
26
|
-
|
|
27
|
-
return () => {
|
|
28
|
-
window.removeEventListener( 'resize', onResize );
|
|
29
|
-
};
|
|
30
|
-
}, [] );
|
|
31
|
-
|
|
32
|
-
if ( isOverflown ) {
|
|
33
|
-
return (
|
|
34
|
-
<Tooltip title={ title } placement="top">
|
|
35
|
-
<Content maxWidth={ maxWidth } ref={ elRef }>
|
|
36
|
-
{ title }
|
|
37
|
-
</Content>
|
|
38
|
-
</Tooltip>
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return (
|
|
43
|
-
<Content maxWidth={ maxWidth } ref={ elRef }>
|
|
44
|
-
{ title }
|
|
45
|
-
</Content>
|
|
46
|
-
);
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
type ContentProps = React.PropsWithChildren< Omit< ConditionalTooltipWrapperProps, 'title' > >;
|
|
50
|
-
|
|
51
|
-
const Content = React.forwardRef( ( { maxWidth, ...tooltipProps }: ContentProps, ref ) => (
|
|
52
|
-
<Box
|
|
53
|
-
ref={ ref }
|
|
54
|
-
position="relative"
|
|
55
|
-
sx={ { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth } }
|
|
56
|
-
{ ...tooltipProps }
|
|
57
|
-
/>
|
|
58
|
-
) );
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
import { createContext, type PropsWithChildren, useContext } from 'react';
|
|
3
|
-
|
|
4
|
-
type ClassItemContextType = {
|
|
5
|
-
styleId: string;
|
|
6
|
-
isGlobal: boolean;
|
|
7
|
-
isActive: boolean;
|
|
8
|
-
};
|
|
9
|
-
const ClassItemContext = createContext< ClassItemContextType >( {
|
|
10
|
-
styleId: '',
|
|
11
|
-
isGlobal: false,
|
|
12
|
-
isActive: false,
|
|
13
|
-
} );
|
|
14
|
-
|
|
15
|
-
type ClassItemProviderProps = PropsWithChildren< ClassItemContextType >;
|
|
16
|
-
|
|
17
|
-
export function CssClassItemProvider( { styleId, isGlobal, isActive, children }: ClassItemProviderProps ) {
|
|
18
|
-
return (
|
|
19
|
-
<ClassItemContext.Provider value={ { styleId, isGlobal, isActive } }>{ children }</ClassItemContext.Provider>
|
|
20
|
-
);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function useCssClassItem() {
|
|
24
|
-
const context = useContext( ClassItemContext );
|
|
25
|
-
|
|
26
|
-
if ( ! context ) {
|
|
27
|
-
throw new Error( 'useCssClassItem must be used within a CssClassItemProvider' );
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return context;
|
|
31
|
-
}
|