@elementor/editor-editing-panel 1.9.0 → 1.11.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 +105 -0
- package/dist/index.d.mts +1 -35
- package/dist/index.d.ts +1 -35
- package/dist/index.js +996 -1059
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +905 -970
- package/dist/index.mjs.map +1 -1
- package/package.json +18 -14
- package/src/components/css-classes/css-class-item.tsx +130 -0
- package/src/components/css-classes/css-class-menu.tsx +151 -0
- package/src/components/{css-class-selector.tsx → css-classes/css-class-selector.tsx} +34 -160
- package/src/components/style-sections/layout-section/display-field.tsx +9 -1
- package/src/components/style-sections/layout-section/flex-order-field.tsx +5 -5
- package/src/components/style-sections/layout-section/flex-size-field.tsx +1 -1
- package/src/components/style-sections/layout-section/gap-control-field.tsx +0 -2
- package/src/components/style-sections/position-section/dimensions-field.tsx +1 -1
- package/src/components/style-sections/position-section/position-section.tsx +1 -1
- package/src/components/style-sections/typography-section/font-weight-field.tsx +9 -5
- package/src/components/style-sections/typography-section/text-alignment-field.tsx +16 -8
- package/src/components/style-sections/typography-section/transform-field.tsx +12 -3
- package/src/components/style-tab.tsx +1 -1
- package/src/contexts/style-context.tsx +36 -5
- package/src/controls-registry/control.tsx +3 -12
- package/src/controls-registry/controls-registry.tsx +3 -1
- package/src/controls-registry/settings-field.tsx +8 -1
- package/src/dynamics/components/dynamic-selection.tsx +1 -1
- package/src/dynamics/dynamic-control.tsx +1 -1
- package/src/dynamics/types.ts +2 -2
- package/src/dynamics/utils.ts +2 -2
- package/src/errors.ts +22 -0
- package/src/hooks/use-persist-dynamic-value.ts +1 -1
- package/src/hooks/use-styles-fields.ts +151 -9
- package/src/hooks/use-unapply-class.ts +4 -0
- package/src/index.ts +1 -2
- package/src/init.ts +2 -4
- package/src/sync/types.ts +4 -3
- package/src/components/collapsible-field.tsx +0 -36
- package/src/components/conditional-tooltip-wrapper.tsx +0 -58
- package/src/components/css-class-menu.tsx +0 -125
- package/src/components/editable-field.tsx +0 -166
- package/src/contexts/css-class-item-context.tsx +0 -31
- package/src/css-classes.ts +0 -45
- package/src/hooks/use-session-storage.ts +0 -46
- package/src/sync/enqueue-font.ts +0 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elementor/editor-editing-panel",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"author": "Elementor Team",
|
|
6
6
|
"homepage": "https://elementor.com/",
|
|
@@ -39,25 +39,29 @@
|
|
|
39
39
|
"dev": "tsup --config=../../tsup.dev.ts"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@elementor/editor": "0.
|
|
43
|
-
"@elementor/editor-controls": "0.
|
|
44
|
-
"@elementor/editor-elements": "0.5.
|
|
45
|
-
"@elementor/
|
|
46
|
-
"@elementor/editor-props": "0.
|
|
47
|
-
"@elementor/editor-
|
|
48
|
-
"@elementor/editor-
|
|
49
|
-
"@elementor/editor-styles": "0.
|
|
50
|
-
"@elementor/editor-
|
|
51
|
-
"@elementor/editor-v1-adapters": "0.
|
|
52
|
-
"@elementor/icons": "1.
|
|
42
|
+
"@elementor/editor": "0.18.0",
|
|
43
|
+
"@elementor/editor-controls": "0.9.0",
|
|
44
|
+
"@elementor/editor-elements": "0.5.2",
|
|
45
|
+
"@elementor/editor-panels": "0.11.0",
|
|
46
|
+
"@elementor/editor-props": "0.9.0",
|
|
47
|
+
"@elementor/editor-responsive": "0.13.0",
|
|
48
|
+
"@elementor/editor-styles": "0.5.5",
|
|
49
|
+
"@elementor/editor-styles-repository": "0.7.0",
|
|
50
|
+
"@elementor/editor-ui": "0.2.0",
|
|
51
|
+
"@elementor/editor-v1-adapters": "0.10.0",
|
|
52
|
+
"@elementor/icons": "1.31.0",
|
|
53
53
|
"@elementor/locations": "0.7.6",
|
|
54
|
+
"@elementor/menus": "0.1.3",
|
|
54
55
|
"@elementor/schema": "0.1.2",
|
|
55
56
|
"@elementor/session": "0.1.0",
|
|
56
|
-
"@elementor/ui": "1.
|
|
57
|
-
"@elementor/utils": "0.3.
|
|
57
|
+
"@elementor/ui": "1.26.0",
|
|
58
|
+
"@elementor/utils": "0.3.1",
|
|
58
59
|
"@wordpress/i18n": "^5.13.0"
|
|
59
60
|
},
|
|
60
61
|
"peerDependencies": {
|
|
61
62
|
"react": "^18.3.1"
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"tsup": "^8.3.5"
|
|
62
66
|
}
|
|
63
67
|
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { stylesRepository } from '@elementor/editor-styles-repository';
|
|
4
|
+
import { EditableField, EllipsisWithTooltip, useEditable } from '@elementor/editor-ui';
|
|
5
|
+
import { DotsVerticalIcon } from '@elementor/icons';
|
|
6
|
+
import {
|
|
7
|
+
type AutocompleteRenderGetTagProps,
|
|
8
|
+
bindTrigger,
|
|
9
|
+
Chip,
|
|
10
|
+
type ChipOwnProps,
|
|
11
|
+
Stack,
|
|
12
|
+
Typography,
|
|
13
|
+
UnstableChipGroup,
|
|
14
|
+
usePopupState,
|
|
15
|
+
} from '@elementor/ui';
|
|
16
|
+
import { __ } from '@wordpress/i18n';
|
|
17
|
+
|
|
18
|
+
import { useStyle } from '../../contexts/style-context';
|
|
19
|
+
import { CssClassMenu } from './css-class-menu';
|
|
20
|
+
|
|
21
|
+
type CssClassItemProps = {
|
|
22
|
+
id: string;
|
|
23
|
+
label: string;
|
|
24
|
+
provider: string;
|
|
25
|
+
isActive: boolean;
|
|
26
|
+
color: ChipOwnProps[ 'color' ];
|
|
27
|
+
chipProps: ReturnType< AutocompleteRenderGetTagProps >;
|
|
28
|
+
onClickActive: ( id: string ) => void;
|
|
29
|
+
renameLabel: ( newLabel: string ) => void;
|
|
30
|
+
validateLabel?: ( newLabel: string ) => string | undefined | null;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const CHIP_SIZE = 'tiny';
|
|
34
|
+
|
|
35
|
+
export function CssClassItem( {
|
|
36
|
+
id,
|
|
37
|
+
label,
|
|
38
|
+
provider,
|
|
39
|
+
isActive,
|
|
40
|
+
color: colorProp,
|
|
41
|
+
chipProps,
|
|
42
|
+
onClickActive,
|
|
43
|
+
renameLabel,
|
|
44
|
+
}: CssClassItemProps ) {
|
|
45
|
+
const { meta } = useStyle();
|
|
46
|
+
const popupState = usePopupState( { variant: 'popover' } );
|
|
47
|
+
const [ chipRef, setChipRef ] = useState< HTMLElement | null >( null );
|
|
48
|
+
const { onDelete, ...chipGroupProps } = chipProps;
|
|
49
|
+
|
|
50
|
+
const {
|
|
51
|
+
ref,
|
|
52
|
+
isEditing,
|
|
53
|
+
openEditMode,
|
|
54
|
+
error,
|
|
55
|
+
getProps: getEditableProps,
|
|
56
|
+
} = useEditable( {
|
|
57
|
+
value: label,
|
|
58
|
+
onSubmit: renameLabel,
|
|
59
|
+
validation: validateLabel,
|
|
60
|
+
} );
|
|
61
|
+
|
|
62
|
+
const color = error ? 'error' : colorProp;
|
|
63
|
+
|
|
64
|
+
const providerActions = stylesRepository.getProviderByKey( provider )?.actions;
|
|
65
|
+
const allowRename = Boolean( providerActions?.update );
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<>
|
|
69
|
+
<UnstableChipGroup ref={ setChipRef } { ...chipGroupProps } aria-label={ `Edit ${ label }` } role="group">
|
|
70
|
+
<Chip
|
|
71
|
+
size={ CHIP_SIZE }
|
|
72
|
+
label={
|
|
73
|
+
isEditing ? (
|
|
74
|
+
<EditableField ref={ ref } error={ error } { ...getEditableProps() } />
|
|
75
|
+
) : (
|
|
76
|
+
<EllipsisWithTooltip maxWidth="10ch" title={ label } as="div" />
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
variant={ isActive && ! meta.state ? 'filled' : 'standard' }
|
|
80
|
+
color={ color }
|
|
81
|
+
onClick={ () => {
|
|
82
|
+
if ( isActive && allowRename ) {
|
|
83
|
+
openEditMode();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
onClickActive( id );
|
|
87
|
+
} }
|
|
88
|
+
aria-pressed={ isActive }
|
|
89
|
+
sx={ {
|
|
90
|
+
'&.Mui-focusVisible': {
|
|
91
|
+
boxShadow: 'none !important',
|
|
92
|
+
},
|
|
93
|
+
} }
|
|
94
|
+
/>
|
|
95
|
+
{ ! isEditing && (
|
|
96
|
+
<Chip
|
|
97
|
+
size={ CHIP_SIZE }
|
|
98
|
+
label={
|
|
99
|
+
<Stack direction="row" gap={ 0.5 } alignItems="center">
|
|
100
|
+
{ isActive && meta.state && <Typography variant="inherit">{ meta.state }</Typography> }
|
|
101
|
+
<DotsVerticalIcon fontSize="inherit" />
|
|
102
|
+
</Stack>
|
|
103
|
+
}
|
|
104
|
+
variant="filled"
|
|
105
|
+
color={ color }
|
|
106
|
+
{ ...bindTrigger( popupState ) }
|
|
107
|
+
aria-label={ __( 'Open CSS Class Menu', 'elementor' ) }
|
|
108
|
+
/>
|
|
109
|
+
) }
|
|
110
|
+
</UnstableChipGroup>
|
|
111
|
+
<CssClassMenu
|
|
112
|
+
styleId={ id }
|
|
113
|
+
popupState={ popupState }
|
|
114
|
+
provider={ provider }
|
|
115
|
+
handleRename={ openEditMode }
|
|
116
|
+
anchorEl={ chipRef }
|
|
117
|
+
/>
|
|
118
|
+
</>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const validateLabel = ( newLabel: string ) => {
|
|
123
|
+
if ( ! stylesRepository.isLabelValid( newLabel ) ) {
|
|
124
|
+
return __( 'Format is not valid', 'elementor' );
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if ( stylesRepository.isLabelExist( newLabel ) ) {
|
|
128
|
+
return __( 'Existing name', 'elementor' );
|
|
129
|
+
}
|
|
130
|
+
};
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { type StyleDefinitionState } from '@elementor/editor-styles';
|
|
3
|
+
import { stylesRepository } from '@elementor/editor-styles-repository';
|
|
4
|
+
import { bindMenu, Divider, ListSubheader, Menu, MenuItem, type PopupState } from '@elementor/ui';
|
|
5
|
+
import { __ } from '@wordpress/i18n';
|
|
6
|
+
|
|
7
|
+
import { useStyle } from '../../contexts/style-context';
|
|
8
|
+
import { useUnapplyClass } from '../../hooks/use-unapply-class';
|
|
9
|
+
|
|
10
|
+
const STATES: NonNullable< StyleDefinitionState >[] = [ 'hover', 'focus', 'active' ];
|
|
11
|
+
|
|
12
|
+
type CssClassMenuProps = {
|
|
13
|
+
styleId: string;
|
|
14
|
+
provider: string;
|
|
15
|
+
popupState: PopupState;
|
|
16
|
+
handleRename: () => void;
|
|
17
|
+
anchorEl: HTMLElement | null;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export function CssClassMenu( { styleId, provider, popupState, handleRename, anchorEl }: CssClassMenuProps ) {
|
|
21
|
+
const handleKeyDown = ( e: React.KeyboardEvent< HTMLElement > ) => {
|
|
22
|
+
e.stopPropagation();
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<Menu
|
|
27
|
+
MenuListProps={ { dense: true } }
|
|
28
|
+
{ ...bindMenu( popupState ) }
|
|
29
|
+
anchorEl={ anchorEl }
|
|
30
|
+
anchorOrigin={ {
|
|
31
|
+
vertical: 'bottom',
|
|
32
|
+
horizontal: 'left',
|
|
33
|
+
} }
|
|
34
|
+
transformOrigin={ {
|
|
35
|
+
horizontal: 'left',
|
|
36
|
+
vertical: -4,
|
|
37
|
+
} }
|
|
38
|
+
onKeyDown={ handleKeyDown }
|
|
39
|
+
>
|
|
40
|
+
{ /* 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 */ }
|
|
41
|
+
{ getMenuItemsByProvider( { provider, styleId, handleRename, closeMenu: popupState.close } ) }
|
|
42
|
+
<ListSubheader sx={ { typography: 'caption', color: 'text.secondary', pb: 0.5, pt: 1 } }>
|
|
43
|
+
{ __( 'Pseudo selector', 'elementor' ) }
|
|
44
|
+
</ListSubheader>
|
|
45
|
+
{ STATES.map( ( state ) => {
|
|
46
|
+
return <StateMenuItem key={ state } state={ state } styleId={ styleId } />;
|
|
47
|
+
} ) }
|
|
48
|
+
</Menu>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function getMenuItemsByProvider( {
|
|
53
|
+
provider,
|
|
54
|
+
styleId,
|
|
55
|
+
handleRename,
|
|
56
|
+
closeMenu,
|
|
57
|
+
}: {
|
|
58
|
+
provider: string;
|
|
59
|
+
styleId: string;
|
|
60
|
+
handleRename: () => void;
|
|
61
|
+
closeMenu: () => void;
|
|
62
|
+
} ) {
|
|
63
|
+
const providerInstance = stylesRepository.getProviderByKey( provider );
|
|
64
|
+
const providerActions = providerInstance?.actions;
|
|
65
|
+
|
|
66
|
+
const [ canUpdate, canDelete ] = [ providerActions?.update, providerActions?.delete ];
|
|
67
|
+
|
|
68
|
+
const actions = [
|
|
69
|
+
canUpdate && <RenameClassMenuItem key="rename-class" handleRename={ handleRename } closeMenu={ closeMenu } />,
|
|
70
|
+
canDelete && <UnapplyClassMenuItem key="unapply-class" styleId={ styleId } />,
|
|
71
|
+
].filter( Boolean );
|
|
72
|
+
|
|
73
|
+
if ( actions.length ) {
|
|
74
|
+
actions.unshift(
|
|
75
|
+
<ListSubheader
|
|
76
|
+
key="provider-label"
|
|
77
|
+
sx={ { typography: 'caption', color: 'text.secondary', pb: 0.5, pt: 1 } }
|
|
78
|
+
>
|
|
79
|
+
{ providerInstance?.labels?.singular }
|
|
80
|
+
</ListSubheader>
|
|
81
|
+
);
|
|
82
|
+
actions.push( <Divider key="provider-actions-divider" /> );
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return actions;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
type StateMenuItemProps = {
|
|
89
|
+
state: StyleDefinitionState;
|
|
90
|
+
styleId: string;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
function StateMenuItem( { state, styleId, ...props }: StateMenuItemProps ) {
|
|
94
|
+
const { id: activeId, setId: setActiveId, setMetaState: setActiveMetaState, meta } = useStyle();
|
|
95
|
+
const { state: activeState } = meta;
|
|
96
|
+
|
|
97
|
+
const isActive = styleId === activeId;
|
|
98
|
+
const isSelected = state === activeState && isActive;
|
|
99
|
+
|
|
100
|
+
return (
|
|
101
|
+
<StyledMenuItem
|
|
102
|
+
{ ...props }
|
|
103
|
+
selected={ isSelected }
|
|
104
|
+
sx={ { textTransform: 'capitalize' } }
|
|
105
|
+
onClick={ () => {
|
|
106
|
+
if ( ! isActive ) {
|
|
107
|
+
setActiveId( styleId );
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
setActiveMetaState( state );
|
|
111
|
+
} }
|
|
112
|
+
>
|
|
113
|
+
{ state }
|
|
114
|
+
</StyledMenuItem>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function UnapplyClassMenuItem( { styleId, ...props }: { styleId: string } ) {
|
|
119
|
+
const unapplyClass = useUnapplyClass( styleId );
|
|
120
|
+
|
|
121
|
+
return (
|
|
122
|
+
<StyledMenuItem { ...props } onClick={ unapplyClass }>
|
|
123
|
+
{ __( 'Remove', 'elementor' ) }
|
|
124
|
+
</StyledMenuItem>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function RenameClassMenuItem( {
|
|
129
|
+
handleRename,
|
|
130
|
+
closeMenu,
|
|
131
|
+
...props
|
|
132
|
+
}: {
|
|
133
|
+
handleRename: () => void;
|
|
134
|
+
closeMenu: () => void;
|
|
135
|
+
} ) {
|
|
136
|
+
return (
|
|
137
|
+
<StyledMenuItem
|
|
138
|
+
{ ...props }
|
|
139
|
+
onClick={ () => {
|
|
140
|
+
closeMenu();
|
|
141
|
+
handleRename();
|
|
142
|
+
} }
|
|
143
|
+
>
|
|
144
|
+
{ __( 'Rename', 'elementor' ) }
|
|
145
|
+
</StyledMenuItem>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const StyledMenuItem = ( { ...props } ) => (
|
|
150
|
+
<MenuItem { ...props } sx={ { ...( props.sx ?? {} ), typography: 'caption', color: 'text.primary' } } />
|
|
151
|
+
);
|
|
@@ -1,37 +1,24 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { useId, useRef } from 'react';
|
|
3
2
|
import { getElementSetting, updateElementSettings, useElementSetting } from '@elementor/editor-elements';
|
|
4
3
|
import { classesPropTypeUtil, type ClassesPropValue } from '@elementor/editor-props';
|
|
5
4
|
import { type StyleDefinitionID } from '@elementor/editor-styles';
|
|
6
5
|
import {
|
|
7
6
|
ELEMENTS_STYLES_PROVIDER_KEY,
|
|
7
|
+
type StylesProvider,
|
|
8
8
|
stylesRepository,
|
|
9
9
|
type UpdateActionPayload,
|
|
10
|
-
useAllStylesByProvider,
|
|
11
10
|
useCreateActionsByProvider,
|
|
11
|
+
useProviders,
|
|
12
12
|
} from '@elementor/editor-styles-repository';
|
|
13
|
-
import { DotsVerticalIcon } from '@elementor/icons';
|
|
14
13
|
import { createLocation } from '@elementor/locations';
|
|
15
|
-
import {
|
|
16
|
-
type AutocompleteRenderGetTagProps,
|
|
17
|
-
bindTrigger,
|
|
18
|
-
Chip,
|
|
19
|
-
type ChipOwnProps,
|
|
20
|
-
Stack,
|
|
21
|
-
Typography,
|
|
22
|
-
UnstableChipGroup,
|
|
23
|
-
usePopupState,
|
|
24
|
-
} from '@elementor/ui';
|
|
14
|
+
import { Chip, Stack, Typography } from '@elementor/ui';
|
|
25
15
|
import { __ } from '@wordpress/i18n';
|
|
26
16
|
|
|
27
|
-
import { useClassesProp } from '
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
31
|
-
import {
|
|
32
|
-
import { CssClassMenu } from './css-class-menu';
|
|
33
|
-
import { EditableField, EditableFieldProvider, useEditableField } from './editable-field';
|
|
34
|
-
import { type Action, MultiCombobox, type Option } from './multi-combobox';
|
|
17
|
+
import { useClassesProp } from '../../contexts/classes-prop-context';
|
|
18
|
+
import { useElement } from '../../contexts/element-context';
|
|
19
|
+
import { useStyle } from '../../contexts/style-context';
|
|
20
|
+
import { type Action, MultiCombobox, type Option } from '../multi-combobox';
|
|
21
|
+
import { CssClassItem } from './css-class-item';
|
|
35
22
|
|
|
36
23
|
const ID = 'elementor-css-class-selector';
|
|
37
24
|
const TAGS_LIMIT = 8;
|
|
@@ -65,14 +52,13 @@ export function CssClassSelector() {
|
|
|
65
52
|
const actions = useCreateActions( { pushAppliedId, setActiveId } );
|
|
66
53
|
|
|
67
54
|
const handleApply = useHandleApply( appliedIds, setAppliedIds );
|
|
68
|
-
const handleActivate = ( { value }: Option ) => setActiveId( value );
|
|
69
55
|
|
|
70
56
|
const applied = useAppliedOptions( options, appliedIds );
|
|
71
57
|
const active = applied.find( ( option ) => option.value === activeId ) ?? EMPTY_OPTION;
|
|
72
58
|
|
|
73
59
|
return (
|
|
74
60
|
<Stack gap={ 1 } p={ 2 }>
|
|
75
|
-
<Stack direction="row" gap={ 1 } alignItems="
|
|
61
|
+
<Stack direction="row" gap={ 1 } alignItems="center" justifyContent="space-between">
|
|
76
62
|
<Typography component="label" variant="caption" htmlFor={ ID }>
|
|
77
63
|
{ __( 'CSS Classes', 'elementor' ) }
|
|
78
64
|
</Typography>
|
|
@@ -95,34 +81,26 @@ export function CssClassSelector() {
|
|
|
95
81
|
values.map( ( value, index ) => {
|
|
96
82
|
const chipProps = getTagProps( { index } );
|
|
97
83
|
const isActive = value.value === active?.value;
|
|
84
|
+
const isElementsProvider = value.provider === ELEMENTS_STYLES_PROVIDER_KEY;
|
|
98
85
|
|
|
99
86
|
const renameLabel = ( newLabel: string ) => {
|
|
100
87
|
return updateClassByProvider( value.provider, { label: newLabel, id: value.value } );
|
|
101
88
|
};
|
|
102
89
|
|
|
103
90
|
return (
|
|
104
|
-
<
|
|
91
|
+
<CssClassItem
|
|
105
92
|
key={ chipProps.key }
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
label={ value.label }
|
|
118
|
-
id={ value.value }
|
|
119
|
-
isActive={ isActive }
|
|
120
|
-
isGlobal={ value.color === 'global' }
|
|
121
|
-
color={ isActive && value.color ? value.color : 'default' }
|
|
122
|
-
chipProps={ chipProps }
|
|
123
|
-
onClickActive={ () => handleActivate( value ) }
|
|
124
|
-
/>
|
|
125
|
-
</EditableFieldProvider>
|
|
93
|
+
label={ value.label }
|
|
94
|
+
provider={ value.provider }
|
|
95
|
+
id={ value.value }
|
|
96
|
+
isActive={ isActive }
|
|
97
|
+
color={ isActive && value.color ? value.color : 'default' }
|
|
98
|
+
chipProps={ chipProps }
|
|
99
|
+
// There is only a single local style, which might not exist, so setting it to
|
|
100
|
+
// `null` will either return the actual style or the fallback one.
|
|
101
|
+
onClickActive={ () => setActiveId( isElementsProvider ? null : value.value ) }
|
|
102
|
+
renameLabel={ renameLabel }
|
|
103
|
+
/>
|
|
126
104
|
);
|
|
127
105
|
} )
|
|
128
106
|
}
|
|
@@ -131,91 +109,6 @@ export function CssClassSelector() {
|
|
|
131
109
|
);
|
|
132
110
|
}
|
|
133
111
|
|
|
134
|
-
type CssClassItemProps = {
|
|
135
|
-
id: string;
|
|
136
|
-
label: string;
|
|
137
|
-
isActive: boolean;
|
|
138
|
-
isGlobal: boolean;
|
|
139
|
-
color: ChipOwnProps[ 'color' ];
|
|
140
|
-
chipProps: ReturnType< AutocompleteRenderGetTagProps >;
|
|
141
|
-
onClickActive: ( id: string ) => void;
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
const CHIP_SIZE = 'tiny';
|
|
145
|
-
|
|
146
|
-
export function CssClassItem( {
|
|
147
|
-
id,
|
|
148
|
-
label,
|
|
149
|
-
isActive,
|
|
150
|
-
isGlobal,
|
|
151
|
-
color: colorProp,
|
|
152
|
-
chipProps,
|
|
153
|
-
onClickActive,
|
|
154
|
-
}: CssClassItemProps ) {
|
|
155
|
-
const { meta } = useStyle();
|
|
156
|
-
// TODO - resolve the useId issue with invalid characters upon CSS selectors (EDS-1089)
|
|
157
|
-
const popupId = useId().replace( /:/g, '_' );
|
|
158
|
-
const popupState = usePopupState( { variant: 'popover', popupId } );
|
|
159
|
-
const chipRef = useRef< Element >( null );
|
|
160
|
-
const { onDelete, ...chipGroupProps } = chipProps;
|
|
161
|
-
const { isEditing, openEditMode, error, submitting } = useEditableField();
|
|
162
|
-
|
|
163
|
-
const color = error ? 'error' : colorProp;
|
|
164
|
-
|
|
165
|
-
return (
|
|
166
|
-
<CssClassItemProvider styleId={ id } isActive={ isActive } isGlobal={ isGlobal }>
|
|
167
|
-
<UnstableChipGroup ref={ chipRef } { ...chipGroupProps } aria-label={ `Edit ${ label }` } role="group">
|
|
168
|
-
<Chip
|
|
169
|
-
disabled={ submitting }
|
|
170
|
-
size={ CHIP_SIZE }
|
|
171
|
-
label={
|
|
172
|
-
<EditableField
|
|
173
|
-
onDoubleClick={ () => {
|
|
174
|
-
if ( ! isActive ) {
|
|
175
|
-
openEditMode();
|
|
176
|
-
}
|
|
177
|
-
} }
|
|
178
|
-
onClick={ () => {
|
|
179
|
-
if ( isActive ) {
|
|
180
|
-
openEditMode();
|
|
181
|
-
}
|
|
182
|
-
} }
|
|
183
|
-
>
|
|
184
|
-
<ConditionalTooltipWrapper maxWidth="10ch" title={ label } />
|
|
185
|
-
</EditableField>
|
|
186
|
-
}
|
|
187
|
-
variant={ isActive && ! meta.state ? 'filled' : 'standard' }
|
|
188
|
-
color={ color }
|
|
189
|
-
onClick={ () => onClickActive( id ) }
|
|
190
|
-
aria-pressed={ isActive }
|
|
191
|
-
sx={ {
|
|
192
|
-
'&.Mui-focusVisible': {
|
|
193
|
-
boxShadow: 'none !important',
|
|
194
|
-
},
|
|
195
|
-
} }
|
|
196
|
-
/>
|
|
197
|
-
{ ! isEditing && (
|
|
198
|
-
<Chip
|
|
199
|
-
disabled={ submitting }
|
|
200
|
-
size={ CHIP_SIZE }
|
|
201
|
-
label={
|
|
202
|
-
<Stack direction="row" gap={ 0.5 } alignItems="center">
|
|
203
|
-
{ isActive && meta.state && <Typography variant="inherit">{ meta.state }</Typography> }
|
|
204
|
-
<DotsVerticalIcon fontSize="inherit" />
|
|
205
|
-
</Stack>
|
|
206
|
-
}
|
|
207
|
-
variant="filled"
|
|
208
|
-
color={ color }
|
|
209
|
-
{ ...bindTrigger( popupState ) }
|
|
210
|
-
aria-label={ __( 'Open CSS Class Menu', 'elementor' ) }
|
|
211
|
-
/>
|
|
212
|
-
) }
|
|
213
|
-
</UnstableChipGroup>
|
|
214
|
-
<CssClassMenu popupState={ popupState } containerRef={ chipRef } />
|
|
215
|
-
</CssClassItemProvider>
|
|
216
|
-
);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
112
|
const updateClassByProvider = ( provider: string, data: UpdateActionPayload ) => {
|
|
220
113
|
const providerInstance = stylesRepository.getProviderByKey( provider );
|
|
221
114
|
|
|
@@ -226,34 +119,16 @@ const updateClassByProvider = ( provider: string, data: UpdateActionPayload ) =>
|
|
|
226
119
|
return providerInstance.actions.update?.( data );
|
|
227
120
|
};
|
|
228
121
|
|
|
229
|
-
const VALID_SELECTOR_REGEX = /^[a-zA-Z0-9_-]+$/;
|
|
230
|
-
|
|
231
|
-
const renameValidation = ( newLabel: string, options: Option[] ) => {
|
|
232
|
-
if ( isNameExist( newLabel, options ) ) {
|
|
233
|
-
return __( 'Existing name', 'elementor' );
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
if ( isCharactersNotSupported( newLabel ) ) {
|
|
237
|
-
return __( 'Format is not valid', 'elementor' );
|
|
238
|
-
}
|
|
239
|
-
};
|
|
240
|
-
|
|
241
|
-
const isNameExist = ( newLabel: string, options: Option[] ) => {
|
|
242
|
-
if ( ! options?.length ) {
|
|
243
|
-
return false;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
return options.some( ( option ) => option.label.toLowerCase() === newLabel.toLowerCase() );
|
|
247
|
-
};
|
|
248
|
-
|
|
249
|
-
const isCharactersNotSupported = ( newLabel: string ) => ! VALID_SELECTOR_REGEX.test( newLabel );
|
|
250
|
-
|
|
251
122
|
function useOptions() {
|
|
252
123
|
const { element } = useElement();
|
|
253
124
|
|
|
254
|
-
|
|
255
|
-
|
|
125
|
+
const isProviderEditable = ( provider: StylesProvider ) => !! provider.actions.updateProps;
|
|
126
|
+
|
|
127
|
+
return useProviders()
|
|
128
|
+
.filter( isProviderEditable )
|
|
129
|
+
.flatMap< StyleDefOption >( ( provider ) => {
|
|
256
130
|
const isElements = provider.key === ELEMENTS_STYLES_PROVIDER_KEY;
|
|
131
|
+
const styleDefs = provider.actions.get( { elementId: element.id } );
|
|
257
132
|
|
|
258
133
|
// Add empty local option for elements, as fallback.
|
|
259
134
|
if ( isElements && styleDefs.length === 0 ) {
|
|
@@ -270,8 +145,7 @@ function useOptions() {
|
|
|
270
145
|
group: provider.labels?.plural,
|
|
271
146
|
};
|
|
272
147
|
} );
|
|
273
|
-
}
|
|
274
|
-
);
|
|
148
|
+
} );
|
|
275
149
|
}
|
|
276
150
|
|
|
277
151
|
function useCreateActions( {
|
|
@@ -285,15 +159,15 @@ function useCreateActions( {
|
|
|
285
159
|
return {
|
|
286
160
|
// translators: %s is the label of the new class.
|
|
287
161
|
label: ( value ) => __( 'Create new "%s"', 'elementor' ).replace( '%s', value ),
|
|
288
|
-
apply:
|
|
289
|
-
const
|
|
162
|
+
apply: ( label ) => {
|
|
163
|
+
const createdId = create( label );
|
|
290
164
|
|
|
291
|
-
if ( !
|
|
165
|
+
if ( ! createdId ) {
|
|
292
166
|
return;
|
|
293
167
|
}
|
|
294
168
|
|
|
295
|
-
pushAppliedId(
|
|
296
|
-
setActiveId(
|
|
169
|
+
pushAppliedId( createdId );
|
|
170
|
+
setActiveId( createdId );
|
|
297
171
|
},
|
|
298
172
|
condition: ( options, inputValue ) => {
|
|
299
173
|
const isUniqueLabel = ! options.some(
|
|
@@ -5,7 +5,7 @@ import { __ } from '@wordpress/i18n';
|
|
|
5
5
|
|
|
6
6
|
import { StylesField } from '../../../controls-registry/styles-field';
|
|
7
7
|
|
|
8
|
-
type Displays = 'block' | 'flex';
|
|
8
|
+
type Displays = 'block' | 'flex' | 'inline-block';
|
|
9
9
|
|
|
10
10
|
export const DisplayField = () => {
|
|
11
11
|
const options: ToggleButtonGroupItem< Displays >[] = [
|
|
@@ -13,11 +13,19 @@ export const DisplayField = () => {
|
|
|
13
13
|
value: 'block',
|
|
14
14
|
renderContent: () => __( 'Block', 'elementor' ),
|
|
15
15
|
label: __( 'Block', 'elementor' ),
|
|
16
|
+
showTooltip: true,
|
|
16
17
|
},
|
|
17
18
|
{
|
|
18
19
|
value: 'flex',
|
|
19
20
|
renderContent: () => __( 'Flex', 'elementor' ),
|
|
20
21
|
label: __( 'Flex', 'elementor' ),
|
|
22
|
+
showTooltip: true,
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
value: 'inline-block',
|
|
26
|
+
renderContent: () => __( 'In-blk', 'elementor' ),
|
|
27
|
+
label: __( 'Inline-block', 'elementor' ),
|
|
28
|
+
showTooltip: true,
|
|
21
29
|
},
|
|
22
30
|
];
|
|
23
31
|
|
|
@@ -17,11 +17,11 @@ import { useStylesField } from '../../../hooks/use-styles-field';
|
|
|
17
17
|
|
|
18
18
|
type GroupControlItemOption = 'first' | 'last' | 'custom';
|
|
19
19
|
|
|
20
|
-
export const FIRST_DEFAULT_VALUE = -99999
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
export const FIRST_DEFAULT_VALUE = -99999;
|
|
21
|
+
export const LAST_DEFAULT_VALUE = 99999;
|
|
22
|
+
const FIRST = 'first';
|
|
23
|
+
const LAST = 'last';
|
|
24
|
+
const CUSTOM = 'custom';
|
|
25
25
|
|
|
26
26
|
const orderValueMap = {
|
|
27
27
|
[ FIRST ]: FIRST_DEFAULT_VALUE,
|
|
@@ -6,7 +6,7 @@ import { __ } from '@wordpress/i18n';
|
|
|
6
6
|
|
|
7
7
|
import { StylesField } from '../../../controls-registry/styles-field';
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
type Side = 'left' | 'right' | 'top' | 'bottom';
|
|
10
10
|
|
|
11
11
|
const sideIcons = {
|
|
12
12
|
left: <SideLeftIcon fontSize={ 'tiny' } />,
|