@elementor/editor-variables 0.13.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.
Files changed (34) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/dist/index.js +837 -334
  3. package/dist/index.js.map +1 -1
  4. package/dist/index.mjs +865 -317
  5. package/dist/index.mjs.map +1 -1
  6. package/package.json +10 -9
  7. package/src/components/color-variable-creation.tsx +28 -7
  8. package/src/components/color-variable-edit.tsx +117 -0
  9. package/src/components/color-variables-selection.tsx +98 -52
  10. package/src/components/font-variable-creation.tsx +18 -6
  11. package/src/components/font-variable-edit.tsx +146 -0
  12. package/src/components/font-variables-selection.tsx +97 -51
  13. package/src/components/ui/menu-item-content.tsx +51 -0
  14. package/src/components/ui/no-search-results.tsx +38 -0
  15. package/src/components/ui/no-variables.tsx +35 -0
  16. package/src/components/ui/styled-menu-list.tsx +31 -0
  17. package/src/components/variable-selection-popover.tsx +133 -0
  18. package/src/components/variables-repeater-item-slot.tsx +29 -0
  19. package/src/controls/color-variable-control.tsx +90 -0
  20. package/src/controls/font-variable-control.tsx +88 -0
  21. package/src/create-style-variables-repository.ts +3 -2
  22. package/src/hooks/use-prop-color-variable-action.tsx +7 -2
  23. package/src/hooks/use-prop-font-variable-action.tsx +7 -2
  24. package/src/hooks/use-prop-variables.ts +31 -4
  25. package/src/init-color-variables.ts +51 -3
  26. package/src/init-font-variables.ts +2 -2
  27. package/src/service.ts +23 -3
  28. package/src/storage.ts +5 -1
  29. package/src/types.ts +12 -8
  30. package/src/components/styled-menu-item.tsx +0 -10
  31. package/src/components/variables-selection-popover.tsx +0 -119
  32. package/src/controls/color-variables-selection-control.tsx +0 -34
  33. package/src/controls/font-variables-selection-control.tsx +0 -29
  34. /package/src/components/{color-indicator.tsx → ui/color-indicator.tsx} +0 -0
@@ -1,71 +1,117 @@
1
1
  import * as React from 'react';
2
- import { Fragment } from 'react';
2
+ import { useState } from 'react';
3
3
  import { useBoundProp } from '@elementor/editor-controls';
4
- import { EditIcon } from '@elementor/icons';
5
- import { Box, Divider, ListItemIcon, ListItemText, MenuList } from '@elementor/ui';
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 { usePropVariables } from '../hooks/use-prop-variables';
9
+ import { useFilteredVariables } from '../hooks/use-prop-variables';
8
10
  import { colorVariablePropTypeUtil } from '../prop-types/color-variable-prop-type';
9
- import { type VariableKey } from '../types';
10
- import { ColorIndicator } from './color-indicator';
11
- import { StyledMenuItem } from './styled-menu-item';
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
- onSelect?: () => void;
21
+ closePopover: () => void;
22
+ onAdd?: () => void;
23
+ onEdit?: ( key: string ) => void;
24
+ onSettings?: () => void;
15
25
  };
16
26
 
