@elementor/editor-editing-panel 1.1.0 → 1.2.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.js +630 -377
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +605 -337
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -10
- package/src/components/css-class-selector.tsx +131 -0
- package/src/components/multi-combobox/multi-combobox.tsx +34 -32
- package/src/components/multi-combobox/types.ts +2 -0
- package/src/components/multi-combobox/use-combobox-actions.ts +4 -4
- package/src/components/style-sections/layout-section/align-items-field.tsx +92 -0
- package/src/components/style-sections/layout-section/flex-direction-field.tsx +64 -0
- package/src/components/style-sections/layout-section/flex-order-field.tsx +114 -0
- package/src/components/style-sections/layout-section/justify-content-field.tsx +58 -31
- package/src/components/style-sections/layout-section/layout-section.tsx +21 -2
- package/src/components/style-sections/layout-section/wrap-field.tsx +52 -0
- package/src/components/style-sections/typography-section/text-stroke-field.tsx +41 -6
- package/src/components/style-tab.tsx +2 -2
- package/src/components/css-class-selector-section.tsx +0 -76
- package/src/components/style-sections/layout-section/utils/rotate-flex-icon.ts +0 -12
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elementor/editor-editing-panel",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"author": "Elementor Team",
|
|
6
6
|
"homepage": "https://elementor.com/",
|
|
@@ -39,19 +39,19 @@
|
|
|
39
39
|
"dev": "tsup --config=../../tsup.dev.ts"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@elementor/editor": "^0.17.
|
|
43
|
-
"@elementor/editor-controls": "^0.1.
|
|
44
|
-
"@elementor/editor-elements": "^0.3.
|
|
42
|
+
"@elementor/editor": "^0.17.1",
|
|
43
|
+
"@elementor/editor-controls": "^0.1.1",
|
|
44
|
+
"@elementor/editor-elements": "^0.3.1",
|
|
45
45
|
"@elementor/menus": "^0.1.1",
|
|
46
46
|
"@elementor/editor-props": "^0.3.0",
|
|
47
|
-
"@elementor/editor-panels": "^0.10.
|
|
48
|
-
"@elementor/editor-responsive": "^0.12.
|
|
49
|
-
"@elementor/editor-styles": "^0.
|
|
47
|
+
"@elementor/editor-panels": "^0.10.1",
|
|
48
|
+
"@elementor/editor-responsive": "^0.12.3",
|
|
49
|
+
"@elementor/editor-styles": "^0.3.0",
|
|
50
50
|
"@elementor/editor-v1-adapters": "^0.8.4",
|
|
51
|
-
"@elementor/icons": "^1.
|
|
52
|
-
"@elementor/ui": "^1.
|
|
51
|
+
"@elementor/icons": "^1.20.0",
|
|
52
|
+
"@elementor/ui": "^1.22.0",
|
|
53
53
|
"@elementor/utils": "^0.3.0",
|
|
54
|
-
"@wordpress/i18n": "^
|
|
54
|
+
"@wordpress/i18n": "^5.13.0"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
57
|
"react": "^18.3.1"
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { updateSettings, useElementSetting, useElementStyles } from '@elementor/editor-elements';
|
|
3
|
+
import { classesPropTypeUtil, type ClassesPropValue } from '@elementor/editor-props';
|
|
4
|
+
import { type StyleDefinitionID } from '@elementor/editor-styles';
|
|
5
|
+
import { Chip, Stack, Typography } from '@elementor/ui';
|
|
6
|
+
import { __ } from '@wordpress/i18n';
|
|
7
|
+
|
|
8
|
+
import { useClassesProp } from '../contexts/classes-prop-context';
|
|
9
|
+
import { useElement } from '../contexts/element-context';
|
|
10
|
+
import { useStyle } from '../contexts/style-context';
|
|
11
|
+
import { MultiCombobox, type Option } from './multi-combobox';
|
|
12
|
+
|
|
13
|
+
const ID = 'elementor-css-class-selector';
|
|
14
|
+
const TAGS_LIMIT = 8;
|
|
15
|
+
|
|
16
|
+
export function CssClassSelector() {
|
|
17
|
+
const options = useOptions();
|
|
18
|
+
|
|
19
|
+
const { id: activeId, setId: setActiveId } = useStyle();
|
|
20
|
+
const [ appliedIds ] = useAppliedClassesIds();
|
|
21
|
+
|
|
22
|
+
const handleApply = useHandleApply();
|
|
23
|
+
const handleActivate = ( { value }: Option ) => setActiveId( value );
|
|
24
|
+
|
|
25
|
+
const active = options.find( ( option ) => option.value === activeId ) || null;
|
|
26
|
+
|
|
27
|
+
const applied = appliedIds
|
|
28
|
+
.map( ( id ) => options.find( ( option ) => option.value === id ) )
|
|
29
|
+
.filter( ( option ) => !! option );
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<Stack gap={ 1 } p={ 2 }>
|
|
33
|
+
<Typography component="label" variant="caption" htmlFor={ ID }>
|
|
34
|
+
{ __( 'CSS Classes', 'elementor' ) }
|
|
35
|
+
</Typography>
|
|
36
|
+
<MultiCombobox
|
|
37
|
+
id={ ID }
|
|
38
|
+
size="tiny"
|
|
39
|
+
options={ options }
|
|
40
|
+
selected={ applied }
|
|
41
|
+
onSelect={ handleApply }
|
|
42
|
+
limitTags={ TAGS_LIMIT }
|
|
43
|
+
optionsLabel={ __( 'Global CSS Classes', 'elementor' ) }
|
|
44
|
+
renderTags={ ( values, getTagProps ) =>
|
|
45
|
+
values.map( ( value, index ) => {
|
|
46
|
+
const chipProps = getTagProps( { index } );
|
|
47
|
+
const isActive = value.value === active?.value;
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<Chip
|
|
51
|
+
{ ...chipProps }
|
|
52
|
+
key={ chipProps.key }
|
|
53
|
+
size="small"
|
|
54
|
+
label={ value.label }
|
|
55
|
+
variant={ isActive ? 'filled' : 'standard' }
|
|
56
|
+
color={ isActive && value.color ? value.color : 'default' }
|
|
57
|
+
onClick={ () => handleActivate( value ) }
|
|
58
|
+
onDelete={ null }
|
|
59
|
+
/>
|
|
60
|
+
);
|
|
61
|
+
} )
|
|
62
|
+
}
|
|
63
|
+
/>
|
|
64
|
+
</Stack>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function useOptions() {
|
|
69
|
+
const { element } = useElement();
|
|
70
|
+
|
|
71
|
+
const styleDefs = useElementStyles( element.id );
|
|
72
|
+
|
|
73
|
+
return Object.values( styleDefs ).map< Option >( ( styleDef ) => ( {
|
|
74
|
+
label: styleDef.label,
|
|
75
|
+
value: styleDef.id,
|
|
76
|
+
fixed: true,
|
|
77
|
+
color: 'primary',
|
|
78
|
+
} ) );
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function useAppliedClassesIds() {
|
|
82
|
+
const { element } = useElement();
|
|
83
|
+
const currentClassesProp = useClassesProp();
|
|
84
|
+
|
|
85
|
+
const value = useElementSetting< ClassesPropValue >( element.id, currentClassesProp )?.value || [];
|
|
86
|
+
|
|
87
|
+
const setValue = ( ids: StyleDefinitionID[] ) => {
|
|
88
|
+
updateSettings( {
|
|
89
|
+
id: element.id,
|
|
90
|
+
props: {
|
|
91
|
+
[ currentClassesProp ]: classesPropTypeUtil.create( ids ),
|
|
92
|
+
},
|
|
93
|
+
} );
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
return [ value, setValue ] as const;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function useHandleApply() {
|
|
100
|
+
const { id: activeId, setId: setActiveId } = useStyle();
|
|
101
|
+
const [ appliedIds, setAppliedIds ] = useAppliedClassesIds();
|
|
102
|
+
|
|
103
|
+
return ( selectedOptions: Option[] ) => {
|
|
104
|
+
const selectedValues = selectedOptions.map( ( { value } ) => value );
|
|
105
|
+
|
|
106
|
+
const isSameClassesAlreadyApplied =
|
|
107
|
+
selectedValues.length === appliedIds.length &&
|
|
108
|
+
selectedValues.every( ( value ) => appliedIds.includes( value ) );
|
|
109
|
+
|
|
110
|
+
// Should not trigger to avoid register an undo step.
|
|
111
|
+
if ( isSameClassesAlreadyApplied ) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
setAppliedIds( selectedValues );
|
|
116
|
+
|
|
117
|
+
const addedValue = selectedValues.find( ( id ) => ! appliedIds.includes( id ) );
|
|
118
|
+
|
|
119
|
+
if ( addedValue ) {
|
|
120
|
+
setActiveId( addedValue );
|
|
121
|
+
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const removedValue = appliedIds.find( ( id ) => ! selectedValues.includes( id ) );
|
|
126
|
+
|
|
127
|
+
if ( removedValue && removedValue === activeId ) {
|
|
128
|
+
setActiveId( selectedValues[ 0 ] ?? null );
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
}
|
|
@@ -13,12 +13,12 @@ import { type FilterOptionsState } from '@mui/base';
|
|
|
13
13
|
import { type ActionOption, type Actions, type Option } from './types';
|
|
14
14
|
import { useComboboxActions } from './use-combobox-actions';
|
|
15
15
|
|
|
16
|
-
type Props = Omit< AutocompleteProps< Option, true, true, true >, 'renderInput' | 'getLimitTagsText' > & {
|
|
16
|
+
type Props = Omit< AutocompleteProps< Option, true, true, true >, 'renderInput' | 'getLimitTagsText' | 'onSelect' > & {
|
|
17
17
|
actions?: Actions;
|
|
18
18
|
selected: Option[];
|
|
19
19
|
options: Option[];
|
|
20
20
|
optionsLabel?: string;
|
|
21
|
-
|
|
21
|
+
onSelect?: ( value: Option[] ) => void;
|
|
22
22
|
onCreate?: ( value: string ) => void;
|
|
23
23
|
};
|
|
24
24
|
|
|
@@ -27,27 +27,17 @@ export const MultiCombobox = ( {
|
|
|
27
27
|
selected,
|
|
28
28
|
options,
|
|
29
29
|
optionsLabel,
|
|
30
|
-
|
|
30
|
+
onSelect,
|
|
31
31
|
onCreate,
|
|
32
32
|
...props
|
|
33
33
|
}: Props ) => {
|
|
34
|
-
const { action: actionProps, option: optionProps } = useComboboxActions(
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return optionProps.onChange( values as Option[] );
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
const handleCreateOption = ( values: Array< ActionOption | Option | string > ) => {
|
|
47
|
-
const value = values.find( ( option ) => typeof option === 'string' );
|
|
48
|
-
|
|
49
|
-
onCreate?.( value as string );
|
|
50
|
-
};
|
|
34
|
+
const { action: actionProps, option: optionProps } = useComboboxActions(
|
|
35
|
+
selected,
|
|
36
|
+
actions,
|
|
37
|
+
// TODO: make the group mechanism more generic, allow passing list of groups.
|
|
38
|
+
optionsLabel,
|
|
39
|
+
onSelect
|
|
40
|
+
);
|
|
51
41
|
|
|
52
42
|
return (
|
|
53
43
|
<Autocomplete
|
|
@@ -62,31 +52,43 @@ export const MultiCombobox = ( {
|
|
|
62
52
|
options={ options }
|
|
63
53
|
renderGroup={ renderGroup }
|
|
64
54
|
renderInput={ ( params ) => <TextField { ...params } /> }
|
|
55
|
+
// TODO: is it relevant for the combobox? or should be in the parent component?
|
|
65
56
|
getLimitTagsText={ ( more ) => <Chip size="tiny" variant="standard" label={ `+${ more }` } clickable /> }
|
|
66
|
-
onChange={ ( _,
|
|
67
|
-
if ( reason === '
|
|
68
|
-
|
|
57
|
+
onChange={ ( _, selectedOrTypedValue, reason ) => {
|
|
58
|
+
if ( reason === 'createOption' ) {
|
|
59
|
+
const typedValue = selectedOrTypedValue.find( ( option ) => typeof option === 'string' );
|
|
60
|
+
|
|
61
|
+
return typedValue && onCreate?.( typedValue );
|
|
69
62
|
}
|
|
70
63
|
|
|
71
|
-
|
|
72
|
-
|
|
64
|
+
const action = selectedOrTypedValue.find( ( value ) => actionProps.is( value ) );
|
|
65
|
+
|
|
66
|
+
if ( reason === 'selectOption' && action ) {
|
|
67
|
+
return actionProps.onChange( action );
|
|
73
68
|
}
|
|
74
69
|
|
|
75
|
-
|
|
70
|
+
const selectedValues = selectedOrTypedValue.filter( ( v ) => typeof v !== 'string' );
|
|
71
|
+
const fixedValues = options.filter( ( option ) => option.fixed );
|
|
72
|
+
|
|
73
|
+
optionProps.onChange( [ ...new Set( [ ...fixedValues, ...selectedValues ] ) ] );
|
|
76
74
|
} }
|
|
77
75
|
getOptionLabel={ ( option ) => {
|
|
78
|
-
if ( optionProps.is( option
|
|
79
|
-
return optionProps.getLabel( option
|
|
76
|
+
if ( optionProps.is( option ) ) {
|
|
77
|
+
return optionProps.getLabel( option );
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if ( actionProps.is( option ) ) {
|
|
81
|
+
return actionProps.getLabel( option );
|
|
80
82
|
}
|
|
81
83
|
|
|
82
|
-
return
|
|
84
|
+
return '';
|
|
83
85
|
} }
|
|
84
86
|
filterOptions={ ( optionList: Option[], params: FilterOptionsState< ActionOption | Option > ) => {
|
|
85
|
-
const
|
|
87
|
+
const filteredOptions = optionProps.getFilteredOptions( optionList, params );
|
|
86
88
|
|
|
87
89
|
const actionOptions = actionProps.getFilteredActions( optionList, params );
|
|
88
90
|
|
|
89
|
-
return [ ...actionOptions, ...
|
|
91
|
+
return [ ...actionOptions, ...filteredOptions ];
|
|
90
92
|
} }
|
|
91
93
|
groupBy={ ( option ) =>
|
|
92
94
|
( optionProps.is( option ) ? optionProps.groupBy() : actionProps.groupBy( option ) ) ?? ''
|
|
@@ -95,7 +97,7 @@ export const MultiCombobox = ( {
|
|
|
95
97
|
);
|
|
96
98
|
};
|
|
97
99
|
|
|
98
|
-
|
|
100
|
+
const renderGroup = ( params: AutocompleteRenderGroupParams ) => (
|
|
99
101
|
<Group key={ params.key }>
|
|
100
102
|
<GroupHeader>{ params.group }</GroupHeader>
|
|
101
103
|
<GroupItems>{ params.children }</GroupItems>
|
|
@@ -7,10 +7,10 @@ export const useComboboxActions = (
|
|
|
7
7
|
applied: Option[],
|
|
8
8
|
actions: Actions,
|
|
9
9
|
optionsLabel?: string,
|
|
10
|
-
|
|
10
|
+
onSelect?: ( value: Option[] ) => void
|
|
11
11
|
) => ( {
|
|
12
12
|
action: {
|
|
13
|
-
is: ( opt: ActionOption ): opt is ActionOption =>
|
|
13
|
+
is: ( opt: ActionOption | Option | string ): opt is ActionOption => typeof opt !== 'string' && 'action' in opt,
|
|
14
14
|
getLabel: ( option: ActionOption ) => option.action.getLabel( option.label ),
|
|
15
15
|
groupBy: ( option: ActionOption ) => option.action.groupLabel,
|
|
16
16
|
onChange: ( { action, label }: ActionOption ) => action?.apply( label ),
|
|
@@ -34,10 +34,10 @@ export const useComboboxActions = (
|
|
|
34
34
|
},
|
|
35
35
|
},
|
|
36
36
|
option: {
|
|
37
|
-
is: ( opt: ActionOption | Option ): opt is Option => ! ( 'action' in opt ),
|
|
37
|
+
is: ( opt: ActionOption | Option | string ): opt is Option => typeof opt !== 'string' && ! ( 'action' in opt ),
|
|
38
38
|
getLabel: ( option: Option ) => option.label,
|
|
39
39
|
groupBy: () => optionsLabel ?? '',
|
|
40
|
-
onChange: ( optionValues: Option[] ) =>
|
|
40
|
+
onChange: ( optionValues: Option[] ) => onSelect?.( optionValues ),
|
|
41
41
|
getFilteredOptions: ( optionList: Option[], params: FilterOptionsState< Option > ) => {
|
|
42
42
|
const appliedValues = applied.map( ( option ) => option.value );
|
|
43
43
|
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { JSX } from 'react';
|
|
3
|
+
import { ControlLabel, type ToggleButtonGroupItem, ToggleControl } from '@elementor/editor-controls';
|
|
4
|
+
import {
|
|
5
|
+
LayoutAlignCenterIcon as CenterIcon,
|
|
6
|
+
LayoutAlignLeftIcon,
|
|
7
|
+
LayoutAlignRightIcon,
|
|
8
|
+
LayoutDistributeVerticalIcon as JustifyIcon,
|
|
9
|
+
} from '@elementor/icons';
|
|
10
|
+
import { DirectionProvider, Grid, ThemeProvider, type ToggleButtonProps, useTheme, withDirection } from '@elementor/ui';
|
|
11
|
+
import { __ } from '@wordpress/i18n';
|
|
12
|
+
|
|
13
|
+
import { StylesField } from '../../../controls-registry/styles-field';
|
|
14
|
+
import { useDirection } from '../../../hooks/use-direction';
|
|
15
|
+
import { useStylesField } from '../../../hooks/use-styles-field';
|
|
16
|
+
import { type FlexDirection } from './flex-direction-field';
|
|
17
|
+
|
|
18
|
+
type AlignItems = 'start' | 'center' | 'end' | 'justify';
|
|
19
|
+
|
|
20
|
+
export const AlignItemsField = () => {
|
|
21
|
+
const options = useOptions(),
|
|
22
|
+
{ isSiteRtl } = useDirection();
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<DirectionProvider rtl={ isSiteRtl }>
|
|
26
|
+
<ThemeProvider>
|
|
27
|
+
<StylesField bind="align-items">
|
|
28
|
+
<Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
|
|
29
|
+
<Grid item xs={ 6 }>
|
|
30
|
+
<ControlLabel>{ __( 'Align Items', 'elementor' ) }</ControlLabel>
|
|
31
|
+
</Grid>
|
|
32
|
+
<Grid item xs={ 6 } sx={ { display: 'flex', justifyContent: 'end' } }>
|
|
33
|
+
<ToggleControl options={ options } />
|
|
34
|
+
</Grid>
|
|
35
|
+
</Grid>
|
|
36
|
+
</StylesField>
|
|
37
|
+
</ThemeProvider>
|
|
38
|
+
</DirectionProvider>
|
|
39
|
+
);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const useOptions = (): ToggleButtonGroupItem< AlignItems >[] => {
|
|
43
|
+
const StartIcon = withDirection( LayoutAlignLeftIcon ),
|
|
44
|
+
EndIcon = withDirection( LayoutAlignRightIcon );
|
|
45
|
+
|
|
46
|
+
return [
|
|
47
|
+
{
|
|
48
|
+
value: 'start',
|
|
49
|
+
label: __( 'Start', 'elementor' ),
|
|
50
|
+
renderContent: ( { size } ) => <RotatedIcon icon={ StartIcon } size={ size } />,
|
|
51
|
+
showTooltip: true,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
value: 'center',
|
|
55
|
+
label: __( 'Center', 'elementor' ),
|
|
56
|
+
renderContent: ( { size } ) => <RotatedIcon icon={ CenterIcon } size={ size } />,
|
|
57
|
+
showTooltip: true,
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
value: 'end',
|
|
61
|
+
label: __( 'End', 'elementor' ),
|
|
62
|
+
renderContent: ( { size } ) => <RotatedIcon icon={ EndIcon } size={ size } />,
|
|
63
|
+
showTooltip: true,
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
value: 'justify',
|
|
67
|
+
label: __( 'Justify', 'elementor' ),
|
|
68
|
+
renderContent: ( { size } ) => <RotatedIcon icon={ JustifyIcon } size={ size } />,
|
|
69
|
+
showTooltip: true,
|
|
70
|
+
},
|
|
71
|
+
];
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const RotatedIcon = ( { icon: Icon, size }: { icon: JSX.ElementType; size: ToggleButtonProps[ 'size' ] } ) => {
|
|
75
|
+
const [ direction ] = useStylesField< FlexDirection >( 'flex-direction' ),
|
|
76
|
+
isRtl = 'rtl' === useTheme().direction,
|
|
77
|
+
rotationMultiplier = isRtl ? -1 : 1;
|
|
78
|
+
|
|
79
|
+
const rotationAngelMap: Record< FlexDirection, number > = {
|
|
80
|
+
row: 90,
|
|
81
|
+
column: 0,
|
|
82
|
+
'row-reverse': -90,
|
|
83
|
+
'column-reverse': -180,
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<Icon
|
|
88
|
+
fontSize={ size }
|
|
89
|
+
sx={ { transition: '.3s', rotate: `${ rotationAngelMap[ direction || 'row' ] * rotationMultiplier }deg` } }
|
|
90
|
+
/>
|
|
91
|
+
);
|
|
92
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { ControlLabel, type ToggleButtonGroupItem, ToggleControl } from '@elementor/editor-controls';
|
|
3
|
+
import { ArrowDownSmallIcon, ArrowLeftIcon, ArrowRightIcon, ArrowUpSmallIcon } from '@elementor/icons';
|
|
4
|
+
import { DirectionProvider, Grid, ThemeProvider, withDirection } from '@elementor/ui';
|
|
5
|
+
import { __ } from '@wordpress/i18n';
|
|
6
|
+
|
|
7
|
+
import { StylesField } from '../../../controls-registry/styles-field';
|
|
8
|
+
import { useDirection } from '../../../hooks/use-direction';
|
|
9
|
+
|
|
10
|
+
export type FlexDirection = 'row' | 'row-reverse' | 'column' | 'column-reverse';
|
|
11
|
+
|
|
12
|
+
const options: ToggleButtonGroupItem< FlexDirection >[] = [
|
|
13
|
+
{
|
|
14
|
+
value: 'row',
|
|
15
|
+
label: __( 'Row', 'elementor' ),
|
|
16
|
+
renderContent: ( { size } ) => {
|
|
17
|
+
const StartIcon = withDirection( ArrowRightIcon );
|
|
18
|
+
return <StartIcon fontSize={ size } />;
|
|
19
|
+
},
|
|
20
|
+
showTooltip: true,
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
value: 'column',
|
|
24
|
+
label: __( 'Column', 'elementor' ),
|
|
25
|
+
renderContent: ( { size } ) => <ArrowDownSmallIcon fontSize={ size } />,
|
|
26
|
+
showTooltip: true,
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
value: 'row-reverse',
|
|
30
|
+
label: __( 'Row reverse', 'elementor' ),
|
|
31
|
+
renderContent: ( { size } ) => {
|
|
32
|
+
const EndIcon = withDirection( ArrowLeftIcon );
|
|
33
|
+
return <EndIcon fontSize={ size } />;
|
|
34
|
+
},
|
|
35
|
+
showTooltip: true,
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
value: 'column-reverse',
|
|
39
|
+
label: __( 'Column reverse', 'elementor' ),
|
|
40
|
+
renderContent: ( { size } ) => <ArrowUpSmallIcon fontSize={ size } />,
|
|
41
|
+
showTooltip: true,
|
|
42
|
+
},
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
export const FlexDirectionField = () => {
|
|
46
|
+
const { isSiteRtl } = useDirection();
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<DirectionProvider rtl={ isSiteRtl }>
|
|
50
|
+
<ThemeProvider>
|
|
51
|
+
<StylesField bind="flex-direction">
|
|
52
|
+
<Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
|
|
53
|
+
<Grid item xs={ 6 }>
|
|
54
|
+
<ControlLabel>{ __( 'Direction', 'elementor' ) }</ControlLabel>
|
|
55
|
+
</Grid>
|
|
56
|
+
<Grid item xs={ 6 } sx={ { display: 'flex', justifyContent: 'end' } }>
|
|
57
|
+
<ToggleControl options={ options } />
|
|
58
|
+
</Grid>
|
|
59
|
+
</Grid>
|
|
60
|
+
</StylesField>
|
|
61
|
+
</ThemeProvider>
|
|
62
|
+
</DirectionProvider>
|
|
63
|
+
);
|
|
64
|
+
};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import {
|
|
4
|
+
ControlLabel,
|
|
5
|
+
ControlToggleButtonGroup,
|
|
6
|
+
NumberControl,
|
|
7
|
+
type ToggleButtonGroupItem,
|
|
8
|
+
} from '@elementor/editor-controls';
|
|
9
|
+
import { ArrowDownSmallIcon, ArrowUpSmallIcon, PencilIcon } from '@elementor/icons';
|
|
10
|
+
import { DirectionProvider, Grid, Stack, ThemeProvider } from '@elementor/ui';
|
|
11
|
+
import { __ } from '@wordpress/i18n';
|
|
12
|
+
|
|
13
|
+
import { StylesField } from '../../../controls-registry/styles-field';
|
|
14
|
+
import { useDirection } from '../../../hooks/use-direction';
|
|
15
|
+
import { useStylesField } from '../../../hooks/use-styles-field';
|
|
16
|
+
|
|
17
|
+
type GroupControlItemOption = 'first' | 'last' | 'custom';
|
|
18
|
+
|
|
19
|
+
export const FIRST_DEFAULT_VALUE = -99999,
|
|
20
|
+
LAST_DEFAULT_VALUE = 99999,
|
|
21
|
+
FIRST = 'first',
|
|
22
|
+
LAST = 'last',
|
|
23
|
+
CUSTOM = 'custom';
|
|
24
|
+
|
|
25
|
+
const items: ToggleButtonGroupItem< GroupControlItemOption >[] = [
|
|
26
|
+
{
|
|
27
|
+
value: FIRST,
|
|
28
|
+
label: __( 'First', 'elementor' ),
|
|
29
|
+
renderContent: ( { size } ) => <ArrowUpSmallIcon fontSize={ size } />,
|
|
30
|
+
showTooltip: true,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
value: LAST,
|
|
34
|
+
label: __( 'Last', 'elementor' ),
|
|
35
|
+
renderContent: ( { size } ) => <ArrowDownSmallIcon fontSize={ size } />,
|
|
36
|
+
showTooltip: true,
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
value: CUSTOM,
|
|
40
|
+
label: __( 'Custom', 'elementor' ),
|
|
41
|
+
renderContent: ( { size } ) => <PencilIcon fontSize={ size } />,
|
|
42
|
+
showTooltip: true,
|
|
43
|
+
},
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
export const FlexOrderField = () => {
|
|
47
|
+
const { isSiteRtl } = useDirection(),
|
|
48
|
+
[ order, setOrder ] = useStylesField< number | null >( 'order' );
|
|
49
|
+
|
|
50
|
+
const [ groupControlValue, setGroupControlValue ] = useState( getGroupControlValue( order ) );
|
|
51
|
+
|
|
52
|
+
const handleToggleButtonChange = ( group: GroupControlItemOption | null ) => {
|
|
53
|
+
setGroupControlValue( group );
|
|
54
|
+
|
|
55
|
+
const orderValueMap = {
|
|
56
|
+
[ FIRST ]: FIRST_DEFAULT_VALUE,
|
|
57
|
+
[ LAST ]: LAST_DEFAULT_VALUE,
|
|
58
|
+
[ CUSTOM ]: null,
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
setOrder( group ? orderValueMap[ group ] : null );
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<DirectionProvider rtl={ isSiteRtl }>
|
|
66
|
+
<ThemeProvider>
|
|
67
|
+
<Stack gap={ 2 }>
|
|
68
|
+
<Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
|
|
69
|
+
<Grid item xs={ 6 }>
|
|
70
|
+
<ControlLabel>{ __( 'Order', 'elementor' ) }</ControlLabel>
|
|
71
|
+
</Grid>
|
|
72
|
+
<Grid item xs={ 6 } sx={ { display: 'flex', justifyContent: 'end' } }>
|
|
73
|
+
<ControlToggleButtonGroup
|
|
74
|
+
items={ items }
|
|
75
|
+
value={ groupControlValue }
|
|
76
|
+
onChange={ handleToggleButtonChange }
|
|
77
|
+
exclusive={ true }
|
|
78
|
+
/>
|
|
79
|
+
</Grid>
|
|
80
|
+
</Grid>
|
|
81
|
+
|
|
82
|
+
{ CUSTOM === groupControlValue && (
|
|
83
|
+
<StylesField bind={ 'order' }>
|
|
84
|
+
<Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
|
|
85
|
+
<Grid item xs={ 6 }>
|
|
86
|
+
<ControlLabel>{ __( 'Custom order', 'elementor' ) }</ControlLabel>
|
|
87
|
+
</Grid>
|
|
88
|
+
<Grid item xs={ 6 } sx={ { display: 'flex', justifyContent: 'end' } }>
|
|
89
|
+
<NumberControl
|
|
90
|
+
min={ FIRST_DEFAULT_VALUE + 1 }
|
|
91
|
+
max={ LAST_DEFAULT_VALUE - 1 }
|
|
92
|
+
shouldForceInt={ true }
|
|
93
|
+
/>
|
|
94
|
+
</Grid>
|
|
95
|
+
</Grid>
|
|
96
|
+
</StylesField>
|
|
97
|
+
) }
|
|
98
|
+
</Stack>
|
|
99
|
+
</ThemeProvider>
|
|
100
|
+
</DirectionProvider>
|
|
101
|
+
);
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const getGroupControlValue = ( order: number | null ): GroupControlItemOption | null => {
|
|
105
|
+
if ( LAST_DEFAULT_VALUE === order ) {
|
|
106
|
+
return LAST;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if ( FIRST_DEFAULT_VALUE === order ) {
|
|
110
|
+
return FIRST;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return 0 === order || order ? CUSTOM : null;
|
|
114
|
+
};
|