@elementor/editor-editing-panel 1.1.0 → 1.3.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.
Files changed (34) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/dist/index.js +868 -444
  3. package/dist/index.js.map +1 -1
  4. package/dist/index.mjs +851 -403
  5. package/dist/index.mjs.map +1 -1
  6. package/package.json +15 -14
  7. package/src/components/css-class-selector.tsx +131 -0
  8. package/src/components/multi-combobox/multi-combobox.tsx +34 -32
  9. package/src/components/multi-combobox/types.ts +2 -0
  10. package/src/components/multi-combobox/use-combobox-actions.ts +4 -4
  11. package/src/components/style-sections/border-section/border-radius-field.tsx +4 -4
  12. package/src/components/style-sections/border-section/border-width-field.tsx +4 -4
  13. package/src/components/style-sections/layout-section/align-items-field.tsx +72 -0
  14. package/src/components/style-sections/layout-section/align-self-child-field.tsx +72 -0
  15. package/src/components/style-sections/layout-section/flex-direction-field.tsx +64 -0
  16. package/src/components/style-sections/layout-section/flex-order-field.tsx +120 -0
  17. package/src/components/style-sections/layout-section/flex-size-field.tsx +164 -0
  18. package/src/components/style-sections/layout-section/justify-content-field.tsx +62 -62
  19. package/src/components/style-sections/layout-section/layout-section.tsx +27 -3
  20. package/src/components/style-sections/layout-section/utils/rotated-icon.tsx +52 -0
  21. package/src/components/style-sections/layout-section/wrap-field.tsx +52 -0
  22. package/src/components/style-sections/position-section/position-section.tsx +3 -3
  23. package/src/components/style-sections/typography-section/line-height-field.tsx +21 -0
  24. package/src/components/style-sections/typography-section/text-stroke-field.tsx +41 -6
  25. package/src/components/style-sections/typography-section/text-style-field.tsx +31 -8
  26. package/src/components/style-sections/typography-section/typography-section.tsx +3 -1
  27. package/src/components/style-tab.tsx +2 -2
  28. package/src/controls-registry/controls-registry.tsx +4 -0
  29. package/src/dynamics/components/dynamic-selection-control.tsx +8 -5
  30. package/src/dynamics/components/dynamic-selection.tsx +10 -8
  31. package/src/dynamics/dynamic-control.tsx +9 -11
  32. package/src/dynamics/utils.ts +20 -3
  33. package/src/components/css-class-selector-section.tsx +0 -76
  34. 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.1.0",
3
+ "version": "1.3.0",
4
4
  "private": false,
5
5
  "author": "Elementor Team",
6
6
  "homepage": "https://elementor.com/",
@@ -39,19 +39,20 @@
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",
45
- "@elementor/menus": "^0.1.1",
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",
50
- "@elementor/editor-v1-adapters": "^0.8.4",
51
- "@elementor/icons": "^1.19.0",
52
- "@elementor/ui": "^1.21.13",
53
- "@elementor/utils": "^0.3.0",
54
- "@wordpress/i18n": "^4.45.0"
42
+ "@elementor/editor": "0.17.2",
43
+ "@elementor/editor-controls": "0.2.0",
44
+ "@elementor/editor-elements": "0.3.2",
45
+ "@elementor/menus": "0.1.2",
46
+ "@elementor/editor-props": "0.4.0",
47
+ "@elementor/editor-panels": "0.10.2",
48
+ "@elementor/editor-responsive": "0.12.4",
49
+ "@elementor/editor-styles": "0.3.1",
50
+ "@elementor/editor-v1-adapters": "0.8.5",
51
+ "@elementor/icons": "^1.20.0",
52
+ "@elementor/schema": "0.1.2",
53
+ "@elementor/ui": "^1.22.0",
54
+ "@elementor/utils": "0.3.0",
55
+ "@wordpress/i18n": "^5.13.0"
55
56
  },
