@finos/legend-query-builder 4.14.61 → 4.14.62
Sign up to get free protection for your applications and to get access to all the features.
- package/lib/components/QueryBuilder.d.ts.map +1 -1
- package/lib/components/QueryBuilder.js +1 -2
- package/lib/components/QueryBuilder.js.map +1 -1
- package/lib/components/QueryBuilderParametersPanel.d.ts.map +1 -1
- package/lib/components/QueryBuilderParametersPanel.js +4 -3
- package/lib/components/QueryBuilderParametersPanel.js.map +1 -1
- package/lib/components/shared/BasicValueSpecificationEditor.d.ts.map +1 -1
- package/lib/components/shared/BasicValueSpecificationEditor.js +107 -14
- package/lib/components/shared/BasicValueSpecificationEditor.js.map +1 -1
- package/lib/index.css +2 -2
- package/lib/index.css.map +1 -1
- package/lib/package.json +1 -1
- package/lib/stores/QueryBuilderState.d.ts.map +1 -1
- package/lib/stores/QueryBuilderState.js +1 -1
- package/lib/stores/QueryBuilderState.js.map +1 -1
- package/lib/stores/milestoning/QueryBuilderBitemporalMilestoningImplementation.d.ts.map +1 -1
- package/lib/stores/milestoning/QueryBuilderBitemporalMilestoningImplementation.js +0 -2
- package/lib/stores/milestoning/QueryBuilderBitemporalMilestoningImplementation.js.map +1 -1
- package/lib/stores/milestoning/QueryBuilderBusinessTemporalMilestoningImplementation.d.ts.map +1 -1
- package/lib/stores/milestoning/QueryBuilderBusinessTemporalMilestoningImplementation.js +0 -2
- package/lib/stores/milestoning/QueryBuilderBusinessTemporalMilestoningImplementation.js.map +1 -1
- package/lib/stores/milestoning/QueryBuilderProcessingTemporalMilestoningImplementation.d.ts.map +1 -1
- package/lib/stores/milestoning/QueryBuilderProcessingTemporalMilestoningImplementation.js +0 -2
- package/lib/stores/milestoning/QueryBuilderProcessingTemporalMilestoningImplementation.js.map +1 -1
- package/lib/stores/shared/ValueSpecificationEditorHelper.d.ts +2 -1
- package/lib/stores/shared/ValueSpecificationEditorHelper.d.ts.map +1 -1
- package/lib/stores/shared/ValueSpecificationEditorHelper.js +1 -0
- package/lib/stores/shared/ValueSpecificationEditorHelper.js.map +1 -1
- package/package.json +1 -1
- package/src/components/QueryBuilder.tsx +6 -2
- package/src/components/QueryBuilderParametersPanel.tsx +11 -3
- package/src/components/shared/BasicValueSpecificationEditor.tsx +167 -25
- package/src/stores/QueryBuilderState.ts +6 -1
- package/src/stores/milestoning/QueryBuilderBitemporalMilestoningImplementation.ts +0 -2
- package/src/stores/milestoning/QueryBuilderBusinessTemporalMilestoningImplementation.ts +0 -2
- package/src/stores/milestoning/QueryBuilderProcessingTemporalMilestoningImplementation.ts +0 -2
- package/src/stores/shared/ValueSpecificationEditorHelper.ts +6 -0
@@ -36,6 +36,7 @@ import {
|
|
36
36
|
DragPreviewLayer,
|
37
37
|
CalculateIcon,
|
38
38
|
InputWithInlineValidation,
|
39
|
+
CopyIcon,
|
39
40
|
} from '@finos/legend-art';
|
40
41
|
import {
|
41
42
|
type Enum,
|
@@ -94,6 +95,7 @@ import {
|
|
94
95
|
import { evaluate } from 'mathjs';
|
95
96
|
import { isUsedDateFunctionSupportedInFormMode } from '../../stores/QueryBuilderStateBuilder.js';
|
96
97
|
import {
|
98
|
+
convertTextToEnum,
|
97
99
|
convertTextToPrimitiveInstanceValue,
|
98
100
|
getValueSpecificationStringValue,
|
99
101
|
} from '../../stores/shared/ValueSpecificationEditorHelper.js';
|
@@ -775,6 +777,8 @@ const PrimitiveCollectionInstanceValueEditor = observer(
|
|
775
777
|
: undefined;
|
776
778
|
const noMatchMessage =
|
777
779
|
isTypeaheadSearchEnabled && isLoading ? 'Loading...' : undefined;
|
780
|
+
const copyButtonName = `copy-${valueSpecification.hashCode}`;
|
781
|
+
const inputName = `input-${valueSpecification.hashCode}`;
|
778
782
|
|
779
783
|
// helper functions
|
780
784
|
const buildOptionForValueSpec = (
|
@@ -879,6 +883,11 @@ const PrimitiveCollectionInstanceValueEditor = observer(
|
|
879
883
|
}
|
880
884
|
};
|
881
885
|
|
886
|
+
const copyValueToClipboard = async () =>
|
887
|
+
navigator.clipboard.writeText(
|
888
|
+
selectedOptions.map((option) => option.value).join(','),
|
889
|
+
);
|
890
|
+
|
882
891
|
const updateValueSpecAndSaveEdit = (): void => {
|
883
892
|
const newValueSpec = convertInputValueToValueSpec();
|
884
893
|
const finalSelectedOptions =
|
@@ -936,8 +945,19 @@ const PrimitiveCollectionInstanceValueEditor = observer(
|
|
936
945
|
event.preventDefault();
|
937
946
|
};
|
938
947
|
|
948
|
+
const onBlur = (
|
949
|
+
event: React.FocusEvent<HTMLInputElement, HTMLButtonElement>,
|
950
|
+
): void => {
|
951
|
+
if (
|
952
|
+
event.relatedTarget?.name !== copyButtonName &&
|
953
|
+
event.relatedTarget?.name !== inputName
|
954
|
+
) {
|
955
|
+
updateValueSpecAndSaveEdit();
|
956
|
+
}
|
957
|
+
};
|
958
|
+
|
939
959
|
return (
|
940
|
-
|
960
|
+
<div className="value-spec-editor" onBlur={onBlur}>
|
941
961
|
<CustomSelectorInput
|
942
962
|
className={clsx('value-spec-editor__primitive-collection-selector', {
|
943
963
|
'value-spec-editor__primitive-collection-selector--error':
|
@@ -954,7 +974,6 @@ const PrimitiveCollectionInstanceValueEditor = observer(
|
|
954
974
|
inputRef={inputRef}
|
955
975
|
onChange={changeValue}
|
956
976
|
onInputChange={handleInputChange}
|
957
|
-
onBlur={() => updateValueSpecAndSaveEdit()}
|
958
977
|
onKeyDown={handleKeyDown}
|
959
978
|
onPaste={handlePaste}
|
960
979
|
value={selectedOptions}
|
@@ -968,14 +987,26 @@ const PrimitiveCollectionInstanceValueEditor = observer(
|
|
968
987
|
components={{
|
969
988
|
DropdownIndicator: null,
|
970
989
|
}}
|
990
|
+
inputName={inputName}
|
971
991
|
/>
|
992
|
+
<button
|
993
|
+
className="value-spec-editor__list-editor__copy-button"
|
994
|
+
// eslint-disable-next-line no-void
|
995
|
+
onClick={() => void copyValueToClipboard()}
|
996
|
+
name={copyButtonName}
|
997
|
+
title="Copy values to clipboard"
|
998
|
+
>
|
999
|
+
<CopyIcon />
|
1000
|
+
</button>
|
972
1001
|
<button
|
973
1002
|
className="value-spec-editor__list-editor__save-button btn--dark"
|
1003
|
+
name="Save"
|
1004
|
+
title="Save"
|
974
1005
|
onClick={updateValueSpecAndSaveEdit}
|
975
1006
|
>
|
976
1007
|
<SaveIcon />
|
977
1008
|
</button>
|
978
|
-
|
1009
|
+
</div>
|
979
1010
|
);
|
980
1011
|
},
|
981
1012
|
);
|
@@ -987,12 +1018,15 @@ const EnumCollectionInstanceValueEditor = observer(
|
|
987
1018
|
saveEdit: () => void;
|
988
1019
|
}) => {
|
989
1020
|
const { valueSpecification, observerContext, saveEdit } = props;
|
1021
|
+
|
1022
|
+
// local state and variables
|
990
1023
|
const applicationStore = useApplicationStore();
|
991
1024
|
const enumType = guaranteeType(
|
992
1025
|
valueSpecification.genericType?.value.rawType,
|
993
1026
|
Enumeration,
|
994
1027
|
);
|
995
|
-
|
1028
|
+
const [inputValue, setInputValue] = useState('');
|
1029
|
+
const [inputValueIsError, setInputValueIsError] = useState(false);
|
996
1030
|
const [selectedOptions, setSelectedOptions] = useState<
|
997
1031
|
{ label: string; value: Enum }[]
|
998
1032
|
>(
|
@@ -1016,12 +1050,107 @@ const EnumCollectionInstanceValueEditor = observer(
|
|
1016
1050
|
value: value,
|
1017
1051
|
}));
|
1018
1052
|
|
1053
|
+
const copyButtonName = `copy-${valueSpecification.hashCode}`;
|
1054
|
+
const inputName = `input-${valueSpecification.hashCode}`;
|
1055
|
+
|
1056
|
+
// helper functions
|
1057
|
+
const isValueAlreadySelected = (value: Enum): boolean =>
|
1058
|
+
selectedOptions.map((option) => option.value).includes(value);
|
1059
|
+
|
1060
|
+
/**
|
1061
|
+
* NOTE: We attempt to be less disruptive here by not throwing errors left and right, instead
|
1062
|
+
* we simply return null for values which are not valid or parsable. But perhaps, we can consider
|
1063
|
+
* passing in logger or notifier to give the users some idea of what went wrong instead of ignoring
|
1064
|
+
* their input.
|
1065
|
+
*/
|
1066
|
+
const convertInputValueToEnum = (): Enum | null => {
|
1067
|
+
const trimmedInputValue = inputValue.trim();
|
1068
|
+
|
1069
|
+
if (trimmedInputValue.length) {
|
1070
|
+
const newEnum = convertTextToEnum(trimmedInputValue, enumType);
|
1071
|
+
|
1072
|
+
if (newEnum === undefined || isValueAlreadySelected(newEnum)) {
|
1073
|
+
return null;
|
1074
|
+
}
|
1075
|
+
|
1076
|
+
return newEnum;
|
1077
|
+
}
|
1078
|
+
return null;
|
1079
|
+
};
|
1080
|
+
|
1081
|
+
const addInputValueToSelectedOptions = (): void => {
|
1082
|
+
const newEnum = convertInputValueToEnum();
|
1083
|
+
|
1084
|
+
if (newEnum !== null) {
|
1085
|
+
setSelectedOptions([
|
1086
|
+
...selectedOptions,
|
1087
|
+
{
|
1088
|
+
label: newEnum.name,
|
1089
|
+
value: newEnum,
|
1090
|
+
},
|
1091
|
+
]);
|
1092
|
+
setInputValue('');
|
1093
|
+
} else if (inputValue.trim().length) {
|
1094
|
+
setInputValueIsError(true);
|
1095
|
+
}
|
1096
|
+
};
|
1097
|
+
|
1098
|
+
// event handlers
|
1019
1099
|
const changeValue = (
|
1020
1100
|
newSelectedOptions: { value: Enum; label: string }[],
|
1101
|
+
actionChange: SelectActionData<{ value: Enum; label: string }>,
|
1021
1102
|
): void => {
|
1022
1103
|
setSelectedOptions(newSelectedOptions);
|
1104
|
+
if (actionChange.action === 'select-option') {
|
1105
|
+
setInputValue('');
|
1106
|
+
} else if (
|
1107
|
+
actionChange.action === 'remove-value' &&
|
1108
|
+
actionChange.removedValue.value.name === inputValue
|
1109
|
+
) {
|
1110
|
+
setInputValueIsError(false);
|
1111
|
+
}
|
1112
|
+
};
|
1113
|
+
|
1114
|
+
const handleInputChange = (
|
1115
|
+
newInputValue: string,
|
1116
|
+
actionChange: InputActionData,
|
1117
|
+
): void => {
|
1118
|
+
if (actionChange.action === 'input-change') {
|
1119
|
+
setInputValue(newInputValue);
|
1120
|
+
setInputValueIsError(false);
|
1121
|
+
}
|
1122
|
+
};
|
1123
|
+
|
1124
|
+
const handleKeyDown = (event: KeyboardEvent): void => {
|
1125
|
+
if ((event.key === 'Enter' || event.key === ',') && !event.shiftKey) {
|
1126
|
+
addInputValueToSelectedOptions();
|
1127
|
+
event.preventDefault();
|
1128
|
+
}
|
1129
|
+
};
|
1130
|
+
|
1131
|
+
const handlePaste = (event: React.ClipboardEvent<string>): void => {
|
1132
|
+
const pastedText = event.clipboardData.getData('text');
|
1133
|
+
const parsedData = parseCSVString(pastedText);
|
1134
|
+
if (!parsedData) {
|
1135
|
+
return;
|
1136
|
+
}
|
1137
|
+
const newValues = uniq(
|
1138
|
+
uniq(parsedData)
|
1139
|
+
.map((value) => convertTextToEnum(value, enumType))
|
1140
|
+
.filter(isNonNullable),
|
1141
|
+
).filter((value) => !isValueAlreadySelected(value));
|
1142
|
+
setSelectedOptions([
|
1143
|
+
...selectedOptions,
|
1144
|
+
...newValues.map((value) => ({ label: value.name, value })),
|
1145
|
+
]);
|
1146
|
+
event.preventDefault();
|
1023
1147
|
};
|
1024
1148
|
|
1149
|
+
const copyValueToClipboard = async () =>
|
1150
|
+
navigator.clipboard.writeText(
|
1151
|
+
selectedOptions.map((option) => option.value.name).join(','),
|
1152
|
+
);
|
1153
|
+
|
1025
1154
|
const updateValueSpecAndSaveEdit = (): void => {
|
1026
1155
|
const result = selectedOptions
|
1027
1156
|
.map((value) => {
|
@@ -1040,35 +1169,59 @@ const EnumCollectionInstanceValueEditor = observer(
|
|
1040
1169
|
saveEdit();
|
1041
1170
|
};
|
1042
1171
|
|
1172
|
+
const onBlur = (
|
1173
|
+
event: React.FocusEvent<HTMLInputElement, HTMLButtonElement>,
|
1174
|
+
): void => {
|
1175
|
+
if (
|
1176
|
+
event.relatedTarget?.name !== copyButtonName &&
|
1177
|
+
event.relatedTarget?.name !== inputName
|
1178
|
+
) {
|
1179
|
+
updateValueSpecAndSaveEdit();
|
1180
|
+
}
|
1181
|
+
};
|
1182
|
+
|
1043
1183
|
return (
|
1044
|
-
|
1184
|
+
<div className="value-spec-editor" onBlur={onBlur}>
|
1045
1185
|
<CustomSelectorInput
|
1046
|
-
className=
|
1186
|
+
className={clsx('value-spec-editor__enum-collection-selector', {
|
1187
|
+
'value-spec-editor__enum-collection-selector--error':
|
1188
|
+
inputValueIsError,
|
1189
|
+
})}
|
1047
1190
|
options={availableOptions}
|
1191
|
+
inputValue={inputValue}
|
1048
1192
|
isMulti={true}
|
1193
|
+
autoFocus={true}
|
1049
1194
|
onChange={changeValue}
|
1050
|
-
|
1051
|
-
onKeyDown={
|
1052
|
-
|
1053
|
-
updateValueSpecAndSaveEdit();
|
1054
|
-
}
|
1055
|
-
}}
|
1195
|
+
onInputChange={handleInputChange}
|
1196
|
+
onKeyDown={handleKeyDown}
|
1197
|
+
onPaste={handlePaste}
|
1056
1198
|
value={selectedOptions}
|
1057
1199
|
darkMode={
|
1058
1200
|
!applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled
|
1059
1201
|
}
|
1060
1202
|
placeholder={null}
|
1061
1203
|
inputPlaceholder="Add"
|
1062
|
-
autoFocus={true}
|
1063
1204
|
menuIsOpen={true}
|
1205
|
+
inputName={inputName}
|
1064
1206
|
/>
|
1207
|
+
<button
|
1208
|
+
className="value-spec-editor__list-editor__copy-button"
|
1209
|
+
// eslint-disable-next-line no-void
|
1210
|
+
onClick={() => void copyValueToClipboard()}
|
1211
|
+
name={copyButtonName}
|
1212
|
+
title="Copy values to clipboard"
|
1213
|
+
>
|
1214
|
+
<CopyIcon />
|
1215
|
+
</button>
|
1065
1216
|
<button
|
1066
1217
|
className="value-spec-editor__list-editor__save-button btn--dark"
|
1218
|
+
name="Save"
|
1219
|
+
title="Save"
|
1067
1220
|
onClick={updateValueSpecAndSaveEdit}
|
1068
1221
|
>
|
1069
1222
|
<SaveIcon />
|
1070
1223
|
</button>
|
1071
|
-
|
1224
|
+
</div>
|
1072
1225
|
);
|
1073
1226
|
},
|
1074
1227
|
);
|
@@ -1081,7 +1234,6 @@ const CollectionValueInstanceValueEditor = observer(
|
|
1081
1234
|
graph: PureModel;
|
1082
1235
|
expectedType: Type;
|
1083
1236
|
className?: string | undefined;
|
1084
|
-
resetValue: () => void;
|
1085
1237
|
setValueSpecification: (val: ValueSpecification) => void;
|
1086
1238
|
selectorConfig?: BasicValueSpecificationEditorSelectorConfig | undefined;
|
1087
1239
|
observerContext: ObserverContext;
|
@@ -1090,7 +1242,6 @@ const CollectionValueInstanceValueEditor = observer(
|
|
1090
1242
|
valueSpecification,
|
1091
1243
|
expectedType,
|
1092
1244
|
className,
|
1093
|
-
resetValue,
|
1094
1245
|
setValueSpecification,
|
1095
1246
|
selectorConfig,
|
1096
1247
|
observerContext,
|
@@ -1138,14 +1289,6 @@ const CollectionValueInstanceValueEditor = observer(
|
|
1138
1289
|
observerContext={observerContext}
|
1139
1290
|
/>
|
1140
1291
|
)}
|
1141
|
-
<button
|
1142
|
-
className="value-spec-editor__reset-btn"
|
1143
|
-
name="Reset"
|
1144
|
-
title="Reset"
|
1145
|
-
onClick={resetValue}
|
1146
|
-
>
|
1147
|
-
<RefreshIcon />
|
1148
|
-
</button>
|
1149
1292
|
</div>
|
1150
1293
|
</>
|
1151
1294
|
);
|
@@ -1358,7 +1501,6 @@ export const BasicValueSpecificationEditor = forwardRef<
|
|
1358
1501
|
graph={graph}
|
1359
1502
|
expectedType={typeCheckOption.expectedType}
|
1360
1503
|
className={className}
|
1361
|
-
resetValue={resetValue}
|
1362
1504
|
setValueSpecification={setValueSpecification}
|
1363
1505
|
selectorConfig={selectorConfig}
|
1364
1506
|
observerContext={observerContext}
|
@@ -697,7 +697,12 @@ export abstract class QueryBuilderState implements CommandRegistrar {
|
|
697
697
|
},
|
698
698
|
);
|
699
699
|
}
|
700
|
-
if (
|
700
|
+
if (
|
701
|
+
this.parametersState.parameterStates.filter(
|
702
|
+
(paramState) =>
|
703
|
+
!this.milestoningState.isMilestoningParameter(paramState.parameter),
|
704
|
+
).length > 0
|
705
|
+
) {
|
701
706
|
this.setShowParametersPanel(true);
|
702
707
|
}
|
703
708
|
this.fetchStructureState.initializeWithQuery();
|
@@ -69,8 +69,6 @@ export class QueryBuilderBitemporalMilestoningImplementation extends QueryBuilde
|
|
69
69
|
),
|
70
70
|
);
|
71
71
|
}
|
72
|
-
// Show the parameter panel because we populate paramaters state with milestoning parameters
|
73
|
-
this.milestoningState.queryBuilderState.setShowParametersPanel(true);
|
74
72
|
}
|
75
73
|
|
76
74
|
buildParameterStatesFromMilestoningParameters(): LambdaParameterState[] {
|
@@ -52,8 +52,6 @@ export class QueryBuilderBusinessTemporalMilestoningImplementation extends Query
|
|
52
52
|
),
|
53
53
|
);
|
54
54
|
}
|
55
|
-
// Show the parameter panel because we populate paramaters state with milestoning parameters
|
56
|
-
this.milestoningState.queryBuilderState.setShowParametersPanel(true);
|
57
55
|
}
|
58
56
|
|
59
57
|
buildParameterStatesFromMilestoningParameters(): LambdaParameterState[] {
|
@@ -51,8 +51,6 @@ export class QueryBuilderProcessingTemporalMilestoningImplementation extends Que
|
|
51
51
|
),
|
52
52
|
);
|
53
53
|
}
|
54
|
-
// Show the parameter panel because we populate paramaters state with milestoning parameters
|
55
|
-
this.milestoningState.queryBuilderState.setShowParametersPanel(true);
|
56
54
|
}
|
57
55
|
|
58
56
|
buildParameterStatesFromMilestoningParameters(): LambdaParameterState[] {
|
@@ -435,3 +435,9 @@ export const convertTextToPrimitiveInstanceValue = (
|
|
435
435
|
}
|
436
436
|
return result;
|
437
437
|
};
|
438
|
+
|
439
|
+
export const convertTextToEnum = (
|
440
|
+
value: string,
|
441
|
+
enumType: Enumeration,
|
442
|
+
): Enum | undefined =>
|
443
|
+
enumType.values.find((enumValue) => enumValue.name === value);
|