@elementor/editor-variables 0.14.0 → 0.16.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 +76 -0
- package/dist/index.js +743 -500
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +720 -528
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -10
- package/src/components/color-variable-creation.tsx +18 -57
- package/src/components/color-variable-edit.tsx +100 -79
- package/src/components/color-variables-selection.tsx +33 -20
- package/src/components/fields/color-field.tsx +54 -0
- package/src/components/fields/font-field.tsx +85 -0
- package/src/components/fields/label-field.tsx +54 -0
- package/src/components/font-variable-creation.tsx +19 -76
- package/src/components/font-variable-edit.tsx +98 -106
- package/src/components/font-variables-selection.tsx +33 -20
- package/src/components/ui/color-indicator.tsx +1 -0
- package/src/components/ui/delete-confirmation-dialog.tsx +55 -0
- package/src/components/ui/menu-item-content.tsx +29 -23
- package/src/components/ui/no-search-results.tsx +6 -8
- package/src/components/ui/no-variables.tsx +3 -2
- package/src/components/ui/tags/assigned-tag.tsx +43 -0
- package/src/components/ui/tags/deleted-tag.tsx +26 -0
- package/src/components/ui/variable/assigned-variable.tsx +70 -0
- package/src/components/ui/variable/deleted-variable.tsx +20 -0
- package/src/components/variable-selection-popover.context.ts +7 -0
- package/src/components/variable-selection-popover.tsx +17 -8
- package/src/controls/color-variable-control.tsx +15 -72
- package/src/controls/font-variable-control.tsx +14 -71
- package/src/hooks/use-prop-variables.ts +12 -18
- package/src/init-color-variables.ts +3 -48
- package/src/repeater-injections.ts +35 -0
- package/src/utils/validations.ts +42 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { DetachIcon } from '@elementor/icons';
|
|
3
|
+
import { Box, IconButton, Stack, Typography, UnstableTag as Tag, type UnstableTagProps } from '@elementor/ui';
|
|
4
|
+
import { __ } from '@wordpress/i18n';
|
|
5
|
+
|
|
6
|
+
export const SIZE = 'tiny';
|
|
7
|
+
|
|
8
|
+
interface VariableTagProps extends UnstableTagProps {
|
|
9
|
+
onUnlink?: () => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const AssignedTag = ( { startIcon, label, onUnlink, ...props }: VariableTagProps ) => {
|
|
13
|
+
const actions = [];
|
|
14
|
+
|
|
15
|
+
if ( onUnlink ) {
|
|
16
|
+
actions.push(
|
|
17
|
+
<IconButton key="unlink" size={ SIZE } onClick={ onUnlink } aria-label={ __( 'Unlink', 'elementor' ) }>
|
|
18
|
+
<DetachIcon fontSize={ SIZE } />
|
|
19
|
+
</IconButton>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<Tag
|
|
25
|
+
fullWidth
|
|
26
|
+
showActionsOnHover
|
|
27
|
+
startIcon={
|
|
28
|
+
<Stack gap={ 0.5 } direction="row" alignItems="center">
|
|
29
|
+
{ startIcon }
|
|
30
|
+
</Stack>
|
|
31
|
+
}
|
|
32
|
+
label={
|
|
33
|
+
<Box sx={ { display: 'inline-grid', minWidth: 0 } }>
|
|
34
|
+
<Typography sx={ { lineHeight: 1.34 } } variant="caption" noWrap>
|
|
35
|
+
{ label }
|
|
36
|
+
</Typography>
|
|
37
|
+
</Box>
|
|
38
|
+
}
|
|
39
|
+
actions={ actions }
|
|
40
|
+
{ ...props }
|
|
41
|
+
/>
|
|
42
|
+
);
|
|
43
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { ColorFilterIcon } from '@elementor/icons';
|
|
3
|
+
import { Box, Typography, UnstableTag as Tag, type UnstableTagProps } from '@elementor/ui';
|
|
4
|
+
import { __ } from '@wordpress/i18n';
|
|
5
|
+
|
|
6
|
+
export const DeletedTag = ( { label }: UnstableTagProps ) => {
|
|
7
|
+
return (
|
|
8
|
+
<Tag
|
|
9
|
+
showActionsOnHover
|
|
10
|
+
fullWidth
|
|
11
|
+
label={
|
|
12
|
+
<Box sx={ { display: 'inline-grid', minWidth: 0 } }>
|
|
13
|
+
<Typography sx={ { lineHeight: 1.34 } } variant="caption" noWrap>
|
|
14
|
+
{ label }
|
|
15
|
+
</Typography>
|
|
16
|
+
</Box>
|
|
17
|
+
}
|
|
18
|
+
startIcon={ <ColorFilterIcon fontSize="tiny" /> }
|
|
19
|
+
endAdornment={
|
|
20
|
+
<Typography sx={ { lineHeight: 1.34 } } variant="caption" noWrap>
|
|
21
|
+
({ __( 'deleted', 'elementor' ) })
|
|
22
|
+
</Typography>
|
|
23
|
+
}
|
|
24
|
+
/>
|
|
25
|
+
);
|
|
26
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { useId, useRef } from 'react';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { useBoundProp } from '@elementor/editor-controls';
|
|
4
|
+
import { type PropTypeUtil } from '@elementor/editor-props';
|
|
5
|
+
import { ColorFilterIcon } from '@elementor/icons';
|
|
6
|
+
import { bindPopover, bindTrigger, Box, Popover, usePopupState } from '@elementor/ui';
|
|
7
|
+
|
|
8
|
+
import { type Variable } from '../../../types';
|
|
9
|
+
import { VariableSelectionPopover } from '../../variable-selection-popover';
|
|
10
|
+
import { AssignedTag, SIZE } from '../tags/assigned-tag';
|
|
11
|
+
|
|
12
|
+
type Props = {
|
|
13
|
+
variablePropTypeUtil: PropTypeUtil< string, string >;
|
|
14
|
+
fallbackPropTypeUtil: PropTypeUtil< string, string | null > | PropTypeUtil< string, string >;
|
|
15
|
+
additionalStartIcon?: React.ReactNode;
|
|
16
|
+
variable: Variable;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const AssignedVariable = ( {
|
|
20
|
+
variable,
|
|
21
|
+
variablePropTypeUtil,
|
|
22
|
+
fallbackPropTypeUtil,
|
|
23
|
+
additionalStartIcon,
|
|
24
|
+
}: Props ) => {
|
|
25
|
+
const { setValue } = useBoundProp();
|
|
26
|
+
const anchorRef = useRef< HTMLDivElement >( null );
|
|
27
|
+
|
|
28
|
+
const popupId = useId();
|
|
29
|
+
const popupState = usePopupState( {
|
|
30
|
+
variant: 'popover',
|
|
31
|
+
popupId: `elementor-variables-list-${ popupId }`,
|
|
32
|
+
} );
|
|
33
|
+
|
|
34
|
+
const unlinkVariable = () => {
|
|
35
|
+
setValue( fallbackPropTypeUtil.create( variable.value ) );
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<Box ref={ anchorRef }>
|
|
40
|
+
<AssignedTag
|
|
41
|
+
label={ variable.label }
|
|
42
|
+
startIcon={
|
|
43
|
+
<>
|
|
44
|
+
<ColorFilterIcon fontSize={ SIZE } />
|
|
45
|
+
|
|
46
|
+
{ additionalStartIcon }
|
|
47
|
+
</>
|
|
48
|
+
}
|
|
49
|
+
onUnlink={ unlinkVariable }
|
|
50
|
+
{ ...bindTrigger( popupState ) }
|
|
51
|
+
/>
|
|
52
|
+
<Popover
|
|
53
|
+
disableScrollLock
|
|
54
|
+
anchorEl={ anchorRef.current }
|
|
55
|
+
anchorOrigin={ { vertical: 'bottom', horizontal: 'right' } }
|
|
56
|
+
transformOrigin={ { vertical: 'top', horizontal: 'right' } }
|
|
57
|
+
PaperProps={ {
|
|
58
|
+
sx: { my: 1 },
|
|
59
|
+
} }
|
|
60
|
+
{ ...bindPopover( popupState ) }
|
|
61
|
+
>
|
|
62
|
+
<VariableSelectionPopover
|
|
63
|
+
selectedVariable={ variable }
|
|
64
|
+
closePopover={ popupState.close }
|
|
65
|
+
propTypeKey={ variablePropTypeUtil.key }
|
|
66
|
+
/>
|
|
67
|
+
</Popover>
|
|
68
|
+
</Box>
|
|
69
|
+
);
|
|
70
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { useRef } from 'react';
|
|
3
|
+
import { Box } from '@elementor/ui';
|
|
4
|
+
|
|
5
|
+
import { type Variable } from '../../../types';
|
|
6
|
+
import { DeletedTag } from '../tags/deleted-tag';
|
|
7
|
+
|
|
8
|
+
type Props = {
|
|
9
|
+
variable: Variable;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const DeletedVariable = ( { variable }: Props ) => {
|
|
13
|
+
const anchorRef = useRef< HTMLDivElement >( null );
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<Box ref={ anchorRef }>
|
|
17
|
+
<DeletedTag label={ variable.label } />
|
|
18
|
+
</Box>
|
|
19
|
+
);
|
|
20
|
+
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { useRef, useState } from 'react';
|
|
3
|
+
import { Box } from '@elementor/ui';
|
|
3
4
|
|
|
4
5
|
import { colorVariablePropTypeUtil } from '../prop-types/color-variable-prop-type';
|
|
5
6
|
import { fontVariablePropTypeUtil } from '../prop-types/font-variable-prop-type';
|
|
@@ -10,6 +11,7 @@ import { ColorVariablesSelection } from './color-variables-selection';
|
|
|
10
11
|
import { FontVariableCreation } from './font-variable-creation';
|
|
11
12
|
import { FontVariableEdit } from './font-variable-edit';
|
|
12
13
|
import { FontVariablesSelection } from './font-variables-selection';
|
|
14
|
+
import { PopoverContentRefContext } from './variable-selection-popover.context';
|
|
13
15
|
|
|
14
16
|
const VIEW_LIST = 'list';
|
|
15
17
|
const VIEW_ADD = 'add';
|
|
@@ -26,15 +28,22 @@ type Props = {
|
|
|
26
28
|
export const VariableSelectionPopover = ( { closePopover, propTypeKey, selectedVariable }: Props ) => {
|
|
27
29
|
const [ currentView, setCurrentView ] = useState< View >( VIEW_LIST );
|
|
28
30
|
const editIdRef = useRef< string >( '' );
|
|
31
|
+
const anchorRef = useRef< HTMLDivElement >( null );
|
|
29
32
|
|
|
30
|
-
return
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
return (
|
|
34
|
+
<PopoverContentRefContext.Provider value={ anchorRef }>
|
|
35
|
+
<Box ref={ anchorRef }>
|
|
36
|
+
{ renderStage( {
|
|
37
|
+
propTypeKey,
|
|
38
|
+
currentView,
|
|
39
|
+
selectedVariable,
|
|
40
|
+
editIdRef,
|
|
41
|
+
setCurrentView,
|
|
42
|
+
closePopover,
|
|
43
|
+
} ) }
|
|
44
|
+
</Box>
|
|
45
|
+
</PopoverContentRefContext.Provider>
|
|
46
|
+
);
|
|
38
47
|
};
|
|
39
48
|
|
|
40
49
|
type StageProps = {
|
|
@@ -1,90 +1,33 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { useId, useRef } from 'react';
|
|
3
2
|
import { useBoundProp } from '@elementor/editor-controls';
|
|
4
3
|
import { colorPropTypeUtil } from '@elementor/editor-props';
|
|
5
|
-
import { ColorFilterIcon, DetachIcon } from '@elementor/icons';
|
|
6
|
-
import {
|
|
7
|
-
bindPopover,
|
|
8
|
-
bindTrigger,
|
|
9
|
-
Box,
|
|
10
|
-
IconButton,
|
|
11
|
-
Popover,
|
|
12
|
-
Stack,
|
|
13
|
-
Typography,
|
|
14
|
-
UnstableTag as Tag,
|
|
15
|
-
usePopupState,
|
|
16
|
-
} from '@elementor/ui';
|
|
17
|
-
import { __ } from '@wordpress/i18n';
|
|
18
4
|
|
|
19
5
|
import { ColorIndicator } from '../components/ui/color-indicator';
|
|
20
|
-
import {
|
|
6
|
+
import { AssignedVariable } from '../components/ui/variable/assigned-variable';
|
|
7
|
+
import { DeletedVariable } from '../components/ui/variable/deleted-variable';
|
|
21
8
|
import { useVariable } from '../hooks/use-prop-variables';
|
|
22
9
|
import { colorVariablePropTypeUtil } from '../prop-types/color-variable-prop-type';
|
|
23
10
|
|
|
24
|
-
const SIZE = 'tiny';
|
|
25
|
-
|
|
26
11
|
export const ColorVariableControl = () => {
|
|
27
|
-
const { setValue: setColor } = useBoundProp();
|
|
28
12
|
const { value: variableValue } = useBoundProp( colorVariablePropTypeUtil );
|
|
13
|
+
const assignedVariable = useVariable( variableValue );
|
|
29
14
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const popupId = useId();
|
|
33
|
-
const popupState = usePopupState( {
|
|
34
|
-
variant: 'popover',
|
|
35
|
-
popupId: `elementor-variables-list-${ popupId }`,
|
|
36
|
-
} );
|
|
37
|
-
|
|
38
|
-
const selectedVariable = useVariable( variableValue );
|
|
39
|
-
if ( ! selectedVariable ) {
|
|
15
|
+
if ( ! assignedVariable ) {
|
|
40
16
|
throw new Error( `Global color variable ${ variableValue } not found` );
|
|
41
17
|
}
|
|
42
18
|
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
19
|
+
const isVariableDeleted = assignedVariable?.deleted;
|
|
20
|
+
|
|
21
|
+
if ( isVariableDeleted ) {
|
|
22
|
+
return <DeletedVariable variable={ assignedVariable } />;
|
|
23
|
+
}
|
|
46
24
|
|
|
47
25
|
return (
|
|
48
|
-
<
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
<ColorIndicator size="inherit" value={ selectedVariable.value } component="span" />
|
|
55
|
-
<ColorFilterIcon fontSize="inherit" sx={ { mr: 1 } } />
|
|
56
|
-
</Stack>
|
|
57
|
-
}
|
|
58
|
-
label={
|
|
59
|
-
<Box sx={ { display: 'inline-grid', minWidth: 0 } }>
|
|
60
|
-
<Typography
|
|
61
|
-
sx={ { textOverflow: 'ellipsis', overflowX: 'hidden', lineHeight: 1 } }
|
|
62
|
-
variant="caption"
|
|
63
|
-
>
|
|
64
|
-
{ selectedVariable.label }
|
|
65
|
-
</Typography>
|
|
66
|
-
</Box>
|
|
67
|
-
}
|
|
68
|
-
actions={
|
|
69
|
-
<IconButton size={ SIZE } onClick={ unlinkVariable } aria-label={ __( 'Unlink', 'elementor' ) }>
|
|
70
|
-
<DetachIcon fontSize={ SIZE } />
|
|
71
|
-
</IconButton>
|
|
72
|
-
}
|
|
73
|
-
{ ...bindTrigger( popupState ) }
|
|
74
|
-
/>
|
|
75
|
-
<Popover
|
|
76
|
-
disableScrollLock
|
|
77
|
-
anchorEl={ anchorRef.current }
|
|
78
|
-
anchorOrigin={ { vertical: 'bottom', horizontal: 'right' } }
|
|
79
|
-
transformOrigin={ { vertical: 'top', horizontal: 'right' } }
|
|
80
|
-
{ ...bindPopover( popupState ) }
|
|
81
|
-
>
|
|
82
|
-
<VariableSelectionPopover
|
|
83
|
-
selectedVariable={ selectedVariable }
|
|
84
|
-
closePopover={ popupState.close }
|
|
85
|
-
propTypeKey={ colorVariablePropTypeUtil.key }
|
|
86
|
-
/>
|
|
87
|
-
</Popover>
|
|
88
|
-
</Box>
|
|
26
|
+
<AssignedVariable
|
|
27
|
+
variable={ assignedVariable }
|
|
28
|
+
variablePropTypeUtil={ colorVariablePropTypeUtil }
|
|
29
|
+
fallbackPropTypeUtil={ colorPropTypeUtil }
|
|
30
|
+
additionalStartIcon={ <ColorIndicator size="inherit" value={ assignedVariable.value } component="span" /> }
|
|
31
|
+
/>
|
|
89
32
|
);
|
|
90
33
|
};
|
|
@@ -1,88 +1,31 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { useId, useRef } from 'react';
|
|
3
2
|
import { useBoundProp } from '@elementor/editor-controls';
|
|
4
3
|
import { stringPropTypeUtil } from '@elementor/editor-props';
|
|
5
|
-
import { ColorFilterIcon, DetachIcon } from '@elementor/icons';
|
|
6
|
-
import {
|
|
7
|
-
bindPopover,
|
|
8
|
-
bindTrigger,
|
|
9
|
-
Box,
|
|
10
|
-
IconButton,
|
|
11
|
-
Popover,
|
|
12
|
-
Stack,
|
|
13
|
-
Typography,
|
|
14
|
-
UnstableTag as Tag,
|
|
15
|
-
usePopupState,
|
|
16
|
-
} from '@elementor/ui';
|
|
17
|
-
import { __ } from '@wordpress/i18n';
|
|
18
4
|
|
|
19
|
-
import {
|
|
5
|
+
import { AssignedVariable } from '../components/ui/variable/assigned-variable';
|
|
6
|
+
import { DeletedVariable } from '../components/ui/variable/deleted-variable';
|
|
20
7
|
import { useVariable } from '../hooks/use-prop-variables';
|
|
21
8
|
import { fontVariablePropTypeUtil } from '../prop-types/font-variable-prop-type';
|
|
22
9
|
|
|
23
|
-
const SIZE = 'tiny';
|
|
24
|
-
|
|
25
10
|
export const FontVariableControl = () => {
|
|
26
|
-
const { setValue: setFontFamily } = useBoundProp();
|
|
27
11
|
const { value: variableValue } = useBoundProp( fontVariablePropTypeUtil );
|
|
12
|
+
const assignedVariable = useVariable( variableValue );
|
|
28
13
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const popupId = useId();
|
|
32
|
-
const popupState = usePopupState( {
|
|
33
|
-
variant: 'popover',
|
|
34
|
-
popupId: `elementor-variables-list-${ popupId }`,
|
|
35
|
-
} );
|
|
36
|
-
|
|
37
|
-
const selectedVariable = useVariable( variableValue );
|
|
38
|
-
if ( ! selectedVariable ) {
|
|
14
|
+
if ( ! assignedVariable ) {
|
|
39
15
|
throw new Error( `Global font variable ${ variableValue } not found` );
|
|
40
16
|
}
|
|
41
17
|
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
18
|
+
const isVariableDeleted = assignedVariable?.deleted;
|
|
19
|
+
|
|
20
|
+
if ( isVariableDeleted ) {
|
|
21
|
+
return <DeletedVariable variable={ assignedVariable } />;
|
|
22
|
+
}
|
|
45
23
|
|
|
46
24
|
return (
|
|
47
|
-
<
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
<Stack spacing={ 0.75 } direction="row" alignItems="center">
|
|
53
|
-
<ColorFilterIcon fontSize={ 'inherit' } sx={ { mr: 1 } } />
|
|
54
|
-
</Stack>
|
|
55
|
-
}
|
|
56
|
-
label={
|
|
57
|
-
<Box sx={ { display: 'inline-grid', minWidth: 0 } }>
|
|
58
|
-
<Typography
|
|
59
|
-
sx={ { textOverflow: 'ellipsis', overflowX: 'hidden', lineHeight: 1 } }
|
|
60
|
-
variant="caption"
|
|
61
|
-
>
|
|
62
|
-
{ selectedVariable.label }
|
|
63
|
-
</Typography>
|
|
64
|
-
</Box>
|
|
65
|
-
}
|
|
66
|
-
actions={
|
|
67
|
-
<IconButton size={ SIZE } onClick={ unlinkVariable } aria-label={ __( 'Unlink', 'elementor' ) }>
|
|
68
|
-
<DetachIcon fontSize={ SIZE } />
|
|
69
|
-
</IconButton>
|
|
70
|
-
}
|
|
71
|
-
{ ...bindTrigger( popupState ) }
|
|
72
|
-
/>
|
|
73
|
-
<Popover
|
|
74
|
-
disableScrollLock
|
|
75
|
-
anchorEl={ anchorRef.current }
|
|
76
|
-
anchorOrigin={ { vertical: 'bottom', horizontal: 'right' } }
|
|
77
|
-
transformOrigin={ { vertical: 'top', horizontal: 'right' } }
|
|
78
|
-
{ ...bindPopover( popupState ) }
|
|
79
|
-
>
|
|
80
|
-
<VariableSelectionPopover
|
|
81
|
-
selectedVariable={ selectedVariable }
|
|
82
|
-
closePopover={ popupState.close }
|
|
83
|
-
propTypeKey={ fontVariablePropTypeUtil.key }
|
|
84
|
-
/>
|
|
85
|
-
</Popover>
|
|
86
|
-
</Box>
|
|
25
|
+
<AssignedVariable
|
|
26
|
+
variable={ assignedVariable }
|
|
27
|
+
variablePropTypeUtil={ fontVariablePropTypeUtil }
|
|
28
|
+
fallbackPropTypeUtil={ stringPropTypeUtil }
|
|
29
|
+
/>
|
|
87
30
|
);
|
|
88
31
|
};
|
|
@@ -2,8 +2,6 @@ import { useMemo } from 'react';
|
|
|
2
2
|
import { type PropKey } from '@elementor/editor-props';
|
|
3
3
|
|
|
4
4
|
import { service } from '../service';
|
|
5
|
-
import { type TVariable } from '../storage';
|
|
6
|
-
import { styleVariablesRepository } from '../style-variables-repository';
|
|
7
5
|
import { type Variable } from '../types';
|
|
8
6
|
|
|
9
7
|
export const useVariable = ( key: string ) => {
|
|
@@ -37,13 +35,13 @@ const usePropVariables = ( propKey: PropKey ) => {
|
|
|
37
35
|
return useMemo( () => normalizeVariables( propKey ), [ propKey ] );
|
|
38
36
|
};
|
|
39
37
|
|
|
38
|
+
const isNotDeleted = ( { deleted }: { deleted?: boolean } ) => ! deleted;
|
|
39
|
+
|
|
40
40
|
const normalizeVariables = ( propKey: string ) => {
|
|
41
41
|
const variables = service.variables();
|
|
42
42
|
|
|
43
|
-
styleVariablesRepository.update( variables );
|
|
44
|
-
|
|
45
43
|
return Object.entries( variables )
|
|
46
|
-
.filter( ( [ ,
|
|
44
|
+
.filter( ( [ , variable ] ) => variable.type === propKey && isNotDeleted( variable ) )
|
|
47
45
|
.map( ( [ key, { label, value } ] ) => ( {
|
|
48
46
|
key,
|
|
49
47
|
label,
|
|
@@ -52,23 +50,19 @@ const normalizeVariables = ( propKey: string ) => {
|
|
|
52
50
|
};
|
|
53
51
|
|
|
54
52
|
export const createVariable = ( newVariable: Variable ): Promise< string > => {
|
|
55
|
-
return service.create( newVariable ).then( ( { id
|
|
56
|
-
styleVariablesRepository.update( {
|
|
57
|
-
[ id ]: variable,
|
|
58
|
-
} );
|
|
59
|
-
|
|
53
|
+
return service.create( newVariable ).then( ( { id }: { id: string } ) => {
|
|
60
54
|
return id;
|
|
61
55
|
} );
|
|
62
56
|
};
|
|
63
57
|
|
|
64
58
|
export const updateVariable = ( updateId: string, { value, label }: { value: string; label: string } ) => {
|
|
65
|
-
return service
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
[ id ]: variable,
|
|
70
|
-
} );
|
|
59
|
+
return service.update( updateId, { value, label } ).then( ( { id }: { id: string } ) => {
|
|
60
|
+
return id;
|
|
61
|
+
} );
|
|
62
|
+
};
|
|
71
63
|
|
|
72
|
-
|
|
73
|
-
|
|
64
|
+
export const deleteVariable = ( deleteId: string ) => {
|
|
65
|
+
return service.delete( deleteId ).then( ( { id }: { id: string } ) => {
|
|
66
|
+
return id;
|
|
67
|
+
} );
|
|
74
68
|
};
|
|
@@ -1,32 +1,16 @@
|
|
|
1
1
|
import { styleTransformersRegistry } from '@elementor/editor-canvas';
|
|
2
|
-
import { injectIntoRepeaterItemIcon, injectIntoRepeaterItemLabel } from '@elementor/editor-controls';
|
|
3
2
|
import { controlActionsMenu, registerControlReplacement } from '@elementor/editor-editing-panel';
|
|
4
|
-
import { backgroundColorOverlayPropTypeUtil, type PropValue, shadowPropTypeUtil } from '@elementor/editor-props';
|
|
5
3
|
|
|
6
|
-
import {
|
|
7
|
-
BackgroundRepeaterColorIndicator,
|
|
8
|
-
BackgroundRepeaterLabel,
|
|
9
|
-
BoxShadowRepeaterColorIndicator,
|
|
10
|
-
} from './components/variables-repeater-item-slot';
|
|
11
4
|
import { ColorVariableControl } from './controls/color-variable-control';
|
|
12
5
|
import { usePropColorVariableAction } from './hooks/use-prop-color-variable-action';
|
|
13
6
|
import { colorVariablePropTypeUtil } from './prop-types/color-variable-prop-type';
|
|
7
|
+
import { registerRepeaterInjections } from './repeater-injections';
|
|
14
8
|
import { variableTransformer } from './transformers/variable-transformer';
|
|
15
9
|
import { hasAssignedColorVariable } from './utils';
|
|
16
10
|
|
|
17
11
|
const { registerPopoverAction } = controlActionsMenu;
|
|
18
12
|
|
|
19
|
-
|
|
20
|
-
backgroundOverlay: ( { value: prop }: { value: PropValue } ) => {
|
|
21
|
-
return hasAssignedColorVariable( backgroundColorOverlayPropTypeUtil.extract( prop )?.color );
|
|
22
|
-
},
|
|
23
|
-
|
|
24
|
-
boxShadow: ( { value: prop }: { value: PropValue } ) => {
|
|
25
|
-
return hasAssignedColorVariable( shadowPropTypeUtil.extract( prop )?.color );
|
|
26
|
-
},
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
function registerControlsAndActions() {
|
|
13
|
+
export function initColorVariables() {
|
|
30
14
|
registerControlReplacement( {
|
|
31
15
|
component: ColorVariableControl,
|
|
32
16
|
condition: ( { value } ) => hasAssignedColorVariable( value ),
|
|
@@ -36,37 +20,8 @@ function registerControlsAndActions() {
|
|
|
36
20
|
id: 'color-variables',
|
|
37
21
|
useProps: usePropColorVariableAction,
|
|
38
22
|
} );
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function registerRepeaterItemIcons() {
|
|
42
|
-
injectIntoRepeaterItemIcon( {
|
|
43
|
-
id: 'color-variables-background-icon',
|
|
44
|
-
component: BackgroundRepeaterColorIndicator,
|
|
45
|
-
condition: conditions.backgroundOverlay,
|
|
46
|
-
} );
|
|
47
|
-
|
|
48
|
-
injectIntoRepeaterItemIcon( {
|
|
49
|
-
id: 'color-variables-icon',
|
|
50
|
-
component: BoxShadowRepeaterColorIndicator,
|
|
51
|
-
condition: conditions.boxShadow,
|
|
52
|
-
} );
|
|
53
|
-
}
|
|
54
23
|
|
|
55
|
-
function registerRepeaterItemLabels() {
|
|
56
|
-
injectIntoRepeaterItemLabel( {
|
|
57
|
-
id: 'color-variables-label',
|
|
58
|
-
component: BackgroundRepeaterLabel,
|
|
59
|
-
condition: conditions.backgroundOverlay,
|
|
60
|
-
} );
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function registerStyleTransformers() {
|
|
64
24
|
styleTransformersRegistry.register( colorVariablePropTypeUtil.key, variableTransformer );
|
|
65
|
-
}
|
|
66
25
|
|
|
67
|
-
|
|
68
|
-
registerControlsAndActions();
|
|
69
|
-
registerRepeaterItemIcons();
|
|
70
|
-
registerRepeaterItemLabels();
|
|
71
|
-
registerStyleTransformers();
|
|
26
|
+
registerRepeaterInjections();
|
|
72
27
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { injectIntoRepeaterItemIcon, injectIntoRepeaterItemLabel } from '@elementor/editor-controls';
|
|
2
|
+
import { backgroundColorOverlayPropTypeUtil, type PropValue, shadowPropTypeUtil } from '@elementor/editor-props';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
BackgroundRepeaterColorIndicator,
|
|
6
|
+
BackgroundRepeaterLabel,
|
|
7
|
+
BoxShadowRepeaterColorIndicator,
|
|
8
|
+
} from './components/variables-repeater-item-slot';
|
|
9
|
+
import { hasAssignedColorVariable } from './utils';
|
|
10
|
+
|
|
11
|
+
export function registerRepeaterInjections() {
|
|
12
|
+
injectIntoRepeaterItemIcon( {
|
|
13
|
+
id: 'color-variables-background-icon',
|
|
14
|
+
component: BackgroundRepeaterColorIndicator,
|
|
15
|
+
condition: ( { value: prop }: { value: PropValue } ) => {
|
|
16
|
+
return hasAssignedColorVariable( backgroundColorOverlayPropTypeUtil.extract( prop )?.color );
|
|
17
|
+
},
|
|
18
|
+
} );
|
|
19
|
+
|
|
20
|
+
injectIntoRepeaterItemIcon( {
|
|
21
|
+
id: 'color-variables-icon',
|
|
22
|
+
component: BoxShadowRepeaterColorIndicator,
|
|
23
|
+
condition: ( { value: prop }: { value: PropValue } ) => {
|
|
24
|
+
return hasAssignedColorVariable( shadowPropTypeUtil.extract( prop )?.color );
|
|
25
|
+
},
|
|
26
|
+
} );
|
|
27
|
+
|
|
28
|
+
injectIntoRepeaterItemLabel( {
|
|
29
|
+
id: 'color-variables-label',
|
|
30
|
+
component: BackgroundRepeaterLabel,
|
|
31
|
+
condition: ( { value: prop }: { value: PropValue } ) => {
|
|
32
|
+
return hasAssignedColorVariable( backgroundColorOverlayPropTypeUtil.extract( prop )?.color );
|
|
33
|
+
},
|
|
34
|
+
} );
|
|
35
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { __ } from '@wordpress/i18n';
|
|
2
|
+
|
|
3
|
+
export const VARIABLE_LABEL_MAX_LENGTH = 50;
|
|
4
|
+
|
|
5
|
+
export const validateLabel = ( name: string ): string => {
|
|
6
|
+
if ( ! name.trim() ) {
|
|
7
|
+
return __( 'Missing variable name.', 'elementor' );
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const allowedChars = /^[a-zA-Z0-9_-]+$/;
|
|
11
|
+
if ( ! allowedChars.test( name ) ) {
|
|
12
|
+
return __( 'Names can only use letters, numbers, dashes (-) and underscores (_).', 'elementor' );
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const hasAlphanumeric = /[a-zA-Z0-9]/;
|
|
16
|
+
if ( ! hasAlphanumeric.test( name ) ) {
|
|
17
|
+
return __( 'Names have to include at least one non-special character.', 'elementor' );
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if ( VARIABLE_LABEL_MAX_LENGTH < name.length ) {
|
|
21
|
+
return __( 'Variable names can contain up to 50 characters.', 'elementor' );
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return '';
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const labelHint = ( name: string ): string => {
|
|
28
|
+
const hintThreshold = VARIABLE_LABEL_MAX_LENGTH * 0.8 - 1;
|
|
29
|
+
if ( hintThreshold < name.length ) {
|
|
30
|
+
return __( 'Variable names can contain up to 50 characters.', 'elementor' );
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return '';
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const validateValue = ( value: string ): string => {
|
|
37
|
+
if ( ! value.trim() ) {
|
|
38
|
+
return __( 'Missing variable value.', 'elementor' );
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return '';
|
|
42
|
+
};
|