@elementor/editor-variables 0.13.0 → 0.15.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 +84 -0
- package/dist/index.js +938 -344
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +950 -335
- package/dist/index.mjs.map +1 -1
- package/package.json +11 -10
- package/src/components/color-variable-creation.tsx +39 -16
- package/src/components/color-variable-edit.tsx +133 -0
- package/src/components/color-variables-selection.tsx +111 -52
- package/src/components/font-variable-creation.tsx +30 -16
- package/src/components/font-variable-edit.tsx +163 -0
- package/src/components/font-variables-selection.tsx +110 -51
- package/src/components/{color-indicator.tsx → ui/color-indicator.tsx} +1 -0
- package/src/components/ui/menu-item-content.tsx +60 -0
- package/src/components/ui/no-search-results.tsx +36 -0
- package/src/components/ui/no-variables.tsx +36 -0
- package/src/components/ui/styled-menu-list.tsx +31 -0
- package/src/components/ui/variable-tag.tsx +43 -0
- package/src/components/variable-selection-popover.context.ts +7 -0
- package/src/components/variable-selection-popover.tsx +142 -0
- package/src/components/variables-repeater-item-slot.tsx +29 -0
- package/src/controls/color-variable-control.tsx +66 -0
- package/src/controls/font-variable-control.tsx +60 -0
- package/src/create-style-variables-repository.ts +3 -2
- package/src/hooks/use-prop-color-variable-action.tsx +7 -2
- package/src/hooks/use-prop-font-variable-action.tsx +7 -2
- package/src/hooks/use-prop-variables.ts +34 -13
- package/src/init-color-variables.ts +51 -3
- package/src/init-font-variables.ts +2 -2
- package/src/service.ts +23 -3
- package/src/storage.ts +5 -1
- package/src/types.ts +12 -8
- package/src/components/styled-menu-item.tsx +0 -10
- package/src/components/variables-selection-popover.tsx +0 -119
- package/src/controls/color-variables-selection-control.tsx +0 -34
- package/src/controls/font-variables-selection-control.tsx +0 -29
|
@@ -1,70 +1,129 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { useState } from 'react';
|
|
3
3
|
import { useBoundProp } from '@elementor/editor-controls';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { PopoverScrollableContent } from '@elementor/editor-editing-panel';
|
|
5
|
+
import { PopoverHeader, PopoverMenuList, PopoverSearch, type VirtualizedItem } from '@elementor/editor-ui';
|
|
6
|
+
import { ColorFilterIcon, PlusIcon, SettingsIcon, TextIcon } from '@elementor/icons';
|
|
7
|
+
import { Divider, IconButton } from '@elementor/ui';
|
|
8
|
+
import { __ } from '@wordpress/i18n';
|
|
6
9
|
|
|
7
|
-
import {
|
|
10
|
+
import { useFilteredVariables } from '../hooks/use-prop-variables';
|
|
8
11
|
import { fontVariablePropTypeUtil } from '../prop-types/font-variable-prop-type';
|
|
9
|
-
import { type
|
|
10
|
-
import {
|
|
12
|
+
import { type ExtendedVirtualizedItem } from '../types';
|
|
13
|
+
import { MenuItemContent } from './ui/menu-item-content';
|
|
14
|
+
import { NoSearchResults } from './ui/no-search-results';
|
|
15
|
+
import { NoVariables } from './ui/no-variables';
|
|
16
|
+
import { VariablesStyledMenuList } from './ui/styled-menu-list';
|
|
17
|
+
|
|
18
|
+
const SIZE = 'tiny';
|
|
11
19
|
|
|
12
20
|
type Props = {
|
|
13
|
-
|
|
21
|
+
closePopover: () => void;
|
|
22
|
+
onAdd?: () => void;
|
|
23
|
+
onEdit?: ( key: string ) => void;
|
|
24
|
+
onSettings?: () => void;
|
|
14
25
|
};
|
|
15
26
|
|
|
16
|
-
export const FontVariablesSelection = ( {
|
|
27
|
+
export const FontVariablesSelection = ( { closePopover, onAdd, onEdit, onSettings }: Props ) => {
|
|
17
28
|
const { value: variable, setValue: setVariable } = useBoundProp( fontVariablePropTypeUtil );
|
|
29
|
+
const [ searchValue, setSearchValue ] = useState( '' );
|
|
18
30
|
|
|
19
|
-
const
|
|
31
|
+
const {
|
|
32
|
+
list: variables,
|
|
33
|
+
hasMatches: hasSearchResults,
|
|
34
|
+
isSourceNotEmpty: hasVariables,
|
|
35
|
+
} = useFilteredVariables( searchValue, fontVariablePropTypeUtil.key );
|
|
20
36
|
|
|
21
|
-
const handleSetVariable = ( key:
|
|
37
|
+
const handleSetVariable = ( key: string ) => {
|
|
22
38
|
setVariable( key );
|
|
39
|
+
closePopover();
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const actions = [];
|
|
43
|
+
|
|
44
|
+
if ( onAdd ) {
|
|
45
|
+
actions.push(
|
|
46
|
+
<IconButton key="add" size={ SIZE } onClick={ onAdd }>
|
|
47
|
+
<PlusIcon fontSize={ SIZE } />
|
|
48
|
+
</IconButton>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
23
51
|
|
|
24
|
-
|
|
52
|
+
if ( onSettings ) {
|
|
53
|
+
actions.push(
|
|
54
|
+
<IconButton key="settings" size={ SIZE } onClick={ onSettings }>
|
|
55
|
+
<SettingsIcon fontSize={ SIZE } />
|
|
56
|
+
</IconButton>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const items: ExtendedVirtualizedItem[] = variables.map( ( { value, label, key } ) => ( {
|
|
61
|
+
type: 'item' as const,
|
|
62
|
+
value: key,
|
|
63
|
+
label,
|
|
64
|
+
icon: <TextIcon fontSize={ SIZE } />,
|
|
65
|
+
secondaryText: value,
|
|
66
|
+
onEdit: () => onEdit?.( key ),
|
|
67
|
+
} ) );
|
|
68
|
+
|
|
69
|
+
const handleSearch = ( search: string ) => {
|
|
70
|
+
setSearchValue( search );
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const handleClearSearch = () => {
|
|
74
|
+
setSearchValue( '' );
|
|
25
75
|
};
|
|
26
76
|
|
|
27
77
|
return (
|
|
28
|
-
|
|
78
|
+
<>
|
|
79
|
+
<PopoverHeader
|
|
80
|
+
title={ __( 'Variables', 'elementor' ) }
|
|
81
|
+
onClose={ closePopover }
|
|
82
|
+
icon={ <ColorFilterIcon fontSize={ SIZE } /> }
|
|
83
|
+
actions={ actions }
|
|
84
|
+
/>
|
|
85
|
+
|
|
86
|
+
{ hasVariables && (
|
|
87
|
+
<PopoverSearch
|
|
88
|
+
value={ searchValue }
|
|
89
|
+
onSearch={ handleSearch }
|
|
90
|
+
placeholder={ __( 'Search', 'elementor' ) }
|
|
91
|
+
/>
|
|
92
|
+
) }
|
|
93
|
+
|
|
29
94
|
<Divider />
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
<EditIcon color="action" fontSize="inherit" sx={ { mx: 1, opacity: '0' } } />
|
|
64
|
-
</StyledMenuItem>
|
|
65
|
-
) ) }
|
|
66
|
-
</MenuList>
|
|
67
|
-
</Box>
|
|
68
|
-
</Fragment>
|
|
95
|
+
|
|
96
|
+
<PopoverScrollableContent>
|
|
97
|
+
{ hasVariables && hasSearchResults && (
|
|
98
|
+
<PopoverMenuList
|
|
99
|
+
items={ items }
|
|
100
|
+
onSelect={ handleSetVariable }
|
|
101
|
+
onClose={ () => {} }
|
|
102
|
+
selectedValue={ variable }
|
|
103
|
+
data-testid="font-variables-list"
|
|
104
|
+
menuListTemplate={ VariablesStyledMenuList }
|
|
105
|
+
menuItemContentTemplate={ ( item: VirtualizedItem< 'item', string > ) => (
|
|
106
|
+
<MenuItemContent item={ item } />
|
|
107
|
+
) }
|
|
108
|
+
/>
|
|
109
|
+
) }
|
|
110
|
+
|
|
111
|
+
{ ! hasSearchResults && hasVariables && (
|
|
112
|
+
<NoSearchResults
|
|
113
|
+
searchValue={ searchValue }
|
|
114
|
+
onClear={ handleClearSearch }
|
|
115
|
+
icon={ <TextIcon fontSize="large" /> }
|
|
116
|
+
/>
|
|
117
|
+
) }
|
|
118
|
+
|
|
119
|
+
{ ! hasVariables && (
|
|
120
|
+
<NoVariables
|
|
121
|
+
title={ __( 'Create your first font variable', 'elementor' ) }
|
|
122
|
+
icon={ <TextIcon fontSize="large" /> }
|
|
123
|
+
onAdd={ onAdd }
|
|
124
|
+
/>
|
|
125
|
+
) }
|
|
126
|
+
</PopoverScrollableContent>
|
|
127
|
+
</>
|
|
69
128
|
);
|
|
70
129
|
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { EllipsisWithTooltip, type VirtualizedItem } from '@elementor/editor-ui';
|
|
3
|
+
import { isExperimentActive } from '@elementor/editor-v1-adapters';
|
|
4
|
+
import { EditIcon } from '@elementor/icons';
|
|
5
|
+
import { Box, IconButton, ListItemIcon, Typography } from '@elementor/ui';
|
|
6
|
+
import { __ } from '@wordpress/i18n';
|
|
7
|
+
|
|
8
|
+
const SIZE = 'tiny';
|
|
9
|
+
|
|
10
|
+
const isVersion330Active = isExperimentActive( 'e_v_3_30' );
|
|
11
|
+
|
|
12
|
+
export const MenuItemContent = < T, V extends string >( { item }: { item: VirtualizedItem< T, V > } ) => {
|
|
13
|
+
const onEdit = item.onEdit as ( ( value: V ) => void ) | undefined;
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<>
|
|
17
|
+
<ListItemIcon>{ item.icon }</ListItemIcon>
|
|
18
|
+
<Box
|
|
19
|
+
sx={ {
|
|
20
|
+
flex: 1,
|
|
21
|
+
minWidth: 0,
|
|
22
|
+
display: 'flex',
|
|
23
|
+
alignItems: 'center',
|
|
24
|
+
gap: 1,
|
|
25
|
+
} }
|
|
26
|
+
>
|
|
27
|
+
<EllipsisWithTooltip
|
|
28
|
+
title={ item.label || item.value }
|
|
29
|
+
as={ Typography }
|
|
30
|
+
variant={ isVersion330Active ? 'caption' : 'body2' }
|
|
31
|
+
color={ isVersion330Active ? 'text.primary' : 'text.secondary' }
|
|
32
|
+
sx={ { marginTop: '1px', lineHeight: '2' } }
|
|
33
|
+
maxWidth="50%"
|
|
34
|
+
/>
|
|
35
|
+
{ item.secondaryText && (
|
|
36
|
+
<EllipsisWithTooltip
|
|
37
|
+
title={ item.secondaryText }
|
|
38
|
+
as={ Typography }
|
|
39
|
+
variant="caption"
|
|
40
|
+
color="text.tertiary"
|
|
41
|
+
sx={ { marginTop: '1px', lineHeight: '1' } }
|
|
42
|
+
maxWidth="50%"
|
|
43
|
+
/>
|
|
44
|
+
) }
|
|
45
|
+
</Box>
|
|
46
|
+
{ !! onEdit && (
|
|
47
|
+
<IconButton
|
|
48
|
+
sx={ { mx: 1, opacity: '0' } }
|
|
49
|
+
onClick={ ( e: React.MouseEvent< HTMLButtonElement > ) => {
|
|
50
|
+
e.stopPropagation();
|
|
51
|
+
onEdit( item.value );
|
|
52
|
+
} }
|
|
53
|
+
aria-label={ __( 'Edit', 'elementor' ) }
|
|
54
|
+
>
|
|
55
|
+
<EditIcon color="action" fontSize={ SIZE } />
|
|
56
|
+
</IconButton>
|
|
57
|
+
) }
|
|
58
|
+
</>
|
|
59
|
+
);
|
|
60
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Link, Stack, Typography } from '@elementor/ui';
|
|
3
|
+
import { __ } from '@wordpress/i18n';
|
|
4
|
+
|
|
5
|
+
type Props = {
|
|
6
|
+
searchValue: string;
|
|
7
|
+
onClear?: () => void;
|
|
8
|
+
icon?: React.ReactNode;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const NoSearchResults = ( { searchValue, onClear, icon }: Props ) => {
|
|
12
|
+
return (
|
|
13
|
+
<Stack
|
|
14
|
+
gap={ 1 }
|
|
15
|
+
alignItems="center"
|
|
16
|
+
justifyContent="center"
|
|
17
|
+
height="100%"
|
|
18
|
+
p={ 2.5 }
|
|
19
|
+
color="text.secondary"
|
|
20
|
+
sx={ { pb: 3.5 } }
|
|
21
|
+
>
|
|
22
|
+
{ icon }
|
|
23
|
+
<Typography align="center" variant="subtitle2">
|
|
24
|
+
{ __( 'Sorry, nothing matched', 'elementor' ) }
|
|
25
|
+
<br />
|
|
26
|
+
“{ searchValue }”.
|
|
27
|
+
</Typography>
|
|
28
|
+
<Typography align="center" variant="caption" sx={ { display: 'flex', flexDirection: 'column' } }>
|
|
29
|
+
{ __( 'Try something else.', 'elementor' ) }
|
|
30
|
+
<Link color="text.secondary" variant="caption" component="button" onClick={ onClear }>
|
|
31
|
+
{ __( 'Clear & try again', 'elementor' ) }
|
|
32
|
+
</Link>
|
|
33
|
+
</Typography>
|
|
34
|
+
</Stack>
|
|
35
|
+
);
|
|
36
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Button, Stack, Typography } from '@elementor/ui';
|
|
3
|
+
import { __ } from '@wordpress/i18n';
|
|
4
|
+
|
|
5
|
+
type Props = {
|
|
6
|
+
icon?: React.ReactNode;
|
|
7
|
+
title?: string;
|
|
8
|
+
onAdd?: () => void;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const NoVariables = ( { icon, title, onAdd }: Props ) => (
|
|
12
|
+
<Stack
|
|
13
|
+
gap={ 1 }
|
|
14
|
+
alignItems="center"
|
|
15
|
+
justifyContent="center"
|
|
16
|
+
height="100%"
|
|
17
|
+
color="text.secondary"
|
|
18
|
+
sx={ { p: 2.5, pb: 5.5 } }
|
|
19
|
+
>
|
|
20
|
+
{ icon }
|
|
21
|
+
|
|
22
|
+
<Typography align="center" variant="subtitle2">
|
|
23
|
+
{ title }
|
|
24
|
+
</Typography>
|
|
25
|
+
|
|
26
|
+
<Typography align="center" variant="caption" maxWidth="180px">
|
|
27
|
+
{ __( 'Variables are saved attributes that you can apply anywhere on your site.', 'elementor' ) }
|
|
28
|
+
</Typography>
|
|
29
|
+
|
|
30
|
+
{ onAdd && (
|
|
31
|
+
<Button variant="outlined" color="secondary" size="small" onClick={ onAdd }>
|
|
32
|
+
{ __( 'Create a variable', 'elementor' ) }
|
|
33
|
+
</Button>
|
|
34
|
+
) }
|
|
35
|
+
</Stack>
|
|
36
|
+
);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { MenuList, styled } from '@elementor/ui';
|
|
2
|
+
|
|
3
|
+
export const VariablesStyledMenuList = styled( MenuList )( ( { theme } ) => ( {
|
|
4
|
+
'& > li': {
|
|
5
|
+
height: 32,
|
|
6
|
+
width: '100%',
|
|
7
|
+
display: 'flex',
|
|
8
|
+
alignItems: 'center',
|
|
9
|
+
},
|
|
10
|
+
'& > [role="option"]': {
|
|
11
|
+
...theme.typography.caption,
|
|
12
|
+
lineHeight: 'inherit',
|
|
13
|
+
padding: theme.spacing( 0.5, 1, 0.5, 2 ),
|
|
14
|
+
'&:hover, &:focus': {
|
|
15
|
+
backgroundColor: theme.palette.action.hover,
|
|
16
|
+
},
|
|
17
|
+
'&[aria-selected="true"]': {
|
|
18
|
+
backgroundColor: theme.palette.action.selected,
|
|
19
|
+
},
|
|
20
|
+
cursor: 'pointer',
|
|
21
|
+
textOverflow: 'ellipsis',
|
|
22
|
+
position: 'absolute',
|
|
23
|
+
top: 0,
|
|
24
|
+
left: 0,
|
|
25
|
+
'&:hover .MuiIconButton-root, .MuiIconButton-root:focus': {
|
|
26
|
+
opacity: 1,
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
width: '100%',
|
|
30
|
+
position: 'relative',
|
|
31
|
+
} ) );
|
|
@@ -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 VariableTag = ( { 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,142 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { useRef, useState } from 'react';
|
|
3
|
+
import { Box } from '@elementor/ui';
|
|
4
|
+
|
|
5
|
+
import { colorVariablePropTypeUtil } from '../prop-types/color-variable-prop-type';
|
|
6
|
+
import { fontVariablePropTypeUtil } from '../prop-types/font-variable-prop-type';
|
|
7
|
+
import { type Variable } from '../types';
|
|
8
|
+
import { ColorVariableCreation } from './color-variable-creation';
|
|
9
|
+
import { ColorVariableEdit } from './color-variable-edit';
|
|
10
|
+
import { ColorVariablesSelection } from './color-variables-selection';
|
|
11
|
+
import { FontVariableCreation } from './font-variable-creation';
|
|
12
|
+
import { FontVariableEdit } from './font-variable-edit';
|
|
13
|
+
import { FontVariablesSelection } from './font-variables-selection';
|
|
14
|
+
import { PopoverContentRefContext } from './variable-selection-popover.context';
|
|
15
|
+
|
|
16
|
+
const VIEW_LIST = 'list';
|
|
17
|
+
const VIEW_ADD = 'add';
|
|
18
|
+
const VIEW_EDIT = 'edit';
|
|
19
|
+
|
|
20
|
+
type View = typeof VIEW_LIST | typeof VIEW_ADD | typeof VIEW_EDIT;
|
|
21
|
+
|
|
22
|
+
type Props = {
|
|
23
|
+
closePopover: () => void;
|
|
24
|
+
propTypeKey: string;
|
|
25
|
+
selectedVariable?: Variable;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const VariableSelectionPopover = ( { closePopover, propTypeKey, selectedVariable }: Props ) => {
|
|
29
|
+
const [ currentView, setCurrentView ] = useState< View >( VIEW_LIST );
|
|
30
|
+
const editIdRef = useRef< string >( '' );
|
|
31
|
+
const anchorRef = useRef< HTMLDivElement >( null );
|
|
32
|
+
|
|
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
|
+
);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
type StageProps = {
|
|
50
|
+
propTypeKey: string;
|
|
51
|
+
currentView: View;
|
|
52
|
+
selectedVariable?: Variable;
|
|
53
|
+
editIdRef: React.MutableRefObject< string >;
|
|
54
|
+
setCurrentView: ( stage: View ) => void;
|
|
55
|
+
closePopover: () => void;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
function renderStage( props: StageProps ): React.ReactNode {
|
|
59
|
+
const handleSubmitOnEdit = () => {
|
|
60
|
+
if ( props?.selectedVariable?.key === props.editIdRef.current ) {
|
|
61
|
+
props.closePopover();
|
|
62
|
+
} else {
|
|
63
|
+
props.setCurrentView( VIEW_LIST );
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
if ( fontVariablePropTypeUtil.key === props.propTypeKey ) {
|
|
68
|
+
if ( VIEW_LIST === props.currentView ) {
|
|
69
|
+
return (
|
|
70
|
+
<FontVariablesSelection
|
|
71
|
+
closePopover={ props.closePopover }
|
|
72
|
+
onAdd={ () => {
|
|
73
|
+
props.setCurrentView( VIEW_ADD );
|
|
74
|
+
} }
|
|
75
|
+
onEdit={ ( key ) => {
|
|
76
|
+
props.editIdRef.current = key;
|
|
77
|
+
props.setCurrentView( VIEW_EDIT );
|
|
78
|
+
} }
|
|
79
|
+
/>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if ( VIEW_ADD === props.currentView ) {
|
|
84
|
+
return (
|
|
85
|
+
<FontVariableCreation
|
|
86
|
+
onGoBack={ () => props.setCurrentView( VIEW_LIST ) }
|
|
87
|
+
onClose={ props.closePopover }
|
|
88
|
+
/>
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if ( VIEW_EDIT === props.currentView ) {
|
|
93
|
+
return (
|
|
94
|
+
<FontVariableEdit
|
|
95
|
+
editId={ props.editIdRef.current ?? '' }
|
|
96
|
+
onGoBack={ () => props.setCurrentView( VIEW_LIST ) }
|
|
97
|
+
onClose={ props.closePopover }
|
|
98
|
+
onSubmit={ handleSubmitOnEdit }
|
|
99
|
+
/>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if ( colorVariablePropTypeUtil.key === props.propTypeKey ) {
|
|
105
|
+
if ( VIEW_LIST === props.currentView ) {
|
|
106
|
+
return (
|
|
107
|
+
<ColorVariablesSelection
|
|
108
|
+
closePopover={ props.closePopover }
|
|
109
|
+
onAdd={ () => {
|
|
110
|
+
props.setCurrentView( VIEW_ADD );
|
|
111
|
+
} }
|
|
112
|
+
onEdit={ ( key ) => {
|
|
113
|
+
props.editIdRef.current = key;
|
|
114
|
+
props.setCurrentView( VIEW_EDIT );
|
|
115
|
+
} }
|
|
116
|
+
/>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if ( VIEW_ADD === props.currentView ) {
|
|
121
|
+
return (
|
|
122
|
+
<ColorVariableCreation
|
|
123
|
+
onGoBack={ () => props.setCurrentView( VIEW_LIST ) }
|
|
124
|
+
onClose={ props.closePopover }
|
|
125
|
+
/>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if ( VIEW_EDIT === props.currentView ) {
|
|
130
|
+
return (
|
|
131
|
+
<ColorVariableEdit
|
|
132
|
+
editId={ props.editIdRef.current ?? '' }
|
|
133
|
+
onGoBack={ () => props.setCurrentView( VIEW_LIST ) }
|
|
134
|
+
onClose={ props.closePopover }
|
|
135
|
+
onSubmit={ handleSubmitOnEdit }
|
|
136
|
+
/>
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { type BackgroundColorOverlayPropValue, type BoxShadowPropValue, type PropValue } from '@elementor/editor-props';
|
|
3
|
+
|
|
4
|
+
import { useVariable } from '../hooks/use-prop-variables';
|
|
5
|
+
import { ColorIndicator } from './ui/color-indicator';
|
|
6
|
+
|
|
7
|
+
const useColorVariable = ( value: BackgroundColorOverlayPropValue | BoxShadowPropValue ) => {
|
|
8
|
+
const variableId = value?.value?.color?.value;
|
|
9
|
+
|
|
10
|
+
return useVariable( variableId || '' );
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const BackgroundRepeaterColorIndicator = ( { value }: { value: PropValue } ) => {
|
|
14
|
+
const colorVariable = useColorVariable( value as BackgroundColorOverlayPropValue );
|
|
15
|
+
|
|
16
|
+
return <ColorIndicator component="span" size="inherit" value={ colorVariable?.value } />;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const BackgroundRepeaterLabel = ( { value }: { value: PropValue } ) => {
|
|
20
|
+
const colorVariable = useColorVariable( value as BackgroundColorOverlayPropValue );
|
|
21
|
+
|
|
22
|
+
return <span>{ colorVariable?.label }</span>;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const BoxShadowRepeaterColorIndicator = ( { value }: { value: PropValue } ) => {
|
|
26
|
+
const colorVariable = useColorVariable( value as BoxShadowPropValue );
|
|
27
|
+
|
|
28
|
+
return <ColorIndicator component="span" size="inherit" value={ colorVariable?.value } />;
|
|
29
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { useId, useRef } from 'react';
|
|
3
|
+
import { useBoundProp } from '@elementor/editor-controls';
|
|
4
|
+
import { colorPropTypeUtil } from '@elementor/editor-props';
|
|
5
|
+
import { ColorFilterIcon } from '@elementor/icons';
|
|
6
|
+
import { bindPopover, bindTrigger, Box, Popover, usePopupState } from '@elementor/ui';
|
|
7
|
+
|
|
8
|
+
import { ColorIndicator } from '../components/ui/color-indicator';
|
|
9
|
+
import { SIZE, VariableTag } from '../components/ui/variable-tag';
|
|
10
|
+
import { VariableSelectionPopover } from '../components/variable-selection-popover';
|
|
11
|
+
import { useVariable } from '../hooks/use-prop-variables';
|
|
12
|
+
import { colorVariablePropTypeUtil } from '../prop-types/color-variable-prop-type';
|
|
13
|
+
|
|
14
|
+
export const ColorVariableControl = () => {
|
|
15
|
+
const { setValue: setColor } = useBoundProp();
|
|
16
|
+
const { value: variableValue } = useBoundProp( colorVariablePropTypeUtil );
|
|
17
|
+
|
|
18
|
+
const anchorRef = useRef< HTMLDivElement >( null );
|
|
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 ) {
|
|
28
|
+
throw new Error( `Global color variable ${ variableValue } not found` );
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const unlinkVariable = () => {
|
|
32
|
+
setColor( colorPropTypeUtil.create( selectedVariable.value ) );
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<Box ref={ anchorRef }>
|
|
37
|
+
<VariableTag
|
|
38
|
+
label={ selectedVariable.label }
|
|
39
|
+
startIcon={
|
|
40
|
+
<>
|
|
41
|
+
<ColorFilterIcon fontSize={ SIZE } />
|
|
42
|
+
<ColorIndicator size="inherit" value={ selectedVariable.value } component="span" />
|
|
43
|
+
</>
|
|
44
|
+
}
|
|
45
|
+
onUnlink={ unlinkVariable }
|
|
46
|
+
{ ...bindTrigger( popupState ) }
|
|
47
|
+
/>
|
|
48
|
+
<Popover
|
|
49
|
+
disableScrollLock
|
|
50
|
+
anchorEl={ anchorRef.current }
|
|
51
|
+
anchorOrigin={ { vertical: 'bottom', horizontal: 'right' } }
|
|
52
|
+
transformOrigin={ { vertical: 'top', horizontal: 'right' } }
|
|
53
|
+
PaperProps={ {
|
|
54
|
+
sx: { my: 1 },
|
|
55
|
+
} }
|
|
56
|
+
{ ...bindPopover( popupState ) }
|
|
57
|
+
>
|
|
58
|
+
<VariableSelectionPopover
|
|
59
|
+
selectedVariable={ selectedVariable }
|
|
60
|
+
closePopover={ popupState.close }
|
|
61
|
+
propTypeKey={ colorVariablePropTypeUtil.key }
|
|
62
|
+
/>
|
|
63
|
+
</Popover>
|
|
64
|
+
</Box>
|
|
65
|
+
);
|
|
66
|
+
};
|