@elementor/editor-variables 0.15.0 → 0.18.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 +88 -0
  2. package/dist/index.js +894 -486
  3. package/dist/index.js.map +1 -1
  4. package/dist/index.mjs +892 -511
  5. package/dist/index.mjs.map +1 -1
  6. package/package.json +9 -9
  7. package/src/components/color-variable-creation.tsx +37 -58
  8. package/src/components/color-variable-edit.tsx +110 -86
  9. package/src/components/color-variables-selection.tsx +32 -34
  10. package/src/components/fields/color-field.tsx +54 -0
  11. package/src/components/fields/font-field.tsx +85 -0
  12. package/src/components/fields/label-field.tsx +54 -0
  13. package/src/components/font-variable-creation.tsx +39 -78
  14. package/src/components/font-variable-edit.tsx +108 -114
  15. package/src/components/font-variables-selection.tsx +32 -34
  16. package/src/components/ui/delete-confirmation-dialog.tsx +52 -0
  17. package/src/components/ui/deleted-variable-alert.tsx +47 -0
  18. package/src/components/ui/menu-item-content.tsx +2 -5
  19. package/src/components/ui/tags/assigned-tag.tsx +45 -0
  20. package/src/components/ui/tags/deleted-tag.tsx +37 -0
  21. package/src/components/ui/variable/assigned-variable.tsx +70 -0
  22. package/src/components/ui/variable/deleted-variable.tsx +76 -0
  23. package/src/controls/color-variable-control.tsx +21 -48
  24. package/src/controls/font-variable-control.tsx +20 -43
  25. package/src/create-style-variables-repository.ts +44 -5
  26. package/src/hooks/use-prop-variables.ts +6 -0
  27. package/src/init-color-variables.ts +3 -48
  28. package/src/renderers/style-variables-renderer.tsx +10 -4
  29. package/src/repeater-injections.ts +35 -0
  30. package/src/service.ts +23 -2
  31. package/src/sync/enqueue-font.ts +7 -0
  32. package/src/sync/types.ts +5 -0
  33. package/src/transformers/variable-transformer.ts +21 -3
  34. package/src/types.ts +1 -1
  35. package/src/utils/validations.ts +42 -0
  36. package/src/components/ui/variable-tag.tsx +0 -43
@@ -0,0 +1,54 @@
1
+ import * as React from 'react';
2
+ import { useId, useState } from 'react';
3
+ import { FormHelperText, FormLabel, Grid, TextField } from '@elementor/ui';
4
+ import { __ } from '@wordpress/i18n';
5
+
6
+ import { labelHint, validateLabel, VARIABLE_LABEL_MAX_LENGTH } from '../../utils/validations';
7
+
8
+ type LabelFieldProps = {
9
+ value: string;
10
+ onChange: ( value: string ) => void;
11
+ };
12
+
13
+ export const LabelField = ( { value, onChange }: LabelFieldProps ) => {
14
+ const [ label, setLabel ] = useState( value );
15
+ const [ errorMessage, setErrorMessage ] = useState( '' );
16
+ const [ noticeMessage, setNoticeMessage ] = useState( () => labelHint( value ) );
17
+
18
+ const handleChange = ( newValue: string ) => {
19
+ setLabel( newValue );
20
+
21
+ const errorMsg = validateLabel( newValue );
22
+ const hintMsg = labelHint( newValue );
23
+
24
+ setErrorMessage( errorMsg );
25
+ setNoticeMessage( errorMsg ? '' : hintMsg );
26
+
27
+ onChange( errorMsg ? '' : newValue );
28
+ };
29
+
30
+ const id = useId();
31
+
32
+ return (
33
+ <Grid container gap={ 0.75 } alignItems="center">
34
+ <Grid item xs={ 12 }>
35
+ <FormLabel htmlFor={ id } size="tiny">
36
+ { __( 'Name', 'elementor' ) }
37
+ </FormLabel>
38
+ </Grid>
39
+ <Grid item xs={ 12 }>
40
+ <TextField
41
+ id={ id }
42
+ size="tiny"
43
+ fullWidth
44
+ value={ label }
45
+ error={ !! errorMessage }
46
+ onChange={ ( e: React.ChangeEvent< HTMLInputElement > ) => handleChange( e.target.value ) }
47
+ inputProps={ { maxLength: VARIABLE_LABEL_MAX_LENGTH } }
48
+ />
49
+ { errorMessage && <FormHelperText error>{ errorMessage }</FormHelperText> }
50
+ { noticeMessage && <FormHelperText>{ noticeMessage }</FormHelperText> }
51
+ </Grid>
52
+ </Grid>
53
+ );
54
+ };
@@ -1,27 +1,16 @@
1
1
  import * as React from 'react';
