@elementor/editor-variables 0.15.0 → 0.18.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 +88 -0
- package/dist/index.js +894 -486
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +892 -511
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -9
- package/src/components/color-variable-creation.tsx +37 -58
- package/src/components/color-variable-edit.tsx +110 -86
- package/src/components/color-variables-selection.tsx +32 -34
- 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 +39 -78
- package/src/components/font-variable-edit.tsx +108 -114
- package/src/components/font-variables-selection.tsx +32 -34
- package/src/components/ui/delete-confirmation-dialog.tsx +52 -0
- package/src/components/ui/deleted-variable-alert.tsx +47 -0
- package/src/components/ui/menu-item-content.tsx +2 -5
- package/src/components/ui/tags/assigned-tag.tsx +45 -0
- package/src/components/ui/tags/deleted-tag.tsx +37 -0
- package/src/components/ui/variable/assigned-variable.tsx +70 -0
- package/src/components/ui/variable/deleted-variable.tsx +76 -0
- package/src/controls/color-variable-control.tsx +21 -48
- package/src/controls/font-variable-control.tsx +20 -43
- package/src/create-style-variables-repository.ts +44 -5
- package/src/hooks/use-prop-variables.ts +6 -0
- package/src/init-color-variables.ts +3 -48
- package/src/renderers/style-variables-renderer.tsx +10 -4
- package/src/repeater-injections.ts +35 -0
- package/src/service.ts +23 -2
- package/src/sync/enqueue-font.ts +7 -0
- package/src/sync/types.ts +5 -0
- package/src/transformers/variable-transformer.ts +21 -3
- package/src/types.ts +1 -1
- package/src/utils/validations.ts +42 -0
- package/src/components/ui/variable-tag.tsx +0 -43
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { EllipsisWithTooltip, type VirtualizedItem } from '@elementor/editor-ui';
|
|
3
|
-
import { isExperimentActive } from '@elementor/editor-v1-adapters';
|
|
4
3
|
import { EditIcon } from '@elementor/icons';
|
|
5
4
|
import { Box, IconButton, ListItemIcon, Typography } from '@elementor/ui';
|
|
6
5
|
import { __ } from '@wordpress/i18n';
|
|
7
6
|
|
|
8
7
|
const SIZE = 'tiny';
|
|
9
8
|
|
|
10
|
-
const isVersion330Active = isExperimentActive( 'e_v_3_30' );
|
|
11
|
-
|
|
12
9
|
export const MenuItemContent = < T, V extends string >( { item }: { item: VirtualizedItem< T, V > } ) => {
|
|
13
10
|
const onEdit = item.onEdit as ( ( value: V ) => void ) | undefined;
|
|
14
11
|
|
|
@@ -27,8 +24,8 @@ export const MenuItemContent = < T, V extends string >( { item }: { item: Virtua
|
|
|
27
24
|
<EllipsisWithTooltip
|
|
28
25
|
title={ item.label || item.value }
|
|
29
26
|
as={ Typography }
|
|
30
|
-
variant=
|
|
31
|
-
color=
|
|
27
|
+
variant="caption"
|
|
28
|
+
color="text.primary"
|
|
32
29
|
sx={ { marginTop: '1px', lineHeight: '2' } }
|
|
33
30
|
maxWidth="50%"
|
|
34
31
|
/>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { DetachIcon } from '@elementor/icons';
|
|
3
|
+
import { Box, IconButton, Stack, Tooltip, 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
|
+
<Tooltip title={ label } placement="top">
|
|
25
|
+
<Tag
|
|
26
|
+
fullWidth
|
|
27
|
+
showActionsOnHover
|
|
28
|
+
startIcon={
|
|
29
|
+
<Stack gap={ 0.5 } direction="row" alignItems="center">
|
|
30
|
+
{ startIcon }
|
|
31
|
+
</Stack>
|
|
32
|
+
}
|
|
33
|
+
label={
|
|
34
|
+
<Box sx={ { display: 'inline-grid', minWidth: 0 } }>
|
|
35
|
+
<Typography sx={ { lineHeight: 1.34 } } variant="caption" noWrap>
|
|
36
|
+
{ label }
|
|
37
|
+
</Typography>
|
|
38
|
+
</Box>
|
|
39
|
+
}
|
|
40
|
+
actions={ actions }
|
|
41
|
+
{ ...props }
|
|
42
|
+
/>
|
|
43
|
+
</Tooltip>
|
|
44
|
+
);
|
|
45
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { AlertTriangleFilledIcon } from '@elementor/icons';
|
|
3
|
+
import { Box, Chip, type ChipProps, type Theme, Tooltip, Typography } from '@elementor/ui';
|
|
4
|
+
import { __ } from '@wordpress/i18n';
|
|
5
|
+
|
|
6
|
+
export const DeletedTag = React.forwardRef< HTMLDivElement, ChipProps >( ( { label, onClick, ...props }, ref ) => {
|
|
7
|
+
return (
|
|
8
|
+
<Chip
|
|
9
|
+
ref={ ref }
|
|
10
|
+
size="tiny"
|
|
11
|
+
color="warning"
|
|
12
|
+
shape="rounded"
|
|
13
|
+
variant="standard"
|
|
14
|
+
onClick={ onClick }
|
|
15
|
+
icon={ <AlertTriangleFilledIcon /> }
|
|
16
|
+
label={
|
|
17
|
+
<Tooltip title={ label } placement="top">
|
|
18
|
+
<Box sx={ { display: 'flex', gap: 0.5, alignItems: 'center' } }>
|
|
19
|
+
<Typography variant="caption" noWrap>
|
|
20
|
+
{ label }
|
|
21
|
+
</Typography>
|
|
22
|
+
<Typography variant="caption" noWrap sx={ { textOverflow: 'initial', overflow: 'visible' } }>
|
|
23
|
+
({ __( 'deleted', 'elementor' ) })
|
|
24
|
+
</Typography>
|
|
25
|
+
</Box>
|
|
26
|
+
</Tooltip>
|
|
27
|
+
}
|
|
28
|
+
sx={ {
|
|
29
|
+
height: ( theme: Theme ) => theme.spacing( 3.5 ),
|
|
30
|
+
borderRadius: ( theme: Theme ) => theme.spacing( 1 ),
|
|
31
|
+
justifyContent: 'flex-start',
|
|
32
|
+
width: '100%',
|
|
33
|
+
} }
|
|
34
|
+
{ ...props }
|
|
35
|
+
/>
|
|
36
|
+
);
|
|
37
|
+
} );
|
|
@@ -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,76 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { useBoundProp } from '@elementor/editor-controls';
|
|
4
|
+
import { type PropTypeUtil } from '@elementor/editor-props';
|
|
5
|
+
import { isExperimentActive } from '@elementor/editor-v1-adapters';
|
|
6
|
+
import { Backdrop, Infotip } from '@elementor/ui';
|
|
7
|
+
|
|
8
|
+
import { restoreVariable } from '../../../hooks/use-prop-variables';
|
|
9
|
+
import { type Variable } from '../../../types';
|
|
10
|
+
import { DeletedVariableAlert } from '../deleted-variable-alert';
|
|
11
|
+
import { DeletedTag } from '../tags/deleted-tag';
|
|
12
|
+
|
|
13
|
+
const isV331Active = isExperimentActive( 'e_v_3_31' );
|
|
14
|
+
|
|
15
|
+
type Props = {
|
|
16
|
+
variable: Variable;
|
|
17
|
+
variablePropTypeUtil: PropTypeUtil< string, string >;
|
|
18
|
+
fallbackPropTypeUtil: PropTypeUtil< string, string | null > | PropTypeUtil< string, string >;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const DeletedVariable = ( { variable, variablePropTypeUtil, fallbackPropTypeUtil }: Props ) => {
|
|
22
|
+
const { setValue } = useBoundProp();
|
|
23
|
+
const [ showInfotip, setShowInfotip ] = useState< boolean >( false );
|
|
24
|
+
|
|
25
|
+
const toggleInfotip = () => setShowInfotip( ( prev ) => ! prev );
|
|
26
|
+
|
|
27
|
+
const closeInfotip = () => setShowInfotip( false );
|
|
28
|
+
|
|
29
|
+
const unlinkVariable = () => {
|
|
30
|
+
setValue( fallbackPropTypeUtil.create( variable.value ) );
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const handleRestore = () => {
|
|
34
|
+
if ( ! variable.key ) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
restoreVariable( variable.key ).then( ( key ) => {
|
|
39
|
+
setValue( variablePropTypeUtil.create( key ) );
|
|
40
|
+
closeInfotip();
|
|
41
|
+
} );
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<>
|
|
46
|
+
{ showInfotip && <Backdrop open onClick={ closeInfotip } invisible /> }
|
|
47
|
+
<Infotip
|
|
48
|
+
color="warning"
|
|
49
|
+
placement="right-start"
|
|
50
|
+
open={ showInfotip }
|
|
51
|
+
disableHoverListener
|
|
52
|
+
onClose={ closeInfotip }
|
|
53
|
+
content={
|
|
54
|
+
<DeletedVariableAlert
|
|
55
|
+
onClose={ closeInfotip }
|
|
56
|
+
onUnlink={ unlinkVariable }
|
|
57
|
+
onRestore={ isV331Active ? handleRestore : undefined }
|
|
58
|
+
label={ variable.label }
|
|
59
|
+
/>
|
|
60
|
+
}
|
|
61
|
+
slotProps={ {
|
|
62
|
+
popper: {
|
|
63
|
+
modifiers: [
|
|
64
|
+
{
|
|
65
|
+
name: 'offset',
|
|
66
|
+
options: { offset: [ 0, 24 ] },
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
} }
|
|
71
|
+
>
|
|
72
|
+
<DeletedTag label={ variable.label } onClick={ toggleInfotip } />
|
|
73
|
+
</Infotip>
|
|
74
|
+
</>
|
|
75
|
+
);
|
|
76
|
+
};
|
|
@@ -1,66 +1,39 @@
|
|
|
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 } from '@elementor/icons';
|
|
6
|
-
import { bindPopover, bindTrigger, Box, Popover, usePopupState } from '@elementor/ui';
|
|
7
4
|
|
|
8
5
|
import { ColorIndicator } from '../components/ui/color-indicator';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
6
|
+
import { AssignedVariable } from '../components/ui/variable/assigned-variable';
|
|
7
|
+
import { DeletedVariable } from '../components/ui/variable/deleted-variable';
|
|
11
8
|
import { useVariable } from '../hooks/use-prop-variables';
|
|
12
9
|
import { colorVariablePropTypeUtil } from '../prop-types/color-variable-prop-type';
|
|
13
10
|
|
|
14
11
|
export const ColorVariableControl = () => {
|
|
15
|
-
const { setValue: setColor } = useBoundProp();
|
|
16
12
|
const { value: variableValue } = useBoundProp( colorVariablePropTypeUtil );
|
|
13
|
+
const assignedVariable = useVariable( variableValue );
|
|
17
14
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const popupId = useId();
|
|
21
|
-
const popupState = usePopupState( {
|
|
22
|
-
variant: 'popover',
|
|
23
|
-
popupId: `elementor-variables-list-${ popupId }`,
|
|
24
|
-
} );
|
|
25
|
-
|
|
26
|
-
const selectedVariable = useVariable( variableValue );
|
|
27
|
-
if ( ! selectedVariable ) {
|
|
15
|
+
if ( ! assignedVariable ) {
|
|
28
16
|
throw new Error( `Global color variable ${ variableValue } not found` );
|
|
29
17
|
}
|
|
30
18
|
|
|
31
|
-
const
|
|
32
|
-
setColor( colorPropTypeUtil.create( selectedVariable.value ) );
|
|
33
|
-
};
|
|
19
|
+
const isVariableDeleted = assignedVariable?.deleted;
|
|
34
20
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
<
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
<ColorFilterIcon fontSize={ SIZE } />
|
|
42
|
-
<ColorIndicator size="inherit" value={ selectedVariable.value } component="span" />
|
|
43
|
-
</>
|
|
44
|
-
}
|
|
45
|
-
onUnlink={ unlinkVariable }
|
|
46
|
-
{ ...bindTrigger( popupState ) }
|
|
21
|
+
if ( isVariableDeleted ) {
|
|
22
|
+
return (
|
|
23
|
+
<DeletedVariable
|
|
24
|
+
variable={ assignedVariable }
|
|
25
|
+
variablePropTypeUtil={ colorVariablePropTypeUtil }
|
|
26
|
+
fallbackPropTypeUtil={ colorPropTypeUtil }
|
|
47
27
|
/>
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
<VariableSelectionPopover
|
|
59
|
-
selectedVariable={ selectedVariable }
|
|
60
|
-
closePopover={ popupState.close }
|
|
61
|
-
propTypeKey={ colorVariablePropTypeUtil.key }
|
|
62
|
-
/>
|
|
63
|
-
</Popover>
|
|
64
|
-
</Box>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<AssignedVariable
|
|
33
|
+
variable={ assignedVariable }
|
|
34
|
+
variablePropTypeUtil={ colorVariablePropTypeUtil }
|
|
35
|
+
fallbackPropTypeUtil={ colorPropTypeUtil }
|
|
36
|
+
additionalStartIcon={ <ColorIndicator size="inherit" value={ assignedVariable.value } component="span" /> }
|
|
37
|
+
/>
|
|
65
38
|
);
|
|
66
39
|
};
|
|
@@ -1,60 +1,37 @@
|
|
|
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 } from '@elementor/icons';
|
|
6
|
-
import { bindPopover, bindTrigger, Box, Popover, usePopupState } from '@elementor/ui';
|
|
7
4
|
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
5
|
+
import { AssignedVariable } from '../components/ui/variable/assigned-variable';
|
|
6
|
+
import { DeletedVariable } from '../components/ui/variable/deleted-variable';
|
|
10
7
|
import { useVariable } from '../hooks/use-prop-variables';
|
|
11
8
|
import { fontVariablePropTypeUtil } from '../prop-types/font-variable-prop-type';
|
|
12
9
|
|
|
13
10
|
export const FontVariableControl = () => {
|
|
14
|
-
const { setValue: setFontFamily } = useBoundProp();
|
|
15
11
|
const { value: variableValue } = useBoundProp( fontVariablePropTypeUtil );
|
|
12
|
+
const assignedVariable = useVariable( variableValue );
|
|
16
13
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const popupId = useId();
|
|
20
|
-
const popupState = usePopupState( {
|
|
21
|
-
variant: 'popover',
|
|
22
|
-
popupId: `elementor-variables-list-${ popupId }`,
|
|
23
|
-
} );
|
|
24
|
-
|
|
25
|
-
const selectedVariable = useVariable( variableValue );
|
|
26
|
-
if ( ! selectedVariable ) {
|
|
14
|
+
if ( ! assignedVariable ) {
|
|
27
15
|
throw new Error( `Global font variable ${ variableValue } not found` );
|
|
28
16
|
}
|
|
29
17
|
|
|
30
|
-
const
|
|
31
|
-
setFontFamily( stringPropTypeUtil.create( selectedVariable.value ) );
|
|
32
|
-
};
|
|
18
|
+
const isVariableDeleted = assignedVariable?.deleted;
|
|
33
19
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
<
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
{ ...bindTrigger( popupState ) }
|
|
20
|
+
if ( isVariableDeleted ) {
|
|
21
|
+
return (
|
|
22
|
+
<DeletedVariable
|
|
23
|
+
variable={ assignedVariable }
|
|
24
|
+
variablePropTypeUtil={ fontVariablePropTypeUtil }
|
|
25
|
+
fallbackPropTypeUtil={ stringPropTypeUtil }
|
|
41
26
|
/>
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
>
|
|
52
|
-
<VariableSelectionPopover
|
|
53
|
-
selectedVariable={ selectedVariable }
|
|
54
|
-
closePopover={ popupState.close }
|
|
55
|
-
propTypeKey={ fontVariablePropTypeUtil.key }
|
|
56
|
-
/>
|
|
57
|
-
</Popover>
|
|
58
|
-
</Box>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<AssignedVariable
|
|
32
|
+
variable={ assignedVariable }
|
|
33
|
+
variablePropTypeUtil={ fontVariablePropTypeUtil }
|
|
34
|
+
fallbackPropTypeUtil={ stringPropTypeUtil }
|
|
35
|
+
/>
|
|
59
36
|
);
|
|
60
37
|
};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { fontVariablePropTypeUtil } from './prop-types/font-variable-prop-type';
|
|
2
|
+
import { enqueueFont } from './sync/enqueue-font';
|
|
1
3
|
import { type StyleVariables, type Variable } from './types';
|
|
2
4
|
|
|
3
5
|
type VariablesChangeCallback = ( variables: StyleVariables ) => void;
|
|
@@ -21,16 +23,41 @@ export const createStyleVariablesRepository = () => {
|
|
|
21
23
|
}
|
|
22
24
|
};
|
|
23
25
|
|
|
24
|
-
const shouldUpdate = ( key: string,
|
|
25
|
-
|
|
26
|
+
const shouldUpdate = ( key: string, maybeUpdated: Variable ): boolean => {
|
|
27
|
+
if ( ! ( key in variables ) ) {
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if ( variables[ key ].label !== maybeUpdated.label ) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if ( variables[ key ].value !== maybeUpdated.value ) {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if ( ! variables[ key ]?.deleted && maybeUpdated?.deleted ) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if ( variables[ key ]?.deleted && ! maybeUpdated?.deleted ) {
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return false;
|
|
26
48
|
};
|
|
27
49
|
|
|
28
50
|
const applyUpdates = ( updatedVars: Variables ): boolean => {
|
|
29
51
|
let hasChanges = false;
|
|
30
52
|
|
|
31
|
-
for ( const [ key,
|
|
32
|
-
if ( shouldUpdate( key,
|
|
33
|
-
variables[ key ] =
|
|
53
|
+
for ( const [ key, variable ] of Object.entries( updatedVars ) ) {
|
|
54
|
+
if ( shouldUpdate( key, variable ) ) {
|
|
55
|
+
variables[ key ] = variable;
|
|
56
|
+
|
|
57
|
+
if ( variable.type === fontVariablePropTypeUtil.key ) {
|
|
58
|
+
fontEnqueue( variable.value );
|
|
59
|
+
}
|
|
60
|
+
|
|
34
61
|
hasChanges = true;
|
|
35
62
|
}
|
|
36
63
|
}
|
|
@@ -38,6 +65,18 @@ export const createStyleVariablesRepository = () => {
|
|
|
38
65
|
return hasChanges;
|
|
39
66
|
};
|
|
40
67
|
|
|
68
|
+
const fontEnqueue = ( value: string ): void => {
|
|
69
|
+
if ( ! value ) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
enqueueFont( value );
|
|
75
|
+
} catch {
|
|
76
|
+
// This prevents font enqueueing failures from breaking variable updates
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
41
80
|
const update = ( updatedVars: Variables ) => {
|
|
42
81
|
if ( applyUpdates( updatedVars ) ) {
|
|
43
82
|
notify();
|
|
@@ -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
|
}
|
|
@@ -5,7 +5,7 @@ import { Portal } from '@elementor/ui';
|
|
|
5
5
|
|
|
6
6
|
import { styleVariablesRepository } from '../style-variables-repository';
|
|
7
7
|
import { getCanvasIframeDocument } from '../sync/get-canvas-iframe-document';
|
|
8
|
-
import { type StyleVariables } from '../types';
|
|
8
|
+
import { type StyleVariables, type Variable } from '../types';
|
|
9
9
|
|
|
10
10
|
const VARIABLES_WRAPPER = 'body';
|
|
11
11
|
|
|
@@ -49,8 +49,14 @@ function useStyleVariables() {
|
|
|
49
49
|
return variables;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
function cssVariableDeclaration( key: string, variable: Variable ) {
|
|
53
|
+
const variableName = variable?.deleted ? key : variable.label;
|
|
54
|
+
const value = variable.value;
|
|
55
|
+
|
|
56
|
+
return `--${ variableName }:${ value };`;
|
|
57
|
+
}
|
|
58
|
+
|
|
52
59
|
function convertToCssVariables( variables: StyleVariables ): string {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
.join( '' );
|
|
60
|
+
const listOfVariables = Object.entries( variables );
|
|
61
|
+
return listOfVariables.map( ( [ key, variable ] ) => cssVariableDeclaration( key, variable ) ).join( '' );
|
|
56
62
|
}
|
|
@@ -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
|
+
}
|