@finos/legend-query-builder 4.14.39 → 4.14.41

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. package/lib/components/QueryBuilder.d.ts.map +1 -1
  2. package/lib/components/QueryBuilder.js +1 -1
  3. package/lib/components/QueryBuilder.js.map +1 -1
  4. package/lib/components/QueryBuilderParametersPanel.d.ts.map +1 -1
  5. package/lib/components/QueryBuilderParametersPanel.js +20 -2
  6. package/lib/components/QueryBuilderParametersPanel.js.map +1 -1
  7. package/lib/components/QueryBuilderSideBar.d.ts.map +1 -1
  8. package/lib/components/QueryBuilderSideBar.js +5 -14
  9. package/lib/components/QueryBuilderSideBar.js.map +1 -1
  10. package/lib/components/fetch-structure/QueryBuilderTDSPanel.d.ts.map +1 -1
  11. package/lib/components/fetch-structure/QueryBuilderTDSPanel.js +12 -4
  12. package/lib/components/fetch-structure/QueryBuilderTDSPanel.js.map +1 -1
  13. package/lib/components/filter/QueryBuilderFilterPanel.d.ts.map +1 -1
  14. package/lib/components/filter/QueryBuilderFilterPanel.js +6 -2
  15. package/lib/components/filter/QueryBuilderFilterPanel.js.map +1 -1
  16. package/lib/components/result/QueryBuilderResultPanel.d.ts.map +1 -1
  17. package/lib/components/result/QueryBuilderResultPanel.js +2 -1
  18. package/lib/components/result/QueryBuilderResultPanel.js.map +1 -1
  19. package/lib/components/shared/BasicValueSpecificationEditor.d.ts +11 -9
  20. package/lib/components/shared/BasicValueSpecificationEditor.d.ts.map +1 -1
  21. package/lib/components/shared/BasicValueSpecificationEditor.js +158 -143
  22. package/lib/components/shared/BasicValueSpecificationEditor.js.map +1 -1
  23. package/lib/components/shared/QueryBuilderVariableSelector.d.ts.map +1 -1
  24. package/lib/components/shared/QueryBuilderVariableSelector.js +8 -1
  25. package/lib/components/shared/QueryBuilderVariableSelector.js.map +1 -1
  26. package/lib/index.css +2 -2
  27. package/lib/index.css.map +1 -1
  28. package/lib/package.json +5 -5
  29. package/lib/stores/QueryBuilderPropertyEditorState.d.ts +1 -0
  30. package/lib/stores/QueryBuilderPropertyEditorState.d.ts.map +1 -1
  31. package/lib/stores/QueryBuilderPropertyEditorState.js +2 -1
  32. package/lib/stores/QueryBuilderPropertyEditorState.js.map +1 -1
  33. package/lib/stores/QueryBuilderState.d.ts.map +1 -1
  34. package/lib/stores/QueryBuilderState.js +12 -0
  35. package/lib/stores/QueryBuilderState.js.map +1 -1
  36. package/lib/stores/QueryBuilder_LegendApplicationPlugin_Extension.d.ts +1 -0
  37. package/lib/stores/QueryBuilder_LegendApplicationPlugin_Extension.d.ts.map +1 -1
  38. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_In.js +2 -2
  39. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_In.js.map +1 -1
  40. package/lib/stores/filter/QueryBuilderFilterState.d.ts +1 -1
  41. package/lib/stores/filter/QueryBuilderFilterState.d.ts.map +1 -1
  42. package/lib/stores/filter/QueryBuilderFilterState.js +4 -3
  43. package/lib/stores/filter/QueryBuilderFilterState.js.map +1 -1
  44. package/lib/stores/filter/operators/QueryBuilderFilterOperator_In.js +2 -2
  45. package/lib/stores/filter/operators/QueryBuilderFilterOperator_In.js.map +1 -1
  46. package/lib/stores/milestoning/QueryBuilderMilestoningHelper.d.ts +1 -0
  47. package/lib/stores/milestoning/QueryBuilderMilestoningHelper.d.ts.map +1 -1
  48. package/lib/stores/milestoning/QueryBuilderMilestoningHelper.js +16 -0
  49. package/lib/stores/milestoning/QueryBuilderMilestoningHelper.js.map +1 -1
  50. package/lib/stores/milestoning/QueryBuilderMilestoningState.d.ts +3 -0
  51. package/lib/stores/milestoning/QueryBuilderMilestoningState.d.ts.map +1 -1
  52. package/lib/stores/milestoning/QueryBuilderMilestoningState.js +40 -0
  53. package/lib/stores/milestoning/QueryBuilderMilestoningState.js.map +1 -1
  54. package/lib/stores/shared/ValueSpecificationEditorHelper.d.ts +1 -0
  55. package/lib/stores/shared/ValueSpecificationEditorHelper.d.ts.map +1 -1
  56. package/lib/stores/shared/ValueSpecificationEditorHelper.js +48 -0
  57. package/lib/stores/shared/ValueSpecificationEditorHelper.js.map +1 -1
  58. package/package.json +13 -13
  59. package/src/components/QueryBuilder.tsx +1 -0
  60. package/src/components/QueryBuilderParametersPanel.tsx +55 -0
  61. package/src/components/QueryBuilderSideBar.tsx +0 -35
  62. package/src/components/fetch-structure/QueryBuilderTDSPanel.tsx +22 -6
  63. package/src/components/filter/QueryBuilderFilterPanel.tsx +13 -5
  64. package/src/components/result/QueryBuilderResultPanel.tsx +9 -1
  65. package/src/components/shared/BasicValueSpecificationEditor.tsx +288 -285
  66. package/src/components/shared/QueryBuilderVariableSelector.tsx +22 -0
  67. package/src/stores/QueryBuilderPropertyEditorState.ts +2 -1
  68. package/src/stores/QueryBuilderState.ts +14 -0
  69. package/src/stores/QueryBuilder_LegendApplicationPlugin_Extension.ts +1 -0
  70. package/src/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_In.ts +2 -2
  71. package/src/stores/filter/QueryBuilderFilterState.ts +6 -3
  72. package/src/stores/filter/operators/QueryBuilderFilterOperator_In.ts +2 -2
  73. package/src/stores/milestoning/QueryBuilderMilestoningHelper.ts +24 -0
  74. package/src/stores/milestoning/QueryBuilderMilestoningState.ts +54 -0
  75. package/src/stores/shared/ValueSpecificationEditorHelper.ts +63 -0
