@elementor/editor-editing-panel 1.20.0 → 1.22.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.20.0",
3
+ "version": "1.22.0",
4
4
  "private": false,
5
5
  "author": "Elementor Team",
6
6
  "homepage": "https://elementor.com/",
@@ -39,23 +39,23 @@
39
39
  "dev": "tsup --config=../../tsup.dev.ts"
40
40
  },
41
41
  "dependencies": {
42
- "@elementor/editor": "0.18.3",
43
- "@elementor/editor-controls": "0.18.1",
42
+ "@elementor/editor": "0.18.4",
43
+ "@elementor/editor-controls": "0.19.1",
44
44
  "@elementor/editor-current-user": "0.3.0",
45
- "@elementor/editor-elements": "0.6.4",
46
- "@elementor/editor-panels": "0.13.0",
47
- "@elementor/editor-props": "0.11.0",
45
+ "@elementor/editor-elements": "0.6.6",
46
+ "@elementor/editor-panels": "0.13.1",
47
+ "@elementor/editor-props": "0.11.1",
48
48
  "@elementor/editor-responsive": "0.13.3",
49
- "@elementor/editor-styles": "0.6.3",
50
- "@elementor/editor-styles-repository": "0.7.7",
51
- "@elementor/editor-ui": "0.4.2",
49
+ "@elementor/editor-styles": "0.6.4",
50
+ "@elementor/editor-styles-repository": "0.8.0",
51
+ "@elementor/editor-ui": "0.4.3",
52
52
  "@elementor/editor-v1-adapters": "0.11.0",
53
53
  "@elementor/icons": "1.37.0",
54
- "@elementor/locations": "0.7.6",
55
- "@elementor/menus": "0.1.3",
54
+ "@elementor/locations": "0.7.7",
55
+ "@elementor/menus": "0.1.4",
56
56
  "@elementor/schema": "0.1.2",
57
57
  "@elementor/session": "0.1.0",
58
- "@elementor/ui": "1.26.0",
58
+ "@elementor/ui": "1.32.1",
59
59
  "@elementor/utils": "0.4.0",
60
60
  "@wordpress/i18n": "^5.13.0"
61
61
  },
