@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
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { useRef, useState } from 'react';
|
|
3
|
+
import { PopoverHeader } from '@elementor/editor-ui';
|
|
4
|
+
import { ArrowLeftIcon, BrushIcon } from '@elementor/icons';
|
|
5
|
+
import {
|
|
6
|
+
Button,
|
|
7
|
+
CardActions,
|
|
8
|
+
Divider,
|
|
9
|
+
FormLabel,
|
|
10
|
+
Grid,
|
|
11
|
+
IconButton,
|
|
12
|
+
Stack,
|
|
13
|
+
TextField,
|
|
14
|
+
UnstableColorField,
|
|
15
|
+
} from '@elementor/ui';
|
|
16
|
+
import { __ } from '@wordpress/i18n';
|
|
17
|
+
|
|
18
|
+
import { updateVariable, useVariable } from '../hooks/use-prop-variables';
|
|
19
|
+
|
|
20
|
+
const SIZE = 'tiny';
|
|
21
|
+
|
|
22
|
+
type Props = {
|
|
23
|
+
editId: string;
|
|
24
|
+
onClose: () => void;
|
|
25
|
+
onGoBack?: () => void;
|
|
26
|
+
onSubmit?: () => void;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const ColorVariableEdit = ( { onClose, onGoBack, onSubmit, editId }: Props ) => {
|
|
30
|
+
const variable = useVariable( editId );
|
|
31
|
+
|
|
32
|
+
if ( ! variable ) {
|
|
33
|
+
throw new Error( `Global color variable not found` );
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const anchorRef = useRef< HTMLDivElement >( null );
|
|
37
|
+
|
|
38
|
+
const [ color, setColor ] = useState( variable.value );
|
|
39
|
+
const [ label, setLabel ] = useState( variable.label );
|
|
40
|
+
|
|
41
|
+
const handleUpdate = () => {
|
|
42
|
+
updateVariable( editId, {
|
|
43
|
+
value: color,
|
|
44
|
+
label,
|
|
45
|
+
} ).then( () => {
|
|
46
|
+
onSubmit?.();
|
|
47
|
+
} );
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const noValueChanged = () => color === variable.value && label === variable.label;
|
|
51
|
+
const hasEmptyValue = () => '' === color.trim() || '' === label.trim();
|
|
52
|
+
const isSaveDisabled = () => noValueChanged() || hasEmptyValue();
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<>
|
|
56
|
+
<PopoverHeader
|
|
57
|
+
title={ __( 'Edit variable', 'elementor' ) }
|
|
58
|
+
onClose={ onClose }
|
|
59
|
+
icon={
|
|
60
|
+
<>
|
|
61
|
+
{ onGoBack && (
|
|
62
|
+
<IconButton size={ SIZE } aria-label={ __( 'Go Back', 'elementor' ) } onClick={ onGoBack }>
|
|
63
|
+
<ArrowLeftIcon fontSize={ SIZE } />
|
|
64
|
+
</IconButton>
|
|
65
|
+
) }
|
|
66
|
+
<BrushIcon fontSize={ SIZE } />
|
|
67
|
+
</>
|
|
68
|
+
}
|
|
69
|
+
/>
|
|
70
|
+
|
|
71
|
+
<Divider />
|
|
72
|
+
|
|
73
|
+
<Stack p={ 1.5 } gap={ 1.5 }>
|
|
74
|
+
<Grid container gap={ 0.75 } alignItems="center">
|
|
75
|
+
<Grid item xs={ 12 }>
|
|
76
|
+
<FormLabel size="small">{ __( 'Name', 'elementor' ) }</FormLabel>
|
|
77
|
+
</Grid>
|
|
78
|
+
<Grid item xs={ 12 }>
|
|
79
|
+
<TextField
|
|
80
|
+
size="tiny"
|
|
81
|
+
fullWidth
|
|
82
|
+
value={ label }
|
|
83
|
+
onChange={ ( e: React.ChangeEvent< HTMLInputElement > ) => setLabel( e.target.value ) }
|
|
84
|
+
/>
|
|
85
|
+
</Grid>
|
|
86
|
+
</Grid>
|
|
87
|
+
|
|
88
|
+
<Grid container gap={ 0.75 } alignItems="center">
|
|
89
|
+
<Grid item xs={ 12 }>
|
|
90
|
+
<FormLabel size="small">{ __( 'Value', 'elementor' ) }</FormLabel>
|
|
91
|
+
</Grid>
|
|
92
|
+
<Grid item xs={ 12 }>
|
|
93
|
+
<UnstableColorField
|
|
94
|
+
size="tiny"
|
|
95
|
+
fullWidth
|
|
96
|
+
value={ color }
|
|
97
|
+
onChange={ setColor }
|
|
98
|
+
slotProps={ {
|
|
99
|
+
colorPicker: {
|
|
100
|
+
anchorEl: anchorRef.current,
|
|
101
|
+
anchorOrigin: { vertical: 'top', horizontal: 'right' },
|
|
102
|
+
transformOrigin: { vertical: 'top', horizontal: -10 },
|
|
103
|
+
},
|
|
104
|
+
} }
|
|
105
|
+
/>
|
|
106
|
+
</Grid>
|
|
107
|
+
</Grid>
|
|
108
|
+
</Stack>
|
|
109
|
+
|
|
110
|
+
<CardActions>
|
|
111
|
+
<Button size="small" variant="contained" disabled={ isSaveDisabled() } onClick={ handleUpdate }>
|
|
112
|
+
{ __( 'Save', 'elementor' ) }
|
|
113
|
+
</Button>
|
|
114
|
+
</CardActions>
|
|
115
|
+
</>
|
|
116
|
+
);
|
|
117
|
+
};
|
|
@@ -1,71 +1,117 @@
|
|
|
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 } 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 { colorVariablePropTypeUtil } from '../prop-types/color-variable-prop-type';
|
|
9
|
-
import { type
|
|
10
|
-
import { ColorIndicator } from './color-indicator';
|
|
11
|
-
import {
|
|
11
|
+
import { type ExtendedVirtualizedItem } from '../types';
|
|
12
|
+
import { ColorIndicator } from './ui/color-indicator';
|
|
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';
|
|
12
19
|
|
|
13
20
|
type Props = {
|
|
14
|
-
|
|
21
|
+
closePopover: () => void;
|
|
22
|
+
onAdd?: () => void;
|
|
23
|
+
onEdit?: ( key: string ) => void;
|
|
24
|
+
onSettings?: () => void;
|
|
15
25
|
};
|
|
16
26
|
|
|
17
|
-
export const ColorVariablesSelection = ( {
|
|
27
|
+
export const ColorVariablesSelection = ( { closePopover, onAdd, onEdit, onSettings }: Props ) => {
|
|
18
28
|
const { value: variable, setValue: setVariable } = useBoundProp( colorVariablePropTypeUtil );
|
|
29
|
+
const [ searchValue, setSearchValue ] = useState( '' );
|
|
19
30
|
|
|
20
|
-
const
|
|
31
|
+
const {
|
|
32
|
+
list: variables,
|
|
33
|
+
hasMatches: hasSearchResults,
|
|
34
|
+
isSourceNotEmpty: hasVariables,
|
|
35
|
+
} = useFilteredVariables( searchValue, colorVariablePropTypeUtil.key );
|
|
21
36
|
|
|
22
|
-
const handleSetColorVariable = ( key:
|
|
37
|
+
const handleSetColorVariable = ( key: string ) => {
|
|
23
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
|
+
}
|
|
24
51
|
|
|
25
|
-
|
|
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: <ColorIndicator size="inherit" component="span" value={ value } />,
|
|
65
|
+
secondaryText: value,
|
|
66
|
+
onEdit: () => onEdit?.( key ),
|
|
67
|
+
} ) );
|
|
68
|
+
|
|
69
|
+
const handleSearch = ( search: string ) => {
|
|
70
|
+
setSearchValue( search );
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const handleClearSearch = () => {
|
|
74
|
+
setSearchValue( '' );
|
|
26
75
|
};
|
|
27
76
|
|
|
28
77
|
return (
|
|
29
|
-
|
|
78
|
+
<>
|
|
79
|
+
<PopoverHeader
|
|
80
|
+
title={ __( 'Variables', 'elementor' ) }
|
|
81
|
+
icon={ <ColorFilterIcon fontSize={ SIZE } /> }
|
|
82
|
+
onClose={ closePopover }
|
|
83
|
+
actions={ actions }
|
|
84
|
+
/>
|
|
85
|
+
|
|
86
|
+
{ hasVariables && (
|
|
87
|
+
<PopoverSearch
|
|
88
|
+
value={ searchValue }
|
|
89
|
+
onSearch={ handleSearch }
|
|
90
|
+
placeholder={ __( 'Search', 'elementor' ) }
|
|
91
|
+
/>
|
|
92
|
+
) }
|
|
93
|
+
|
|
30
94
|
<Divider />
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
textOverflow: 'ellipsis',
|
|
53
|
-
whiteSpace: 'nowrap',
|
|
54
|
-
maxWidth: '81px',
|
|
55
|
-
},
|
|
56
|
-
} }
|
|
57
|
-
secondaryTypographyProps={ {
|
|
58
|
-
variant: 'caption',
|
|
59
|
-
color: 'text.tertiary',
|
|
60
|
-
style: { marginTop: '1px', lineHeight: '1' },
|
|
61
|
-
} }
|
|
62
|
-
sx={ { display: 'flex', alignItems: 'center', gap: 1 } }
|
|
63
|
-
/>
|
|
64
|
-
<EditIcon color="action" fontSize="inherit" sx={ { mx: 1, opacity: '0' } } />
|
|
65
|
-
</StyledMenuItem>
|
|
66
|
-
) ) }
|
|
67
|
-
</MenuList>
|
|
68
|
-
</Box>
|
|
69
|
-
</Fragment>
|
|
95
|
+
|
|
96
|
+
{ hasVariables && hasSearchResults && (
|
|
97
|
+
<PopoverMenuList< 'item', string >
|
|
98
|
+
items={ items }
|
|
99
|
+
onSelect={ handleSetColorVariable }
|
|
100
|
+
onClose={ () => {} }
|
|
101
|
+
selectedValue={ variable }
|
|
102
|
+
data-testid="color-variables-list"
|
|
103
|
+
menuListTemplate={ VariablesStyledMenuList }
|
|
104
|
+
menuItemContentTemplate={ ( item: VirtualizedItem< 'item', string > ) => (
|
|
105
|
+
<MenuItemContent item={ item } />
|
|
106
|
+
) }
|
|
107
|
+
/>
|
|
108
|
+
) }
|
|
109
|
+
|
|
110
|
+
{ ! hasSearchResults && hasVariables && (
|
|
111
|
+
<NoSearchResults searchValue={ searchValue } onClear={ handleClearSearch } />
|
|
112
|
+
) }
|
|
113
|
+
|
|
114
|
+
{ ! hasVariables && <NoVariables icon={ <ColorFilterIcon fontSize="large" /> } onAdd={ onAdd } /> }
|
|
115
|
+
</>
|
|
70
116
|
);
|
|
71
117
|
};
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { useRef, useState } from 'react';
|
|
3
|
+
import { FontFamilySelector, useBoundProp } from '@elementor/editor-controls';
|
|
4
|
+
import { useFontFamilies } from '@elementor/editor-editing-panel';
|
|
5
|
+
import { PopoverHeader } from '@elementor/editor-ui';
|
|
6
|
+
import { ArrowLeftIcon, ChevronDownIcon, TextIcon } from '@elementor/icons';
|
|
7
|
+
import {
|
|
8
|
+
bindPopover,
|
|
9
|
+
bindTrigger,
|
|
10
|
+
Button,
|
|
11
|
+
CardActions,
|
|
12
|
+
Divider,
|
|
13
|
+
FormLabel,
|
|
14
|
+
Grid,
|
|
15
|
+
IconButton,
|
|
16
|
+
Popover,
|
|
17
|
+
Stack,
|
|
18
|
+
TextField,
|
|
19
|
+
UnstableTag,
|
|
20
|
+
usePopupState,
|
|
21
|
+
} from '@elementor/ui';
|
|
22
|
+
import { __ } from '@wordpress/i18n';
|
|
23
|
+
|
|
24
|
+
import { createVariable } from '../hooks/use-prop-variables';
|
|
25
|
+
import { fontVariablePropTypeUtil } from '../prop-types/font-variable-prop-type';
|
|
26
|
+
|
|
27
|
+
const SIZE = 'tiny';
|
|
28
|
+
|
|
29
|
+
type Props = {
|
|
30
|
+
onGoBack?: () => void;
|
|
31
|
+
onClose: () => void;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const FontVariableCreation = ( { onClose, onGoBack }: Props ) => {
|
|
35
|
+
const fontFamilies = useFontFamilies();
|
|
36
|
+
const { setValue: setVariable } = useBoundProp( fontVariablePropTypeUtil );
|
|
37
|
+
|
|
38
|
+
const [ fontFamily, setFontFamily ] = useState( '' );
|
|
39
|
+
const [ label, setLabel ] = useState( '' );
|
|
40
|
+
|
|
41
|
+
const anchorRef = useRef< HTMLDivElement >( null );
|
|
42
|
+
const fontPopoverState = usePopupState( { variant: 'popover' } );
|
|
43
|
+
|
|
44
|
+
const resetFields = () => {
|
|
45
|
+
setFontFamily( '' );
|
|
46
|
+
setLabel( '' );
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const closePopover = () => {
|
|
50
|
+
resetFields();
|
|
51
|
+
onClose();
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const handleCreate = () => {
|
|
55
|
+
createVariable( {
|
|
56
|
+
value: fontFamily,
|
|
57
|
+
label,
|
|
58
|
+
type: fontVariablePropTypeUtil.key,
|
|
59
|
+
} ).then( ( key ) => {
|
|
60
|
+
setVariable( key );
|
|
61
|
+
closePopover();
|
|
62
|
+
} );
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const isFormInvalid = () => {
|
|
66
|
+
return ! fontFamily?.trim() || ! label?.trim();
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<>
|
|
71
|
+
<PopoverHeader
|
|
72
|
+
icon={
|
|
73
|
+
<>
|
|
74
|
+
{ onGoBack && (
|
|
75
|
+
<IconButton size={ SIZE } aria-label={ __( 'Go Back', 'elementor' ) } onClick={ onGoBack }>
|
|
76
|
+
<ArrowLeftIcon fontSize={ SIZE } />
|
|
77
|
+
</IconButton>
|
|
78
|
+
) }
|
|
79
|
+
<TextIcon fontSize={ SIZE } />
|
|
80
|
+
</>
|
|
81
|
+
}
|
|
82
|
+
title={ __( 'Create variable', 'elementor' ) }
|
|
83
|
+
onClose={ closePopover }
|
|
84
|
+
/>
|
|
85
|
+
|
|
86
|
+
<Divider />
|
|
87
|
+
|
|
88
|
+
<Stack p={ 1.5 } gap={ 1.5 }>
|
|
89
|
+
<Grid container gap={ 0.75 } alignItems="center">
|
|
90
|
+
<Grid item xs={ 12 }>
|
|
91
|
+
<FormLabel size="small">{ __( 'Name', 'elementor' ) }</FormLabel>
|
|
92
|
+
</Grid>
|
|
93
|
+
<Grid item xs={ 12 }>
|
|
94
|
+
<TextField
|
|
95
|
+
size="tiny"
|
|
96
|
+
fullWidth
|
|
97
|
+
value={ label }
|
|
98
|
+
onChange={ ( e: React.ChangeEvent< HTMLInputElement > ) => setLabel( e.target.value ) }
|
|
99
|
+
/>
|
|
100
|
+
</Grid>
|
|
101
|
+
</Grid>
|
|
102
|
+
|
|
103
|
+
<Grid container gap={ 0.75 } alignItems="center">
|
|
104
|
+
<Grid item xs={ 12 }>
|
|
105
|
+
<FormLabel size="small">{ __( 'Value', 'elementor' ) }</FormLabel>
|
|
106
|
+
</Grid>
|
|
107
|
+
<Grid item xs={ 12 }>
|
|
108
|
+
<>
|
|
109
|
+
<UnstableTag
|
|
110
|
+
variant="outlined"
|
|
111
|
+
label={ fontFamily }
|
|
112
|
+
endIcon={ <ChevronDownIcon fontSize="tiny" /> }
|
|
113
|
+
{ ...bindTrigger( fontPopoverState ) }
|
|
114
|
+
fullWidth
|
|
115
|
+
/>
|
|
116
|
+
<Popover
|
|
117
|
+
disablePortal
|
|
118
|
+
disableScrollLock
|
|
119
|
+
anchorEl={ anchorRef.current }
|
|
120
|
+
anchorOrigin={ { vertical: 'top', horizontal: 'right' } }
|
|
121
|
+
transformOrigin={ { vertical: 'top', horizontal: -20 } }
|
|
122
|
+
{ ...bindPopover( fontPopoverState ) }
|
|
123
|
+
>
|
|
124
|
+
<FontFamilySelector
|
|
125
|
+
fontFamilies={ fontFamilies }
|
|
126
|
+
fontFamily={ fontFamily }
|
|
127
|
+
onFontFamilyChange={ setFontFamily }
|
|
128
|
+
onClose={ fontPopoverState.close }
|
|
129
|
+
/>
|
|
130
|
+
</Popover>
|
|
131
|
+
</>
|
|
132
|
+
</Grid>
|
|
133
|
+
</Grid>
|
|
134
|
+
</Stack>
|
|
135
|
+
|
|
136
|
+
<CardActions>
|
|
137
|
+
<Button size="small" variant="contained" disabled={ isFormInvalid() } onClick={ handleCreate }>
|
|
138
|
+
{ __( 'Create', 'elementor' ) }
|
|
139
|
+
</Button>
|
|
140
|
+
</CardActions>
|
|
141
|
+
</>
|
|
142
|
+
);
|
|
143
|
+
};
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { useId, useRef, useState } from 'react';
|
|
3
|
+
import { FontFamilySelector } from '@elementor/editor-controls';
|
|
4
|
+
import { useFontFamilies } from '@elementor/editor-editing-panel';
|
|
5
|
+
import { PopoverHeader } from '@elementor/editor-ui';
|
|
6
|
+
import { ArrowLeftIcon, ChevronDownIcon, TextIcon } from '@elementor/icons';
|
|
7
|
+
import {
|
|
8
|
+
bindPopover,
|
|
9
|
+
bindTrigger,
|
|
10
|
+
Button,
|
|
11
|
+
CardActions,
|
|
12
|
+
Divider,
|
|
13
|
+
FormLabel,
|
|
14
|
+
Grid,
|
|
15
|
+
IconButton,
|
|
16
|
+
Popover,
|
|
17
|
+
Stack,
|
|
18
|
+
TextField,
|
|
19
|
+
UnstableTag,
|
|
20
|
+
usePopupState,
|
|
21
|
+
} from '@elementor/ui';
|
|
22
|
+
import { __ } from '@wordpress/i18n';
|
|
23
|
+
|
|
24
|
+
import { updateVariable, useVariable } from '../hooks/use-prop-variables';
|
|
25
|
+
|
|
26
|
+
const SIZE = 'tiny';
|
|
27
|
+
|
|
28
|
+
type Props = {
|
|
29
|
+
editId: string;
|
|
30
|
+
onClose: () => void;
|
|
31
|
+
onGoBack?: () => void;
|
|
32
|
+
onSubmit?: () => void;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const FontVariableEdit = ( { onClose, onGoBack, onSubmit, editId }: Props ) => {
|
|
36
|
+
const variable = useVariable( editId );
|
|
37
|
+
|
|
38
|
+
if ( ! variable ) {
|
|
39
|
+
throw new Error( `Global font variable "${ editId }" not found` );
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const [ fontFamily, setFontFamily ] = useState( variable.value );
|
|
43
|
+
const [ label, setLabel ] = useState( variable.label );
|
|
44
|
+
|
|
45
|
+
const variableNameId = useId();
|
|
46
|
+
const variableValueId = useId();
|
|
47
|
+
|
|
48
|
+
const anchorRef = useRef< HTMLDivElement >( null );
|
|
49
|
+
const fontPopoverState = usePopupState( { variant: 'popover' } );
|
|
50
|
+
|
|
51
|
+
const fontFamilies = useFontFamilies();
|
|
52
|
+
|
|
53
|
+
const handleUpdate = () => {
|
|
54
|
+
updateVariable( editId, {
|
|
55
|
+
value: fontFamily,
|
|
56
|
+
label,
|
|
57
|
+
} ).then( () => {
|
|
58
|
+
onSubmit?.();
|
|
59
|
+
} );
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const noValueChanged = () => fontFamily === variable.value && label === variable.label;
|
|
63
|
+
const hasEmptyValue = () => '' === fontFamily.trim() || '' === label.trim();
|
|
64
|
+
const isSaveDisabled = () => noValueChanged() || hasEmptyValue();
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<>
|
|
68
|
+
<PopoverHeader
|
|
69
|
+
icon={
|
|
70
|
+
<>
|
|
71
|
+
{ onGoBack && (
|
|
72
|
+
<IconButton size={ SIZE } aria-label={ __( 'Go Back', 'elementor' ) } onClick={ onGoBack }>
|
|
73
|
+
<ArrowLeftIcon fontSize={ SIZE } />
|
|
74
|
+
</IconButton>
|
|
75
|
+
) }
|
|
76
|
+
<TextIcon fontSize={ SIZE } />
|
|
77
|
+
</>
|
|
78
|
+
}
|
|
79
|
+
title={ __( 'Edit variable', 'elementor' ) }
|
|
80
|
+
onClose={ onClose }
|
|
81
|
+
/>
|
|
82
|
+
|
|
83
|
+
<Divider />
|
|
84
|
+
|
|
85
|
+
<Stack p={ 1.5 } gap={ 1.5 }>
|
|
86
|
+
<Grid container gap={ 0.75 } alignItems="center">
|
|
87
|
+
<Grid item xs={ 12 }>
|
|
88
|
+
<FormLabel htmlFor={ variableNameId } size="small">
|
|
89
|
+
{ __( 'Name', 'elementor' ) }
|
|
90
|
+
</FormLabel>
|
|
91
|
+
</Grid>
|
|
92
|
+
<Grid item xs={ 12 }>
|
|
93
|
+
<TextField
|
|
94
|
+
id={ variableNameId }
|
|
95
|
+
size="tiny"
|
|
96
|
+
fullWidth
|
|
97
|
+
value={ label }
|
|
98
|
+
onChange={ ( e: React.ChangeEvent< HTMLInputElement > ) => setLabel( e.target.value ) }
|
|
99
|
+
/>
|
|
100
|
+
</Grid>
|
|
101
|
+
</Grid>
|
|
102
|
+
|
|
103
|
+
<Grid container gap={ 0.75 } alignItems="center">
|
|
104
|
+
<Grid item xs={ 12 }>
|
|
105
|
+
<FormLabel htmlFor={ variableValueId } size="small">
|
|
106
|
+
{ __( 'Value', 'elementor' ) }
|
|
107
|
+
</FormLabel>
|
|
108
|
+
</Grid>
|
|
109
|
+
<Grid item xs={ 12 }>
|
|
110
|
+
<>
|
|
111
|
+
<UnstableTag
|
|
112
|
+
id={ variableValueId }
|
|
113
|
+
variant="outlined"
|
|
114
|
+
label={ fontFamily }
|
|
115
|
+
endIcon={ <ChevronDownIcon fontSize="tiny" /> }
|
|
116
|
+
{ ...bindTrigger( fontPopoverState ) }
|
|
117
|
+
fullWidth
|
|
118
|
+
/>
|
|
119
|
+
<Popover
|
|
120
|
+
disablePortal
|
|
121
|
+
disableScrollLock
|
|
122
|
+
anchorEl={ anchorRef.current }
|
|
123
|
+
anchorOrigin={ { vertical: 'top', horizontal: 'right' } }
|
|
124
|
+
transformOrigin={ { vertical: 'top', horizontal: -20 } }
|
|
125
|
+
{ ...bindPopover( fontPopoverState ) }
|
|
126
|
+
>
|
|
127
|
+
<FontFamilySelector
|
|
128
|
+
fontFamilies={ fontFamilies }
|
|
129
|
+
fontFamily={ fontFamily }
|
|
130
|
+
onFontFamilyChange={ setFontFamily }
|
|
131
|
+
onClose={ fontPopoverState.close }
|
|
132
|
+
/>
|
|
133
|
+
</Popover>
|
|
134
|
+
</>
|
|
135
|
+
</Grid>
|
|
136
|
+
</Grid>
|
|
137
|
+
</Stack>
|
|
138
|
+
|
|
139
|
+
<CardActions>
|
|
140
|
+
<Button size="small" variant="contained" disabled={ isSaveDisabled() } onClick={ handleUpdate }>
|
|
141
|
+
{ __( 'Save', 'elementor' ) }
|
|
142
|
+
</Button>
|
|
143
|
+
</CardActions>
|
|
144
|
+
</>
|
|
145
|
+
);
|
|
146
|
+
};
|