@elementor/editor-editing-panel 1.5.0 → 1.6.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 +24 -0
- package/dist/index.d.mts +37 -3
- package/dist/index.d.ts +37 -3
- package/dist/index.js +708 -538
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +604 -420
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -6
- package/src/components/css-class-menu.tsx +125 -0
- package/src/components/css-class-selector.tsx +71 -9
- package/src/components/style-tab.tsx +12 -3
- package/src/contexts/css-class-item-context.tsx +31 -0
- package/src/contexts/style-context.tsx +4 -3
- package/src/css-classes.ts +37 -0
- package/src/index.ts +1 -0
- package/src/init.ts +2 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elementor/editor-editing-panel",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"author": "Elementor Team",
|
|
6
6
|
"homepage": "https://elementor.com/",
|
|
@@ -40,14 +40,14 @@
|
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@elementor/editor": "0.17.2",
|
|
43
|
-
"@elementor/editor-controls": "0.4.
|
|
44
|
-
"@elementor/editor-elements": "0.3.
|
|
43
|
+
"@elementor/editor-controls": "0.4.1",
|
|
44
|
+
"@elementor/editor-elements": "0.3.5",
|
|
45
45
|
"@elementor/menus": "0.1.2",
|
|
46
|
-
"@elementor/editor-props": "0.5.
|
|
46
|
+
"@elementor/editor-props": "0.5.1",
|
|
47
47
|
"@elementor/editor-panels": "0.10.2",
|
|
48
48
|
"@elementor/editor-responsive": "0.12.4",
|
|
49
|
-
"@elementor/editor-styles": "0.
|
|
50
|
-
"@elementor/editor-styles-repository": "0.3.
|
|
49
|
+
"@elementor/editor-styles": "0.4.0",
|
|
50
|
+
"@elementor/editor-styles-repository": "0.3.2",
|
|
51
51
|
"@elementor/editor-v1-adapters": "0.8.5",
|
|
52
52
|
"@elementor/icons": "^1.20.0",
|
|
53
53
|
"@elementor/schema": "0.1.2",
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { type RefObject } from 'react';
|
|
3
|
+
import { type StyleState } from '@elementor/editor-styles';
|
|
4
|
+
import { CheckIcon } from '@elementor/icons';
|
|
5
|
+
import { createMenu } from '@elementor/menus';
|
|
6
|
+
import {
|
|
7
|
+
bindMenu,
|
|
8
|
+
Box,
|
|
9
|
+
ListItemIcon,
|
|
10
|
+
ListItemText,
|
|
11
|
+
ListSubheader,
|
|
12
|
+
Menu,
|
|
13
|
+
MenuItem,
|
|
14
|
+
type PopupState,
|
|
15
|
+
styled,
|
|
16
|
+
} from '@elementor/ui';
|
|
17
|
+
import { __ } from '@wordpress/i18n';
|
|
18
|
+
|
|
19
|
+
import { useCssClassItem } from '../contexts/css-class-item-context';
|
|
20
|
+
import { useStyle } from '../contexts/style-context';
|
|
21
|
+
|
|
22
|
+
export const { useMenuItems: useStateMenuItems, registerStateMenuItem } = createMenu( {
|
|
23
|
+
components: {
|
|
24
|
+
StateMenuItem,
|
|
25
|
+
},
|
|
26
|
+
} );
|
|
27
|
+
|
|
28
|
+
export const { useMenuItems: useGlobalClassMenuItems, registerGlobalClassMenuItem } = createMenu( {
|
|
29
|
+
components: {
|
|
30
|
+
GlobalClassMenuItem,
|
|
31
|
+
},
|
|
32
|
+
} );
|
|
33
|
+
|
|
34
|
+
export function CssClassMenu( {
|
|
35
|
+
popupState,
|
|
36
|
+
containerRef,
|
|
37
|
+
}: {
|
|
38
|
+
popupState: PopupState;
|
|
39
|
+
containerRef: RefObject< Element >;
|
|
40
|
+
} ) {
|
|
41
|
+
const { isGlobal } = useCssClassItem();
|
|
42
|
+
const { default: globalClassMenuItems } = useGlobalClassMenuItems();
|
|
43
|
+
const { default: stateMenuItems } = useStateMenuItems();
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<Menu
|
|
47
|
+
MenuListProps={ { dense: true } }
|
|
48
|
+
{ ...bindMenu( popupState ) }
|
|
49
|
+
anchorOrigin={ {
|
|
50
|
+
vertical: 'top',
|
|
51
|
+
horizontal: 'right',
|
|
52
|
+
} }
|
|
53
|
+
anchorEl={ containerRef.current }
|
|
54
|
+
>
|
|
55
|
+
{ isGlobal && (
|
|
56
|
+
<GlobalClassMenuSection>
|
|
57
|
+
{ globalClassMenuItems.map( ( { id, MenuItem: MenuItemComponent } ) => (
|
|
58
|
+
<MenuItemComponent key={ id } />
|
|
59
|
+
) ) }
|
|
60
|
+
</GlobalClassMenuSection>
|
|
61
|
+
) }
|
|
62
|
+
<ListSubheader>{ __( 'Add a pseudo selector', 'elementor' ) }</ListSubheader>
|
|
63
|
+
{ stateMenuItems.map( ( { id, MenuItem: MenuItemComponent } ) => (
|
|
64
|
+
<MenuItemComponent key={ id } />
|
|
65
|
+
) ) }
|
|
66
|
+
</Menu>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
type StateMenuItemProps = {
|
|
71
|
+
state: StyleState;
|
|
72
|
+
disabled?: boolean;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export function StateMenuItem( { state, disabled }: StateMenuItemProps ) {
|
|
76
|
+
const { isActive, styleId } = useCssClassItem();
|
|
77
|
+
const { setId: setActiveId, setMetaState: setActiveMetaState, meta } = useStyle();
|
|
78
|
+
const { state: activeState } = meta;
|
|
79
|
+
|
|
80
|
+
const isSelected = state === activeState && isActive;
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<StyledMenuItem
|
|
84
|
+
selected={ state === activeState && isActive }
|
|
85
|
+
disabled={ disabled }
|
|
86
|
+
onClick={ () => {
|
|
87
|
+
if ( ! isActive ) {
|
|
88
|
+
setActiveId( styleId );
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
setActiveMetaState( state );
|
|
92
|
+
} }
|
|
93
|
+
>
|
|
94
|
+
{ isSelected && (
|
|
95
|
+
<ListItemIcon>
|
|
96
|
+
<CheckIcon />
|
|
97
|
+
</ListItemIcon>
|
|
98
|
+
) }
|
|
99
|
+
<ListItemText primary={ state ? `:${ state }` : 'Normal' } />
|
|
100
|
+
</StyledMenuItem>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
type GlobalClassMenuItemProps = {
|
|
105
|
+
text: string;
|
|
106
|
+
onClick: () => void;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
export function GlobalClassMenuItem( { text, onClick }: GlobalClassMenuItemProps ) {
|
|
110
|
+
return (
|
|
111
|
+
<StyledMenuItem onClick={ onClick }>
|
|
112
|
+
<ListItemText primary={ text } />
|
|
113
|
+
</StyledMenuItem>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const GlobalClassMenuSection = styled( Box )( ( { theme } ) => ( {
|
|
118
|
+
borderBottom: `1px solid ${ theme?.palette.divider }`,
|
|
119
|
+
} ) );
|
|
120
|
+
|
|
121
|
+
const StyledMenuItem = styled( MenuItem )( {
|
|
122
|
+
'&:hover': {
|
|
123
|
+
color: 'text.primary', // Overriding global CSS from the editor.
|
|
124
|
+
},
|
|
125
|
+
} );
|
|
@@ -1,15 +1,28 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
+
import { useId, useRef } from 'react';
|
|
2
3
|
import { updateSettings, useElementSetting } from '@elementor/editor-elements';
|
|
3
4
|
import { classesPropTypeUtil, type ClassesPropValue } from '@elementor/editor-props';
|
|
4
5
|
import { type StyleDefinitionID } from '@elementor/editor-styles';
|
|
5
6
|
import { ELEMENTS_STYLES_PROVIDER_KEY, useAllStylesByProvider } from '@elementor/editor-styles-repository';
|
|
6
|
-
import {
|
|
7
|
+
import { DotsVerticalIcon } from '@elementor/icons';
|
|
8
|
+
import {
|
|
9
|
+
type AutocompleteRenderGetTagProps,
|
|
10
|
+
bindTrigger,
|
|
11
|
+
Chip,
|
|
12
|
+
type ChipOwnProps,
|
|
13
|
+
Stack,
|
|
14
|
+
Typography,
|
|
15
|
+
UnstableChipGroup,
|
|
16
|
+
usePopupState,
|
|
17
|
+
} from '@elementor/ui';
|
|
7
18
|
import { __ } from '@wordpress/i18n';
|
|
8
19
|
|
|
9
20
|
import { useClassesProp } from '../contexts/classes-prop-context';
|
|
21
|
+
import { CssClassItemProvider } from '../contexts/css-class-item-context';
|
|
10
22
|
import { useElement } from '../contexts/element-context';
|
|
11
23
|
import { useStyle } from '../contexts/style-context';
|
|
12
24
|
import { ConditionalTooltipWrapper } from './conditional-tooltip-wrapper';
|
|
25
|
+
import { CssClassMenu } from './css-class-menu';
|
|
13
26
|
import { MultiCombobox, type Option } from './multi-combobox';
|
|
14
27
|
|
|
15
28
|
const ID = 'elementor-css-class-selector';
|
|
@@ -59,16 +72,15 @@ export function CssClassSelector() {
|
|
|
59
72
|
const isActive = value.value === active?.value;
|
|
60
73
|
|
|
61
74
|
return (
|
|
62
|
-
<
|
|
63
|
-
{ ...chipProps }
|
|
75
|
+
<CssClassItem
|
|
64
76
|
key={ chipProps.key }
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
77
|
+
label={ value.label }
|
|
78
|
+
id={ value.value }
|
|
79
|
+
isActive={ isActive }
|
|
80
|
+
isGlobal={ value.color === 'global' }
|
|
68
81
|
color={ isActive && value.color ? value.color : 'default' }
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
aria-pressed={ isActive }
|
|
82
|
+
chipProps={ chipProps }
|
|
83
|
+
onClickActive={ () => handleActivate( value ) }
|
|
72
84
|
/>
|
|
73
85
|
);
|
|
74
86
|
} )
|
|
@@ -78,6 +90,56 @@ export function CssClassSelector() {
|
|
|
78
90
|
);
|
|
79
91
|
}
|
|
80
92
|
|
|
93
|
+
type CssClassItemProps = {
|
|
94
|
+
id: string;
|
|
95
|
+
label: string;
|
|
96
|
+
isActive: boolean;
|
|
97
|
+
isGlobal: boolean;
|
|
98
|
+
color: ChipOwnProps[ 'color' ];
|
|
99
|
+
chipProps: ReturnType< AutocompleteRenderGetTagProps >;
|
|
100
|
+
onClickActive: ( id: string ) => void;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
function CssClassItem( { id, label, isActive, isGlobal, color, chipProps, onClickActive }: CssClassItemProps ) {
|
|
104
|
+
const { meta } = useStyle();
|
|
105
|
+
// TODO - resolve the useId issue with invalid characters upon CSS selectors (EDS-1089)
|
|
106
|
+
const popupId = useId().replace( /:/g, '_' );
|
|
107
|
+
const popupState = usePopupState( { variant: 'popover', popupId } );
|
|
108
|
+
const chipRef = useRef< Element >( null );
|
|
109
|
+
const { onDelete, ...chipGroupProps } = chipProps;
|
|
110
|
+
|
|
111
|
+
return (
|
|
112
|
+
<CssClassItemProvider styleId={ id } isActive={ isActive } isGlobal={ isGlobal }>
|
|
113
|
+
<UnstableChipGroup ref={ chipRef } { ...chipGroupProps } aria-label={ `Edit ${ label }` } role="group">
|
|
114
|
+
<Chip
|
|
115
|
+
key={ chipProps.key }
|
|
116
|
+
size="small"
|
|
117
|
+
label={ <ConditionalTooltipWrapper maxWidth="10ch" title={ label } /> }
|
|
118
|
+
variant={ isActive && ! meta.state ? 'filled' : 'standard' }
|
|
119
|
+
color={ color }
|
|
120
|
+
onClick={ () => onClickActive( id ) }
|
|
121
|
+
aria-pressed={ isActive }
|
|
122
|
+
/>
|
|
123
|
+
<Chip
|
|
124
|
+
key={ `${ chipProps.key }-menu` }
|
|
125
|
+
size="small"
|
|
126
|
+
label={
|
|
127
|
+
<Stack direction="row" gap={ 0.5 } alignItems="center">
|
|
128
|
+
{ isActive && meta.state && <Typography variant="inherit">{ meta.state }</Typography> }
|
|
129
|
+
<DotsVerticalIcon fontSize="inherit" />
|
|
130
|
+
</Stack>
|
|
131
|
+
}
|
|
132
|
+
variant="filled"
|
|
133
|
+
color={ color }
|
|
134
|
+
{ ...bindTrigger( popupState ) }
|
|
135
|
+
aria-label={ __( 'Open CSS Class Menu', 'elementor' ) }
|
|
136
|
+
/>
|
|
137
|
+
</UnstableChipGroup>
|
|
138
|
+
<CssClassMenu popupState={ popupState } containerRef={ chipRef } />
|
|
139
|
+
</CssClassItemProvider>
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
81
143
|
function useOptions() {
|
|
82
144
|
const { element } = useElement();
|
|
83
145
|
|
|
@@ -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
|
|
6
|
+
import { type StyleDefinitionID, type StyleState } from '@elementor/editor-styles';
|
|
7
7
|
import { Divider } from '@elementor/ui';
|
|
8
8
|
import { __ } from '@wordpress/i18n';
|
|
9
9
|
|
|
@@ -27,11 +27,20 @@ const CLASSES_PROP_KEY = 'classes';
|
|
|
27
27
|
export const StyleTab = () => {
|
|
28
28
|
const currentClassesProp = useCurrentClassesProp();
|
|
29
29
|
const [ activeStyleDefId, setActiveStyleDefId ] = useActiveStyleDefId( currentClassesProp );
|
|
30
|
+
const [ activeStyleState, setActiveStyleState ] = useState< StyleState | null >( null );
|
|
30
31
|
const breakpoint = useActiveBreakpoint();
|
|
31
32
|
|
|
32
33
|
return (
|
|
33
34
|
<ClassesPropProvider prop={ currentClassesProp }>
|
|
34
|
-
<StyleProvider
|
|
35
|
+
<StyleProvider
|
|
36
|
+
meta={ { breakpoint, state: activeStyleState } }
|
|
37
|
+
id={ activeStyleDefId }
|
|
38
|
+
setId={ ( id: StyleDefinitionID | null ) => {
|
|
39
|
+
setActiveStyleDefId( id );
|
|
40
|
+
setActiveStyleState( null );
|
|
41
|
+
} }
|
|
42
|
+
setMetaState={ setActiveStyleState }
|
|
43
|
+
>
|
|
35
44
|
<CssClassSelector />
|
|
36
45
|
<Divider />
|
|
37
46
|
<SectionsList>
|
|
@@ -66,7 +75,7 @@ export const StyleTab = () => {
|
|
|
66
75
|
};
|
|
67
76
|
|
|
68
77
|
function useActiveStyleDefId( currentClassesProp: PropKey ) {
|
|
69
|
-
const [ activeStyledDefId, setActiveStyledDefId ] = useState<
|
|
78
|
+
const [ activeStyledDefId, setActiveStyledDefId ] = useState< StyleDefinitionID | null >( null );
|
|
70
79
|
|
|
71
80
|
const fallback = useFirstElementStyleDef( currentClassesProp );
|
|
72
81
|
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { createContext, type PropsWithChildren } 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 = React.useContext( ClassItemContext );
|
|
25
|
+
|
|
26
|
+
if ( ! context ) {
|
|
27
|
+
throw new Error( 'useCssClassItem must be used within a CssClassItemProvider' );
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return context;
|
|
31
|
+
}
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { createContext, type Dispatch, type PropsWithChildren, useContext } from 'react';
|
|
3
|
-
import { type StyleDefinition, type StyleVariant } from '@elementor/editor-styles';
|
|
3
|
+
import { type StyleDefinition, type StyleState, type StyleVariant } from '@elementor/editor-styles';
|
|
4
4
|
|
|
5
5
|
type ContextValue = {
|
|
6
6
|
id: StyleDefinition[ 'id' ] | null;
|
|
7
7
|
setId: Dispatch< StyleDefinition[ 'id' ] | null >;
|
|
8
8
|
meta: StyleVariant[ 'meta' ];
|
|
9
|
+
setMetaState: Dispatch< StyleState >;
|
|
9
10
|
};
|
|
10
11
|
|
|
11
12
|
const Context = createContext< ContextValue | null >( null );
|
|
12
13
|
|
|
13
14
|
type Props = PropsWithChildren< ContextValue >;
|
|
14
15
|
|
|
15
|
-
export function StyleProvider( { children, id, setId, meta }: Props ) {
|
|
16
|
-
return <Context.Provider value={ { id, setId, meta } }>{ children }</Context.Provider>;
|
|
16
|
+
export function StyleProvider( { children, id, setId, meta, setMetaState }: Props ) {
|
|
17
|
+
return <Context.Provider value={ { id, setId, meta, setMetaState } }>{ children }</Context.Provider>;
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
export function useStyle() {
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type StyleState } from '@elementor/editor-styles';
|
|
2
|
+
|
|
3
|
+
import { registerStateMenuItem } from './components/css-class-menu';
|
|
4
|
+
|
|
5
|
+
const STATES: NonNullable< StyleState >[] = [ 'hover', 'focus', 'active' ];
|
|
6
|
+
|
|
7
|
+
export function initCssClasses() {
|
|
8
|
+
registerStateItems();
|
|
9
|
+
registerGlobalClassItems();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function registerStateItems() {
|
|
13
|
+
registerStateMenuItem( {
|
|
14
|
+
id: 'normal',
|
|
15
|
+
props: {
|
|
16
|
+
state: null,
|
|
17
|
+
},
|
|
18
|
+
} );
|
|
19
|
+
|
|
20
|
+
STATES.forEach( ( state ) => {
|
|
21
|
+
registerStateMenuItem( {
|
|
22
|
+
id: state,
|
|
23
|
+
props: {
|
|
24
|
+
state,
|
|
25
|
+
},
|
|
26
|
+
} );
|
|
27
|
+
} );
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function registerGlobalClassItems() {
|
|
31
|
+
/**
|
|
32
|
+
* TODO - register the relevant global classes here
|
|
33
|
+
* change the import statement from 'css-class-menu' to -
|
|
34
|
+
* import { registerGlobalClassMenuItem, registerStateMenuItem } from './components/css-class-menu';
|
|
35
|
+
* and use registerGlobalClassMenuItem
|
|
36
|
+
*/
|
|
37
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
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';
|
|
4
5
|
|
|
5
6
|
import init from './init';
|
|
6
7
|
|
package/src/init.ts
CHANGED
|
@@ -3,6 +3,7 @@ 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';
|
|
6
7
|
import { init as initDynamics } from './dynamics/init';
|
|
7
8
|
import { panel } from './panel';
|
|
8
9
|
import { isAtomicWidgetSelected } from './sync/is-atomic-widget-selected';
|
|
@@ -18,6 +19,7 @@ export default function init() {
|
|
|
18
19
|
|
|
19
20
|
// TODO: Move it from here once we have dynamic package.
|
|
20
21
|
initDynamics();
|
|
22
|
+
initCssClasses();
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
const blockV1Panel = () => {
|