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