@finos/legend-query-builder 4.11.4 → 4.11.6

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 (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
+ );