@elementor/editor-editing-panel 1.2.0 → 1.4.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 +48 -0
- package/dist/index.js +852 -615
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +790 -544
- package/dist/index.mjs.map +1 -1
- package/package.json +13 -11
- package/src/components/conditional-tooltip-wrapper.tsx +52 -0
- package/src/components/css-class-selector.tsx +59 -19
- package/src/components/multi-combobox/types.ts +1 -0
- package/src/components/style-sections/border-section/border-radius-field.tsx +4 -4
- package/src/components/style-sections/border-section/border-width-field.tsx +4 -4
- package/src/components/style-sections/layout-section/align-items-field.tsx +40 -60
- package/src/components/style-sections/layout-section/align-self-child-field.tsx +72 -0
- package/src/components/style-sections/layout-section/flex-direction-field.tsx +2 -2
- package/src/components/style-sections/layout-section/flex-order-field.tsx +14 -8
- package/src/components/style-sections/layout-section/flex-size-field.tsx +164 -0
- package/src/components/style-sections/layout-section/gap-control-field.tsx +18 -0
- package/src/components/style-sections/layout-section/justify-content-field.tsx +51 -78
- package/src/components/style-sections/layout-section/layout-section.tsx +9 -2
- package/src/components/style-sections/layout-section/utils/rotated-icon.tsx +52 -0
- package/src/components/style-sections/layout-section/wrap-field.tsx +1 -1
- package/src/components/style-sections/position-section/position-section.tsx +3 -3
- package/src/components/style-sections/typography-section/line-height-field.tsx +21 -0
- package/src/components/style-sections/typography-section/text-style-field.tsx +31 -8
- package/src/components/style-sections/typography-section/typography-section.tsx +3 -1
- package/src/components/style-tab.tsx +2 -11
- package/src/contexts/style-context.tsx +2 -2
- package/src/controls-registry/controls-registry.tsx +4 -0
- package/src/dynamics/components/dynamic-selection-control.tsx +8 -5
- package/src/dynamics/components/dynamic-selection.tsx +10 -8
- package/src/dynamics/dynamic-control.tsx +9 -11
- package/src/dynamics/utils.ts +20 -3
- package/src/hooks/use-style-prop-history.ts +1 -1
- package/src/hooks/use-styles-field.ts +2 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import {
|
|
3
|
+
ControlLabel,
|
|
4
|
+
ControlToggleButtonGroup,
|
|
5
|
+
NumberControl,
|
|
6
|
+
SizeControl,
|
|
7
|
+
type ToggleButtonGroupItem,
|
|
8
|
+
} from '@elementor/editor-controls';
|
|
9
|
+
import type { NumberPropValue, SizePropValue } from '@elementor/editor-props';
|
|
10
|
+
import { ExpandIcon, PencilIcon, ShrinkIcon } from '@elementor/icons';
|
|
11
|
+
import { DirectionProvider, Grid, Stack, ThemeProvider } 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 { useStylesField } from '../../../hooks/use-styles-field';
|
|
17
|
+
|
|
18
|
+
type GroupItem = 'flex-grow' | 'flex-shrink' | 'custom';
|
|
19
|
+
|
|
20
|
+
export const DEFAULT = 1;
|
|
21
|
+
|
|
22
|
+
const items: ToggleButtonGroupItem< GroupItem >[] = [
|
|
23
|
+
{
|
|
24
|
+
value: 'flex-grow',
|
|
25
|
+
label: __( 'Grow', 'elementor' ),
|
|
26
|
+
renderContent: ( { size } ) => <ExpandIcon fontSize={ size } />,
|
|
27
|
+
showTooltip: true,
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
value: 'flex-shrink',
|
|
31
|
+
label: __( 'Shrink', 'elementor' ),
|
|
32
|
+
renderContent: ( { size } ) => <ShrinkIcon fontSize={ size } />,
|
|
33
|
+
showTooltip: true,
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
value: 'custom',
|
|
37
|
+
label: __( 'Custom', 'elementor' ),
|
|
38
|
+
renderContent: ( { size } ) => <PencilIcon fontSize={ size } />,
|
|
39
|
+
showTooltip: true,
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
export const FlexSizeField = () => {
|
|
44
|
+
const { isSiteRtl } = useDirection(),
|
|
45
|
+
[ growField, setGrowField ] = useStylesField< NumberPropValue | null >( 'flex-grow' ),
|
|
46
|
+
[ shrinkField, setShrinkField ] = useStylesField< NumberPropValue | null >( 'flex-shrink' ),
|
|
47
|
+
[ basisField, setBasisField ] = useStylesField< SizePropValue | null >( 'flex-basis' );
|
|
48
|
+
|
|
49
|
+
const grow = growField?.value || null,
|
|
50
|
+
shrink = shrinkField?.value || null,
|
|
51
|
+
basis = basisField?.value || null;
|
|
52
|
+
|
|
53
|
+
const currentGroup = React.useMemo( () => getActiveGroup( { grow, shrink, basis } ), [ grow, shrink, basis ] ),
|
|
54
|
+
[ activeGroup, setActiveGroup ] = React.useState( currentGroup );
|
|
55
|
+
|
|
56
|
+
const onChangeGroup = ( group: GroupItem | null = null ) => {
|
|
57
|
+
setActiveGroup( group );
|
|
58
|
+
setBasisField( null );
|
|
59
|
+
|
|
60
|
+
if ( ! group || group === 'custom' ) {
|
|
61
|
+
setGrowField( null );
|
|
62
|
+
setShrinkField( null );
|
|
63
|
+
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if ( group === 'flex-grow' ) {
|
|
68
|
+
setGrowField( { $$type: 'number', value: DEFAULT } );
|
|
69
|
+
setShrinkField( null );
|
|
70
|
+
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
setGrowField( null );
|
|
75
|
+
setShrinkField( { $$type: 'number', value: DEFAULT } );
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<DirectionProvider rtl={ isSiteRtl }>
|
|
80
|
+
<ThemeProvider>
|
|
81
|
+
<Stack gap={ 2 }>
|
|
82
|
+
<Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
|
|
83
|
+
<Grid item xs={ 6 }>
|
|
84
|
+
<ControlLabel>{ __( 'Size', 'elementor' ) }</ControlLabel>
|
|
85
|
+
</Grid>
|
|
86
|
+
<Grid item xs={ 6 } sx={ { display: 'flex', justifyContent: 'end' } }>
|
|
87
|
+
<ControlToggleButtonGroup
|
|
88
|
+
value={ activeGroup }
|
|
89
|
+
onChange={ onChangeGroup }
|
|
90
|
+
items={ items }
|
|
91
|
+
exclusive={ true }
|
|
92
|
+
/>
|
|
93
|
+
</Grid>
|
|
94
|
+
</Grid>
|
|
95
|
+
|
|
96
|
+
{ 'custom' === activeGroup && <FlexCustomField /> }
|
|
97
|
+
</Stack>
|
|
98
|
+
</ThemeProvider>
|
|
99
|
+
</DirectionProvider>
|
|
100
|
+
);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const FlexCustomField = () => (
|
|
104
|
+
<>
|
|
105
|
+
<StylesField bind={ 'flex-grow' }>
|
|
106
|
+
<Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
|
|
107
|
+
<Grid item xs={ 6 }>
|
|
108
|
+
<ControlLabel>{ __( 'Grow', 'elementor' ) }</ControlLabel>
|
|
109
|
+
</Grid>
|
|
110
|
+
<Grid item xs={ 6 } sx={ { display: 'flex', justifyContent: 'end' } }>
|
|
111
|
+
<NumberControl min={ 0 } shouldForceInt={ true } />
|
|
112
|
+
</Grid>
|
|
113
|
+
</Grid>
|
|
114
|
+
</StylesField>
|
|
115
|
+
<StylesField bind={ 'flex-shrink' }>
|
|
116
|
+
<Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
|
|
117
|
+
<Grid item xs={ 6 }>
|
|
118
|
+
<ControlLabel>{ __( 'Shrink', 'elementor' ) }</ControlLabel>
|
|
119
|
+
</Grid>
|
|
120
|
+
<Grid item xs={ 6 } sx={ { display: 'flex', justifyContent: 'end' } }>
|
|
121
|
+
<NumberControl min={ 0 } shouldForceInt={ true } />
|
|
122
|
+
</Grid>
|
|
123
|
+
</Grid>
|
|
124
|
+
</StylesField>
|
|
125
|
+
<StylesField bind={ 'flex-basis' }>
|
|
126
|
+
<Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
|
|
127
|
+
<Grid item xs={ 6 }>
|
|
128
|
+
<ControlLabel>{ __( 'Basis', 'elementor' ) }</ControlLabel>
|
|
129
|
+
</Grid>
|
|
130
|
+
<Grid item xs={ 6 } sx={ { display: 'flex', justifyContent: 'end' } }>
|
|
131
|
+
<SizeControl />
|
|
132
|
+
</Grid>
|
|
133
|
+
</Grid>
|
|
134
|
+
</StylesField>
|
|
135
|
+
</>
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
const getActiveGroup = ( {
|
|
139
|
+
grow,
|
|
140
|
+
shrink,
|
|
141
|
+
basis,
|
|
142
|
+
}: {
|
|
143
|
+
grow: NumberPropValue[ 'value' ] | null;
|
|
144
|
+
shrink: NumberPropValue[ 'value' ] | null;
|
|
145
|
+
basis: SizePropValue[ 'value' ] | null;
|
|
146
|
+
} ): GroupItem | null => {
|
|
147
|
+
if ( null === grow && null === shrink && ! basis ) {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if ( ( shrink && grow ) || basis ) {
|
|
152
|
+
return 'custom';
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if ( grow === DEFAULT ) {
|
|
156
|
+
return 'flex-grow';
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if ( shrink === DEFAULT ) {
|
|
160
|
+
return 'flex-shrink';
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return 'custom';
|
|
164
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { GapControl } from '@elementor/editor-controls';
|
|
3
|
+
import { Stack } from '@elementor/ui';
|
|
4
|
+
import { __ } from '@wordpress/i18n';
|
|
5
|
+
|
|
6
|
+
import { StylesField } from '../../../controls-registry/styles-field';
|
|
7
|
+
|
|
8
|
+
export type Gap = 'column' | 'row';
|
|
9
|
+
|
|
10
|
+
export const GapControlField = () => {
|
|
11
|
+
return (
|
|
12
|
+
<Stack gap={ 1 }>
|
|
13
|
+
<StylesField bind={ 'gap' }>
|
|
14
|
+
<GapControl label={ __( 'Gaps', 'elementor' ) } />
|
|
15
|
+
</StylesField>
|
|
16
|
+
</Stack>
|
|
17
|
+
);
|
|
18
|
+
};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { type JSX } from 'react';
|
|
3
2
|
import { ControlLabel, type ToggleButtonGroupItem, ToggleControl } from '@elementor/editor-controls';
|
|
4
3
|
import {
|
|
5
4
|
JustifyBottomIcon,
|
|
@@ -9,33 +8,71 @@ import {
|
|
|
9
8
|
JustifySpaceBetweenVerticalIcon as BetweenIcon,
|
|
10
9
|
JustifyTopIcon,
|
|
11
10
|
} from '@elementor/icons';
|
|
12
|
-
import {
|
|
13
|
-
DirectionProvider,
|
|
14
|
-
Stack,
|
|
15
|
-
ThemeProvider,
|
|
16
|
-
type ToggleButtonProps,
|
|
17
|
-
useTheme,
|
|
18
|
-
withDirection,
|
|
19
|
-
} from '@elementor/ui';
|
|
11
|
+
import { DirectionProvider, Stack, ThemeProvider, withDirection } from '@elementor/ui';
|
|
20
12
|
import { __ } from '@wordpress/i18n';
|
|
21
13
|
|
|
22
14
|
import { StylesField } from '../../../controls-registry/styles-field';
|
|
23
15
|
import { useDirection } from '../../../hooks/use-direction';
|
|
24
|
-
import {
|
|
25
|
-
import { type FlexDirection } from './flex-direction-field';
|
|
16
|
+
import { RotatedIcon } from './utils/rotated-icon';
|
|
26
17
|
|
|
27
18
|
type JustifyContent = 'start' | 'center' | 'end' | 'space-between' | 'space-around' | 'space-evenly';
|
|
28
19
|
|
|
20
|
+
const StartIcon = withDirection( JustifyTopIcon );
|
|
21
|
+
const EndIcon = withDirection( JustifyBottomIcon );
|
|
22
|
+
|
|
23
|
+
const iconProps = {
|
|
24
|
+
isClockwise: true,
|
|
25
|
+
offset: -90,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const options: ToggleButtonGroupItem< JustifyContent >[] = [
|
|
29
|
+
{
|
|
30
|
+
value: 'start',
|
|
31
|
+
label: __( 'Start', 'elementor' ),
|
|
32
|
+
renderContent: ( { size } ) => <RotatedIcon icon={ StartIcon } size={ size } { ...iconProps } />,
|
|
33
|
+
showTooltip: true,
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
value: 'center',
|
|
37
|
+
label: __( 'Center', 'elementor' ),
|
|
38
|
+
renderContent: ( { size } ) => <RotatedIcon icon={ CenterIcon } size={ size } { ...iconProps } />,
|
|
39
|
+
showTooltip: true,
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
value: 'end',
|
|
43
|
+
label: __( 'End', 'elementor' ),
|
|
44
|
+
renderContent: ( { size } ) => <RotatedIcon icon={ EndIcon } size={ size } { ...iconProps } />,
|
|
45
|
+
showTooltip: true,
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
value: 'space-between',
|
|
49
|
+
label: __( 'Space between', 'elementor' ),
|
|
50
|
+
renderContent: ( { size } ) => <RotatedIcon icon={ BetweenIcon } size={ size } { ...iconProps } />,
|
|
51
|
+
showTooltip: true,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
value: 'space-around',
|
|
55
|
+
label: __( 'Space around', 'elementor' ),
|
|
56
|
+
renderContent: ( { size } ) => <RotatedIcon icon={ AroundIcon } size={ size } { ...iconProps } />,
|
|
57
|
+
showTooltip: true,
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
value: 'space-evenly',
|
|
61
|
+
label: __( 'Space evenly', 'elementor' ),
|
|
62
|
+
renderContent: ( { size } ) => <RotatedIcon icon={ EvenlyIcon } size={ size } { ...iconProps } />,
|
|
63
|
+
showTooltip: true,
|
|
64
|
+
},
|
|
65
|
+
];
|
|
66
|
+
|
|
29
67
|
export const JustifyContentField = () => {
|
|
30
|
-
const
|
|
31
|
-
{ isSiteRtl } = useDirection();
|
|
68
|
+
const { isSiteRtl } = useDirection();
|
|
32
69
|
|
|
33
70
|
return (
|
|
34
71
|
<DirectionProvider rtl={ isSiteRtl }>
|
|
35
72
|
<ThemeProvider>
|
|
36
73
|
<StylesField bind="justify-content">
|
|
37
74
|
<Stack gap={ 1 }>
|
|
38
|
-
<ControlLabel>{ __( 'Justify
|
|
75
|
+
<ControlLabel>{ __( 'Justify content', 'elementor' ) }</ControlLabel>
|
|
39
76
|
<ToggleControl options={ options } fullWidth={ true } />
|
|
40
77
|
</Stack>
|
|
41
78
|
</StylesField>
|
|
@@ -43,67 +80,3 @@ export const JustifyContentField = () => {
|
|
|
43
80
|
</DirectionProvider>
|
|
44
81
|
);
|
|
45
82
|
};
|
|
46
|
-
|
|
47
|
-
const useOptions = (): ToggleButtonGroupItem< JustifyContent >[] => {
|
|
48
|
-
const StartIcon = withDirection( JustifyTopIcon ),
|
|
49
|
-
EndIcon = withDirection( JustifyBottomIcon );
|
|
50
|
-
|
|
51
|
-
return [
|
|
52
|
-
{
|
|
53
|
-
value: 'start',
|
|
54
|
-
label: __( 'Start', 'elementor' ),
|
|
55
|
-
renderContent: ( { size } ) => <RotatedIcon icon={ StartIcon } size={ size } />,
|
|
56
|
-
showTooltip: true,
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
value: 'center',
|
|
60
|
-
label: __( 'Center', 'elementor' ),
|
|
61
|
-
renderContent: ( { size } ) => <RotatedIcon icon={ CenterIcon } size={ size } />,
|
|
62
|
-
showTooltip: true,
|
|
63
|
-
},
|
|
64
|
-
{
|
|
65
|
-
value: 'end',
|
|
66
|
-
label: __( 'End', 'elementor' ),
|
|
67
|
-
renderContent: ( { size } ) => <RotatedIcon icon={ EndIcon } size={ size } />,
|
|
68
|
-
showTooltip: true,
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
value: 'space-between',
|
|
72
|
-
label: __( 'Space between', 'elementor' ),
|
|
73
|
-
renderContent: ( { size } ) => <RotatedIcon icon={ BetweenIcon } size={ size } />,
|
|
74
|
-
showTooltip: true,
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
value: 'space-around',
|
|
78
|
-
label: __( 'Space around', 'elementor' ),
|
|
79
|
-
renderContent: ( { size } ) => <RotatedIcon icon={ AroundIcon } size={ size } />,
|
|
80
|
-
showTooltip: true,
|
|
81
|
-
},
|
|
82
|
-
{
|
|
83
|
-
value: 'space-evenly',
|
|
84
|
-
label: __( 'Space evenly', 'elementor' ),
|
|
85
|
-
renderContent: ( { size } ) => <RotatedIcon icon={ EvenlyIcon } size={ size } />,
|
|
86
|
-
showTooltip: true,
|
|
87
|
-
},
|
|
88
|
-
];
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
const RotatedIcon = ( { icon: Icon, size }: { icon: JSX.ElementType; size: ToggleButtonProps[ 'size' ] } ) => {
|
|
92
|
-
const [ direction ] = useStylesField< FlexDirection >( 'flex-direction' ),
|
|
93
|
-
isRtl = 'rtl' === useTheme().direction,
|
|
94
|
-
rotationMultiplier = isRtl ? -1 : 1;
|
|
95
|
-
|
|
96
|
-
const rotationAngelMap: Record< FlexDirection, number > = {
|
|
97
|
-
row: -90,
|
|
98
|
-
column: 0,
|
|
99
|
-
'row-reverse': 90,
|
|
100
|
-
'column-reverse': 180,
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
return (
|
|
104
|
-
<Icon
|
|
105
|
-
fontSize={ size }
|
|
106
|
-
sx={ { transition: '.3s', rotate: `${ rotationAngelMap[ direction || 'row' ] * rotationMultiplier }deg` } }
|
|
107
|
-
/>
|
|
108
|
-
);
|
|
109
|
-
};
|
|
@@ -1,23 +1,27 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { ControlLabel } from '@elementor/editor-controls';
|
|
3
|
+
import { type StringPropValue } from '@elementor/editor-props';
|
|
3
4
|
import { Divider, Stack } from '@elementor/ui';
|
|
4
5
|
import { __ } from '@wordpress/i18n';
|
|
5
6
|
|
|
6
7
|
import { useStylesField } from '../../../hooks/use-styles-field';
|
|
7
8
|
import { AlignItemsField } from './align-items-field';
|
|
9
|
+
import { AlignSelfChild } from './align-self-child-field';
|
|
8
10
|
import { DisplayField } from './display-field';
|
|
9
11
|
import { FlexDirectionField } from './flex-direction-field';
|
|
10
12
|
import { FlexOrderField } from './flex-order-field';
|
|
13
|
+
import { FlexSizeField } from './flex-size-field';
|
|
14
|
+
import { GapControlField } from './gap-control-field';
|
|
11
15
|
import { JustifyContentField } from './justify-content-field';
|
|
12
16
|
import { WrapField } from './wrap-field';
|
|
13
17
|
|
|
14
18
|
export const LayoutSection = () => {
|
|
15
|
-
const [ display ] = useStylesField( 'display' );
|
|
19
|
+
const [ display ] = useStylesField< StringPropValue >( 'display' );
|
|
16
20
|
|
|
17
21
|
return (
|
|
18
22
|
<Stack gap={ 2 }>
|
|
19
23
|
<DisplayField />
|
|
20
|
-
{ 'flex' === display && <FlexFields /> }
|
|
24
|
+
{ 'flex' === display?.value && <FlexFields /> }
|
|
21
25
|
</Stack>
|
|
22
26
|
);
|
|
23
27
|
};
|
|
@@ -28,9 +32,12 @@ const FlexFields = () => (
|
|
|
28
32
|
<JustifyContentField />
|
|
29
33
|
<AlignItemsField />
|
|
30
34
|
<Divider />
|
|
35
|
+
<GapControlField />
|
|
31
36
|
<WrapField />
|
|
32
37
|
<Divider />
|
|
33
38
|
<ControlLabel>{ __( 'Flex child', 'elementor' ) }</ControlLabel>
|
|
39
|
+
<AlignSelfChild />
|
|
34
40
|
<FlexOrderField />
|
|
41
|
+
<FlexSizeField />
|
|
35
42
|
</>
|
|
36
43
|
);
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { useRef } from 'react';
|
|
3
|
+
import { type StringPropValue } from '@elementor/editor-props';
|
|
4
|
+
import { type ToggleButtonProps, useTheme } from '@elementor/ui';
|
|
5
|
+
|
|
6
|
+
import { useStylesField } from '../../../../hooks/use-styles-field';
|
|
7
|
+
import type { FlexDirection } from '../flex-direction-field';
|
|
8
|
+
|
|
9
|
+
type Props = {
|
|
10
|
+
icon: React.JSX.ElementType;
|
|
11
|
+
size: ToggleButtonProps[ 'size' ];
|
|
12
|
+
isClockwise?: boolean;
|
|
13
|
+
offset?: number;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const CLOCKWISE_ANGLES: Record< FlexDirection, number > = {
|
|
17
|
+
row: 0,
|
|
18
|
+
column: 90,
|
|
19
|
+
'row-reverse': 180,
|
|
20
|
+
'column-reverse': 270,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const COUNTER_CLOCKWISE_ANGLES: Record< FlexDirection, number > = {
|
|
24
|
+
row: 0,
|
|
25
|
+
column: -90,
|
|
26
|
+
'row-reverse': -180,
|
|
27
|
+
'column-reverse': -270,
|
|
28
|
+
};
|
|
29
|
+
|
|
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 );
|
|
33
|
+
|
|
34
|
+
return <Icon fontSize={ size } sx={ { transition: '.3s', rotate: `${ rotate.current }deg` } } />;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const useGetTargetAngle = ( isClockwise: boolean, offset: number, existingRef?: React.MutableRefObject< number > ) => {
|
|
38
|
+
const [ direction ] = useStylesField< StringPropValue >( 'flex-direction' );
|
|
39
|
+
const isRtl = 'rtl' === useTheme().direction;
|
|
40
|
+
const rotationMultiplier = isRtl ? -1 : 1;
|
|
41
|
+
const angleMap = isClockwise ? CLOCKWISE_ANGLES : COUNTER_CLOCKWISE_ANGLES;
|
|
42
|
+
|
|
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;
|
|
47
|
+
|
|
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
|
|
50
|
+
|
|
51
|
+
return ( currentAngle + formattedDiff ) * rotationMultiplier;
|
|
52
|
+
};
|
|
@@ -24,7 +24,7 @@ const options: ToggleButtonGroupItem< FlexWrap >[] = [
|
|
|
24
24
|
},
|
|
25
25
|
{
|
|
26
26
|
value: 'wrap-reverse',
|
|
27
|
-
label: __( '
|
|
27
|
+
label: __( 'Reversed wrap', 'elementor' ),
|
|
28
28
|
renderContent: ( { size } ) => <ArrowForwardIcon fontSize={ size } />,
|
|
29
29
|
showTooltip: true,
|
|
30
30
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { useCallback } from 'react';
|
|
3
|
-
import type { PropValue } from '@elementor/editor-props';
|
|
3
|
+
import type { PropValue, StringPropValue } from '@elementor/editor-props';
|
|
4
4
|
import { Stack } from '@elementor/ui';
|
|
5
5
|
|
|
6
6
|
import { useStylePropsHistory } from '../../../hooks/use-style-prop-history';
|
|
@@ -12,10 +12,10 @@ import { ZIndexField } from './z-index-field';
|
|
|
12
12
|
const dimensionsPropKeys = [ 'top', 'bottom', 'left', 'right' ];
|
|
13
13
|
|
|
14
14
|
export const PositionSection = () => {
|
|
15
|
-
const [ positionValue ] = useStylesField( 'position' );
|
|
15
|
+
const [ positionValue ] = useStylesField< StringPropValue >( 'position' );
|
|
16
16
|
usePositionChangeHandler();
|
|
17
17
|
|
|
18
|
-
const isNotStatic = positionValue && positionValue !== 'static';
|
|
18
|
+
const isNotStatic = positionValue && positionValue?.value !== 'static';
|
|
19
19
|
|
|
20
20
|
return (
|
|
21
21
|
<Stack gap={ 1.5 }>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { ControlLabel, SizeControl } from '@elementor/editor-controls';
|
|
3
|
+
import { Grid } from '@elementor/ui';
|
|
4
|
+
import { __ } from '@wordpress/i18n';
|
|
5
|
+
|
|
6
|
+
import { StylesField } from '../../../controls-registry/styles-field';
|
|
7
|
+
|
|
8
|
+
export const LineHeightField = () => {
|
|
9
|
+
return (
|
|
10
|
+
<StylesField bind="line-height">
|
|
11
|
+
<Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
|
|
12
|
+
<Grid item xs={ 6 }>
|
|
13
|
+
<ControlLabel>{ __( 'Line Height', 'elementor' ) }</ControlLabel>
|
|
14
|
+
</Grid>
|
|
15
|
+
<Grid item xs={ 6 }>
|
|
16
|
+
<SizeControl />
|
|
17
|
+
</Grid>
|
|
18
|
+
</Grid>
|
|
19
|
+
</StylesField>
|
|
20
|
+
);
|
|
21
|
+
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { ControlLabel } from '@elementor/editor-controls';
|
|
3
|
+
import { type StringPropValue } from '@elementor/editor-props';
|
|
3
4
|
import { ItalicIcon, StrikethroughIcon, UnderlineIcon } from '@elementor/icons';
|
|
4
5
|
import { Grid, ToggleButton as ToggleButtonBase, ToggleButtonGroup, type ToggleButtonProps } from '@elementor/ui';
|
|
5
6
|
import { __ } from '@wordpress/i18n';
|
|
@@ -9,10 +10,32 @@ import { useStylesField } from '../../../hooks/use-styles-field';
|
|
|
9
10
|
const buttonSize = 'tiny';
|
|
10
11
|
|
|
11
12
|
export const TextStyleField = () => {
|
|
12
|
-
const [ fontStyle, setFontStyle ] = useStylesField<
|
|
13
|
-
const [ textDecoration, setTextDecoration ] = useStylesField<
|
|
13
|
+
const [ fontStyle, setFontStyle ] = useStylesField< StringPropValue | null >( 'font-style' );
|
|
14
|
+
const [ textDecoration, setTextDecoration ] = useStylesField< StringPropValue | null >( 'text-decoration' );
|
|
14
15
|
|
|
15
|
-
const formats = [ fontStyle, ...( textDecoration || '' ).split( ' ' ) ];
|
|
16
|
+
const formats = [ fontStyle?.value, ...( textDecoration?.value || '' ).split( ' ' ) ];
|
|
17
|
+
|
|
18
|
+
const handleSetFontStyle = ( newValue: string | null ) => {
|
|
19
|
+
if ( newValue === null ) {
|
|
20
|
+
return setFontStyle( null );
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
setFontStyle( {
|
|
24
|
+
$$type: 'string',
|
|
25
|
+
value: newValue,
|
|
26
|
+
} );
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const handleSetTextDecoration = ( newValue: string | null ) => {
|
|
30
|
+
if ( newValue === null ) {
|
|
31
|
+
return setTextDecoration( null );
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
setTextDecoration( {
|
|
35
|
+
$$type: 'string',
|
|
36
|
+
value: newValue,
|
|
37
|
+
} );
|
|
38
|
+
};
|
|
16
39
|
|
|
17
40
|
return (
|
|
18
41
|
<Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
|
|
@@ -23,7 +46,7 @@ export const TextStyleField = () => {
|
|
|
23
46
|
<ToggleButtonGroup value={ formats }>
|
|
24
47
|
<ToggleButton
|
|
25
48
|
value="italic"
|
|
26
|
-
onChange={ ( v ) =>
|
|
49
|
+
onChange={ ( v ) => handleSetFontStyle( fontStyle?.value === v ? null : v ) }
|
|
27
50
|
aria-label="italic"
|
|
28
51
|
sx={ { marginLeft: 'auto' } }
|
|
29
52
|
>
|
|
@@ -31,16 +54,16 @@ export const TextStyleField = () => {
|
|
|
31
54
|
</ToggleButton>
|
|
32
55
|
<ShorthandControl
|
|
33
56
|
value="line-through"
|
|
34
|
-
currentValues={ textDecoration || '' }
|
|
35
|
-
updateValues={
|
|
57
|
+
currentValues={ textDecoration?.value || '' }
|
|
58
|
+
updateValues={ handleSetTextDecoration }
|
|
36
59
|
aria-label="line-through"
|
|
37
60
|
>
|
|
38
61
|
<StrikethroughIcon fontSize={ buttonSize } />
|
|
39
62
|
</ShorthandControl>
|
|
40
63
|
<ShorthandControl
|
|
41
64
|
value="underline"
|
|
42
|
-
currentValues={ textDecoration || '' }
|
|
43
|
-
updateValues={
|
|
65
|
+
currentValues={ textDecoration?.value || '' }
|
|
66
|
+
updateValues={ handleSetTextDecoration }
|
|
44
67
|
aria-label="underline"
|
|
45
68
|
>
|
|
46
69
|
<UnderlineIcon fontSize={ buttonSize } />
|
|
@@ -6,6 +6,7 @@ import { FontFamilyField } from './font-family-field';
|
|
|
6
6
|
import { FontSizeField } from './font-size-field';
|
|
7
7
|
import { FontWeightField } from './font-weight-field';
|
|
8
8
|
import { LetterSpacingField } from './letter-spacing-field';
|
|
9
|
+
import { LineHeightField } from './line-height-field';
|
|
9
10
|
import { TextAlignmentField } from './text-alignment-field';
|
|
10
11
|
import { TextColorField } from './text-color-field';
|
|
11
12
|
import { TextDirectionField } from './text-direction-field';
|
|
@@ -21,13 +22,14 @@ export const TypographySection = () => {
|
|
|
21
22
|
<FontWeightField />
|
|
22
23
|
<FontSizeField />
|
|
23
24
|
<Divider />
|
|
25
|
+
<TextAlignmentField />
|
|
24
26
|
<TextColorField />
|
|
25
27
|
<CollapsibleContent>
|
|
26
28
|
<Stack gap={ 1.5 } sx={ { pt: 1.5 } }>
|
|
29
|
+
<LineHeightField />
|
|
27
30
|
<LetterSpacingField />
|
|
28
31
|
<WordSpacingField />
|
|
29
32
|
<Divider />
|
|
30
|
-
<TextAlignmentField />
|
|
31
33
|
<TextStyleField />
|
|
32
34
|
<TransformField />
|
|
33
35
|
<TextDirectionField />
|
|
@@ -3,7 +3,7 @@ import { useState } from 'react';
|
|
|
3
3
|
import { useElementSetting, useElementStyles } from '@elementor/editor-elements';
|
|
4
4
|
import { type ClassesPropValue, type PropKey } from '@elementor/editor-props';
|
|
5
5
|
import { useActiveBreakpoint } from '@elementor/editor-responsive';
|
|
6
|
-
import {
|
|
6
|
+
import { type StyleDefinition } from '@elementor/editor-styles';
|
|
7
7
|
import { Divider } from '@elementor/ui';
|
|
8
8
|
import { __ } from '@wordpress/i18n';
|
|
9
9
|
|
|
@@ -70,9 +70,7 @@ function useActiveStyleDefId( currentClassesProp: PropKey ) {
|
|
|
70
70
|
|
|
71
71
|
const fallback = useFirstElementStyleDef( currentClassesProp );
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
return [ activeStyledDefId || fallback?.id || newId, setActiveStyledDefId ] as const;
|
|
73
|
+
return [ activeStyledDefId || fallback?.id || null, setActiveStyledDefId ] as const;
|
|
76
74
|
}
|
|
77
75
|
|
|
78
76
|
function useFirstElementStyleDef( currentClassesProp: PropKey ) {
|
|
@@ -84,13 +82,6 @@ function useFirstElementStyleDef( currentClassesProp: PropKey ) {
|
|
|
84
82
|
return Object.values( stylesDefs ).find( ( styleDef ) => classesIds.includes( styleDef.id ) );
|
|
85
83
|
}
|
|
86
84
|
|
|
87
|
-
function useGeneratedId() {
|
|
88
|
-
const { element } = useElement();
|
|
89
|
-
const stylesDefs = useElementStyles( element.id );
|
|
90
|
-
|
|
91
|
-
return generateId( `e-${ element.id }-`, Object.keys( stylesDefs ) );
|
|
92
|
-
}
|
|
93
|
-
|
|
94
85
|
function useCurrentClassesProp(): string {
|
|
95
86
|
const { elementType } = useElement();
|
|
96
87
|
|
|
@@ -3,8 +3,8 @@ import { createContext, type Dispatch, type PropsWithChildren, useContext } from
|
|
|
3
3
|
import { type StyleDefinition, type StyleVariant } from '@elementor/editor-styles';
|
|
4
4
|
|
|
5
5
|
type ContextValue = {
|
|
6
|
-
id: StyleDefinition[ 'id' ];
|
|
7
|
-
setId: Dispatch< StyleDefinition[ 'id' ] >;
|
|
6
|
+
id: StyleDefinition[ 'id' ] | null;
|
|
7
|
+
setId: Dispatch< StyleDefinition[ 'id' ] | null >;
|
|
8
8
|
meta: StyleVariant[ 'meta' ];
|
|
9
9
|
};
|
|
10
10
|
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type ControlComponent,
|
|
3
3
|
ImageControl,
|
|
4
|
+
LinkControl,
|
|
4
5
|
SelectControl,
|
|
5
6
|
SizeControl,
|
|
6
7
|
TextAreaControl,
|
|
7
8
|
TextControl,
|
|
9
|
+
UrlControl,
|
|
8
10
|
} from '@elementor/editor-controls';
|
|
9
11
|
|
|
10
12
|
export type ControlLayout = 'full' | 'two-columns';
|
|
@@ -17,6 +19,8 @@ const controlTypes = {
|
|
|
17
19
|
textarea: { component: TextAreaControl, layout: 'full' },
|
|
18
20
|
size: { component: SizeControl, layout: 'two-columns' },
|
|
19
21
|
select: { component: SelectControl, layout: 'two-columns' },
|
|
22
|
+
link: { component: LinkControl, layout: 'full' },
|
|
23
|
+
url: { component: UrlControl, layout: 'full' },
|
|
20
24
|
} as const satisfies ControlRegistry;
|
|
21
25
|
|
|
22
26
|
export type ControlType = keyof typeof controlTypes;
|