@elementor/editor-variables 3.33.0-98 → 3.34.2
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/dist/index.d.mts +11 -3
- package/dist/index.d.ts +11 -3
- package/dist/index.js +1874 -801
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1819 -737
- package/dist/index.mjs.map +1 -1
- package/package.json +16 -14
- package/src/api.ts +24 -0
- package/src/batch-operations.ts +86 -0
- package/src/components/fields/color-field.tsx +1 -0
- package/src/components/fields/font-field.tsx +2 -1
- package/src/components/fields/label-field.tsx +42 -6
- package/src/components/ui/deleted-variable-alert.tsx +14 -10
- package/src/components/ui/{no-variables.tsx → empty-state.tsx} +8 -13
- package/src/components/ui/menu-item-content.tsx +14 -11
- package/src/components/ui/mismatch-variable-alert.tsx +5 -9
- package/src/components/ui/missing-variable-alert.tsx +8 -9
- package/src/components/ui/no-search-results.tsx +1 -2
- package/src/components/ui/tags/assigned-tag.tsx +6 -3
- package/src/components/ui/tags/warning-variable-tag.tsx +44 -0
- package/src/components/ui/variable/deleted-variable.tsx +13 -6
- package/src/components/ui/variable/mismatch-variable.tsx +11 -4
- package/src/components/ui/variable/missing-variable.tsx +2 -2
- package/src/components/variable-creation.tsx +10 -3
- package/src/components/variable-edit.tsx +11 -12
- package/src/components/variable-restore.tsx +3 -2
- package/src/components/variables-manager/hooks/use-auto-edit.ts +21 -0
- package/src/components/variables-manager/hooks/use-error-navigation.ts +49 -0
- package/src/components/variables-manager/hooks/use-variables-manager-state.ts +89 -0
- package/src/components/variables-manager/variable-editable-cell.tsx +131 -67
- package/src/components/variables-manager/variables-manager-create-menu.tsx +116 -0
- package/src/components/variables-manager/variables-manager-panel.tsx +290 -59
- package/src/components/variables-manager/variables-manager-table.tsx +111 -14
- package/src/components/variables-selection.tsx +61 -15
- package/src/controls/variable-control.tsx +1 -1
- package/src/hooks/use-prop-variables.ts +11 -8
- package/src/hooks/use-variable-bound-prop.ts +42 -0
- package/src/index.ts +1 -0
- package/src/init.ts +19 -6
- package/src/mcp/create-variable-tool.ts +70 -0
- package/src/mcp/delete-variable-tool.ts +50 -0
- package/src/mcp/index.ts +17 -0
- package/src/mcp/list-variables-tool.ts +58 -0
- package/src/mcp/update-variable-tool.ts +81 -0
- package/src/mcp/variables-resource.ts +28 -0
- package/src/register-variable-types.tsx +2 -0
- package/src/service.ts +60 -1
- package/src/storage.ts +8 -0
- package/src/types.ts +1 -0
- package/src/utils/filter-by-search.ts +5 -0
- package/src/utils/tracking.ts +37 -22
- package/src/utils/validations.ts +72 -3
- package/src/variables-registry/create-variable-type-registry.ts +10 -1
- package/src/variables-registry/variable-type-registry.ts +2 -1
- package/src/components/ui/tags/deleted-tag.tsx +0 -37
- package/src/components/ui/tags/mismatch-tag.tsx +0 -37
- package/src/components/ui/tags/missing-tag.tsx +0 -25
- /package/src/components/variables-manager/{variable-edit-menu.tsx → ui/variable-edit-menu.tsx} +0 -0
- /package/src/components/variables-manager/{variable-table-cell.tsx → ui/variable-table-cell.tsx} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { useEffect, useState } from 'react';
|
|
2
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
3
3
|
import {
|
|
4
4
|
__createPanel as createPanel,
|
|
5
5
|
Panel,
|
|
@@ -8,14 +8,32 @@ import {
|
|
|
8
8
|
PanelHeader,
|
|
9
9
|
PanelHeaderTitle,
|
|
10
10
|
} from '@elementor/editor-panels';
|
|
11
|
-
import { ThemeProvider } from '@elementor/editor-ui';
|
|
11
|
+
import { SaveChangesDialog, SearchField, ThemeProvider, useDialog } from '@elementor/editor-ui';
|
|
12
12
|
import { changeEditMode } from '@elementor/editor-v1-adapters';
|
|
13
|
-
import { ColorFilterIcon, TrashIcon
|
|
14
|
-
import {
|
|
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
|
|
|
17
|
-
import {
|
|
18
|
-
import { type
|
|
27
|
+
import { trackVariablesManagerEvent } from '../../utils/tracking';
|
|
28
|
+
import { type ErrorResponse, type MappedError, mapServerError } from '../../utils/validations';
|
|
29
|
+
import { getVariableType } from '../../variables-registry/variable-type-registry';
|
|
30
|
+
import { DeleteConfirmationDialog } from '../ui/delete-confirmation-dialog';
|
|
31
|
+
import { EmptyState } from '../ui/empty-state';
|
|
32
|
+
import { NoSearchResults } from '../ui/no-search-results';
|
|
33
|
+
import { useAutoEdit } from './hooks/use-auto-edit';
|
|
34
|
+
import { useErrorNavigation } from './hooks/use-error-navigation';
|
|
35
|
+
import { useVariablesManagerState } from './hooks/use-variables-manager-state';
|
|
36
|
+
import { SIZE, VariableManagerCreateMenu } from './variables-manager-create-menu';
|
|
19
37
|
import { VariablesManagerTable } from './variables-manager-table';
|
|
20
38
|
|
|
21
39
|
const id = 'variables-manager';
|
|
@@ -30,96 +48,309 @@ export const { panel, usePanelActions } = createPanel( {
|
|
|
30
48
|
onClose: () => {
|
|
31
49
|
changeEditMode( 'edit' );
|
|
32
50
|
},
|
|
51
|
+
isOpenPreviousElement: true,
|
|
33
52
|
} );
|
|
34
53
|
|
|
35
54
|
export function VariablesManagerPanel() {
|
|
36
55
|
const { close: closePanel } = usePanelActions();
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
const
|
|
56
|
+
const { open: openSaveChangesDialog, close: closeSaveChangesDialog, isOpen: isSaveChangesDialogOpen } = useDialog();
|
|
57
|
+
|
|
58
|
+
const createMenuState = usePopupState( {
|
|
59
|
+
variant: 'popover',
|
|
60
|
+
} );
|
|
61
|
+
|
|
62
|
+
const {
|
|
63
|
+
variables,
|
|
64
|
+
isDirty,
|
|
65
|
+
searchValue,
|
|
66
|
+
isSaveDisabled,
|
|
67
|
+
handleOnChange,
|
|
68
|
+
createVariable,
|
|
69
|
+
handleDeleteVariable,
|
|
70
|
+
handleSave,
|
|
71
|
+
isSaving,
|
|
72
|
+
handleSearch,
|
|
73
|
+
setIsSaving,
|
|
74
|
+
setIsSaveDisabled,
|
|
75
|
+
} = useVariablesManagerState();
|
|
76
|
+
|
|
77
|
+
const { autoEditVariableId, startAutoEdit, handleAutoEditComplete } = useAutoEdit();
|
|
78
|
+
const { createNavigationCallback, resetNavigation } = useErrorNavigation();
|
|
79
|
+
|
|
80
|
+
const [ deleteConfirmation, setDeleteConfirmation ] = useState< { id: string; label: string } | null >( null );
|
|
81
|
+
const [ serverError, setServerError ] = useState< MappedError | null >( null );
|
|
40
82
|
|
|
41
83
|
usePreventUnload( isDirty );
|
|
42
84
|
|
|
85
|
+
const handleClosePanel = () => {
|
|
86
|
+
if ( isDirty ) {
|
|
87
|
+
openSaveChangesDialog();
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
closePanel();
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const handleCreateVariable = useCallback(
|
|
95
|
+
( type: string, defaultName: string, defaultValue: string ) => {
|
|
96
|
+
const newId = createVariable( type, defaultName, defaultValue );
|
|
97
|
+
if ( newId ) {
|
|
98
|
+
startAutoEdit( newId );
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
[ createVariable, startAutoEdit ]
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
const handleSaveClick = async () => {
|
|
105
|
+
try {
|
|
106
|
+
setServerError( null );
|
|
107
|
+
resetNavigation();
|
|
108
|
+
|
|
109
|
+
const result = await handleSave();
|
|
110
|
+
trackVariablesManagerEvent( { action: 'saveChanges' } );
|
|
111
|
+
return result;
|
|
112
|
+
} catch ( error ) {
|
|
113
|
+
const mappedError = mapServerError( error as ErrorResponse );
|
|
114
|
+
const duplicatedIds = mappedError?.action?.data?.duplicatedIds;
|
|
115
|
+
|
|
116
|
+
if ( mappedError && 'label' === mappedError.field ) {
|
|
117
|
+
if ( duplicatedIds && mappedError.action ) {
|
|
118
|
+
mappedError.action.callback = createNavigationCallback( duplicatedIds, startAutoEdit, () => {
|
|
119
|
+
setIsSaveDisabled( false );
|
|
120
|
+
} );
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
setServerError( mappedError );
|
|
124
|
+
setIsSaveDisabled( true );
|
|
125
|
+
resetNavigation();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return { success: false, error: mappedError };
|
|
129
|
+
} finally {
|
|
130
|
+
setIsSaving( false );
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const handleDeleteVariableWithConfirmation = useCallback(
|
|
135
|
+
( itemId: string ) => {
|
|
136
|
+
handleDeleteVariable( itemId );
|
|
137
|
+
setDeleteConfirmation( null );
|
|
138
|
+
},
|
|
139
|
+
[ handleDeleteVariable ]
|
|
140
|
+
);
|
|
141
|
+
|
|
43
142
|
const menuActions = [
|
|
44
143
|
{
|
|
45
144
|
name: __( 'Delete', 'elementor' ),
|
|
46
145
|
icon: TrashIcon,
|
|
47
146
|
color: 'error.main',
|
|
48
147
|
onClick: ( itemId: string ) => {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
148
|
+
const variable = variables[ itemId ];
|
|
149
|
+
if ( variable ) {
|
|
150
|
+
setDeleteConfirmation( { id: itemId, label: variable.label } );
|
|
151
|
+
|
|
152
|
+
const variableTypeOptions = getVariableType( variable.type );
|
|
153
|
+
trackVariablesManagerEvent( { action: 'delete', varType: variableTypeOptions?.variableType } );
|
|
154
|
+
}
|
|
52
155
|
},
|
|
53
156
|
},
|
|
54
157
|
];
|
|
55
158
|
|
|
56
|
-
const
|
|
57
|
-
setVariables( newVariables );
|
|
58
|
-
setIsDirty( true );
|
|
59
|
-
};
|
|
159
|
+
const hasVariables = Object.values( variables ).some( ( variable ) => ! variable.deleted );
|
|
60
160
|
|
|
61
161
|
return (
|
|
62
162
|
<ThemeProvider>
|
|
63
|
-
<
|
|
64
|
-
<
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
163
|
+
<Panel>
|
|
164
|
+
<PanelHeader
|
|
165
|
+
sx={ {
|
|
166
|
+
height: 'unset',
|
|
167
|
+
} }
|
|
168
|
+
>
|
|
169
|
+
<Stack width="100%" direction="column" alignItems="center">
|
|
170
|
+
<Stack p={ 1 } pl={ 2 } width="100%" direction="row" alignItems="center">
|
|
171
|
+
<Stack width="100%" direction="row" gap={ 1 }>
|
|
172
|
+
<PanelHeaderTitle sx={ { display: 'flex', alignItems: 'center', gap: 0.5 } }>
|
|
173
|
+
<ColorFilterIcon fontSize="inherit" />
|
|
174
|
+
{ __( 'Variables Manager', 'elementor' ) }
|
|
175
|
+
</PanelHeaderTitle>
|
|
176
|
+
</Stack>
|
|
177
|
+
<Stack direction="row" gap={ 0.5 } alignItems="center">
|
|
178
|
+
<VariableManagerCreateMenu
|
|
179
|
+
onCreate={ handleCreateVariable }
|
|
180
|
+
variables={ variables }
|
|
181
|
+
menuState={ createMenuState }
|
|
182
|
+
/>
|
|
74
183
|
<CloseButton
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
184
|
+
aria-label="Close"
|
|
185
|
+
slotProps={ { icon: { fontSize: SIZE } } }
|
|
186
|
+
onClick={ () => {
|
|
187
|
+
handleClosePanel();
|
|
78
188
|
} }
|
|
79
189
|
/>
|
|
80
190
|
</Stack>
|
|
81
|
-
<Divider sx={ { width: '100%' } } />
|
|
82
191
|
</Stack>
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
192
|
+
<Stack width="100%" direction="row" gap={ 1 }>
|
|
193
|
+
<SearchField
|
|
194
|
+
sx={ {
|
|
195
|
+
display: 'flex',
|
|
196
|
+
flex: 1,
|
|
197
|
+
} }
|
|
198
|
+
placeholder={ __( 'Search', 'elementor' ) }
|
|
199
|
+
value={ searchValue }
|
|
200
|
+
onSearch={ handleSearch }
|
|
201
|
+
/>
|
|
202
|
+
</Stack>
|
|
203
|
+
<Divider sx={ { width: '100%' } } />
|
|
204
|
+
</Stack>
|
|
205
|
+
</PanelHeader>
|
|
206
|
+
<PanelBody
|
|
207
|
+
sx={ {
|
|
208
|
+
display: 'flex',
|
|
209
|
+
flexDirection: 'column',
|
|
210
|
+
height: '100%',
|
|
211
|
+
} }
|
|
212
|
+
>
|
|
213
|
+
{ hasVariables && (
|
|
91
214
|
<VariablesManagerTable
|
|
92
215
|
menuActions={ menuActions }
|
|
93
216
|
variables={ variables }
|
|
94
217
|
onChange={ handleOnChange }
|
|
218
|
+
autoEditVariableId={ autoEditVariableId }
|
|
219
|
+
onAutoEditComplete={ handleAutoEditComplete }
|
|
220
|
+
onFieldError={ setIsSaveDisabled }
|
|
221
|
+
/>
|
|
222
|
+
) }
|
|
223
|
+
|
|
224
|
+
{ ! hasVariables && searchValue && (
|
|
225
|
+
<NoSearchResults
|
|
226
|
+
searchValue={ searchValue }
|
|
227
|
+
onClear={ () => handleSearch( '' ) }
|
|
228
|
+
icon={ <ColorFilterIcon fontSize="large" /> }
|
|
229
|
+
/>
|
|
230
|
+
) }
|
|
231
|
+
|
|
232
|
+
{ ! hasVariables && ! searchValue && (
|
|
233
|
+
<EmptyState
|
|
234
|
+
title={ __( 'Create your first variable', 'elementor' ) }
|
|
235
|
+
message={ __(
|
|
236
|
+
'Variables are saved attributes that you can apply anywhere on your site.',
|
|
237
|
+
'elementor'
|
|
238
|
+
) }
|
|
239
|
+
icon={ <ColorFilterIcon fontSize="large" /> }
|
|
240
|
+
onAdd={ createMenuState.open }
|
|
95
241
|
/>
|
|
96
|
-
|
|
242
|
+
) }
|
|
243
|
+
</PanelBody>
|
|
97
244
|
|
|
98
|
-
|
|
99
|
-
|
|
245
|
+
<PanelFooter>
|
|
246
|
+
<Infotip
|
|
247
|
+
placement="right"
|
|
248
|
+
open={ !! serverError }
|
|
249
|
+
content={
|
|
250
|
+
serverError ? (
|
|
251
|
+
<Alert
|
|
252
|
+
severity={ serverError.severity ?? 'error' }
|
|
253
|
+
action={
|
|
254
|
+
serverError.action?.label ? (
|
|
255
|
+
<AlertAction onClick={ serverError.action.callback }>
|
|
256
|
+
{ serverError.action.label }
|
|
257
|
+
</AlertAction>
|
|
258
|
+
) : undefined
|
|
259
|
+
}
|
|
260
|
+
onClose={
|
|
261
|
+
! serverError.action?.label
|
|
262
|
+
? () => {
|
|
263
|
+
setServerError( null );
|
|
264
|
+
setIsSaveDisabled( false );
|
|
265
|
+
}
|
|
266
|
+
: undefined
|
|
267
|
+
}
|
|
268
|
+
icon={
|
|
269
|
+
serverError.IconComponent ? (
|
|
270
|
+
<serverError.IconComponent />
|
|
271
|
+
) : (
|
|
272
|
+
<AlertTriangleFilledIcon />
|
|
273
|
+
)
|
|
274
|
+
}
|
|
275
|
+
>
|
|
276
|
+
<AlertTitle>{ serverError.message }</AlertTitle>
|
|
277
|
+
{ serverError.action?.message }
|
|
278
|
+
</Alert>
|
|
279
|
+
) : null
|
|
280
|
+
}
|
|
281
|
+
arrow={ false }
|
|
282
|
+
slotProps={ {
|
|
283
|
+
popper: {
|
|
284
|
+
modifiers: [
|
|
285
|
+
{
|
|
286
|
+
name: 'offset',
|
|
287
|
+
options: { offset: [ -10, 10 ] },
|
|
288
|
+
},
|
|
289
|
+
],
|
|
290
|
+
},
|
|
291
|
+
} }
|
|
292
|
+
>
|
|
293
|
+
<Button
|
|
294
|
+
fullWidth
|
|
295
|
+
size="small"
|
|
296
|
+
color="global"
|
|
297
|
+
variant="contained"
|
|
298
|
+
disabled={ isSaveDisabled || ! isDirty || isSaving }
|
|
299
|
+
onClick={ handleSaveClick }
|
|
300
|
+
loading={ isSaving }
|
|
301
|
+
>
|
|
100
302
|
{ __( 'Save changes', 'elementor' ) }
|
|
101
303
|
</Button>
|
|
102
|
-
</
|
|
103
|
-
</
|
|
104
|
-
</
|
|
304
|
+
</Infotip>
|
|
305
|
+
</PanelFooter>
|
|
306
|
+
</Panel>
|
|
307
|
+
|
|
308
|
+
{ deleteConfirmation && (
|
|
309
|
+
<DeleteConfirmationDialog
|
|
310
|
+
open
|
|
311
|
+
label={ deleteConfirmation.label }
|
|
312
|
+
onConfirm={ () => handleDeleteVariableWithConfirmation( deleteConfirmation.id ) }
|
|
313
|
+
closeDialog={ () => setDeleteConfirmation( null ) }
|
|
314
|
+
/>
|
|
315
|
+
) }
|
|
316
|
+
|
|
317
|
+
{ isSaveChangesDialogOpen && (
|
|
318
|
+
<SaveChangesDialog>
|
|
319
|
+
<SaveChangesDialog.Title onClose={ closeSaveChangesDialog }>
|
|
320
|
+
{ __( 'You have unsaved changes', 'elementor' ) }
|
|
321
|
+
</SaveChangesDialog.Title>
|
|
322
|
+
<SaveChangesDialog.Content>
|
|
323
|
+
<SaveChangesDialog.ContentText>
|
|
324
|
+
{ __( 'To avoid losing your updates, save your changes before leaving.', 'elementor' ) }
|
|
325
|
+
</SaveChangesDialog.ContentText>
|
|
326
|
+
</SaveChangesDialog.Content>
|
|
327
|
+
<SaveChangesDialog.Actions
|
|
328
|
+
actions={ {
|
|
329
|
+
discard: {
|
|
330
|
+
label: __( 'Discard', 'elementor' ),
|
|
331
|
+
action: () => {
|
|
332
|
+
closeSaveChangesDialog();
|
|
333
|
+
closePanel();
|
|
334
|
+
},
|
|
335
|
+
},
|
|
336
|
+
confirm: {
|
|
337
|
+
label: __( 'Save', 'elementor' ),
|
|
338
|
+
action: async () => {
|
|
339
|
+
const result = await handleSaveClick();
|
|
340
|
+
closeSaveChangesDialog();
|
|
341
|
+
if ( result?.success ) {
|
|
342
|
+
closePanel();
|
|
343
|
+
}
|
|
344
|
+
},
|
|
345
|
+
},
|
|
346
|
+
} }
|
|
347
|
+
/>
|
|
348
|
+
</SaveChangesDialog>
|
|
349
|
+
) }
|
|
105
350
|
</ThemeProvider>
|
|
106
351
|
);
|
|
107
352
|
}
|
|
108
353
|
|
|
109
|
-
const CloseButton = ( { onClose, ...props }: IconButtonProps & { onClose: () => void } ) => (
|
|
110
|
-
<IconButton size="small" color="secondary" onClick={ onClose } aria-label="Close" { ...props }>
|
|
111
|
-
<XIcon fontSize="small" />
|
|
112
|
-
</IconButton>
|
|
113
|
-
);
|
|
114
|
-
|
|
115
|
-
const ErrorBoundaryFallback = () => (
|
|
116
|
-
<Box role="alert" sx={ { minHeight: '100%', p: 2 } }>
|
|
117
|
-
<Alert severity="error" sx={ { mb: 2, maxWidth: 400, textAlign: 'center' } }>
|
|
118
|
-
<strong>{ __( 'Something went wrong', 'elementor' ) }</strong>
|
|
119
|
-
</Alert>
|
|
120
|
-
</Box>
|
|
121
|
-
);
|
|
122
|
-
|
|
123
354
|
const usePreventUnload = ( isDirty: boolean ) => {
|
|
124
355
|
useEffect( () => {
|
|
125
356
|
const handleBeforeUnload = ( event: BeforeUnloadEvent ) => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { createElement,
|
|
2
|
+
import { createElement, useEffect, useRef } from 'react';
|
|
3
3
|
import { EllipsisWithTooltip } from '@elementor/editor-ui';
|
|
4
4
|
import { GripVerticalIcon } from '@elementor/icons';
|
|
5
5
|
import {
|
|
@@ -20,18 +20,54 @@ import { __ } from '@wordpress/i18n';
|
|
|
20
20
|
import { type TVariablesList } from '../../storage';
|
|
21
21
|
import { getVariableType } from '../../variables-registry/variable-type-registry';
|
|
22
22
|
import { LabelField } from '../fields/label-field';
|
|
23
|
-
import { VariableEditMenu, type VariableManagerMenuAction } from './variable-edit-menu';
|
|
23
|
+
import { VariableEditMenu, type VariableManagerMenuAction } from './ui/variable-edit-menu';
|
|
24
|
+
import { VariableTableCell } from './ui/variable-table-cell';
|
|
24
25
|
import { VariableEditableCell } from './variable-editable-cell';
|
|
25
|
-
import { VariableTableCell } from './variable-table-cell';
|
|
26
26
|
|
|
27
27
|
type Props = {
|
|
28
28
|
menuActions: VariableManagerMenuAction[];
|
|
29
29
|
variables: TVariablesList;
|
|
30
30
|
onChange: ( variables: TVariablesList ) => void;
|
|
31
|
+
autoEditVariableId?: string;
|
|
32
|
+
onAutoEditComplete?: () => void;
|
|
33
|
+
onFieldError?: ( hasError: boolean ) => void;
|
|
31
34
|
};
|
|
32
35
|
|
|
33
|
-
export const VariablesManagerTable = ( {
|
|
34
|
-
|
|
36
|
+
export const VariablesManagerTable = ( {
|
|
37
|
+
menuActions,
|
|
38
|
+
variables,
|
|
39
|
+
onChange: handleOnChange,
|
|
40
|
+
autoEditVariableId,
|
|
41
|
+
onAutoEditComplete,
|
|
42
|
+
onFieldError,
|
|
43
|
+
}: Props ) => {
|
|
44
|
+
const tableContainerRef = useRef< HTMLDivElement >( null );
|
|
45
|
+
const variableRowRefs = useRef< Map< string, HTMLTableRowElement > >( new Map() );
|
|
46
|
+
|
|
47
|
+
useEffect( () => {
|
|
48
|
+
if ( autoEditVariableId && tableContainerRef.current ) {
|
|
49
|
+
const rowElement = variableRowRefs.current.get( autoEditVariableId );
|
|
50
|
+
if ( rowElement ) {
|
|
51
|
+
setTimeout( () => {
|
|
52
|
+
rowElement.scrollIntoView( {
|
|
53
|
+
behavior: 'smooth',
|
|
54
|
+
block: 'center',
|
|
55
|
+
inline: 'nearest',
|
|
56
|
+
} );
|
|
57
|
+
}, 100 );
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}, [ autoEditVariableId ] );
|
|
61
|
+
|
|
62
|
+
const handleRowRef = ( id: string ) => ( ref: HTMLTableRowElement | null ) => {
|
|
63
|
+
if ( ref ) {
|
|
64
|
+
variableRowRefs.current.set( id, ref );
|
|
65
|
+
} else {
|
|
66
|
+
variableRowRefs.current.delete( id );
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const ids = Object.keys( variables ).sort( sortVariablesOrder( variables ) );
|
|
35
71
|
const rows = ids
|
|
36
72
|
.filter( ( id ) => ! variables[ id ].deleted )
|
|
37
73
|
.map( ( id ) => {
|
|
@@ -40,9 +76,9 @@ export const VariablesManagerTable = ( { menuActions, variables, onChange: handl
|
|
|
40
76
|
|
|
41
77
|
return {
|
|
42
78
|
id,
|
|
79
|
+
type: variable.type,
|
|
43
80
|
name: variable.label,
|
|
44
81
|
value: variable.value,
|
|
45
|
-
type: variable.type,
|
|
46
82
|
...variableType,
|
|
47
83
|
};
|
|
48
84
|
} );
|
|
@@ -52,8 +88,24 @@ export const VariablesManagerTable = ( { menuActions, variables, onChange: handl
|
|
|
52
88
|
tableLayout: 'fixed',
|
|
53
89
|
};
|
|
54
90
|
|
|
91
|
+
const handleReorder = ( newIds: string[] ) => {
|
|
92
|
+
const updatedVariables = { ...variables };
|
|
93
|
+
|
|
94
|
+
newIds.forEach( ( id, index ) => {
|
|
95
|
+
const current = updatedVariables[ id ];
|
|
96
|
+
|
|
97
|
+
if ( ! current ) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
updatedVariables[ id ] = Object.assign( {}, current, { order: index + 1 } );
|
|
102
|
+
} );
|
|
103
|
+
|
|
104
|
+
handleOnChange( updatedVariables );
|
|
105
|
+
};
|
|
106
|
+
|
|
55
107
|
return (
|
|
56
|
-
<TableContainer sx={ { overflow: 'initial' } }>
|
|
108
|
+
<TableContainer ref={ tableContainerRef } sx={ { overflow: 'initial' } }>
|
|
57
109
|
<Table sx={ tableSX } aria-label="Variables manager list with drag and drop reordering" stickyHeader>
|
|
58
110
|
<TableHead>
|
|
59
111
|
<TableRow>
|
|
@@ -66,7 +118,7 @@ export const VariablesManagerTable = ( { menuActions, variables, onChange: handl
|
|
|
66
118
|
<TableBody>
|
|
67
119
|
<UnstableSortableProvider
|
|
68
120
|
value={ ids }
|
|
69
|
-
onChange={
|
|
121
|
+
onChange={ handleReorder }
|
|
70
122
|
variant="static"
|
|
71
123
|
restrictAxis
|
|
72
124
|
dragOverlay={ ( { children: dragOverlayChildren, ...dragOverlayProps } ) => (
|
|
@@ -88,9 +140,7 @@ export const VariablesManagerTable = ( { menuActions, variables, onChange: handl
|
|
|
88
140
|
isDragged,
|
|
89
141
|
dropPosition,
|
|
90
142
|
setTriggerRef,
|
|
91
|
-
isDragOverlay,
|
|
92
143
|
isSorting,
|
|
93
|
-
index,
|
|
94
144
|
}: UnstableSortableItemRenderProps ) => {
|
|
95
145
|
const showIndicationBefore = showDropIndication && dropPosition === 'before';
|
|
96
146
|
const showIndicationAfter = showDropIndication && dropPosition === 'after';
|
|
@@ -123,7 +173,6 @@ export const VariablesManagerTable = ( { menuActions, variables, onChange: handl
|
|
|
123
173
|
},
|
|
124
174
|
} }
|
|
125
175
|
style={ { ...itemStyle, ...triggerStyle } }
|
|
126
|
-
disableDivider={ isDragOverlay || index === rows.length - 1 }
|
|
127
176
|
>
|
|
128
177
|
<VariableTableCell noPadding width={ 10 } maxWidth={ 10 }>
|
|
129
178
|
<IconButton
|
|
@@ -148,15 +197,34 @@ export const VariablesManagerTable = ( { menuActions, variables, onChange: handl
|
|
|
148
197
|
}
|
|
149
198
|
} }
|
|
150
199
|
prefixElement={ createElement( row.icon, { fontSize: 'inherit' } ) }
|
|
151
|
-
editableElement={ ( {
|
|
200
|
+
editableElement={ ( {
|
|
201
|
+
value,
|
|
202
|
+
onChange,
|
|
203
|
+
onValidationChange,
|
|
204
|
+
error,
|
|
205
|
+
} ) => (
|
|
152
206
|
<LabelField
|
|
153
207
|
id={ 'variable-label-' + row.id }
|
|
154
208
|
size="tiny"
|
|
155
209
|
value={ value }
|
|
156
210
|
onChange={ onChange }
|
|
211
|
+
onErrorChange={ ( errorMsg ) => {
|
|
212
|
+
onValidationChange?.( errorMsg );
|
|
213
|
+
onFieldError?.( !! errorMsg );
|
|
214
|
+
} }
|
|
215
|
+
error={ error }
|
|
157
216
|
focusOnShow
|
|
217
|
+
selectOnShow={ autoEditVariableId === row.id }
|
|
218
|
+
showWarningInfotip={ true }
|
|
219
|
+
variables={ variables }
|
|
158
220
|
/>
|
|
159
221
|
) }
|
|
222
|
+
autoEdit={ autoEditVariableId === row.id }
|
|
223
|
+
onRowRef={ handleRowRef( row.id ) }
|
|
224
|
+
onAutoEditComplete={
|
|
225
|
+
autoEditVariableId === row.id ? onAutoEditComplete : undefined
|
|
226
|
+
}
|
|
227
|
+
fieldType="label"
|
|
160
228
|
>
|
|
161
229
|
<EllipsisWithTooltip
|
|
162
230
|
title={ row.name }
|
|
@@ -177,12 +245,34 @@ export const VariablesManagerTable = ( { menuActions, variables, onChange: handl
|
|
|
177
245
|
} );
|
|
178
246
|
}
|
|
179
247
|
} }
|
|
180
|
-
editableElement={
|
|
248
|
+
editableElement={ ( {
|
|
249
|
+
value,
|
|
250
|
+
onChange,
|
|
251
|
+
onValidationChange,
|
|
252
|
+
error,
|
|
253
|
+
} ) =>
|
|
254
|
+
row.valueField( {
|
|
255
|
+
value,
|
|
256
|
+
onChange,
|
|
257
|
+
onValidationChange: ( errorMsg ) => {
|
|
258
|
+
onValidationChange?.( errorMsg );
|
|
259
|
+
onFieldError?.( !! errorMsg );
|
|
260
|
+
},
|
|
261
|
+
error,
|
|
262
|
+
} )
|
|
263
|
+
}
|
|
264
|
+
onRowRef={ handleRowRef( row.id ) }
|
|
265
|
+
gap={ 0.25 }
|
|
266
|
+
fieldType="value"
|
|
181
267
|
>
|
|
182
268
|
{ row.startIcon && row.startIcon( { value: row.value } ) }
|
|
183
269
|
<EllipsisWithTooltip
|
|
184
270
|
title={ row.value }
|
|
185
|
-
sx={ {
|
|
271
|
+
sx={ {
|
|
272
|
+
border: '4px solid transparent',
|
|
273
|
+
lineHeight: '1',
|
|
274
|
+
pt: 0.25,
|
|
275
|
+
} }
|
|
186
276
|
>
|
|
187
277
|
{ row.value }
|
|
188
278
|
</EllipsisWithTooltip>
|
|
@@ -214,3 +304,10 @@ export const VariablesManagerTable = ( { menuActions, variables, onChange: handl
|
|
|
214
304
|
</TableContainer>
|
|
215
305
|
);
|
|
216
306
|
};
|
|
307
|
+
function sortVariablesOrder( variables: TVariablesList ): ( a: string, b: string ) => number {
|
|
308
|
+
return ( a, b ) => {
|
|
309
|
+
const orderA = variables[ a ]?.order ?? Number.MAX_SAFE_INTEGER;
|
|
310
|
+
const orderB = variables[ b ]?.order ?? Number.MAX_SAFE_INTEGER;
|
|
311
|
+
return orderA - orderB;
|
|
312
|
+
};
|
|
313
|
+
}
|