@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.
- package/lib/components/QueryBuilder.js +1 -1
- package/lib/components/QueryBuilder.js.map +1 -1
- package/lib/components/execution-plan/SQLExecutionNodeViewer.js +1 -1
- package/lib/components/execution-plan/SQLExecutionNodeViewer.js.map +1 -1
- package/lib/components/{QueryBuilderResultPanel.d.ts → result/QueryBuilderResultPanel.d.ts} +1 -2
- package/lib/components/result/QueryBuilderResultPanel.d.ts.map +1 -0
- package/lib/components/result/QueryBuilderResultPanel.js +185 -0
- package/lib/components/result/QueryBuilderResultPanel.js.map +1 -0
- package/lib/components/result/tds/QueryBuilderTDSGridResult.d.ts +24 -0
- package/lib/components/result/tds/QueryBuilderTDSGridResult.d.ts.map +1 -0
- package/lib/components/result/tds/QueryBuilderTDSGridResult.js +204 -0
- package/lib/components/result/tds/QueryBuilderTDSGridResult.js.map +1 -0
- package/lib/components/result/tds/QueryBuilderTDSResultShared.d.ts +37 -0
- package/lib/components/result/tds/QueryBuilderTDSResultShared.d.ts.map +1 -0
- package/lib/components/result/tds/QueryBuilderTDSResultShared.js +251 -0
- package/lib/components/result/tds/QueryBuilderTDSResultShared.js.map +1 -0
- package/lib/components/result/tds/QueryBuilderTDSSimpleGridResult.d.ts +24 -0
- package/lib/components/result/tds/QueryBuilderTDSSimpleGridResult.d.ts.map +1 -0
- package/lib/components/result/tds/QueryBuilderTDSSimpleGridResult.js +224 -0
- package/lib/components/result/tds/QueryBuilderTDSSimpleGridResult.js.map +1 -0
- package/lib/index.css +2 -2
- package/lib/index.css.map +1 -1
- package/lib/package.json +1 -1
- package/lib/stores/QueryBuilderResultState.d.ts +5 -1
- package/lib/stores/QueryBuilderResultState.d.ts.map +1 -1
- package/lib/stores/QueryBuilderResultState.js.map +1 -1
- package/package.json +3 -3
- package/src/components/QueryBuilder.tsx +1 -1
- package/src/components/execution-plan/SQLExecutionNodeViewer.tsx +1 -1
- package/src/components/result/QueryBuilderResultPanel.tsx +569 -0
- package/src/components/result/tds/QueryBuilderTDSGridResult.tsx +346 -0
- package/src/components/result/tds/QueryBuilderTDSResultShared.tsx +502 -0
- package/src/components/result/tds/QueryBuilderTDSSimpleGridResult.tsx +387 -0
- package/src/stores/QueryBuilderResultState.ts +12 -1
- package/tsconfig.json +4 -1
- package/lib/components/QueryBuilderResultPanel.d.ts.map +0 -1
- package/lib/components/QueryBuilderResultPanel.js +0 -633
- package/lib/components/QueryBuilderResultPanel.js.map +0 -1
- 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
|
+
);
|