@elementor/editor-variables 3.33.0-174 → 3.33.0-176

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elementor/editor-variables",
3
- "version": "3.33.0-174",
3
+ "version": "3.33.0-176",
4
4
  "private": false,
5
5
  "author": "Elementor Team",
6
6
  "homepage": "https://elementor.com/",
@@ -39,20 +39,20 @@
39
39
  "dev": "tsup --config=../../tsup.dev.ts"
40
40
  },
41
41
  "dependencies": {
42
- "@elementor/editor": "3.33.0-174",
43
- "@elementor/editor-canvas": "3.33.0-174",
44
- "@elementor/editor-controls": "3.33.0-174",
45
- "@elementor/editor-current-user": "3.33.0-174",
46
- "@elementor/editor-editing-panel": "3.33.0-174",
47
- "@elementor/editor-mcp": "3.33.0-174",
48
- "@elementor/editor-panels": "3.33.0-174",
49
- "@elementor/editor-props": "3.33.0-174",
50
- "@elementor/editor-ui": "3.33.0-174",
51
- "@elementor/editor-v1-adapters": "3.33.0-174",
52
- "@elementor/http-client": "3.33.0-174",
42
+ "@elementor/editor": "3.33.0-176",
43
+ "@elementor/editor-canvas": "3.33.0-176",
44
+ "@elementor/editor-controls": "3.33.0-176",
45
+ "@elementor/editor-current-user": "3.33.0-176",
46
+ "@elementor/editor-editing-panel": "3.33.0-176",
47
+ "@elementor/editor-mcp": "3.33.0-176",
48
+ "@elementor/editor-panels": "3.33.0-176",
49
+ "@elementor/editor-props": "3.33.0-176",
50
+ "@elementor/editor-ui": "3.33.0-176",
51
+ "@elementor/editor-v1-adapters": "3.33.0-176",
52
+ "@elementor/http-client": "3.33.0-176",
53
53
  "@elementor/icons": "1.53.0",
54
- "@elementor/mixpanel": "3.33.0-174",
55
- "@elementor/schema": "3.33.0-174",
54
+ "@elementor/mixpanel": "3.33.0-176",
55
+ "@elementor/schema": "3.33.0-176",
56
56
  "@elementor/ui": "1.36.14",
57
57
  "@wordpress/i18n": "^5.13.0"
58
58
  },
@@ -1,10 +1,11 @@
1
1
  import * as React from 'react';
2
2
  import { EllipsisWithTooltip, type VirtualizedItem } from '@elementor/editor-ui';
3
3
  import { EditIcon } from '@elementor/icons';
4
- import { Box, IconButton, ListItemIcon, Typography } from '@elementor/ui';
4
+ import { Box, IconButton, ListItemIcon, Tooltip, Typography } from '@elementor/ui';
5
5
  import { __ } from '@wordpress/i18n';
6
6
 
7
7
  const SIZE = 'tiny';
8
+ const EDIT_LABEL = __( 'Edit variable', 'elementor' );
8
9
 
9
10
  export const MenuItemContent = < T, V extends string >( { item }: { item: VirtualizedItem< T, V > } ) => {
10
11
  const onEdit = item.onEdit as ( ( value: V ) => void ) | undefined;
@@ -41,16 +42,18 @@ export const MenuItemContent = < T, V extends string >( { item }: { item: Virtua
41
42
  ) }
42
43
  </Box>
43
44
  { !! onEdit && (
44
- <IconButton
45
- sx={ { mx: 1, opacity: '0' } }
46
- onClick={ ( e: React.MouseEvent< HTMLButtonElement > ) => {
47
- e.stopPropagation();
48
- onEdit( item.value );
49
- } }
50
- aria-label={ __( 'Edit', 'elementor' ) }
51
- >
52
- <EditIcon color="action" fontSize={ SIZE } />
53
- </IconButton>
45
+ <Tooltip placement="top" title={ EDIT_LABEL }>
46
+ <IconButton
47
+ sx={ { mx: 1, opacity: '0' } }
48
+ onClick={ ( e: React.MouseEvent< HTMLButtonElement > ) => {
49
+ e.stopPropagation();
50
+ onEdit( item.value );
51
+ } }
52
+ aria-label={ EDIT_LABEL }
53
+ >
54
+ <EditIcon color="action" fontSize={ SIZE } />
55
+ </IconButton>
56
+ </Tooltip>
54
57
  ) }
55
58
  </>
56
59
  );