@@ -14,10 +14,14 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- import { useApplicationStore } from '@finos/legend-application';
17
+ import {
18
+ DEFAULT_TYPEAHEAD_SEARCH_MINIMUM_SEARCH_LENGTH,
19
+ useApplicationStore,
20
+ } from '@finos/legend-application';
18
21
  import {
19
22
  type TooltipPlacement,
20
23
  type InputActionData,
24
+ type SelectActionData,
21
25
  Tooltip,
22
26
  DollarIcon,
23
27
  clsx,
@@ -29,9 +33,6 @@ import {
29
33
  SaveIcon,
30
34
  PencilIcon,
31
35
  DragPreviewLayer,
32
- FilledWindowMaximizeIcon,
33
- BasePopover,
34
- PanelFormSection,
35
36
  CalculateIcon,
36
37
  InputWithInlineValidation,
37
38
  } from '@finos/legend-art';
@@ -52,7 +53,6 @@ import {
52
53
  GenericTypeExplicitReference,
53
54
  GenericType,
54
55
  Enumeration,
55
- getEnumValue,
56
56
  getMultiplicityDescription,
57
57
  type ObserverContext,
58
58
  matchFunctionName,
@@ -63,16 +63,16 @@ import {
63
63
  type GeneratorFn,
64
64
  guaranteeNonNullable,
65
65
  isNonNullable,
66
- returnUndefOnError,
67
- uniq,
68
- parseCSVString,
69
66
  guaranteeIsNumber,
70
67
  csvStringify,
71
68
  guaranteeType,
69
+ isNonEmptyString,
70
+ parseCSVString,
71
+ uniq,
72
72
  } from '@finos/legend-shared';
73
73
  import { flowResult } from 'mobx';
74
74
  import { observer } from 'mobx-react-lite';
75
- import {
75
+ import React, {
76
76
  forwardRef,
77
77
  useEffect,
78
78
  useImperativeHandle,
@@ -91,6 +91,10 @@ import {
91
91
  } from '../../stores/QueryBuilderValueSpecificationHelper.js';
92
92
  import { evaluate } from 'mathjs';
93
93
  import { isUsedDateFunctionSupportedInFormMode } from '../../stores/QueryBuilderStateBuilder.js';
94
+ import {
95
+ convertTextToPrimitiveInstanceValue,
96
+ getValueSpecificationStringValue,
97
+ } from '../../stores/shared/ValueSpecificationEditorHelper.js';
94
98
 
95
99
  type TypeCheckOption = {
96
100
  expectedType: Type;
@@ -222,16 +226,7 @@ const StringPrimitiveInstanceValueEditor = observer(
222
226
  className?: string | undefined;
223
227
  setValueSpecification: (val: ValueSpecification) => void;
224
228
  resetValue: () => void;
225
- selectorConfig?:
226
- | {
227
- values: string[] | undefined;
228
- isLoading: boolean;
229
- reloadValues:
230
- | DebouncedFunc<(inputValue: string) => GeneratorFn<void>>
231
- | undefined;
232
- cleanUpReloadValues?: () => void;
233
- }
234
- | undefined;
229
+ selectorConfig?: BasicValueSpecificationEditorSelectorConfig | undefined;
235
230
  obseverContext: ObserverContext;
236
231
  }
237
232
  >(function StringPrimitiveInstanceValueEditor(props, ref) {
@@ -628,153 +623,276 @@ const stringifyValue = (values: ValueSpecification[]): string => {
628
623
  ]).trim();
629
624
  };
630
625
 
631
- /**
632
- * NOTE: We attempt to be less disruptive here by not throwing errors left and right, instead
633
- * we silently remove values which are not valid or parsable. But perhaps, we can consider
634
- * passing in logger or notifier to show give the users some idea of what went wrong instead
635
- * of silently swallow parts of their inputs like this.
636
- */
637
- const setCollectionValue = (
638
- valueSpecification: CollectionInstanceValue,
639
- expectedType: Type,
640
- value: string,
641
- obseverContext: ObserverContext,
642
- ): void => {
643
- if (value.trim().length === 0) {
644
- instanceValue_setValues(valueSpecification, [], obseverContext);
645
- return;
626
+ const getPlaceHolder = (expectedType: Type): string => {
627
+ if (expectedType instanceof PrimitiveType) {
628
+ switch (expectedType.path) {
629
+ case PRIMITIVE_TYPE.DATE:
630
+ case PRIMITIVE_TYPE.STRICTDATE:
631
+ return 'yyyy-mm-dd';
632
+ case PRIMITIVE_TYPE.DATETIME:
633
+ return 'yyyy-mm-ddThh:mm:ss';
634
+ default:
635
+ return 'Add';
636
+ }
646
637
  }
647
- let result: unknown[] = [];
638
+ return 'Add';
639
+ };
648
640
 
649
- const parseData = parseCSVString(value);
641
+ interface BasicValueSpecificationEditorSelectorConfig {
642
+ values: string[] | undefined;
643
+ isLoading: boolean;
644
+ reloadValues:
645
+ | DebouncedFunc<(inputValue: string) => GeneratorFn<void>>
646
+ | undefined;
647
+ cleanUpReloadValues?: () => void;
648
+ }
650
649
 
651
- if (!parseData) {
652
- return;
653
- }
650
+ const PrimitiveCollectionInstanceValueEditor = observer(
651
+ (props: {
652
+ valueSpecification: CollectionInstanceValue;
653
+ expectedType: Type;
654
+ saveEdit: () => void;
655
+ selectorConfig?: BasicValueSpecificationEditorSelectorConfig | undefined;
656
+ observerContext: ObserverContext;
657
+ }) => {
658
+ const {
659
+ valueSpecification,
660
+ expectedType,
661
+ saveEdit,
662
+ selectorConfig,
663
+ observerContext,
664
+ } = props;
654
665
 
655
- if (expectedType instanceof PrimitiveType) {
656
- switch (expectedType.path) {
657
- case PRIMITIVE_TYPE.STRING: {
658
- result = uniq(parseData)
659
- .map((item): PrimitiveInstanceValue | undefined => {
660
- const primitiveInstanceValue = new PrimitiveInstanceValue(
661
- GenericTypeExplicitReference.create(
662
- new GenericType(expectedType),
663
- ),
664
- );
665
- instanceValue_setValues(
666
- primitiveInstanceValue,
667
- [item.toString()],
668
- obseverContext,
669
- );
670
- return primitiveInstanceValue;
671
- })
672
- .filter(isNonNullable);
673
- break;
666
+ // local state and variables
667
+ const applicationStore = useApplicationStore();
668
+ const inputRef = useRef(null);
669
+ const [inputValue, setInputValue] = useState('');
670
+ const [inputValueIsError, setInputValueIsError] = useState(false);
671
+ const [selectedOptions, setSelectedOptions] = useState<
672
+ { label: string; value: string }[]
673
+ >(
674
+ valueSpecification.values
675
+ .map((valueSpec) => getValueSpecificationStringValue(valueSpec))
676
+ .filter(isNonEmptyString)
677
+ .map((value) => ({
678
+ label: value,
679
+ value,
680
+ })),
681
+ );
682
+
683
+ // typehead search setup
684
+ const isTypeaheadSearchEnabled =
685
+ expectedType === PrimitiveType.STRING && Boolean(selectorConfig);
686
+ const reloadValuesFunc = isTypeaheadSearchEnabled
687
+ ? selectorConfig?.reloadValues
688
+ : undefined;
689
+ const cleanUpReloadValuesFunc = isTypeaheadSearchEnabled
690
+ ? selectorConfig?.cleanUpReloadValues
691
+ : undefined;
692
+ const isLoading = isTypeaheadSearchEnabled
693
+ ? selectorConfig?.isLoading
694
+ : undefined;
695
+ const queryOptions =
696
+ isTypeaheadSearchEnabled && selectorConfig?.values?.length
697
+ ? selectorConfig.values.map((e) => ({
698
+ value: e,
699
+ label: e.toString(),
700
+ }))
701
+ : undefined;
702
+ const noMatchMessage =
703
+ isTypeaheadSearchEnabled && isLoading ? 'Loading...' : undefined;
704
+
705
+ // helper functions
706
+ const buildOptionForValueSpec = (
707
+ value: ValueSpecification,
708
+ ): { label: string; value: string } => {
709
+ const stringValue = guaranteeNonNullable(
710
+ getValueSpecificationStringValue(value),
711
+ );
712
+ return {
713
+ label: stringValue,
714
+ value: stringValue,
715
+ };
716
+ };
717
+
718
+ const isValueAlreadySelected = (value: string): boolean =>
719
+ selectedOptions.map((option) => option.value).includes(value);
720
+
721
+ /**
722
+ * NOTE: We attempt to be less disruptive here by not throwing errors left and right, instead
723
+ * we simply return null for values which are not valid or parsable. But perhaps, we can consider
724
+ * passing in logger or notifier to give the users some idea of what went wrong instead of ignoring
725
+ * their input.
726
+ */
727
+ const convertInputValueToValueSpec = (): ValueSpecification | null => {
728
+ const trimmedInputValue = inputValue.trim();
729
+
730
+ if (isValueAlreadySelected(trimmedInputValue)) {
731
+ return null;
674
732
  }
675
- case PRIMITIVE_TYPE.NUMBER:
676
- case PRIMITIVE_TYPE.FLOAT:
677
- case PRIMITIVE_TYPE.DECIMAL:
678
- case PRIMITIVE_TYPE.INTEGER: {
679
- result = uniq(
680
- parseData
681
- .filter((val) => !isNaN(Number(val)))
682
- .map((val) => Number(val)),
683
- )
684
- .map((item): PrimitiveInstanceValue | undefined => {
685
- const primitiveInstanceValue = new PrimitiveInstanceValue(
686
- GenericTypeExplicitReference.create(
687
- new GenericType(expectedType),
688
- ),
689
- );
690
- instanceValue_setValues(
691
- primitiveInstanceValue,
692
- [item],
693
- obseverContext,
694
- );
695
- return primitiveInstanceValue;
696
- })
697
- .filter(isNonNullable);
698
- break;
733
+
734
+ if (trimmedInputValue.length) {
735
+ return convertTextToPrimitiveInstanceValue(
736
+ expectedType,
737
+ trimmedInputValue,
738
+ observerContext,
739
+ );
699
740
  }
700
- case PRIMITIVE_TYPE.DATE:
701
- case PRIMITIVE_TYPE.STRICTDATE: {
702
- result = uniq(
703
- parseData
704
- .filter((val) => !isNaN(Date.parse(val)))
705
- .map((val) => val.trim()),
706
- )
707
- .map((item): PrimitiveInstanceValue | undefined => {
708
- const primitiveInstanceValue = new PrimitiveInstanceValue(
709
- GenericTypeExplicitReference.create(
710
- new GenericType(expectedType),
711
- ),
712
- );
713
- instanceValue_setValues(
714
- primitiveInstanceValue,
715
- [item],
716
- obseverContext,
717
- );
718
- return primitiveInstanceValue;
719
- })
720
- .filter(isNonNullable);
721
- break;
741
+ return null;
742
+ };
743
+
744
+ const addInputValueToSelectedOptions = (): void => {
745
+ const newValueSpec = convertInputValueToValueSpec();
746
+
747
+ if (newValueSpec !== null) {
748
+ setSelectedOptions([
749
+ ...selectedOptions,
750
+ buildOptionForValueSpec(newValueSpec),
751
+ ]);
752
+ setInputValue('');
753
+ reloadValuesFunc?.cancel();
754
+ } else if (inputValue.trim().length) {
755
+ setInputValueIsError(true);
722
756
  }
723
- case PRIMITIVE_TYPE.DATETIME: {
724
- result = uniq(
725
- parseData
726
- .filter(
727
- (val) =>
728
- (!isNaN(Date.parse(val)) && new Date(val).getTime()) ||
729
- (val.includes('%') &&
730
- !isNaN(Date.parse(val.slice(1))) &&
731
- new Date(val.slice(1)).getTime()),
732
- )
733
- .map((val) => val.trim()),
734
- )
735
- .map((item): PrimitiveInstanceValue | undefined => {
736
- const primitiveInstanceValue = new PrimitiveInstanceValue(
737
- GenericTypeExplicitReference.create(
738
- new GenericType(expectedType),
739
- ),
740
- );
741
- instanceValue_setValues(
742
- primitiveInstanceValue,
743
- [item],
744
- obseverContext,
745
- );
746
- return primitiveInstanceValue;
747
- })
748
- .filter(isNonNullable);
749
- break;
757
+ };
758
+
759
+ // event handlers
760
+ const changeValue = (
761
+ newSelectedOptions: { value: string; label: string }[],
762
+ actionChange: SelectActionData<{ value: string; label: string }>,
763
+ ): void => {
764
+ setSelectedOptions(newSelectedOptions);
765
+ if (actionChange.action === 'select-option') {
766
+ setInputValue('');
767
+ } else if (
768
+ actionChange.action === 'remove-value' &&
769
+ actionChange.removedValue.value === inputValue
770
+ ) {
771
+ setInputValueIsError(false);
750
772
  }
751
- default:
752
- // unsupported expected type, just escape
753
- return;
754
- }
755
- } else if (expectedType instanceof Enumeration) {
756
- result = uniq(parseData.map((item) => item.trim()))
757
- .map((item): EnumValueInstanceValue | undefined => {
758
- const _enum = returnUndefOnError(() =>
759
- getEnumValue(expectedType, item),
760
- );
761
- if (!_enum) {
762
- return undefined;
773
+ };
774
+
775
+ const handleInputChange = (
776
+ newInputValue: string,
777
+ actionChange: InputActionData,
778
+ ): void => {
779
+ if (actionChange.action === 'input-change') {
780
+ setInputValue(newInputValue);
781
+ setInputValueIsError(false);
782
+ reloadValuesFunc?.cancel();
783
+ const reloadValuesFuncTransformation =
784
+ reloadValuesFunc?.(newInputValue);
785
+ if (reloadValuesFuncTransformation) {
786
+ flowResult(reloadValuesFuncTransformation).catch(
787
+ applicationStore.alertUnhandledError,
788
+ );
763
789
  }
764
- const enumValueInstanceValue = new EnumValueInstanceValue(
765
- GenericTypeExplicitReference.create(new GenericType(expectedType)),
766
- );
767
- instanceValue_setValues(
768
- enumValueInstanceValue,
769
- [EnumValueExplicitReference.create(_enum)],
770
- obseverContext,
771
- );
772
- return enumValueInstanceValue;
773
- })
774
- .filter(isNonNullable);
775
- }
776
- instanceValue_setValues(valueSpecification, result, obseverContext);
777
- };
790
+ }
791
+ if (actionChange.action === 'input-blur') {
792
+ reloadValuesFunc?.cancel();
793
+ cleanUpReloadValuesFunc?.();
794
+ }
795
+ };
796
+
797
+ const updateValueSpecAndSaveEdit = (): void => {
798
+ const newValueSpec = convertInputValueToValueSpec();
799
+ const finalSelectedOptions =
800
+ newValueSpec !== null
801
+ ? [...selectedOptions, buildOptionForValueSpec(newValueSpec)]
802
+ : selectedOptions;
803
+ instanceValue_setValues(
804
+ valueSpecification,
805
+ finalSelectedOptions
806
+ .map((option) => option.value)
807
+ .map((value) =>
808
+ convertTextToPrimitiveInstanceValue(
809
+ expectedType,
810
+ value,
811
+ observerContext,
812
+ ),
813
+ )
814
+ .filter(isNonNullable),
815
+ observerContext,
816
+ );
817
+ saveEdit();
818
+ };
819
+
820
+ const handleKeyDown = (event: KeyboardEvent): void => {
821
+ if ((event.key === 'Enter' || event.key === ',') && !event.shiftKey) {
822
+ addInputValueToSelectedOptions();
823
+ event.preventDefault();
824
+ }
825
+ };
826
+
827
+ const handlePaste = (event: React.ClipboardEvent<string>): void => {
828
+ const pastedText = event.clipboardData.getData('text');
829
+ const parsedData = parseCSVString(pastedText);
830
+ if (!parsedData) {
831
+ return;
832
+ }
833
+ const newValues = uniq(parsedData)
834
+ .map((value) => {
835
+ const newValueSpec = convertTextToPrimitiveInstanceValue(
836
+ expectedType,
837
+ value,
838
+ observerContext,
839
+ );
840
+ return newValueSpec
841
+ ? getValueSpecificationStringValue(newValueSpec)
842
+ : null;
843
+ })
844
+ .filter(isNonNullable)
845
+ .filter((value) => !isValueAlreadySelected(value));
846
+ setSelectedOptions([
847
+ ...selectedOptions,
848
+ ...newValues.map((value) => ({ label: value, value })),
849
+ ]);
850
+ event.preventDefault();
851
+ };
852
+
853
+ return (
854
+ <>
855
+ <CustomSelectorInput
856
+ className={clsx('value-spec-editor__primitive-collection-selector', {
857
+ 'value-spec-editor__primitive-collection-selector--error':
858
+ inputValueIsError,
859
+ })}
860
+ options={queryOptions}
861
+ inputValue={inputValue}
862
+ isMulti={true}
863
+ menuIsOpen={
864
+ isTypeaheadSearchEnabled &&
865
+ inputValue.length >= DEFAULT_TYPEAHEAD_SEARCH_MINIMUM_SEARCH_LENGTH
866
+ }
867
+ autoFocus={true}
868
+ inputRef={inputRef}
869
+ onChange={changeValue}
870
+ onInputChange={handleInputChange}
871
+ onBlur={() => updateValueSpecAndSaveEdit()}
872
+ onKeyDown={handleKeyDown}
873
+ onPaste={handlePaste}
874
+ value={selectedOptions}
875
+ darkMode={
876
+ !applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled
877
+ }
878
+ isLoading={isLoading}
879
+ noMatchMessage={noMatchMessage}
880
+ placeholder={null}
881
+ inputPlaceholder={getPlaceHolder(expectedType)}
882
+ components={{
883
+ DropdownIndicator: null,
884
+ }}
885
+ />
886
+ <button
887
+ className="value-spec-editor__list-editor__save-button btn--dark"
888
+ onClick={updateValueSpecAndSaveEdit}
889
+ >
890
+ <SaveIcon />
891
+ </button>
892
+ </>
893
+ );
894
+ },
895
+ );
778
896
 
779
897
  const EnumCollectionInstanceValueEditor = observer(
780
898
  (props: {
@@ -853,7 +971,8 @@ const EnumCollectionInstanceValueEditor = observer(
853
971
  darkMode={
854
972
  !applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled
855
973
  }
856
- placeholder="Select value"
974
+ placeholder={null}
975
+ inputPlaceholder="Add"
857
976
  autoFocus={true}
858
977
  menuIsOpen={true}
859
978
  />
@@ -870,21 +989,6 @@ const EnumCollectionInstanceValueEditor = observer(
870
989
 
871
990
  const COLLECTION_PREVIEW_CHAR_LIMIT = 50;
872
991
 
873
- const getPlaceHolder = (expectedType: Type): string => {
874
- if (expectedType instanceof PrimitiveType) {
875
- switch (expectedType.path) {
876
- case PRIMITIVE_TYPE.DATE:
877
- case PRIMITIVE_TYPE.STRICTDATE:
878
- return 'yyyy-mm-dd';
879
- case PRIMITIVE_TYPE.DATETIME:
880
- return 'yyyy-mm-ddThh:mm:ss';
881
- default:
882
- return '(empty)';
883
- }
884
- }
885
- return '(empty)';
886
- };
887
-
888
992
  const CollectionValueInstanceValueEditor = observer(
889
993
  (props: {
890
994
  valueSpecification: CollectionInstanceValue;
@@ -893,6 +997,7 @@ const CollectionValueInstanceValueEditor = observer(
893
997
  className?: string | undefined;
894
998
  resetValue: () => void;
895
999
  setValueSpecification: (val: ValueSpecification) => void;
1000
+ selectorConfig?: BasicValueSpecificationEditorSelectorConfig | undefined;
896
1001
  obseverContext: ObserverContext;
897
1002
  }) => {
898
1003
  const {
@@ -901,14 +1006,11 @@ const CollectionValueInstanceValueEditor = observer(
901
1006
  className,
902
1007
  resetValue,
903
1008
  setValueSpecification,
1009
+ selectorConfig,
904
1010
  obseverContext,
905
1011
  } = props;
906
- const inputRef = useRef<HTMLTextAreaElement>(null);
907
1012
 
908
- const [text, setText] = useState(stringifyValue(valueSpecification.values));
909
1013
  const [editable, setEditable] = useState(false);
910
- const [showAdvancedEditorPopover, setShowAdvancedEditorPopover] =
911
- useState(false);
912
1014
  const valueText = stringifyValue(valueSpecification.values);
913
1015
  const previewText = `List(${
914
1016
  valueSpecification.values.length === 0
@@ -927,78 +1029,13 @@ const CollectionValueInstanceValueEditor = observer(
927
1029
  const saveEdit = (): void => {
928
1030
  if (editable) {
929
1031
  setEditable(false);
930
- setShowAdvancedEditorPopover(false);
931
1032
  setValueSpecification(valueSpecification);
932
1033
  }
933
1034
  };
934
- const updateValueSpecAndSaveEdit = (): void => {
935
- if (editable) {
936
- setCollectionValue(
937
- valueSpecification,
938
- expectedType,
939
- text,
940
- obseverContext,
941
- );
942
- setText(stringifyValue(valueSpecification.values));
943
- saveEdit();
944
- }
945
- };
946
-
947
- const changeValueTextArea: React.ChangeEventHandler<HTMLTextAreaElement> = (
948
- event,
949
- ) => {
950
- setText(event.target.value);
951
- };
952
- const expandButtonName = `${valueSpecification.hashCode}ExpandButton`;
953
- const handleOnBlur: React.FocusEventHandler<HTMLTextAreaElement> = (
954
- event,
955
- ) => {
956
- // disable save if target is expand button
957
- if (
958
- (event.relatedTarget as HTMLButtonElement | undefined)?.name !==
959
- expandButtonName
960
- ) {
961
- updateValueSpecAndSaveEdit();
962
- }
963
- };
964
-
965
- const placeholder = text === '' ? getPlaceHolder(expectedType) : undefined;
966
-
967
- // focus the input box when edit is enabled
968
- useEffect(() => {
969
- if (editable) {
970
- inputRef.current?.focus();
971
- }
972
- }, [editable]);
973
1035
 
974
1036
  if (editable) {
975
1037
  return (
976
1038
  <>
977
- {showAdvancedEditorPopover && (
978
- <BasePopover
979
- onClose={() => setShowAdvancedEditorPopover(false)}
980
- open={showAdvancedEditorPopover}
981
- anchorEl={inputRef.current}
982
- >
983
- <textarea
984
- className="panel__content__form__section__input value-spec-editor__list-editor__textarea"
985
- spellCheck={false}
986
- value={text}
987
- placeholder={placeholder}
988
- onChange={changeValueTextArea}
989
- onKeyDown={(event): void => {
990
- if (event.key === 'Enter' && !event.shiftKey) {
991
- updateValueSpecAndSaveEdit();
992
- }
993
- }}
994
- />
995
- <PanelFormSection>
996
- <div className="value-spec-editor__list-editor__textarea__description">
997
- Hit Enter to Apply Change
998
- </div>
999
- </PanelFormSection>
1000
- </BasePopover>
1001
- )}
1002
1039
  <div className={clsx('value-spec-editor', className)}>
1003
1040
  {expectedType instanceof Enumeration ? (
1004
1041
  <EnumCollectionInstanceValueEditor
@@ -1007,39 +1044,13 @@ const CollectionValueInstanceValueEditor = observer(
1007
1044
  saveEdit={saveEdit}
1008
1045
  />
1009
1046
  ) : (
1010
- <>
1011
- <textarea
1012
- ref={inputRef}
1013
- className={clsx(
1014
- 'panel__content__form__section__input value-spec-editor__input value-spec-editor__textarea ',
1015
- )}
1016
- spellCheck={false}
1017
- value={text}
1018
- placeholder={placeholder}
1019
- onChange={changeValueTextArea}
1020
- onKeyDown={(event): void => {
1021
- if (event.key === 'Enter' && !event.shiftKey) {
1022
- updateValueSpecAndSaveEdit();
1023
- }
1024
- }}
1025
- onBlur={handleOnBlur}
1026
- />
1027
- <button
1028
- className="value-spec-editor__list-editor__expand-button btn--dark"
1029
- onClick={() => setShowAdvancedEditorPopover(true)}
1030
- tabIndex={-1}
1031
- name={expandButtonName}
1032
- title="Expand window..."
1033
- >
1034
- <FilledWindowMaximizeIcon />
1035
- </button>
1036
- <button
1037
- className="value-spec-editor__list-editor__save-button btn--dark"
1038
- onClick={saveEdit}
1039
- >
1040
- <SaveIcon />
1041
- </button>
1042
- </>
1047
+ <PrimitiveCollectionInstanceValueEditor
1048
+ valueSpecification={valueSpecification}
1049
+ expectedType={expectedType}
1050
+ saveEdit={saveEdit}
1051
+ selectorConfig={selectorConfig}
1052
+ observerContext={obseverContext}
1053
+ />
1043
1054
  )}
1044
1055
  <button
1045
1056
  className="value-spec-editor__reset-btn"
@@ -1141,16 +1152,7 @@ export const BasicValueSpecificationEditor = forwardRef<
1141
1152
  setValueSpecification: (val: ValueSpecification) => void;
1142
1153
  resetValue: () => void;
1143
1154
  isConstant?: boolean;
1144
- selectorConfig?:
1145
- | {
1146
- values: string[] | undefined;
1147
- isLoading: boolean;
1148
- reloadValues:
1149
- | DebouncedFunc<(inputValue: string) => GeneratorFn<void>>
1150
- | undefined;
1151
- cleanUpReloadValues?: () => void;
1152
- }
1153
- | undefined;
1155
+ selectorConfig?: BasicValueSpecificationEditorSelectorConfig | undefined;
1154
1156
  }
1155
1157
  >(function BasicValueSpecificationEditor(props, ref) {
1156
1158
  const {
@@ -1249,6 +1251,7 @@ export const BasicValueSpecificationEditor = forwardRef<
1249
1251
  className={className}
1250
1252
  resetValue={resetValue}
1251
1253
  setValueSpecification={setValueSpecification}
1254
+ selectorConfig={selectorConfig}
1252
1255
  obseverContext={obseverContext}
1253
1256
  />
1254
1257
  );