@finos/legend-query-builder 4.16.20 → 4.16.22
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/lib/components/__test-utils__/QueryBuilderComponentTestUtils.d.ts +23 -1
- package/lib/components/__test-utils__/QueryBuilderComponentTestUtils.d.ts.map +1 -1
- package/lib/components/__test-utils__/QueryBuilderComponentTestUtils.js +16 -2
- package/lib/components/__test-utils__/QueryBuilderComponentTestUtils.js.map +1 -1
- package/lib/components/fetch-structure/QueryBuilderPostFilterPanel.js +1 -1
- package/lib/components/fetch-structure/QueryBuilderPostFilterPanel.js.map +1 -1
- package/lib/components/filter/QueryBuilderFilterPanel.js +1 -1
- package/lib/components/filter/QueryBuilderFilterPanel.js.map +1 -1
- package/lib/components/shared/BasicValueSpecificationEditor.d.ts +162 -4
- package/lib/components/shared/BasicValueSpecificationEditor.d.ts.map +1 -1
- package/lib/components/shared/BasicValueSpecificationEditor.js +253 -172
- package/lib/components/shared/BasicValueSpecificationEditor.js.map +1 -1
- package/lib/components/shared/CustomDatePicker.d.ts +8 -55
- package/lib/components/shared/CustomDatePicker.d.ts.map +1 -1
- package/lib/components/shared/CustomDatePicker.js +33 -417
- package/lib/components/shared/CustomDatePicker.js.map +1 -1
- package/lib/components/shared/CustomDatePickerHelper.d.ts +145 -0
- package/lib/components/shared/CustomDatePickerHelper.d.ts.map +1 -0
- package/lib/components/shared/CustomDatePickerHelper.js +517 -0
- package/lib/components/shared/CustomDatePickerHelper.js.map +1 -0
- package/lib/components/shared/QueryBuilderVariableSelector.js +1 -1
- package/lib/components/shared/QueryBuilderVariableSelector.js.map +1 -1
- package/lib/components/shared/V1_BasicValueSpecificationEditor.d.ts +38 -0
- package/lib/components/shared/V1_BasicValueSpecificationEditor.d.ts.map +1 -0
- package/lib/components/shared/V1_BasicValueSpecificationEditor.js +166 -0
- package/lib/components/shared/V1_BasicValueSpecificationEditor.js.map +1 -0
- package/lib/index.css +2 -2
- package/lib/index.css.map +1 -1
- package/lib/index.d.ts +4 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +4 -0
- package/lib/index.js.map +1 -1
- package/lib/package.json +1 -1
- package/lib/stores/shared/V1_ValueSpecificationEditorHelper.d.ts +23 -0
- package/lib/stores/shared/V1_ValueSpecificationEditorHelper.d.ts.map +1 -0
- package/lib/stores/shared/V1_ValueSpecificationEditorHelper.js +83 -0
- package/lib/stores/shared/V1_ValueSpecificationEditorHelper.js.map +1 -0
- package/lib/stores/shared/V1_ValueSpecificationModifierHelper.d.ts +20 -0
- package/lib/stores/shared/V1_ValueSpecificationModifierHelper.d.ts.map +1 -0
- package/lib/stores/shared/V1_ValueSpecificationModifierHelper.js +38 -0
- package/lib/stores/shared/V1_ValueSpecificationModifierHelper.js.map +1 -0
- package/lib/stores/shared/ValueSpecificationEditorHelper.d.ts +4 -1
- package/lib/stores/shared/ValueSpecificationEditorHelper.d.ts.map +1 -1
- package/lib/stores/shared/ValueSpecificationEditorHelper.js +23 -2
- package/lib/stores/shared/ValueSpecificationEditorHelper.js.map +1 -1
- package/package.json +10 -10
- package/src/components/__test-utils__/QueryBuilderComponentTestUtils.tsx +103 -12
- package/src/components/fetch-structure/QueryBuilderPostFilterPanel.tsx +1 -1
- package/src/components/filter/QueryBuilderFilterPanel.tsx +1 -1
- package/src/components/shared/BasicValueSpecificationEditor.tsx +1477 -1088
- package/src/components/shared/CustomDatePicker.tsx +146 -905
- package/src/components/shared/CustomDatePickerHelper.ts +984 -0
- package/src/components/shared/QueryBuilderVariableSelector.tsx +1 -1
- package/src/components/shared/V1_BasicValueSpecificationEditor.tsx +409 -0
- package/src/index.ts +7 -0
- package/src/stores/shared/V1_ValueSpecificationEditorHelper.ts +131 -0
- package/src/stores/shared/V1_ValueSpecificationModifierHelper.ts +76 -0
- package/src/stores/shared/ValueSpecificationEditorHelper.ts +71 -2
- package/tsconfig.json +4 -0
@@ -40,26 +40,29 @@ import {
|
|
40
40
|
} from '@finos/legend-art';
|
41
41
|
import {
|
42
42
|
type Enum,
|
43
|
-
type Type,
|
44
|
-
type ValueSpecification,
|
45
|
-
type PureModel,
|
46
43
|
type ObserverContext,
|
47
|
-
|
44
|
+
type PureModel,
|
45
|
+
type ValueSpecification,
|
48
46
|
CollectionInstanceValue,
|
49
|
-
|
50
|
-
INTERNAL__PropagatedValue,
|
51
|
-
SimpleFunctionExpression,
|
52
|
-
VariableExpression,
|
47
|
+
Enumeration,
|
53
48
|
EnumValueExplicitReference,
|
54
|
-
|
55
|
-
PRIMITIVE_TYPE,
|
56
|
-
GenericTypeExplicitReference,
|
49
|
+
EnumValueInstanceValue,
|
57
50
|
GenericType,
|
58
|
-
|
51
|
+
GenericTypeExplicitReference,
|
59
52
|
getMultiplicityDescription,
|
60
|
-
|
61
|
-
isSubType,
|
53
|
+
getPrimitiveTypeInstanceFromEnum,
|
62
54
|
InstanceValue,
|
55
|
+
INTERNAL__PropagatedValue,
|
56
|
+
isSubType,
|
57
|
+
matchFunctionName,
|
58
|
+
PRIMITIVE_TYPE,
|
59
|
+
PrimitiveInstanceValue,
|
60
|
+
PrimitiveType,
|
61
|
+
SimpleFunctionExpression,
|
62
|
+
Type,
|
63
|
+
VariableExpression,
|
64
|
+
observe_ValueSpecification,
|
65
|
+
V1_PackageableType,
|
63
66
|
} from '@finos/legend-graph';
|
64
67
|
import {
|
65
68
|
type DebouncedFunc,
|
@@ -72,7 +75,6 @@ import {
|
|
72
75
|
isNonEmptyString,
|
73
76
|
parseCSVString,
|
74
77
|
uniq,
|
75
|
-
at,
|
76
78
|
} from '@finos/legend-shared';
|
77
79
|
import { flowResult } from 'mobx';
|
78
80
|
import { observer } from 'mobx-react-lite';
|
@@ -86,8 +88,8 @@ import React, {
|
|
86
88
|
import {
|
87
89
|
instanceValue_setValue,
|
88
90
|
instanceValue_setValues,
|
91
|
+
valueSpecification_setGenericType,
|
89
92
|
} from '../../stores/shared/ValueSpecificationModifierHelper.js';
|
90
|
-
import { CustomDatePicker } from './CustomDatePicker.js';
|
91
93
|
import { QUERY_BUILDER_SUPPORTED_FUNCTIONS } from '../../graph/QueryBuilderMetaModelConst.js';
|
92
94
|
import {
|
93
95
|
isValidInstanceValue,
|
@@ -96,12 +98,25 @@ import {
|
|
96
98
|
import { evaluate } from 'mathjs';
|
97
99
|
import { isUsedDateFunctionSupportedInFormMode } from '../../stores/QueryBuilderStateBuilder.js';
|
98
100
|
import {
|
101
|
+
buildPrimitiveInstanceValue,
|
99
102
|
convertTextToEnum,
|
100
103
|
convertTextToPrimitiveInstanceValue,
|
101
104
|
getValueSpecificationStringValue,
|
102
105
|
} from '../../stores/shared/ValueSpecificationEditorHelper.js';
|
103
|
-
|
104
|
-
|
106
|
+
import { CustomDatePicker } from './CustomDatePicker.js';
|
107
|
+
import {
|
108
|
+
type CustomDatePickerValueSpecification,
|
109
|
+
type CustomDatePickerUpdateValueSpecification,
|
110
|
+
CustomDateOption,
|
111
|
+
buildPureAdjustDateFunction,
|
112
|
+
CustomFirstDayOfOption,
|
113
|
+
buildPureDateFunctionExpression,
|
114
|
+
CustomPreviousDayOfWeekOption,
|
115
|
+
DatePickerOption,
|
116
|
+
} from './CustomDatePickerHelper.js';
|
117
|
+
import type { V1_TypeCheckOption } from './V1_BasicValueSpecificationEditor.js';
|
118
|
+
|
119
|
+
export type TypeCheckOption = {
|
105
120
|
expectedType: Type;
|
106
121
|
/**
|
107
122
|
* Indicates if a strict type-matching will happen.
|
@@ -109,6 +124,10 @@ type TypeCheckOption = {
|
|
109
124
|
* for example we can assign a Float to an Integer, a
|
110
125
|
* Date to a DateTime. With this flag set to `true`
|
111
126
|
* we will not allow this.
|
127
|
+
*
|
128
|
+
* For example, if `match=true`, it means that options in the
|
129
|
+
* date-capability-dropdown which are not returning type DateTime
|
130
|
+
* will be filtered out.
|
112
131
|
*/
|
113
132
|
match?: boolean;
|
114
133
|
};
|
@@ -223,461 +242,512 @@ const VariableExpressionParameterEditor = observer(
|
|
223
242
|
},
|
224
243
|
);
|
225
244
|
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
const updateValueSpec = (val: string): void => {
|
254
|
-
instanceValue_setValue(valueSpecification, val, 0, observerContext);
|
255
|
-
setValueSpecification(valueSpecification);
|
256
|
-
};
|
257
|
-
const changeInputValue: React.ChangeEventHandler<HTMLInputElement> = (
|
258
|
-
event,
|
259
|
-
) => {
|
260
|
-
updateValueSpec(event.target.value);
|
261
|
-
};
|
262
|
-
// custom select
|
263
|
-
const selectedValue = value ? { value: value, label: value } : null;
|
264
|
-
const reloadValuesFunc = selectorConfig?.reloadValues;
|
265
|
-
const changeValue = (
|
266
|
-
val: null | { value: number | string; label: string },
|
267
|
-
): void => {
|
268
|
-
const newValue = val === null ? '' : val.value.toString();
|
269
|
-
updateValueSpec(newValue);
|
270
|
-
};
|
271
|
-
const handleInputChange = (
|
272
|
-
inputValue: string,
|
273
|
-
actionChange: InputActionData,
|
274
|
-
): void => {
|
275
|
-
if (actionChange.action === 'input-change') {
|
276
|
-
updateValueSpec(inputValue);
|
277
|
-
reloadValuesFunc?.cancel();
|
278
|
-
const reloadValuesFuncTransformation = reloadValuesFunc?.(inputValue);
|
279
|
-
if (reloadValuesFuncTransformation) {
|
280
|
-
flowResult(reloadValuesFuncTransformation).catch(
|
281
|
-
applicationStore.alertUnhandledError,
|
282
|
-
);
|
283
|
-
}
|
284
|
-
}
|
285
|
-
if (actionChange.action === 'input-blur') {
|
286
|
-
reloadValuesFunc?.cancel();
|
287
|
-
selectorConfig?.cleanUpReloadValues?.();
|
288
|
-
}
|
289
|
-
};
|
290
|
-
const isLoading = selectorConfig?.isLoading;
|
291
|
-
const queryOptions = selectorConfig?.values?.length
|
292
|
-
? selectorConfig.values.map((e) => ({
|
293
|
-
value: e,
|
294
|
-
label: e.toString(),
|
295
|
-
}))
|
296
|
-
: undefined;
|
297
|
-
const noOptionsMessage =
|
298
|
-
selectorConfig?.values === undefined ? (): null => null : undefined;
|
299
|
-
const resetButtonName = `reset-${valueSpecification.hashCode}`;
|
300
|
-
const inputName = `input-${valueSpecification.hashCode}`;
|
301
|
-
|
302
|
-
const onBlur = (
|
303
|
-
event: React.FocusEvent<HTMLInputElement, HTMLButtonElement>,
|
304
|
-
): void => {
|
305
|
-
if (
|
306
|
-
event.relatedTarget?.name !== resetButtonName &&
|
307
|
-
event.relatedTarget?.name !== inputName
|
308
|
-
) {
|
309
|
-
handleBlur?.();
|
310
|
-
}
|
311
|
-
};
|
245
|
+
/**
|
246
|
+
* This is the base interface for primitive instance value editors (non-collection values).
|
247
|
+
* The interface is made generic so that it can support various types of objects that hold the value
|
248
|
+
* to be edited (currently, we just use this for ValueSpecification and V1_ValueSpecification).
|
249
|
+
*
|
250
|
+
* T represents the type of the object that holds the value to be edited (i.e. ValueSpecification or V1_ValueSpecification).
|
251
|
+
* U represents the type of data that the object holds.
|
252
|
+
*
|
253
|
+
* valueSelector: callback that handles extracting the data value from the object.
|
254
|
+
* updateValueSpecification: callback that takes the valueSpecification object and the new value and handles updating
|
255
|
+
* the object with the new value.
|
256
|
+
* errorChecker: optional callback that should return true if the valueSpecification is invalid.
|
257
|
+
*/
|
258
|
+
export interface PrimitiveInstanceValueEditorProps<
|
259
|
+
T,
|
260
|
+
U extends string | number | boolean | Enum | null,
|
261
|
+
> {
|
262
|
+
valueSpecification: T;
|
263
|
+
valueSelector: (val: T) => U;
|
264
|
+
updateValueSpecification: (valueSpecification: T, value: U) => void;
|
265
|
+
errorChecker?: (valueSpecification: T) => boolean;
|
266
|
+
resetValue: () => void;
|
267
|
+
handleBlur?: (() => void) | undefined;
|
268
|
+
handleKeyDown?: React.KeyboardEventHandler<HTMLDivElement> | undefined;
|
269
|
+
className?: string | undefined;
|
270
|
+
readOnly?: boolean | undefined;
|
271
|
+
}
|
312
272
|
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
inputValue={value ?? ''}
|
322
|
-
onInputChange={handleInputChange}
|
323
|
-
darkMode={
|
324
|
-
!applicationStore.layoutService
|
325
|
-
.TEMPORARY__isLightColorThemeEnabled
|
326
|
-
}
|
327
|
-
isLoading={isLoading}
|
328
|
-
allowCreateWhileLoading={true}
|
329
|
-
noOptionsMessage={noOptionsMessage}
|
330
|
-
components={{
|
331
|
-
DropdownIndicator: null,
|
332
|
-
}}
|
333
|
-
hasError={!isValidInstanceValue(valueSpecification)}
|
334
|
-
placeholder={value === '' ? '(empty)' : undefined}
|
335
|
-
inputRef={ref as React.Ref<SelectComponent>}
|
336
|
-
onKeyDown={
|
337
|
-
handleKeyDown as React.KeyboardEventHandler<HTMLDivElement>
|
338
|
-
}
|
339
|
-
inputName={inputName}
|
340
|
-
/>
|
341
|
-
) : (
|
342
|
-
<InputWithInlineValidation
|
343
|
-
className="panel__content__form__section__input value-spec-editor__input"
|
344
|
-
spellCheck={false}
|
345
|
-
value={value ?? ''}
|
346
|
-
placeholder={value === '' ? '(empty)' : undefined}
|
347
|
-
onChange={changeInputValue}
|
348
|
-
ref={ref as React.Ref<HTMLInputElement>}
|
349
|
-
error={
|
350
|
-
!isValidInstanceValue(valueSpecification)
|
351
|
-
? 'Invalid String value'
|
352
|
-
: undefined
|
353
|
-
}
|
354
|
-
onKeyDown={handleKeyDown}
|
355
|
-
name={inputName}
|
356
|
-
/>
|
357
|
-
)}
|
358
|
-
<button
|
359
|
-
className="value-spec-editor__reset-btn"
|
360
|
-
name={resetButtonName}
|
361
|
-
title="Reset"
|
362
|
-
onClick={resetValue}
|
363
|
-
>
|
364
|
-
<RefreshIcon />
|
365
|
-
</button>
|
366
|
-
</div>
|
367
|
-
);
|
368
|
-
}),
|
369
|
-
);
|
273
|
+
export interface BasicValueSpecificationEditorSelectorSearchConfig {
|
274
|
+
values: string[] | undefined;
|
275
|
+
isLoading: boolean;
|
276
|
+
reloadValues:
|
277
|
+
| DebouncedFunc<(inputValue: string) => GeneratorFn<void>>
|
278
|
+
| undefined;
|
279
|
+
cleanUpReloadValues?: () => void;
|
280
|
+
}
|
370
281
|
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
className?: string | undefined;
|
375
|
-
resetValue: () => void;
|
376
|
-
setValueSpecification: (val: ValueSpecification) => void;
|
377
|
-
observerContext: ObserverContext;
|
378
|
-
}) => {
|
379
|
-
const {
|
380
|
-
valueSpecification,
|
381
|
-
className,
|
382
|
-
resetValue,
|
383
|
-
setValueSpecification,
|
384
|
-
observerContext,
|
385
|
-
} = props;
|
386
|
-
const value = valueSpecification.values[0] as boolean;
|
387
|
-
const toggleValue = (): void => {
|
388
|
-
instanceValue_setValue(valueSpecification, !value, 0, observerContext);
|
389
|
-
setValueSpecification(valueSpecification);
|
390
|
-
};
|
282
|
+
export interface BasicValueSpecificationEditorSelectorConfig {
|
283
|
+
optionCustomization?: { rowHeight?: number | undefined } | undefined;
|
284
|
+
}
|
391
285
|
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
{value ? <CheckSquareIcon /> : <SquareIcon />}
|
401
|
-
</button>
|
402
|
-
<button
|
403
|
-
className="value-spec-editor__reset-btn"
|
404
|
-
name="Reset"
|
405
|
-
title="Reset"
|
406
|
-
onClick={resetValue}
|
407
|
-
>
|
408
|
-
<RefreshIcon />
|
409
|
-
</button>
|
410
|
-
</div>
|
411
|
-
);
|
412
|
-
},
|
413
|
-
);
|
286
|
+
interface StringPrimitiveInstanceValueEditorProps<T>
|
287
|
+
extends PrimitiveInstanceValueEditorProps<T, string | null> {
|
288
|
+
selectorSearchConfig?:
|
289
|
+
| BasicValueSpecificationEditorSelectorSearchConfig
|
290
|
+
| undefined;
|
291
|
+
selectorConfig?: BasicValueSpecificationEditorSelectorConfig | undefined;
|
292
|
+
lightMode?: boolean | undefined;
|
293
|
+
}
|
414
294
|
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
const
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
)
|
463
|
-
instanceValue_setValue(
|
464
|
-
valueSpecification,
|
465
|
-
parsedValue,
|
466
|
-
0,
|
467
|
-
observerContext,
|
468
|
-
);
|
469
|
-
setValueSpecification(valueSpecification);
|
470
|
-
}
|
471
|
-
} else {
|
472
|
-
resetValue();
|
295
|
+
// eslint-disable-next-line comma-spacing
|
296
|
+
const StringPrimitiveInstanceValueEditorInner = <T,>(
|
297
|
+
props: StringPrimitiveInstanceValueEditorProps<T>,
|
298
|
+
ref: React.ForwardedRef<HTMLInputElement | SelectComponent | null>,
|
299
|
+
): React.ReactElement => {
|
300
|
+
const {
|
301
|
+
valueSpecification,
|
302
|
+
valueSelector,
|
303
|
+
updateValueSpecification,
|
304
|
+
errorChecker,
|
305
|
+
resetValue,
|
306
|
+
handleBlur,
|
307
|
+
handleKeyDown,
|
308
|
+
className,
|
309
|
+
selectorSearchConfig,
|
310
|
+
selectorConfig,
|
311
|
+
lightMode,
|
312
|
+
readOnly,
|
313
|
+
} = props;
|
314
|
+
const useSelector = Boolean(selectorSearchConfig);
|
315
|
+
const applicationStore = useApplicationStore();
|
316
|
+
const value = valueSelector(valueSpecification);
|
317
|
+
const changeInputValue: React.ChangeEventHandler<HTMLInputElement> = (
|
318
|
+
event,
|
319
|
+
) => {
|
320
|
+
updateValueSpecification(valueSpecification, event.target.value);
|
321
|
+
};
|
322
|
+
// custom select
|
323
|
+
const selectedValue = value ? { value: value, label: value } : null;
|
324
|
+
const reloadValuesFunc = selectorSearchConfig?.reloadValues;
|
325
|
+
const changeValue = (
|
326
|
+
val: null | { value: number | string; label: string },
|
327
|
+
): void => {
|
328
|
+
const newValue = val === null ? '' : val.value.toString();
|
329
|
+
updateValueSpecification(valueSpecification, newValue);
|
330
|
+
};
|
331
|
+
const handleInputChange = (
|
332
|
+
inputValue: string,
|
333
|
+
actionChange: InputActionData,
|
334
|
+
): void => {
|
335
|
+
if (actionChange.action === 'input-change') {
|
336
|
+
updateValueSpecification(valueSpecification, inputValue);
|
337
|
+
reloadValuesFunc?.cancel();
|
338
|
+
const reloadValuesFuncTransformation = reloadValuesFunc?.(inputValue);
|
339
|
+
if (reloadValuesFuncTransformation) {
|
340
|
+
flowResult(reloadValuesFuncTransformation).catch(
|
341
|
+
applicationStore.alertUnhandledError,
|
342
|
+
);
|
473
343
|
}
|
474
|
-
}
|
344
|
+
}
|
345
|
+
if (actionChange.action === 'input-blur') {
|
346
|
+
reloadValuesFunc?.cancel();
|
347
|
+
selectorSearchConfig?.cleanUpReloadValues?.();
|
348
|
+
}
|
349
|
+
};
|
350
|
+
const isLoading = selectorSearchConfig?.isLoading;
|
351
|
+
const queryOptions = selectorSearchConfig?.values?.length
|
352
|
+
? selectorSearchConfig.values.map((e) => ({
|
353
|
+
value: e,
|
354
|
+
label: e.toString(),
|
355
|
+
}))
|
356
|
+
: undefined;
|
357
|
+
const noOptionsMessage =
|
358
|
+
selectorSearchConfig?.values === undefined ? (): null => null : undefined;
|
359
|
+
const resetButtonName = `reset-${valueSelector(valueSpecification)}`;
|
360
|
+
const inputName = `input-${valueSelector(valueSpecification)}`;
|
361
|
+
|
362
|
+
const onBlur = (
|
363
|
+
event: React.FocusEvent<HTMLInputElement, HTMLButtonElement>,
|
364
|
+
): void => {
|
365
|
+
if (
|
366
|
+
event.relatedTarget?.name !== resetButtonName &&
|
367
|
+
event.relatedTarget?.name !== inputName
|
368
|
+
) {
|
369
|
+
handleBlur?.();
|
370
|
+
}
|
371
|
+
};
|
475
372
|
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
373
|
+
return (
|
374
|
+
<div className={clsx('value-spec-editor', className)} onBlur={onBlur}>
|
375
|
+
{useSelector ? (
|
376
|
+
<CustomSelectorInput
|
377
|
+
className="value-spec-editor__enum-selector"
|
378
|
+
options={queryOptions}
|
379
|
+
onChange={changeValue}
|
380
|
+
value={selectedValue}
|
381
|
+
inputValue={value ?? ''}
|
382
|
+
onInputChange={handleInputChange}
|
383
|
+
darkMode={!lightMode}
|
384
|
+
isLoading={isLoading}
|
385
|
+
allowCreateWhileLoading={true}
|
386
|
+
noOptionsMessage={noOptionsMessage}
|
387
|
+
components={{
|
388
|
+
DropdownIndicator: null,
|
389
|
+
}}
|
390
|
+
hasError={errorChecker?.(valueSpecification)}
|
391
|
+
placeholder={value === '' ? '(empty)' : undefined}
|
392
|
+
inputRef={ref as React.Ref<SelectComponent>}
|
393
|
+
onKeyDown={
|
394
|
+
handleKeyDown as React.KeyboardEventHandler<HTMLDivElement>
|
395
|
+
}
|
396
|
+
inputName={inputName}
|
397
|
+
optionCustomization={selectorConfig?.optionCustomization}
|
398
|
+
disabled={readOnly}
|
399
|
+
/>
|
400
|
+
) : (
|
401
|
+
<InputWithInlineValidation
|
402
|
+
className="panel__content__form__section__input value-spec-editor__input"
|
403
|
+
spellCheck={false}
|
404
|
+
value={value ?? ''}
|
405
|
+
placeholder={value === '' ? '(empty)' : undefined}
|
406
|
+
onChange={changeInputValue}
|
407
|
+
ref={ref as React.Ref<HTMLInputElement>}
|
408
|
+
error={
|
409
|
+
errorChecker?.(valueSpecification)
|
410
|
+
? 'Invalid String value'
|
411
|
+
: undefined
|
412
|
+
}
|
413
|
+
onKeyDown={handleKeyDown}
|
414
|
+
name={inputName}
|
415
|
+
disabled={readOnly}
|
416
|
+
/>
|
417
|
+
)}
|
418
|
+
<button
|
419
|
+
className="value-spec-editor__reset-btn"
|
420
|
+
name={resetButtonName}
|
421
|
+
title="Reset"
|
422
|
+
onClick={resetValue}
|
423
|
+
disabled={readOnly}
|
424
|
+
>
|
425
|
+
<RefreshIcon />
|
426
|
+
</button>
|
427
|
+
</div>
|
428
|
+
);
|
429
|
+
};
|
482
430
|
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
setValue(calculatedValue.toString());
|
491
|
-
} catch {
|
492
|
-
// If we fail to evaluate the expression, we just keep the previous value
|
493
|
-
const prevValue =
|
494
|
-
valueSpecification.values[0] !== null &&
|
495
|
-
valueSpecification.values[0] !== undefined
|
496
|
-
? valueSpecification.values[0].toString()
|
497
|
-
: '';
|
498
|
-
updateValueSpecIfValid(prevValue);
|
499
|
-
setValue(prevValue);
|
500
|
-
}
|
501
|
-
} else if (numericValue !== null) {
|
502
|
-
// If numericValue is a number, update the value spec
|
503
|
-
updateValueSpecIfValid(numericValue.toString());
|
504
|
-
setValue(numericValue.toString());
|
505
|
-
} else {
|
506
|
-
// If numericValue is null, reset the value spec
|
507
|
-
resetValue();
|
508
|
-
}
|
509
|
-
};
|
431
|
+
export const StringPrimitiveInstanceValueEditor = observer(
|
432
|
+
forwardRef(StringPrimitiveInstanceValueEditorInner) as <T>(
|
433
|
+
props: StringPrimitiveInstanceValueEditorProps<T> & {
|
434
|
+
ref: React.ForwardedRef<HTMLInputElement | SelectComponent | null>;
|
435
|
+
},
|
436
|
+
) => ReturnType<typeof StringPrimitiveInstanceValueEditorInner>,
|
437
|
+
);
|
510
438
|
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
} else if (event.code === 'Escape') {
|
516
|
-
inputRef.current?.select();
|
517
|
-
}
|
518
|
-
};
|
439
|
+
type BooleanInstanceValueEditorProps<T> = PrimitiveInstanceValueEditorProps<
|
440
|
+
T,
|
441
|
+
boolean
|
442
|
+
>;
|
519
443
|
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
444
|
+
// eslint-disable-next-line comma-spacing
|
445
|
+
const BooleanInstanceValueEditorInner = <T,>(
|
446
|
+
props: BooleanInstanceValueEditorProps<T>,
|
447
|
+
): React.ReactElement => {
|
448
|
+
const {
|
449
|
+
valueSpecification,
|
450
|
+
valueSelector,
|
451
|
+
updateValueSpecification,
|
452
|
+
resetValue,
|
453
|
+
className,
|
454
|
+
readOnly,
|
455
|
+
} = props;
|
456
|
+
const value = valueSelector(valueSpecification);
|
457
|
+
const toggleValue = (): void => {
|
458
|
+
updateValueSpecification(valueSpecification, !value);
|
459
|
+
};
|
533
460
|
|
534
|
-
|
535
|
-
|
536
|
-
|
461
|
+
return (
|
462
|
+
<div className={clsx('value-spec-editor', className)}>
|
463
|
+
<button
|
464
|
+
role="checkbox"
|
465
|
+
className={clsx('value-spec-editor__toggler__btn', {
|
466
|
+
'value-spec-editor__toggler__btn--toggled': value,
|
467
|
+
})}
|
468
|
+
onClick={toggleValue}
|
469
|
+
disabled={readOnly}
|
470
|
+
>
|
471
|
+
{value ? <CheckSquareIcon /> : <SquareIcon />}
|
472
|
+
</button>
|
473
|
+
<button
|
474
|
+
className="value-spec-editor__reset-btn"
|
475
|
+
name="Reset"
|
476
|
+
title="Reset"
|
477
|
+
onClick={resetValue}
|
478
|
+
disabled={readOnly}
|
479
|
+
>
|
480
|
+
<RefreshIcon />
|
481
|
+
</button>
|
482
|
+
</div>
|
483
|
+
);
|
484
|
+
};
|
485
|
+
|
486
|
+
export const BooleanPrimitiveInstanceValueEditor = observer(
|
487
|
+
BooleanInstanceValueEditorInner as <T>(
|
488
|
+
props: BooleanInstanceValueEditorProps<T>,
|
489
|
+
) => ReturnType<typeof BooleanInstanceValueEditorInner>,
|
490
|
+
);
|
491
|
+
|
492
|
+
interface NumberPrimitiveInstanceValueEditorProps<T>
|
493
|
+
extends PrimitiveInstanceValueEditorProps<T, number | null> {
|
494
|
+
isInteger: boolean;
|
495
|
+
}
|
537
496
|
|
538
|
-
|
539
|
-
|
540
|
-
|
497
|
+
// eslint-disable-next-line comma-spacing
|
498
|
+
const NumberPrimitiveInstanceValueEditorInner = <T,>(
|
499
|
+
props: NumberPrimitiveInstanceValueEditorProps<T>,
|
500
|
+
ref: React.ForwardedRef<HTMLInputElement>,
|
501
|
+
): React.ReactElement => {
|
502
|
+
const {
|
503
|
+
valueSpecification,
|
504
|
+
valueSelector,
|
505
|
+
updateValueSpecification,
|
506
|
+
errorChecker,
|
507
|
+
resetValue,
|
508
|
+
handleBlur,
|
509
|
+
handleKeyDown,
|
510
|
+
className,
|
511
|
+
isInteger,
|
512
|
+
readOnly,
|
513
|
+
} = props;
|
514
|
+
const [value, setValue] = useState(
|
515
|
+
valueSelector(valueSpecification)?.toString() ?? '',
|
516
|
+
);
|
517
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
518
|
+
useImperativeHandle(ref, () => inputRef.current as HTMLInputElement, []);
|
519
|
+
const numericValue = value
|
520
|
+
? isInteger
|
521
|
+
? Number.parseInt(Number(value).toString(), 10)
|
522
|
+
: Number(value)
|
523
|
+
: null;
|
524
|
+
|
525
|
+
const updateValueSpecIfValid = (val: string): void => {
|
526
|
+
if (val) {
|
527
|
+
const parsedValue = isInteger
|
528
|
+
? Number.parseInt(Number(val).toString(), 10)
|
529
|
+
: Number(val);
|
541
530
|
if (
|
542
|
-
|
543
|
-
|
544
|
-
event.relatedTarget?.name !== calculateButtonName
|
531
|
+
!isNaN(parsedValue) &&
|
532
|
+
parsedValue !== valueSelector(valueSpecification)
|
545
533
|
) {
|
546
|
-
|
534
|
+
updateValueSpecification(valueSpecification, parsedValue);
|
547
535
|
}
|
548
|
-
}
|
536
|
+
} else {
|
537
|
+
resetValue();
|
538
|
+
}
|
539
|
+
};
|
540
|
+
|
541
|
+
const handleInputChange: React.ChangeEventHandler<HTMLInputElement> = (
|
542
|
+
event,
|
543
|
+
) => {
|
544
|
+
setValue(event.target.value);
|
545
|
+
updateValueSpecIfValid(event.target.value);
|
546
|
+
};
|
547
|
+
|
548
|
+
// Support expression evaluation
|
549
|
+
const calculateExpression = (): void => {
|
550
|
+
if (numericValue !== null && isNaN(numericValue)) {
|
551
|
+
// If the value is not a number, try to evaluate it as an expression
|
552
|
+
try {
|
553
|
+
const calculatedValue = guaranteeIsNumber(evaluate(value));
|
554
|
+
updateValueSpecIfValid(calculatedValue.toString());
|
555
|
+
setValue(calculatedValue.toString());
|
556
|
+
} catch {
|
557
|
+
// If we fail to evaluate the expression, we just keep the previous value
|
558
|
+
const prevValue = valueSelector(valueSpecification)?.toString() ?? '';
|
559
|
+
updateValueSpecIfValid(prevValue);
|
560
|
+
setValue(prevValue);
|
561
|
+
}
|
562
|
+
} else if (numericValue !== null) {
|
563
|
+
// If numericValue is a number, update the value spec
|
564
|
+
updateValueSpecIfValid(numericValue.toString());
|
565
|
+
setValue(numericValue.toString());
|
566
|
+
} else {
|
567
|
+
// If numericValue is null, reset the value spec
|
568
|
+
resetValue();
|
569
|
+
}
|
570
|
+
};
|
571
|
+
|
572
|
+
const onKeyDown: React.KeyboardEventHandler<HTMLInputElement> = (event) => {
|
573
|
+
if (event.code === 'Enter') {
|
574
|
+
calculateExpression();
|
575
|
+
inputRef.current?.focus();
|
576
|
+
} else if (event.code === 'Escape') {
|
577
|
+
inputRef.current?.select();
|
578
|
+
}
|
579
|
+
};
|
549
580
|
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
581
|
+
useEffect(() => {
|
582
|
+
if (
|
583
|
+
numericValue !== null &&
|
584
|
+
!isNaN(numericValue) &&
|
585
|
+
numericValue !== valueSelector(valueSpecification)
|
586
|
+
) {
|
587
|
+
const valueFromValueSpec =
|
588
|
+
valueSelector(valueSpecification) !== null
|
589
|
+
? (valueSelector(valueSpecification) as number).toString()
|
590
|
+
: '';
|
591
|
+
setValue(valueFromValueSpec);
|
592
|
+
}
|
593
|
+
}, [numericValue, valueSpecification, valueSelector]);
|
594
|
+
|
595
|
+
const resetButtonName = `reset-${valueSelector(valueSpecification)}`;
|
596
|
+
const inputName = `input-${valueSelector(valueSpecification)}`;
|
597
|
+
const calculateButtonName = `calculate-${valueSelector(valueSpecification)}`;
|
598
|
+
|
599
|
+
const onBlur = (
|
600
|
+
event: React.FocusEvent<HTMLInputElement, HTMLButtonElement>,
|
601
|
+
): void => {
|
602
|
+
if (
|
603
|
+
event.relatedTarget?.name !== resetButtonName &&
|
604
|
+
event.relatedTarget?.name !== inputName &&
|
605
|
+
event.relatedTarget?.name !== calculateButtonName
|
606
|
+
) {
|
607
|
+
handleBlur?.();
|
608
|
+
}
|
609
|
+
};
|
610
|
+
|
611
|
+
return (
|
612
|
+
<div className={clsx('value-spec-editor', className)} onBlur={onBlur}>
|
613
|
+
<div className="value-spec-editor__number__input-container">
|
614
|
+
<input
|
615
|
+
ref={inputRef}
|
616
|
+
className={clsx(
|
617
|
+
'panel__content__form__section__input',
|
618
|
+
'value-spec-editor__input',
|
619
|
+
'value-spec-editor__number__input',
|
620
|
+
{
|
621
|
+
'value-spec-editor__number__input--error':
|
622
|
+
errorChecker?.(valueSpecification),
|
623
|
+
},
|
624
|
+
)}
|
625
|
+
spellCheck={false}
|
626
|
+
type="text" // NOTE: we leave this as text so that we can support expression evaluation
|
627
|
+
inputMode="numeric"
|
628
|
+
value={value}
|
629
|
+
onChange={handleInputChange}
|
630
|
+
onBlur={calculateExpression}
|
631
|
+
onKeyDown={(event: React.KeyboardEvent<HTMLInputElement>) => {
|
632
|
+
onKeyDown(event);
|
633
|
+
handleKeyDown?.(event);
|
634
|
+
}}
|
635
|
+
name={inputName}
|
636
|
+
disabled={readOnly}
|
637
|
+
/>
|
638
|
+
<div className="value-spec-editor__number__actions">
|
639
|
+
<button
|
640
|
+
className="value-spec-editor__number__action"
|
641
|
+
title="Evaluate Expression (Enter)"
|
642
|
+
name={calculateButtonName}
|
643
|
+
onClick={calculateExpression}
|
644
|
+
disabled={readOnly}
|
645
|
+
>
|
646
|
+
<CalculateIcon />
|
647
|
+
</button>
|
586
648
|
</div>
|
587
|
-
<button
|
588
|
-
className="value-spec-editor__reset-btn"
|
589
|
-
name={resetButtonName}
|
590
|
-
title="Reset"
|
591
|
-
onClick={resetValue}
|
592
|
-
>
|
593
|
-
<RefreshIcon />
|
594
|
-
</button>
|
595
649
|
</div>
|
596
|
-
|
597
|
-
|
650
|
+
<button
|
651
|
+
className="value-spec-editor__reset-btn"
|
652
|
+
name={resetButtonName}
|
653
|
+
title="Reset"
|
654
|
+
onClick={resetValue}
|
655
|
+
disabled={readOnly}
|
656
|
+
>
|
657
|
+
<RefreshIcon />
|
658
|
+
</button>
|
659
|
+
</div>
|
660
|
+
);
|
661
|
+
};
|
662
|
+
|
663
|
+
export const NumberPrimitiveInstanceValueEditor = observer(
|
664
|
+
forwardRef(NumberPrimitiveInstanceValueEditorInner) as <T>(
|
665
|
+
props: NumberPrimitiveInstanceValueEditorProps<T> & {
|
666
|
+
ref: React.ForwardedRef<HTMLInputElement>;
|
667
|
+
},
|
668
|
+
) => ReturnType<typeof NumberPrimitiveInstanceValueEditorInner>,
|
598
669
|
);
|
599
670
|
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
valueSpecification,
|
611
|
-
className,
|
612
|
-
resetValue,
|
613
|
-
setValueSpecification,
|
614
|
-
observerContext,
|
615
|
-
handleBlur,
|
616
|
-
} = props;
|
617
|
-
const applicationStore = useApplicationStore();
|
618
|
-
const enumType = guaranteeType(
|
619
|
-
valueSpecification.genericType?.value.rawType,
|
620
|
-
Enumeration,
|
621
|
-
);
|
622
|
-
const enumValue =
|
623
|
-
valueSpecification.values[0] === undefined
|
624
|
-
? null
|
625
|
-
: valueSpecification.values[0].value;
|
626
|
-
const options = enumType.values.map((value) => ({
|
627
|
-
label: value.name,
|
628
|
-
value: value,
|
629
|
-
}));
|
630
|
-
const resetButtonName = `reset-${valueSpecification.hashCode}`;
|
631
|
-
const inputName = `input-${valueSpecification.hashCode}`;
|
671
|
+
/**
|
672
|
+
* Generic interface for handling editing enum values. The editor component
|
673
|
+
* expects an options array which contains the list of possible enum values.
|
674
|
+
*/
|
675
|
+
interface EnumInstanceValueEditorProps<T>
|
676
|
+
extends PrimitiveInstanceValueEditorProps<T, string | null> {
|
677
|
+
options: { label: string; value: string }[];
|
678
|
+
selectorConfig?: BasicValueSpecificationEditorSelectorConfig | undefined;
|
679
|
+
lightMode?: boolean | undefined;
|
680
|
+
}
|
632
681
|
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
682
|
+
// eslint-disable-next-line comma-spacing
|
683
|
+
const EnumInstanceValueEditorInner = <T,>(
|
684
|
+
props: EnumInstanceValueEditorProps<T>,
|
685
|
+
): React.ReactElement => {
|
686
|
+
const {
|
687
|
+
valueSpecification,
|
688
|
+
valueSelector,
|
689
|
+
updateValueSpecification,
|
690
|
+
errorChecker,
|
691
|
+
resetValue,
|
692
|
+
handleBlur,
|
693
|
+
options,
|
694
|
+
className,
|
695
|
+
selectorConfig,
|
696
|
+
lightMode,
|
697
|
+
readOnly,
|
698
|
+
} = props;
|
699
|
+
const enumValue = valueSelector(valueSpecification);
|
700
|
+
const resetButtonName = `reset-${valueSelector(valueSpecification)}`;
|
701
|
+
const inputName = `input-${valueSelector(valueSpecification)}`;
|
702
|
+
|
703
|
+
const changeValue = (val: { value: string; label: string }): void => {
|
704
|
+
updateValueSpecification(valueSpecification, val.value);
|
705
|
+
handleBlur?.();
|
706
|
+
};
|
707
|
+
|
708
|
+
const onBlur = (
|
709
|
+
event: React.FocusEvent<HTMLInputElement, HTMLButtonElement>,
|
710
|
+
): void => {
|
711
|
+
if (
|
712
|
+
event.relatedTarget?.name !== resetButtonName &&
|
713
|
+
event.relatedTarget?.name !== inputName
|
714
|
+
) {
|
641
715
|
handleBlur?.();
|
642
|
-
}
|
716
|
+
}
|
717
|
+
};
|
643
718
|
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
719
|
+
return (
|
720
|
+
<div className={clsx('value-spec-editor', className)} onBlur={onBlur}>
|
721
|
+
<CustomSelectorInput
|
722
|
+
className="value-spec-editor__enum-selector"
|
723
|
+
options={options}
|
724
|
+
onChange={changeValue}
|
725
|
+
value={enumValue ? { value: enumValue, label: enumValue } : null}
|
726
|
+
darkMode={!lightMode}
|
727
|
+
hasError={errorChecker?.(valueSpecification)}
|
728
|
+
placeholder="Select value"
|
729
|
+
autoFocus={true}
|
730
|
+
inputName={inputName}
|
731
|
+
optionCustomization={selectorConfig?.optionCustomization}
|
732
|
+
disabled={readOnly}
|
733
|
+
/>
|
734
|
+
<button
|
735
|
+
className="value-spec-editor__reset-btn"
|
736
|
+
name={resetButtonName}
|
737
|
+
title="Reset"
|
738
|
+
onClick={resetValue}
|
739
|
+
disabled={readOnly}
|
740
|
+
>
|
741
|
+
<RefreshIcon />
|
742
|
+
</button>
|
743
|
+
</div>
|
744
|
+
);
|
745
|
+
};
|
654
746
|
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
options={options}
|
660
|
-
onChange={changeValue}
|
661
|
-
value={enumValue ? { value: enumValue, label: enumValue.name } : null}
|
662
|
-
darkMode={
|
663
|
-
!applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled
|
664
|
-
}
|
665
|
-
hasError={!isValidInstanceValue(valueSpecification)}
|
666
|
-
placeholder="Select value"
|
667
|
-
autoFocus={true}
|
668
|
-
inputName={inputName}
|
669
|
-
/>
|
670
|
-
<button
|
671
|
-
className="value-spec-editor__reset-btn"
|
672
|
-
name={resetButtonName}
|
673
|
-
title="Reset"
|
674
|
-
onClick={resetValue}
|
675
|
-
>
|
676
|
-
<RefreshIcon />
|
677
|
-
</button>
|
678
|
-
</div>
|
679
|
-
);
|
680
|
-
},
|
747
|
+
export const EnumInstanceValueEditor = observer(
|
748
|
+
EnumInstanceValueEditorInner as <T>(
|
749
|
+
props: EnumInstanceValueEditorProps<T>,
|
750
|
+
) => ReturnType<typeof EnumInstanceValueEditorInner>,
|
681
751
|
);
|
682
752
|
|
683
753
|
const stringifyValue = (values: ValueSpecification[]): string => {
|
@@ -698,7 +768,7 @@ const stringifyValue = (values: ValueSpecification[]): string => {
|
|
698
768
|
]).trim();
|
699
769
|
};
|
700
770
|
|
701
|
-
const getPlaceHolder = (expectedType: Type): string => {
|
771
|
+
const getPlaceHolder = (expectedType: Type | V1_PackageableType): string => {
|
702
772
|
if (expectedType instanceof PrimitiveType) {
|
703
773
|
switch (expectedType.path) {
|
704
774
|
case PRIMITIVE_TYPE.DATE:
|
@@ -709,674 +779,749 @@ const getPlaceHolder = (expectedType: Type): string => {
|
|
709
779
|
default:
|
710
780
|
return 'Add';
|
711
781
|
}
|
782
|
+
} else if (expectedType instanceof V1_PackageableType) {
|
783
|
+
switch (expectedType.fullPath) {
|
784
|
+
case PRIMITIVE_TYPE.DATE:
|
785
|
+
case PRIMITIVE_TYPE.STRICTDATE:
|
786
|
+
return 'yyyy-mm-dd';
|
787
|
+
case PRIMITIVE_TYPE.DATETIME:
|
788
|
+
case PRIMITIVE_TYPE.STRICTTIME:
|
789
|
+
return 'yyyy-mm-ddThh:mm:ss';
|
790
|
+
default:
|
791
|
+
return 'Add';
|
792
|
+
}
|
793
|
+
} else {
|
794
|
+
throw new Error(`Cannot get placeholder for type ${expectedType}`);
|
712
795
|
}
|
713
|
-
return 'Add';
|
714
796
|
};
|
715
797
|
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
798
|
+
/**
|
799
|
+
* This is the base interface for collection primitive instance value editors.
|
800
|
+
* The interface is made generic so that it can support various types of objects that hold the value
|
801
|
+
* to be edited (currently, we just use this for CollectionInstanceValue and V1_Collection).
|
802
|
+
*
|
803
|
+
* T represents the type of the objects held in the collection (i.e. ValueSpecification or V1_ValueSpecification).
|
804
|
+
* U represents the interface of the collection object (i.e. CollectionInstanceValue or V1_Collection). Currently,
|
805
|
+
* this only supports collection objects that hold their data in a property called values.
|
806
|
+
*
|
807
|
+
* updateValueSpecification: callback that takes the collection object and the new values and handles updating
|
808
|
+
* the collection object with the new values.
|
809
|
+
* convertTextToValueSpecification: callback that takes a string and converts it to the expected valueSpecification type.
|
810
|
+
* convertValueSpecificationToText: callback that takes a valueSpecification and converts it to a string.
|
811
|
+
* expectedType: the expected type of the values in the collection.
|
812
|
+
* errorChecker: optional callback that should return true if the valueSpecification is invalid.
|
813
|
+
*/
|
814
|
+
interface PrimitiveCollectionInstanceValueEditorProps<
|
815
|
+
T,
|
816
|
+
U extends { values: T[] },
|
817
|
+
> {
|
818
|
+
valueSpecification: U;
|
819
|
+
updateValueSpecification: (valueSpecification: U, values: T[]) => void;
|
820
|
+
convertTextToValueSpecification: (
|
821
|
+
type: Type | V1_PackageableType,
|
822
|
+
text: string,
|
823
|
+
) => T | null;
|
824
|
+
convertValueSpecificationToText: (
|
825
|
+
valueSpecification: T,
|
826
|
+
) => string | undefined;
|
827
|
+
expectedType: Type | V1_PackageableType;
|
828
|
+
saveEdit: () => void;
|
829
|
+
selectorSearchConfig?:
|
830
|
+
| BasicValueSpecificationEditorSelectorSearchConfig
|
721
831
|
| undefined;
|
722
|
-
|
832
|
+
selectorConfig?: BasicValueSpecificationEditorSelectorConfig | undefined;
|
833
|
+
errorChecker?: (valueSpecification: U) => boolean;
|
834
|
+
className?: string | undefined;
|
835
|
+
lightMode?: boolean | undefined;
|
836
|
+
readOnly?: boolean | undefined;
|
723
837
|
}
|
724
838
|
|
725
|
-
const
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
839
|
+
const PrimitiveCollectionInstanceValueEditorInner = <
|
840
|
+
T,
|
841
|
+
U extends { values: T[] },
|
842
|
+
>(
|
843
|
+
props: PrimitiveCollectionInstanceValueEditorProps<T, U>,
|
844
|
+
): React.ReactElement => {
|
845
|
+
const {
|
846
|
+
valueSpecification,
|
847
|
+
convertTextToValueSpecification,
|
848
|
+
convertValueSpecificationToText,
|
849
|
+
updateValueSpecification,
|
850
|
+
saveEdit,
|
851
|
+
selectorSearchConfig,
|
852
|
+
selectorConfig,
|
853
|
+
expectedType,
|
854
|
+
lightMode,
|
855
|
+
readOnly,
|
856
|
+
} = props;
|
740
857
|
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
);
|
858
|
+
// local state and variables
|
859
|
+
const applicationStore = useApplicationStore();
|
860
|
+
const inputRef = useRef(null);
|
861
|
+
const [inputValue, setInputValue] = useState('');
|
862
|
+
const [inputValueIsError, setInputValueIsError] = useState(false);
|
863
|
+
const [selectedOptions, setSelectedOptions] = useState<
|
864
|
+
{ label: string; value: string }[]
|
865
|
+
>(
|
866
|
+
valueSpecification.values
|
867
|
+
.filter((value) => guaranteeNonNullable(value))
|
868
|
+
.map(convertValueSpecificationToText)
|
869
|
+
.filter(isNonEmptyString)
|
870
|
+
.map((value) => ({
|
871
|
+
label: value,
|
872
|
+
value,
|
873
|
+
})),
|
874
|
+
);
|
759
875
|
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
876
|
+
// typehead search setup
|
877
|
+
const isTypeaheadSearchEnabled =
|
878
|
+
expectedType === PrimitiveType.STRING && Boolean(selectorSearchConfig);
|
879
|
+
const reloadValuesFunc = isTypeaheadSearchEnabled
|
880
|
+
? selectorSearchConfig?.reloadValues
|
881
|
+
: undefined;
|
882
|
+
const cleanUpReloadValuesFunc = isTypeaheadSearchEnabled
|
883
|
+
? selectorSearchConfig?.cleanUpReloadValues
|
884
|
+
: undefined;
|
885
|
+
const isLoading = isTypeaheadSearchEnabled
|
886
|
+
? selectorSearchConfig?.isLoading
|
887
|
+
: undefined;
|
888
|
+
const queryOptions =
|
889
|
+
isTypeaheadSearchEnabled && selectorSearchConfig?.values?.length
|
890
|
+
? selectorSearchConfig.values.map((e) => ({
|
891
|
+
value: e,
|
892
|
+
label: e.toString(),
|
893
|
+
}))
|
771
894
|
: undefined;
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
const
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
value:
|
787
|
-
): { label: string; value: string } => {
|
788
|
-
const stringValue = guaranteeNonNullable(
|
789
|
-
getValueSpecificationStringValue(value, applicationStore),
|
790
|
-
);
|
791
|
-
return {
|
792
|
-
label: stringValue,
|
793
|
-
value: stringValue,
|
794
|
-
};
|
895
|
+
const noMatchMessage =
|
896
|
+
isTypeaheadSearchEnabled && isLoading ? 'Loading...' : undefined;
|
897
|
+
const copyButtonName = `copy-${valueSpecification.values[0] ? convertValueSpecificationToText(valueSpecification.values[0]) : ''}`;
|
898
|
+
const inputName = `input-${valueSpecification.values[0] ? convertValueSpecificationToText(valueSpecification.values[0]) : ''}`;
|
899
|
+
|
900
|
+
// helper functions
|
901
|
+
const buildOptionForValueSpec = (
|
902
|
+
value: T,
|
903
|
+
): { label: string; value: string } => {
|
904
|
+
const stringValue = guaranteeNonNullable(
|
905
|
+
convertValueSpecificationToText(value),
|
906
|
+
);
|
907
|
+
return {
|
908
|
+
label: stringValue,
|
909
|
+
value: stringValue,
|
795
910
|
};
|
911
|
+
};
|
796
912
|
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
/**
|
801
|
-
* NOTE: We attempt to be less disruptive here by not throwing errors left and right, instead
|
802
|
-
* we simply return null for values which are not valid or parsable. But perhaps, we can consider
|
803
|
-
* passing in logger or notifier to give the users some idea of what went wrong instead of ignoring
|
804
|
-
* their input.
|
805
|
-
*/
|
806
|
-
const convertInputValueToValueSpec = (): ValueSpecification | null => {
|
807
|
-
const trimmedInputValue = inputValue.trim();
|
808
|
-
|
809
|
-
if (trimmedInputValue.length) {
|
810
|
-
const newValueSpec = convertTextToPrimitiveInstanceValue(
|
811
|
-
expectedType,
|
812
|
-
trimmedInputValue,
|
813
|
-
observerContext,
|
814
|
-
);
|
913
|
+
const isValueAlreadySelected = (value: string): boolean =>
|
914
|
+
selectedOptions.map((option) => option.value).includes(value);
|
815
915
|
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
)
|
825
|
-
) {
|
826
|
-
return null;
|
827
|
-
}
|
828
|
-
|
829
|
-
return newValueSpec;
|
830
|
-
}
|
831
|
-
return null;
|
832
|
-
};
|
916
|
+
/**
|
917
|
+
* NOTE: We attempt to be less disruptive here by not throwing errors left and right, instead
|
918
|
+
* we simply return null for values which are not valid or parsable. But perhaps, we can consider
|
919
|
+
* passing in logger or notifier to give the users some idea of what went wrong instead of ignoring
|
920
|
+
* their input.
|
921
|
+
*/
|
922
|
+
const convertInputValueToValueSpec = (): T | null => {
|
923
|
+
const trimmedInputValue = inputValue.trim();
|
833
924
|
|
834
|
-
|
835
|
-
const newValueSpec =
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
...selectedOptions,
|
840
|
-
buildOptionForValueSpec(newValueSpec),
|
841
|
-
]);
|
842
|
-
setInputValue('');
|
843
|
-
reloadValuesFunc?.cancel();
|
844
|
-
} else if (inputValue.trim().length) {
|
845
|
-
setInputValueIsError(true);
|
846
|
-
}
|
847
|
-
};
|
925
|
+
if (trimmedInputValue.length) {
|
926
|
+
const newValueSpec = convertTextToValueSpecification(
|
927
|
+
expectedType,
|
928
|
+
trimmedInputValue,
|
929
|
+
);
|
848
930
|
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
if (actionChange.action === 'select-option') {
|
856
|
-
setInputValue('');
|
857
|
-
} else if (
|
858
|
-
actionChange.action === 'remove-value' &&
|
859
|
-
actionChange.removedValue.value === inputValue
|
931
|
+
if (
|
932
|
+
newValueSpec === null ||
|
933
|
+
convertValueSpecificationToText(newValueSpec) === undefined ||
|
934
|
+
isValueAlreadySelected(
|
935
|
+
guaranteeNonNullable(convertValueSpecificationToText(newValueSpec)),
|
936
|
+
)
|
860
937
|
) {
|
861
|
-
|
938
|
+
return null;
|
862
939
|
}
|
863
|
-
};
|
864
|
-
|
865
|
-
const handleInputChange = (
|
866
|
-
newInputValue: string,
|
867
|
-
actionChange: InputActionData,
|
868
|
-
): void => {
|
869
|
-
if (actionChange.action === 'input-change') {
|
870
|
-
setInputValue(newInputValue);
|
871
|
-
setInputValueIsError(false);
|
872
|
-
reloadValuesFunc?.cancel();
|
873
|
-
const reloadValuesFuncTransformation =
|
874
|
-
reloadValuesFunc?.(newInputValue);
|
875
|
-
if (reloadValuesFuncTransformation) {
|
876
|
-
flowResult(reloadValuesFuncTransformation).catch(
|
877
|
-
applicationStore.alertUnhandledError,
|
878
|
-
);
|
879
|
-
}
|
880
|
-
}
|
881
|
-
if (actionChange.action === 'input-blur') {
|
882
|
-
reloadValuesFunc?.cancel();
|
883
|
-
cleanUpReloadValuesFunc?.();
|
884
|
-
}
|
885
|
-
};
|
886
940
|
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
const updateValueSpecAndSaveEdit = (): void => {
|
893
|
-
const newValueSpec = convertInputValueToValueSpec();
|
894
|
-
const finalSelectedOptions =
|
895
|
-
newValueSpec !== null
|
896
|
-
? [...selectedOptions, buildOptionForValueSpec(newValueSpec)]
|
897
|
-
: selectedOptions;
|
898
|
-
instanceValue_setValues(
|
899
|
-
valueSpecification,
|
900
|
-
finalSelectedOptions
|
901
|
-
.map((option) => option.value)
|
902
|
-
.map((value) =>
|
903
|
-
convertTextToPrimitiveInstanceValue(
|
904
|
-
expectedType,
|
905
|
-
value,
|
906
|
-
observerContext,
|
907
|
-
),
|
908
|
-
)
|
909
|
-
.filter(isNonNullable),
|
910
|
-
observerContext,
|
911
|
-
);
|
912
|
-
saveEdit();
|
913
|
-
};
|
941
|
+
return newValueSpec;
|
942
|
+
}
|
943
|
+
return null;
|
944
|
+
};
|
914
945
|
|
915
|
-
|
916
|
-
|
917
|
-
) => {
|
918
|
-
if ((event.key === 'Enter' || event.key === ',') && !event.shiftKey) {
|
919
|
-
addInputValueToSelectedOptions();
|
920
|
-
event.preventDefault();
|
921
|
-
}
|
922
|
-
};
|
946
|
+
const addInputValueToSelectedOptions = (): void => {
|
947
|
+
const newValueSpec = convertInputValueToValueSpec();
|
923
948
|
|
924
|
-
|
925
|
-
event,
|
926
|
-
) => {
|
927
|
-
const pastedText = event.clipboardData.getData('text');
|
928
|
-
const parsedData = parseCSVString(pastedText);
|
929
|
-
if (!parsedData) {
|
930
|
-
return;
|
931
|
-
}
|
932
|
-
const newValues = uniq(
|
933
|
-
uniq(parsedData)
|
934
|
-
.map((value) => {
|
935
|
-
const newValueSpec = convertTextToPrimitiveInstanceValue(
|
936
|
-
expectedType,
|
937
|
-
value,
|
938
|
-
observerContext,
|
939
|
-
);
|
940
|
-
return newValueSpec
|
941
|
-
? getValueSpecificationStringValue(newValueSpec, applicationStore)
|
942
|
-
: null;
|
943
|
-
})
|
944
|
-
.filter(isNonNullable),
|
945
|
-
).filter((value) => !isValueAlreadySelected(value));
|
949
|
+
if (newValueSpec !== null) {
|
946
950
|
setSelectedOptions([
|
947
951
|
...selectedOptions,
|
948
|
-
|
952
|
+
buildOptionForValueSpec(newValueSpec),
|
949
953
|
]);
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
|
957
|
-
|
958
|
-
|
959
|
-
|
960
|
-
|
954
|
+
setInputValue('');
|
955
|
+
reloadValuesFunc?.cancel();
|
956
|
+
} else if (inputValue.trim().length) {
|
957
|
+
setInputValueIsError(true);
|
958
|
+
}
|
959
|
+
};
|
960
|
+
|
961
|
+
// event handlers
|
962
|
+
const changeValue = (
|
963
|
+
newSelectedOptions: { value: string; label: string }[],
|
964
|
+
actionChange: SelectActionData<{ value: string; label: string }>,
|
965
|
+
): void => {
|
966
|
+
setSelectedOptions(newSelectedOptions);
|
967
|
+
if (actionChange.action === 'select-option') {
|
968
|
+
setInputValue('');
|
969
|
+
} else if (
|
970
|
+
actionChange.action === 'remove-value' &&
|
971
|
+
actionChange.removedValue.value === inputValue
|
972
|
+
) {
|
973
|
+
setInputValueIsError(false);
|
974
|
+
}
|
975
|
+
};
|
976
|
+
|
977
|
+
const handleInputChange = (
|
978
|
+
newInputValue: string,
|
979
|
+
actionChange: InputActionData,
|
980
|
+
): void => {
|
981
|
+
if (actionChange.action === 'input-change') {
|
982
|
+
setInputValue(newInputValue);
|
983
|
+
setInputValueIsError(false);
|
984
|
+
reloadValuesFunc?.cancel();
|
985
|
+
const reloadValuesFuncTransformation = reloadValuesFunc?.(newInputValue);
|
986
|
+
if (reloadValuesFuncTransformation) {
|
987
|
+
flowResult(reloadValuesFuncTransformation).catch(
|
988
|
+
applicationStore.alertUnhandledError,
|
989
|
+
);
|
961
990
|
}
|
962
|
-
}
|
991
|
+
}
|
992
|
+
if (actionChange.action === 'input-blur') {
|
993
|
+
reloadValuesFunc?.cancel();
|
994
|
+
cleanUpReloadValuesFunc?.();
|
995
|
+
}
|
996
|
+
};
|
963
997
|
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
className={clsx('value-spec-editor__primitive-collection-selector', {
|
968
|
-
'value-spec-editor__primitive-collection-selector--error':
|
969
|
-
inputValueIsError,
|
970
|
-
})}
|
971
|
-
options={queryOptions}
|
972
|
-
inputValue={inputValue}
|
973
|
-
isMulti={true}
|
974
|
-
menuIsOpen={
|
975
|
-
isTypeaheadSearchEnabled &&
|
976
|
-
inputValue.length >= DEFAULT_TYPEAHEAD_SEARCH_MINIMUM_SEARCH_LENGTH
|
977
|
-
}
|
978
|
-
autoFocus={true}
|
979
|
-
inputRef={inputRef}
|
980
|
-
onChange={changeValue}
|
981
|
-
onInputChange={handleInputChange}
|
982
|
-
onKeyDown={handleKeyDown}
|
983
|
-
onPaste={handlePaste}
|
984
|
-
value={selectedOptions}
|
985
|
-
darkMode={
|
986
|
-
!applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled
|
987
|
-
}
|
988
|
-
isLoading={isLoading}
|
989
|
-
noMatchMessage={noMatchMessage}
|
990
|
-
placeholder={getPlaceHolder(expectedType)}
|
991
|
-
components={{
|
992
|
-
DropdownIndicator: null,
|
993
|
-
}}
|
994
|
-
inputName={inputName}
|
995
|
-
/>
|
996
|
-
<button
|
997
|
-
className="value-spec-editor__list-editor__copy-button"
|
998
|
-
// eslint-disable-next-line no-void
|
999
|
-
onClick={() => void copyValueToClipboard()}
|
1000
|
-
name={copyButtonName}
|
1001
|
-
title="Copy values to clipboard"
|
1002
|
-
>
|
1003
|
-
<CopyIcon />
|
1004
|
-
</button>
|
1005
|
-
<button
|
1006
|
-
className="value-spec-editor__list-editor__save-button btn--dark"
|
1007
|
-
name="Save"
|
1008
|
-
title="Save"
|
1009
|
-
onClick={updateValueSpecAndSaveEdit}
|
1010
|
-
>
|
1011
|
-
<SaveIcon />
|
1012
|
-
</button>
|
1013
|
-
</div>
|
998
|
+
const copyValueToClipboard = async () =>
|
999
|
+
navigator.clipboard.writeText(
|
1000
|
+
selectedOptions.map((option) => option.value).join(','),
|
1014
1001
|
);
|
1015
|
-
|
1002
|
+
|
1003
|
+
const updateValueSpecAndSaveEdit = (): void => {
|
1004
|
+
const newValueSpec = convertInputValueToValueSpec();
|
1005
|
+
const finalSelectedOptions =
|
1006
|
+
newValueSpec !== null
|
1007
|
+
? [...selectedOptions, buildOptionForValueSpec(newValueSpec)]
|
1008
|
+
: selectedOptions;
|
1009
|
+
const finalFormattedSelectedOptions = finalSelectedOptions
|
1010
|
+
.map((option) => option.value)
|
1011
|
+
.map((value) => convertTextToValueSpecification(expectedType, value))
|
1012
|
+
.filter(isNonNullable);
|
1013
|
+
updateValueSpecification(valueSpecification, finalFormattedSelectedOptions);
|
1014
|
+
saveEdit();
|
1015
|
+
};
|
1016
|
+
|
1017
|
+
const handleKeyDown: React.KeyboardEventHandler<HTMLDivElement> = (event) => {
|
1018
|
+
if ((event.key === 'Enter' || event.key === ',') && !event.shiftKey) {
|
1019
|
+
addInputValueToSelectedOptions();
|
1020
|
+
event.preventDefault();
|
1021
|
+
}
|
1022
|
+
};
|
1023
|
+
|
1024
|
+
const handlePaste: React.ClipboardEventHandler<HTMLInputElement> = (
|
1025
|
+
event,
|
1026
|
+
) => {
|
1027
|
+
const pastedText = event.clipboardData.getData('text');
|
1028
|
+
const parsedData = parseCSVString(pastedText);
|
1029
|
+
if (!parsedData) {
|
1030
|
+
return;
|
1031
|
+
}
|
1032
|
+
const newValues = uniq(
|
1033
|
+
uniq(parsedData)
|
1034
|
+
.map((value) => {
|
1035
|
+
const newValueSpec = convertTextToValueSpecification(
|
1036
|
+
expectedType,
|
1037
|
+
value,
|
1038
|
+
);
|
1039
|
+
return newValueSpec
|
1040
|
+
? convertValueSpecificationToText(newValueSpec)
|
1041
|
+
: null;
|
1042
|
+
})
|
1043
|
+
.filter(isNonNullable),
|
1044
|
+
).filter((value) => !isValueAlreadySelected(value));
|
1045
|
+
setSelectedOptions([
|
1046
|
+
...selectedOptions,
|
1047
|
+
...newValues.map((value) => ({ label: value, value })),
|
1048
|
+
]);
|
1049
|
+
event.preventDefault();
|
1050
|
+
};
|
1051
|
+
|
1052
|
+
const onBlur = (
|
1053
|
+
event: React.FocusEvent<HTMLInputElement, HTMLButtonElement>,
|
1054
|
+
): void => {
|
1055
|
+
if (
|
1056
|
+
event.relatedTarget?.name !== copyButtonName &&
|
1057
|
+
event.relatedTarget?.name !== inputName
|
1058
|
+
) {
|
1059
|
+
updateValueSpecAndSaveEdit();
|
1060
|
+
}
|
1061
|
+
};
|
1062
|
+
|
1063
|
+
return (
|
1064
|
+
<div className="value-spec-editor" onBlur={onBlur}>
|
1065
|
+
<CustomSelectorInput
|
1066
|
+
className={clsx('value-spec-editor__primitive-collection-selector', {
|
1067
|
+
'value-spec-editor__primitive-collection-selector--error':
|
1068
|
+
inputValueIsError,
|
1069
|
+
})}
|
1070
|
+
options={queryOptions}
|
1071
|
+
inputValue={inputValue}
|
1072
|
+
isMulti={true}
|
1073
|
+
menuIsOpen={
|
1074
|
+
isTypeaheadSearchEnabled &&
|
1075
|
+
inputValue.length >= DEFAULT_TYPEAHEAD_SEARCH_MINIMUM_SEARCH_LENGTH
|
1076
|
+
}
|
1077
|
+
autoFocus={true}
|
1078
|
+
inputRef={inputRef}
|
1079
|
+
onChange={changeValue}
|
1080
|
+
onInputChange={handleInputChange}
|
1081
|
+
onKeyDown={handleKeyDown}
|
1082
|
+
onPaste={handlePaste}
|
1083
|
+
value={selectedOptions}
|
1084
|
+
darkMode={!lightMode}
|
1085
|
+
isLoading={isLoading}
|
1086
|
+
noMatchMessage={noMatchMessage}
|
1087
|
+
placeholder={getPlaceHolder(expectedType)}
|
1088
|
+
components={{
|
1089
|
+
DropdownIndicator: null,
|
1090
|
+
}}
|
1091
|
+
inputName={inputName}
|
1092
|
+
optionCustomization={selectorConfig?.optionCustomization}
|
1093
|
+
disabled={readOnly}
|
1094
|
+
/>
|
1095
|
+
<button
|
1096
|
+
className="value-spec-editor__list-editor__copy-button"
|
1097
|
+
// eslint-disable-next-line no-void
|
1098
|
+
onClick={() => void copyValueToClipboard()}
|
1099
|
+
name={copyButtonName}
|
1100
|
+
title="Copy values to clipboard"
|
1101
|
+
>
|
1102
|
+
<CopyIcon />
|
1103
|
+
</button>
|
1104
|
+
<button
|
1105
|
+
className="value-spec-editor__list-editor__save-button btn--dark"
|
1106
|
+
name="Save"
|
1107
|
+
title="Save"
|
1108
|
+
onClick={updateValueSpecAndSaveEdit}
|
1109
|
+
disabled={readOnly}
|
1110
|
+
>
|
1111
|
+
<SaveIcon />
|
1112
|
+
</button>
|
1113
|
+
</div>
|
1114
|
+
);
|
1115
|
+
};
|
1116
|
+
|
1117
|
+
export const PrimitiveCollectionInstanceValueEditor = observer(
|
1118
|
+
PrimitiveCollectionInstanceValueEditorInner as <T, U extends { values: T[] }>(
|
1119
|
+
props: PrimitiveCollectionInstanceValueEditorProps<T, U>,
|
1120
|
+
) => ReturnType<typeof PrimitiveCollectionInstanceValueEditorInner>,
|
1016
1121
|
);
|
1017
1122
|
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
saveEdit: () => void;
|
1023
|
-
}) => {
|
1024
|
-
const { valueSpecification, observerContext, saveEdit } = props;
|
1123
|
+
interface EnumCollectionInstanceValueEditorProps<T, U extends { values: T[] }>
|
1124
|
+
extends PrimitiveCollectionInstanceValueEditorProps<T, U> {
|
1125
|
+
enumOptions: { label: string; value: string }[] | undefined;
|
1126
|
+
}
|
1025
1127
|
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
value: at(valueSpec.values, 0).value,
|
1042
|
-
})),
|
1043
|
-
);
|
1128
|
+
const EnumCollectionInstanceValueEditorInner = <T, U extends { values: T[] }>(
|
1129
|
+
props: EnumCollectionInstanceValueEditorProps<T, U>,
|
1130
|
+
): React.ReactElement => {
|
1131
|
+
const {
|
1132
|
+
valueSpecification,
|
1133
|
+
convertTextToValueSpecification,
|
1134
|
+
convertValueSpecificationToText,
|
1135
|
+
updateValueSpecification,
|
1136
|
+
saveEdit,
|
1137
|
+
expectedType,
|
1138
|
+
enumOptions,
|
1139
|
+
selectorConfig,
|
1140
|
+
lightMode,
|
1141
|
+
readOnly,
|
1142
|
+
} = props;
|
1044
1143
|
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
1144
|
+
guaranteeNonNullable(
|
1145
|
+
enumOptions,
|
1146
|
+
'Must pass enum options to EnumCollectionInstanceValueEditor',
|
1147
|
+
);
|
1148
|
+
|
1149
|
+
// local state and variables
|
1150
|
+
const [inputValue, setInputValue] = useState('');
|
1151
|
+
const [inputValueIsError, setInputValueIsError] = useState(false);
|
1152
|
+
const [selectedOptions, setSelectedOptions] = useState<
|
1153
|
+
{ label: string; value: string }[]
|
1154
|
+
>(
|
1155
|
+
valueSpecification.values
|
1156
|
+
.filter((value) => guaranteeNonNullable(value))
|
1157
|
+
.map(convertValueSpecificationToText)
|
1158
|
+
.filter(isNonEmptyString)
|
1052
1159
|
.map((value) => ({
|
1053
|
-
label: value
|
1054
|
-
value
|
1055
|
-
}))
|
1056
|
-
|
1057
|
-
const copyButtonName = `copy-${valueSpecification.hashCode}`;
|
1058
|
-
const inputName = `input-${valueSpecification.hashCode}`;
|
1059
|
-
|
1060
|
-
// helper functions
|
1061
|
-
const isValueAlreadySelected = (value: Enum): boolean =>
|
1062
|
-
selectedOptions.map((option) => option.value).includes(value);
|
1063
|
-
|
1064
|
-
/**
|
1065
|
-
* NOTE: We attempt to be less disruptive here by not throwing errors left and right, instead
|
1066
|
-
* we simply return null for values which are not valid or parsable. But perhaps, we can consider
|
1067
|
-
* passing in logger or notifier to give the users some idea of what went wrong instead of ignoring
|
1068
|
-
* their input.
|
1069
|
-
*/
|
1070
|
-
const convertInputValueToEnum = (): Enum | null => {
|
1071
|
-
const trimmedInputValue = inputValue.trim();
|
1072
|
-
|
1073
|
-
if (trimmedInputValue.length) {
|
1074
|
-
const newEnum = convertTextToEnum(trimmedInputValue, enumType);
|
1075
|
-
|
1076
|
-
if (newEnum === undefined || isValueAlreadySelected(newEnum)) {
|
1077
|
-
return null;
|
1078
|
-
}
|
1160
|
+
label: value,
|
1161
|
+
value,
|
1162
|
+
})),
|
1163
|
+
);
|
1079
1164
|
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1083
|
-
|
1165
|
+
const availableOptions = enumOptions?.filter(
|
1166
|
+
(value) =>
|
1167
|
+
!selectedOptions.some(
|
1168
|
+
(selectedValue) => selectedValue.value === value.value,
|
1169
|
+
),
|
1170
|
+
);
|
1084
1171
|
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1088
|
-
if (newEnum !== null) {
|
1089
|
-
setSelectedOptions([
|
1090
|
-
...selectedOptions,
|
1091
|
-
{
|
1092
|
-
label: newEnum.name,
|
1093
|
-
value: newEnum,
|
1094
|
-
},
|
1095
|
-
]);
|
1096
|
-
setInputValue('');
|
1097
|
-
} else if (inputValue.trim().length) {
|
1098
|
-
setInputValueIsError(true);
|
1099
|
-
}
|
1100
|
-
};
|
1172
|
+
const copyButtonName = `copy-${valueSpecification.values[0] ? convertValueSpecificationToText(valueSpecification.values[0]) : ''}`;
|
1173
|
+
const inputName = `input-${valueSpecification.values[0] ? convertValueSpecificationToText(valueSpecification.values[0]) : ''}`;
|
1101
1174
|
|
1102
|
-
|
1103
|
-
|
1104
|
-
|
1105
|
-
actionChange: SelectActionData<{ value: Enum; label: string }>,
|
1106
|
-
): void => {
|
1107
|
-
setSelectedOptions(newSelectedOptions);
|
1108
|
-
if (actionChange.action === 'select-option') {
|
1109
|
-
setInputValue('');
|
1110
|
-
} else if (
|
1111
|
-
actionChange.action === 'remove-value' &&
|
1112
|
-
actionChange.removedValue.value.name === inputValue
|
1113
|
-
) {
|
1114
|
-
setInputValueIsError(false);
|
1115
|
-
}
|
1116
|
-
};
|
1175
|
+
// helper functions
|
1176
|
+
const isValueAlreadySelected = (value: string): boolean =>
|
1177
|
+
selectedOptions.map((option) => option.value).includes(value);
|
1117
1178
|
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1121
|
-
|
1122
|
-
|
1123
|
-
|
1124
|
-
|
1125
|
-
|
1126
|
-
};
|
1179
|
+
/**
|
1180
|
+
* NOTE: We attempt to be less disruptive here by not throwing errors left and right, instead
|
1181
|
+
* we simply return null for values which are not valid or parsable. But perhaps, we can consider
|
1182
|
+
* passing in logger or notifier to give the users some idea of what went wrong instead of ignoring
|
1183
|
+
* their input.
|
1184
|
+
*/
|
1185
|
+
const convertInputValueToEnum = (): string | null => {
|
1186
|
+
const trimmedInputValue = inputValue.trim();
|
1127
1187
|
|
1128
|
-
|
1129
|
-
|
1130
|
-
|
1131
|
-
|
1132
|
-
|
1133
|
-
|
1134
|
-
|
1135
|
-
};
|
1188
|
+
if (
|
1189
|
+
!trimmedInputValue.length ||
|
1190
|
+
isValueAlreadySelected(trimmedInputValue) ||
|
1191
|
+
!enumOptions?.some((option) => option.value === trimmedInputValue)
|
1192
|
+
) {
|
1193
|
+
return null;
|
1194
|
+
}
|
1136
1195
|
|
1137
|
-
|
1138
|
-
|
1139
|
-
|
1140
|
-
|
1141
|
-
|
1142
|
-
|
1143
|
-
|
1144
|
-
}
|
1145
|
-
const newValues = uniq(
|
1146
|
-
uniq(parsedData)
|
1147
|
-
.map((value) => convertTextToEnum(value, enumType))
|
1148
|
-
.filter(isNonNullable),
|
1149
|
-
).filter((value) => !isValueAlreadySelected(value));
|
1196
|
+
return trimmedInputValue;
|
1197
|
+
};
|
1198
|
+
|
1199
|
+
const addInputValueToSelectedOptions = (): void => {
|
1200
|
+
const newEnum = convertInputValueToEnum();
|
1201
|
+
|
1202
|
+
if (newEnum !== null) {
|
1150
1203
|
setSelectedOptions([
|
1151
1204
|
...selectedOptions,
|
1152
|
-
|
1205
|
+
{
|
1206
|
+
label: newEnum,
|
1207
|
+
value: newEnum,
|
1208
|
+
},
|
1153
1209
|
]);
|
1154
|
-
|
1155
|
-
}
|
1210
|
+
setInputValue('');
|
1211
|
+
} else if (inputValue.trim().length) {
|
1212
|
+
setInputValueIsError(true);
|
1213
|
+
}
|
1214
|
+
};
|
1215
|
+
|
1216
|
+
// event handlers
|
1217
|
+
const changeValue = (
|
1218
|
+
newSelectedOptions: { value: string; label: string }[],
|
1219
|
+
actionChange: SelectActionData<{ value: string; label: string }>,
|
1220
|
+
): void => {
|
1221
|
+
setSelectedOptions(newSelectedOptions);
|
1222
|
+
if (actionChange.action === 'select-option') {
|
1223
|
+
setInputValue('');
|
1224
|
+
} else if (
|
1225
|
+
actionChange.action === 'remove-value' &&
|
1226
|
+
actionChange.removedValue.value === inputValue
|
1227
|
+
) {
|
1228
|
+
setInputValueIsError(false);
|
1229
|
+
}
|
1230
|
+
};
|
1231
|
+
|
1232
|
+
const handleInputChange = (
|
1233
|
+
newInputValue: string,
|
1234
|
+
actionChange: InputActionData,
|
1235
|
+
): void => {
|
1236
|
+
if (actionChange.action === 'input-change') {
|
1237
|
+
setInputValue(newInputValue);
|
1238
|
+
setInputValueIsError(false);
|
1239
|
+
}
|
1240
|
+
};
|
1156
1241
|
|
1157
|
-
|
1158
|
-
|
1159
|
-
|
1160
|
-
);
|
1242
|
+
const handleKeyDown: React.KeyboardEventHandler<HTMLDivElement> = (event) => {
|
1243
|
+
if ((event.key === 'Enter' || event.key === ',') && !event.shiftKey) {
|
1244
|
+
addInputValueToSelectedOptions();
|
1245
|
+
event.preventDefault();
|
1246
|
+
}
|
1247
|
+
};
|
1248
|
+
|
1249
|
+
const handlePaste: React.ClipboardEventHandler<HTMLInputElement> = (
|
1250
|
+
event,
|
1251
|
+
) => {
|
1252
|
+
const pastedText = event.clipboardData.getData('text');
|
1253
|
+
const parsedData = parseCSVString(pastedText);
|
1254
|
+
if (!parsedData) {
|
1255
|
+
return;
|
1256
|
+
}
|
1257
|
+
const newValues = uniq(
|
1258
|
+
uniq(parsedData).filter((value) =>
|
1259
|
+
enumOptions?.some((option) => option.value === value),
|
1260
|
+
),
|
1261
|
+
).filter((value) => !isValueAlreadySelected(value));
|
1262
|
+
setSelectedOptions([
|
1263
|
+
...selectedOptions,
|
1264
|
+
...newValues.map((value) => ({ label: value, value })),
|
1265
|
+
]);
|
1266
|
+
event.preventDefault();
|
1267
|
+
};
|
1268
|
+
|
1269
|
+
const copyValueToClipboard = async () =>
|
1270
|
+
navigator.clipboard.writeText(
|
1271
|
+
selectedOptions.map((option) => option.value).join(','),
|
1272
|
+
);
|
1161
1273
|
|
1162
|
-
|
1163
|
-
|
1164
|
-
|
1165
|
-
|
1166
|
-
|
1167
|
-
|
1168
|
-
|
1169
|
-
|
1170
|
-
|
1171
|
-
|
1172
|
-
|
1173
|
-
|
1174
|
-
|
1175
|
-
|
1176
|
-
|
1177
|
-
|
1178
|
-
|
1274
|
+
const updateValueSpecAndSaveEdit = (): void => {
|
1275
|
+
const result = selectedOptions
|
1276
|
+
.map((option) => option.value)
|
1277
|
+
.map((value) => convertTextToValueSpecification(expectedType, value))
|
1278
|
+
.filter(isNonNullable);
|
1279
|
+
updateValueSpecification(valueSpecification, result);
|
1280
|
+
saveEdit();
|
1281
|
+
};
|
1282
|
+
|
1283
|
+
const onBlur = (
|
1284
|
+
event: React.FocusEvent<HTMLInputElement, HTMLButtonElement>,
|
1285
|
+
): void => {
|
1286
|
+
if (
|
1287
|
+
event.relatedTarget?.name !== copyButtonName &&
|
1288
|
+
event.relatedTarget?.name !== inputName
|
1289
|
+
) {
|
1290
|
+
updateValueSpecAndSaveEdit();
|
1291
|
+
}
|
1292
|
+
};
|
1179
1293
|
|
1180
|
-
|
1181
|
-
|
1182
|
-
|
1183
|
-
|
1184
|
-
|
1185
|
-
|
1186
|
-
|
1187
|
-
|
1188
|
-
|
1189
|
-
|
1294
|
+
return (
|
1295
|
+
<div className="value-spec-editor" onBlur={onBlur}>
|
1296
|
+
<CustomSelectorInput
|
1297
|
+
className={clsx('value-spec-editor__enum-collection-selector', {
|
1298
|
+
'value-spec-editor__enum-collection-selector--error':
|
1299
|
+
inputValueIsError,
|
1300
|
+
})}
|
1301
|
+
options={availableOptions}
|
1302
|
+
inputValue={inputValue}
|
1303
|
+
isMulti={true}
|
1304
|
+
autoFocus={true}
|
1305
|
+
onChange={changeValue}
|
1306
|
+
onInputChange={handleInputChange}
|
1307
|
+
onKeyDown={handleKeyDown}
|
1308
|
+
onPaste={handlePaste}
|
1309
|
+
value={selectedOptions}
|
1310
|
+
darkMode={!lightMode}
|
1311
|
+
placeholder="Add"
|
1312
|
+
menuIsOpen={true}
|
1313
|
+
inputName={inputName}
|
1314
|
+
optionCustomization={selectorConfig?.optionCustomization}
|
1315
|
+
disabled={readOnly}
|
1316
|
+
/>
|
1317
|
+
<button
|
1318
|
+
className="value-spec-editor__list-editor__copy-button"
|
1319
|
+
// eslint-disable-next-line no-void
|
1320
|
+
onClick={() => void copyValueToClipboard()}
|
1321
|
+
name={copyButtonName}
|
1322
|
+
title="Copy values to clipboard"
|
1323
|
+
>
|
1324
|
+
<CopyIcon />
|
1325
|
+
</button>
|
1326
|
+
<button
|
1327
|
+
className="value-spec-editor__list-editor__save-button btn--dark"
|
1328
|
+
name="Save"
|
1329
|
+
title="Save"
|
1330
|
+
onClick={updateValueSpecAndSaveEdit}
|
1331
|
+
disabled={readOnly}
|
1332
|
+
>
|
1333
|
+
<SaveIcon />
|
1334
|
+
</button>
|
1335
|
+
</div>
|
1336
|
+
);
|
1337
|
+
};
|
1190
1338
|
|
1191
|
-
|
1192
|
-
|
1193
|
-
|
1194
|
-
|
1195
|
-
'value-spec-editor__enum-collection-selector--error':
|
1196
|
-
inputValueIsError,
|
1197
|
-
})}
|
1198
|
-
options={availableOptions}
|
1199
|
-
inputValue={inputValue}
|
1200
|
-
isMulti={true}
|
1201
|
-
autoFocus={true}
|
1202
|
-
onChange={changeValue}
|
1203
|
-
onInputChange={handleInputChange}
|
1204
|
-
onKeyDown={handleKeyDown}
|
1205
|
-
onPaste={handlePaste}
|
1206
|
-
value={selectedOptions}
|
1207
|
-
darkMode={
|
1208
|
-
!applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled
|
1209
|
-
}
|
1210
|
-
placeholder="Add"
|
1211
|
-
menuIsOpen={true}
|
1212
|
-
inputName={inputName}
|
1213
|
-
/>
|
1214
|
-
<button
|
1215
|
-
className="value-spec-editor__list-editor__copy-button"
|
1216
|
-
// eslint-disable-next-line no-void
|
1217
|
-
onClick={() => void copyValueToClipboard()}
|
1218
|
-
name={copyButtonName}
|
1219
|
-
title="Copy values to clipboard"
|
1220
|
-
>
|
1221
|
-
<CopyIcon />
|
1222
|
-
</button>
|
1223
|
-
<button
|
1224
|
-
className="value-spec-editor__list-editor__save-button btn--dark"
|
1225
|
-
name="Save"
|
1226
|
-
title="Save"
|
1227
|
-
onClick={updateValueSpecAndSaveEdit}
|
1228
|
-
>
|
1229
|
-
<SaveIcon />
|
1230
|
-
</button>
|
1231
|
-
</div>
|
1232
|
-
);
|
1233
|
-
},
|
1339
|
+
export const EnumCollectionInstanceValueEditor = observer(
|
1340
|
+
EnumCollectionInstanceValueEditorInner as <T, U extends { values: T[] }>(
|
1341
|
+
props: EnumCollectionInstanceValueEditorProps<T, U>,
|
1342
|
+
) => ReturnType<typeof EnumCollectionInstanceValueEditorInner>,
|
1234
1343
|
);
|
1235
1344
|
|
1236
1345
|
const COLLECTION_PREVIEW_CHAR_LIMIT = 50;
|
1237
1346
|
|
1238
|
-
|
1239
|
-
|
1240
|
-
|
1241
|
-
|
1242
|
-
|
1243
|
-
className?: string | undefined;
|
1244
|
-
setValueSpecification: (val: ValueSpecification) => void;
|
1245
|
-
selectorConfig?: BasicValueSpecificationEditorSelectorConfig | undefined;
|
1246
|
-
observerContext: ObserverContext;
|
1247
|
-
}) => {
|
1248
|
-
const {
|
1249
|
-
valueSpecification,
|
1250
|
-
expectedType,
|
1251
|
-
className,
|
1252
|
-
setValueSpecification,
|
1253
|
-
selectorConfig,
|
1254
|
-
observerContext,
|
1255
|
-
} = props;
|
1347
|
+
interface CollectionValueInstanceValueEditorProps<T, U extends { values: T[] }>
|
1348
|
+
extends Omit<PrimitiveCollectionInstanceValueEditorProps<T, U>, 'saveEdit'>,
|
1349
|
+
Omit<EnumCollectionInstanceValueEditorProps<T, U>, 'saveEdit'> {
|
1350
|
+
stringifyCollectionValueSpecification: (valueSpecification: U) => string;
|
1351
|
+
}
|
1256
1352
|
|
1257
|
-
|
1258
|
-
|
1259
|
-
|
1260
|
-
|
1261
|
-
|
1262
|
-
|
1263
|
-
|
1264
|
-
|
1265
|
-
|
1266
|
-
|
1267
|
-
|
1268
|
-
|
1269
|
-
|
1270
|
-
|
1271
|
-
|
1272
|
-
|
1273
|
-
|
1274
|
-
|
1275
|
-
setEditable(false);
|
1276
|
-
setValueSpecification(valueSpecification);
|
1277
|
-
}
|
1278
|
-
};
|
1353
|
+
const CollectionValueInstanceValueEditorInner = <T, U extends { values: T[] }>(
|
1354
|
+
props: CollectionValueInstanceValueEditorProps<T, U>,
|
1355
|
+
): React.ReactElement => {
|
1356
|
+
const {
|
1357
|
+
valueSpecification,
|
1358
|
+
convertTextToValueSpecification,
|
1359
|
+
convertValueSpecificationToText,
|
1360
|
+
updateValueSpecification,
|
1361
|
+
stringifyCollectionValueSpecification,
|
1362
|
+
errorChecker,
|
1363
|
+
className,
|
1364
|
+
selectorSearchConfig,
|
1365
|
+
selectorConfig,
|
1366
|
+
expectedType,
|
1367
|
+
enumOptions,
|
1368
|
+
lightMode,
|
1369
|
+
readOnly,
|
1370
|
+
} = props;
|
1279
1371
|
|
1372
|
+
const [editable, setEditable] = useState(false);
|
1373
|
+
const valueText = stringifyCollectionValueSpecification(valueSpecification);
|
1374
|
+
const previewText = `List(${
|
1375
|
+
valueSpecification.values.length === 0
|
1376
|
+
? 'empty'
|
1377
|
+
: valueSpecification.values.length
|
1378
|
+
})${
|
1379
|
+
valueSpecification.values.length === 0
|
1380
|
+
? ''
|
1381
|
+
: `: ${
|
1382
|
+
valueText.length > COLLECTION_PREVIEW_CHAR_LIMIT
|
1383
|
+
? `${valueText.substring(0, COLLECTION_PREVIEW_CHAR_LIMIT)}...`
|
1384
|
+
: valueText
|
1385
|
+
}`
|
1386
|
+
}`;
|
1387
|
+
const enableEdit = (): void => setEditable(true);
|
1388
|
+
const saveEdit = (): void => {
|
1280
1389
|
if (editable) {
|
1281
|
-
|
1282
|
-
<>
|
1283
|
-
<div className={clsx('value-spec-editor', className)}>
|
1284
|
-
{expectedType instanceof Enumeration ? (
|
1285
|
-
<EnumCollectionInstanceValueEditor
|
1286
|
-
valueSpecification={valueSpecification}
|
1287
|
-
observerContext={observerContext}
|
1288
|
-
saveEdit={saveEdit}
|
1289
|
-
/>
|
1290
|
-
) : (
|
1291
|
-
<PrimitiveCollectionInstanceValueEditor
|
1292
|
-
valueSpecification={valueSpecification}
|
1293
|
-
expectedType={expectedType}
|
1294
|
-
saveEdit={saveEdit}
|
1295
|
-
selectorConfig={selectorConfig}
|
1296
|
-
observerContext={observerContext}
|
1297
|
-
/>
|
1298
|
-
)}
|
1299
|
-
</div>
|
1300
|
-
</>
|
1301
|
-
);
|
1390
|
+
setEditable(false);
|
1302
1391
|
}
|
1392
|
+
};
|
1393
|
+
|
1394
|
+
if (editable) {
|
1303
1395
|
return (
|
1396
|
+
<>
|
1397
|
+
<div className={clsx('value-spec-editor', className)}>
|
1398
|
+
{enumOptions !== undefined ? (
|
1399
|
+
<EnumCollectionInstanceValueEditor<T, U>
|
1400
|
+
valueSpecification={valueSpecification}
|
1401
|
+
updateValueSpecification={updateValueSpecification}
|
1402
|
+
convertTextToValueSpecification={convertTextToValueSpecification}
|
1403
|
+
convertValueSpecificationToText={convertValueSpecificationToText}
|
1404
|
+
expectedType={expectedType}
|
1405
|
+
saveEdit={saveEdit}
|
1406
|
+
enumOptions={enumOptions}
|
1407
|
+
selectorConfig={selectorConfig}
|
1408
|
+
lightMode={lightMode}
|
1409
|
+
readOnly={readOnly}
|
1410
|
+
/>
|
1411
|
+
) : (
|
1412
|
+
<PrimitiveCollectionInstanceValueEditor<T, U>
|
1413
|
+
valueSpecification={valueSpecification}
|
1414
|
+
updateValueSpecification={updateValueSpecification}
|
1415
|
+
convertTextToValueSpecification={convertTextToValueSpecification}
|
1416
|
+
convertValueSpecificationToText={convertValueSpecificationToText}
|
1417
|
+
expectedType={expectedType}
|
1418
|
+
saveEdit={saveEdit}
|
1419
|
+
selectorSearchConfig={selectorSearchConfig}
|
1420
|
+
selectorConfig={selectorConfig}
|
1421
|
+
lightMode={lightMode}
|
1422
|
+
readOnly={readOnly}
|
1423
|
+
/>
|
1424
|
+
)}
|
1425
|
+
</div>
|
1426
|
+
</>
|
1427
|
+
);
|
1428
|
+
}
|
1429
|
+
return (
|
1430
|
+
<div
|
1431
|
+
className={clsx('value-spec-editor', className)}
|
1432
|
+
onClick={readOnly ? () => {} : enableEdit}
|
1433
|
+
title={readOnly ? '' : 'Click to edit'}
|
1434
|
+
style={{ cursor: readOnly ? 'not-allowed' : '' }}
|
1435
|
+
>
|
1304
1436
|
<div
|
1305
|
-
className={clsx('value-spec-
|
1306
|
-
|
1307
|
-
|
1437
|
+
className={clsx('value-spec-editor__list-editor__preview', {
|
1438
|
+
'value-spec-editor__list-editor__preview--error':
|
1439
|
+
errorChecker?.(valueSpecification),
|
1440
|
+
})}
|
1308
1441
|
>
|
1309
|
-
|
1310
|
-
className={clsx('value-spec-editor__list-editor__preview', {
|
1311
|
-
'value-spec-editor__list-editor__preview--error':
|
1312
|
-
!isValidInstanceValue(valueSpecification),
|
1313
|
-
})}
|
1314
|
-
>
|
1315
|
-
{previewText}
|
1316
|
-
</div>
|
1317
|
-
<button className="value-spec-editor__list-editor__edit-icon">
|
1318
|
-
<PencilIcon />
|
1319
|
-
</button>
|
1442
|
+
{previewText}
|
1320
1443
|
</div>
|
1321
|
-
|
1322
|
-
|
1444
|
+
<button className="value-spec-editor__list-editor__edit-icon">
|
1445
|
+
<PencilIcon />
|
1446
|
+
</button>
|
1447
|
+
</div>
|
1448
|
+
);
|
1449
|
+
};
|
1450
|
+
|
1451
|
+
export const CollectionValueInstanceValueEditor = observer(
|
1452
|
+
CollectionValueInstanceValueEditorInner as <T, U extends { values: T[] }>(
|
1453
|
+
props: CollectionValueInstanceValueEditorProps<T, U>,
|
1454
|
+
) => ReturnType<typeof CollectionValueInstanceValueEditorInner>,
|
1323
1455
|
);
|
1324
1456
|
|
1325
1457
|
const UnsupportedValueSpecificationEditor: React.FC = () => (
|
1326
1458
|
<div className="value-spec-editor--unsupported">unsupported</div>
|
1327
1459
|
);
|
1328
1460
|
|
1329
|
-
|
1330
|
-
|
1331
|
-
|
1332
|
-
|
1333
|
-
|
1334
|
-
|
1335
|
-
|
1336
|
-
|
1337
|
-
|
1338
|
-
|
1339
|
-
displayAsEditableValue?: boolean | undefined;
|
1340
|
-
}) => {
|
1341
|
-
const {
|
1342
|
-
valueSpecification,
|
1343
|
-
setValueSpecification,
|
1344
|
-
graph,
|
1345
|
-
observerContext,
|
1346
|
-
typeCheckOption,
|
1347
|
-
resetValue,
|
1348
|
-
handleBlur,
|
1349
|
-
displayAsEditableValue,
|
1350
|
-
} = props;
|
1461
|
+
interface DateInstanceValueEditorProps<
|
1462
|
+
T extends CustomDatePickerValueSpecification | undefined,
|
1463
|
+
> extends Omit<
|
1464
|
+
PrimitiveInstanceValueEditorProps<T, string | null>,
|
1465
|
+
'updateValueSpecification'
|
1466
|
+
> {
|
1467
|
+
updateValueSpecification: CustomDatePickerUpdateValueSpecification<T>;
|
1468
|
+
typeCheckOption: TypeCheckOption | V1_TypeCheckOption;
|
1469
|
+
displayAsEditableValue?: boolean | undefined;
|
1470
|
+
}
|
1351
1471
|
|
1352
|
-
|
1353
|
-
|
1354
|
-
|
1355
|
-
|
1356
|
-
|
1357
|
-
|
1358
|
-
|
1359
|
-
|
1360
|
-
|
1361
|
-
|
1362
|
-
|
1363
|
-
|
1364
|
-
|
1365
|
-
|
1366
|
-
|
1367
|
-
|
1368
|
-
|
1369
|
-
|
1370
|
-
|
1371
|
-
|
1372
|
-
|
1373
|
-
|
1374
|
-
|
1375
|
-
|
1376
|
-
|
1377
|
-
|
1378
|
-
|
1379
|
-
|
1472
|
+
const DateInstanceValueEditorInner = <
|
1473
|
+
T extends CustomDatePickerValueSpecification | undefined,
|
1474
|
+
>(
|
1475
|
+
props: DateInstanceValueEditorProps<T>,
|
1476
|
+
): React.ReactElement => {
|
1477
|
+
const {
|
1478
|
+
valueSpecification,
|
1479
|
+
valueSelector,
|
1480
|
+
updateValueSpecification,
|
1481
|
+
resetValue,
|
1482
|
+
handleBlur,
|
1483
|
+
typeCheckOption,
|
1484
|
+
displayAsEditableValue,
|
1485
|
+
className,
|
1486
|
+
readOnly,
|
1487
|
+
} = props;
|
1488
|
+
|
1489
|
+
return (
|
1490
|
+
<div className={clsx('value-spec-editor', className)}>
|
1491
|
+
<CustomDatePicker<T>
|
1492
|
+
valueSpecification={valueSpecification}
|
1493
|
+
valueSelector={valueSelector}
|
1494
|
+
typeCheckOption={typeCheckOption}
|
1495
|
+
updateValueSpecification={updateValueSpecification}
|
1496
|
+
hasError={
|
1497
|
+
valueSpecification instanceof PrimitiveInstanceValue &&
|
1498
|
+
!isValidInstanceValue(valueSpecification)
|
1499
|
+
}
|
1500
|
+
handleBlur={handleBlur}
|
1501
|
+
displayAsEditableValue={displayAsEditableValue}
|
1502
|
+
readOnly={readOnly}
|
1503
|
+
/>
|
1504
|
+
{!displayAsEditableValue && (
|
1505
|
+
<button
|
1506
|
+
className="value-spec-editor__reset-btn"
|
1507
|
+
name="Reset"
|
1508
|
+
title="Reset"
|
1509
|
+
onClick={resetValue}
|
1510
|
+
disabled={readOnly}
|
1511
|
+
>
|
1512
|
+
<RefreshIcon />
|
1513
|
+
</button>
|
1514
|
+
)}
|
1515
|
+
</div>
|
1516
|
+
);
|
1517
|
+
};
|
1518
|
+
|
1519
|
+
export const DateInstanceValueEditor = observer(
|
1520
|
+
DateInstanceValueEditorInner as <
|
1521
|
+
T extends CustomDatePickerValueSpecification | undefined,
|
1522
|
+
>(
|
1523
|
+
props: DateInstanceValueEditorProps<T>,
|
1524
|
+
) => ReturnType<typeof DateInstanceValueEditorInner>,
|
1380
1525
|
);
|
1381
1526
|
|
1382
1527
|
/**
|
@@ -1396,14 +1541,18 @@ export const BasicValueSpecificationEditor = forwardRef<
|
|
1396
1541
|
setValueSpecification: (val: ValueSpecification) => void;
|
1397
1542
|
resetValue: () => void;
|
1398
1543
|
isConstant?: boolean | undefined;
|
1544
|
+
selectorSearchConfig?:
|
1545
|
+
| BasicValueSpecificationEditorSelectorSearchConfig
|
1546
|
+
| undefined;
|
1399
1547
|
selectorConfig?: BasicValueSpecificationEditorSelectorConfig | undefined;
|
1400
1548
|
handleBlur?: (() => void) | undefined;
|
1401
1549
|
handleKeyDown?:
|
1402
1550
|
| ((event: React.KeyboardEvent<HTMLInputElement>) => void)
|
1403
1551
|
| undefined;
|
1404
1552
|
displayDateEditorAsEditableValue?: boolean | undefined;
|
1553
|
+
readOnly?: boolean | undefined;
|
1405
1554
|
}
|
1406
|
-
>(function
|
1555
|
+
>(function BasicValueSpecificationEditorInner(props, ref) {
|
1407
1556
|
const {
|
1408
1557
|
className,
|
1409
1558
|
valueSpecification,
|
@@ -1412,37 +1561,134 @@ export const BasicValueSpecificationEditor = forwardRef<
|
|
1412
1561
|
typeCheckOption,
|
1413
1562
|
setValueSpecification,
|
1414
1563
|
resetValue,
|
1564
|
+
selectorSearchConfig,
|
1415
1565
|
selectorConfig,
|
1416
1566
|
isConstant,
|
1417
1567
|
handleBlur,
|
1418
1568
|
handleKeyDown,
|
1419
1569
|
displayDateEditorAsEditableValue,
|
1570
|
+
readOnly,
|
1420
1571
|
} = props;
|
1572
|
+
|
1573
|
+
const applicationStore = useApplicationStore();
|
1574
|
+
|
1575
|
+
const errorChecker = (_valueSpecification: InstanceValue) =>
|
1576
|
+
!isValidInstanceValue(_valueSpecification);
|
1577
|
+
const dateValueSelector = (
|
1578
|
+
_valueSpecification: SimpleFunctionExpression | PrimitiveInstanceValue,
|
1579
|
+
): string | null => {
|
1580
|
+
return _valueSpecification instanceof SimpleFunctionExpression
|
1581
|
+
? ''
|
1582
|
+
: (_valueSpecification.values[0] as string | null);
|
1583
|
+
};
|
1584
|
+
const dateUpdateValueSpecification: CustomDatePickerUpdateValueSpecification<
|
1585
|
+
SimpleFunctionExpression | PrimitiveInstanceValue | undefined
|
1586
|
+
> = (_valueSpecification, value, options): void => {
|
1587
|
+
if (value instanceof CustomDateOption) {
|
1588
|
+
setValueSpecification(
|
1589
|
+
buildPureAdjustDateFunction(value, graph, observerContext),
|
1590
|
+
);
|
1591
|
+
} else if (value instanceof CustomFirstDayOfOption) {
|
1592
|
+
setValueSpecification(
|
1593
|
+
buildPureDateFunctionExpression(value, graph, observerContext),
|
1594
|
+
);
|
1595
|
+
} else if (value instanceof CustomPreviousDayOfWeekOption) {
|
1596
|
+
setValueSpecification(
|
1597
|
+
buildPureDateFunctionExpression(value, graph, observerContext),
|
1598
|
+
);
|
1599
|
+
} else if (value instanceof DatePickerOption) {
|
1600
|
+
setValueSpecification(
|
1601
|
+
buildPureDateFunctionExpression(value, graph, observerContext),
|
1602
|
+
);
|
1603
|
+
} else {
|
1604
|
+
if (_valueSpecification instanceof SimpleFunctionExpression) {
|
1605
|
+
setValueSpecification(
|
1606
|
+
buildPrimitiveInstanceValue(
|
1607
|
+
graph,
|
1608
|
+
guaranteeNonNullable(options?.primitiveTypeEnum),
|
1609
|
+
value,
|
1610
|
+
observerContext,
|
1611
|
+
),
|
1612
|
+
);
|
1613
|
+
} else if (_valueSpecification instanceof InstanceValue) {
|
1614
|
+
instanceValue_setValue(_valueSpecification, value, 0, observerContext);
|
1615
|
+
if (
|
1616
|
+
_valueSpecification.genericType.value.rawType.path !==
|
1617
|
+
guaranteeNonNullable(options?.primitiveTypeEnum)
|
1618
|
+
) {
|
1619
|
+
valueSpecification_setGenericType(
|
1620
|
+
_valueSpecification,
|
1621
|
+
GenericTypeExplicitReference.create(
|
1622
|
+
new GenericType(
|
1623
|
+
getPrimitiveTypeInstanceFromEnum(
|
1624
|
+
guaranteeNonNullable(options?.primitiveTypeEnum),
|
1625
|
+
),
|
1626
|
+
),
|
1627
|
+
),
|
1628
|
+
);
|
1629
|
+
}
|
1630
|
+
setValueSpecification(_valueSpecification);
|
1631
|
+
} else if (options?.primitiveTypeEnum === PRIMITIVE_TYPE.LATESTDATE) {
|
1632
|
+
setValueSpecification(
|
1633
|
+
buildPrimitiveInstanceValue(
|
1634
|
+
graph,
|
1635
|
+
PRIMITIVE_TYPE.LATESTDATE,
|
1636
|
+
value,
|
1637
|
+
observerContext,
|
1638
|
+
),
|
1639
|
+
);
|
1640
|
+
}
|
1641
|
+
}
|
1642
|
+
};
|
1643
|
+
|
1421
1644
|
if (valueSpecification instanceof PrimitiveInstanceValue) {
|
1422
1645
|
const _type = valueSpecification.genericType.value.rawType;
|
1646
|
+
|
1647
|
+
// eslint-disable-next-line comma-spacing
|
1648
|
+
const valueSelector = <T,>(val: PrimitiveInstanceValue): T =>
|
1649
|
+
val.values[0] as T;
|
1650
|
+
// eslint-disable-next-line comma-spacing
|
1651
|
+
const updateValueSpecification = <T,>(
|
1652
|
+
_valueSpecification: PrimitiveInstanceValue,
|
1653
|
+
value: T,
|
1654
|
+
) => {
|
1655
|
+
instanceValue_setValue(_valueSpecification, value, 0, observerContext);
|
1656
|
+
setValueSpecification(_valueSpecification);
|
1657
|
+
};
|
1423
1658
|
switch (_type.path) {
|
1424
1659
|
case PRIMITIVE_TYPE.STRING:
|
1425
1660
|
return (
|
1426
|
-
<StringPrimitiveInstanceValueEditor
|
1661
|
+
<StringPrimitiveInstanceValueEditor<PrimitiveInstanceValue>
|
1427
1662
|
valueSpecification={valueSpecification}
|
1428
|
-
|
1663
|
+
valueSelector={valueSelector}
|
1664
|
+
updateValueSpecification={updateValueSpecification}
|
1665
|
+
errorChecker={errorChecker}
|
1429
1666
|
className={className}
|
1430
1667
|
resetValue={resetValue}
|
1668
|
+
selectorSearchConfig={selectorSearchConfig}
|
1431
1669
|
selectorConfig={selectorConfig}
|
1432
|
-
|
1433
|
-
|
1670
|
+
ref={
|
1671
|
+
ref as React.ForwardedRef<
|
1672
|
+
HTMLInputElement | SelectComponent | null
|
1673
|
+
>
|
1674
|
+
}
|
1434
1675
|
handleBlur={handleBlur}
|
1435
1676
|
handleKeyDown={handleKeyDown}
|
1677
|
+
lightMode={
|
1678
|
+
applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled
|
1679
|
+
}
|
1680
|
+
readOnly={readOnly}
|
1436
1681
|
/>
|
1437
1682
|
);
|
1438
1683
|
case PRIMITIVE_TYPE.BOOLEAN:
|
1439
1684
|
return (
|
1440
|
-
<BooleanPrimitiveInstanceValueEditor
|
1685
|
+
<BooleanPrimitiveInstanceValueEditor<PrimitiveInstanceValue>
|
1441
1686
|
valueSpecification={valueSpecification}
|
1442
|
-
|
1687
|
+
valueSelector={valueSelector}
|
1688
|
+
updateValueSpecification={updateValueSpecification}
|
1443
1689
|
className={className}
|
1444
1690
|
resetValue={resetValue}
|
1445
|
-
|
1691
|
+
readOnly={readOnly}
|
1446
1692
|
/>
|
1447
1693
|
);
|
1448
1694
|
case PRIMITIVE_TYPE.NUMBER:
|
@@ -1452,16 +1698,18 @@ export const BasicValueSpecificationEditor = forwardRef<
|
|
1452
1698
|
case PRIMITIVE_TYPE.BYTE:
|
1453
1699
|
case PRIMITIVE_TYPE.INTEGER:
|
1454
1700
|
return (
|
1455
|
-
<NumberPrimitiveInstanceValueEditor
|
1701
|
+
<NumberPrimitiveInstanceValueEditor<PrimitiveInstanceValue>
|
1456
1702
|
valueSpecification={valueSpecification}
|
1703
|
+
valueSelector={valueSelector}
|
1457
1704
|
isInteger={_type.path === PRIMITIVE_TYPE.INTEGER}
|
1458
|
-
|
1705
|
+
updateValueSpecification={updateValueSpecification}
|
1706
|
+
errorChecker={errorChecker}
|
1459
1707
|
className={className}
|
1460
1708
|
resetValue={resetValue}
|
1461
|
-
observerContext={observerContext}
|
1462
1709
|
ref={ref}
|
1463
1710
|
handleBlur={handleBlur}
|
1464
1711
|
handleKeyDown={handleKeyDown}
|
1712
|
+
readOnly={readOnly}
|
1465
1713
|
/>
|
1466
1714
|
);
|
1467
1715
|
case PRIMITIVE_TYPE.DATE:
|
@@ -1469,48 +1717,159 @@ export const BasicValueSpecificationEditor = forwardRef<
|
|
1469
1717
|
case PRIMITIVE_TYPE.DATETIME:
|
1470
1718
|
case PRIMITIVE_TYPE.LATESTDATE:
|
1471
1719
|
return (
|
1472
|
-
<DateInstanceValueEditor
|
1720
|
+
<DateInstanceValueEditor<
|
1721
|
+
SimpleFunctionExpression | PrimitiveInstanceValue
|
1722
|
+
>
|
1473
1723
|
valueSpecification={valueSpecification}
|
1474
|
-
|
1475
|
-
observerContext={observerContext}
|
1724
|
+
valueSelector={dateValueSelector}
|
1476
1725
|
typeCheckOption={typeCheckOption}
|
1477
1726
|
className={className}
|
1478
|
-
|
1727
|
+
updateValueSpecification={dateUpdateValueSpecification}
|
1479
1728
|
resetValue={resetValue}
|
1480
1729
|
handleBlur={handleBlur}
|
1481
1730
|
displayAsEditableValue={displayDateEditorAsEditableValue}
|
1731
|
+
errorChecker={(_valueSpecification) =>
|
1732
|
+
_valueSpecification instanceof PrimitiveInstanceValue &&
|
1733
|
+
errorChecker(_valueSpecification)
|
1734
|
+
}
|
1735
|
+
readOnly={readOnly}
|
1482
1736
|
/>
|
1483
1737
|
);
|
1484
1738
|
default:
|
1485
1739
|
return <UnsupportedValueSpecificationEditor />;
|
1486
1740
|
}
|
1487
1741
|
} else if (valueSpecification instanceof EnumValueInstanceValue) {
|
1742
|
+
const enumType = guaranteeType(
|
1743
|
+
valueSpecification.genericType?.value.rawType,
|
1744
|
+
Enumeration,
|
1745
|
+
);
|
1746
|
+
const options = enumType.values.map((value) => ({
|
1747
|
+
label: value.name,
|
1748
|
+
value: value.name,
|
1749
|
+
}));
|
1488
1750
|
return (
|
1489
|
-
<
|
1751
|
+
<EnumInstanceValueEditor<EnumValueInstanceValue>
|
1490
1752
|
valueSpecification={valueSpecification}
|
1753
|
+
valueSelector={(val) =>
|
1754
|
+
val.values[0] === undefined ? null : val.values[0].value.name
|
1755
|
+
}
|
1756
|
+
options={options}
|
1491
1757
|
className={className}
|
1492
1758
|
resetValue={resetValue}
|
1493
|
-
|
1494
|
-
|
1759
|
+
updateValueSpecification={(
|
1760
|
+
_valueSpecification: EnumValueInstanceValue,
|
1761
|
+
value: string | null,
|
1762
|
+
) => {
|
1763
|
+
const enumValue = guaranteeNonNullable(
|
1764
|
+
enumType.values.find((val: Enum) => val.name === value),
|
1765
|
+
`Unable to find enum value ${value} in enumeration ${enumType.name}`,
|
1766
|
+
);
|
1767
|
+
instanceValue_setValue(
|
1768
|
+
_valueSpecification,
|
1769
|
+
EnumValueExplicitReference.create(enumValue),
|
1770
|
+
0,
|
1771
|
+
observerContext,
|
1772
|
+
);
|
1773
|
+
setValueSpecification(_valueSpecification);
|
1774
|
+
}}
|
1775
|
+
errorChecker={(_valueSpecification: EnumValueInstanceValue) =>
|
1776
|
+
!isValidInstanceValue(_valueSpecification)
|
1777
|
+
}
|
1495
1778
|
handleBlur={handleBlur}
|
1779
|
+
selectorConfig={selectorConfig}
|
1780
|
+
lightMode={
|
1781
|
+
applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled
|
1782
|
+
}
|
1783
|
+
readOnly={readOnly}
|
1496
1784
|
/>
|
1497
1785
|
);
|
1498
1786
|
} else if (
|
1499
1787
|
valueSpecification instanceof CollectionInstanceValue &&
|
1500
1788
|
valueSpecification.genericType
|
1501
1789
|
) {
|
1790
|
+
const updateValueSpecification = (
|
1791
|
+
collectionValueSpecification: CollectionInstanceValue,
|
1792
|
+
valueSpecifications: ValueSpecification[],
|
1793
|
+
) => {
|
1794
|
+
instanceValue_setValues(
|
1795
|
+
collectionValueSpecification,
|
1796
|
+
valueSpecifications,
|
1797
|
+
observerContext,
|
1798
|
+
);
|
1799
|
+
setValueSpecification(collectionValueSpecification);
|
1800
|
+
};
|
1801
|
+
const convertTextToValueSpecification = (
|
1802
|
+
type: Type | V1_PackageableType,
|
1803
|
+
text: string,
|
1804
|
+
): ValueSpecification | null => {
|
1805
|
+
if (type instanceof Enumeration) {
|
1806
|
+
const enumValue = convertTextToEnum(text, type);
|
1807
|
+
if (enumValue) {
|
1808
|
+
const enumValueInstanceValue = new EnumValueInstanceValue(
|
1809
|
+
GenericTypeExplicitReference.create(new GenericType(type)),
|
1810
|
+
);
|
1811
|
+
instanceValue_setValues(
|
1812
|
+
enumValueInstanceValue,
|
1813
|
+
[EnumValueExplicitReference.create(enumValue)],
|
1814
|
+
observerContext,
|
1815
|
+
);
|
1816
|
+
return observe_ValueSpecification(
|
1817
|
+
enumValueInstanceValue,
|
1818
|
+
observerContext,
|
1819
|
+
);
|
1820
|
+
}
|
1821
|
+
} else {
|
1822
|
+
const primitiveVal = convertTextToPrimitiveInstanceValue(
|
1823
|
+
guaranteeType(type, Type),
|
1824
|
+
text,
|
1825
|
+
observerContext,
|
1826
|
+
);
|
1827
|
+
if (primitiveVal) {
|
1828
|
+
return observe_ValueSpecification(primitiveVal, observerContext);
|
1829
|
+
}
|
1830
|
+
}
|
1831
|
+
return null;
|
1832
|
+
};
|
1833
|
+
const enumOptions =
|
1834
|
+
typeCheckOption.expectedType instanceof Enumeration
|
1835
|
+
? typeCheckOption.expectedType.values.map((enumValue) => ({
|
1836
|
+
label: enumValue.name,
|
1837
|
+
value: enumValue.name,
|
1838
|
+
}))
|
1839
|
+
: undefined;
|
1502
1840
|
// NOTE: since when we fill in the arguments, `[]` (or `nullish` value in Pure)
|
1503
1841
|
// is used for parameters we don't handle, we should not attempt to support empty collection
|
1504
1842
|
// without generic type here as that is equivalent to `[]`
|
1505
1843
|
return (
|
1506
|
-
<CollectionValueInstanceValueEditor
|
1844
|
+
<CollectionValueInstanceValueEditor<
|
1845
|
+
ValueSpecification,
|
1846
|
+
CollectionInstanceValue
|
1847
|
+
>
|
1507
1848
|
valueSpecification={valueSpecification}
|
1508
|
-
|
1849
|
+
updateValueSpecification={updateValueSpecification}
|
1509
1850
|
expectedType={typeCheckOption.expectedType}
|
1510
1851
|
className={className}
|
1511
|
-
|
1852
|
+
selectorSearchConfig={selectorSearchConfig}
|
1512
1853
|
selectorConfig={selectorConfig}
|
1513
|
-
|
1854
|
+
stringifyCollectionValueSpecification={(
|
1855
|
+
collectionValueSpecification: CollectionInstanceValue,
|
1856
|
+
) => stringifyValue(collectionValueSpecification.values)}
|
1857
|
+
errorChecker={errorChecker}
|
1858
|
+
convertValueSpecificationToText={(
|
1859
|
+
_valueSpecification: ValueSpecification,
|
1860
|
+
) =>
|
1861
|
+
getValueSpecificationStringValue(
|
1862
|
+
_valueSpecification,
|
1863
|
+
applicationStore,
|
1864
|
+
{ omitEnumOwnerName: true },
|
1865
|
+
)
|
1866
|
+
}
|
1867
|
+
convertTextToValueSpecification={convertTextToValueSpecification}
|
1868
|
+
enumOptions={enumOptions}
|
1869
|
+
lightMode={
|
1870
|
+
applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled
|
1871
|
+
}
|
1872
|
+
readOnly={readOnly}
|
1514
1873
|
/>
|
1515
1874
|
);
|
1516
1875
|
}
|
@@ -1536,22 +1895,27 @@ export const BasicValueSpecificationEditor = forwardRef<
|
|
1536
1895
|
handleBlur={handleBlur}
|
1537
1896
|
handleKeyDown={handleKeyDown}
|
1538
1897
|
displayDateEditorAsEditableValue={displayDateEditorAsEditableValue}
|
1898
|
+
selectorSearchConfig={selectorSearchConfig}
|
1899
|
+
selectorConfig={selectorConfig}
|
1900
|
+
readOnly={readOnly}
|
1539
1901
|
/>
|
1540
1902
|
);
|
1541
1903
|
} else if (valueSpecification instanceof SimpleFunctionExpression) {
|
1542
1904
|
if (isSubType(typeCheckOption.expectedType, PrimitiveType.DATE)) {
|
1543
1905
|
if (isUsedDateFunctionSupportedInFormMode(valueSpecification)) {
|
1544
1906
|
return (
|
1545
|
-
<DateInstanceValueEditor
|
1907
|
+
<DateInstanceValueEditor<
|
1908
|
+
SimpleFunctionExpression | PrimitiveInstanceValue
|
1909
|
+
>
|
1546
1910
|
valueSpecification={valueSpecification}
|
1547
|
-
|
1548
|
-
observerContext={observerContext}
|
1911
|
+
valueSelector={dateValueSelector}
|
1549
1912
|
typeCheckOption={typeCheckOption}
|
1550
1913
|
className={className}
|
1551
|
-
|
1914
|
+
updateValueSpecification={dateUpdateValueSpecification}
|
1552
1915
|
resetValue={resetValue}
|
1553
1916
|
handleBlur={handleBlur}
|
1554
1917
|
displayAsEditableValue={displayDateEditorAsEditableValue}
|
1918
|
+
readOnly={readOnly}
|
1555
1919
|
/>
|
1556
1920
|
);
|
1557
1921
|
} else {
|
@@ -1580,17 +1944,29 @@ export const BasicValueSpecificationEditor = forwardRef<
|
|
1580
1944
|
return (
|
1581
1945
|
<NumberPrimitiveInstanceValueEditor
|
1582
1946
|
valueSpecification={simplifiedValue}
|
1947
|
+
valueSelector={(val) => val.values[0] as number}
|
1583
1948
|
isInteger={
|
1584
1949
|
simplifiedValue.genericType.value.rawType ===
|
1585
1950
|
PrimitiveType.INTEGER
|
1586
1951
|
}
|
1587
|
-
|
1952
|
+
updateValueSpecification={(
|
1953
|
+
_valueSpecification: PrimitiveInstanceValue,
|
1954
|
+
value: number | null,
|
1955
|
+
) => {
|
1956
|
+
instanceValue_setValue(
|
1957
|
+
_valueSpecification,
|
1958
|
+
value,
|
1959
|
+
0,
|
1960
|
+
observerContext,
|
1961
|
+
);
|
1962
|
+
setValueSpecification(_valueSpecification);
|
1963
|
+
}}
|
1588
1964
|
className={className}
|
1589
1965
|
resetValue={resetValue}
|
1590
|
-
observerContext={observerContext}
|
1591
1966
|
ref={ref}
|
1592
1967
|
handleBlur={handleBlur}
|
1593
1968
|
handleKeyDown={handleKeyDown}
|
1969
|
+
readOnly={readOnly}
|
1594
1970
|
/>
|
1595
1971
|
);
|
1596
1972
|
}
|
@@ -1608,9 +1984,13 @@ export const EditableBasicValueSpecificationEditor = observer(
|
|
1608
1984
|
observerContext: ObserverContext;
|
1609
1985
|
typeCheckOption: TypeCheckOption;
|
1610
1986
|
resetValue: () => void;
|
1987
|
+
selectorSearchConfig?:
|
1988
|
+
| BasicValueSpecificationEditorSelectorSearchConfig
|
1989
|
+
| undefined;
|
1611
1990
|
selectorConfig?: BasicValueSpecificationEditorSelectorConfig | undefined;
|
1612
1991
|
isConstant?: boolean;
|
1613
1992
|
initializeAsEditable?: boolean;
|
1993
|
+
readOnly?: boolean;
|
1614
1994
|
}) => {
|
1615
1995
|
const {
|
1616
1996
|
valueSpecification,
|
@@ -1619,9 +1999,11 @@ export const EditableBasicValueSpecificationEditor = observer(
|
|
1619
1999
|
observerContext,
|
1620
2000
|
typeCheckOption,
|
1621
2001
|
resetValue,
|
2002
|
+
selectorSearchConfig,
|
1622
2003
|
selectorConfig,
|
1623
2004
|
isConstant,
|
1624
2005
|
initializeAsEditable,
|
2006
|
+
readOnly,
|
1625
2007
|
} = props;
|
1626
2008
|
const applicationStore = useApplicationStore();
|
1627
2009
|
|
@@ -1664,6 +2046,7 @@ export const EditableBasicValueSpecificationEditor = observer(
|
|
1664
2046
|
observerContext={observerContext}
|
1665
2047
|
typeCheckOption={typeCheckOption}
|
1666
2048
|
resetValue={resetValue}
|
2049
|
+
selectorSearchConfig={selectorSearchConfig}
|
1667
2050
|
selectorConfig={selectorConfig}
|
1668
2051
|
isConstant={isConstant}
|
1669
2052
|
ref={inputRef}
|
@@ -1674,6 +2057,7 @@ export const EditableBasicValueSpecificationEditor = observer(
|
|
1674
2057
|
}
|
1675
2058
|
}}
|
1676
2059
|
displayDateEditorAsEditableValue={true}
|
2060
|
+
readOnly={readOnly}
|
1677
2061
|
/>
|
1678
2062
|
) : (
|
1679
2063
|
<div className="value-spec-editor__editable__display">
|
@@ -1686,9 +2070,14 @@ export const EditableBasicValueSpecificationEditor = observer(
|
|
1686
2070
|
!isValidInstanceValue(valueSpecification),
|
1687
2071
|
},
|
1688
2072
|
)}
|
1689
|
-
onClick={
|
1690
|
-
|
1691
|
-
|
2073
|
+
onClick={
|
2074
|
+
readOnly
|
2075
|
+
? () => {}
|
2076
|
+
: () => {
|
2077
|
+
setIsEditingValue(true);
|
2078
|
+
}
|
2079
|
+
}
|
2080
|
+
style={{ cursor: readOnly ? 'not-allowed' : '' }}
|
1692
2081
|
>
|
1693
2082
|
{`"${valueSpecStringValue !== undefined ? valueSpecStringValue : ''}"`}
|
1694
2083
|
</span>
|