@@ -165,6 +165,9 @@ function useCreateActions( {
165
165
  return {
166
166
  // translators: %s is the label of the new class.
167
167
  label: ( value ) => __( 'Create new "%s"', 'elementor' ).replace( '%s', value ),
168
+ // translators: %s is the singular label of css class provider (e.g "Global CSS Class").
169
+ group: __( 'Create New %s', 'elementor' ).replace( '%s', provider.labels?.singular ?? '' ),
170
+ condition: ( _, inputValue ) => isLabelValid( inputValue ) && ! hasReachedLimit( provider ),
168
171
  apply: ( label ) => {
169
172
  const createdId = create( label );
170
173
 
@@ -175,14 +178,22 @@ function useCreateActions( {
175
178
  pushAppliedId( createdId );
176
179
  setActiveId( createdId );
177
180
  },
178
- condition: ( _, inputValue ) =>
179
- stylesRepository.isLabelValid( inputValue ) && ! stylesRepository.isLabelExist( inputValue ),
180
- // translators: %s is the singular label of css class provider (e.g "Global CSS Class").
181
- group: __( 'Create New %s', 'elementor' ).replace( '%s', provider.labels?.singular ?? '' ),
182
181
  };
183
182
  } );
184
183
  }
185
184
 
185
+ function hasReachedLimit( provider: StylesProvider ) {
186
+ if ( provider.limit === undefined ) {
187
+ return false;
188
+ }
189
+
190
+ return provider.actions.get().length >= provider.limit;
191
+ }
192
+
193
+ function isLabelValid( newLabel: string ) {
194
+ return stylesRepository.isLabelValid( newLabel ) && ! stylesRepository.isLabelExist( newLabel );
195
+ }
196
+
186
197
  function useAppliedOptions( options: StyleDefOption[], appliedIds: StyleDefinitionID[] ) {
187
198
  const applied = options.filter( ( option ) => option.value && appliedIds.includes( option.value ) );
188
199
 
@@ -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.value ) {
75
+ if ( firstAction?.value ) {
76
76
  return run( firstAction.apply, firstAction.value );
77
77
  }
78
78
  }
@@ -0,0 +1,83 @@
1
+ import * as React from 'react';
2
+ import { ControlLabel, type ToggleButtonGroupItem, ToggleControl } from '@elementor/editor-controls';
3
+ import {
4
+ JustifyBottomIcon,
5
+ JustifyCenterIcon as CenterIcon,
6
+ JustifyDistributeVerticalIcon as EvenlyIcon,
7
+ JustifySpaceAroundVerticalIcon as AroundIcon,
8
+ JustifySpaceBetweenVerticalIcon as BetweenIcon,
9
+ JustifyTopIcon,
10
+ } from '@elementor/icons';
11
+ import { DirectionProvider, Stack, ThemeProvider, withDirection } from '@elementor/ui';
12
+ import { __ } from '@wordpress/i18n';
13
+
14
+ import { StylesField } from '../../../controls-registry/styles-field';
15
+ import { useDirection } from '../../../hooks/use-direction';
16
+ import { RotatedIcon } from './utils/rotated-icon';
17
+
18
+ type AlignContent = 'start' | 'center' | 'end' | 'space-between' | 'space-around' | 'space-evenly';
19
+
20
+ const StartIcon = withDirection( JustifyTopIcon );
21
+ const EndIcon = withDirection( JustifyBottomIcon );
22
+
23
+ const iconProps = {
24
+ isClockwise: false,
25
+ offset: 0,
26
+ disableRotationForReversed: true,
27
+ };
28
+
29
+ const options: ToggleButtonGroupItem< AlignContent >[] = [
30
+ {
31
+ value: 'start',
32
+ label: __( 'Start', 'elementor' ),
33
+ renderContent: ( { size } ) => <RotatedIcon icon={ StartIcon } size={ size } { ...iconProps } />,
34
+ showTooltip: true,
35
+ },
36
+ {
37
+ value: 'center',
38
+ label: __( 'Center', 'elementor' ),
39
+ renderContent: ( { size } ) => <RotatedIcon icon={ CenterIcon } size={ size } { ...iconProps } />,
40
+ showTooltip: true,
41
+ },
42
+ {
43
+ value: 'end',
44
+ label: __( 'End', 'elementor' ),
45
+ renderContent: ( { size } ) => <RotatedIcon icon={ EndIcon } size={ size } { ...iconProps } />,
46
+ showTooltip: true,
47
+ },
48
+ {
49
+ value: 'space-between',
50
+ label: __( 'Space between', 'elementor' ),
51
+ renderContent: ( { size } ) => <RotatedIcon icon={ BetweenIcon } size={ size } { ...iconProps } />,
52
+ showTooltip: true,
53
+ },
54
+ {
55
+ value: 'space-around',
56
+ label: __( 'Space around', 'elementor' ),
57
+ renderContent: ( { size } ) => <RotatedIcon icon={ AroundIcon } size={ size } { ...iconProps } />,
58
+ showTooltip: true,
59
+ },
60
+ {
61
+ value: 'space-evenly',
62
+ label: __( 'Space evenly', 'elementor' ),
63
+ renderContent: ( { size } ) => <RotatedIcon icon={ EvenlyIcon } size={ size } { ...iconProps } />,
64
+ showTooltip: true,
65
+ },
66
+ ];
67
+
68
+ export const AlignContentField = () => {
69
+ const { isSiteRtl } = useDirection();
70
+
71
+ return (
72
+ <DirectionProvider rtl={ isSiteRtl }>
73
+ <ThemeProvider>
74
+ <StylesField bind="align-content">
75
+ <Stack gap={ 1 }>
76
+ <ControlLabel>{ __( 'Align content', 'elementor' ) }</ControlLabel>
77
+ <ToggleControl options={ options } fullWidth={ true } />
78
+ </Stack>
79
+ </StylesField>
80
+ </ThemeProvider>
81
+ </DirectionProvider>
82
+ );
83
+ };
@@ -9,6 +9,7 @@ import { useComputedStyle } from '../../../hooks/use-computed-style';
9
9
  import { useStylesField } from '../../../hooks/use-styles-field';
10
10
  import { PanelDivider } from '../../panel-divider';
11
11
  import { SectionContent } from '../../section-content';
12
+ import { AlignContentField } from './align-content-field';
12
13
  import { AlignItemsField } from './align-items-field';
13
14
  import { AlignSelfChild } from './align-self-child-field';
14
15
  import { DisplayField } from './display-field';
@@ -34,16 +35,21 @@ export const LayoutSection = () => {
34
35
  );
35
36
  };
36
37
 
37
- const FlexFields = () => (
38
- <>
39
- <FlexDirectionField />
40
- <JustifyContentField />
41
- <AlignItemsField />
42
- <PanelDivider />
43
- <GapControlField />
44
- <WrapField />
45
- </>
46
- );
38
+ const FlexFields = () => {
39
+ const [ flexWrap ] = useStylesField< StringPropValue >( 'flex-wrap' );
40
+
41
+ return (
42
+ <>
43
+ <FlexDirectionField />
44
+ <JustifyContentField />
45
+ <AlignItemsField />
46
+ <PanelDivider />
47
+ <GapControlField />
48
+ <WrapField />
49
+ { [ 'wrap', 'wrap-reverse' ].includes( flexWrap?.value as string ) && <AlignContentField /> }
50
+ </>
51
+ );
52
+ };
47
53
 