56
57
  "peerDependencies": {
57
58
  "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
 
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import { type EqualUnequalItems, EqualUnequalSizesControl } from '@elementor/editor-controls';
3
- import type { BorderRadiusPropValue } from '@elementor/editor-props';
3
+ import { borderRadiusPropTypeUtil } from '@elementor/editor-props';
4
4
  import {
5
5
  BorderCornersIcon,
6
6
  RadiusBottomLeftIcon,
@@ -12,7 +12,7 @@ import { __ } from '@wordpress/i18n';
12
12
 
13
13
  import { StylesField } from '../../../controls-registry/styles-field';
14
14
 
15
- const corners: EqualUnequalItems< BorderRadiusPropValue[ '$$type' ], BorderRadiusPropValue > = [
15
+ const corners: EqualUnequalItems = [
16
16
  {
17
17
  label: __( 'Top Left', 'elementor' ),
18
18
  icon: <RadiusTopLeftIcon fontSize={ 'tiny' } />,
@@ -39,10 +39,10 @@ export const BorderRadiusField = () => {
39
39
  return (
40
40
  <StylesField bind={ 'border-radius' }>
41
41
  <EqualUnequalSizesControl
42
+ items={ corners }
42
43
  label={ __( 'Border Radius', 'elementor' ) }
43
44
  icon={ <BorderCornersIcon fontSize={ 'tiny' } /> }
44
- items={ corners }
45
- multiSizeType={ 'border-radius' }
45
+ multiSizePropTypeUtil={ borderRadiusPropTypeUtil }
46
46
  />
47
47
  </StylesField>
48
48
  );
@@ -1,12 +1,12 @@
1
1
  import * as React from 'react';
2
2
  import { type EqualUnequalItems, EqualUnequalSizesControl } from '@elementor/editor-controls';
3
- import { type BorderWidthPropValue } from '@elementor/editor-props';
3
+ import { borderWidthPropTypeUtil } from '@elementor/editor-props';
4
4
  import { SideAllIcon, SideBottomIcon, SideLeftIcon, SideRightIcon, SideTopIcon } from '@elementor/icons';
5
5
  import { __ } from '@wordpress/i18n';
6
6
 
7
7
  import { StylesField } from '../../../controls-registry/styles-field';
8
8
 
9
- const edges: EqualUnequalItems< BorderWidthPropValue[ '$$type' ], BorderWidthPropValue > = [
9
+ const edges: EqualUnequalItems = [
10
10
  {
11
11
  label: __( 'Top', 'elementor' ),
12
12
  icon: <SideTopIcon fontSize={ 'tiny' } />,
@@ -33,10 +33,10 @@ export const BorderWidthField = () => {
33
33
  return (
34
34
  <StylesField bind={ 'border-width' }>
35
35
  <EqualUnequalSizesControl
36
+ items={ edges }
36
37
  label={ __( 'Border Width', 'elementor' ) }
37
38
  icon={ <SideAllIcon fontSize={ 'tiny' } /> }
38
- items={ edges }
39
- multiSizeType={ 'border-width' }
39
+ multiSizePropTypeUtil={ borderWidthPropTypeUtil }
40
40
  />
41
41
  </StylesField>
42
42
  );
@@ -0,0 +1,72 @@
1
+ import * as React from 'react';
2
+ import { ControlLabel, type ToggleButtonGroupItem, ToggleControl } from '@elementor/editor-controls';
3
+ import {
4
+ LayoutAlignCenterIcon as CenterIcon,
5
+ LayoutAlignLeftIcon,
6
+ LayoutAlignRightIcon,
7
+ LayoutDistributeVerticalIcon as JustifyIcon,
8
+ } from '@elementor/icons';
9
+ import { DirectionProvider, Grid, ThemeProvider, withDirection } from '@elementor/ui';
10
+ import { __ } from '@wordpress/i18n';
11
+
12
+ import { StylesField } from '../../../controls-registry/styles-field';
13
+ import { useDirection } from '../../../hooks/use-direction';
14
+ import { RotatedIcon } from './utils/rotated-icon';
15
+
16
+ type AlignItems = 'start' | 'center' | 'end' | 'stretch';
17
+
18
+ const StartIcon = withDirection( LayoutAlignLeftIcon );
19
+ const EndIcon = withDirection( LayoutAlignRightIcon );
20
+
21
+ const iconProps = {
22
+ isClockwise: false,
23
+ offset: 90,
24
+ };
25
+
26
+ const options: ToggleButtonGroupItem< AlignItems >[] = [
27
+ {
28
+ value: 'start',
29
+ label: __( 'Start', 'elementor' ),
30
+ renderContent: ( { size } ) => <RotatedIcon icon={ StartIcon } size={ size } { ...iconProps } />,
31
+ showTooltip: true,
32
+ },
33
+ {
34
+ value: 'center',
35
+ label: __( 'Center', 'elementor' ),
36
+ renderContent: ( { size } ) => <RotatedIcon icon={ CenterIcon } size={ size } { ...iconProps } />,
37
+ showTooltip: true,
38
+ },
39
+ {
40
+ value: 'end',
41
+ label: __( 'End', 'elementor' ),
42
+ renderContent: ( { size } ) => <RotatedIcon icon={ EndIcon } size={ size } { ...iconProps } />,
43
+ showTooltip: true,
44
+ },
45
+ {
46
+ value: 'stretch',
47
+ label: __( 'Stretch', 'elementor' ),
48
+ renderContent: ( { size } ) => <RotatedIcon icon={ JustifyIcon } size={ size } { ...iconProps } />,
49
+ showTooltip: true,
50
+ },
51
+ ];
52
+
53
+ export const AlignItemsField = () => {
54
+ const { isSiteRtl } = useDirection();
55
+
56
+ return (
57
+ <DirectionProvider rtl={ isSiteRtl }>
58
+ <ThemeProvider>
59
+ <StylesField bind="align-items">
60
+ <Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
61
+ <Grid item xs={ 6 }>
62
+ <ControlLabel>{ __( 'Align items', 'elementor' ) }</ControlLabel>
63
+ </Grid>
64
+ <Grid item xs={ 6 } sx={ { display: 'flex', justifyContent: 'end' } }>
65
+ <ToggleControl options={ options } />
66
+ </Grid>
67
+ </Grid>
68
+ </StylesField>
69
+ </ThemeProvider>
70
+ </DirectionProvider>
71
+ );
72
+ };
@@ -0,0 +1,72 @@
1
+ import * as React from 'react';
2
+ import { ControlLabel, type ToggleButtonGroupItem, ToggleControl } from '@elementor/editor-controls';
3
+ import {
4
+ LayoutAlignCenterIcon as CenterIcon,
5
+ LayoutAlignLeftIcon,
6
+ LayoutAlignRightIcon,
7
+ LayoutDistributeVerticalIcon as JustifyIcon,
8
+ } from '@elementor/icons';
9
+ import { DirectionProvider, Grid, ThemeProvider, withDirection } from '@elementor/ui';
10
+ import { __ } from '@wordpress/i18n';
11
+
12
+ import { StylesField } from '../../../controls-registry/styles-field';
13
+ import { useDirection } from '../../../hooks/use-direction';
14
+ import { RotatedIcon } from './utils/rotated-icon';
15
+
16
+ type AlignItems = 'start' | 'center' | 'end' | 'stretch';
17
+
18
+ const StartIcon = withDirection( LayoutAlignLeftIcon );
19
+ const EndIcon = withDirection( LayoutAlignRightIcon );
20
+
21
+ const iconProps = {
22
+ isClockwise: false,
23
+ offset: 90,
24
+ };
25
+
26
+ const options: ToggleButtonGroupItem< AlignItems >[] = [
27
+ {
28
+ value: 'start',
29
+ label: __( 'Start', 'elementor' ),
30
+ renderContent: ( { size } ) => <RotatedIcon icon={ StartIcon } size={ size } { ...iconProps } />,
31
+ showTooltip: true,
32
+ },
33
+ {
34
+ value: 'center',
35
+ label: __( 'Center', 'elementor' ),
36
+ renderContent: ( { size } ) => <RotatedIcon icon={ CenterIcon } size={ size } { ...iconProps } />,
37
+ showTooltip: true,
38
+ },
39
+ {
40
+ value: 'end',
41
+ label: __( 'End', 'elementor' ),
42
+ renderContent: ( { size } ) => <RotatedIcon icon={ EndIcon } size={ size } { ...iconProps } />,
43
+ showTooltip: true,
44
+ },
45
+ {
46
+ value: 'stretch',
47
+ label: __( 'Stretch', 'elementor' ),
48
+ renderContent: ( { size } ) => <RotatedIcon icon={ JustifyIcon } size={ size } { ...iconProps } />,
49
+ showTooltip: true,
50
+ },
51
+ ];
52
+
53
+ export const AlignSelfChild = () => {
54
+ const { isSiteRtl } = useDirection();
55
+
56
+ return (
57
+ <DirectionProvider rtl={ isSiteRtl }>
58
+ <ThemeProvider>
59
+ <StylesField bind={ 'align-self' }>
60
+ <Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
61
+ <Grid item xs={ 6 }>
62
+ <ControlLabel>{ __( 'Align self', 'elementor' ) }</ControlLabel>
63
+ </Grid>
64
+ <Grid item xs={ 6 } sx={ { display: 'flex', justifyContent: 'flex-end' } }>
65
+ <ToggleControl options={ options } />
66
+ </Grid>
67
+ </Grid>
68
+ </StylesField>
69
+ </ThemeProvider>
70
+ </DirectionProvider>
71
+ );
72
+ };
@@ -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: __( 'Reversed row', '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: __( 'Reversed column', '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
+ };