@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.
Files changed (36) hide show
  1. package/CHANGELOG.md +84 -0
  2. package/dist/index.js +938 -344
  3. package/dist/index.js.map +1 -1
  4. package/dist/index.mjs +950 -335
  5. package/dist/index.mjs.map +1 -1
  6. package/package.json +11 -10
  7. package/src/components/color-variable-creation.tsx +39 -16
  8. package/src/components/color-variable-edit.tsx +133 -0
  9. package/src/components/color-variables-selection.tsx +111 -52
  10. package/src/components/font-variable-creation.tsx +30 -16
  11. package/src/components/font-variable-edit.tsx +163 -0
  12. package/src/components/font-variables-selection.tsx +110 -51
  13. package/src/components/{color-indicator.tsx → ui/color-indicator.tsx} +1 -0
  14. package/src/components/ui/menu-item-content.tsx +60 -0
  15. package/src/components/ui/no-search-results.tsx +36 -0
  16. package/src/components/ui/no-variables.tsx +36 -0
  17. package/src/components/ui/styled-menu-list.tsx +31 -0
  18. package/src/components/ui/variable-tag.tsx +43 -0
  19. package/src/components/variable-selection-popover.context.ts +7 -0
  20. package/src/components/variable-selection-popover.tsx +142 -0
  21. package/src/components/variables-repeater-item-slot.tsx +29 -0
  22. package/src/controls/color-variable-control.tsx +66 -0
  23. package/src/controls/font-variable-control.tsx +60 -0
  24. package/src/create-style-variables-repository.ts +3 -2
  25. package/src/hooks/use-prop-color-variable-action.tsx +7 -2
  26. package/src/hooks/use-prop-font-variable-action.tsx +7 -2
  27. package/src/hooks/use-prop-variables.ts +34 -13
  28. package/src/init-color-variables.ts +51 -3
  29. package/src/init-font-variables.ts +2 -2
  30. package/src/service.ts +23 -3
  31. package/src/storage.ts +5 -1
  32. package/src/types.ts +12 -8
  33. package/src/components/styled-menu-item.tsx +0 -10
  34. package/src/components/variables-selection-popover.tsx +0 -119
  35. package/src/controls/color-variables-selection-control.tsx +0 -34
  36. package/src/controls/font-variables-selection-control.tsx +0 -29
