@finos/legend-query-builder 4.11.4 → 4.11.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. package/lib/components/QueryBuilder.js +1 -1
  2. package/lib/components/QueryBuilder.js.map +1 -1
  3. package/lib/components/execution-plan/SQLExecutionNodeViewer.js +1 -1
  4. package/lib/components/execution-plan/SQLExecutionNodeViewer.js.map +1 -1
  5. package/lib/components/{QueryBuilderResultPanel.d.ts → result/QueryBuilderResultPanel.d.ts} +1 -2
  6. package/lib/components/result/QueryBuilderResultPanel.d.ts.map +1 -0
  7. package/lib/components/result/QueryBuilderResultPanel.js +185 -0
  8. package/lib/components/result/QueryBuilderResultPanel.js.map +1 -0
  9. package/lib/components/result/tds/QueryBuilderTDSGridResult.d.ts +24 -0
  10. package/lib/components/result/tds/QueryBuilderTDSGridResult.d.ts.map +1 -0
  11. package/lib/components/result/tds/QueryBuilderTDSGridResult.js +204 -0
  12. package/lib/components/result/tds/QueryBuilderTDSGridResult.js.map +1 -0
  13. package/lib/components/result/tds/QueryBuilderTDSResultShared.d.ts +37 -0
  14. package/lib/components/result/tds/QueryBuilderTDSResultShared.d.ts.map +1 -0
  15. package/lib/components/result/tds/QueryBuilderTDSResultShared.js +251 -0
  16. package/lib/components/result/tds/QueryBuilderTDSResultShared.js.map +1 -0
  17. package/lib/components/result/tds/QueryBuilderTDSSimpleGridResult.d.ts +24 -0
  18. package/lib/components/result/tds/QueryBuilderTDSSimpleGridResult.d.ts.map +1 -0
  19. package/lib/components/result/tds/QueryBuilderTDSSimpleGridResult.js +224 -0
  20. package/lib/components/result/tds/QueryBuilderTDSSimpleGridResult.js.map +1 -0
  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/QueryBuilderResultState.d.ts +5 -1
  25. package/lib/stores/QueryBuilderResultState.d.ts.map +1 -1
  26. package/lib/stores/QueryBuilderResultState.js.map +1 -1
  27. package/package.json +3 -3
  28. package/src/components/QueryBuilder.tsx +1 -1
  29. package/src/components/execution-plan/SQLExecutionNodeViewer.tsx +1 -1
  30. package/src/components/result/QueryBuilderResultPanel.tsx +569 -0
  31. package/src/components/result/tds/QueryBuilderTDSGridResult.tsx +346 -0
  32. package/src/components/result/tds/QueryBuilderTDSResultShared.tsx +502 -0
  33. package/src/components/result/tds/QueryBuilderTDSSimpleGridResult.tsx +387 -0
  34. package/src/stores/QueryBuilderResultState.ts +12 -1
  35. package/tsconfig.json +4 -1
  36. package/lib/components/QueryBuilderResultPanel.d.ts.map +0 -1
  37. package/lib/components/QueryBuilderResultPanel.js +0 -633
  38. package/lib/components/QueryBuilderResultPanel.js.map +0 -1
  39. package/src/components/QueryBuilderResultPanel.tsx +0 -1412
