@finos/legend-lego 2.0.151 → 2.0.152

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 (29) hide show
  1. package/lib/index.css +2 -2
  2. package/lib/index.css.map +1 -1
  3. package/lib/sql-playground/SQLPlaygroundEditor.d.ts +26 -0
  4. package/lib/sql-playground/SQLPlaygroundEditor.d.ts.map +1 -0
  5. package/lib/sql-playground/SQLPlaygroundEditor.js +122 -0
  6. package/lib/sql-playground/SQLPlaygroundEditor.js.map +1 -0
  7. package/lib/sql-playground/SQLPlaygroundGrid.d.ts +24 -0
  8. package/lib/sql-playground/SQLPlaygroundGrid.d.ts.map +1 -0
  9. package/lib/sql-playground/SQLPlaygroundGrid.js +143 -0
  10. package/lib/sql-playground/SQLPlaygroundGrid.js.map +1 -0
  11. package/lib/sql-playground/SQLPlaygroundPanel.d.ts +20 -0
  12. package/lib/sql-playground/SQLPlaygroundPanel.d.ts.map +1 -0
  13. package/lib/sql-playground/SQLPlaygroundPanel.js +39 -0
  14. package/lib/sql-playground/SQLPlaygroundPanel.js.map +1 -0
  15. package/lib/sql-playground/index.d.ts +20 -0
  16. package/lib/sql-playground/index.d.ts.map +1 -0
  17. package/lib/sql-playground/index.js +20 -0
  18. package/lib/sql-playground/index.js.map +1 -0
  19. package/lib/sql-playground/store/AbstractSQLPlaygroundState.d.ts +47 -0
  20. package/lib/sql-playground/store/AbstractSQLPlaygroundState.d.ts.map +1 -0
  21. package/lib/sql-playground/store/AbstractSQLPlaygroundState.js +122 -0
  22. package/lib/sql-playground/store/AbstractSQLPlaygroundState.js.map +1 -0
  23. package/package.json +6 -5
  24. package/src/sql-playground/SQLPlaygroundEditor.tsx +197 -0
  25. package/src/sql-playground/SQLPlaygroundGrid.tsx +245 -0
  26. package/src/sql-playground/SQLPlaygroundPanel.tsx +150 -0
  27. package/src/sql-playground/index.ts +20 -0
  28. package/src/sql-playground/store/AbstractSQLPlaygroundState.ts +152 -0
  29. package/tsconfig.json +6 -1