48
54
  const FlexChildFields = () => (
49
55
  <>
@@ -11,6 +11,7 @@ type Props = {
11
11
  size: ToggleButtonProps[ 'size' ];
12
12
  isClockwise?: boolean;
13
13
  offset?: number;
14
+ disableRotationForReversed?: boolean;
14
15
  };
15
16
 
16
17
  const CLOCKWISE_ANGLES: Record< FlexDirection, number > = {
@@ -27,26 +28,40 @@ const COUNTER_CLOCKWISE_ANGLES: Record< FlexDirection, number > = {
27
28
  'column-reverse': -270,
28
29
  };
29
30
 
30
- export const RotatedIcon = ( { icon: Icon, size, isClockwise = true, offset = 0 }: Props ) => {
31
- const rotate = useRef( useGetTargetAngle( isClockwise, offset ) );
32
- rotate.current = useGetTargetAngle( isClockwise, offset, rotate );
31
+ export const RotatedIcon = ( {
32
+ icon: Icon,
33
+ size,
34
+ isClockwise = true,
35
+ offset = 0,
36
+ disableRotationForReversed = false,
37
+ }: Props ) => {
38
+ const rotate = useRef( useGetTargetAngle( isClockwise, offset, disableRotationForReversed ) );
39
+ rotate.current = useGetTargetAngle( isClockwise, offset, disableRotationForReversed, rotate );
33
40
 
34
41
  return <Icon fontSize={ size } sx={ { transition: '.3s', rotate: `${ rotate.current }deg` } } />;
35
42
  };
36
43
 
37
- const useGetTargetAngle = ( isClockwise: boolean, offset: number, existingRef?: React.MutableRefObject< number > ) => {
44
+ const useGetTargetAngle = (
45
+ isClockwise: boolean,
46
+ offset: number,
47
+ disableRotationForReversed: boolean,
48
+ existingRef?: React.MutableRefObject< number >
49
+ ) => {
38
50
  const [ direction ] = useStylesField< StringPropValue >( 'flex-direction' );
39
51
  const isRtl = 'rtl' === useTheme().direction;
40
52
  const rotationMultiplier = isRtl ? -1 : 1;
41
53
  const angleMap = isClockwise ? CLOCKWISE_ANGLES : COUNTER_CLOCKWISE_ANGLES;
42
54
 
43
- const currentAngle = existingRef
44
- ? existingRef.current * rotationMultiplier // Multiply by rotationMultiplier to get the correct angle for RTL, as it will have returned multiplied by this
45
- : angleMap[ ( direction?.value as FlexDirection ) || 'row' ] + offset;
46
- const targetAngle = angleMap[ ( direction?.value as FlexDirection ) || 'row' ] + offset;
55
+ const currentDirection = ( direction?.value as FlexDirection ) || 'row';
56
+ const currentAngle = existingRef ? existingRef.current * rotationMultiplier : angleMap[ currentDirection ] + offset;
57
+ const targetAngle = angleMap[ currentDirection ] + offset;
47
58
 
48
- const diffToTargetAngle = ( targetAngle - currentAngle + 360 ) % 360; // Make sure the diff is between 0, 360;
49
- const formattedDiff = ( ( diffToTargetAngle + 180 ) % 360 ) - 180; // Get the angle to rotate as a value between -180, 180
59
+ const diffToTargetAngle = ( targetAngle - currentAngle + 360 ) % 360;
60
+ const formattedDiff = ( ( diffToTargetAngle + 180 ) % 360 ) - 180;
61
+
62
+ if ( disableRotationForReversed && [ 'row-reverse', 'column-reverse' ].includes( currentDirection ) ) {
63
+ return 0;
64
+ }
50
65
 
51
66
  return ( currentAngle + formattedDiff ) * rotationMultiplier;
52
67
  };
@@ -146,7 +146,7 @@ const DynamicSettings = ( { controls }: { controls: DynamicTag[ 'atomic_controls
146
146
 
147
147
  return (
148
148
  <>
149
- <Tabs indicatorColor="secondary" textColor="secondary" { ...getTabsProps() }>
149
+ <Tabs size="small" variant="fullWidth" { ...getTabsProps() }>
150
150
  { tabs.map( ( { value }, index ) => (
151
151
  <Tab key={ index } label={ value.label } sx={ { px: 1, py: 0.5 } } { ...getTabProps( index ) } />
152
152
  ) ) }