@@ -0,0 +1,133 @@
1
+ import * as React from 'react';
2
+ import { useRef, useState } from 'react';
3
+ import { PopoverContent } from '@elementor/editor-controls';
4
+ import { PopoverHeader, PopoverScrollableContent } from '@elementor/editor-ui';
5
+ import { ArrowLeftIcon, BrushIcon, TrashIcon } from '@elementor/icons';
6
+ import {
7
+ Button,
8
+ CardActions,
9
+ Divider,
10
+ FormLabel,
11
+ Grid,
12
+ IconButton,
13
+ TextField,
14
+ UnstableColorField,
15
+ } from '@elementor/ui';
16
+ import { __ } from '@wordpress/i18n';
17
+
18
+ import { deleteVariable, updateVariable, useVariable } from '../hooks/use-prop-variables';
19
+ import { usePopoverContentRef } from './variable-selection-popover.context';
20
+
21
+ const SIZE = 'tiny';
22
+
23
+ type Props = {
24
+ editId: string;
25
+ onClose: () => void;
26
+ onGoBack?: () => void;
27
+ onSubmit?: () => void;
28
+ };
29
+
30
+ export const ColorVariableEdit = ( { onClose, onGoBack, onSubmit, editId }: Props ) => {
31
+ const variable = useVariable( editId );
32
+ if ( ! variable ) {
33
+ throw new Error( `Global color variable not found` );
34
+ }
35
+
36
+ const defaultRef = useRef< HTMLDivElement >( null );
37
+ const anchorRef = usePopoverContentRef() ?? defaultRef;
38
+
39
+ const [ color, setColor ] = useState( variable.value );
40
+ const [ label, setLabel ] = useState( variable.label );
41
+
42
+ const handleUpdate = () => {
43
+ updateVariable( editId, {
44
+ value: color,
45
+ label,
46
+ } ).then( () => {
47
+ onSubmit?.();
48
+ } );
49
+ };
50
+
51
+ const handleDelete = () => {
52
+ deleteVariable( editId ).then( () => {
53
+ onSubmit?.();
54
+ } );
55
+ };
56
+
57
+ const noValueChanged = () => color === variable.value && label === variable.label;
58
+ const hasEmptyValue = () => '' === color.trim() || '' === label.trim();
59
+ const isSaveDisabled = () => noValueChanged() || hasEmptyValue();
60
+
61
+ const actions = [];
62
+
63
+ actions.push(
64
+ <IconButton key="delete" size={ SIZE } aria-label={ __( 'Delete', 'elementor' ) } onClick={ handleDelete }>
65
+ <TrashIcon fontSize={ SIZE } />
66
+ </IconButton>
67
+ );
68
+
69
+ return (
70
+ <PopoverScrollableContent height="auto">
71
+ <PopoverHeader
72
+ title={ __( 'Edit variable', 'elementor' ) }
73
+ onClose={ onClose }
74
+ icon={
75
+ <>
76
+ { onGoBack && (
77
+ <IconButton size={ SIZE } aria-label={ __( 'Go Back', 'elementor' ) } onClick={ onGoBack }>
78
+ <ArrowLeftIcon fontSize={ SIZE } />
79
+ </IconButton>
80
+ ) }
81
+ <BrushIcon fontSize={ SIZE } />
82
+ </>
83
+ }
84
+ actions={ actions }
85
+ />
86
+
87
+ <Divider />
88
+
89
+ <PopoverContent p={ 2 }>
90
+ <Grid container gap={ 0.75 } alignItems="center">
91
+ <Grid item xs={ 12 }>
92
+ <FormLabel size="tiny">{ __( 'Name', 'elementor' ) }</FormLabel>
93
+ </Grid>
94
+ <Grid item xs={ 12 }>
95
+ <TextField
96
+ size="tiny"
97
+ fullWidth
98
+ value={ label }
99
+ onChange={ ( e: React.ChangeEvent< HTMLInputElement > ) => setLabel( e.target.value ) }
100
+ />
101
+ </Grid>
102
+ </Grid>
103
+
104
+ <Grid container gap={ 0.75 } alignItems="center">
105
+ <Grid item xs={ 12 }>
106
+ <FormLabel size="tiny">{ __( 'Value', 'elementor' ) }</FormLabel>
107
+ </Grid>
108
+ <Grid item xs={ 12 }>
109
+ <UnstableColorField
110
+ size="tiny"
111
+ fullWidth
112
+ value={ color }
113
+ onChange={ setColor }
114
+ slotProps={ {
115
+ colorPicker: {
116
+ anchorEl: anchorRef.current,
117
+ anchorOrigin: { vertical: 'top', horizontal: 'right' },
118
+ transformOrigin: { vertical: 'top', horizontal: -10 },
119
+ },
120
+ } }
121
+ />
122
+ </Grid>
123
+ </Grid>
124
+ </PopoverContent>
125
+
126
+ <CardActions sx={ { pt: 0.5, pb: 1 } }>
127
+ <Button size="small" variant="contained" disabled={ isSaveDisabled() } onClick={ handleUpdate }>
128
+ { __( 'Save', 'elementor' ) }
129
+ </Button>
130
+ </CardActions>
131
+ </PopoverScrollableContent>
132
+ );
133
+ };
@@ -1,71 +1,130 @@
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 { PopoverScrollableContent } from '@elementor/editor-editing-panel';
5
+ import { PopoverHeader, PopoverMenuList, PopoverSearch, type VirtualizedItem } from '@elementor/editor-ui';
6
+ import { BrushIcon, ColorFilterIcon, PlusIcon, SettingsIcon } from '@elementor/icons';
7
+ import { Divider, IconButton } from '@elementor/ui';
8
+ import { __ } from '@wordpress/i18n';
6
9
 