@@ -0,0 +1,346 @@
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 { clsx } from '@finos/legend-art';
18
+ import { observer } from 'mobx-react-lite';
19
+ import type { QueryBuilderState } from '../../../stores/QueryBuilderState.js';
20
+ import type { TDSExecutionResult } from '@finos/legend-graph';
21
+ import { useState, useCallback } from 'react';
22
+ import {
23
+ DataGrid,
24
+ type DataGridApi,
25
+ type DataGridCellRange,
26
+ type DataGridColumnApi,
27
+ type DataGridColumnDefinition,
28
+ type DataGridGetContextMenuItemsParams,
29
+ type DataGridIRowNode,
30
+ type DataGridMenuItemDef,
31
+ } from '@finos/legend-lego/data-grid';
32
+ import {
33
+ getAggregationTDSColumnCustomizations,
34
+ getRowDataFromExecutionResult,
35
+ type IQueryRendererParamsWithGridType,
36
+ filterByOrOutValues,
37
+ } from './QueryBuilderTDSResultShared.js';
38
+ import type {
39
+ QueryBuilderResultState,
40
+ QueryBuilderTDSResultCellData,
41
+ QueryBuilderTDSResultCellDataType,
42
+ QueryBuilderTDSRowDataType,
43
+ } from '../../../stores/QueryBuilderResultState.js';
44
+ import { QueryBuilderTDSState } from '../../../stores/fetch-structure/tds/QueryBuilderTDSState.js';
45
+ import { DEFAULT_LOCALE } from '../../../graph-manager/QueryBuilderConst.js';
46
+ import { isNumber, isString, isValidURL } from '@finos/legend-shared';
47
+ import { useApplicationStore } from '@finos/legend-application';
48
+
49
+ const getAdvancedColDefs = (
50
+ executionResult: TDSExecutionResult,
51
+ resultState: QueryBuilderResultState,
52
+ ): DataGridColumnDefinition<
53
+ QueryBuilderTDSRowDataType,
54
+ QueryBuilderTDSResultCellDataType
55
+ >[] =>
56
+ executionResult.result.columns.map((colName) => {
57
+ const col = {
58
+ minWidth: 50,
59
+ sortable: true,
60
+ resizable: true,
61
+ field: colName,
62
+ flex: 1,
63
+ enablePivot: true,
64
+ enableRowGroup: true,
65
+ enableValue: true,
66
+ ...getAggregationTDSColumnCustomizations(executionResult, colName),
67
+ } as DataGridColumnDefinition;
68
+ const persistedColumn = resultState.gridConfig.columns.find(
69
+ (c) => c.colId === colName,
70
+ );
71
+ if (persistedColumn) {
72
+ if (persistedColumn.width) {
73
+ col.width = persistedColumn.width;
74
+ }
75
+ col.pinned = persistedColumn.pinned ?? null;
76
+ col.rowGroup = persistedColumn.rowGroup ?? false;
77
+ col.rowGroupIndex = persistedColumn.rowGroupIndex ?? null;
78
+ col.aggFunc = persistedColumn.aggFunc ?? null;
79
+ col.pivot = persistedColumn.pivot ?? false;
80
+ col.hide = persistedColumn.hide ?? false;
81
+ }
82
+ return col;
83
+ });
84
+
85
+ const QueryResultCellRenderer = observer(
86
+ (params: IQueryRendererParamsWithGridType) => {
87
+ const resultState = params.resultState;
88
+ const cellValue = params.value as QueryBuilderTDSResultCellDataType;
89
+ const formattedCellValue = (): QueryBuilderTDSResultCellDataType => {
90
+ if (isNumber(cellValue)) {
91
+ return Intl.NumberFormat(DEFAULT_LOCALE, {
92
+ maximumFractionDigits: 4,
93
+ }).format(Number(cellValue));
94
+ }
95
+ return cellValue;
96
+ };
97
+ const cellValueUrlLink =
98
+ isString(cellValue) && isValidURL(cellValue) ? cellValue : undefined;
99
+
100
+ const mouseDown: React.MouseEventHandler = (event) => {
101
+ event.preventDefault();
102
+ if (event.button === 0 || event.button === 2) {
103
+ resultState.setMouseOverCell(resultState.selectedCells[0] ?? null);
104
+ }
105
+ };
106
+ const mouseUp: React.MouseEventHandler = (event) => {
107
+ resultState.setIsSelectingCells(false);
108
+ };
109
+ const mouseOver: React.MouseEventHandler = (event) => {
110
+ resultState.setMouseOverCell(resultState.selectedCells[0] ?? null);
111
+ };
112
+ return (
113
+ <div
114
+ className={clsx('query-builder__result__values__table__cell')}
115
+ onMouseDown={(event) => mouseDown(event)}
116
+ onMouseUp={(event) => mouseUp(event)}
117
+ onMouseOver={(event) => mouseOver(event)}
118
+ >
119
+ {cellValueUrlLink ? (
120
+ <a href={cellValueUrlLink} target="_blank" rel="noreferrer">
121
+ {cellValueUrlLink}
122
+ </a>
123
+ ) : (
124
+ <span>{formattedCellValue()}</span>
125
+ )}
126
+ </div>
127
+ );
128
+ },
129
+ );
130
+
131
+ const getColDefs = (
132
+ executionResult: TDSExecutionResult,
133
+ resultState: QueryBuilderResultState,
134
+ ): DataGridColumnDefinition<
135
+ QueryBuilderTDSRowDataType,
136
+ QueryBuilderTDSResultCellDataType
137
+ >[] =>
138
+ executionResult.result.columns.map(
139
+ (colName) =>
140
+ ({
141
+ minWidth: 50,
142
+ sortable: true,
143
+ resizable: true,
144
+ field: colName,
145
+ flex: 1,
146
+ cellRenderer: QueryResultCellRenderer,
147
+ cellRendererParams: {
148
+ resultState: resultState,
149
+ tdsExecutionResult: executionResult,
150
+ },
151
+ }) as DataGridColumnDefinition,
152
+ );
153
+
154
+ export const QueryBuilderTDSGridResult = observer(
155
+ (props: {
156
+ executionResult: TDSExecutionResult;
157
+ queryBuilderState: QueryBuilderState;
158
+ }) => {
159
+ const { executionResult, queryBuilderState } = props;
160
+ const applicationStore = useApplicationStore();
161
+ const [columnAPi, setColumnApi] = useState<DataGridColumnApi | undefined>(
162
+ undefined,
163
+ );
164
+ const resultState = queryBuilderState.resultState;
165
+ const isAdvancedModeEnabled = queryBuilderState.isAdvancedModeEnabled;
166
+ const colDefs = isAdvancedModeEnabled
167
+ ? getAdvancedColDefs(executionResult, resultState)
168
+ : getColDefs(executionResult, resultState);
169
+
170
+ const onSaveGridColumnState = (): void => {
171
+ if (!columnAPi) {
172
+ return;
173
+ }
174
+ resultState.setGridConfig({
175
+ columns: columnAPi.getColumnState(),
176
+ isPivotModeEnabled: columnAPi.isPivotMode(),
177
+ });
178
+ };
179
+
180
+ const getSelectedCells = (
181
+ api: DataGridApi<QueryBuilderTDSRowDataType>,
182
+ ): QueryBuilderTDSResultCellData[] => {
183
+ const selectedRanges: DataGridCellRange[] | null = api.getCellRanges();
184
+ const nodes = api.getRenderedNodes();
185
+ const columns = api.getColumnDefs() as DataGridColumnDefinition[];
186
+ const selectedCells = [];
187
+ if (selectedRanges) {
188
+ for (const selectedRange of selectedRanges) {
189
+ const startRow: number = selectedRange.startRow?.rowIndex ?? 0;
190
+ const endRow: number = selectedRange.endRow?.rowIndex ?? 0;
191
+ const selectedColumns: string[] = selectedRange.columns.map((col) =>
192
+ col.getColId(),
193
+ );
194
+ for (let x: number = startRow; x <= endRow; x++) {
195
+ const curRowData = nodes.find(
196
+ (n) => (n as DataGridIRowNode).rowIndex === x,
197
+ )?.data;
198
+ if (curRowData) {
199
+ for (const col of selectedColumns) {
200
+ const valueAndColumnId = {
201
+ value: Object.entries(curRowData)
202
+ .find((rData) => rData[0] === col)
203
+ ?.at(1),
204
+ columnName: col,
205
+ coordinates: {
206
+ rowIndex: x,
207
+ colIndex: columns.findIndex(
208
+ (colDef) => colDef.colId === col,
209
+ ),
210
+ },
211
+ } as QueryBuilderTDSResultCellData;
212
+ selectedCells.push(valueAndColumnId);
213
+ }
214
+ }
215
+ }
216
+ }
217
+ }
218
+ return selectedCells;
219
+ };
220
+
221
+ const getContextMenuItems = useCallback(
222
+ (
223
+ params: DataGridGetContextMenuItemsParams<QueryBuilderTDSRowDataType>,
224
+ ): (string | DataGridMenuItemDef)[] => {
225
+ let result: (string | DataGridMenuItemDef)[] = [];
226
+ const fetchStructureImplementation =
227
+ resultState.queryBuilderState.fetchStructureState.implementation;
228
+ if (fetchStructureImplementation instanceof QueryBuilderTDSState) {
229
+ result = [
230
+ {
231
+ name: 'Filter by',
232
+ action: () => {
233
+ filterByOrOutValues(
234
+ applicationStore,
235
+ resultState.mousedOverCell,
236
+ true,
237
+ fetchStructureImplementation,
238
+ );
239
+ },
240
+ },
241
+ {
242
+ name: 'Filter out',
243
+ action: () => {
244
+ filterByOrOutValues(
245
+ applicationStore,
246
+ resultState.mousedOverCell,
247
+ false,
248
+ fetchStructureImplementation,
249
+ );
250
+ },
251
+ },
252
+ 'copy',
253
+ 'copyWithHeaders',
254
+ {
255
+ name: 'Copy row value',
256
+ action: () => {
257
+ params.api.copySelectedRowsToClipboard();
258
+ },
259
+ },
260
+ ];
261
+ }
262
+ return result;
263
+ },
264
+ [
265
+ applicationStore,
266
+ resultState.mousedOverCell,
267
+ resultState.queryBuilderState.fetchStructureState.implementation,
268
+ ],
269
+ );
270
+
271
+ return (
272
+ <div className="query-builder__result__values__table">
273
+ <div
274
+ className={clsx(
275
+ 'ag-theme-balham-dark query-builder__result__tds-grid',
276
+ )}
277
+ >
278
+ {isAdvancedModeEnabled ? (
279
+ <DataGrid
280
+ rowData={getRowDataFromExecutionResult(executionResult)}
281
+ onGridReady={(params): void => {
282
+ setColumnApi(params.columnApi);
283
+ params.columnApi.setPivotMode(
284
+ resultState.gridConfig.isPivotModeEnabled,
285
+ );
286
+ }}
287
+ gridOptions={{
288
+ suppressScrollOnNewData: true,
289
+ getRowId: (data) => data.data.rowNumber as string,
290
+ rowSelection: 'multiple',
291
+ pivotPanelShow: 'always',
292
+ rowGroupPanelShow: 'always',
293
+ enableRangeSelection: true,
294
+ }}
295
+ // NOTE: when column definition changed, we need to force refresh the cell to make sure the cell renderer is updated
296
+ // See https://stackoverflow.com/questions/56341073/how-to-refresh-an-ag-grid-when-a-change-occurs-inside-a-custom-cell-renderer-com
297
+ onRowDataUpdated={(params) => {
298
+ params.api.refreshCells({ force: true });
299
+ }}
300
+ suppressFieldDotNotation={true}
301
+ suppressContextMenu={false}
302
+ columnDefs={colDefs}
303
+ sideBar={['columns', 'filters']}
304
+ onColumnVisible={onSaveGridColumnState}
305
+ onColumnPinned={onSaveGridColumnState}
306
+ onColumnResized={onSaveGridColumnState}
307
+ onColumnRowGroupChanged={onSaveGridColumnState}
308
+ onColumnValueChanged={onSaveGridColumnState}
309
+ onColumnPivotChanged={onSaveGridColumnState}
310
+ onColumnPivotModeChanged={onSaveGridColumnState}
311
+ />
312
+ ) : (
313
+ <DataGrid
314
+ rowData={getRowDataFromExecutionResult(executionResult)}
315
+ gridOptions={{
316
+ suppressScrollOnNewData: true,
317
+ getRowId: (data) => data.data.rowNumber as string,
318
+ rowSelection: 'multiple',
319
+ enableRangeSelection: true,
320
+ }}
321
+ // NOTE: when column definition changed, we need to force refresh the cell to make sure the cell renderer is updated
322
+ // See https://stackoverflow.com/questions/56341073/how-to-refresh-an-ag-grid-when-a-change-occurs-inside-a-custom-cell-renderer-com
323
+ onRowDataUpdated={(params) => {
324
+ params.api.refreshCells({ force: true });
325
+ }}
326
+ onRangeSelectionChanged={(event) => {
327
+ const selectedCells = getSelectedCells(event.api);
328
+ resultState.setSelectedCells([]);
329
+ selectedCells.forEach((cell) =>
330
+ resultState.addSelectedCell(cell),
331
+ );
332
+ }}
333
+ suppressFieldDotNotation={true}
334
+ suppressClipboardPaste={false}
335
+ suppressContextMenu={false}
336
+ columnDefs={colDefs}
337
+ getContextMenuItems={(params): (string | DataGridMenuItemDef)[] =>
338
+ getContextMenuItems(params)
339
+ }
340
+ />
341
+ )}
342
+ </div>
343
+ </div>
344
+ );
345
+ },
346
+ );