@@ -4,6 +4,7 @@ import { Box, IconButton, Stack, Tooltip, Typography, UnstableTag as Tag, type U
4
4
  import { __ } from '@wordpress/i18n';
5
5
 
6
6
  export const SIZE = 'tiny';
7
+ const UNLINK_LABEL = __( 'Unlink variable', 'elementor' );
7
8
 
8
9
  interface VariableTagProps extends UnstableTagProps {
9
10
  onUnlink?: () => void;
@@ -14,9 +15,11 @@ export const AssignedTag = ( { startIcon, label, onUnlink, ...props }: VariableT
14
15
 
15
16
  if ( onUnlink ) {
16
17
  actions.push(
17
- <IconButton key="unlink" size={ SIZE } onClick={ onUnlink } aria-label={ __( 'Unlink', 'elementor' ) }>
18
- <DetachIcon fontSize={ SIZE } />
19
- </IconButton>
18
+ <Tooltip key="unlink" title={ UNLINK_LABEL } placement="bottom">
19
+ <IconButton size={ SIZE } onClick={ onUnlink } aria-label={ UNLINK_LABEL }>
20
+ <DetachIcon fontSize={ SIZE } />
21
+ </IconButton>
22
+ </Tooltip>
20
23
  );
21
24
  }
22
25
 
@@ -5,7 +5,7 @@ import { useSuppressedMessage } from '@elementor/editor-current-user';
5
5
  import { PopoverBody } from '@elementor/editor-editing-panel';
6
6
  import { PopoverHeader } from '@elementor/editor-ui';
7
7
  import { ArrowLeftIcon, TrashIcon } from '@elementor/icons';
8
- import { Button, CardActions, Divider, FormHelperText, IconButton, Typography } from '@elementor/ui';
8
+ import { Button, CardActions, Divider, FormHelperText, IconButton, Tooltip, Typography } from '@elementor/ui';
9
9
  import { __ } from '@wordpress/i18n';
10
10
 
11
11
  import { useVariableType } from '../context/variable-type-context';
@@ -20,6 +20,7 @@ import { EDIT_CONFIRMATION_DIALOG_ID, EditConfirmationDialog } from './ui/edit-c
20
20
  import { FormField } from './ui/form-field';
21
21
 
22
22
  const SIZE = 'tiny';
23
+ const DELETE_LABEL = __( 'Delete variable', 'elementor' );
23
24
 
24
25
  type Props = {
25
26
  editId: string;
@@ -127,14 +128,11 @@ export const VariableEdit = ( { onClose, onGoBack, onSubmit, editId }: Props ) =
127
128
 
128
129
  if ( userPermissions.canDelete() ) {
129
130
  actions.push(
130
- <IconButton
131
- key="delete"
132
- size={ SIZE }
133
- aria-label={ __( 'Delete', 'elementor' ) }
134
- onClick={ handleDeleteConfirmation }
135
- >
136
- <TrashIcon fontSize={ SIZE } />
137
- </IconButton>
131
+ <Tooltip key="delete" placement="top" title={ DELETE_LABEL }>
132
+ <IconButton size={ SIZE } onClick={ handleDeleteConfirmation } aria-label={ DELETE_LABEL }>
133
+ <TrashIcon fontSize={ SIZE } />
134
+ </IconButton>
135
+ </Tooltip>
138
136
  );
139
137
  }
140
138
 
@@ -0,0 +1,49 @@
1
+ import { useCallback, useRef } from 'react';
2
+
3
+ export interface UseErrorNavigationReturn {
4
+ createNavigationCallback: (
5
+ ids: string[],
6
+ onNavigate: ( id: string ) => void,
7
+ onComplete: () => void
8
+ ) => () => void;
9
+ resetNavigation: () => void;
10
+ }
11
+
12
+ export const useErrorNavigation = (): UseErrorNavigationReturn => {
13
+ const currentIndexRef = useRef( 0 );
14
+
15
+ const createNavigationCallback = useCallback(
16
+ ( ids: string[], onNavigate: ( id: string ) => void, onComplete: () => void ) => {
17
+ return () => {
18
+ if ( ! ids?.length ) {
19
+ return;
20
+ }
21
+
22
+ const currentIndex = currentIndexRef.current;
23
+ const currentId = ids[ currentIndex ];
24
+
25
+ if ( currentId ) {
26
+ onNavigate( currentId );
27
+
28
+ const nextIndex = currentIndex + 1;
29
+ if ( nextIndex >= ids.length ) {
30
+ onComplete();
31
+ currentIndexRef.current = 0;
32
+ } else {
33
+ currentIndexRef.current = nextIndex;
34
+ }
35
+ }
36
+ };
37
+ },
38
+ []
39
+ );
40
+
41
+ const resetNavigation = useCallback( () => {
42
+ currentIndexRef.current = 0;
43
+ }, [] );
44
+
45
+ return {
46
+ createNavigationCallback,
47
+ resetNavigation,
48
+ };
49
+ };
@@ -1,18 +1,16 @@
1
1
  import { useCallback, useState } from 'react';
2
- import { __ } from '@wordpress/i18n';
3
2
 
4
3
  import { generateTempId } from '../../../batch-operations';
5
4
  import { getVariables } from '../../../hooks/use-prop-variables';
6
5
  import { service } from '../../../service';
7
6
  import { type TVariablesList } from '../../../storage';
8
7
  import { filterBySearch } from '../../../utils/filter-by-search';
9
- import { ERROR_MESSAGES } from '../../../utils/validations';
10
8
 
11
9
  export const useVariablesManagerState = () => {
12
10
  const [ variables, setVariables ] = useState( () => getVariables( false ) );
13
11
  const [ deletedVariables, setDeletedVariables ] = useState< string[] >( [] );
12
+ const [ isSaveDisabled, setIsSaveDisabled ] = useState( false );
14
13
  const [ isDirty, setIsDirty ] = useState( false );
15
- const [ hasValidationErrors, setHasValidationErrors ] = useState( false );
16
14
  const [ isSaving, setIsSaving ] = useState( false );
17
15
  const [ searchValue, setSearchValue ] = useState( '' );
18
16
 
@@ -49,28 +47,21 @@ export const useVariablesManagerState = () => {
49
47
  setSearchValue( searchTerm );
50
48
  };
51
49
 
52
- const handleSave = useCallback( async (): Promise< { success: boolean; error?: string } > => {
53
- try {
54
- const originalVariables = getVariables( false );
55
- setIsSaving( true );
56
- const result = await service.batchSave( originalVariables, variables );
50
+ const handleSave = useCallback( async (): Promise< { success: boolean } > => {
51
+ const originalVariables = getVariables( false );
52
+ setIsSaving( true );
53
+ const result = await service.batchSave( originalVariables, variables );
57
54
 
58
- if ( result.success ) {
59
- await service.load();
60
- const updatedVariables = service.variables();
55
+ if ( result.success ) {
56
+ await service.load();
57
+ const updatedVariables = service.variables();
61
58
 
62
- setVariables( updatedVariables );
63
- setDeletedVariables( [] );
64
- setIsDirty( false );
65
- setIsSaving( false );
66
- return { success: true };
67
- }
68
- throw new Error( __( 'Failed to save variables. Please try again.', 'elementor' ) );
69
- } catch ( error ) {
70
- const errorMessage = error instanceof Error ? error.message : ERROR_MESSAGES.UNEXPECTED_ERROR;
71
- setIsSaving( false );
72
- return { success: false, error: errorMessage };
59
+ setVariables( updatedVariables );
60
+ setDeletedVariables( [] );
61
+ setIsDirty( false );
73
62
  }
63
+
64
+ return { success: result.success };
74
65
  }, [ variables ] );
75
66
 
76
67
  const filteredVariables = () => {
@@ -84,7 +75,7 @@ export const useVariablesManagerState = () => {
84
75
  variables: filteredVariables(),
85
76
  deletedVariables,
86
77
  isDirty,
87
- hasValidationErrors,
78
+ isSaveDisabled,
88
79
  handleOnChange,
89
80
  createVariable,
90
81
  handleDeleteVariable,
@@ -92,6 +83,7 @@ export const useVariablesManagerState = () => {
92
83
  isSaving,
93
84
  handleSearch,
94
85
  searchValue,
95
- setHasValidationErrors,
86
+ setIsSaving,
87
+ setIsSaveDisabled,
96
88
  };
97
89
  };
@@ -10,14 +10,26 @@ import {
10
10
  } from '@elementor/editor-panels';
11
11
  import { SaveChangesDialog, SearchField, ThemeProvider, useDialog } from '@elementor/editor-ui';
12
12
  import { changeEditMode } from '@elementor/editor-v1-adapters';
13
- import { ColorFilterIcon, TrashIcon } from '@elementor/icons';
14
- import { Alert, Box, Button, CloseButton, Divider, ErrorBoundary, Stack, usePopupState } from '@elementor/ui';
13
+ import { AlertTriangleFilledIcon, ColorFilterIcon, TrashIcon } from '@elementor/icons';
14
+ import {
15
+ Alert,
16
+ AlertAction,
17
+ AlertTitle,
18
+ Button,
19
+ CloseButton,
20
+ Divider,
21
+ Infotip,
22
+ Stack,
23
+ usePopupState,
24
+ } from '@elementor/ui';
15
25
  import { __ } from '@wordpress/i18n';
16
26
 
27
+ import { type ErrorResponse, type MappedError, mapServerError } from '../../utils/validations';
17
28
  import { DeleteConfirmationDialog } from '../ui/delete-confirmation-dialog';
18
29
  import { EmptyState } from '../ui/empty-state';
19
30
  import { NoSearchResults } from '../ui/no-search-results';
20
31
  import { useAutoEdit } from './hooks/use-auto-edit';
32
+ import { useErrorNavigation } from './hooks/use-error-navigation';
21
33
  import { useVariablesManagerState } from './hooks/use-variables-manager-state';
22
34
  import { SIZE, VariableManagerCreateMenu } from './variables-manager-create-menu';
23
35
  import { VariablesManagerTable } from './variables-manager-table';
@@ -47,20 +59,23 @@ export function VariablesManagerPanel() {
47
59
  const {
48
60
  variables,
49
61
  isDirty,
50
- hasValidationErrors,
51
62
  searchValue,
63
+ isSaveDisabled,
52
64
  handleOnChange,
53
65
  createVariable,
54
66
  handleDeleteVariable,
55
67
  handleSave,
56
68
  isSaving,
57
69
  handleSearch,
58
- setHasValidationErrors,
70
+ setIsSaving,
71
+ setIsSaveDisabled,
59
72
  } = useVariablesManagerState();
60
73
 
61
74
  const { autoEditVariableId, startAutoEdit, handleAutoEditComplete } = useAutoEdit();
75
+ const { createNavigationCallback, resetNavigation } = useErrorNavigation();
62
76
 
63
77
  const [ deleteConfirmation, setDeleteConfirmation ] = useState< { id: string; label: string } | null >( null );
78
+ const [ serverError, setServerError ] = useState< MappedError | null >( null );
64
79
 
65
80
  usePreventUnload( isDirty );
66
81
 
@@ -83,6 +98,32 @@ export function VariablesManagerPanel() {
83
98
  [ createVariable, startAutoEdit ]
84
99
  );
85
100
 
101
+ const handleSaveClick = async () => {
102
+ try {
103
+ setServerError( null );
104
+ resetNavigation();
105
+
106
+ return await handleSave();
107
+ } catch ( error ) {
108
+ const mappedError = mapServerError( error as ErrorResponse );
109
+ const duplicatedIds = mappedError?.action?.data?.duplicatedIds;
110
+
111
+ if ( mappedError && 'label' === mappedError.field ) {
112
+ if ( duplicatedIds && mappedError.action ) {
113
+ mappedError.action.callback = createNavigationCallback( duplicatedIds, startAutoEdit, () => {
114
+ setIsSaveDisabled( false );
115
+ } );
116
+ }
117
+ setServerError( mappedError );
118
+ setIsSaveDisabled( true );
119
+ resetNavigation();
120
+ }
121
+ return { success: false, error: mappedError };
122
+ } finally {
123
+ setIsSaving( false );
124
+ }
125
+ };
126
+
86
127
  const handleDeleteVariableWithConfirmation = useCallback(
87
128
  ( itemId: string ) => {
88
129
  handleDeleteVariable( itemId );
@@ -108,113 +149,145 @@ export function VariablesManagerPanel() {
108
149
 
109
150
  return (
110
151
  <ThemeProvider>
111
- <ErrorBoundary fallback={ <ErrorBoundaryFallback /> }>
112
- <Panel>
113
- <PanelHeader
114
- sx={ {
115
- height: 'unset',
116
- } }
117
- >
118
- <Stack width="100%" direction="column" alignItems="center">
119
- <Stack p={ 1 } pl={ 2 } width="100%" direction="row" alignItems="center">
120
- <Stack width="100%" direction="row" gap={ 1 }>
121
- <PanelHeaderTitle sx={ { display: 'flex', alignItems: 'center', gap: 0.5 } }>
122
- <ColorFilterIcon fontSize="inherit" />
123
- { __( 'Variable Manager', 'elementor' ) }
124
- </PanelHeaderTitle>
125
- </Stack>
126
- <Stack direction="row" gap={ 0.5 } alignItems="center">
127
- <VariableManagerCreateMenu
128
- onCreate={ handleCreateVariable }
129
- variables={ variables }
130
- menuState={ createMenuState }
131
- />
132
- <CloseButton
133
- aria-label="Close"
134
- slotProps={ { icon: { fontSize: SIZE } } }
135
- onClick={ () => {
136
- handleClosePanel();
137
- } }
138
- />
139
- </Stack>
140
- </Stack>
152
+ <Panel>
153
+ <PanelHeader
154
+ sx={ {
155
+ height: 'unset',
156
+ } }
157
+ >
158
+ <Stack width="100%" direction="column" alignItems="center">
159
+ <Stack p={ 1 } pl={ 2 } width="100%" direction="row" alignItems="center">
141
160
  <Stack width="100%" direction="row" gap={ 1 }>
142
- <SearchField
143
- sx={ {
144
- display: 'flex',
145
- flex: 1,
161
+ <PanelHeaderTitle sx={ { display: 'flex', alignItems: 'center', gap: 0.5 } }>
162
+ <ColorFilterIcon fontSize="inherit" />
163
+ { __( 'Variable Manager', 'elementor' ) }
164
+ </PanelHeaderTitle>
165
+ </Stack>
166
+ <Stack direction="row" gap={ 0.5 } alignItems="center">
167
+ <VariableManagerCreateMenu
168
+ onCreate={ handleCreateVariable }
169
+ variables={ variables }
170
+ menuState={ createMenuState }
171
+ />
172
+ <CloseButton
173
+ aria-label="Close"
174
+ slotProps={ { icon: { fontSize: SIZE } } }
175
+ onClick={ () => {
176
+ handleClosePanel();
146
177
  } }
147
- placeholder={ __( 'Search', 'elementor' ) }
148
- value={ searchValue }
149
- onSearch={ handleSearch }
150
178
  />
151
179
  </Stack>
152
- <Divider sx={ { width: '100%' } } />
153
180
  </Stack>
154
- </PanelHeader>
155
- <PanelBody
156
- sx={ {
157
- display: 'flex',
158
- flexDirection: 'column',
159
- height: '100%',
160
- } }
161
- >
162
- { hasVariables && (
163
- <VariablesManagerTable
164
- menuActions={ menuActions }
165
- variables={ variables }
166
- onChange={ handleOnChange }
167
- autoEditVariableId={ autoEditVariableId }
168
- onAutoEditComplete={ handleAutoEditComplete }
169
- onFieldError={ setHasValidationErrors }
181
+ <Stack width="100%" direction="row" gap={ 1 }>
182
+ <SearchField
183
+ sx={ {
184
+ display: 'flex',
185
+ flex: 1,
186
+ } }
187
+ placeholder={ __( 'Search', 'elementor' ) }
188
+ value={ searchValue }
189
+ onSearch={ handleSearch }
170
190
  />
171
- ) }
191
+ </Stack>
192
+ <Divider sx={ { width: '100%' } } />
193
+ </Stack>
194
+ </PanelHeader>
195
+ <PanelBody
196
+ sx={ {
197
+ display: 'flex',
198
+ flexDirection: 'column',
199
+ height: '100%',
200
+ } }
201
+ >
202
+ { hasVariables && (
203
+ <VariablesManagerTable
204
+ menuActions={ menuActions }
205
+ variables={ variables }
206
+ onChange={ handleOnChange }
207
+ autoEditVariableId={ autoEditVariableId }
208
+ onAutoEditComplete={ handleAutoEditComplete }
209
+ onFieldError={ setIsSaveDisabled }
210
+ />
211
+ ) }
172
212
 
173
- { ! hasVariables && searchValue && (
174
- <NoSearchResults
175
- searchValue={ searchValue }
176
- onClear={ () => handleSearch( '' ) }
177
- icon={ <ColorFilterIcon fontSize="large" /> }
178
- />
179
- ) }
213
+ { ! hasVariables && searchValue && (
214
+ <NoSearchResults
215
+ searchValue={ searchValue }
216
+ onClear={ () => handleSearch( '' ) }
217
+ icon={ <ColorFilterIcon fontSize="large" /> }
218
+ />
219
+ ) }
180
220
 
181
- { ! hasVariables && ! searchValue && (
182
- <EmptyState
183
- title={ __( 'Create your first variable', 'elementor' ) }
184
- message={ __(
185
- 'Variables are saved attributes that you can apply anywhere on your site.',
186
- 'elementor'
187
- ) }
188
- icon={ <ColorFilterIcon fontSize="large" /> }
189
- onAdd={ createMenuState.open }
190
- />
191
- ) }
192
- </PanelBody>
221
+ { ! hasVariables && ! searchValue && (
222
+ <EmptyState
223
+ title={ __( 'Create your first variable', 'elementor' ) }
224
+ message={ __(
225
+ 'Variables are saved attributes that you can apply anywhere on your site.',
226
+ 'elementor'
227
+ ) }
228
+ icon={ <ColorFilterIcon fontSize="large" /> }
229
+ onAdd={ createMenuState.open }
230
+ />
231
+ ) }
232
+ </PanelBody>
193
233
 
194
- <PanelFooter>
234
+ <PanelFooter>
235
+ <Infotip
236
+ placement="right"
237
+ open={ !! serverError }
238
+ content={
239
+ serverError ? (
240
+ <Alert
241
+ severity="error"
242
+ action={
243
+ serverError.action ? (
244
+ <AlertAction onClick={ serverError.action.callback }>
245
+ { serverError.action.label }
246
+ </AlertAction>
247
+ ) : undefined
248
+ }
249
+ icon={ <AlertTriangleFilledIcon /> }
250
+ >
251
+ <AlertTitle>{ serverError.message }</AlertTitle>
252
+ { serverError.action?.message }
253
+ </Alert>
254
+ ) : null
255
+ }
256
+ arrow={ false }
257
+ slotProps={ {
258
+ popper: {
259
+ modifiers: [
260
+ {
261
+ name: 'offset',
262
+ options: { offset: [ -10, 10 ] },
263
+ },
264
+ ],
265
+ },
266
+ } }
267
+ >
195
268
  <Button
196
269
  fullWidth
197
270
  size="small"
198
271
  color="global"
199
272
  variant="contained"
200
- disabled={ ! isDirty || hasValidationErrors || isSaving }
201
- onClick={ handleSave }
273
+ disabled={ isSaveDisabled || ! isDirty || isSaving }
274
+ onClick={ handleSaveClick }
202
275
  loading={ isSaving }
203
276
  >
204
277
  { __( 'Save changes', 'elementor' ) }
205
278
  </Button>
206
- </PanelFooter>
207
- </Panel>
279
+ </Infotip>
280
+ </PanelFooter>
281
+ </Panel>
208
282
 
209
- { deleteConfirmation && (
210
- <DeleteConfirmationDialog
211
- open
212
- label={ deleteConfirmation.label }
213
- onConfirm={ () => handleDeleteVariableWithConfirmation( deleteConfirmation.id ) }
214
- closeDialog={ () => setDeleteConfirmation( null ) }
215
- />
216
- ) }
217
- </ErrorBoundary>
283
+ { deleteConfirmation && (
284
+ <DeleteConfirmationDialog
285
+ open
286
+ label={ deleteConfirmation.label }
287
+ onConfirm={ () => handleDeleteVariableWithConfirmation( deleteConfirmation.id ) }
288
+ closeDialog={ () => setDeleteConfirmation( null ) }
289
+ />
290
+ ) }
218
291
 
219
292
  { isSaveChangesDialogOpen && (
220
293
  <SaveChangesDialog>
@@ -238,9 +311,11 @@ export function VariablesManagerPanel() {
238
311
  confirm: {
239
312
  label: __( 'Save', 'elementor' ),
240
313
  action: async () => {
241
- await handleSave();
314
+ const result = await handleSaveClick();
242
315
  closeSaveChangesDialog();
243
- closePanel();
316
+ if ( result?.success ) {
317
+ closePanel();
318
+ }
244
319
  },
245
320
  },
246
321
  } }
@@ -251,14 +326,6 @@ export function VariablesManagerPanel() {
251
326
  );
252
327
  }
253
328
 
254
- const ErrorBoundaryFallback = () => (
255
- <Box role="alert" sx={ { minHeight: '100%', p: 2 } }>
256
- <Alert severity="error" sx={ { mb: 2, maxWidth: 400, textAlign: 'center' } }>
257
- <strong>{ __( 'Something went wrong', 'elementor' ) }</strong>
258
- </Alert>
259
- </Box>
260
- );
261
-
262
329
  const usePreventUnload = ( isDirty: boolean ) => {
263
330
  useEffect( () => {
264
331
  const handleBeforeUnload = ( event: BeforeUnloadEvent ) => {