@@ -0,0 +1,47 @@
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
+ import { ActionState, type GeneratorFn } from '@finos/legend-shared';
17
+ import * as monaco from 'monaco-editor';
18
+ import type { CommandRegistrar } from '@finos/legend-application';
19
+ export type SQLPlaygroundTheme = 'light' | 'dark';
20
+ export interface SQL_ExecutionResult {
21
+ value: string;
22
+ sqlDuration: number;
23
+ }
24
+ export declare abstract class AbstractSQLPlaygroundState implements CommandRegistrar {
25
+ theme: SQLPlaygroundTheme;
26
+ sqlText: string;
27
+ executeRawSQLState: ActionState;
28
+ sqlExecutionResult?: SQL_ExecutionResult | undefined;
29
+ sqlEditorViewState: monaco.editor.ICodeEditorViewState | undefined;
30
+ sqlEditorTextModel: monaco.editor.ITextModel;
31
+ sqlEditor?: monaco.editor.IStandaloneCodeEditor | undefined;
32
+ isLocalModeEnabled: boolean;
33
+ constructor();
34
+ abstract executeRawSQL(): GeneratorFn<void>;
35
+ abstract registerCommands(): void;
36
+ abstract deregisterCommands(): void;
37
+ getCodeCompletionSuggestions(): string[];
38
+ stopExecuteSQL(): void;
39
+ setSQLText(val: string): void;
40
+ setTheme(val: SQLPlaygroundTheme): void;
41
+ setSQLEditorViewState(state: monaco.editor.ICodeEditorViewState | undefined): void;
42
+ setSQLEditor(val: monaco.editor.IStandaloneCodeEditor | undefined): void;
43
+ toggleIsLocalModeEnabled(): void;
44
+ get isExecuting(): boolean;
45
+ get hasResult(): boolean;
46
+ }
47
+ //# sourceMappingURL=AbstractSQLPlaygroundState.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AbstractSQLPlaygroundState.d.ts","sourceRoot":"","sources":["../../../src/sql-playground/store/AbstractSQLPlaygroundState.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAOlE,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,MAAM,CAAC;AAClD,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACrB;AAuCD,8BAAsB,0BAA2B,YAAW,gBAAgB;IAC1E,KAAK,EAAE,kBAAkB,CAAC;IAC1B,OAAO,SAAM;IACb,kBAAkB,EAAE,WAAW,CAAC;IAChC,kBAAkB,CAAC,EAAE,mBAAmB,GAAG,SAAS,CAAC;IACrD,kBAAkB,EAAE,MAAM,CAAC,MAAM,CAAC,oBAAoB,GAAG,SAAS,CAAC;IACnE,kBAAkB,EAAE,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,qBAAqB,GAAG,SAAS,CAAC;IAC5D,kBAAkB,EAAE,OAAO,CAAC;;IA0B5B,QAAQ,CAAC,aAAa,IAAI,WAAW,CAAC,IAAI,CAAC;IAC3C,QAAQ,CAAC,gBAAgB,IAAI,IAAI;IACjC,QAAQ,CAAC,kBAAkB,IAAI,IAAI;IAEnC,4BAA4B,IAAI,MAAM,EAAE;IAIxC,cAAc,IAAI,IAAI;IAItB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAI7B,QAAQ,CAAC,GAAG,EAAE,kBAAkB,GAAG,IAAI;IAIvC,qBAAqB,CACnB,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,oBAAoB,GAAG,SAAS,GACpD,IAAI;IAIP,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,CAAC,qBAAqB,GAAG,SAAS,GAAG,IAAI;IAWxE,wBAAwB,IAAI,IAAI;IAKhC,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED,IAAI,SAAS,IAAI,OAAO,CAEvB;CACF"}
@@ -0,0 +1,122 @@
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
+ import { ActionState } from '@finos/legend-shared';
17
+ import * as monaco from 'monaco-editor';
18
+ import { CODE_EDITOR_LANGUAGE, moveCursorToPosition, } from '@finos/legend-code-editor';
19
+ import { action, makeObservable, observable } from 'mobx';
20
+ const SQL_KEYWORDS = [
21
+ 'AND',
22
+ 'AS',
23
+ 'ASC',
24
+ 'BETWEEN',
25
+ 'DESC',
26
+ 'DISTINCT',
27
+ 'EXEC',
28
+ 'EXISTS',
29
+ 'FROM',
30
+ 'FULL OUTER JOIN',
31
+ 'GROUP BY',
32
+ 'HAVING',
33
+ 'IN',
34
+ 'INNER JOIN',
35
+ 'IS NULL',
36
+ 'IS NOT NULL',
37
+ 'JOIN',
38
+ 'LEFT JOIN',
39
+ 'LIKE',
40
+ 'LIMIT',
41
+ 'NOT',
42
+ 'NOT NULL',
43
+ 'OR',
44
+ 'ORDER BY',
45
+ 'OUTER JOIN',
46
+ 'RIGHT JOIN',
47
+ 'SELECT',
48
+ 'SELECT DISTINCT',
49
+ 'SELECT INTO',
50
+ 'SELECT TOP',
51
+ 'TOP',
52
+ 'UNION',
53
+ 'UNION ALL',
54
+ 'UNIQUE',
55
+ 'WHERE',
56
+ ];
57
+ export class AbstractSQLPlaygroundState {
58
+ theme;
59
+ sqlText = '';
60
+ executeRawSQLState;
61
+ sqlExecutionResult;
62
+ sqlEditorViewState;
63
+ sqlEditorTextModel;
64
+ sqlEditor;
65
+ isLocalModeEnabled;
66
+ constructor() {
67
+ this.sqlEditorTextModel = monaco.editor.createModel(this.sqlText, CODE_EDITOR_LANGUAGE.SQL);
68
+ this.isLocalModeEnabled = false;
69
+ this.executeRawSQLState = ActionState.create();
70
+ this.theme = 'light';
71
+ makeObservable(this, {
72
+ sqlExecutionResult: observable,
73
+ isLocalModeEnabled: observable,
74
+ sqlText: observable,
75
+ executeRawSQLState: observable,
76
+ stopExecuteSQL: action,
77
+ setSQLEditor: action,
78
+ setSQLEditorViewState: action,
79
+ setSQLText: action,
80
+ setTheme: action,
81
+ toggleIsLocalModeEnabled: action,
82
+ sqlEditorViewState: observable.ref,
83
+ sqlEditor: observable.ref,
84
+ });
85
+ }
86
+ getCodeCompletionSuggestions() {
87
+ return SQL_KEYWORDS;
88
+ }
89
+ stopExecuteSQL() {
90
+ this.sqlExecutionResult = undefined;
91
+ }
92
+ setSQLText(val) {
93
+ this.sqlText = val;
94
+ }
95
+ setTheme(val) {
96
+ this.theme = val;
97
+ }
98
+ setSQLEditorViewState(state) {
99
+ this.sqlEditorViewState = state;
100
+ }
101
+ setSQLEditor(val) {
102
+ this.sqlEditor = val;
103
+ if (val) {
104
+ const lines = this.sqlText.split('\n');
105
+ moveCursorToPosition(val, {
106
+ lineNumber: lines.length,
107
+ column: lines.at(-1)?.length ?? 0,
108
+ });
109
+ }
110
+ }
111
+ toggleIsLocalModeEnabled() {
112
+ this.isLocalModeEnabled = !this.isLocalModeEnabled;
113
+ this.sqlExecutionResult = undefined;
114
+ }
115
+ get isExecuting() {
116
+ return this.executeRawSQLState.isInProgress;
117
+ }
118
+ get hasResult() {
119
+ return this.sqlExecutionResult !== undefined;
120
+ }
121
+ }
122
+ //# sourceMappingURL=AbstractSQLPlaygroundState.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AbstractSQLPlaygroundState.js","sourceRoot":"","sources":["../../../src/sql-playground/store/AbstractSQLPlaygroundState.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finos/legend-lego",
3
- "version": "2.0.151",
3
+ "version": "2.0.152",
4
4
  "description": "Legend code editor support",
