@elementor/editor-editing-panel 1.17.1 → 1.18.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 +58 -0
- package/dist/index.js +692 -501
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +610 -419
- package/dist/index.mjs.map +1 -1
- package/package.json +12 -11
- package/src/components/css-classes/css-class-item.tsx +32 -10
- package/src/components/css-classes/css-class-menu.tsx +25 -9
- package/src/components/css-classes/css-class-selector.tsx +13 -7
- package/src/components/editing-panel-hooks.tsx +0 -2
- package/src/components/editing-panel.tsx +2 -2
- package/src/components/multi-combobox.tsx +9 -4
- package/src/components/style-sections/border-section/border-radius-field.tsx +6 -5
- package/src/components/style-sections/position-section/dimensions-field.tsx +2 -2
- package/src/components/style-sections/spacing-section/spacing-section.tsx +4 -4
- package/src/components/style-sections/typography-section/font-family-field.tsx +2 -46
- package/src/components/style-sections/typography-section/font-style-field.tsx +1 -1
- package/src/components/style-sections/typography-section/hooks/use-font-families.ts +52 -0
- package/src/components/style-sections/typography-section/text-decoration-field.tsx +40 -89
- package/src/components/style-tab.tsx +34 -33
- package/src/contexts/styles-inheritance-context.tsx +65 -0
- package/src/controls-registry/control.tsx +3 -1
- package/src/dynamics/components/dynamic-selection.tsx +111 -74
- package/src/init.ts +6 -0
- package/src/sync/types.ts +1 -1
- package/src/hooks/use-close-editor-panel.ts +0 -23
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elementor/editor-editing-panel",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.18.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"author": "Elementor Team",
|
|
6
6
|
"homepage": "https://elementor.com/",
|
|
@@ -39,16 +39,17 @@
|
|
|
39
39
|
"dev": "tsup --config=../../tsup.dev.ts"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@elementor/editor": "0.18.
|
|
43
|
-
"@elementor/editor-controls": "0.
|
|
44
|
-
"@elementor/editor-
|
|
45
|
-
"@elementor/editor-
|
|
46
|
-
"@elementor/editor-
|
|
47
|
-
"@elementor/editor-
|
|
48
|
-
"@elementor/editor-
|
|
49
|
-
"@elementor/editor-styles
|
|
50
|
-
"@elementor/editor-
|
|
51
|
-
"@elementor/editor-
|
|
42
|
+
"@elementor/editor": "0.18.3",
|
|
43
|
+
"@elementor/editor-controls": "0.17.0",
|
|
44
|
+
"@elementor/editor-current-user": "0.3.0",
|
|
45
|
+
"@elementor/editor-elements": "0.6.3",
|
|
46
|
+
"@elementor/editor-panels": "0.13.0",
|
|
47
|
+
"@elementor/editor-props": "0.11.0",
|
|
48
|
+
"@elementor/editor-responsive": "0.13.3",
|
|
49
|
+
"@elementor/editor-styles": "0.6.3",
|
|
50
|
+
"@elementor/editor-styles-repository": "0.7.6",
|
|
51
|
+
"@elementor/editor-ui": "0.4.2",
|
|
52
|
+
"@elementor/editor-v1-adapters": "0.11.0",
|
|
52
53
|
"@elementor/icons": "1.37.0",
|
|
53
54
|
"@elementor/locations": "0.7.6",
|
|
54
55
|
"@elementor/menus": "0.1.3",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { useState } from 'react';
|
|
2
|
+
import { type ReactElement, useState } from 'react';
|
|
3
3
|
import { stylesRepository } from '@elementor/editor-styles-repository';
|
|
4
4
|
import { EditableField, EllipsisWithTooltip, useEditable } from '@elementor/editor-ui';
|
|
5
5
|
import { DotsVerticalIcon } from '@elementor/icons';
|
|
@@ -19,13 +19,14 @@ import { useStyle } from '../../contexts/style-context';
|
|
|
19
19
|
import { CssClassMenu } from './css-class-menu';
|
|
20
20
|
|
|
21
21
|
type CssClassItemProps = {
|
|
22
|
-
id: string;
|
|
22
|
+
id: string | null;
|
|
23
23
|
label: string;
|
|
24
24
|
provider: string;
|
|
25
25
|
isActive: boolean;
|
|
26
26
|
color: ChipOwnProps[ 'color' ];
|
|
27
|
+
icon: ReactElement | null;
|
|
27
28
|
chipProps: ReturnType< AutocompleteRenderGetTagProps >;
|
|
28
|
-
onClickActive: ( id: string ) => void;
|
|
29
|
+
onClickActive: ( id: string | null ) => void;
|
|
29
30
|
renameLabel: ( newLabel: string ) => void;
|
|
30
31
|
validateLabel?: ( newLabel: string ) => string | undefined | null;
|
|
31
32
|
};
|
|
@@ -38,11 +39,12 @@ export function CssClassItem( {
|
|
|
38
39
|
provider,
|
|
39
40
|
isActive,
|
|
40
41
|
color: colorProp,
|
|
42
|
+
icon,
|
|
41
43
|
chipProps,
|
|
42
44
|
onClickActive,
|
|
43
45
|
renameLabel,
|
|
44
46
|
}: CssClassItemProps ) {
|
|
45
|
-
const { meta } = useStyle();
|
|
47
|
+
const { meta, setMetaState } = useStyle();
|
|
46
48
|
const popupState = usePopupState( { variant: 'popover' } );
|
|
47
49
|
const [ chipRef, setChipRef ] = useState< HTMLElement | null >( null );
|
|
48
50
|
const { onDelete, ...chipGroupProps } = chipProps;
|
|
@@ -64,6 +66,8 @@ export function CssClassItem( {
|
|
|
64
66
|
const providerActions = stylesRepository.getProviderByKey( provider )?.actions;
|
|
65
67
|
const allowRename = Boolean( providerActions?.update );
|
|
66
68
|
|
|
69
|
+
const isShowingState = isActive && meta.state;
|
|
70
|
+
|
|
67
71
|
return (
|
|
68
72
|
<>
|
|
69
73
|
<UnstableChipGroup ref={ setChipRef } { ...chipGroupProps } aria-label={ `Edit ${ label }` } role="group">
|
|
@@ -76,17 +80,26 @@ export function CssClassItem( {
|
|
|
76
80
|
<EllipsisWithTooltip maxWidth="10ch" title={ label } as="div" />
|
|
77
81
|
)
|
|
78
82
|
}
|
|
79
|
-
variant={ isActive && ! meta.state ? 'filled' : 'standard' }
|
|
83
|
+
variant={ isActive && ! meta.state && ! isEditing ? 'filled' : 'standard' }
|
|
84
|
+
shape="rounded"
|
|
85
|
+
icon={ icon }
|
|
80
86
|
color={ color }
|
|
81
87
|
onClick={ () => {
|
|
82
|
-
if (
|
|
88
|
+
if ( isShowingState ) {
|
|
89
|
+
setMetaState( null );
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if ( allowRename && isActive ) {
|
|
83
94
|
openEditMode();
|
|
95
|
+
return;
|
|
84
96
|
}
|
|
85
97
|
|
|
86
98
|
onClickActive( id );
|
|
87
99
|
} }
|
|
88
100
|
aria-pressed={ isActive }
|
|
89
101
|
sx={ {
|
|
102
|
+
cursor: isActive && allowRename && ! isShowingState ? 'text' : 'pointer',
|
|
90
103
|
'&.Mui-focusVisible': {
|
|
91
104
|
boxShadow: 'none !important',
|
|
92
105
|
},
|
|
@@ -94,17 +107,26 @@ export function CssClassItem( {
|
|
|
94
107
|
/>
|
|
95
108
|
{ ! isEditing && (
|
|
96
109
|
<Chip
|
|
110
|
+
icon={ isShowingState ? undefined : <DotsVerticalIcon fontSize="tiny" /> }
|
|
97
111
|
size={ CHIP_SIZE }
|
|
98
112
|
label={
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
113
|
+
isShowingState ? (
|
|
114
|
+
<Stack direction="row" gap={ 0.5 } alignItems="center">
|
|
115
|
+
<Typography variant="inherit">{ meta.state }</Typography>
|
|
116
|
+
<DotsVerticalIcon fontSize="tiny" />
|
|
117
|
+
</Stack>
|
|
118
|
+
) : undefined
|
|
103
119
|
}
|
|
104
120
|
variant="filled"
|
|
121
|
+
shape="rounded"
|
|
105
122
|
color={ color }
|
|
106
123
|
{ ...bindTrigger( popupState ) }
|
|
107
124
|
aria-label={ __( 'Open CSS Class Menu', 'elementor' ) }
|
|
125
|
+
sx={ {
|
|
126
|
+
paddingRight: 0,
|
|
127
|
+
...( ! isShowingState ? { paddingLeft: 0 } : {} ),
|
|
128
|
+
'.MuiChip-label': isShowingState ? { paddingRight: 0 } : { padding: 0 },
|
|
129
|
+
} }
|
|
108
130
|
/>
|
|
109
131
|
) }
|
|
110
132
|
</UnstableChipGroup>
|
|
@@ -10,7 +10,7 @@ import { useUnapplyClass } from '../../hooks/use-unapply-class';
|
|
|
10
10
|
const STATES: NonNullable< StyleDefinitionState >[] = [ 'hover', 'focus', 'active' ];
|
|
11
11
|
|
|
12
12
|
type CssClassMenuProps = {
|
|
13
|
-
styleId: string;
|
|
13
|
+
styleId: string | null;
|
|
14
14
|
provider: string;
|
|
15
15
|
popupState: PopupState;
|
|
16
16
|
handleRename: () => void;
|
|
@@ -42,8 +42,11 @@ export function CssClassMenu( { styleId, provider, popupState, handleRename, anc
|
|
|
42
42
|
<ListSubheader sx={ { typography: 'caption', color: 'text.secondary', pb: 0.5, pt: 1 } }>
|
|
43
43
|
{ __( 'Pseudo classes', 'elementor' ) }
|
|
44
44
|
</ListSubheader>
|
|
45
|
+
<StateMenuItem key="normal" state={ null } styleId={ styleId } closeMenu={ popupState.close } />
|
|
45
46
|
{ STATES.map( ( state ) => {
|
|
46
|
-
return
|
|
47
|
+
return (
|
|
48
|
+
<StateMenuItem key={ state } state={ state } styleId={ styleId } closeMenu={ popupState.close } />
|
|
49
|
+
);
|
|
47
50
|
} ) }
|
|
48
51
|
</Menu>
|
|
49
52
|
);
|
|
@@ -56,10 +59,14 @@ function getMenuItemsByProvider( {
|
|
|
56
59
|
closeMenu,
|
|
57
60
|
}: {
|
|
58
61
|
provider: string;
|
|
59
|
-
styleId: string;
|
|
62
|
+
styleId: string | null;
|
|
60
63
|
handleRename: () => void;
|
|
61
64
|
closeMenu: () => void;
|
|
62
65
|
} ) {
|
|
66
|
+
if ( ! styleId ) {
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
|
|
63
70
|
const providerInstance = stylesRepository.getProviderByKey( provider );
|
|
64
71
|
const providerActions = providerInstance?.actions;
|
|
65
72
|
|
|
@@ -67,7 +74,7 @@ function getMenuItemsByProvider( {
|
|
|
67
74
|
|
|
68
75
|
const actions = [
|
|
69
76
|
canUpdate && <RenameClassMenuItem key="rename-class" handleRename={ handleRename } closeMenu={ closeMenu } />,
|
|
70
|
-
canDelete && <UnapplyClassMenuItem key="unapply-class" styleId={ styleId } />,
|
|
77
|
+
canDelete && <UnapplyClassMenuItem key="unapply-class" styleId={ styleId } closeMenu={ closeMenu } />,
|
|
71
78
|
].filter( Boolean );
|
|
72
79
|
|
|
73
80
|
if ( actions.length ) {
|
|
@@ -87,10 +94,11 @@ function getMenuItemsByProvider( {
|
|
|
87
94
|
|
|
88
95
|
type StateMenuItemProps = {
|
|
89
96
|
state: StyleDefinitionState;
|
|
90
|
-
styleId: string;
|
|
97
|
+
styleId: string | null;
|
|
98
|
+
closeMenu: () => void;
|
|
91
99
|
};
|
|
92
100
|
|
|
93
|
-
function StateMenuItem( { state, styleId, ...props }: StateMenuItemProps ) {
|
|
101
|
+
function StateMenuItem( { state, styleId, closeMenu, ...props }: StateMenuItemProps ) {
|
|
94
102
|
const { id: activeId, setId: setActiveId, setMetaState: setActiveMetaState, meta } = useStyle();
|
|
95
103
|
const { state: activeState } = meta;
|
|
96
104
|
|
|
@@ -108,18 +116,26 @@ function StateMenuItem( { state, styleId, ...props }: StateMenuItemProps ) {
|
|
|
108
116
|
}
|
|
109
117
|
|
|
110
118
|
setActiveMetaState( state );
|
|
119
|
+
|
|
120
|
+
closeMenu();
|
|
111
121
|
} }
|
|
112
122
|
>
|
|
113
|
-
{ state }
|
|
123
|
+
{ state ? state : 'Normal' }
|
|
114
124
|
</StyledMenuItem>
|
|
115
125
|
);
|
|
116
126
|
}
|
|
117
127
|
|
|
118
|
-
function UnapplyClassMenuItem( { styleId, ...props }: { styleId: string } ) {
|
|
128
|
+
function UnapplyClassMenuItem( { styleId, closeMenu, ...props }: { styleId: string; closeMenu: () => void } ) {
|
|
119
129
|
const unapplyClass = useUnapplyClass( styleId );
|
|
120
130
|
|
|
121
131
|
return (
|
|
122
|
-
<StyledMenuItem
|
|
132
|
+
<StyledMenuItem
|
|
133
|
+
{ ...props }
|
|
134
|
+
onClick={ () => {
|
|
135
|
+
unapplyClass();
|
|
136
|
+
closeMenu();
|
|
137
|
+
} }
|
|
138
|
+
>
|
|
123
139
|
{ __( 'Remove', 'elementor' ) }
|
|
124
140
|
</StyledMenuItem>
|
|
125
141
|
);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
+
import { type ReactElement } from 'react';
|
|
2
3
|
import { getElementSetting, updateElementSettings, useElementSetting } from '@elementor/editor-elements';
|
|
3
4
|
import { classesPropTypeUtil, type ClassesPropValue } from '@elementor/editor-props';
|
|
4
5
|
import { type StyleDefinitionID } from '@elementor/editor-styles';
|
|
@@ -10,6 +11,7 @@ import {
|
|
|
10
11
|
useCreateActionsByProvider,
|
|
11
12
|
useProviders,
|
|
12
13
|
} from '@elementor/editor-styles-repository';
|
|
14
|
+
import { MapPinIcon } from '@elementor/icons';
|
|
13
15
|
import { createLocation } from '@elementor/locations';
|
|
14
16
|
import { Chip, Stack, Typography } from '@elementor/ui';
|
|
15
17
|
import { __ } from '@wordpress/i18n';
|
|
@@ -21,18 +23,20 @@ import { type Action, MultiCombobox, type Option } from '../multi-combobox';
|
|
|
21
23
|
import { CssClassItem } from './css-class-item';
|
|
22
24
|
|
|
23
25
|
const ID = 'elementor-css-class-selector';
|
|
24
|
-
const TAGS_LIMIT =
|
|
26
|
+
const TAGS_LIMIT = 50;
|
|
25
27
|
|
|
26
28
|
type StyleDefOption = Option & {
|
|
27
29
|
color: 'primary' | 'global';
|
|
30
|
+
icon: ReactElement | null;
|
|
28
31
|
provider: string;
|
|
29
32
|
};
|
|
30
33
|
|
|
31
34
|
const EMPTY_OPTION = {
|
|
32
35
|
label: __( 'local', 'elementor' ),
|
|
33
|
-
value:
|
|
36
|
+
value: null,
|
|
34
37
|
fixed: true,
|
|
35
38
|
color: 'primary',
|
|
39
|
+
icon: <MapPinIcon />,
|
|
36
40
|
provider: ELEMENTS_STYLES_PROVIDER_KEY,
|
|
37
41
|
} satisfies StyleDefOption;
|
|
38
42
|
|
|
@@ -81,9 +85,11 @@ export function CssClassSelector() {
|
|
|
81
85
|
values.map( ( value, index ) => {
|
|
82
86
|
const chipProps = getTagProps( { index } );
|
|
83
87
|
const isActive = value.value === active?.value;
|
|
84
|
-
const isElementsProvider = value.provider === ELEMENTS_STYLES_PROVIDER_KEY;
|
|
85
88
|
|
|
86
89
|
const renameLabel = ( newLabel: string ) => {
|
|
90
|
+
if ( ! value.value ) {
|
|
91
|
+
throw new Error( `Cannot rename a class without style id` );
|
|
92
|
+
}
|
|
87
93
|
return updateClassByProvider( value.provider, { label: newLabel, id: value.value } );
|
|
88
94
|
};
|
|
89
95
|
|
|
@@ -95,10 +101,9 @@ export function CssClassSelector() {
|
|
|
95
101
|
id={ value.value }
|
|
96
102
|
isActive={ isActive }
|
|
97
103
|
color={ isActive && value.color ? value.color : 'default' }
|
|
104
|
+
icon={ value.icon }
|
|
98
105
|
chipProps={ chipProps }
|
|
99
|
-
|
|
100
|
-
// `null` will either return the actual style or the fallback one.
|
|
101
|
-
onClickActive={ () => setActiveId( isElementsProvider ? null : value.value ) }
|
|
106
|
+
onClickActive={ () => setActiveId( value.value ) }
|
|
102
107
|
renameLabel={ renameLabel }
|
|
103
108
|
/>
|
|
104
109
|
);
|
|
@@ -141,6 +146,7 @@ function useOptions() {
|
|
|
141
146
|
value: styleDef.id,
|
|
142
147
|
fixed: isElements,
|
|
143
148
|
color: isElements ? 'primary' : 'global',
|
|
149
|
+
icon: isElements ? <MapPinIcon /> : null,
|
|
144
150
|
provider: provider.key,
|
|
145
151
|
group: provider.labels?.plural,
|
|
146
152
|
};
|
|
@@ -178,7 +184,7 @@ function useCreateActions( {
|
|
|
178
184
|
}
|
|
179
185
|
|
|
180
186
|
function useAppliedOptions( options: StyleDefOption[], appliedIds: StyleDefinitionID[] ) {
|
|
181
|
-
const applied = options.filter( ( option ) => appliedIds.includes( option.value ) );
|
|
187
|
+
const applied = options.filter( ( option ) => option.value && appliedIds.includes( option.value ) );
|
|
182
188
|
|
|
183
189
|
const hasElementsProviderStyleApplied = applied.some(
|
|
184
190
|
( option ) => option.provider === ELEMENTS_STYLES_PROVIDER_KEY
|
|
@@ -2,7 +2,7 @@ import * as React from 'react';
|
|
|
2
2
|
import { ControlActionsProvider, ControlReplacementProvider } from '@elementor/editor-controls';
|
|
3
3
|
import { useSelectedElement } from '@elementor/editor-elements';
|
|
4
4
|
import { Panel, PanelBody, PanelHeader, PanelHeaderTitle } from '@elementor/editor-panels';
|
|
5
|
-
import {
|
|
5
|
+
import { AtomIcon } from '@elementor/icons';
|
|
6
6
|
import { SessionStorageProvider } from '@elementor/session';
|
|
7
7
|
import { ErrorBoundary } from '@elementor/ui';
|
|
8
8
|
import { __ } from '@wordpress/i18n';
|
|
@@ -33,7 +33,7 @@ export const EditingPanel = () => {
|
|
|
33
33
|
<Panel>
|
|
34
34
|
<PanelHeader>
|
|
35
35
|
<PanelHeaderTitle>{ panelTitle }</PanelHeaderTitle>
|
|
36
|
-
<
|
|
36
|
+
<AtomIcon fontSize="small" sx={ { color: 'text.tertiary' } } />
|
|
37
37
|
</PanelHeader>
|
|
38
38
|
<PanelBody>
|
|
39
39
|
<ControlActionsProvider items={ menuItems }>
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
|
|
13
13
|
export type Option = {
|
|
14
14
|
label: string;
|
|
15
|
-
value: string;
|
|
15
|
+
value: string | null;
|
|
16
16
|
fixed?: boolean;
|
|
17
17
|
group?: string;
|
|
18
18
|
key?: string;
|
|
@@ -72,7 +72,7 @@ export function MultiCombobox< TOption extends Option >( {
|
|
|
72
72
|
if ( reason === 'createOption' ) {
|
|
73
73
|
const [ firstAction ] = filterActions( actions, { options, inputValue: inputValue ?? '' } );
|
|
74
74
|
|
|
75
|
-
if ( firstAction ) {
|
|
75
|
+
if ( firstAction.value ) {
|
|
76
76
|
return run( firstAction.apply, firstAction.value );
|
|
77
77
|
}
|
|
78
78
|
}
|
|
@@ -80,7 +80,7 @@ export function MultiCombobox< TOption extends Option >( {
|
|
|
80
80
|
// Handles the user's action selection when triggered.
|
|
81
81
|
const action = optionsAndActions.find( ( value ) => isAction( value ) );
|
|
82
82
|
|
|
83
|
-
if ( reason === 'selectOption' && action ) {
|
|
83
|
+
if ( reason === 'selectOption' && action?.value ) {
|
|
84
84
|
return run( action.apply, action.value );
|
|
85
85
|
}
|
|
86
86
|
|
|
@@ -95,7 +95,7 @@ export function MultiCombobox< TOption extends Option >( {
|
|
|
95
95
|
return option;
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
return option.key ?? option.value;
|
|
98
|
+
return option.key ?? option.value ?? option.label;
|
|
99
99
|
} }
|
|
100
100
|
filterOptions={ ( optionList, params ) => {
|
|
101
101
|
const selectedValues = selected.map( ( option ) => option.value );
|
|
@@ -109,6 +109,11 @@ export function MultiCombobox< TOption extends Option >( {
|
|
|
109
109
|
];
|
|
110
110
|
} }
|
|
111
111
|
groupBy={ ( option ) => option.group ?? '' }
|
|
112
|
+
renderOption={ ( optionProps, { label } ) => (
|
|
113
|
+
<li { ...optionProps } style={ { display: 'block', textOverflow: 'ellipsis' } }>
|
|
114
|
+
{ label }
|
|
115
|
+
</li>
|
|
116
|
+
) }
|
|
112
117
|
/>
|
|
113
118
|
);
|
|
114
119
|
}
|
|
@@ -28,6 +28,7 @@ const getEndStartLabel = ( isSiteRtl: boolean ) =>
|
|
|
28
28
|
isSiteRtl ? __( 'Bottom right', 'elementor' ) : __( 'Bottom left', 'elementor' );
|
|
29
29
|
const getEndEndLabel = ( isSiteRtl: boolean ) =>
|
|
30
30
|
isSiteRtl ? __( 'Bottom left', 'elementor' ) : __( 'Bottom right', 'elementor' );
|
|
31
|
+
|
|
31
32
|
const getCorners = ( isSiteRtl: boolean ): EqualUnequalItems => [
|
|
32
33
|
{
|
|
33
34
|
label: getStartStartLabel( isSiteRtl ),
|
|
@@ -39,16 +40,16 @@ const getCorners = ( isSiteRtl: boolean ): EqualUnequalItems => [
|
|
|
39
40
|
icon: <RotatedIcon icon={ StartEndIcon } size="tiny" />,
|
|
40
41
|
bind: 'start-end',
|
|
41
42
|
},
|
|
42
|
-
{
|
|
43
|
-
label: getEndEndLabel( isSiteRtl ),
|
|
44
|
-
icon: <RotatedIcon icon={ EndEndIcon } size="tiny" />,
|
|
45
|
-
bind: 'end-end',
|
|
46
|
-
},
|
|
47
43
|
{
|
|
48
44
|
label: getEndStartLabel( isSiteRtl ),
|
|
49
45
|
icon: <RotatedIcon icon={ EndStartIcon } size="tiny" />,
|
|
50
46
|
bind: 'end-start',
|
|
51
47
|
},
|
|
48
|
+
{
|
|
49
|
+
label: getEndEndLabel( isSiteRtl ),
|
|
50
|
+
icon: <RotatedIcon icon={ EndEndIcon } size="tiny" />,
|
|
51
|
+
bind: 'end-end',
|
|
52
|
+
},
|
|
52
53
|
];
|
|
53
54
|
|
|
54
55
|
export const BorderRadiusField = () => {
|
|
@@ -33,11 +33,11 @@ export const DimensionsField = () => {
|
|
|
33
33
|
<>
|
|
34
34
|
<Stack direction="row" gap={ 2 } flexWrap="nowrap">
|
|
35
35
|
<DimensionField side="inset-block-start" label={ __( 'Top', 'elementor' ) } />
|
|
36
|
-
<DimensionField side="inset-inline-
|
|
36
|
+
<DimensionField side="inset-inline-end" label={ getInlineEndLabel( isSiteRtl ) } />
|
|
37
37
|
</Stack>
|
|
38
38
|
<Stack direction="row" gap={ 2 } flexWrap="nowrap">
|
|
39
39
|
<DimensionField side="inset-block-end" label={ __( 'Bottom', 'elementor' ) } />
|
|
40
|
-
<DimensionField side="inset-inline-
|
|
40
|
+
<DimensionField side="inset-inline-start" label={ getInlineStartLabel( isSiteRtl ) } />
|
|
41
41
|
</Stack>
|
|
42
42
|
</>
|
|
43
43
|
);
|
|
@@ -12,10 +12,6 @@ export const SpacingSection = () => {
|
|
|
12
12
|
|
|
13
13
|
return (
|
|
14
14
|
<SectionContent>
|
|
15
|
-
<StylesField bind={ 'padding' }>
|
|
16
|
-
<LinkedDimensionsControl label={ __( 'Padding', 'elementor' ) } isSiteRtl={ isSiteRtl } />
|
|
17
|
-
</StylesField>
|
|
18
|
-
<PanelDivider />
|
|
19
15
|
<StylesField bind={ 'margin' }>
|
|
20
16
|
<LinkedDimensionsControl
|
|
21
17
|
label={ __( 'Margin', 'elementor' ) }
|
|
@@ -23,6 +19,10 @@ export const SpacingSection = () => {
|
|
|
23
19
|
extendedValues={ [ 'auto' ] }
|
|
24
20
|
/>
|
|
25
21
|
</StylesField>
|
|
22
|
+
<PanelDivider />
|
|
23
|
+
<StylesField bind={ 'padding' }>
|
|
24
|
+
<LinkedDimensionsControl label={ __( 'Padding', 'elementor' ) } isSiteRtl={ isSiteRtl } />
|
|
25
|
+
</StylesField>
|
|
26
26
|
</SectionContent>
|
|
27
27
|
);
|
|
28
28
|
};
|
|
@@ -1,22 +1,15 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { useMemo } from 'react';
|
|
3
2
|
import { ControlLabel, FontFamilyControl } from '@elementor/editor-controls';
|
|
4
3
|
import { Grid } from '@elementor/ui';
|
|
5
4
|
import { __ } from '@wordpress/i18n';
|
|
6
5
|
|
|
7
6
|
import { StylesField } from '../../../controls-registry/styles-field';
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
const supportedCategories: Record< string, string > = {
|
|
11
|
-
system: __( 'System', 'elementor' ),
|
|
12
|
-
custom: __( 'Custom Fonts', 'elementor' ),
|
|
13
|
-
googlefonts: __( 'Google Fonts', 'elementor' ),
|
|
14
|
-
};
|
|
7
|
+
import { useFontFamilies } from './hooks/use-font-families';
|
|
15
8
|
|
|
16
9
|
export const FontFamilyField = () => {
|
|
17
10
|
const fontFamilies = useFontFamilies();
|
|
18
11
|
|
|
19
|
-
if (
|
|
12
|
+
if ( fontFamilies.length === 0 ) {
|
|
20
13
|
return null;
|
|
21
14
|
}
|
|
22
15
|
|
|
@@ -33,40 +26,3 @@ export const FontFamilyField = () => {
|
|
|
33
26
|
</StylesField>
|
|
34
27
|
);
|
|
35
28
|
};
|
|
36
|
-
|
|
37
|
-
const getFontFamilies = () => {
|
|
38
|
-
const { controls } = getElementorConfig();
|
|
39
|
-
|
|
40
|
-
const options = controls?.font?.options;
|
|
41
|
-
|
|
42
|
-
if ( ! options ) {
|
|
43
|
-
return null;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return options;
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const useFontFamilies = () => {
|
|
50
|
-
const fontFamilies = getFontFamilies();
|
|
51
|
-
|
|
52
|
-
return useMemo(
|
|
53
|
-
() =>
|
|
54
|
-
Object.entries( fontFamilies || {} ).reduce< Record< string, string[] > >( ( acc, [ font, category ] ) => {
|
|
55
|
-
const categoryLabel = supportedCategories[ category as keyof typeof supportedCategories ];
|
|
56
|
-
const existingCategory = acc[ categoryLabel ];
|
|
57
|
-
|
|
58
|
-
if ( ! categoryLabel ) {
|
|
59
|
-
return acc;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if ( ! existingCategory ) {
|
|
63
|
-
acc[ categoryLabel ] = [];
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
acc[ categoryLabel ].push( font );
|
|
67
|
-
|
|
68
|
-
return acc;
|
|
69
|
-
}, {} ),
|
|
70
|
-
[ fontFamilies ]
|
|
71
|
-
);
|
|
72
|
-
};
|
|
@@ -26,7 +26,7 @@ export const FontStyleField = () => (
|
|
|
26
26
|
<StylesField bind="font-style">
|
|
27
27
|
<Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
|
|
28
28
|
<Grid item xs={ 6 }>
|
|
29
|
-
<ControlLabel>{ __( 'Font
|
|
29
|
+
<ControlLabel>{ __( 'Font style', 'elementor' ) }</ControlLabel>
|
|
30
30
|
</Grid>
|
|
31
31
|
<Grid item xs={ 6 } display="flex" justifyContent="end">
|
|
32
32
|
<ToggleControl options={ options } />
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import { type FontCategory } from '@elementor/editor-controls';
|
|
3
|
+
import { __ } from '@wordpress/i18n';
|
|
4
|
+
|
|
5
|
+
import { getElementorConfig } from '../../../../sync/get-elementor-config';
|
|
6
|
+
|
|
7
|
+
const supportedCategories: Record< string, string > = {
|
|
8
|
+
system: __( 'System', 'elementor' ),
|
|
9
|
+
custom: __( 'Custom Fonts', 'elementor' ),
|
|
10
|
+
googlefonts: __( 'Google Fonts', 'elementor' ),
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const getFontFamilies = () => {
|
|
14
|
+
const { controls } = getElementorConfig();
|
|
15
|
+
|
|
16
|
+
const options = controls?.font?.options;
|
|
17
|
+
|
|
18
|
+
if ( ! options ) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return options;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const useFontFamilies = () => {
|
|
26
|
+
const fontFamilies = getFontFamilies();
|
|
27
|
+
|
|
28
|
+
return useMemo( () => {
|
|
29
|
+
const categoriesOrder = [ 'system', 'custom', 'googlefonts' ];
|
|
30
|
+
|
|
31
|
+
return Object.entries( fontFamilies || {} )
|
|
32
|
+
.reduce< FontCategory[] >( ( acc, [ font, category ] ) => {
|
|
33
|
+
if ( ! supportedCategories[ category ] ) {
|
|
34
|
+
return acc;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const categoryIndex = categoriesOrder.indexOf( category );
|
|
38
|
+
|
|
39
|
+
if ( ! acc[ categoryIndex ] ) {
|
|
40
|
+
acc[ categoryIndex ] = {
|
|
41
|
+
label: supportedCategories[ category ],
|
|
42
|
+
fonts: [],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
acc[ categoryIndex ].fonts.push( font );
|
|
47
|
+
|
|
48
|
+
return acc;
|
|
49
|
+
}, [] )
|
|
50
|
+
.filter( Boolean );
|
|
51
|
+
}, [ fontFamilies ] );
|
|
52
|
+
};
|