@finos/legend-query-builder 4.14.9 → 4.14.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. package/lib/__lib__/QueryBuilderEvent.d.ts +2 -1
  2. package/lib/__lib__/QueryBuilderEvent.d.ts.map +1 -1
  3. package/lib/__lib__/QueryBuilderEvent.js +1 -0
  4. package/lib/__lib__/QueryBuilderEvent.js.map +1 -1
  5. package/lib/__lib__/QueryBuilderTesting.d.ts +3 -1
  6. package/lib/__lib__/QueryBuilderTesting.d.ts.map +1 -1
  7. package/lib/__lib__/QueryBuilderTesting.js +3 -0
  8. package/lib/__lib__/QueryBuilderTesting.js.map +1 -1
  9. package/lib/components/QueryBuilder.d.ts.map +1 -1
  10. package/lib/components/QueryBuilder.js +45 -27
  11. package/lib/components/QueryBuilder.js.map +1 -1
  12. package/lib/components/fetch-structure/QueryBuilderResultModifierPanel.d.ts.map +1 -1
  13. package/lib/components/fetch-structure/QueryBuilderResultModifierPanel.js +86 -44
  14. package/lib/components/fetch-structure/QueryBuilderResultModifierPanel.js.map +1 -1
  15. package/lib/components/shared/LambdaEditor.d.ts.map +1 -1
  16. package/lib/components/shared/LambdaEditor.js +10 -5
  17. package/lib/components/shared/LambdaEditor.js.map +1 -1
  18. package/lib/components/shared/QueryBuilderPropertyInfoTooltip.d.ts.map +1 -1
  19. package/lib/components/shared/QueryBuilderPropertyInfoTooltip.js +12 -11
  20. package/lib/components/shared/QueryBuilderPropertyInfoTooltip.js.map +1 -1
  21. package/lib/index.css +2 -2
  22. package/lib/index.css.map +1 -1
  23. package/lib/package.json +1 -1
  24. package/lib/stores/QueryBuilderChangeHistoryState.d.ts +35 -0
  25. package/lib/stores/QueryBuilderChangeHistoryState.d.ts.map +1 -0
  26. package/lib/stores/QueryBuilderChangeHistoryState.js +106 -0
  27. package/lib/stores/QueryBuilderChangeHistoryState.js.map +1 -0
  28. package/lib/stores/QueryBuilderState.d.ts +2 -0
  29. package/lib/stores/QueryBuilderState.d.ts.map +1 -1
  30. package/lib/stores/QueryBuilderState.js +6 -0
  31. package/lib/stores/QueryBuilderState.js.map +1 -1
  32. package/lib/stores/fetch-structure/tds/QueryResultSetModifierState.d.ts +2 -2
  33. package/lib/stores/fetch-structure/tds/QueryResultSetModifierState.d.ts.map +1 -1
  34. package/lib/stores/fetch-structure/tds/QueryResultSetModifierState.js +7 -7
  35. package/lib/stores/fetch-structure/tds/QueryResultSetModifierState.js.map +1 -1
  36. package/package.json +5 -5
  37. package/src/__lib__/QueryBuilderEvent.ts +2 -0
  38. package/src/__lib__/QueryBuilderTesting.ts +3 -0
  39. package/src/components/QueryBuilder.tsx +41 -0
  40. package/src/components/fetch-structure/QueryBuilderResultModifierPanel.tsx +174 -94
  41. package/src/components/shared/LambdaEditor.tsx +10 -8
  42. package/src/components/shared/QueryBuilderPropertyInfoTooltip.tsx +81 -65
  43. package/src/stores/QueryBuilderChangeHistoryState.ts +129 -0
  44. package/src/stores/QueryBuilderState.ts +6 -0
  45. package/src/stores/fetch-structure/tds/QueryResultSetModifierState.ts +7 -12
  46. package/tsconfig.json +1 -0
@@ -18,6 +18,7 @@ import {
18
18
  ShareBoxIcon,
19
19
  type TooltipPlacement,
20
20
  Tooltip,
21
+ ClickAwayListener,
21
22
  } from '@finos/legend-art';