2
- import { useRef, useState } from 'react';
3
- import { FontFamilySelector, PopoverContent, useBoundProp } from '@elementor/editor-controls';
4
- import { PopoverScrollableContent, useFontFamilies, useSectionWidth } from '@elementor/editor-editing-panel';
2
+ import { useState } from 'react';
3
+ import { PopoverContent, useBoundProp } from '@elementor/editor-controls';
4
+ import { PopoverBody } from '@elementor/editor-editing-panel';
5
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
- TextField,
18
- UnstableTag,
19
- usePopupState,
20
- } from '@elementor/ui';
6
+ import { ArrowLeftIcon, TextIcon } from '@elementor/icons';
7
+ import { Button, CardActions, Divider, FormHelperText, IconButton } from '@elementor/ui';
21
8
  import { __ } from '@wordpress/i18n';
22
9
 
23
10
  import { createVariable } from '../hooks/use-prop-variables';
24
11
  import { fontVariablePropTypeUtil } from '../prop-types/font-variable-prop-type';
12
+ import { FontField } from './fields/font-field';
13
+ import { LabelField } from './fields/label-field';
25
14
 
26
15
  const SIZE = 'tiny';
27
16
 
@@ -31,18 +20,16 @@ type Props = {
31
20
  };
32
21
 
33
22
  export const FontVariableCreation = ( { onClose, onGoBack }: Props ) => {
34
- const fontFamilies = useFontFamilies();
35
23
  const { setValue: setVariable } = useBoundProp( fontVariablePropTypeUtil );
36
24
 
37
25
  const [ fontFamily, setFontFamily ] = useState( '' );
38
26
  const [ label, setLabel ] = useState( '' );
39
-
40
- const anchorRef = useRef< HTMLDivElement >( null );
41
- const fontPopoverState = usePopupState( { variant: 'popover' } );
27
+ const [ errorMessage, setErrorMessage ] = useState( '' );
42
28
 
43
29
  const resetFields = () => {
44
30
  setFontFamily( '' );
45
31
  setLabel( '' );
32
+ setErrorMessage( '' );
46
33
  };
47
34
 
48
35
  const closePopover = () => {
@@ -55,20 +42,24 @@ export const FontVariableCreation = ( { onClose, onGoBack }: Props ) => {
55
42
  value: fontFamily,
56
43
  label,
57
44
  type: fontVariablePropTypeUtil.key,
58
- } ).then( ( key ) => {
59
- setVariable( key );
60
- closePopover();
61
- } );
45
+ } )
46
+ .then( ( key ) => {
47
+ setVariable( key );
48
+ closePopover();
49
+ } )
50
+ .catch( ( error ) => {
51
+ setErrorMessage( error.message );
52
+ } );
62
53
  };
63
54
 