17
- export const ColorVariablesSelection = ( { onSelect }: Props ) => {
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 variables = usePropVariables( colorVariablePropTypeUtil.key );
31
+ const {
32
+ list: variables,
33
+ hasMatches: hasSearchResults,
34
+ isSourceNotEmpty: hasVariables,
35
+ } = useFilteredVariables( searchValue, colorVariablePropTypeUtil.key );
21
36
 
22
- const handleSetColorVariable = ( key: VariableKey ) => {
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
- onSelect?.();
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
- <Fragment>
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
- <Box sx={ { overflowY: 'auto', height: 260, width: 220 } }>
32
- <MenuList role="listbox" tabIndex={ 0 }>
33
- { variables.map( ( { value, label, key } ) => (
34
- <StyledMenuItem
35
- key={ key }
36
- onClick={ () => handleSetColorVariable( key ) }
37
- selected={ key === variable }
38
- >
39
- <ListItemIcon>
40
- <ColorIndicator size="inherit" component="span" value={ value } />
41
- </ListItemIcon>
42
- <ListItemText
43
- primary={ label }
44
- secondary={ value }
45
- primaryTypographyProps={ {
46
- variant: 'body2',
47
- color: 'text.secondary',
48
- style: {
49
- lineHeight: 2,
50
- display: 'inline-block',
51
- overflow: 'hidden',
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
  };
@@ -3,7 +3,7 @@ import { useRef, useState } from 'react';
3
3
  import { FontFamilySelector, useBoundProp } from '@elementor/editor-controls';
4
4
  import { useFontFamilies } from '@elementor/editor-editing-panel';
5
5
  import { PopoverHeader } from '@elementor/editor-ui';
6
- import { ChevronDownIcon, TextIcon } from '@elementor/icons';
6
+ import { ArrowLeftIcon, ChevronDownIcon, TextIcon } from '@elementor/icons';
7
7
  import {
8
8
  bindPopover,
9
9
  bindTrigger,
@@ -12,6 +12,7 @@ import {
12
12
  Divider,
13
13
  FormLabel,
14
14
  Grid,
15
+ IconButton,
15
16
  Popover,
16
17
  Stack,
17
18
  TextField,
@@ -25,7 +26,12 @@ import { fontVariablePropTypeUtil } from '../prop-types/font-variable-prop-type'
25
26
 
26
27
  const SIZE = 'tiny';
27
28
 
28
- export const FontVariableCreation = ( { onClose }: { onClose: () => void } ) => {
29
+ type Props = {
30
+ onGoBack?: () => void;
31
+ onClose: () => void;
32
+ };
33
+
34
+ export const FontVariableCreation = ( { onClose, onGoBack }: Props ) => {
29
35
  const fontFamilies = useFontFamilies();
30
36
  const { setValue: setVariable } = useBoundProp( fontVariablePropTypeUtil );
31
37
 
@@ -63,9 +69,18 @@ export const FontVariableCreation = ( { onClose }: { onClose: () => void } ) =>
63
69
  return (
64
70
  <>
65
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
+ }
66
82
  title={ __( 'Create variable', 'elementor' ) }
67
83
  onClose={ closePopover }
68
- icon={ <TextIcon fontSize={ SIZE } /> }
69
84
  />
70
85
 
71
86
  <Divider />
@@ -119,9 +134,6 @@ export const FontVariableCreation = ( { onClose }: { onClose: () => void } ) =>
119
134
  </Stack>
120
135
 
121
136
  <CardActions>
122
- <Button size="small" onClick={ closePopover } color="secondary" variant="text">
123
- { __( 'Cancel', 'elementor' ) }
124
- </Button>
125
137
  <Button size="small" variant="contained" disabled={ isFormInvalid() } onClick={ handleCreate }>
126
138
  { __( 'Create', 'elementor' ) }
127
139
  </Button>
@@ -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
+ };
@@ -1,70 +1,116 @@
1
1
  import * as React from 'react';
2
- import { Fragment } from 'react';
2
+ import { useState } from 'react';
3
3
  import { useBoundProp } from '@elementor/editor-controls';
4
- import { EditIcon, TextIcon } from '@elementor/icons';
5
- import { Box, Divider, ListItemIcon, ListItemText, MenuList } from '@elementor/ui';
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 { usePropVariables } from '../hooks/use-prop-variables';
9
+ import { useFilteredVariables } from '../hooks/use-prop-variables';
8
10
  import { fontVariablePropTypeUtil } from '../prop-types/font-variable-prop-type';
9
- import { type VariableKey } from '../types';
10
- import { StyledMenuItem } from './styled-menu-item';
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
- onSelect?: () => void;
20
+ closePopover: () => void;
21
+ onAdd?: () => void;
22
+ onEdit?: ( key: string ) => void;
23
+ onSettings?: () => void;
14
24
  };
15
25
 
16
- export const FontVariablesSelection = ( { onSelect }: Props ) => {
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 variables = usePropVariables( fontVariablePropTypeUtil.key );
30
+ const {
31
+ list: variables,
32
+ hasMatches: hasSearchResults,
33
+ isSourceNotEmpty: hasVariables,
34
+ } = useFilteredVariables( searchValue, fontVariablePropTypeUtil.key );
20
35
 
21
- const handleSetVariable = ( key: VariableKey ) => {
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
- onSelect?.();
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
- <Fragment>
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
- <Box sx={ { overflowY: 'auto', height: 260, width: 220 } }>
31
- <MenuList role="listbox" tabIndex={ 0 }>
32
- { variables.map( ( { value, label, key } ) => (
33
- <StyledMenuItem
34
- key={ key }
35
- onClick={ () => handleSetVariable( key ) }
36
- selected={ key === variable }
37
- >
38
- <ListItemIcon>
39
- <TextIcon />
40
- </ListItemIcon>
41
- <ListItemText
42
- primary={ label }
43
- secondary={ value }
44
- primaryTypographyProps={ {
45
- variant: 'body2',
46
- color: 'text.secondary',
47
- style: {
48
- lineHeight: 2,
49
- display: 'inline-block',
50
- overflow: 'hidden',
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
+ &ldquo;{ searchValue }&rdquo;.
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
+ );