@finos/legend-query-builder 4.14.40 → 4.14.41

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.
Files changed (68) hide show
  1. package/lib/components/QueryBuilderParametersPanel.d.ts.map +1 -1
  2. package/lib/components/QueryBuilderParametersPanel.js +20 -2
  3. package/lib/components/QueryBuilderParametersPanel.js.map +1 -1
  4. package/lib/components/QueryBuilderSideBar.d.ts.map +1 -1
  5. package/lib/components/QueryBuilderSideBar.js +5 -14
  6. package/lib/components/QueryBuilderSideBar.js.map +1 -1
  7. package/lib/components/fetch-structure/QueryBuilderTDSPanel.d.ts.map +1 -1
  8. package/lib/components/fetch-structure/QueryBuilderTDSPanel.js +12 -4
  9. package/lib/components/fetch-structure/QueryBuilderTDSPanel.js.map +1 -1
  10. package/lib/components/filter/QueryBuilderFilterPanel.d.ts.map +1 -1
  11. package/lib/components/filter/QueryBuilderFilterPanel.js +6 -2
  12. package/lib/components/filter/QueryBuilderFilterPanel.js.map +1 -1
  13. package/lib/components/result/QueryBuilderResultPanel.d.ts.map +1 -1
  14. package/lib/components/result/QueryBuilderResultPanel.js +2 -1
  15. package/lib/components/result/QueryBuilderResultPanel.js.map +1 -1
  16. package/lib/components/shared/BasicValueSpecificationEditor.d.ts +11 -9
  17. package/lib/components/shared/BasicValueSpecificationEditor.d.ts.map +1 -1
  18. package/lib/components/shared/BasicValueSpecificationEditor.js +158 -143
  19. package/lib/components/shared/BasicValueSpecificationEditor.js.map +1 -1
  20. package/lib/components/shared/QueryBuilderVariableSelector.d.ts.map +1 -1
  21. package/lib/components/shared/QueryBuilderVariableSelector.js +8 -1
  22. package/lib/components/shared/QueryBuilderVariableSelector.js.map +1 -1
  23. package/lib/index.css +2 -2
  24. package/lib/index.css.map +1 -1
  25. package/lib/package.json +1 -1
  26. package/lib/stores/QueryBuilderPropertyEditorState.d.ts +1 -0
  27. package/lib/stores/QueryBuilderPropertyEditorState.d.ts.map +1 -1
  28. package/lib/stores/QueryBuilderPropertyEditorState.js +2 -1
  29. package/lib/stores/QueryBuilderPropertyEditorState.js.map +1 -1
  30. package/lib/stores/QueryBuilderState.d.ts.map +1 -1
  31. package/lib/stores/QueryBuilderState.js +12 -0
  32. package/lib/stores/QueryBuilderState.js.map +1 -1
  33. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_In.js +2 -2
  34. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_In.js.map +1 -1
  35. package/lib/stores/filter/QueryBuilderFilterState.d.ts +1 -1
  36. package/lib/stores/filter/QueryBuilderFilterState.d.ts.map +1 -1
  37. package/lib/stores/filter/QueryBuilderFilterState.js +4 -3
  38. package/lib/stores/filter/QueryBuilderFilterState.js.map +1 -1
  39. package/lib/stores/filter/operators/QueryBuilderFilterOperator_In.js +2 -2
  40. package/lib/stores/filter/operators/QueryBuilderFilterOperator_In.js.map +1 -1
  41. package/lib/stores/milestoning/QueryBuilderMilestoningHelper.d.ts +1 -0
  42. package/lib/stores/milestoning/QueryBuilderMilestoningHelper.d.ts.map +1 -1
  43. package/lib/stores/milestoning/QueryBuilderMilestoningHelper.js +16 -0
  44. package/lib/stores/milestoning/QueryBuilderMilestoningHelper.js.map +1 -1
  45. package/lib/stores/milestoning/QueryBuilderMilestoningState.d.ts +3 -0
  46. package/lib/stores/milestoning/QueryBuilderMilestoningState.d.ts.map +1 -1
  47. package/lib/stores/milestoning/QueryBuilderMilestoningState.js +40 -0
  48. package/lib/stores/milestoning/QueryBuilderMilestoningState.js.map +1 -1
  49. package/lib/stores/shared/ValueSpecificationEditorHelper.d.ts +1 -0
  50. package/lib/stores/shared/ValueSpecificationEditorHelper.d.ts.map +1 -1
  51. package/lib/stores/shared/ValueSpecificationEditorHelper.js +48 -0
  52. package/lib/stores/shared/ValueSpecificationEditorHelper.js.map +1 -1
  53. package/package.json +8 -8
  54. package/src/components/QueryBuilderParametersPanel.tsx +55 -0
  55. package/src/components/QueryBuilderSideBar.tsx +0 -35
  56. package/src/components/fetch-structure/QueryBuilderTDSPanel.tsx +22 -6
  57. package/src/components/filter/QueryBuilderFilterPanel.tsx +13 -5
  58. package/src/components/result/QueryBuilderResultPanel.tsx +9 -1
  59. package/src/components/shared/BasicValueSpecificationEditor.tsx +288 -285
  60. package/src/components/shared/QueryBuilderVariableSelector.tsx +22 -0
  61. package/src/stores/QueryBuilderPropertyEditorState.ts +2 -1
  62. package/src/stores/QueryBuilderState.ts +14 -0
  63. package/src/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_In.ts +2 -2
  64. package/src/stores/filter/QueryBuilderFilterState.ts +6 -3
  65. package/src/stores/filter/operators/QueryBuilderFilterOperator_In.ts +2 -2
  66. package/src/stores/milestoning/QueryBuilderMilestoningHelper.ts +24 -0
  67. package/src/stores/milestoning/QueryBuilderMilestoningState.ts +54 -0
  68. 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
  );