64
- const isFormInvalid = () => {
65
- return ! fontFamily?.trim() || ! label?.trim();
55
+ const hasEmptyValue = () => {
56
+ return '' === fontFamily.trim() || '' === label.trim();
66
57
  };
67
58
 
68
- const sectionWidth = useSectionWidth();
59
+ const isSubmitDisabled = hasEmptyValue();
69
60
 
70
61
  return (
71
- <PopoverScrollableContent height="auto">
62
+ <PopoverBody height="auto">
72
63
  <PopoverHeader
73
64
  icon={
74
65
  <>
@@ -87,59 +78,29 @@ export const FontVariableCreation = ( { onClose, onGoBack }: Props ) => {
87
78
  <Divider />
88
79
 
89
80
  <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
- <>
110
- <UnstableTag
111
- variant="outlined"
112
- label={ fontFamily }
113
- endIcon={ <ChevronDownIcon fontSize="tiny" /> }
114
- { ...bindTrigger( fontPopoverState ) }
115
- fullWidth
116
- />
117
- <Popover
118
- disablePortal
119
- disableScrollLock
120
- anchorEl={ anchorRef.current }
121
- anchorOrigin={ { vertical: 'top', horizontal: 'right' } }
122
- transformOrigin={ { vertical: 'top', horizontal: -20 } }
123
- { ...bindPopover( fontPopoverState ) }
124
- >
125
- <FontFamilySelector
126
- fontFamilies={ fontFamilies }
127
- fontFamily={ fontFamily }
128
- onFontFamilyChange={ setFontFamily }
129
- onClose={ fontPopoverState.close }
130
- sectionWidth={ sectionWidth }
131
- />
132
- </Popover>
133
- </>
134
- </Grid>
135
- </Grid>
81
+ <LabelField
82
+ value={ label }
83
+ onChange={ ( value ) => {
84
+ setLabel( value );
85
+ setErrorMessage( '' );
86
+ } }
87
+ />
88
+ <FontField
89
+ value={ fontFamily }
90
+ onChange={ ( value ) => {
91
+ setFontFamily( value );
92
+ setErrorMessage( '' );
93
+ } }
94
+ />
95
+
96
+ { errorMessage && <FormHelperText error>{ errorMessage }</FormHelperText> }
136
97
  </PopoverContent>
137
98
 
138
99
  <CardActions sx={ { pt: 0.5, pb: 1 } }>
139
- <Button size="small" variant="contained" disabled={ isFormInvalid() } onClick={ handleCreate }>
100
+ <Button size="small" variant="contained" disabled={ isSubmitDisabled } onClick={ handleCreate }>
140
101
  { __( 'Create', 'elementor' ) }
141
102
  </Button>
142
103
  </CardActions>
143
- </PopoverScrollableContent>
104
+ </PopoverBody>
144
105
  );
145
106
  };
@@ -1,26 +1,17 @@
1
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';
2
+ import { useState } from 'react';
3
+ import { PopoverContent, useBoundProp } from '@elementor/editor-controls';
4
+ import { PopoverBody } from '@elementor/editor-editing-panel';
5
+ import { PopoverHeader } from '@elementor/editor-ui';
6
+ import { ArrowLeftIcon, TextIcon, TrashIcon } from '@elementor/icons';
7
+ import { Button, CardActions, Divider, FormHelperText, IconButton } from '@elementor/ui';
21
8
  import { __ } from '@wordpress/i18n';
22
9
 
23
10
  import { deleteVariable, updateVariable, useVariable } from '../hooks/use-prop-variables';
11
+ import { fontVariablePropTypeUtil } from '../prop-types/font-variable-prop-type';
12
+ import { FontField } from './fields/font-field';
13
+ import { LabelField } from './fields/label-field';
14
+ import { DeleteConfirmationDialog } from './ui/delete-confirmation-dialog';
24
15
 
25
16
  const SIZE = 'tiny';
26
17
 
@@ -32,8 +23,11 @@ type Props = {
32
23
  };
33
24
 
34
25
  export const FontVariableEdit = ( { onClose, onGoBack, onSubmit, editId }: Props ) => {
35
- const variable = useVariable( editId );
26
+ const { setValue: notifyBoundPropChange, value: assignedValue } = useBoundProp( fontVariablePropTypeUtil );
27
+ const [ deleteConfirmation, setDeleteConfirmation ] = useState( false );
28
+ const [ errorMessage, setErrorMessage ] = useState( '' );
36
29
 
30
+ const variable = useVariable( editId );
37
31
  if ( ! variable ) {
38
32
  throw new Error( `Global font variable "${ editId }" not found` );
39
33
  }
@@ -41,123 +35,123 @@ export const FontVariableEdit = ( { onClose, onGoBack, onSubmit, editId }: Props
41
35
  const [ fontFamily, setFontFamily ] = useState( variable.value );
42
36
  const [ label, setLabel ] = useState( variable.label );
43
37
 
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
38
  const handleUpdate = () => {
53
39
  updateVariable( editId, {
54
40
  value: fontFamily,
55
41
  label,
56
- } ).then( () => {
57
- onSubmit?.();
58
- } );
42
+ } )
43
+ .then( () => {
44
+ maybeTriggerBoundPropChange();
45
+ onSubmit?.();
46
+ } )
47
+ .catch( ( error ) => {
48
+ setErrorMessage( error.message );
49
+ } );
59
50
  };
60
51
 
61
52
  const handleDelete = () => {
62
53
  deleteVariable( editId ).then( () => {
54
+ maybeTriggerBoundPropChange();
63
55
  onSubmit?.();
64
56
  } );
65
57
  };
66
58
 
67
- const noValueChanged = () => fontFamily === variable.value && label === variable.label;
68
- const hasEmptyValue = () => '' === fontFamily.trim() || '' === label.trim();
69
- const isSaveDisabled = () => noValueChanged() || hasEmptyValue();
59
+ const maybeTriggerBoundPropChange = () => {
60
+ if ( editId === assignedValue ) {
61
+ notifyBoundPropChange( editId );
62
+ }
63
+ };
64
+
65
+ const handleDeleteConfirmation = () => {
66
+ setDeleteConfirmation( true );
67
+ };
68
+
69
+ const closeDeleteDialog = () => () => {
70
+ setDeleteConfirmation( false );
71
+ };
72
+
73
+ const hasEmptyValue = () => {
74
+ return ! fontFamily.trim() || ! label.trim();
75
+ };
76
+
77
+ const noValueChanged = () => {
78
+ return fontFamily === variable.value && label === variable.label;
79
+ };
70
80
 
71
- const sectionWidth = useSectionWidth();
81
+ const isSubmitDisabled = noValueChanged() || hasEmptyValue();
72
82
 
73
83
  const actions = [];
74
84
 
75
85
  actions.push(
76
- <IconButton key="delete" size={ SIZE } aria-label={ __( 'Delete', 'elementor' ) } onClick={ handleDelete }>
86
+ <IconButton
87
+ key="delete"
88
+ size={ SIZE }
89
+ aria-label={ __( 'Delete', 'elementor' ) }
90
+ onClick={ handleDeleteConfirmation }
91
+ >
77
92
  <TrashIcon fontSize={ SIZE } />
78
93
  </IconButton>
79
94
  );
80
95
 
81
96
  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 }>
97
+ <>
98
+ <PopoverBody height="auto">
99
+ <PopoverHeader
100
+ icon={
126
101
  <>
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>
102
+ { onGoBack && (
103
+ <IconButton
104
+ size={ SIZE }
105
+ aria-label={ __( 'Go Back', 'elementor' ) }
106
+ onClick={ onGoBack }
107
+ >
108
+ <ArrowLeftIcon fontSize={ SIZE } />
109
+ </IconButton>
110
+ ) }
111
+ <TextIcon fontSize={ SIZE } />
151
112
  </>
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>
113
+ }
114
+ title={ __( 'Edit variable', 'elementor' ) }
115
+ onClose={ onClose }
116
+ actions={ actions }
117
+ />
118
+
119
+ <Divider />
120
+
121
+ <PopoverContent p={ 2 }>
122
+ <LabelField
123
+ value={ label }
124
+ onChange={ ( value ) => {
125
+ setLabel( value );
126
+ setErrorMessage( '' );
127
+ } }
128
+ />
129
+ <FontField
130
+ value={ fontFamily }
131
+ onChange={ ( value ) => {
132
+ setFontFamily( value );
133
+ setErrorMessage( '' );
134
+ } }
135
+ />
136
+
137
+ { errorMessage && <FormHelperText error>{ errorMessage }</FormHelperText> }
138
+ </PopoverContent>
139
+
140
+ <CardActions sx={ { pt: 0.5, pb: 1 } }>
141
+ <Button size="small" variant="contained" disabled={ isSubmitDisabled } onClick={ handleUpdate }>
142
+ { __( 'Save', 'elementor' ) }
143
+ </Button>
144
+ </CardActions>
145
+ </PopoverBody>
146
+
147
+ { deleteConfirmation && (
148
+ <DeleteConfirmationDialog
149
+ open
150
+ label={ label }
151
+ onConfirm={ handleDelete }
152
+ closeDialog={ closeDeleteDialog() }
153
+ />
154
+ ) }
155
+ </>
162
156
  );
