@elementor/editor-variables 0.12.0 → 0.14.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 +63 -0
- package/dist/index.js +918 -332
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +961 -329
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -9
- package/src/components/color-variable-creation.tsx +65 -64
- package/src/components/color-variable-edit.tsx +117 -0
- package/src/components/color-variables-selection.tsx +98 -52
- package/src/components/font-variable-creation.tsx +143 -0
- package/src/components/font-variable-edit.tsx +146 -0
- package/src/components/font-variables-selection.tsx +97 -51
- package/src/components/ui/menu-item-content.tsx +51 -0
- package/src/components/ui/no-search-results.tsx +38 -0
- package/src/components/ui/no-variables.tsx +35 -0
- package/src/components/ui/styled-menu-list.tsx +31 -0
- package/src/components/variable-selection-popover.tsx +133 -0
- package/src/components/variables-repeater-item-slot.tsx +29 -0
- package/src/controls/color-variable-control.tsx +90 -0
- package/src/controls/font-variable-control.tsx +88 -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 +31 -4
- 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 -106
- package/src/controls/color-variables-selection-control.tsx +0 -34
- package/src/controls/font-variables-selection-control.tsx +0 -29
- /package/src/components/{color-indicator.tsx → ui/color-indicator.tsx} +0 -0
|
@@ -1,70 +1,116 @@
|
|
|
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 { PopoverHeader, PopoverMenuList, PopoverSearch, type VirtualizedItem } from '@elementor/editor-ui';
|
|
5
|
+
import { ColorFilterIcon, PlusIcon, SettingsIcon, TextIcon } from '@elementor/icons';
|
|
6
|
+
import { Divider, IconButton } from '@elementor/ui';
|
|
7
|
+
import { __ } from '@wordpress/i18n';
|
|
6
8
|
|
|
7
|
-
import {
|
|
9
|
+
import { useFilteredVariables } from '../hooks/use-prop-variables';
|
|
8
10
|
import { fontVariablePropTypeUtil } from '../prop-types/font-variable-prop-type';
|
|
9
|
-
import { type
|
|
10
|
-
import {
|
|
11
|
+
import { type ExtendedVirtualizedItem } from '../types';
|
|
12
|
+
import { MenuItemContent } from './ui/menu-item-content';
|
|
13
|
+
import { NoSearchResults } from './ui/no-search-results';
|
|
14
|
+
import { NoVariables } from './ui/no-variables';
|
|
15
|
+
import { VariablesStyledMenuList } from './ui/styled-menu-list';
|
|
16
|
+
|
|
17
|
+
const SIZE = 'tiny';
|
|
11
18
|
|
|
12
19
|
type Props = {
|
|
13
|
-
|
|
20
|
+
closePopover: () => void;
|
|
21
|
+
onAdd?: () => void;
|
|
22
|
+
onEdit?: ( key: string ) => void;
|
|
23
|
+
onSettings?: () => void;
|
|
14
24
|
};
|
|
15
25
|
|
|
16
|
-
export const FontVariablesSelection = ( {
|
|
26
|
+
export const FontVariablesSelection = ( { closePopover, onAdd, onEdit, onSettings }: Props ) => {
|
|
17
27
|
const { value: variable, setValue: setVariable } = useBoundProp( fontVariablePropTypeUtil );
|
|
28
|
+
const [ searchValue, setSearchValue ] = useState( '' );
|
|
18
29
|
|
|
19
|
-
const
|
|
30
|
+
const {
|
|
31
|
+
list: variables,
|
|
32
|
+
hasMatches: hasSearchResults,
|
|
33
|
+
isSourceNotEmpty: hasVariables,
|
|
34
|
+
} = useFilteredVariables( searchValue, fontVariablePropTypeUtil.key );
|
|
20
35
|
|
|
21
|
-
const handleSetVariable = ( key:
|
|
36
|
+
const handleSetVariable = ( key: string ) => {
|
|
22
37
|
setVariable( key );
|
|
38
|
+
closePopover();
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const actions = [];
|
|
42
|
+
|
|
43
|
+
if ( onAdd ) {
|
|
44
|
+
actions.push(
|
|
45
|
+
<IconButton key="add" size={ SIZE } onClick={ onAdd }>
|
|
46
|
+
<PlusIcon fontSize={ SIZE } />
|
|
47
|
+
</IconButton>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
23
50
|
|
|
24
|
-
|
|
51
|
+
if ( onSettings ) {
|
|
52
|
+
actions.push(
|
|
53
|
+
<IconButton key="settings" size={ SIZE } onClick={ onSettings }>
|
|
54
|
+
<SettingsIcon fontSize={ SIZE } />
|
|
55
|
+
</IconButton>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const items: ExtendedVirtualizedItem[] = variables.map( ( { value, label, key } ) => ( {
|
|
60
|
+
type: 'item' as const,
|
|
61
|
+
value: key,
|
|
62
|
+
label,
|
|
63
|
+
icon: <TextIcon />,
|
|
64
|
+
secondaryText: value,
|
|
65
|
+
onEdit: () => onEdit?.( key ),
|
|
66
|
+
} ) );
|
|
67
|
+
|
|
68
|
+
const handleSearch = ( search: string ) => {
|
|
69
|
+
setSearchValue( search );
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const handleClearSearch = () => {
|
|
73
|
+
setSearchValue( '' );
|
|
25
74
|
};
|
|
26
75
|
|
|
27
76
|
return (
|
|
28
|
-
|
|
77
|
+
<>
|
|
78
|
+
<PopoverHeader
|
|
79
|
+
title={ __( 'Variables', 'elementor' ) }
|
|
80
|
+
onClose={ closePopover }
|
|
81
|
+
icon={ <ColorFilterIcon fontSize={ SIZE } /> }
|
|
82
|
+
actions={ actions }
|
|
83
|
+
/>
|
|
84
|
+
|
|
85
|
+
{ hasVariables && (
|
|
86
|
+
<PopoverSearch
|
|
87
|
+
value={ searchValue }
|
|
88
|
+
onSearch={ handleSearch }
|
|
89
|
+
placeholder={ __( 'Search', 'elementor' ) }
|
|
90
|
+
/>
|
|
91
|
+
) }
|
|
92
|
+
|
|
29
93
|
<Divider />
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
textOverflow: 'ellipsis',
|
|
52
|
-
whiteSpace: 'nowrap',
|
|
53
|
-
maxWidth: '81px',
|
|
54
|
-
},
|
|
55
|
-
} }
|
|
56
|
-
secondaryTypographyProps={ {
|
|
57
|
-
variant: 'caption',
|
|
58
|
-
color: 'text.tertiary',
|
|
59
|
-
style: { marginTop: '1px', lineHeight: '1' },
|
|
60
|
-
} }
|
|
61
|
-
sx={ { display: 'flex', alignItems: 'center', gap: 1 } }
|
|
62
|
-
/>
|
|
63
|
-
<EditIcon color="action" fontSize="inherit" sx={ { mx: 1, opacity: '0' } } />
|
|
64
|
-
</StyledMenuItem>
|
|
65
|
-
) ) }
|
|
66
|
-
</MenuList>
|
|
67
|
-
</Box>
|
|
68
|
-
</Fragment>
|
|
94
|
+
|
|
95
|
+
{ hasVariables && hasSearchResults && (
|
|
96
|
+
<PopoverMenuList
|
|
97
|
+
items={ items }
|
|
98
|
+
onSelect={ handleSetVariable }
|
|
99
|
+
onClose={ () => {} }
|
|
100
|
+
selectedValue={ variable }
|
|
101
|
+
data-testid="font-variables-list"
|
|
102
|
+
menuListTemplate={ VariablesStyledMenuList }
|
|
103
|
+
menuItemContentTemplate={ ( item: VirtualizedItem< 'item', string > ) => (
|
|
104
|
+
<MenuItemContent item={ item } />
|
|
105
|
+
) }
|
|
106
|
+
/>
|
|
107
|
+
) }
|
|
108
|
+
|
|
109
|
+
{ ! hasSearchResults && hasVariables && (
|
|
110
|
+
<NoSearchResults searchValue={ searchValue } onClear={ handleClearSearch } />
|
|
111
|
+
) }
|
|
112
|
+
|
|
113
|
+
{ ! hasVariables && <NoVariables icon={ <TextIcon fontSize="large" /> } onAdd={ onAdd } /> }
|
|
114
|
+
</>
|
|
69
115
|
);
|
|
70
116
|
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { VirtualizedItem } from '@elementor/editor-ui';
|
|
3
|
+
import { EditIcon } from '@elementor/icons';
|
|
4
|
+
import { IconButton, ListItemIcon, ListItemText } from '@elementor/ui';
|
|
5
|
+
import { __ } from '@wordpress/i18n';
|
|
6
|
+
|
|
7
|
+
const SIZE = 'tiny';
|
|
8
|
+
|
|
9
|
+
export const MenuItemContent = < T, V extends string >( { item }: { item: VirtualizedItem< T, V > } ) => {
|
|
10
|
+
const onEdit = item.onEdit as ( ( value: V ) => void ) | undefined;
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<>
|
|
14
|
+
<ListItemIcon>{ item.icon }</ListItemIcon>
|
|
15
|
+
<ListItemText
|
|
16
|
+
primary={ item.label || item.value }
|
|
17
|
+
secondary={ item.secondaryText }
|
|
18
|
+
primaryTypographyProps={ {
|
|
19
|
+
variant: 'body2',
|
|
20
|
+
color: 'text.secondary',
|
|
21
|
+
style: {
|
|
22
|
+
lineHeight: 2,
|
|
23
|
+
display: 'inline-block',
|
|
24
|
+
overflow: 'hidden',
|
|
25
|
+
textOverflow: 'ellipsis',
|
|
26
|
+
whiteSpace: 'nowrap',
|
|
27
|
+
maxWidth: '81px',
|
|
28
|
+
},
|
|
29
|
+
} }
|
|
30
|
+
secondaryTypographyProps={ {
|
|
31
|
+
variant: 'caption',
|
|
32
|
+
color: 'text.tertiary',
|
|
33
|
+
style: { marginTop: '1px', lineHeight: '1' },
|
|
34
|
+
} }
|
|
35
|
+
sx={ { display: 'flex', alignItems: 'center', gap: 1 } }
|
|
36
|
+
/>
|
|
37
|
+
{ !! onEdit && (
|
|
38
|
+
<IconButton
|
|
39
|
+
sx={ { mx: 1, opacity: '0' } }
|
|
40
|
+
onClick={ ( e: React.MouseEvent< HTMLButtonElement > ) => {
|
|
41
|
+
e.stopPropagation();
|
|
42
|
+
onEdit( item.value );
|
|
43
|
+
} }
|
|
44
|
+
aria-label={ __( 'Edit', 'elementor' ) }
|
|
45
|
+
>
|
|
46
|
+
<EditIcon color="action" fontSize={ SIZE } />
|
|
47
|
+
</IconButton>
|
|
48
|
+
) }
|
|
49
|
+
</>
|
|
50
|
+
);
|
|
51
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { ColorFilterIcon } from '@elementor/icons';
|
|
3
|
+
import { Link, Stack, Typography } from '@elementor/ui';
|
|
4
|
+
import { __ } from '@wordpress/i18n';
|
|
5
|
+
|
|
6
|
+
type Props = {
|
|
7
|
+
searchValue: string;
|
|
8
|
+
onClear?: () => void;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const NoSearchResults = ( { searchValue, onClear }: Props ) => {
|
|
12
|
+
return (
|
|
13
|
+
<Stack
|
|
14
|
+
gap={ 1 }
|
|
15
|
+
alignItems="center"
|
|
16
|
+
justifyContent="center"
|
|
17
|
+
height="100%"
|
|
18
|
+
color="text.secondary"
|
|
19
|
+
sx={ { p: 2.5, pb: 5.5 } }
|
|
20
|
+
>
|
|
21
|
+
<ColorFilterIcon fontSize="large" />
|
|
22
|
+
|
|
23
|
+
<Typography align="center" variant="subtitle2">
|
|
24
|
+
{ __( 'Sorry, nothing matched', 'elementor' ) }
|
|
25
|
+
<br />
|
|
26
|
+
“{ searchValue }”.
|
|
27
|
+
</Typography>
|
|
28
|
+
|
|
29
|
+
<Typography align="center" variant="caption">
|
|
30
|
+
{ __( 'Try something else.', 'elementor' ) }
|
|
31
|
+
<br />
|
|
32
|
+
<Link color="text.secondary" variant="caption" component="button" onClick={ onClear }>
|
|
33
|
+
{ __( 'Clear & try again', 'elementor' ) }
|
|
34
|
+
</Link>
|
|
35
|
+
</Typography>
|
|
36
|
+
</Stack>
|
|
37
|
+
);
|
|
38
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
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
|
+
onAdd?: () => void;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const NoVariables = ( { icon, onAdd }: Props ) => (
|
|
11
|
+
<Stack
|
|
12
|
+
gap={ 1 }
|
|
13
|
+
alignItems="center"
|
|
14
|
+
justifyContent="center"
|
|
15
|
+
height="100%"
|
|
16
|
+
color="text.secondary"
|
|
17
|
+
sx={ { p: 2.5, pb: 5.5 } }
|
|
18
|
+
>
|
|
19
|
+
{ icon }
|
|
20
|
+
|
|
21
|
+
<Typography align="center" variant="subtitle2">
|
|
22
|
+
{ __( 'Create your first variable', 'elementor' ) }
|
|
23
|
+
</Typography>
|
|
24
|
+
|
|
25
|
+
<Typography align="center" variant="caption" maxWidth="180px">
|
|
26
|
+
{ __( 'Variables are saved attributes that you can apply anywhere on your site.', 'elementor' ) }
|
|
27
|
+
</Typography>
|
|
28
|
+
|
|
29
|
+
{ onAdd && (
|
|
30
|
+
<Button variant="outlined" color="secondary" size="small" onClick={ onAdd }>
|
|
31
|
+
{ __( 'Create a variable', 'elementor' ) }
|
|
32
|
+
</Button>
|
|
33
|
+
) }
|
|
34
|
+
</Stack>
|
|
35
|
+
);
|
|
@@ -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,133 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { useRef, useState } from 'react';
|
|
3
|
+
|
|
4
|
+
import { colorVariablePropTypeUtil } from '../prop-types/color-variable-prop-type';
|
|
5
|
+
import { fontVariablePropTypeUtil } from '../prop-types/font-variable-prop-type';
|
|
6
|
+
import { type Variable } from '../types';
|
|
7
|
+
import { ColorVariableCreation } from './color-variable-creation';
|
|
8
|
+
import { ColorVariableEdit } from './color-variable-edit';
|
|
9
|
+
import { ColorVariablesSelection } from './color-variables-selection';
|
|
10
|
+
import { FontVariableCreation } from './font-variable-creation';
|
|
11
|
+
import { FontVariableEdit } from './font-variable-edit';
|
|
12
|
+
import { FontVariablesSelection } from './font-variables-selection';
|
|
13
|
+
|
|
14
|
+
const VIEW_LIST = 'list';
|
|
15
|
+
const VIEW_ADD = 'add';
|
|
16
|
+
const VIEW_EDIT = 'edit';
|
|
17
|
+
|
|
18
|
+
type View = typeof VIEW_LIST | typeof VIEW_ADD | typeof VIEW_EDIT;
|
|
19
|
+
|
|
20
|
+
type Props = {
|
|
21
|
+
closePopover: () => void;
|
|
22
|
+
propTypeKey: string;
|
|
23
|
+
selectedVariable?: Variable;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const VariableSelectionPopover = ( { closePopover, propTypeKey, selectedVariable }: Props ) => {
|
|
27
|
+
const [ currentView, setCurrentView ] = useState< View >( VIEW_LIST );
|
|
28
|
+
const editIdRef = useRef< string >( '' );
|
|
29
|
+
|
|
30
|
+
return renderStage( {
|
|
31
|
+
propTypeKey,
|
|
32
|
+
currentView,
|
|
33
|
+
selectedVariable,
|
|
34
|
+
editIdRef,
|
|
35
|
+
setCurrentView,
|
|
36
|
+
closePopover,
|
|
37
|
+
} );
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
type StageProps = {
|
|
41
|
+
propTypeKey: string;
|
|
42
|
+
currentView: View;
|
|
43
|
+
selectedVariable?: Variable;
|
|
44
|
+
editIdRef: React.MutableRefObject< string >;
|
|
45
|
+
setCurrentView: ( stage: View ) => void;
|
|
46
|
+
closePopover: () => void;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
function renderStage( props: StageProps ): React.ReactNode {
|
|
50
|
+
const handleSubmitOnEdit = () => {
|
|
51
|
+
if ( props?.selectedVariable?.key === props.editIdRef.current ) {
|
|
52
|
+
props.closePopover();
|
|
53
|
+
} else {
|
|
54
|
+
props.setCurrentView( VIEW_LIST );
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
if ( fontVariablePropTypeUtil.key === props.propTypeKey ) {
|
|
59
|
+
if ( VIEW_LIST === props.currentView ) {
|
|
60
|
+
return (
|
|
61
|
+
<FontVariablesSelection
|
|
62
|
+
closePopover={ props.closePopover }
|
|
63
|
+
onAdd={ () => {
|
|
64
|
+
props.setCurrentView( VIEW_ADD );
|
|
65
|
+
} }
|
|
66
|
+
onEdit={ ( key ) => {
|
|
67
|
+
props.editIdRef.current = key;
|
|
68
|
+
props.setCurrentView( VIEW_EDIT );
|
|
69
|
+
} }
|
|
70
|
+
/>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if ( VIEW_ADD === props.currentView ) {
|
|
75
|
+
return (
|
|
76
|
+
<FontVariableCreation
|
|
77
|
+
onGoBack={ () => props.setCurrentView( VIEW_LIST ) }
|
|
78
|
+
onClose={ props.closePopover }
|
|
79
|
+
/>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if ( VIEW_EDIT === props.currentView ) {
|
|
84
|
+
return (
|
|
85
|
+
<FontVariableEdit
|
|
86
|
+
editId={ props.editIdRef.current ?? '' }
|
|
87
|
+
onGoBack={ () => props.setCurrentView( VIEW_LIST ) }
|
|
88
|
+
onClose={ props.closePopover }
|
|
89
|
+
onSubmit={ handleSubmitOnEdit }
|
|
90
|
+
/>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if ( colorVariablePropTypeUtil.key === props.propTypeKey ) {
|
|
96
|
+
if ( VIEW_LIST === props.currentView ) {
|
|
97
|
+
return (
|
|
98
|
+
<ColorVariablesSelection
|
|
99
|
+
closePopover={ props.closePopover }
|
|
100
|
+
onAdd={ () => {
|
|
101
|
+
props.setCurrentView( VIEW_ADD );
|
|
102
|
+
} }
|
|
103
|
+
onEdit={ ( key ) => {
|
|
104
|
+
props.editIdRef.current = key;
|
|
105
|
+
props.setCurrentView( VIEW_EDIT );
|
|
106
|
+
} }
|
|
107
|
+
/>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if ( VIEW_ADD === props.currentView ) {
|
|
112
|
+
return (
|
|
113
|
+
<ColorVariableCreation
|
|
114
|
+
onGoBack={ () => props.setCurrentView( VIEW_LIST ) }
|
|
115
|
+
onClose={ props.closePopover }
|
|
116
|
+
/>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if ( VIEW_EDIT === props.currentView ) {
|
|
121
|
+
return (
|
|
122
|
+
<ColorVariableEdit
|
|
123
|
+
editId={ props.editIdRef.current ?? '' }
|
|
124
|
+
onGoBack={ () => props.setCurrentView( VIEW_LIST ) }
|
|
125
|
+
onClose={ props.closePopover }
|
|
126
|
+
onSubmit={ handleSubmitOnEdit }
|
|
127
|
+
/>
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
@@ -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,90 @@
|
|
|
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, 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
|
+
|
|
19
|
+
import { ColorIndicator } from '../components/ui/color-indicator';
|
|
20
|
+
import { VariableSelectionPopover } from '../components/variable-selection-popover';
|
|
21
|
+
import { useVariable } from '../hooks/use-prop-variables';
|
|
22
|
+
import { colorVariablePropTypeUtil } from '../prop-types/color-variable-prop-type';
|
|
23
|
+
|
|
24
|
+
const SIZE = 'tiny';
|
|
25
|
+
|
|
26
|
+
export const ColorVariableControl = () => {
|
|
27
|
+
const { setValue: setColor } = useBoundProp();
|
|
28
|
+
const { value: variableValue } = useBoundProp( colorVariablePropTypeUtil );
|
|
29
|
+
|
|
30
|
+
const anchorRef = useRef< HTMLDivElement >( null );
|
|
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 ) {
|
|
40
|
+
throw new Error( `Global color variable ${ variableValue } not found` );
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const unlinkVariable = () => {
|
|
44
|
+
setColor( colorPropTypeUtil.create( selectedVariable.value ) );
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<Box ref={ anchorRef }>
|
|
49
|
+
<Tag
|
|
50
|
+
fullWidth
|
|
51
|
+
showActionsOnHover
|
|
52
|
+
startIcon={
|
|
53
|
+
<Stack spacing={ 0.75 } direction="row" alignItems="center">
|
|
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>
|
|
89
|
+
);
|
|
90
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { useId, useRef } from 'react';
|
|
3
|
+
import { useBoundProp } from '@elementor/editor-controls';
|
|
4
|
+
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
|
+
|
|
19
|
+
import { VariableSelectionPopover } from '../components/variable-selection-popover';
|
|
20
|
+
import { useVariable } from '../hooks/use-prop-variables';
|
|
21
|
+
import { fontVariablePropTypeUtil } from '../prop-types/font-variable-prop-type';
|
|
22
|
+
|
|
23
|
+
const SIZE = 'tiny';
|
|
24
|
+
|
|
25
|
+
export const FontVariableControl = () => {
|
|
26
|
+
const { setValue: setFontFamily } = useBoundProp();
|
|
27
|
+
const { value: variableValue } = useBoundProp( fontVariablePropTypeUtil );
|
|
28
|
+
|
|
29
|
+
const anchorRef = useRef< HTMLDivElement >( null );
|
|
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 ) {
|
|
39
|
+
throw new Error( `Global font variable ${ variableValue } not found` );
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const unlinkVariable = () => {
|
|
43
|
+
setFontFamily( stringPropTypeUtil.create( selectedVariable.value ) );
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<Box ref={ anchorRef }>
|
|
48
|
+
<Tag
|
|
49
|
+
fullWidth
|
|
50
|
+
showActionsOnHover
|
|
51
|
+
startIcon={
|
|
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>
|
|
87
|
+
);
|
|
88
|
+
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { type StyleVariables, type
|
|
1
|
+
import { type StyleVariables, type Variable } from './types';
|
|
2
2
|
|
|
3
3
|
type VariablesChangeCallback = ( variables: StyleVariables ) => void;
|
|
4
|
+
type Variables = Record< string, Variable >;
|
|
4
5
|
|
|
5
6
|
export const createStyleVariablesRepository = () => {
|
|
6
7
|
const variables: StyleVariables = {};
|
|
@@ -20,7 +21,7 @@ export const createStyleVariablesRepository = () => {
|
|
|
20
21
|
}
|
|
21
22
|
};
|
|
22
23
|
|
|
23
|
-
const shouldUpdate = ( key: string, newValue:
|
|
24
|
+
const shouldUpdate = ( key: string, newValue: string ): boolean => {
|
|
24
25
|
return ! ( key in variables ) || variables[ key ] !== newValue;
|
|
25
26
|
};
|
|
26
27
|
|