22
23
  import {
23
24
  type AbstractProperty,
@@ -137,73 +138,88 @@ export const QueryBuilderPropertyInfoTooltip: React.FC<{
137
138
  explorerState,
138
139
  } = props;
139
140
 
141
+ const [open, setIsOpen] = useState(false);
142
+
140
143
  return (
141
- <Tooltip
142
- arrow={true}
143
- {...(placement !== undefined ? { placement } : {})}
144
- classes={{
145
- tooltip: 'query-builder__tooltip',
146
- arrow: 'query-builder__tooltip__arrow',
147
- tooltipPlacementRight: 'query-builder__tooltip--right',
148
- }}
149
- TransitionProps={{
150
- // disable transition
151
- // NOTE: somehow, this is the only workaround we have, if for example
152
- // we set `appear = true`, the tooltip will jump out of position
153
- timeout: 0,
154
- }}
155
- title={
156
- <div className="query-builder__tooltip__content">
157
- <div className="query-builder__tooltip__header">{title}</div>
158
- <div className="query-builder__tooltip__item">
159
- <div className="query-builder__tooltip__item__label">Type</div>
160
- <div className="query-builder__tooltip__item__value">
161
- {type?.path ?? property.genericType.value.rawType.path}
162
- </div>
163
- </div>
164
- <div className="query-builder__tooltip__item">
165
- <div className="query-builder__tooltip__item__label">Path</div>
166
- <div className="query-builder__tooltip__item__value">{path}</div>
167
- {explorerState && (
168
- <div className="query-builder__tooltip__item__action">
169
- <button
170
- onClick={() => explorerState.highlightTreeNode(path)}
171
- title="Show in tree"
172
- >
173
- <ShareBoxIcon color="white" />
174
- </button>
144
+ <ClickAwayListener onClickAway={() => setIsOpen(false)}>
145
+ <div>
146
+ <Tooltip
147
+ arrow={true}
148
+ {...(placement !== undefined ? { placement } : {})}
149
+ classes={{
150
+ tooltip: 'query-builder__tooltip',
151
+ arrow: 'query-builder__tooltip__arrow',
152
+ tooltipPlacementRight: 'query-builder__tooltip--right',
153
+ }}
154
+ open={open}
155
+ onClose={() => setIsOpen(false)}
156
+ TransitionProps={{
157
+ // disable transition
158
+ // NOTE: somehow, this is the only workaround we have, if for example
159
+ // we set `appear = true`, the tooltip will jump out of position
160
+ timeout: 0,
161
+ }}
162
+ disableFocusListener={true}
163
+ disableHoverListener={true}
164
+ disableTouchListener={true}
165
+ title={
166
+ <div className="query-builder__tooltip__content">
167
+ <div className="query-builder__tooltip__header">{title}</div>
168
+ <div className="query-builder__tooltip__item">
169
+ <div className="query-builder__tooltip__item__label">Type</div>
170
+ <div className="query-builder__tooltip__item__value">
171
+ {type?.path ?? property.genericType.value.rawType.path}
172
+ </div>
175
173
  </div>
176
- )}
177
- </div>
178
- <div className="query-builder__tooltip__item">
179
- <div className="query-builder__tooltip__item__label">
180
- Multiplicity
181
- </div>
182
- <div className="query-builder__tooltip__item__value">
183
- {getMultiplicityDescription(property.multiplicity)}
184
- </div>
185
- </div>
186
- <div className="query-builder__tooltip__item">
187
- <div className="query-builder__tooltip__item__label">
188
- Derived Property
189
- </div>
190
- <div className="query-builder__tooltip__item__value">
191
- {property instanceof DerivedProperty ? 'Yes' : 'No'}
192
- </div>
193
- </div>
194
- <div className="query-builder__tooltip__item">
195
- <div className="query-builder__tooltip__item__label">Mapped</div>
196
- <div className="query-builder__tooltip__item__value">
197
- {isMapped ? 'Yes' : 'No'}
174
+ <div className="query-builder__tooltip__item">
175
+ <div className="query-builder__tooltip__item__label">Path</div>
176
+ <div className="query-builder__tooltip__item__value">
177
+ {path}
178
+ </div>
179
+ {explorerState && (
180
+ <div className="query-builder__tooltip__item__action">
181
+ <button
182
+ onClick={() => explorerState.highlightTreeNode(path)}
183
+ title="Show in tree"
184
+ >
185
+ <ShareBoxIcon color="white" />
186
+ </button>
187
+ </div>
188
+ )}
189
+ </div>
190
+ <div className="query-builder__tooltip__item">
191
+ <div className="query-builder__tooltip__item__label">
192
+ Multiplicity
193
+ </div>
194
+ <div className="query-builder__tooltip__item__value">
195
+ {getMultiplicityDescription(property.multiplicity)}
196
+ </div>
197
+ </div>
198
+ <div className="query-builder__tooltip__item">
199
+ <div className="query-builder__tooltip__item__label">
200
+ Derived Property
201
+ </div>
202
+ <div className="query-builder__tooltip__item__value">
203
+ {property instanceof DerivedProperty ? 'Yes' : 'No'}
204
+ </div>
205
+ </div>
206
+ <div className="query-builder__tooltip__item">
207
+ <div className="query-builder__tooltip__item__label">
208
+ Mapped
209
+ </div>
210
+ <div className="query-builder__tooltip__item__value">
211
+ {isMapped ? 'Yes' : 'No'}
212
+ </div>
213
+ </div>
214
+ <QueryBuilderTaggedValueInfoTooltip
215
+ taggedValues={property.taggedValues}
216
+ />
198
217
  </div>
199
- </div>
200
- <QueryBuilderTaggedValueInfoTooltip
201
- taggedValues={property.taggedValues}
202
- />
203
- </div>
204
- }
205
- >
206
- {children}
207
- </Tooltip>
218
+ }
219
+ >
220
+ <div onClick={() => setIsOpen(true)}>{children}</div>
221
+ </Tooltip>
222
+ </div>
223
+ </ClickAwayListener>
208
224
  );
209
225
  };
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Copyright (c) 2020-present, Goldman Sachs
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import { type RawLambda } from '@finos/legend-graph';
18
+ import {
19
+ ActionState,
20
+ LogEvent,
21
+ assertErrorThrown,
22
+ guaranteeNonNullable,
23
+ } from '@finos/legend-shared';
24
+ import { makeObservable, observable, computed, action } from 'mobx';
25
+ import type { QueryBuilderState } from './QueryBuilderState.js';
26
+ import { QUERY_BUILDER_EVENT } from '../__lib__/QueryBuilderEvent.js';
27
+
28
+ export class QueryBuilderChangeHistoryState {
29
+ readonly queryBuilderState: QueryBuilderState;
30
+ readonly initState = ActionState.create();
31
+
32
+ querySnapshotBuffer: RawLambda[] = [];
33
+ currentQuery: RawLambda | undefined;
34
+ pointer = -1;
35
+ bufferSize = 10;
36
+
37
+ constructor(queryBuilderState: QueryBuilderState) {
38
+ makeObservable(this, {
39
+ currentQuery: observable,
40
+ pointer: observable,
41
+ querySnapshotBuffer: observable,
42
+ initialize: action,
43
+ undo: action,
44
+ redo: action,
45
+ setCurrentQuery: action,
46
+ cacheNewQuery: action,
47
+ canRedo: computed,
48
+ canUndo: computed,
49
+ });
50
+
51
+ this.queryBuilderState = queryBuilderState;
52
+ }
53
+
54
+ get canRedo(): boolean {
55
+ return this.pointer < this.querySnapshotBuffer.length - 1;
56
+ }
57
+
58
+ get canUndo(): boolean {
59
+ return this.pointer > 0;
60
+ }
61
+
62
+ redo(): void {
63
+ if (this.canRedo) {
64
+ this.pointer = this.pointer + 1;
65
+ const query = this.querySnapshotBuffer[this.pointer];
66
+ this.setCurrentQuery(query);
67
+ this.queryBuilderState.rebuildWithQuery(guaranteeNonNullable(query), {
68
+ preserveParameterValues: true,
69
+ preserveResult: true,
70
+ });
71
+ }
72
+ }
73
+
74
+ undo(): void {
75
+ if (this.canUndo) {
76
+ this.pointer = this.pointer - 1;
77
+ const query = this.querySnapshotBuffer[this.pointer];
78
+ this.setCurrentQuery(query);
79
+ this.queryBuilderState.rebuildWithQuery(guaranteeNonNullable(query), {
80
+ preserveParameterValues: true,
81
+ preserveResult: true,
82
+ });
83
+ }
84
+ }
85
+
86
+ setCurrentQuery(query: RawLambda | undefined): void {
87
+ this.currentQuery = query;
88
+ }
89
+
90
+ initialize(initialQuery: RawLambda): void {
91
+ if (this.queryBuilderState.isQuerySupported) {
92
+ this.initState.inProgress();
93
+ this.currentQuery = initialQuery;
94
+ this.querySnapshotBuffer.push(initialQuery);
95
+ this.pointer = this.pointer + 1;
96
+ this.initState.complete();
97
+ }
98
+ }
99
+
100
+ cacheNewQuery(query: RawLambda): void {
101
+ try {
102
+ if (this.queryBuilderState.isQuerySupported) {
103
+ if (query.hashCode !== this.currentQuery?.hashCode) {
104
+ if (
105
+ this.querySnapshotBuffer.length === this.bufferSize &&
106
+ this.pointer === this.querySnapshotBuffer.length - 1
107
+ ) {
108
+ // only record 10 query snapshots
109
+ this.querySnapshotBuffer = this.querySnapshotBuffer.slice(1);
110
+ } else {
111
+ this.querySnapshotBuffer = this.querySnapshotBuffer.slice(
112
+ 0,
113
+ this.pointer + 1,
114
+ );
115
+ }
116
+ this.querySnapshotBuffer.push(query);
117
+ this.pointer = this.querySnapshotBuffer.length - 1;
118
+ this.setCurrentQuery(query);
119
+ }
120
+ }
121
+ } catch (error) {
122
+ assertErrorThrown(error);
123
+ this.queryBuilderState.applicationStore.logService.error(
124
+ LogEvent.create(QUERY_BUILDER_EVENT.CHANGE_HISTORY_ERROR),
125
+ `Can't cache query in query builder change history buffer: ${error.message}`,
126
+ );
127
+ }
128
+ }
129
+ }
@@ -104,6 +104,7 @@ import {
104
104
  } from './QueryBuilderExecutionContextState.js';