163
157
  };
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import { useState } from 'react';
3
3
  import { useBoundProp } from '@elementor/editor-controls';
4
- import { PopoverScrollableContent } from '@elementor/editor-editing-panel';
4
+ import { PopoverBody } from '@elementor/editor-editing-panel';
5
5
  import { PopoverHeader, PopoverMenuList, PopoverSearch, type VirtualizedItem } from '@elementor/editor-ui';
6
6
  import { ColorFilterIcon, PlusIcon, SettingsIcon, TextIcon } from '@elementor/icons';
7
7
  import { Divider, IconButton } from '@elementor/ui';
@@ -75,7 +75,7 @@ export const FontVariablesSelection = ( { closePopover, onAdd, onEdit, onSetting
75
75
  };
76
76
 
77
77
  return (
78
- <>
78
+ <PopoverBody>
79
79
  <PopoverHeader
80
80
  title={ __( 'Variables', 'elementor' ) }
81
81
  onClose={ closePopover }
@@ -93,37 +93,35 @@ export const FontVariablesSelection = ( { closePopover, onAdd, onEdit, onSetting
93
93
 
94
94
  <Divider />
95
95
 
96
- <PopoverScrollableContent>
97
- { hasVariables && hasSearchResults && (
98
- <PopoverMenuList
99
- items={ items }
100
- onSelect={ handleSetVariable }
101
- onClose={ () => {} }
102
- selectedValue={ variable }
103
- data-testid="font-variables-list"
104
- menuListTemplate={ VariablesStyledMenuList }
105
- menuItemContentTemplate={ ( item: VirtualizedItem< 'item', string > ) => (
106
- <MenuItemContent item={ item } />
107
- ) }
108
- />
109
- ) }
110
-
111
- { ! hasSearchResults && hasVariables && (
112
- <NoSearchResults
113
- searchValue={ searchValue }
114
- onClear={ handleClearSearch }
115
- icon={ <TextIcon fontSize="large" /> }
116
- />
117
- ) }
118
-
119
- { ! hasVariables && (
120
- <NoVariables
121
- title={ __( 'Create your first font variable', 'elementor' ) }
122
- icon={ <TextIcon fontSize="large" /> }
123
- onAdd={ onAdd }
124
- />
125
- ) }
126
- </PopoverScrollableContent>
127
- </>
96
+ { hasVariables && hasSearchResults && (
97
+ <PopoverMenuList
98
+ items={ items }
99
+ onSelect={ handleSetVariable }
100
+ onClose={ () => {} }
101
+ selectedValue={ variable }
102
+ data-testid="font-variables-list"
103
+ menuListTemplate={ VariablesStyledMenuList }
104
+ menuItemContentTemplate={ ( item: VirtualizedItem< 'item', string > ) => (
105
+ <MenuItemContent item={ item } />
106
+ ) }
107
+ />
108
+ ) }
109
+
110
+ { ! hasSearchResults && hasVariables && (
111
+ <NoSearchResults
112
+ searchValue={ searchValue }
113
+ onClear={ handleClearSearch }
114
+ icon={ <TextIcon fontSize="large" /> }
115
+ />
116
+ ) }
117
+
118
+ { ! hasVariables && (
119
+ <NoVariables
120
+ title={ __( 'Create your first font variable', 'elementor' ) }
121
+ icon={ <TextIcon fontSize="large" /> }
122
+ onAdd={ onAdd }
123
+ />
124
+ ) }
125
+ </PopoverBody>
128
126
  );
129
127
  };
@@ -0,0 +1,52 @@
1
+ import * as React from 'react';
2
+ import { AlertOctagonFilledIcon } from '@elementor/icons';
3
+ import {
4
+ Button,
5
+ Dialog,
6
+ DialogActions,
7
+ DialogContent,
8
+ DialogContentText,
9
+ DialogTitle,
10
+ Typography,
11
+ } from '@elementor/ui';
12
+ import { __ } from '@wordpress/i18n';
13
+
14
+ const TITLE_ID = 'delete-variable-dialog';
15
+
16
+ export const DeleteConfirmationDialog = ( {
17
+ open,
18
+ label,
19
+ closeDialog,
20
+ onConfirm,
21
+ }: {
22
+ open: boolean;
23
+ label: string;
24
+ closeDialog: () => void;
25
+ onConfirm: () => void;
26
+ } ) => {
27
+ return (
28
+ <Dialog open={ open } onClose={ closeDialog } aria-labelledby={ TITLE_ID } maxWidth="xs">
29
+ <DialogTitle id={ TITLE_ID } display="flex" alignItems="center" gap={ 1 } sx={ { lineHeight: 1 } }>
30
+ <AlertOctagonFilledIcon color="error" />
31
+ { __( 'Delete this variable?', 'elementor' ) }
32
+ </DialogTitle>
33
+ <DialogContent>
34
+ <DialogContentText variant="body2" color="textPrimary">
35
+ { __( 'All elements using', 'elementor' ) }
36
+ <Typography variant="subtitle2" component="span">
37
+ &nbsp;{ label }&nbsp;
38
+ </Typography>
39
+ { __( 'will keep their current values, but the variable itself will be removed.', 'elementor' ) }
40
+ </DialogContentText>
41
+ </DialogContent>
42
+ <DialogActions>
43
+ <Button color="secondary" onClick={ closeDialog }>
44
+ { __( 'Not now', 'elementor' ) }
45
+ </Button>
46
+ <Button variant="contained" color="error" onClick={ onConfirm }>
47
+ { __( 'Delete', 'elementor' ) }
48
+ </Button>
49
+ </DialogActions>
50
+ </Dialog>
51
+ );
52
+ };
@@ -0,0 +1,47 @@
1
+ import * as React from 'react';
2
+ import { useSectionWidth } from '@elementor/editor-editing-panel';
3
+ import { Alert, AlertAction, AlertTitle, ClickAwayListener } from '@elementor/ui';
4
+ import { __ } from '@wordpress/i18n';
5
+
6
+ type DeletedVariableAlertProps = {
7
+ onClose: () => void;
8
+ onUnlink?: () => void;
9
+ onRestore?: () => void;
10
+ label: string;
11
+ };
12
+
13
+ export const DeletedVariableAlert = ( { onClose, onUnlink, onRestore, label }: DeletedVariableAlertProps ) => {
14
+ const sectionWidth = useSectionWidth();
15
+
16
+ return (
17
+ <ClickAwayListener onClickAway={ onClose }>
18
+ <Alert
19
+ variant="standard"
20
+ severity="warning"
21
+ onClose={ onClose }
22
+ action={
23
+ <>
24
+ { onUnlink && (
25
+ <AlertAction variant="contained" onClick={ onUnlink }>
26
+ { __( 'Unlink', 'elementor' ) }
27
+ </AlertAction>
28
+ ) }
29
+ { onRestore && (
30
+ <AlertAction variant="outlined" onClick={ onRestore }>
31
+ { __( 'Restore', 'elementor' ) }
32
+ </AlertAction>
33
+ ) }
34
+ </>
35
+ }
36
+ sx={ { width: sectionWidth } }
37
+ >
38
+ <AlertTitle>{ __( 'Deleted variable', 'elementor' ) }</AlertTitle>
39
+ { __( 'The variable', 'elementor' ) } &apos;{ label }&apos;{ ' ' }
40
+ { __(
41
+ 'has been deleted, but it is still referenced in this location. You may restore the variable or unlink it to assign a different value.',
42
+ 'elementor'
43
+ ) }
44
+ </Alert>
45
+ </ClickAwayListener>
46
+ );
47
+ };