1
- import React , { useEffect , useRef , useState } from 'react' ;
1
+ import React , { useEffect , useMemo , useRef , useState } from 'react' ;
2
2
import Box from '@mui/material/Box' ;
3
- import { MetadataKeyValuePair } from './MetadataKeyValuePair' ;
3
+ import { MetadataKeyValuePair } from './MetadataKeyValuePair' ;
4
4
import StyledTextField from './StyledTextField' ;
5
5
import IconButton from '@mui/material/IconButton' ;
6
6
import AddIcon from '@mui/icons-material/Add' ;
7
- import { Button , ButtonStretch , ButtonVariant } from '../../../elements' ;
7
+ import { Button , ButtonStretch , ButtonVariant } from '../../../elements' ;
8
+ import theme from "../../../../theme" ;
9
+ import { ThemeProvider } from "@mui/material" ;
8
10
9
11
export type MetadataType = 'BOOLEAN' | 'INTEGER' | 'FLOAT' | 'STRING' | 'BLOB' ;
10
12
@@ -16,6 +18,7 @@ export interface NewMetadataField {
16
18
multiple ?: boolean ;
17
19
isAutoGenerated ?: boolean ;
18
20
isNewlyCreated ?: boolean ;
21
+ editInProgress ?: boolean ;
19
22
}
20
23
21
24
//The format the component expects to receive the metadata list
@@ -46,12 +49,12 @@ export interface MetadataKeyValueListProps {
46
49
}
47
50
48
51
export function MetadataKeyValueList ( {
49
- maxHeight,
50
- metadataList,
51
- onDeleteHandler,
52
- onSaveHandler,
53
- validateValueByType,
54
- } : MetadataKeyValueListProps ) {
52
+ maxHeight,
53
+ metadataList,
54
+ onDeleteHandler,
55
+ onSaveHandler,
56
+ validateValueByType,
57
+ } : MetadataKeyValueListProps ) {
55
58
//Todo:
56
59
// - Not sure what to do with the multiple field. (If I need to use it as part of the validations in the future, and also what value should I set for newly created fields).
57
60
// - Validations that are missing:
@@ -73,7 +76,7 @@ export function MetadataKeyValueList({
73
76
74
77
const checkIfTemporaryMetadataListIsEqualToOriginalMetadataList = ( ) => {
75
78
//Compare the fields - if isNewlyCreated is undefined and all the rest are the same, it means its equal, if there is metadat with isNewlyCreated true, its not equal
76
- if ( temporaryMetadataList . length !== metadataList . length ) return false ;
79
+ if ( temporaryMetadataList . length !== metadataList . length ) return false ;
77
80
return temporaryMetadataList . every ( ( tempField , index ) => {
78
81
const originalField = metadataList [ index ] ;
79
82
return (
@@ -86,9 +89,45 @@ export function MetadataKeyValueList({
86
89
} ) ;
87
90
}
88
91
89
- useEffect ( ( ) => {
90
- setShowButtonsSection ( ! checkIfTemporaryMetadataListIsEqualToOriginalMetadataList ( ) ) ;
91
- } , [ temporaryMetadataList , metadataList ] )
92
+ const checkIfThereAreEmptyFields = ( ) => {
93
+ return temporaryMetadataList . some ( ( field ) => {
94
+ return (
95
+ field . key === undefined ||
96
+ field . key == '' ||
97
+ field . valueType === undefined ||
98
+ ( ( field . value === undefined || field . value === '' ) && field . valueType !== "STRING" )
99
+ ) ;
100
+ } ) ;
101
+ }
102
+
103
+ const checkIfThereAreInvalidFields = ( ) => {
104
+ return temporaryMetadataList . some ( ( field ) => {
105
+ if ( ! ! validateValueByType ) {
106
+ return (
107
+ field . valueType !== undefined &&
108
+ field . value !== undefined &&
109
+ ! validateValueByType ( field . valueType , field . value )
110
+ ) ;
111
+ }
112
+ return false ;
113
+ } ) ;
114
+ }
115
+
116
+ const checkIfThereAreFieldsThatAreStillBeingEdited = ( ) => {
117
+ return temporaryMetadataList . some ( ( field ) => {
118
+ return field . editInProgress ;
119
+ } ) ;
120
+ }
121
+
122
+ const disableSaveButton = useMemo ( ( ) => {
123
+ if ( checkIfThereAreEmptyFields ( ) || checkIfThereAreInvalidFields ( ) || checkIfThereAreFieldsThatAreStillBeingEdited ( ) ) {
124
+ return true ;
125
+ }
126
+ } , [ temporaryMetadataList , validateValueByType ] ) ;
127
+
128
+ useEffect ( ( ) => {
129
+ setShowButtonsSection ( ! checkIfTemporaryMetadataListIsEqualToOriginalMetadataList ( ) || checkIfThereAreFieldsThatAreStillBeingEdited ( ) ) ;
130
+ } , [ temporaryMetadataList , metadataList ] )
92
131
93
132
useEffect ( ( ) => {
94
133
setTemporaryMetadataList ( [ ...metadataList ] ) ;
@@ -129,25 +168,12 @@ export function MetadataKeyValueList({
129
168
}
130
169
} , [ shouldHighlightEmptyFields ] ) ;
131
170
132
- const CheckIfEmptyFieldsAndHighlightThem = ( ) => {
133
- const hasEmptyFields = temporaryMetadataList . some ( ( field ) => {
134
- return (
135
- field . key === undefined ||
136
- field . key == '' ||
137
- field . value === undefined ||
138
- field . value === '' ||
139
- field . valueType === undefined
140
- ) ;
141
- } ) ;
142
-
143
- setShouldHighlightEmptyFields ( hasEmptyFields ) ;
144
- return hasEmptyFields ;
145
- } ;
146
-
147
171
const handleAddNew = ( ) => {
148
172
setShouldScrollToBottom ( true ) ; //scroll to button when clicking on the + button
149
- const hasEmptyFields = CheckIfEmptyFieldsAndHighlightThem ( ) ;
150
- if ( ! hasEmptyFields ) {
173
+ const hasEmptyFields = checkIfThereAreEmptyFields ( ) ;
174
+ setShouldHighlightEmptyFields ( hasEmptyFields ) ;
175
+
176
+ if ( ! hasEmptyFields && ! checkIfThereAreInvalidFields ( ) ) {
151
177
//if there are no empty fields, add new field
152
178
const newField : NewMetadataField = {
153
179
key : undefined ,
@@ -167,23 +193,31 @@ export function MetadataKeyValueList({
167
193
}
168
194
setTemporaryMetadataList ( ( prevList ) => {
169
195
const newList = [ ...prevList ] ;
170
- newList [ index ] = { ...newList [ index ] , key : newKey } ;
196
+ newList [ index ] = { ...newList [ index ] , key : newKey , editInProgress : false } ;
171
197
return newList ;
172
198
} ) ;
173
199
} ;
174
200
175
201
const locallyEditValueAtIndex = ( index : number , newValue : string | undefined ) => {
176
202
setTemporaryMetadataList ( ( prevList ) => {
177
203
const newList = [ ...prevList ] ;
178
- newList [ index ] = { ...newList [ index ] , value : newValue } ;
204
+ newList [ index ] = { ...newList [ index ] , value : newValue , editInProgress : false } ;
205
+ return newList ;
206
+ } ) ;
207
+ } ;
208
+
209
+ const setMetadataFieldStatusAsEditInProgress = ( index : number ) => {
210
+ setTemporaryMetadataList ( ( prevList ) => {
211
+ const newList = [ ...prevList ] ;
212
+ newList [ index ] = { ...newList [ index ] , editInProgress : true } ;
179
213
return newList ;
180
214
} ) ;
181
215
} ;
182
216
183
217
const locallyEditValueTypeAtIndex = ( index : number , newType : MetadataType | undefined ) => {
184
218
setTemporaryMetadataList ( ( prevList ) => {
185
219
const newList = [ ...prevList ] ;
186
- newList [ index ] = { ...newList [ index ] , valueType : newType } ;
220
+ newList [ index ] = { ...newList [ index ] , valueType : newType } ;
187
221
return newList ;
188
222
} ) ;
189
223
} ;
@@ -206,7 +240,8 @@ export function MetadataKeyValueList({
206
240
const newList = prevList . filter ( ( _ , index ) => index !== indexToRemove ) ;
207
241
return newList ;
208
242
} ) ;
209
- } catch ( e ) { }
243
+ } catch ( e ) {
244
+ }
210
245
}
211
246
} ;
212
247
@@ -230,103 +265,105 @@ export function MetadataKeyValueList({
230
265
} ;
231
266
232
267
return (
233
- < Box
234
- sx = { {
235
- display : 'flex' ,
236
- flexDirection : 'column' ,
237
- height : '100%' ,
238
- maxHeight : maxHeight ?? '100%' ,
239
- overflowX : 'hidden' ,
240
- } }
241
- >
268
+ < ThemeProvider theme = { theme } >
242
269
< Box
243
- ref = { metadataFieldsSection }
244
270
sx = { {
245
271
display : 'flex' ,
246
272
flexDirection : 'column' ,
247
273
height : '100%' ,
248
- maxHeight : '100%' ,
249
- overflowY : 'auto' ,
274
+ maxHeight : maxHeight ?? '100%' ,
250
275
overflowX : 'hidden' ,
251
276
} }
252
277
>
253
- { temporaryMetadataList . map ( ( metadataField , index ) => (
254
- < MetadataKeyValuePair
255
- { ...metadataField }
256
- index = { index }
257
- keyName = { metadataField . key }
258
- value = { String ( metadataField . value ) }
259
- isEditable = { ! ! onSaveHandler && ! metadataField . isAutoGenerated }
260
- description = { metadataField . isAutoGenerated ? 'Auto-generated' : undefined }
261
- isRemovable = { checkIfPairIsRemovable ( metadataField ) }
262
- saveKeyNameLocally = { locallyEditKeyAtIndex }
263
- saveValueTypeLocally = { locallyEditValueTypeAtIndex }
264
- saveValueLocally = { locallyEditValueAtIndex }
265
- shouldHighlightEmptyFields = { shouldHighlightEmptyFields }
266
- deleteFieldPermanently = {
267
- metadataField . isNewlyCreated
268
- ? locallyRemoveMetadataFieldByIndex
269
- : permanentlyDeleteMetadataFieldByIndex
270
- }
271
- autoFocusKey = { metadataField . isNewlyCreated && autoFocusNewlyCreatedFieldKey }
272
- validateValueByType = { validateValueByType }
273
- />
274
- ) ) }
275
- </ Box >
276
- { ! ! onSaveHandler && (
277
- < >
278
- < StyledTextField
279
- onClick = { handleAddNew }
280
- sx = { {
281
- borderBottom : '1px solid #E2E8F0' ,
282
- borderRadius : 0 ,
283
- } }
284
- focusModeDisabled
285
- changeColorOnHover
286
- InputProps = { {
287
- sx : { input : { cursor : 'pointer!important' } } ,
288
- readOnly : true ,
289
- endAdornment : (
290
- < IconButton >
291
- < AddIcon />
292
- </ IconButton >
293
- ) ,
294
- } }
295
- value = { 'Add new' }
296
- />
297
- { showButtonsSection && < Box
298
- sx = { {
299
- padding : '16px' ,
300
- display : 'flex' ,
301
- width : '100%' ,
302
- gap : '8px' ,
303
- justifyContent : 'flex-end' ,
304
- } }
305
- >
306
- < Button
307
- variant = { ButtonVariant . Secondary }
308
- style = { { borderRadius : '8px' } }
309
- label = { 'Cancel' }
310
- stretch = { ButtonStretch . Slim }
311
- onClick = { ( ) => {
312
- // onResetChanges();
313
- //Todo: implement
314
- } }
278
+ < Box
279
+ ref = { metadataFieldsSection }
280
+ sx = { {
281
+ display : 'flex' ,
282
+ flexDirection : 'column' ,
283
+ height : '100%' ,
284
+ maxHeight : '100%' ,
285
+ overflowY : 'auto' ,
286
+ overflowX : 'hidden' ,
287
+ } }
288
+ >
289
+ { temporaryMetadataList . map ( ( metadataField , index ) => (
290
+ < MetadataKeyValuePair
291
+ { ...metadataField }
292
+ index = { index }
293
+ keyName = { metadataField . key }
294
+ value = { String ( metadataField . value ) }
295
+ isEditable = { ! ! onSaveHandler && ! metadataField . isAutoGenerated }
296
+ description = { metadataField . isAutoGenerated ? `Auto-generated` : undefined }
297
+ isRemovable = { checkIfPairIsRemovable ( metadataField ) }
298
+ saveKeyNameLocally = { locallyEditKeyAtIndex }
299
+ saveValueTypeLocally = { locallyEditValueTypeAtIndex }
300
+ saveValueLocally = { locallyEditValueAtIndex }
301
+ markFieldStatusAsEditInProgress = { setMetadataFieldStatusAsEditInProgress }
302
+ shouldHighlightEmptyFields = { shouldHighlightEmptyFields }
303
+ deleteFieldPermanently = {
304
+ metadataField . isNewlyCreated
305
+ ? locallyRemoveMetadataFieldByIndex
306
+ : permanentlyDeleteMetadataFieldByIndex
307
+ }
308
+ autoFocusKey = { metadataField . isNewlyCreated && autoFocusNewlyCreatedFieldKey }
309
+ validateValueByType = { validateValueByType }
315
310
/>
316
- < Button
317
- style = { { borderRadius : '8px' } }
318
- label = { 'Save' }
319
- stretch = { ButtonStretch . Slim }
320
- disabled = { false } //Todo
321
- onClick = { ( ) => {
322
- onSaveHandler (
323
- convertNewMetadataFieldToDatapointMetadataInput ( temporaryMetadataList )
324
- ) ;
311
+ ) ) }
312
+ </ Box >
313
+ { ! ! onSaveHandler && (
314
+ < >
315
+ < StyledTextField
316
+ onClick = { handleAddNew }
317
+ sx = { {
318
+ borderBottom : '1px solid #E2E8F0' ,
319
+ borderRadius : 0 ,
320
+ } }
321
+ focusModeDisabled
322
+ changeColorOnHover
323
+ InputProps = { {
324
+ sx : { input : { cursor : 'pointer!important' } } ,
325
+ readOnly : true ,
326
+ endAdornment : (
327
+ < IconButton >
328
+ < AddIcon />
329
+ </ IconButton >
330
+ ) ,
325
331
} }
332
+ value = { 'Add new' }
326
333
/>
327
- </ Box > }
328
- </ >
329
- ) }
330
- </ Box >
334
+ { showButtonsSection && < Box
335
+ sx = { {
336
+ padding : '16px' ,
337
+ display : 'flex' ,
338
+ width : '100%' ,
339
+ gap : '8px' ,
340
+ justifyContent : 'flex-end' ,
341
+ } }
342
+ >
343
+ < Button
344
+ variant = { ButtonVariant . Secondary }
345
+ style = { { borderRadius : '8px' } }
346
+ label = { 'Cancel' }
347
+ stretch = { ButtonStretch . Slim }
348
+ onClick = { ( ) => {
349
+ setTemporaryMetadataList ( [ ...metadataList ] ) ;
350
+ } }
351
+ />
352
+ < Button
353
+ style = { { borderRadius : '8px' } }
354
+ label = { 'Save' }
355
+ stretch = { ButtonStretch . Slim }
356
+ disabled = { disableSaveButton }
357
+ onClick = { ( ) => {
358
+ onSaveHandler (
359
+ convertNewMetadataFieldToDatapointMetadataInput ( temporaryMetadataList )
360
+ ) ;
361
+ } }
362
+ />
363
+ </ Box > }
364
+ </ >
365
+ ) }
366
+ </ Box >
367
+ </ ThemeProvider >
331
368
) ;
332
369
}
0 commit comments