105
105
  import type { QueryBuilderConfig } from '../graph-manager/QueryBuilderConfig.js';
106
106
  import { QUERY_BUILDER_EVENT } from '../__lib__/QueryBuilderEvent.js';
107
+ import { QueryBuilderChangeHistoryState } from './QueryBuilderChangeHistoryState.js';
107
108
 
108
109
  export interface QuerySDLC {}
109
110
 
@@ -136,6 +137,7 @@ export abstract class QueryBuilderState implements CommandRegistrar {
136
137
  resultState: QueryBuilderResultState;
137
138
  textEditorState: QueryBuilderTextEditorState;
138
139
  unsupportedQueryState: QueryBuilderUnsupportedQueryState;
140
+ changeHistoryState: QueryBuilderChangeHistoryState;
139
141
  showFunctionsExplorerPanel = false;
140
142
  showParametersPanel = false;
141
143
  isEditingWatermark = false;
@@ -184,6 +186,7 @@ export abstract class QueryBuilderState implements CommandRegistrar {
184
186
  isCheckingEntitlments: observable,
185
187
  isCalendarEnabled: observable,
186
188
  changeDetectionState: observable,
189
+ changeHistoryState: observable,
187
190
  executionContextState: observable,
188
191
  class: observable,
189
192
  isQueryChatOpened: observable,
@@ -236,6 +239,7 @@ export abstract class QueryBuilderState implements CommandRegistrar {
236
239
  this.graphManagerState.pluginManager.getPureGraphManagerPlugins(),
237
240
  );
238
241
  this.changeDetectionState = new QueryBuilderChangeDetectionState(this);
242
+ this.changeHistoryState = new QueryBuilderChangeHistoryState(this);
239
243
  this.config = config;
240
244
  this.sourceInfo = sourceInfo;
241
245
  }
@@ -441,6 +445,7 @@ export abstract class QueryBuilderState implements CommandRegistrar {
441
445
  this.explorerState.refreshTreeData();
442
446
  this.fetchStructureState.implementation.onClassChange(val);
443
447
  this.milestoningState.updateMilestoningConfiguration();
448
+ this.changeHistoryState.cacheNewQuery(this.buildQuery());
444
449
  }
445
450
 
446
451
  changeMapping(val: Mapping, options?: { keepQueryContent?: boolean }): void {
@@ -553,6 +558,7 @@ export abstract class QueryBuilderState implements CommandRegistrar {
553
558
  });
554
559
  this.resetQueryResult();
555
560
  this.changeDetectionState.initialize(query);
561
+ this.changeHistoryState.initialize(query);
556
562
  }