5
5
  "keywords": [
6
6
  "legend",
@@ -21,6 +21,7 @@
21
21
  "exports": {
22
22
  "./application": "./lib/application/index.js",
23
23
  "./code-editor": "./lib/code-editor/index.js",
24
+ "./sql-playground": "./lib/sql-playground/index.js",
24
25
  "./code-editor/test": "./lib/code-editor/__test__.js",
25
26
  "./code-editor/test/MockedMonacoEditor.js": "./lib/code-editor/__test-utils__/MockedMonacoEditor.js",
26
27
  "./data-grid": "./lib/data-grid/index.js",
@@ -47,10 +48,10 @@
47
48
  "test:watch": "jest --watch"
48
49
  },
49
50
  "dependencies": {
50
- "@finos/legend-application": "16.0.83",
51
- "@finos/legend-art": "7.1.133",
52
- "@finos/legend-code-editor": "2.0.138",
53
- "@finos/legend-graph": "32.3.18",
51
+ "@finos/legend-application": "16.0.84",
52
+ "@finos/legend-art": "7.1.134",
53
+ "@finos/legend-code-editor": "2.0.139",
54
+ "@finos/legend-graph": "32.3.19",
54
55
  "@finos/legend-shared": "11.0.21",
55
56
  "@types/css-font-loading-module": "0.0.14",
56
57
  "@types/react": "19.0.10",
@@ -0,0 +1,197 @@
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 { observer } from 'mobx-react-lite';
18
+ import { PanelDropZone } from '@finos/legend-art';
19
+ import { useCallback, useEffect, useRef, useState } from 'react';
20
+ import { useCommands } from '@finos/legend-application';
21
+ import {
22
+ CODE_EDITOR_LANGUAGE,
23
+ CODE_EDITOR_THEME,
24
+ getBaseCodeEditorOptions,
25
+ } from '@finos/legend-code-editor';
26
+ import {
27
+ editor as monacoEditorAPI,
28
+ languages as monacoLanguagesAPI,
29
+ type IDisposable,
30
+ } from 'monaco-editor';
31
+ import { useDrop } from 'react-dnd';
32
+ import { isString } from '@finos/legend-shared';
33
+ import type { AbstractSQLPlaygroundState } from './store/AbstractSQLPlaygroundState.js';
34
+
35
+ export interface SQLPlaygroundPanelProps {
36
+ playgroundState: AbstractSQLPlaygroundState;
37
+ advancedMode: boolean;
38
+ disableDragDrop?: boolean;
39
+ enableDarkMode?: boolean;
40
+ }
41
+
42
+ const toCompletionItems = (
43
+ labels: string[],
44
+ ): monacoLanguagesAPI.CompletionItem[] =>
45
+ labels.map(
46
+ (label) =>
47
+ ({
48
+ label,
49
+ kind: monacoLanguagesAPI.CompletionItemKind.Keyword,
50
+ insertTextRules:
51
+ monacoLanguagesAPI.CompletionItemInsertTextRule.InsertAsSnippet,
52
+ insertText: `${label} `,
53
+ }) as monacoLanguagesAPI.CompletionItem,
54
+ );
55
+
56
+ export const PlaygroundSQLCodeEditor = observer(
57
+ (props: SQLPlaygroundPanelProps) => {
58
+ const {
59
+ playgroundState,
60
+ disableDragDrop = false,
61
+ enableDarkMode = false,
62
+ } = props;
63
+ const codeEditorRef = useRef<HTMLDivElement>(null);
64
+ const sqlIdentifierSuggestionProviderDisposer = useRef<
65
+ IDisposable | undefined
66
+ >(undefined);
67
+ const [editor, setEditor] = useState<
68
+ monacoEditorAPI.IStandaloneCodeEditor | undefined
69
+ >();
70
+ useEffect(() => {
71
+ if (!editor && codeEditorRef.current) {
72
+ const element = codeEditorRef.current;
73
+ playgroundState.setTheme(enableDarkMode ? 'dark' : 'light');
74
+ const newEditor = monacoEditorAPI.create(element, {
75
+ ...getBaseCodeEditorOptions(),
76
+ theme:
77
+ playgroundState.theme === 'light'
78
+ ? CODE_EDITOR_THEME.GITHUB_LIGHT
79
+ : CODE_EDITOR_THEME.DEFAULT_DARK,
80
+ language: CODE_EDITOR_LANGUAGE.SQL,
81
+ padding: {
82
+ top: 10,
83
+ },
84
+ });
85
+
86
+ newEditor.onDidChangeModelContent(() => {
87
+ const currentVal = newEditor.getValue();
88
+ playgroundState.setSQLText(currentVal);
89
+ });
90
+ newEditor.setModel(playgroundState.sqlEditorTextModel);
91
+ if (playgroundState.sqlEditorViewState) {
92
+ newEditor.restoreViewState(playgroundState.sqlEditorViewState);
93
+ }
94
+ newEditor.focus();
95
+ playgroundState.setSQLEditor(newEditor);
96
+ setEditor(newEditor);
97
+ }
98
+ }, [playgroundState, editor, enableDarkMode]);
99
+ useCommands(playgroundState);
100
+ if (editor) {
101
+ sqlIdentifierSuggestionProviderDisposer.current?.dispose();
102
+ sqlIdentifierSuggestionProviderDisposer.current =
103
+ monacoLanguagesAPI.registerCompletionItemProvider(
104
+ CODE_EDITOR_LANGUAGE.SQL,
105
+ {
106
+ triggerCharacters: [],
107
+ provideCompletionItems: async (model, position, context) => {
108
+ let suggestions: monacoLanguagesAPI.CompletionItem[] = [];
109
+ if (
110
+ context.triggerKind ===
111
+ monacoLanguagesAPI.CompletionTriggerKind.Invoke
112
+ ) {
113
+ const labels = playgroundState.getCodeCompletionSuggestions();
114
+ suggestions = suggestions.concat(toCompletionItems(labels));
115
+ }
116
+ return { suggestions };
117
+ },
118
+ },
119
+ );
120
+ }
121
+ useEffect(
122
+ () => (): void => {
123
+ if (editor) {
124
+ playgroundState.setSQLEditorViewState(
125
+ editor.saveViewState() ?? undefined,
126
+ );
127
+ editor.dispose();
128
+
129
+ sqlIdentifierSuggestionProviderDisposer.current?.dispose();
130
+ }
131
+ },
132
+ [playgroundState, editor],
133
+ );
134
+
135
+ type DatabaseNodeDragType = { text: string };
136
+ const DATABASE_NODE_DND_TYPE = 'DATABASE_NODE_DND_TYPE';
137
+ const handleDatabaseNodeDrop = useCallback(
138
+ (item: DatabaseNodeDragType): void => {
139
+ if (isString(item.text)) {
140
+ if (playgroundState.sqlEditor) {
141
+ const currentValue = playgroundState.sqlEditorTextModel.getValue();
142
+ const lines = currentValue.split('\n');
143
+ const position = playgroundState.sqlEditor.getPosition() ?? {
144
+ lineNumber: lines.length,
145
+ column: lines.at(-1)?.length ?? 0,
146
+ };
147
+ playgroundState.sqlEditor.executeEdits('', [
148
+ {
149
+ range: {
150
+ startLineNumber: position.lineNumber,
151
+ startColumn: position.column,
152
+ endLineNumber: position.lineNumber,
153
+ endColumn: position.column,
154
+ },
155
+ text: item.text,
156
+ forceMoveMarkers: true,
157
+ },
158
+ ]);
159
+ playgroundState.setSQLText(
160
+ playgroundState.sqlEditorTextModel.getValue(),
161
+ );
162
+ }
163
+ }
164
+ },
165
+ [playgroundState],
166
+ );
167
+ const [{ isDatabaseNodeDragOver }, dropConnector] = useDrop<
168
+ DatabaseNodeDragType,
169
+ void,
170
+ { isDatabaseNodeDragOver: boolean }
171
+ >(
172
+ () => ({
173
+ accept: DATABASE_NODE_DND_TYPE,
174
+ drop: (item): void => handleDatabaseNodeDrop(item),
175
+ collect: (monitor) => ({
176
+ isDatabaseNodeDragOver: monitor.isOver({ shallow: true }),
177
+ }),
178
+ }),
179
+ [handleDatabaseNodeDrop],
180
+ );
181
+
182
+ return (
183
+ <div className="sql-playground__code-editor">
184
+ <PanelDropZone
185
+ className="sql-playground__code-editor__content"
186
+ isDragOver={isDatabaseNodeDragOver}
187
+ dropTargetConnector={dropConnector}
188
+ disabled={disableDragDrop}
189
+ >
190
+ <div className="code-editor__container">
191
+ <div className="code-editor__body" ref={codeEditorRef} />
192
+ </div>
193
+ </PanelDropZone>
194
+ </div>
195
+ );
196
+ },
197
+ );
@@ -0,0 +1,245 @@
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 { observer } from 'mobx-react-lite';
18
+ import { BlankPanelContent, clsx } from '@finos/legend-art';
19
+ import { useCallback } from 'react';
20
+ import {
21
+ at,
22
+ isString,
23
+ parseCSVString,
24
+ isNonNullable,
25
+ isNumber,
26
+ isValidURL,
27
+ } from '@finos/legend-shared';
28
+
29
+ import {
30
+ DataGrid,
31
+ type DataGridCellRendererParams,
32
+ type DataGridColumnDefinition,
33
+ type DataGridDefaultMenuItem,
34
+ type DataGridGetContextMenuItemsParams,
35
+ type DataGridMenuItemDef,
36
+ } from '../data-grid/DataGrid.js';
37
+
38
+ const parseExecutionResultData = (
39
+ data: string,
40
+ ): { rowData: Record<string, string>[]; columns: string[] } | undefined => {
41
+ const lines = data.split('\n').filter((line) => line.trim().length);
42
+ if (lines.length) {
43
+ const columns = parseCSVString(at(lines, 0)) ?? [];
44
+ const rowData = lines
45
+ .slice(1)
46
+ .map((item) => {
47
+ const rowItems = parseCSVString(item);
48
+ if (!rowItems) {
49
+ return undefined;
50
+ }
51
+ const row: Record<string, string> = {};
52
+ columns.forEach((column, idx) => {
53
+ row[column] = rowItems[idx] ?? '';
54
+ });
55
+ return row;
56
+ })
57
+ .filter(isNonNullable);
58
+ return { rowData, columns };
59
+ }
60
+ return undefined;
61
+ };
62
+
63
+ const TDSResultCellRenderer = observer((params: DataGridCellRendererParams) => {
64
+ const cellValue = params.value as string;
65
+ const formattedCellValue = (): string => {
66
+ if (isNumber(cellValue)) {
67
+ return Intl.NumberFormat('en-US', {
68
+ maximumFractionDigits: 4,
69
+ }).format(Number(cellValue));
70
+ }
71
+ return cellValue;
72
+ };
73
+ const cellValueUrlLink =
74
+ isString(cellValue) && isValidURL(cellValue) ? cellValue : undefined;
75
+
76
+ return (
77
+ <div className={clsx('query-builder__result__values__table__cell')}>
78
+ {cellValueUrlLink ? (
79
+ <a href={cellValueUrlLink} target="_blank" rel="noreferrer">
80
+ {cellValueUrlLink}
81
+ </a>
82
+ ) : (
83
+ <span>{formattedCellValue()}</span>
84
+ )}
85
+ </div>
86
+ );
87
+ });
88
+
89
+ export const PlayGroundSQLExecutionResultGrid = observer(
90
+ (props: {
91
+ result: string;
92
+ useAdvancedGrid?: boolean;
93
+ useLocalMode?: boolean;
94
+ enableDarkMode?: boolean;
95
+ }) => {
96
+ const {
97
+ result,
98
+ useAdvancedGrid,
99
+ useLocalMode,
100
+ enableDarkMode = false,
101
+ } = props;
102
+ const data = parseExecutionResultData(result);
103
+ const darkMode = enableDarkMode;
104
+
105
+ if (!data) {
106
+ return (
107
+ <BlankPanelContent>{`Can't parse result, displaying raw form:\n${result}`}</BlankPanelContent>
108
+ );
109
+ }
110
+ if (useAdvancedGrid) {
111
+ if (useLocalMode) {
112
+ const localcolDefs = data.columns.map(
113
+ (colName) =>
114
+ ({
115
+ minWidth: 50,
116
+ sortable: true,
117
+ resizable: true,
118
+ field: colName,
119
+ flex: 1,
120
+ enablePivot: true,
121
+ enableRowGroup: true,
122
+ enableValue: true,
123
+ allowedAggFuncs: ['count'],
124
+ }) as DataGridColumnDefinition,
125
+ );
126
+
127
+ return (
128
+ <div
129
+ className={clsx('sql-playground__result__grid', {
130
+ 'ag-theme-balham': !darkMode,
131
+ 'ag-theme-balham-dark': darkMode,
132
+ })}
133
+ >
134
+ <DataGrid
135
+ rowData={data.rowData}
136
+ gridOptions={{
137
+ suppressScrollOnNewData: true,
138
+ rowSelection: {
139
+ mode: 'multiRow',
140
+ checkboxes: false,
141
+ headerCheckbox: false,
142
+ },
143
+ pivotPanelShow: 'always',
144
+ rowGroupPanelShow: 'always',
145
+ cellSelection: true,
146
+ }}
147
+ // NOTE: when column definition changed, we need to force refresh the cell to make sure the cell renderer is updated
148
+ // See https://stackoverflow.com/questions/56341073/how-to-refresh-an-ag-grid-when-a-change-occurs-inside-a-custom-cell-renderer-com
149
+ onRowDataUpdated={(params) => {
150
+ params.api.refreshCells({ force: true });
151
+ }}
152
+ suppressFieldDotNotation={true}
153
+ suppressContextMenu={false}
154
+ columnDefs={localcolDefs}
155
+ sideBar={['columns', 'filters']}
156
+ />
157
+ </div>
158
+ );
159
+ }
160
+ const colDefs = data.columns.map(
161
+ (colName) =>
162
+ ({
163
+ minWidth: 50,
164
+ sortable: true,
165
+ resizable: true,
166
+ field: colName,
167
+ flex: 1,
168
+ cellRenderer: TDSResultCellRenderer,
169
+ filter: true,
170
+ }) as DataGridColumnDefinition,
171
+ );
172
+ const getContextMenuItems = useCallback(
173
+ (
174
+ params: DataGridGetContextMenuItemsParams<{
175
+ [key: string]: string;
176
+ }>,
177
+ ): (DataGridDefaultMenuItem | DataGridMenuItemDef)[] => [
178
+ 'copy',
179
+ 'copyWithHeaders',
180
+ {
181
+ name: 'Copy Row Value',
182
+ action: () => {
183
+ params.api.copySelectedRowsToClipboard();
184
+ },
185
+ },
186
+ ],
187
+ [],
188
+ );
189
+ return (
190
+ <div
191
+ className={clsx('sql-playground__result__grid', {
192
+ 'ag-theme-balham': !darkMode,
193
+ 'ag-theme-balham-dark': darkMode,
194
+ })}
195
+ >
196
+ <DataGrid
197
+ rowData={data.rowData}
198
+ overlayNoRowsTemplate={`<div class="sql-playground__result__grid--empty">No results</div>`}
199
+ gridOptions={{
200
+ suppressScrollOnNewData: true,
201
+ rowSelection: {
202
+ mode: 'multiRow',
203
+ checkboxes: false,
204
+ headerCheckbox: false,
205
+ },
206
+ cellSelection: true,
207
+ }}
208
+ onRowDataUpdated={(params) => {
209
+ params.api.refreshCells({ force: true });
210
+ }}
211
+ suppressFieldDotNotation={true}
212
+ suppressClipboardPaste={false}
213
+ suppressContextMenu={false}
214
+ columnDefs={colDefs}
215
+ getContextMenuItems={(params) => getContextMenuItems(params)}
216
+ />
217
+ </div>
218
+ );
219
+ }
220
+
221
+ return (
222
+ <div
223
+ className={clsx('sql-playground__result__grid', {
224
+ 'ag-theme-balham': !darkMode,
225
+ 'ag-theme-balham-dark': darkMode,
226
+ })}
227
+ >
228
+ <DataGrid
229
+ rowData={data.rowData}
230
+ overlayNoRowsTemplate={`<div class="sql-playground__result__grid--empty">No results</div>`}
231
+ alwaysShowVerticalScroll={true}
232
+ suppressFieldDotNotation={true}
233
+ columnDefs={data.columns.map((column) => ({
234
+ minWidth: 50,
235
+ sortable: true,
236
+ resizable: true,
237
+ headerName: column,
238
+ field: column,
239
+ flex: 1,
240
+ }))}
241
+ />
242
+ </div>
243
+ );
244
+ },
245
+ );