@finos/legend-query-builder 4.5.1 → 4.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. package/lib/__lib__/QueryBuilderTesting.d.ts +1 -0
  2. package/lib/__lib__/QueryBuilderTesting.d.ts.map +1 -1
  3. package/lib/__lib__/QueryBuilderTesting.js +1 -0
  4. package/lib/__lib__/QueryBuilderTesting.js.map +1 -1
  5. package/lib/components/QueryBuilderConstantExpressionPanel.d.ts.map +1 -1
  6. package/lib/components/QueryBuilderConstantExpressionPanel.js +46 -9
  7. package/lib/components/QueryBuilderConstantExpressionPanel.js.map +1 -1
  8. package/lib/components/QueryBuilderParametersPanel.d.ts.map +1 -1
  9. package/lib/components/QueryBuilderParametersPanel.js.map +1 -1
  10. package/lib/components/QueryBuilderResultPanel.d.ts.map +1 -1
  11. package/lib/components/QueryBuilderResultPanel.js +1 -1
  12. package/lib/components/QueryBuilderResultPanel.js.map +1 -1
  13. package/lib/components/shared/BasicValueSpecificationEditor.d.ts.map +1 -1
  14. package/lib/components/shared/BasicValueSpecificationEditor.js +2 -2
  15. package/lib/components/shared/BasicValueSpecificationEditor.js.map +1 -1
  16. package/lib/components/shared/CustomDatePicker.d.ts.map +1 -1
  17. package/lib/components/shared/CustomDatePicker.js +6 -2
  18. package/lib/components/shared/CustomDatePicker.js.map +1 -1
  19. package/lib/components/shared/LambdaEditor.d.ts +11 -0
  20. package/lib/components/shared/LambdaEditor.d.ts.map +1 -1
  21. package/lib/components/shared/LambdaEditor.js +3 -3
  22. package/lib/components/shared/LambdaEditor.js.map +1 -1
  23. package/lib/components/shared/QueryBuilderVariableSelector.d.ts +9 -2
  24. package/lib/components/shared/QueryBuilderVariableSelector.d.ts.map +1 -1
  25. package/lib/components/shared/QueryBuilderVariableSelector.js +38 -19
  26. package/lib/components/shared/QueryBuilderVariableSelector.js.map +1 -1
  27. package/lib/index.css +1 -17
  28. package/lib/index.css.map +1 -1
  29. package/lib/package.json +1 -1
  30. package/lib/stores/QueryBuilderConfig.d.ts +2 -1
  31. package/lib/stores/QueryBuilderConfig.d.ts.map +1 -1
  32. package/lib/stores/QueryBuilderConfig.js +1 -0
  33. package/lib/stores/QueryBuilderConfig.js.map +1 -1
  34. package/lib/stores/QueryBuilderConstantsState.d.ts +26 -2
  35. package/lib/stores/QueryBuilderConstantsState.d.ts.map +1 -1
  36. package/lib/stores/QueryBuilderConstantsState.js +107 -5
  37. package/lib/stores/QueryBuilderConstantsState.js.map +1 -1
  38. package/lib/stores/QueryBuilderStateBuilder.d.ts +1 -1
  39. package/lib/stores/QueryBuilderStateBuilder.d.ts.map +1 -1
  40. package/lib/stores/QueryBuilderStateBuilder.js +9 -4
  41. package/lib/stores/QueryBuilderStateBuilder.js.map +1 -1
  42. package/lib/stores/QueryBuilderValueSpecificationBuilder.d.ts.map +1 -1
  43. package/lib/stores/QueryBuilderValueSpecificationBuilder.js +14 -3
  44. package/lib/stores/QueryBuilderValueSpecificationBuilder.js.map +1 -1
  45. package/package.json +5 -5
  46. package/src/__lib__/QueryBuilderTesting.ts +1 -0
  47. package/src/components/QueryBuilderConstantExpressionPanel.tsx +95 -13
  48. package/src/components/QueryBuilderParametersPanel.tsx +0 -1
  49. package/src/components/QueryBuilderResultPanel.tsx +4 -1
  50. package/src/components/shared/BasicValueSpecificationEditor.tsx +4 -7
  51. package/src/components/shared/CustomDatePicker.tsx +6 -7
  52. package/src/components/shared/LambdaEditor.tsx +4 -2
  53. package/src/components/shared/QueryBuilderVariableSelector.tsx +192 -83
  54. package/src/stores/QueryBuilderConfig.ts +1 -0
  55. package/src/stores/QueryBuilderConstantsState.ts +175 -5
  56. package/src/stores/QueryBuilderStateBuilder.ts +20 -8
  57. package/src/stores/QueryBuilderValueSpecificationBuilder.ts +31 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finos/legend-query-builder",