557
563
 
558
564
  /**
@@ -16,12 +16,7 @@
16
16
 
17
17
  import { action, computed, makeObservable, observable } from 'mobx';
18
18
  import type { QueryBuilderTDSState } from './QueryBuilderTDSState.js';
19
- import {
20
- addUniqueEntry,
21
- deleteEntry,
22
- type Hashable,
23
- hashArray,
24
- } from '@finos/legend-shared';
19
+ import { addUniqueEntry, type Hashable, hashArray } from '@finos/legend-shared';
25
20
  import { QUERY_BUILDER_STATE_HASH_STRUCTURE } from '../../QueryBuilderStateHashUtils.js';
26
21
  import type { QueryBuilderTDSColumnState } from './QueryBuilderTDSColumnState.js';
27
22
  import { COLUMN_SORT_TYPE } from '../../../graph/QueryBuilderMetaModelConst.js';
@@ -76,8 +71,8 @@ export class QueryResultSetModifierState implements Hashable {
76
71
  slice: observable.ref,
77
72
  setShowModal: action,
78
73
  setLimit: action,
79
- toggleDistinct: action,
80
- deleteSortColumn: action,
74
+ setDistinct: action,
75
+ setSortColumns: action,
81
76
  addSortColumn: action,
82
77
  updateSortColumns: action,
83
78
  setSlice: action,
@@ -96,12 +91,12 @@ export class QueryResultSetModifierState implements Hashable {
96
91
  this.limit = val === undefined || val <= 0 ? undefined : val;
97
92
  }
98
93
 
99
- toggleDistinct(): void {
100
- this.distinct = !this.distinct;
94
+ setDistinct(val: boolean): void {
95
+ this.distinct = val;
101
96
  }
102
97
 
103
- deleteSortColumn(val: SortColumnState): void {
104
- deleteEntry(this.sortColumns, val);
98
+ setSortColumns(val: SortColumnState[]): void {
99
+ this.sortColumns = val;
105
100
  }
106
101
 
107
102
  addSortColumn(val: SortColumnState): void {
package/tsconfig.json CHANGED
@@ -56,6 +56,7 @@
56
56
  "./src/graph-manager/protocol/pure/v1/V1_QueryBuilder_PureGraphManagerExtension.ts",
57
57
  "./src/graph-manager/protocol/pure/v1/V1_QueryValueSpecificationBuilderHelper.ts",
58
58
  "./src/stores/QueryBuilderChangeDetectionState.ts",
59
+ "./src/stores/QueryBuilderChangeHistoryState.ts",
59
60
  "./src/stores/QueryBuilderCommand.ts",
60
61
  "./src/stores/QueryBuilderConfig.ts",
61
62
  "./src/stores/QueryBuilderConstantsState.ts",