@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elementor/editor-editing-panel",
3
- "version": "1.1.0",
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.0",
43
- "@elementor/editor-controls": "^0.1.0",
44
- "@elementor/editor-elements": "^0.3.0",
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.0",
48
- "@elementor/editor-responsive": "^0.12.2",
49
- "@elementor/editor-styles": "^0.2.1",
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.19.0",
52
- "@elementor/ui": "^1.21.13",
51
+ "@elementor/icons": "^1.20.0",
52
+ "@elementor/ui": "^1.22.0",
53
53
  "@elementor/utils": "^0.3.0",
54
- "@wordpress/i18n": "^4.45.0"
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
- onApply?: ( value: Option[] ) => void;
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
- onApply,
30
+ onSelect,
31
31
  onCreate,
32
32
  ...props
33
33
  }: Props ) => {
34
- const { action: actionProps, option: optionProps } = useComboboxActions( selected, actions, optionsLabel, onApply );
35
-
36
- const handleSelectOption = ( values: Array< ActionOption | Option | string > ) => {
37
- const action = values.find( ( value ) => actionProps.is( value as ActionOption ) );
38
-
39
- if ( action ) {
40
- return actionProps.onChange( action as ActionOption );
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={ ( _, values, reason ) => {
67
- if ( reason === 'selectOption' ) {
68
- return handleSelectOption( values );
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
- if ( reason === 'createOption' ) {
72
- return handleCreateOption( values );
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
- onApply?.( values as ActionOption[] );
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 as ActionOption ) ) {
79
- return optionProps.getLabel( option as 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 actionProps.getLabel( option as ActionOption ) ?? '';
84
+ return '';
83
85
  } }
84
86
  filterOptions={ ( optionList: Option[], params: FilterOptionsState< ActionOption | Option > ) => {
85
- const filteredoptions = optionProps.getFilteredOptions( optionList, params );
87
+ const filteredOptions = optionProps.getFilteredOptions( optionList, params );
86
88
 
87
89
  const actionOptions = actionProps.getFilteredActions( optionList, params );
88
90
 
89
- return [ ...actionOptions, ...filteredoptions ];
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
- export const renderGroup = ( params: AutocompleteRenderGroupParams ) => (
100
+ const renderGroup = ( params: AutocompleteRenderGroupParams ) => (
99
101
  <Group key={ params.key }>
100
102
  <GroupHeader>{ params.group }</GroupHeader>
101
103
  <GroupItems>{ params.children }</GroupItems>
@@ -1,6 +1,8 @@
1
1
  export type Option = {
2
2
  label: string;
3
3
  value: string;
4
+ fixed?: boolean;
5
+ // TODO: Should be remove from here or use some kind of `meta`
4
6
  color?: 'primary' | 'global';
5
7
  };
6
8
 
@@ -7,10 +7,10 @@ export const useComboboxActions = (
7
7
  applied: Option[],
8
8
  actions: Actions,
9
9
  optionsLabel?: string,
10
- onApply?: ( value: Option[] ) => void
10
+ onSelect?: ( value: Option[] ) => void
11
11
  ) => ( {
12
12
  action: {
13
- is: ( opt: ActionOption ): opt is ActionOption => !! opt.action,
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[] ) => onApply?.( optionValues ),
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
+ };