3
- "version": "4.5.1",
3
+ "version": "4.7.0",
4
4
  "description": "Legend query builder core",
5
5
  "keywords": [
6
6
  "legend",
@@ -42,10 +42,10 @@
42
42
  "test:watch": "jest --watch"
43
43
  },
44
44
  "dependencies": {
45
- "@finos/legend-application": "15.0.40",
46
- "@finos/legend-art": "7.0.40",
47
- "@finos/legend-graph": "31.2.0",
48
- "@finos/legend-lego": "1.1.28",
45
+ "@finos/legend-application": "15.0.41",
46
+ "@finos/legend-art": "7.0.41",
47
+ "@finos/legend-graph": "31.2.2",
48
+ "@finos/legend-lego": "1.1.30",
49
49
  "@finos/legend-server-depot": "6.0.29",
50
50
  "@finos/legend-shared": "10.0.25",
51
51
  "@finos/legend-storage": "3.0.75",
@@ -27,6 +27,7 @@ export enum QUERY_BUILDER_TEST_ID {
27
27
  QUERY_BUILDER_EXPLORER = 'query__builder__explorer',
28
28
  QUERY_BUILDER_PROPERTY_SEARCH_PANEL = 'query__builder__property__search__panel',
29
29
  QUERY_BUILDER_RESULT_PANEL = 'query__builder__result__panel',
30
+ QUERY_BUILDER_RESULT_ANALYTICS = 'query__builder__result__analytics',
30
31
  QUERY_BUILDER_PARAMETERS = 'query-builder__parameters',
31
32
  QUERY_BUILDER_CONSTANTS = 'query-builder__constants',
32
33
  }
@@ -21,6 +21,7 @@ import {
21
21
  } from '@finos/legend-lego/graph-editor';
22
22
  import {
23
23
  BlankPanelPlaceholder,
24
+ clsx,
24
25
  CustomSelectorInput,
25
26
  Dialog,
26
27
  InfoCircleIcon,
@@ -41,23 +42,31 @@ import {
41
42
  Multiplicity,
42
43
  isValidIdentifier,
43
44
  } from '@finos/legend-graph';
44
- import { generateEnumerableNameFromToken } from '@finos/legend-shared';
45
+ import {
46
+ debounce,
47
+ generateEnumerableNameFromToken,
48
+ } from '@finos/legend-shared';
45
49
  import { observer } from 'mobx-react-lite';
46
50
  import { DEFAULT_CONSTANT_VARIABLE_NAME } from '../stores/QueryBuilderConfig.js';
47
51
  import type { QueryBuilderState } from '../stores/QueryBuilderState.js';
48
- import { QueryBuilderConstantExpressionState } from '../stores/QueryBuilderConstantsState.js';
52
+ import {
53
+ type QueryBuilderConstantExpressionState,
54
+ QueryBuilderSimpleConstantExpressionState,
55
+ QueryBuilderCalculatedConstantExpressionState,
56
+ } from '../stores/QueryBuilderConstantsState.js';
49
57
  import { buildDefaultInstanceValue } from '../stores/shared/ValueSpecificationEditorHelper.js';
50
58
  import { BasicValueSpecificationEditor } from './shared/BasicValueSpecificationEditor.js';
51
- import { VariableViewer } from './shared/QueryBuilderVariableSelector.js';
52
59
  import { QUERY_BUILDER_TEST_ID } from '../__lib__/QueryBuilderTesting.js';
53
60
  import { QUERY_BUILDER_DOCUMENTATION_KEY } from '../__lib__/QueryBuilderDocumentation.js';
54
- import { useState } from 'react';
61
+ import React, { useMemo, useState } from 'react';
55
62
  import { variableExpression_setName } from '../stores/shared/ValueSpecificationModifierHelper.js';
63
+ import { LambdaEditor_PopUp } from './shared/LambdaEditor.js';
64
+ import { VariableViewer } from './shared/QueryBuilderVariableSelector.js';
56
65
 
57
66
  // NOTE: We currently only allow constant variables for primitive types of multiplicity ONE.
58
67
  // This is why we don't show multiplicity in the editor.
59
- const QueryBuilderConstantExpressionEditor = observer(
60
- (props: { constantState: QueryBuilderConstantExpressionState }) => {
68
+ const QueryBuilderSimpleConstantExpressionEditor = observer(
69
+ (props: { constantState: QueryBuilderSimpleConstantExpressionState }) => {
61
70
  const { constantState } = props;
62
71
  const queryBuilderState = constantState.queryBuilderState;
63
72
  const applicationStore = queryBuilderState.applicationStore;
@@ -205,6 +214,35 @@ const QueryBuilderConstantExpressionEditor = observer(
205
214
  },
206
215
  );
207
216
 
217
+ const QuerryBuilderCalculatedConstantExpressionEditor = observer(
218
+ (props: { constantState: QueryBuilderCalculatedConstantExpressionState }) => {
219
+ const { constantState } = props;
220
+ const queryBuilderState = constantState.queryBuilderState;
221
+ const lambdaState = constantState.lambdaState;
222
+ const closePopUp = (): void =>
223
+ queryBuilderState.constantState.setSelectedConstant(undefined);
224
+ const debouncedTransformStringToLambda = useMemo(
225
+ () =>
226
+ debounce(() => lambdaState.convertLambdaGrammarStringToObject(), 1000),
227
+ [lambdaState],
228
+ );
229
+ const canDrop = true;
230
+ return (
231
+ <>
232
+ <div className="lambda-editor" />
233
+ <LambdaEditor_PopUp
234
+ title={`Edit Constant ${constantState.variable.name}`}
235
+ className={clsx({ 'lambda-editor--dnd-match': canDrop })}
236
+ disabled={false}
237
+ lambdaEditorState={lambdaState}
238
+ transformStringToLambda={debouncedTransformStringToLambda}
239
+ onClose={closePopUp}
240
+ />
241
+ </>
242
+ );
243
+ },
244
+ );
245
+
208
246
  export const QueryBuilderConstantExpressionPanel = observer(
209
247
  (props: { queryBuilderState: QueryBuilderState }) => {
210
248
  const { queryBuilderState } = props;
@@ -232,7 +270,7 @@ export const QueryBuilderConstantExpressionPanel = observer(
232
270
  Multiplicity.ONE,
233
271
  );
234
272
  variableEx.genericType = defaultVal.genericType;
235
- const constState = new QueryBuilderConstantExpressionState(
273
+ const constState = new QueryBuilderSimpleConstantExpressionState(
236
274
  queryBuilderState,
237
275
  variableEx,
238
276
  defaultVal,
@@ -241,6 +279,46 @@ export const QueryBuilderConstantExpressionPanel = observer(
241
279
  }
242
280
  };
243
281
 
282
+ const renderConstantModal = (
283
+ val: QueryBuilderConstantExpressionState,
284
+ ): React.ReactNode => {
285
+ if (val instanceof QueryBuilderSimpleConstantExpressionState) {
286
+ return (
287
+ <QueryBuilderSimpleConstantExpressionEditor constantState={val} />
288
+ );
289
+ } else if (val instanceof QueryBuilderCalculatedConstantExpressionState) {
290
+ return (
291
+ <QuerryBuilderCalculatedConstantExpressionEditor
292
+ constantState={val}
293
+ />
294
+ );
295
+ }
296
+ return null;
297
+ };
298
+ const getExtraContextMenu = (
299
+ val: QueryBuilderConstantExpressionState,
300
+ ):
301
+ | {
302
+ key: string;
303
+ label: string;
304
+ handler: () => void;
305
+ }[]
306
+ | undefined => {
307
+ if (val instanceof QueryBuilderSimpleConstantExpressionState) {
308
+ return [
309
+ {
310
+ key: 'convert-to-derivation',
311
+ label: 'Convert To Derivation',
312
+ handler: () =>
313
+ constantState.queryBuilderState.constantState.convertToCalculated(
314
+ val,
315
+ ),
316
+ },
317
+ ];
318
+ }
319
+ return undefined;
320
+ };
321
+
244
322
  return (
245
323
  <div
246
324
  data-testid={QUERY_BUILDER_TEST_ID.QUERY_BUILDER_CONSTANTS}
@@ -278,13 +356,20 @@ export const QueryBuilderConstantExpressionPanel = observer(
278
356
  key={constState.uuid}
279
357
  queryBuilderState={queryBuilderState}
280
358
  variable={constState.variable}
281
- constantValue={constState.value}
359
+ value={{
360
+ val:
361
+ constState instanceof
362
+ QueryBuilderSimpleConstantExpressionState
363
+ ? constState.value
364
+ : undefined,
365
+ }}
282
366
  actions={{
283
367
  editVariable: () =>
284
368
  constantState.setSelectedConstant(constState),
285
369
  deleteVariable: () =>
286
370
  constantState.removeConstant(constState),
287
371
  }}
372
+ extraContextMenuActions={getExtraContextMenu(constState)}
288
373
  isReadOnly={isReadOnly}
289
374
  />
290
375
  ))}
@@ -300,11 +385,8 @@ export const QueryBuilderConstantExpressionPanel = observer(
300
385
  )}
301
386
  </>
302
387
  </div>
303
- {constantState.selectedConstant && (
304
- <QueryBuilderConstantExpressionEditor
305
- constantState={constantState.selectedConstant}
306
- />
307
- )}
388
+ {constantState.selectedConstant &&
389
+ renderConstantModal(constantState.selectedConstant)}
308
390
  </div>
309
391
  );
310
392
  },
@@ -266,7 +266,6 @@ export const QueryBuilderParametersPanel = observer(
266
266
  parmaterState.mockParameterValue();
267
267
  }
268
268
  };
269
-
270
269
  return (
271
270
  <div
272
271
  data-testid={QUERY_BUILDER_TEST_ID.QUERY_BUILDER_PARAMETERS}
@@ -1069,7 +1069,10 @@ export const QueryBuilderResultPanel = observer(
1069
1069
  </div>
1070
1070
  )}
1071
1071
 
1072
- <div className="query-builder__result__analytics">
1072
+ <div
1073
+ data-testid={QUERY_BUILDER_TEST_ID.QUERY_BUILDER_RESULT_ANALYTICS}
1074
+ className="query-builder__result__analytics"
1075
+ >
1073
1076
  {resultDescription ?? ''}
1074
1077
  </div>
1075
1078
  {executionResult && resultState.checkForStaleResults && (
@@ -121,7 +121,7 @@ export const VariableInfoTooltip: React.FC<{
121
121
  Type
122
122
  </div>
123
123
  <div className="value-spec-paramater__tooltip__item__value">
124
- {type?.name ?? ''}
124
+ {type?.name ?? '(unknown)'}
125
125
  </div>
126
126
  </div>
127
127
  <div className="value-spec-paramater__tooltip__item">
@@ -871,12 +871,9 @@ const CollectionValueInstanceValueEditor = observer(
871
871
  onClick={enableEdit}
872
872
  title="Click to edit"
873
873
  >
874
- <input
875
- className="value-spec-editor__list-editor__preview"
876
- spellCheck={false}
877
- value={previewText}
878
- disabled={true}
879
- />
874
+ <div className="value-spec-editor__list-editor__preview">
875
+ {previewText}
876
+ </div>
880
877
  <button className="value-spec-editor__list-editor__edit-icon">
881
878
  <PencilIcon />
882
879
  </button>
@@ -840,22 +840,21 @@ const AbsoluteTimeValueSpecificationEditor: React.FC<{
840
840
  const updateAbsoluteTimeValue: React.ChangeEventHandler<HTMLInputElement> = (
841
841
  event,
842
842
  ) => {
843
+ //
844
+ const value = new Date(event.target.value).getUTCSeconds()
845
+ ? event.target.value
846
+ : `${event.target.value}:00`;
843
847
  if (valueSpecification instanceof SimpleFunctionExpression) {
844
848
  setValueSpecification(
845
849
  buildPrimitiveInstanceValue(
846
850
  graph,
847
851
  PRIMITIVE_TYPE.DATETIME,
848
- event.target.value,
852
+ value,
849
853
  observerContext,
850
854
  ),
851
855
  );
852
856
  } else {
853
- instanceValue_setValue(
854
- valueSpecification,
855
- event.target.value,
856
- 0,
857
- observerContext,
858
- );
857
+ instanceValue_setValue(valueSpecification, value, 0, observerContext);
859
858
  if (
860
859
  valueSpecification.genericType.value.rawType.path !==
861
860
  PRIMITIVE_TYPE.DATETIME
@@ -419,8 +419,9 @@ const LambdaEditor_Inner = observer(
419
419
  },
420
420
  );
421
421
 
422
- const LambdaEditor_PopUp = observer(
422
+ export const LambdaEditor_PopUp = observer(
423
423
  (props: {
424
+ title?: string | undefined;
424
425
  className?: string | undefined;
425
426
  disabled: boolean;
426
427
  lambdaEditorState: LambdaEditorState;
@@ -432,6 +433,7 @@ const LambdaEditor_PopUp = observer(
432
433
  disabled,
433
434
  lambdaEditorState,
434
435
  transformStringToLambda,
436
+ title,
435
437
  onClose,
436
438
  } = props;
437
439
  const applicationStore = useApplicationStore();
@@ -607,7 +609,7 @@ const LambdaEditor_PopUp = observer(
607
609
  )}
608
610
  >
609
611
  <ModalHeader>
610
- <ModalTitle title="Edit Lambda" />
612
+ <ModalTitle title={title ?? 'Edit Lambda'} />
611
613
  {lambdaEditorState.parserError && (
612
614
  <div className="modal__title__error-badge">
613
615
  Failed to parse lambda
@@ -15,18 +15,23 @@
15
15
  */
16
16
 
17
17
  import {
18
+ CalculatorIcon,
19
+ ContextMenu,
18
20
  DollarIcon,
19
21
  DragPreviewLayer,
20
22
  InfoCircleIcon,
23
+ MenuContent,
24
+ MenuContentItem,
21
25
  PanelFormListItems,
22
26
  PencilIcon,
23
27
  TimesIcon,
28
+ clsx,
24
29
  useDragPreviewLayer,
25
30
  } from '@finos/legend-art';
26
31
  import {
27
- SimpleFunctionExpression,
28
- type ValueSpecification,
29
32
  type VariableExpression,
33
+ type ValueSpecification,
34
+ SimpleFunctionExpression,
30
35
  } from '@finos/legend-graph';
31
36
  import { observer } from 'mobx-react-lite';
32
37
  import { useDrag } from 'react-dnd';
@@ -38,41 +43,112 @@ import {
38
43
  VariableInfoTooltip,
39
44
  } from './BasicValueSpecificationEditor.js';
40
45
  import { buildDatePickerOption } from './CustomDatePicker.js';
46
+ import { QueryBuilderSimpleConstantExpressionState } from '../../stores/QueryBuilderConstantsState.js';
47
+ import { forwardRef, useState } from 'react';
41
48
 
49
+ const CALCULATED = '(calculated)';
50
+
51
+ const getNameOfValue = (
52
+ value: ValueSpecification,
53
+ queryBuilderState: QueryBuilderState,
54
+ ): string | undefined => {
55
+ if (value instanceof SimpleFunctionExpression) {
56
+ const possibleDateLabel = buildDatePickerOption(
57
+ value,
58
+ queryBuilderState.applicationStore,
59
+ ).label;
60
+ if (possibleDateLabel) {
61
+ return possibleDateLabel;
62
+ }
63
+ }
64
+ return getValueSpecificationStringValue(value);
65
+ };
66
+
67
+ const QueryBuilderVariableContextMenu = observer(
68
+ forwardRef<
69
+ HTMLDivElement,
70
+ {
71
+ variable: VariableExpression;
72
+ variableInUse: boolean;
73
+ actions?:
74
+ | {
75
+ editVariable: () => void;
76
+ deleteVariable: () => void;
77
+ }
78
+ | undefined;
79
+ extraContextMenuActions?:
80
+ | {
81
+ key: string;
82
+ label: string;
83
+ handler: () => void;
84
+ }[]
85
+ | undefined;
86
+ }
87
+ >(function QueryBuilderVariableContextMenu(props, ref) {
88
+ const { actions, extraContextMenuActions, variableInUse } = props;
89
+ return (
90
+ <MenuContent ref={ref}>
91
+ {extraContextMenuActions?.map((action) => (
92
+ <MenuContentItem onClick={action.handler} key={action.key}>
93
+ {action.label}
94
+ </MenuContentItem>
95
+ ))}
96
+ {actions?.editVariable && (
97
+ <MenuContentItem onClick={actions.editVariable}>Edit</MenuContentItem>
98
+ )}
99
+ {actions?.deleteVariable && (
100
+ <MenuContentItem
101
+ disabled={variableInUse}
102
+ onClick={actions.deleteVariable}
103
+ >
104
+ Remove
105
+ </MenuContentItem>
106
+ )}
107
+ </MenuContent>
108
+ );
109
+ }),
110
+ );
42
111
  export const VariableViewer = observer(
43
112
  (props: {
44
113
  variable: VariableExpression;
45
114
  queryBuilderState: QueryBuilderState;
46
115
  isReadOnly: boolean;
47
- constantValue?: ValueSpecification | undefined;
116
+ value?: {
117
+ val: ValueSpecification | undefined;
118
+ };
48
119
  actions?: {
49
120
  editVariable: () => void;
50
121
  deleteVariable: () => void;
51
122
  };
123
+ extraContextMenuActions?:
124
+ | {
125
+ key: string;
126
+ label: string;
127
+ handler: () => void;
128
+ }[]
129
+ | undefined;
52
130
  }) => {
53
- const { variable, constantValue, actions, isReadOnly, queryBuilderState } =
54
- props;
55
-
56
- const getNameOfValue = (value: ValueSpecification): string | undefined => {
57
- if (value instanceof SimpleFunctionExpression) {
58
- const possibleDateLabel = buildDatePickerOption(
59
- value,
60
- queryBuilderState.applicationStore,
61
- ).label;
62
- if (possibleDateLabel) {
63
- return possibleDateLabel;
64
- }
65
- }
66
- return getValueSpecificationStringValue(value);
67
- };
68
-
69
- const valueString = constantValue
70
- ? getNameOfValue(constantValue)
131
+ const {
132
+ variable,
133
+ value,
134
+ actions,
135
+ isReadOnly,
136
+ queryBuilderState,
137
+ extraContextMenuActions,
138
+ } = props;
139
+ const isVariableUsed = queryBuilderState.isVariableUsed(variable);
140
+ const [isSelectedFromContextMenu, setIsSelectedFromContextMenu] =
141
+ useState(false);
142
+ const onContextMenuOpen = (): void => setIsSelectedFromContextMenu(true);
143
+ const onContextMenuClose = (): void => setIsSelectedFromContextMenu(false);
144
+ const isConstant = Boolean(value);
145
+ const constantValueString = value?.val
146
+ ? getNameOfValue(value.val, queryBuilderState)
71
147
  : undefined;
72
148
  const name = variable.name;
73
- const variableType = variable.genericType?.value.rawType;
74
- const typeName = variableType?.name;
75
- const isVariableUsed = queryBuilderState.isVariableUsed(variable);
149
+ const variableTypeName =
150
+ variable.genericType?.value.rawType.name ??
151
+ (isConstant ? CALCULATED : undefined);
76
152
  const deleteDisabled = isReadOnly || isVariableUsed;
77
153
  const deleteTitle = isVariableUsed ? 'Used in query' : 'Remove';
78
154
  const editVariable = (): void => {
@@ -81,6 +157,7 @@ export const VariableViewer = observer(
81
157
  const deleteVariable = (): void => {
82
158
  actions?.deleteVariable();
83
159
  };
160
+
84
161
  const [, dragConnector, dragPreviewConnector] = useDrag(
85
162
  () => ({
86
163
  type: QUERY_BUILDER_VARIABLE_DND_TYPE,
@@ -92,69 +169,96 @@ export const VariableViewer = observer(
92
169
 
93
170
  return (
94
171
  <div className="query-builder__variables__variable" ref={dragConnector}>
95
- <DragPreviewLayer
96
- labelGetter={(item: QueryBuilderVariableDragSource): string =>
97
- item.variable.name === '' ? '(unknown)' : item.variable.name
172
+ <ContextMenu
173
+ content={
174
+ <QueryBuilderVariableContextMenu
175
+ variable={variable}
176
+ variableInUse={isVariableUsed}
177
+ actions={actions}
178
+ extraContextMenuActions={extraContextMenuActions}
179
+ />
98
180
  }
99
- types={[QUERY_BUILDER_VARIABLE_DND_TYPE]}
100
- />
101
- <div
102
- onClick={editVariable}
103
- className="query-builder__variables__variable__content"
181
+ disabled={isReadOnly || !actions}
182
+ className={clsx('query-builder__variables__variable__context-menu', {
183
+ 'query-builder__variables__variable--selected-from-context-menu':
184
+ isSelectedFromContextMenu,
185
+ })}
186
+ menuProps={{ elevation: 7 }}
187
+ onOpen={onContextMenuOpen}
188
+ onClose={onContextMenuClose}
104
189
  >
105
- <div className="query-builder__variables__variable__icon">
106
- <div className="query-builder__variables__variable-icon">
107
- {constantValue ? (
108
- <div className="icon query-builder__variables__variable-icon">
109
- C
190
+ <DragPreviewLayer
191
+ labelGetter={(item: QueryBuilderVariableDragSource): string =>
192
+ item.variable.name === '' ? '(unknown)' : item.variable.name
193
+ }
194
+ types={[QUERY_BUILDER_VARIABLE_DND_TYPE]}
195
+ />
196
+ <div
197
+ onClick={editVariable}
198
+ className="query-builder__variables__variable__content"
199
+ >
200
+ <div className="query-builder__variables__variable__icon">
201
+ <div className="query-builder__variables__variable-icon">
202
+ {isConstant ? (
203
+ <div className="icon query-builder__variables__variable-icon">
204
+ C
205
+ </div>
206
+ ) : (
207
+ <DollarIcon />
208
+ )}
209
+ </div>
210
+ </div>
211
+ <div className="query-builder__variables__variable__label">
212
+ {name}
213
+ {isConstant ? (
214
+ <div
215
+ className={clsx('query-builder__constants__value', {
216
+ 'query-builder__constants__value--icon':
217
+ !constantValueString,
218
+ })}
219
+ >
220
+ {constantValueString}
221
+ {!constantValueString && (
222
+ <CalculatorIcon title="Calculated Constant" />
223
+ )}
110
224
  </div>
111
225
  ) : (
112
- <DollarIcon />
226
+ <div className="query-builder__variables__variable__type">
227
+ <div className="query-builder__variables__variable__type__label">
228
+ {variableTypeName ?? 'unknown'}
229
+ </div>
230
+ </div>
113
231
  )}
114
232
  </div>
115
233
  </div>
116
- <div className="query-builder__variables__variable__label">
117
- {name}
118
- {valueString ? (
119
- <div className="query-builder__constants__value">
120
- {valueString}
121
- </div>
122
- ) : (
123
- <div className="query-builder__variables__variable__type">
124
- <div className="query-builder__variables__variable__type__label">
125
- {typeName}
234
+ {actions && (
235
+ <div className="query-builder__variables__variable__actions">
236
+ <button
237
+ className="query-builder__variables__variable__action"
238
+ tabIndex={-1}
239
+ disabled={isReadOnly}
240
+ onClick={editVariable}
241
+ title="Edit"
242
+ >
243
+ <PencilIcon />
244
+ </button>
245
+ <button
246
+ className="query-builder__variables__variable__action"
247
+ tabIndex={-1}
248
+ onClick={deleteVariable}
249
+ disabled={deleteDisabled}
250
+ title={deleteTitle}
251
+ >
252
+ <TimesIcon />
253
+ </button>
254
+ <VariableInfoTooltip variable={variable}>
255
+ <div className="query-builder__variables__variable__action value-spec-editor__variable__info">
256
+ <InfoCircleIcon />
126
257
  </div>
127
- </div>
128
- )}
129
- </div>
130
- </div>
131
- {actions && (
132
- <div className="query-builder__variables__variable__actions">
133
- <button
134
- className="query-builder__variables__variable__action"
135
- tabIndex={-1}
136
- disabled={isReadOnly}
137
- onClick={editVariable}
138
- title="Edit"
139
- >
140
- <PencilIcon />
141
- </button>
142
- <button
143
- className="query-builder__variables__variable__action"
144
- tabIndex={-1}
145
- onClick={deleteVariable}
146
- disabled={deleteDisabled}
147
- title={deleteTitle}
148
- >
149
- <TimesIcon />
150
- </button>
151
- <VariableInfoTooltip variable={variable}>
152
- <div className="query-builder__variables__variable__action value-spec-editor__variable__info">
153
- <InfoCircleIcon />
154
- </div>
155
- </VariableInfoTooltip>
156
- </div>
157
- )}
258
+ </VariableInfoTooltip>
259
+ </div>
260
+ )}
261
+ </ContextMenu>
158
262
  </div>
159
263
  );
160
264
  },
@@ -166,7 +270,6 @@ export const VariableSelector = observer(
166
270
  filterBy?: (variableExpression: VariableExpression) => boolean;
167
271
  }) => {
168
272
  const { queryBuilderState, filterBy } = props;
169
- const isReadOnly = !queryBuilderState.isQuerySupported;
170
273
  const filteredParameterStates =
171
274
  queryBuilderState.parametersState.parameterStates.filter((p) =>
172
275
  filterBy ? filterBy(p.parameter) : true,
@@ -186,7 +289,7 @@ export const VariableSelector = observer(
186
289
  <VariableViewer
187
290
  key={pState.uuid}
188
291
  variable={pState.parameter}
189
- isReadOnly={isReadOnly}
292
+ isReadOnly={true}
190
293
  queryBuilderState={queryBuilderState}
191
294
  />
192
295
  ))}
@@ -197,9 +300,15 @@ export const VariableSelector = observer(
197
300
  <VariableViewer
198
301
  key={constantState.uuid}
199
302
  variable={constantState.variable}
200
- constantValue={constantState.value}
303
+ value={{
304
+ val:
305
+ constantState instanceof
306
+ QueryBuilderSimpleConstantExpressionState
307
+ ? constantState.value
308
+ : undefined,
309
+ }}
201
310
  queryBuilderState={queryBuilderState}
202
- isReadOnly={isReadOnly}
311
+ isReadOnly={true}
203
312
  />
204
313
  ))}
205
314
  </PanelFormListItems>
@@ -17,6 +17,7 @@
17
17
  export enum QUERY_BUILDER_SOURCE_ID_LABEL {
18
18
  QUERY_BUILDER = 'query-builder',
19
19
  PROJECTION = 'projection',
20
+ CONSTANT = 'constant',
20
21
  }
21
22
 
22
23
  export const DEFAULT_LAMBDA_VARIABLE_NAME = 'x';