7
- import { usePropVariables } from '../hooks/use-prop-variables';
10
+ import { useFilteredVariables } from '../hooks/use-prop-variables';
8
11
  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';
12
+ import { type ExtendedVirtualizedItem } from '../types';
13
+ import { ColorIndicator } from './ui/color-indicator';
14
+ import { MenuItemContent } from './ui/menu-item-content';
15
+ import { NoSearchResults } from './ui/no-search-results';
16
+ import { NoVariables } from './ui/no-variables';
17
+ import { VariablesStyledMenuList } from './ui/styled-menu-list';
18
+
19
+ const SIZE = 'tiny';
12
20
 
13
21
  type Props = {
14
- onSelect?: () => void;
22
+ closePopover: () => void;
23
+ onAdd?: () => void;
24
+ onEdit?: ( key: string ) => void;
25
+ onSettings?: () => void;
15
26
  };
16
27
 
17
- export const ColorVariablesSelection = ( { onSelect }: Props ) => {
28
+ export const ColorVariablesSelection = ( { closePopover, onAdd, onEdit, onSettings }: Props ) => {
18
29
  const { value: variable, setValue: setVariable } = useBoundProp( colorVariablePropTypeUtil );
30
+ const [ searchValue, setSearchValue ] = useState( '' );
19
31
 
20
- const variables = usePropVariables( colorVariablePropTypeUtil.key );
32
+ const {
33
+ list: variables,
34
+ hasMatches: hasSearchResults,
35
+ isSourceNotEmpty: hasVariables,
36
+ } = useFilteredVariables( searchValue, colorVariablePropTypeUtil.key );
21
37
 
22
- const handleSetColorVariable = ( key: VariableKey ) => {
38
+ const handleSetColorVariable = ( key: string ) => {
23
39
  setVariable( key );
40
+ closePopover();
41
+ };
42
+
43
+ const actions = [];
44
+
45
+ if ( onAdd ) {
46
+ actions.push(
47
+ <IconButton key="add" size={ SIZE } onClick={ onAdd }>
48
+ <PlusIcon fontSize={ SIZE } />
49
+ </IconButton>
50
+ );
51
+ }
24
52
 
25
- onSelect?.();
53
+ if ( onSettings ) {
54
+ actions.push(
55
+ <IconButton key="settings" size={ SIZE } onClick={ onSettings }>
56
+ <SettingsIcon fontSize={ SIZE } />
57
+ </IconButton>
58
+ );
59
+ }
60
+
61
+ const items: ExtendedVirtualizedItem[] = variables.map( ( { value, label, key } ) => ( {
62
+ type: 'item' as const,
63
+ value: key,
64
+ label,
65
+ icon: <ColorIndicator size="inherit" component="span" value={ value } />,
66
+ secondaryText: value,
67
+ onEdit: () => onEdit?.( key ),
68
+ } ) );
69
+
70
+ const handleSearch = ( search: string ) => {
71
+ setSearchValue( search );
72
+ };
73
+
74
+ const handleClearSearch = () => {
75
+ setSearchValue( '' );
26
76
  };
27
77
 
28
78
  return (
29
- <Fragment>
79
+ <>
80
+ <PopoverHeader
81
+ title={ __( 'Variables', 'elementor' ) }
82
+ icon={ <ColorFilterIcon fontSize={ SIZE } /> }
83
+ onClose={ closePopover }
84
+ actions={ actions }
85
+ />
86
+
87
+ { hasVariables && (
88
+ <PopoverSearch
89
+ value={ searchValue }
90
+ onSearch={ handleSearch }
91
+ placeholder={ __( 'Search', 'elementor' ) }
92
+ />
93
+ ) }
94
+
30
95
  <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>
96
+
97
+ <PopoverScrollableContent>
98
+ { hasVariables && hasSearchResults && (
99
+ <PopoverMenuList
100
+ items={ items }
101
+ onSelect={ handleSetColorVariable }
102
+ onClose={ () => {} }
103
+ selectedValue={ variable }
104
+ data-testid="color-variables-list"
105
+ menuListTemplate={ VariablesStyledMenuList }
106
+ menuItemContentTemplate={ ( item: VirtualizedItem< 'item', string > ) => (
107
+ <MenuItemContent item={ item } />
108
+ ) }
109
+ />
110
+ ) }
111
+
112
+ { ! hasSearchResults && hasVariables && (
113
+ <NoSearchResults
114
+ searchValue={ searchValue }
115
+ onClear={ handleClearSearch }
116
+ icon={ <BrushIcon fontSize="large" /> }
117
+ />
118
+ ) }
119
+
120
+ { ! hasVariables && (
121
+ <NoVariables
122
+ title={ __( 'Create your first color variable', 'elementor' ) }
123
+ icon={ <BrushIcon fontSize="large" /> }
124
+ onAdd={ onAdd }
125
+ />
126
+ ) }
127
+ </PopoverScrollableContent>
128
+ </>
70
129
  );
71
130
  };
@@ -1,9 +1,9 @@
1
1
  import * as React from 'react';
2
2
  import { useRef, useState } from 'react';
3
- import { FontFamilySelector, useBoundProp } from '@elementor/editor-controls';
4
- import { useFontFamilies } from '@elementor/editor-editing-panel';
3
+ import { FontFamilySelector, PopoverContent, useBoundProp } from '@elementor/editor-controls';
4
+ import { PopoverScrollableContent, useFontFamilies, useSectionWidth } 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,8 +12,8 @@ import {
12
12
  Divider,
13
13
  FormLabel,
14
14
  Grid,
15
+ IconButton,
15
16
  Popover,
16
- Stack,
17
17
  TextField,
18
18
  UnstableTag,
19
19
  usePopupState,
@@ -25,7 +25,12 @@ import { fontVariablePropTypeUtil } from '../prop-types/font-variable-prop-type'
25
25
 
26
26
  const SIZE = 'tiny';
27
27
 
28
- export const FontVariableCreation = ( { onClose }: { onClose: () => void } ) => {
28
+ type Props = {
29
+ onGoBack?: () => void;
30
+ onClose: () => void;
31
+ };
32
+
33
+ export const FontVariableCreation = ( { onClose, onGoBack }: Props ) => {
29
34
  const fontFamilies = useFontFamilies();
30
35
  const { setValue: setVariable } = useBoundProp( fontVariablePropTypeUtil );
31
36
 
@@ -60,20 +65,31 @@ export const FontVariableCreation = ( { onClose }: { onClose: () => void } ) =>
60
65
  return ! fontFamily?.trim() || ! label?.trim();
61
66
  };
62
67
 
68
+ const sectionWidth = useSectionWidth();
69
+
63
70
  return (
64
- <>
71
+ <PopoverScrollableContent height="auto">
65
72
  <PopoverHeader
73
+ icon={
74
+ <>
75
+ { onGoBack && (
76
+ <IconButton size={ SIZE } aria-label={ __( 'Go Back', 'elementor' ) } onClick={ onGoBack }>
77
+ <ArrowLeftIcon fontSize={ SIZE } />
78
+ </IconButton>
79
+ ) }
80
+ <TextIcon fontSize={ SIZE } />
81
+ </>
82
+ }
66
83
  title={ __( 'Create variable', 'elementor' ) }
67
84
  onClose={ closePopover }
68
- icon={ <TextIcon fontSize={ SIZE } /> }
69
85
  />
70
86
 
71
87
  <Divider />
72
88
 
73
- <Stack p={ 1.5 } gap={ 1.5 }>
89
+ <PopoverContent p={ 2 }>
74
90
  <Grid container gap={ 0.75 } alignItems="center">
75
91
  <Grid item xs={ 12 }>
76
- <FormLabel size="small">{ __( 'Name', 'elementor' ) }</FormLabel>
92
+ <FormLabel size="tiny">{ __( 'Name', 'elementor' ) }</FormLabel>
77
93
  </Grid>
78
94
  <Grid item xs={ 12 }>
79
95
  <TextField
@@ -87,7 +103,7 @@ export const FontVariableCreation = ( { onClose }: { onClose: () => void } ) =>
87
103
 
88
104
  <Grid container gap={ 0.75 } alignItems="center">
89
105
  <Grid item xs={ 12 }>
90
- <FormLabel size="small">{ __( 'Value', 'elementor' ) }</FormLabel>
106
+ <FormLabel size="tiny">{ __( 'Value', 'elementor' ) }</FormLabel>
91
107
  </Grid>
92
108
  <Grid item xs={ 12 }>
93
109
  <>
@@ -111,21 +127,19 @@ export const FontVariableCreation = ( { onClose }: { onClose: () => void } ) =>
111
127
  fontFamily={ fontFamily }
112
128
  onFontFamilyChange={ setFontFamily }
113
129
  onClose={ fontPopoverState.close }
130
+ sectionWidth={ sectionWidth }
114
131
  />
115
132
  </Popover>
116
133
  </>
117
134
  </Grid>
118
135
  </Grid>
119
- </Stack>
136
+ </PopoverContent>
120
137
 
121
- <CardActions>
122
- <Button size="small" onClick={ closePopover } color="secondary" variant="text">
123
- { __( 'Cancel', 'elementor' ) }
124
- </Button>
138
+ <CardActions sx={ { pt: 0.5, pb: 1 } }>
125
139
  <Button size="small" variant="contained" disabled={ isFormInvalid() } onClick={ handleCreate }>
126
140
  { __( 'Create', 'elementor' ) }
127
141
  </Button>
128
142
  </CardActions>
129
- </>
143
+ </PopoverScrollableContent>
130
144
  );
131
145
  };
@@ -0,0 +1,163 @@
1
+ import * as React from 'react';
2
+ import { useId, useRef, useState } from 'react';
3
+ import { FontFamilySelector, PopoverContent } from '@elementor/editor-controls';
4
+ import { useFontFamilies, useSectionWidth } from '@elementor/editor-editing-panel';
5
+ import { PopoverHeader, PopoverScrollableContent } from '@elementor/editor-ui';
6
+ import { ArrowLeftIcon, ChevronDownIcon, TextIcon, TrashIcon } from '@elementor/icons';
7
+ import {
8
+ bindPopover,
9
+ bindTrigger,
10
+ Button,
11
+ CardActions,
12
+ Divider,
13
+ FormLabel,
14
+ Grid,
15
+ IconButton,
16
+ Popover,
17
+ TextField,
18
+ UnstableTag,
19
+ usePopupState,
20
+ } from '@elementor/ui';
21
+ import { __ } from '@wordpress/i18n';
22
+
23
+ import { deleteVariable, updateVariable, useVariable } from '../hooks/use-prop-variables';
24
+
25
+ const SIZE = 'tiny';
26
+
27
+ type Props = {
28
+ editId: string;
29
+ onClose: () => void;
30
+ onGoBack?: () => void;
31
+ onSubmit?: () => void;
32
+ };
33
+
34
+ export const FontVariableEdit = ( { onClose, onGoBack, onSubmit, editId }: Props ) => {
35
+ const variable = useVariable( editId );
36
+
37
+ if ( ! variable ) {
38
+ throw new Error( `Global font variable "${ editId }" not found` );
39
+ }
40
+
41
+ const [ fontFamily, setFontFamily ] = useState( variable.value );
42
+ const [ label, setLabel ] = useState( variable.label );
43
+
44
+ const variableNameId = useId();
45
+ const variableValueId = useId();
46
+
47
+ const anchorRef = useRef< HTMLDivElement >( null );
48
+ const fontPopoverState = usePopupState( { variant: 'popover' } );
49
+
50
+ const fontFamilies = useFontFamilies();
51
+
52
+ const handleUpdate = () => {
53
+ updateVariable( editId, {
54
+ value: fontFamily,
55
+ label,
56
+ } ).then( () => {
57
+ onSubmit?.();
58
+ } );
59
+ };
60
+
61
+ const handleDelete = () => {
62
+ deleteVariable( editId ).then( () => {
63
+ onSubmit?.();
64
+ } );
65
+ };
66
+
67
+ const noValueChanged = () => fontFamily === variable.value && label === variable.label;
68
+ const hasEmptyValue = () => '' === fontFamily.trim() || '' === label.trim();
69
+ const isSaveDisabled = () => noValueChanged() || hasEmptyValue();
70
+
71
+ const sectionWidth = useSectionWidth();
72
+
73
+ const actions = [];
74
+
75
+ actions.push(
76
+ <IconButton key="delete" size={ SIZE } aria-label={ __( 'Delete', 'elementor' ) } onClick={ handleDelete }>
77
+ <TrashIcon fontSize={ SIZE } />
78
+ </IconButton>
79
+ );
80
+
81
+ return (
82
+ <PopoverScrollableContent height="auto">
83
+ <PopoverHeader
84
+ icon={
85
+ <>
86
+ { onGoBack && (
87
+ <IconButton size={ SIZE } aria-label={ __( 'Go Back', 'elementor' ) } onClick={ onGoBack }>
88
+ <ArrowLeftIcon fontSize={ SIZE } />
89
+ </IconButton>
90
+ ) }
91
+ <TextIcon fontSize={ SIZE } />
92
+ </>
93
+ }
94
+ title={ __( 'Edit variable', 'elementor' ) }
95
+ onClose={ onClose }
96
+ actions={ actions }
97
+ />
98
+
99
+ <Divider />
100
+
101
+ <PopoverContent p={ 2 }>
102
+ <Grid container gap={ 0.75 } alignItems="center">
103
+ <Grid item xs={ 12 }>
104
+ <FormLabel htmlFor={ variableNameId } size="tiny">
105
+ { __( 'Name', 'elementor' ) }
106
+ </FormLabel>
107
+ </Grid>
108
+ <Grid item xs={ 12 }>
109
+ <TextField
110
+ id={ variableNameId }
111
+ size="tiny"
112
+ fullWidth
113
+ value={ label }
114
+ onChange={ ( e: React.ChangeEvent< HTMLInputElement > ) => setLabel( e.target.value ) }
115
+ />
116
+ </Grid>
117
+ </Grid>
118
+
119
+ <Grid container gap={ 0.75 } alignItems="center">
120
+ <Grid item xs={ 12 }>
121
+ <FormLabel htmlFor={ variableValueId } size="tiny">
122
+ { __( 'Value', 'elementor' ) }
123
+ </FormLabel>
124
+ </Grid>
125
+ <Grid item xs={ 12 }>
126
+ <>
127
+ <UnstableTag
128
+ id={ variableValueId }
129
+ variant="outlined"
130
+ label={ fontFamily }
131
+ endIcon={ <ChevronDownIcon fontSize="tiny" /> }
132
+ { ...bindTrigger( fontPopoverState ) }
133
+ fullWidth
134
+ />
135
+ <Popover
136
+ disablePortal
137
+ disableScrollLock
138
+ anchorEl={ anchorRef.current }
139
+ anchorOrigin={ { vertical: 'top', horizontal: 'right' } }
140
+ transformOrigin={ { vertical: 'top', horizontal: -20 } }
141
+ { ...bindPopover( fontPopoverState ) }
142
+ >
143
+ <FontFamilySelector
144
+ fontFamilies={ fontFamilies }
145
+ fontFamily={ fontFamily }
146
+ onFontFamilyChange={ setFontFamily }
147
+ onClose={ fontPopoverState.close }
148
+ sectionWidth={ sectionWidth }
149
+ />
150
+ </Popover>
151
+ </>
152
+ </Grid>
153
+ </Grid>
154
+ </PopoverContent>
155
+
156
+ <CardActions sx={ { pt: 0.5, pb: 1 } }>
157
+ <Button size="small" variant="contained" disabled={ isSaveDisabled() } onClick={ handleUpdate }>
158
+ { __( 'Save', 'elementor' ) }
159
+ </Button>
160
+ </CardActions>
161
+ </PopoverScrollableContent>
162
+ );
163
+ };