@finos/legend-cube-builder 0.0.0-stage → 0.0.2
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/LICENSE +201 -0
- package/README.md +5 -2
- package/lib/__lib__/LegendCubeCommand.d.ts +26 -0
- package/lib/__lib__/LegendCubeCommand.d.ts.map +1 -0
- package/lib/__lib__/LegendCubeCommand.js +40 -0
- package/lib/__lib__/LegendCubeCommand.js.map +1 -0
- package/lib/__lib__/LegendCubeHelpText.d.ts +19 -0
- package/lib/__lib__/LegendCubeHelpText.d.ts.map +1 -0
- package/lib/__lib__/LegendCubeHelpText.js +27 -0
- package/lib/__lib__/LegendCubeHelpText.js.map +1 -0
- package/lib/__lib__/LegendCubeLabels.d.ts +68 -0
- package/lib/__lib__/LegendCubeLabels.d.ts.map +1 -0
- package/lib/__lib__/LegendCubeLabels.js +109 -0
- package/lib/__lib__/LegendCubeLabels.js.map +1 -0
- package/lib/__lib__/LegendCubeTesting.d.ts +33 -0
- package/lib/__lib__/LegendCubeTesting.d.ts.map +1 -0
- package/lib/__lib__/LegendCubeTesting.js +34 -0
- package/lib/__lib__/LegendCubeTesting.js.map +1 -0
- package/lib/__test-utils__/CubeCanvasTestUtils.d.ts +24 -0
- package/lib/__test-utils__/CubeCanvasTestUtils.d.ts.map +1 -0
- package/lib/__test-utils__/CubeCanvasTestUtils.js +37 -0
- package/lib/__test-utils__/CubeCanvasTestUtils.js.map +1 -0
- package/lib/__test-utils__/CubeEngineTestSupport.d.ts +41 -0
- package/lib/__test-utils__/CubeEngineTestSupport.d.ts.map +1 -0
- package/lib/__test-utils__/CubeEngineTestSupport.js +76 -0
- package/lib/__test-utils__/CubeEngineTestSupport.js.map +1 -0
- package/lib/__test-utils__/CubeNorthwindTestQueries.d.ts +31 -0
- package/lib/__test-utils__/CubeNorthwindTestQueries.d.ts.map +1 -0
- package/lib/__test-utils__/CubeNorthwindTestQueries.js +102 -0
- package/lib/__test-utils__/CubeNorthwindTestQueries.js.map +1 -0
- package/lib/__test-utils__/CubePageTestUtils.d.ts +33 -0
- package/lib/__test-utils__/CubePageTestUtils.d.ts.map +1 -0
- package/lib/__test-utils__/CubePageTestUtils.js +68 -0
- package/lib/__test-utils__/CubePageTestUtils.js.map +1 -0
- package/lib/__test-utils__/CubeTestApplication.d.ts +30 -0
- package/lib/__test-utils__/CubeTestApplication.d.ts.map +1 -0
- package/lib/__test-utils__/CubeTestApplication.js +57 -0
- package/lib/__test-utils__/CubeTestApplication.js.map +1 -0
- package/lib/__test-utils__/FakeCubeEngine.d.ts +41 -0
- package/lib/__test-utils__/FakeCubeEngine.d.ts.map +1 -0
- package/lib/__test-utils__/FakeCubeEngine.js +85 -0
- package/lib/__test-utils__/FakeCubeEngine.js.map +1 -0
- package/lib/components/CubeButton.d.ts +20 -0
- package/lib/components/CubeButton.d.ts.map +1 -0
- package/lib/components/CubeButton.js +24 -0
- package/lib/components/CubeButton.js.map +1 -0
- package/lib/components/CubeEditor.d.ts +31 -0
- package/lib/components/CubeEditor.d.ts.map +1 -0
- package/lib/components/CubeEditor.js +90 -0
- package/lib/components/CubeEditor.js.map +1 -0
- package/lib/components/CubeNodeIcon.d.ts +23 -0
- package/lib/components/CubeNodeIcon.d.ts.map +1 -0
- package/lib/components/CubeNodeIcon.js +32 -0
- package/lib/components/CubeNodeIcon.js.map +1 -0
- package/lib/components/canvas/CubeCanvas.d.ts +37 -0
- package/lib/components/canvas/CubeCanvas.d.ts.map +1 -0
- package/lib/components/canvas/CubeCanvas.js +135 -0
- package/lib/components/canvas/CubeCanvas.js.map +1 -0
- package/lib/components/canvas/CubeCanvasContext.d.ts +19 -0
- package/lib/components/canvas/CubeCanvasContext.d.ts.map +1 -0
- package/lib/components/canvas/CubeCanvasContext.js +22 -0
- package/lib/components/canvas/CubeCanvasContext.js.map +1 -0
- package/lib/components/canvas/CubeCanvasContextMenu.d.ts +28 -0
- package/lib/components/canvas/CubeCanvasContextMenu.d.ts.map +1 -0
- package/lib/components/canvas/CubeCanvasContextMenu.js +35 -0
- package/lib/components/canvas/CubeCanvasContextMenu.js.map +1 -0
- package/lib/components/canvas/CubeCanvasDnd.d.ts +43 -0
- package/lib/components/canvas/CubeCanvasDnd.d.ts.map +1 -0
- package/lib/components/canvas/CubeCanvasDnd.js +51 -0
- package/lib/components/canvas/CubeCanvasDnd.js.map +1 -0
- package/lib/components/canvas/CubeCanvasEdge.d.ts +24 -0
- package/lib/components/canvas/CubeCanvasEdge.d.ts.map +1 -0
- package/lib/components/canvas/CubeCanvasEdge.js +33 -0
- package/lib/components/canvas/CubeCanvasEdge.js.map +1 -0
- package/lib/components/canvas/CubeCanvasElements.d.ts +64 -0
- package/lib/components/canvas/CubeCanvasElements.d.ts.map +1 -0
- package/lib/components/canvas/CubeCanvasElements.js +96 -0
- package/lib/components/canvas/CubeCanvasElements.js.map +1 -0
- package/lib/components/canvas/CubeCanvasLayout.d.ts +32 -0
- package/lib/components/canvas/CubeCanvasLayout.d.ts.map +1 -0
- package/lib/components/canvas/CubeCanvasLayout.js +63 -0
- package/lib/components/canvas/CubeCanvasLayout.js.map +1 -0
- package/lib/components/canvas/CubeCanvasNode.d.ts +29 -0
- package/lib/components/canvas/CubeCanvasNode.d.ts.map +1 -0
- package/lib/components/canvas/CubeCanvasNode.js +72 -0
- package/lib/components/canvas/CubeCanvasNode.js.map +1 -0
- package/lib/components/editors/CubeColumnPicker.d.ts +40 -0
- package/lib/components/editors/CubeColumnPicker.d.ts.map +1 -0
- package/lib/components/editors/CubeColumnPicker.js +51 -0
- package/lib/components/editors/CubeColumnPicker.js.map +1 -0
- package/lib/components/editors/CubeFilterEditor.d.ts +26 -0
- package/lib/components/editors/CubeFilterEditor.d.ts.map +1 -0
- package/lib/components/editors/CubeFilterEditor.js +90 -0
- package/lib/components/editors/CubeFilterEditor.js.map +1 -0
- package/lib/components/editors/CubeJoinEditor.d.ts +26 -0
- package/lib/components/editors/CubeJoinEditor.d.ts.map +1 -0
- package/lib/components/editors/CubeJoinEditor.js +76 -0
- package/lib/components/editors/CubeJoinEditor.js.map +1 -0
- package/lib/components/editors/CubeNodeEditorPanel.d.ts +29 -0
- package/lib/components/editors/CubeNodeEditorPanel.d.ts.map +1 -0
- package/lib/components/editors/CubeNodeEditorPanel.js +71 -0
- package/lib/components/editors/CubeNodeEditorPanel.js.map +1 -0
- package/lib/components/editors/CubeNodeEditorRegistry.d.ts +44 -0
- package/lib/components/editors/CubeNodeEditorRegistry.d.ts.map +1 -0
- package/lib/components/editors/CubeNodeEditorRegistry.js +30 -0
- package/lib/components/editors/CubeNodeEditorRegistry.js.map +1 -0
- package/lib/components/editors/CubeSourceEditor.d.ts +25 -0
- package/lib/components/editors/CubeSourceEditor.d.ts.map +1 -0
- package/lib/components/editors/CubeSourceEditor.js +42 -0
- package/lib/components/editors/CubeSourceEditor.js.map +1 -0
- package/lib/components/editors/CubeValueEditor.d.ts +40 -0
- package/lib/components/editors/CubeValueEditor.d.ts.map +1 -0
- package/lib/components/editors/CubeValueEditor.js +177 -0
- package/lib/components/editors/CubeValueEditor.js.map +1 -0
- package/lib/components/grid/CubeGridRegion.d.ts +23 -0
- package/lib/components/grid/CubeGridRegion.d.ts.map +1 -0
- package/lib/components/grid/CubeGridRegion.js +94 -0
- package/lib/components/grid/CubeGridRegion.js.map +1 -0
- package/lib/components/grid/CubeGridValues.d.ts +25 -0
- package/lib/components/grid/CubeGridValues.d.ts.map +1 -0
- package/lib/components/grid/CubeGridValues.js +95 -0
- package/lib/components/grid/CubeGridValues.js.map +1 -0
- package/lib/components/grid/CubeResultGrid.d.ts +26 -0
- package/lib/components/grid/CubeResultGrid.d.ts.map +1 -0
- package/lib/components/grid/CubeResultGrid.js +50 -0
- package/lib/components/grid/CubeResultGrid.js.map +1 -0
- package/lib/components/palette/CubePalette.d.ts +29 -0
- package/lib/components/palette/CubePalette.d.ts.map +1 -0
- package/lib/components/palette/CubePalette.js +73 -0
- package/lib/components/palette/CubePalette.js.map +1 -0
- package/lib/components/show-pure/CubeShowPureDialog.d.ts +23 -0
- package/lib/components/show-pure/CubeShowPureDialog.d.ts.map +1 -0
- package/lib/components/show-pure/CubeShowPureDialog.js +37 -0
- package/lib/components/show-pure/CubeShowPureDialog.js.map +1 -0
- package/lib/components/source-picker/CubeSourcePicker.d.ts +22 -0
- package/lib/components/source-picker/CubeSourcePicker.d.ts.map +1 -0
- package/lib/components/source-picker/CubeSourcePicker.js +89 -0
- package/lib/components/source-picker/CubeSourcePicker.js.map +1 -0
- package/lib/components/spec-transfer/CubeSpecTransferDialog.d.ts +26 -0
- package/lib/components/spec-transfer/CubeSpecTransferDialog.d.ts.map +1 -0
- package/lib/components/spec-transfer/CubeSpecTransferDialog.js +45 -0
- package/lib/components/spec-transfer/CubeSpecTransferDialog.js.map +1 -0
- package/lib/graph-manager/CubeEngine.d.ts +115 -0
- package/lib/graph-manager/CubeEngine.d.ts.map +1 -0
- package/lib/graph-manager/CubeEngine.js +61 -0
- package/lib/graph-manager/CubeEngine.js.map +1 -0
- package/lib/graph-manager/CubeModelOutlineHelper.d.ts +25 -0
- package/lib/graph-manager/CubeModelOutlineHelper.d.ts.map +1 -0
- package/lib/graph-manager/CubeModelOutlineHelper.js +24 -0
- package/lib/graph-manager/CubeModelOutlineHelper.js.map +1 -0
- package/lib/graph-manager/protocol/pure/CubeEngineBuilder.d.ts +26 -0
- package/lib/graph-manager/protocol/pure/CubeEngineBuilder.d.ts.map +1 -0
- package/lib/graph-manager/protocol/pure/CubeEngineBuilder.js +22 -0
- package/lib/graph-manager/protocol/pure/CubeEngineBuilder.js.map +1 -0
- package/lib/graph-manager/protocol/pure/v1/V1_CubeEngineErrors.d.ts +27 -0
- package/lib/graph-manager/protocol/pure/v1/V1_CubeEngineErrors.d.ts.map +1 -0
- package/lib/graph-manager/protocol/pure/v1/V1_CubeEngineErrors.js +76 -0
- package/lib/graph-manager/protocol/pure/v1/V1_CubeEngineErrors.js.map +1 -0
- package/lib/graph-manager/protocol/pure/v1/V1_CubeExecutionResultReader.d.ts +27 -0
- package/lib/graph-manager/protocol/pure/v1/V1_CubeExecutionResultReader.d.ts.map +1 -0
- package/lib/graph-manager/protocol/pure/v1/V1_CubeExecutionResultReader.js +82 -0
- package/lib/graph-manager/protocol/pure/v1/V1_CubeExecutionResultReader.js.map +1 -0
- package/lib/graph-manager/protocol/pure/v1/V1_CubeLambdaSerializer.d.ts +32 -0
- package/lib/graph-manager/protocol/pure/v1/V1_CubeLambdaSerializer.d.ts.map +1 -0
- package/lib/graph-manager/protocol/pure/v1/V1_CubeLambdaSerializer.js +195 -0
- package/lib/graph-manager/protocol/pure/v1/V1_CubeLambdaSerializer.js.map +1 -0
- package/lib/graph-manager/protocol/pure/v1/V1_CubeModelOutlineBuilder.d.ts +20 -0
- package/lib/graph-manager/protocol/pure/v1/V1_CubeModelOutlineBuilder.d.ts.map +1 -0
- package/lib/graph-manager/protocol/pure/v1/V1_CubeModelOutlineBuilder.js +121 -0
- package/lib/graph-manager/protocol/pure/v1/V1_CubeModelOutlineBuilder.js.map +1 -0
- package/lib/graph-manager/protocol/pure/v1/V1_CubeRelationTypeAdapter.d.ts +24 -0
- package/lib/graph-manager/protocol/pure/v1/V1_CubeRelationTypeAdapter.d.ts.map +1 -0
- package/lib/graph-manager/protocol/pure/v1/V1_CubeRelationTypeAdapter.js +43 -0
- package/lib/graph-manager/protocol/pure/v1/V1_CubeRelationTypeAdapter.js.map +1 -0
- package/lib/graph-manager/protocol/pure/v1/V1_LegendCubeEngine.d.ts +45 -0
- package/lib/graph-manager/protocol/pure/v1/V1_LegendCubeEngine.d.ts.map +1 -0
- package/lib/graph-manager/protocol/pure/v1/V1_LegendCubeEngine.js +171 -0
- package/lib/graph-manager/protocol/pure/v1/V1_LegendCubeEngine.js.map +1 -0
- package/lib/graph-manager/protocol/pure/v1/__test-utils__/V1_CubeEngineTestUtils.d.ts +22 -0
- package/lib/graph-manager/protocol/pure/v1/__test-utils__/V1_CubeEngineTestUtils.d.ts.map +1 -0
- package/lib/graph-manager/protocol/pure/v1/__test-utils__/V1_CubeEngineTestUtils.js +79 -0
- package/lib/graph-manager/protocol/pure/v1/__test-utils__/V1_CubeEngineTestUtils.js.map +1 -0
- package/lib/index.css +17 -0
- package/lib/index.css.map +1 -0
- package/lib/index.d.ts +25 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +24 -0
- package/lib/index.js.map +1 -0
- package/lib/stores/CubeEditorState.d.ts +206 -0
- package/lib/stores/CubeEditorState.d.ts.map +1 -0
- package/lib/stores/CubeEditorState.js +551 -0
- package/lib/stores/CubeEditorState.js.map +1 -0
- package/lib/stores/CubeExecutionState.d.ts +67 -0
- package/lib/stores/CubeExecutionState.d.ts.map +1 -0
- package/lib/stores/CubeExecutionState.js +196 -0
- package/lib/stores/CubeExecutionState.js.map +1 -0
- package/lib/stores/CubeHost.d.ts +31 -0
- package/lib/stores/CubeHost.d.ts.map +1 -0
- package/lib/stores/CubeHost.js +17 -0
- package/lib/stores/CubeHost.js.map +1 -0
- package/lib/stores/CubeNodeEditorState.d.ts +85 -0
- package/lib/stores/CubeNodeEditorState.d.ts.map +1 -0
- package/lib/stores/CubeNodeEditorState.js +207 -0
- package/lib/stores/CubeNodeEditorState.js.map +1 -0
- package/lib/stores/CubeShowPureState.d.ts +38 -0
- package/lib/stores/CubeShowPureState.d.ts.map +1 -0
- package/lib/stores/CubeShowPureState.js +83 -0
- package/lib/stores/CubeShowPureState.js.map +1 -0
- package/lib/stores/CubeSourcePickerState.d.ts +98 -0
- package/lib/stores/CubeSourcePickerState.d.ts.map +1 -0
- package/lib/stores/CubeSourcePickerState.js +334 -0
- package/lib/stores/CubeSourcePickerState.js.map +1 -0
- package/lib/stores/CubeSpecTransferState.d.ts +60 -0
- package/lib/stores/CubeSpecTransferState.d.ts.map +1 -0
- package/lib/stores/CubeSpecTransferState.js +167 -0
- package/lib/stores/CubeSpecTransferState.js.map +1 -0
- package/lib/stores/LocalModelCatalog.d.ts +37 -0
- package/lib/stores/LocalModelCatalog.d.ts.map +1 -0
- package/lib/stores/LocalModelCatalog.js +49 -0
- package/lib/stores/LocalModelCatalog.js.map +1 -0
- package/lib/stores/editors/CubeFilterDraft.d.ts +61 -0
- package/lib/stores/editors/CubeFilterDraft.d.ts.map +1 -0
- package/lib/stores/editors/CubeFilterDraft.js +186 -0
- package/lib/stores/editors/CubeFilterDraft.js.map +1 -0
- package/lib/stores/editors/CubeJoinDraft.d.ts +63 -0
- package/lib/stores/editors/CubeJoinDraft.d.ts.map +1 -0
- package/lib/stores/editors/CubeJoinDraft.js +136 -0
- package/lib/stores/editors/CubeJoinDraft.js.map +1 -0
- package/lib/stores/editors/CubeNodeDraft.d.ts +38 -0
- package/lib/stores/editors/CubeNodeDraft.d.ts.map +1 -0
- package/lib/stores/editors/CubeNodeDraft.js +35 -0
- package/lib/stores/editors/CubeNodeDraft.js.map +1 -0
- package/lib/stores/editors/CubeNodeDraftRegistry.d.ts +29 -0
- package/lib/stores/editors/CubeNodeDraftRegistry.d.ts.map +1 -0
- package/lib/stores/editors/CubeNodeDraftRegistry.js +33 -0
- package/lib/stores/editors/CubeNodeDraftRegistry.js.map +1 -0
- package/lib/stores/fixtures/CubeNorthwindModel.d.ts +23 -0
- package/lib/stores/fixtures/CubeNorthwindModel.d.ts.map +1 -0
- package/lib/stores/fixtures/CubeNorthwindModel.js +338 -0
- package/lib/stores/fixtures/CubeNorthwindModel.js.map +1 -0
- package/package.json +81 -3
- package/src/__lib__/LegendCubeCommand.ts +44 -0
- package/src/__lib__/LegendCubeHelpText.ts +38 -0
- package/src/__lib__/LegendCubeLabels.ts +141 -0
- package/src/__lib__/LegendCubeTesting.ts +33 -0
- package/src/__test-utils__/CubeCanvasTestUtils.ts +53 -0
- package/src/__test-utils__/CubeEngineTestSupport.ts +137 -0
- package/src/__test-utils__/CubeNorthwindTestQueries.ts +165 -0
- package/src/__test-utils__/CubePageTestUtils.tsx +96 -0
- package/src/__test-utils__/CubeTestApplication.ts +82 -0
- package/src/__test-utils__/FakeCubeEngine.ts +139 -0
- package/src/components/CubeButton.tsx +35 -0
- package/src/components/CubeEditor.tsx +239 -0
- package/src/components/CubeNodeIcon.tsx +47 -0
- package/src/components/canvas/CubeCanvas.tsx +260 -0
- package/src/components/canvas/CubeCanvasContext.ts +31 -0
- package/src/components/canvas/CubeCanvasContextMenu.tsx +89 -0
- package/src/components/canvas/CubeCanvasDnd.ts +82 -0
- package/src/components/canvas/CubeCanvasEdge.tsx +57 -0
- package/src/components/canvas/CubeCanvasElements.ts +150 -0
- package/src/components/canvas/CubeCanvasLayout.ts +88 -0
- package/src/components/canvas/CubeCanvasNode.tsx +141 -0
- package/src/components/editors/CubeColumnPicker.tsx +114 -0
- package/src/components/editors/CubeFilterEditor.tsx +309 -0
- package/src/components/editors/CubeJoinEditor.tsx +224 -0
- package/src/components/editors/CubeNodeEditorPanel.tsx +204 -0
- package/src/components/editors/CubeNodeEditorRegistry.ts +61 -0
- package/src/components/editors/CubeSourceEditor.tsx +116 -0
- package/src/components/editors/CubeValueEditor.tsx +332 -0
- package/src/components/grid/CubeGridRegion.tsx +274 -0
- package/src/components/grid/CubeGridValues.ts +125 -0
- package/src/components/grid/CubeResultGrid.tsx +84 -0
- package/src/components/palette/CubePalette.tsx +177 -0
- package/src/components/show-pure/CubeShowPureDialog.tsx +101 -0
- package/src/components/source-picker/CubeSourcePicker.tsx +304 -0
- package/src/components/spec-transfer/CubeSpecTransferDialog.tsx +157 -0
- package/src/graph-manager/CubeEngine.ts +155 -0
- package/src/graph-manager/CubeModelOutlineHelper.ts +32 -0
- package/src/graph-manager/protocol/pure/CubeEngineBuilder.ts +34 -0
- package/src/graph-manager/protocol/pure/v1/V1_CubeEngineErrors.ts +109 -0
- package/src/graph-manager/protocol/pure/v1/V1_CubeExecutionResultReader.ts +115 -0
- package/src/graph-manager/protocol/pure/v1/V1_CubeLambdaSerializer.ts +257 -0
- package/src/graph-manager/protocol/pure/v1/V1_CubeModelOutlineBuilder.ts +157 -0
- package/src/graph-manager/protocol/pure/v1/V1_CubeRelationTypeAdapter.ts +58 -0
- package/src/graph-manager/protocol/pure/v1/V1_LegendCubeEngine.ts +278 -0
- package/src/graph-manager/protocol/pure/v1/__test-utils__/V1_CubeEngineTestUtils.ts +124 -0
- package/src/index.ts +41 -0
- package/src/stores/CubeEditorState.ts +702 -0
- package/src/stores/CubeExecutionState.ts +255 -0
- package/src/stores/CubeHost.ts +32 -0
- package/src/stores/CubeNodeEditorState.ts +255 -0
- package/src/stores/CubeShowPureState.ts +96 -0
- package/src/stores/CubeSourcePickerState.ts +426 -0
- package/src/stores/CubeSpecTransferState.ts +198 -0
- package/src/stores/LocalModelCatalog.ts +70 -0
- package/src/stores/editors/CubeFilterDraft.ts +241 -0
- package/src/stores/editors/CubeJoinDraft.ts +197 -0
- package/src/stores/editors/CubeNodeDraft.ts +46 -0
- package/src/stores/editors/CubeNodeDraftRegistry.ts +49 -0
- package/src/stores/fixtures/CubeNorthwindModel.ts +355 -0
- package/tsconfig.json +118 -0
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026-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, TimesIcon } from '@finos/legend-art';
|
|
18
|
+
import {
|
|
19
|
+
BLANK_PLACEHOLDER,
|
|
20
|
+
checkValue,
|
|
21
|
+
type CubeType,
|
|
22
|
+
type FilterValue,
|
|
23
|
+
type FilterValueItem,
|
|
24
|
+
type FilterValueShape,
|
|
25
|
+
getLiteralText,
|
|
26
|
+
isEnumType,
|
|
27
|
+
isFilterValueList,
|
|
28
|
+
MESSAGE_FILTER_VALUE_INVALID,
|
|
29
|
+
MESSAGE_FILTER_VALUE_OUT_OF_RANGE,
|
|
30
|
+
parseValue,
|
|
31
|
+
TypeFamily,
|
|
32
|
+
} from '@finos/legend-cube';
|
|
33
|
+
import { useEffect, useRef, useState } from 'react';
|
|
34
|
+
|
|
35
|
+
/** Why a value is not one of the type, worded as the validation says it; nothing for a valid one or none */
|
|
36
|
+
export const getValueProblem = (
|
|
37
|
+
item: FilterValueItem | undefined,
|
|
38
|
+
type: CubeType,
|
|
39
|
+
): string | undefined => {
|
|
40
|
+
const problem = checkValue(item, type);
|
|
41
|
+
if (!problem || problem.reason === 'required') {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
return problem.reason === 'invalid'
|
|
45
|
+
? MESSAGE_FILTER_VALUE_INVALID(problem.text, type.displayName)
|
|
46
|
+
: MESSAGE_FILTER_VALUE_OUT_OF_RANGE(problem.text, type.displayName);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/** A value as typed and shown: a literal's text, or the text that is not a value */
|
|
50
|
+
const getItemText = (item: FilterValueItem | undefined): string =>
|
|
51
|
+
item === undefined
|
|
52
|
+
? ''
|
|
53
|
+
: item.kind === 'invalid'
|
|
54
|
+
? item.text
|
|
55
|
+
: getLiteralText(item);
|
|
56
|
+
|
|
57
|
+
const INPUT_CLASS =
|
|
58
|
+
'h-6 min-w-0 flex-1 rounded-sm border bg-[var(--color-bg-input)] px-1 text-base';
|
|
59
|
+
|
|
60
|
+
const DATE_TIME_WITHOUT_SECONDS = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/u;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The text a date-time input gives, with its seconds: a browser leaves out
|
|
64
|
+
* zero seconds (`1997-01-01T10:30`), and the engine truncates a date-time
|
|
65
|
+
* without them
|
|
66
|
+
*/
|
|
67
|
+
export const withDateTimeSeconds = (text: string): string =>
|
|
68
|
+
DATE_TIME_WITHOUT_SECONDS.test(text) ? `${text}:00` : text;
|
|
69
|
+
|
|
70
|
+
/** The input a type's values are typed in (spec §17.7) */
|
|
71
|
+
const getInputType = (type: CubeType): string =>
|
|
72
|
+
type.family === TypeFamily.STRICT_DATE
|
|
73
|
+
? 'date'
|
|
74
|
+
: type.family === TypeFamily.DATETIME
|
|
75
|
+
? 'datetime-local'
|
|
76
|
+
: 'text';
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Picks one of a fixed set of values, never typed: an enumeration's values,
|
|
80
|
+
* or true and false. A value that is not one of them (e.g. after the column
|
|
81
|
+
* changed) stays shown, marked.
|
|
82
|
+
*/
|
|
83
|
+
const CubeValueSelect: React.FC<{
|
|
84
|
+
label: string;
|
|
85
|
+
type: CubeType;
|
|
86
|
+
item: FilterValueItem | undefined;
|
|
87
|
+
onChange: (item: FilterValueItem | undefined) => void;
|
|
88
|
+
disabled: boolean;
|
|
89
|
+
}> = (props) => {
|
|
90
|
+
const { label, type, item, onChange, disabled } = props;
|
|
91
|
+
const choices = isEnumType(type) ? type.values : ['true', 'false'];
|
|
92
|
+
const text = getItemText(item);
|
|
93
|
+
const problem = getValueProblem(item, type);
|
|
94
|
+
return (
|
|
95
|
+
<select
|
|
96
|
+
aria-label={label}
|
|
97
|
+
aria-invalid={problem !== undefined}
|
|
98
|
+
title={problem}
|
|
99
|
+
className={clsx(
|
|
100
|
+
INPUT_CLASS,
|
|
101
|
+
problem
|
|
102
|
+
? 'border-[var(--color-status-error)]'
|
|
103
|
+
: 'border-[var(--color-border-default)]',
|
|
104
|
+
)}
|
|
105
|
+
value={text}
|
|
106
|
+
disabled={disabled}
|
|
107
|
+
onChange={(event) => onChange(parseValue(event.target.value, type))}
|
|
108
|
+
>
|
|
109
|
+
<option value="">{BLANK_PLACEHOLDER}</option>
|
|
110
|
+
{text && !choices.includes(text) && (
|
|
111
|
+
<option value={text} disabled={true}>
|
|
112
|
+
{text}
|
|
113
|
+
</option>
|
|
114
|
+
)}
|
|
115
|
+
{choices.map((choice) => (
|
|
116
|
+
<option key={choice} value={choice}>
|
|
117
|
+
{choice}
|
|
118
|
+
</option>
|
|
119
|
+
))}
|
|
120
|
+
</select>
|
|
121
|
+
);
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* One typed value (spec §17.7): shown as text, or `(blank)`, until clicked;
|
|
126
|
+
* then typed, and read when the input loses focus or on Enter. Text that is
|
|
127
|
+
* not a value of the type is kept, to be fixed, and marked in both modes.
|
|
128
|
+
* Leaving a blank value without typing keeps it blank; Enter on it confirms
|
|
129
|
+
* an empty text, a value of a STRING column. Enter and Escape give the focus
|
|
130
|
+
* back to the value; leaving it by focus leaves the focus where it went.
|
|
131
|
+
*/
|
|
132
|
+
const CubeValueText: React.FC<{
|
|
133
|
+
label: string;
|
|
134
|
+
type: CubeType;
|
|
135
|
+
item: FilterValueItem | undefined;
|
|
136
|
+
onChange: (item: FilterValueItem | undefined) => void;
|
|
137
|
+
disabled: boolean;
|
|
138
|
+
}> = (props) => {
|
|
139
|
+
const { label, type, item, onChange, disabled } = props;
|
|
140
|
+
const [editedText, setEditedText] = useState<string | undefined>(undefined);
|
|
141
|
+
const [returnFocus, setReturnFocus] = useState(false);
|
|
142
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
143
|
+
/** Enter and Escape, as the input being typed handles them now */
|
|
144
|
+
const onKeyRef = useRef<(key: string) => void>(() => undefined);
|
|
145
|
+
const isTyping = editedText !== undefined;
|
|
146
|
+
// a native listener: Chrome's date input gives React no Enter keydown
|
|
147
|
+
useEffect(() => {
|
|
148
|
+
const input = inputRef.current;
|
|
149
|
+
if (!isTyping || !input) {
|
|
150
|
+
return undefined;
|
|
151
|
+
}
|
|
152
|
+
const onKeyDown = (event: KeyboardEvent): void => {
|
|
153
|
+
if (event.key === 'Enter' || event.key === 'Escape') {
|
|
154
|
+
// or the key's press would reach the value it gives the focus to,
|
|
155
|
+
// and open it again
|
|
156
|
+
event.preventDefault();
|
|
157
|
+
onKeyRef.current(event.key);
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
input.addEventListener('keydown', onKeyDown);
|
|
161
|
+
return () => input.removeEventListener('keydown', onKeyDown);
|
|
162
|
+
}, [isTyping]);
|
|
163
|
+
const text = getItemText(item);
|
|
164
|
+
const problem = getValueProblem(item, type);
|
|
165
|
+
const markClass = problem
|
|
166
|
+
? 'border-[var(--color-status-error)] text-[var(--color-status-error)]'
|
|
167
|
+
: 'border-[var(--color-border-default)]';
|
|
168
|
+
if (editedText === undefined) {
|
|
169
|
+
return (
|
|
170
|
+
<button
|
|
171
|
+
aria-label={label}
|
|
172
|
+
aria-invalid={problem !== undefined}
|
|
173
|
+
title={problem ?? 'Click to change'}
|
|
174
|
+
className={clsx(INPUT_CLASS, 'truncate text-left', markClass, {
|
|
175
|
+
'text-[var(--color-text-muted)]': item === undefined,
|
|
176
|
+
})}
|
|
177
|
+
disabled={disabled}
|
|
178
|
+
// back from typing with Enter or Escape
|
|
179
|
+
autoFocus={returnFocus}
|
|
180
|
+
onClick={() => setEditedText(text)}
|
|
181
|
+
>
|
|
182
|
+
{item === undefined
|
|
183
|
+
? BLANK_PLACEHOLDER
|
|
184
|
+
: item.kind === 'string' && !item.value
|
|
185
|
+
? '""'
|
|
186
|
+
: text}
|
|
187
|
+
</button>
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
const commit = (confirmed: boolean): void => {
|
|
191
|
+
setEditedText(undefined);
|
|
192
|
+
const typed =
|
|
193
|
+
type.family === TypeFamily.DATETIME
|
|
194
|
+
? withDateTimeSeconds(editedText)
|
|
195
|
+
: editedText;
|
|
196
|
+
if (
|
|
197
|
+
typed !== text ||
|
|
198
|
+
(confirmed && item === undefined && type.family === TypeFamily.STRING)
|
|
199
|
+
) {
|
|
200
|
+
onChange(parseValue(typed, type));
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
onKeyRef.current = (key: string): void => {
|
|
204
|
+
if (key === 'Enter') {
|
|
205
|
+
setReturnFocus(true);
|
|
206
|
+
commit(true);
|
|
207
|
+
} else if (key === 'Escape') {
|
|
208
|
+
setReturnFocus(true);
|
|
209
|
+
setEditedText(undefined);
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
return (
|
|
213
|
+
<input
|
|
214
|
+
aria-label={label}
|
|
215
|
+
aria-invalid={problem !== undefined}
|
|
216
|
+
title={problem}
|
|
217
|
+
className={clsx(INPUT_CLASS, markClass)}
|
|
218
|
+
type={getInputType(type)}
|
|
219
|
+
// seconds are needed: the engine truncates a date-time without them
|
|
220
|
+
step={type.family === TypeFamily.DATETIME ? 1 : undefined}
|
|
221
|
+
// the input replaces the button the user just clicked
|
|
222
|
+
autoFocus={true}
|
|
223
|
+
value={editedText}
|
|
224
|
+
onChange={(event) => setEditedText(event.target.value)}
|
|
225
|
+
ref={inputRef}
|
|
226
|
+
onBlur={() => {
|
|
227
|
+
setReturnFocus(false);
|
|
228
|
+
commit(false);
|
|
229
|
+
}}
|
|
230
|
+
/>
|
|
231
|
+
);
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
/** One value of a column type, picked or typed as the type wants */
|
|
235
|
+
const CubeSingleValueEditor: React.FC<{
|
|
236
|
+
label: string;
|
|
237
|
+
type: CubeType;
|
|
238
|
+
item: FilterValueItem | undefined;
|
|
239
|
+
onChange: (item: FilterValueItem | undefined) => void;
|
|
240
|
+
disabled: boolean;
|
|
241
|
+
}> = (props) =>
|
|
242
|
+
props.type.family === TypeFamily.ENUM ||
|
|
243
|
+
props.type.family === TypeFamily.BOOLEAN ? (
|
|
244
|
+
<CubeValueSelect {...props} />
|
|
245
|
+
) : (
|
|
246
|
+
<CubeValueText {...props} />
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* The value of a condition, as its operator wants it (spec §17.7): none,
|
|
251
|
+
* one, or a list (In and NotIn), each value entered as its column's type
|
|
252
|
+
* wants (`parseValue`): a date input for dates, a picker for an enumeration
|
|
253
|
+
* or a boolean, text for the rest.
|
|
254
|
+
*/
|
|
255
|
+
export const CubeValueEditor: React.FC<{
|
|
256
|
+
/** What the value is for: the editor's accessible name, e.g. `Filter value` */
|
|
257
|
+
label: string;
|
|
258
|
+
type: CubeType;
|
|
259
|
+
shape: FilterValueShape;
|
|
260
|
+
value: FilterValue | undefined;
|
|
261
|
+
onChange: (value: FilterValue | undefined) => void;
|
|
262
|
+
disabled?: boolean | undefined;
|
|
263
|
+
}> = (props) => {
|
|
264
|
+
const { label, type, shape, value, onChange } = props;
|
|
265
|
+
const disabled = Boolean(props.disabled);
|
|
266
|
+
if (shape === 'none') {
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
if (shape === 'single') {
|
|
270
|
+
return (
|
|
271
|
+
<CubeSingleValueEditor
|
|
272
|
+
label={label}
|
|
273
|
+
type={type}
|
|
274
|
+
item={isFilterValueList(value) ? undefined : value}
|
|
275
|
+
onChange={onChange}
|
|
276
|
+
disabled={disabled}
|
|
277
|
+
/>
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
const items = isFilterValueList(value) ? value : value ? [value] : [];
|
|
281
|
+
const setItems = (next: readonly FilterValueItem[]): void =>
|
|
282
|
+
onChange(next.length ? next : undefined);
|
|
283
|
+
return (
|
|
284
|
+
<ul aria-label={label} className="flex min-w-0 flex-1 flex-col gap-1">
|
|
285
|
+
{items.map((item, index) => (
|
|
286
|
+
// values can repeat, and they hold no state of their own
|
|
287
|
+
// eslint-disable-next-line react/no-array-index-key
|
|
288
|
+
<li key={index} className="flex items-center gap-1">
|
|
289
|
+
<CubeSingleValueEditor
|
|
290
|
+
label={`${label} ${index + 1}`}
|
|
291
|
+
type={type}
|
|
292
|
+
item={item}
|
|
293
|
+
disabled={disabled}
|
|
294
|
+
onChange={(changed) =>
|
|
295
|
+
setItems(
|
|
296
|
+
changed === undefined
|
|
297
|
+
? items.filter((_, other) => other !== index)
|
|
298
|
+
: items.map((other, position) =>
|
|
299
|
+
position === index ? changed : other,
|
|
300
|
+
),
|
|
301
|
+
)
|
|
302
|
+
}
|
|
303
|
+
/>
|
|
304
|
+
<button
|
|
305
|
+
className="flex h-6 w-6 shrink-0 items-center justify-center text-[var(--color-text-secondary)] disabled:text-[var(--color-text-disabled)]"
|
|
306
|
+
aria-label={`Remove ${label.toLowerCase()} ${index + 1}`}
|
|
307
|
+
disabled={disabled}
|
|
308
|
+
onClick={() =>
|
|
309
|
+
setItems(items.filter((_, other) => other !== index))
|
|
310
|
+
}
|
|
311
|
+
>
|
|
312
|
+
<TimesIcon />
|
|
313
|
+
</button>
|
|
314
|
+
</li>
|
|
315
|
+
))}
|
|
316
|
+
<li className="flex items-center gap-1">
|
|
317
|
+
<CubeSingleValueEditor
|
|
318
|
+
// always blank: what it adds goes to the list above
|
|
319
|
+
label={`Add ${label.toLowerCase()}`}
|
|
320
|
+
type={type}
|
|
321
|
+
item={undefined}
|
|
322
|
+
disabled={disabled}
|
|
323
|
+
onChange={(added) => {
|
|
324
|
+
if (added !== undefined) {
|
|
325
|
+
setItems([...items, added]);
|
|
326
|
+
}
|
|
327
|
+
}}
|
|
328
|
+
/>
|
|
329
|
+
</li>
|
|
330
|
+
</ul>
|
|
331
|
+
);
|
|
332
|
+
};
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026-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 { PanelLoadingIndicator } from '@finos/legend-art';
|
|
18
|
+
import { flowResult } from 'mobx';
|
|
19
|
+
import { observer } from 'mobx-react-lite';
|
|
20
|
+
import { useState } from 'react';
|
|
21
|
+
import { EXECUTE_SHORTCUT_LABEL } from '../../__lib__/LegendCubeCommand.js';
|
|
22
|
+
import {
|
|
23
|
+
CUBE_PENDING_LABEL,
|
|
24
|
+
formatDisabledReasons,
|
|
25
|
+
getRowLimitError,
|
|
26
|
+
getTruncationMessage,
|
|
27
|
+
ROW_LIMIT_WARNING_THRESHOLD,
|
|
28
|
+
} from '../../__lib__/LegendCubeLabels.js';
|
|
29
|
+
import { LEGEND_CUBE_TEST_ID } from '../../__lib__/LegendCubeTesting.js';
|
|
30
|
+
import type { CubeEditorState } from '../../stores/CubeEditorState.js';
|
|
31
|
+
import { CubeButton } from '../CubeButton.js';
|
|
32
|
+
import { CubeResultGrid } from './CubeResultGrid.js';
|
|
33
|
+
import { formatDuration } from './CubeGridValues.js';
|
|
34
|
+
|
|
35
|
+
/** The first line of an error is shown, at most this long; the rest is under Details */
|
|
36
|
+
const MAX_ERROR_LINE_LENGTH = 500;
|
|
37
|
+
|
|
38
|
+
const CubeRowLimitInput = observer(
|
|
39
|
+
(props: { editorState: CubeEditorState }) => {
|
|
40
|
+
const { editorState } = props;
|
|
41
|
+
const [draft, setDraft] = useState(String(editorState.rowLimit));
|
|
42
|
+
const [error, setError] = useState<string | undefined>();
|
|
43
|
+
const commit = (): void => {
|
|
44
|
+
const problem = getRowLimitError(draft);
|
|
45
|
+
setError(problem);
|
|
46
|
+
if (!problem) {
|
|
47
|
+
editorState.setRowLimit(Number(draft.trim()));
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
return (
|
|
51
|
+
<div className="flex items-center gap-1">
|
|
52
|
+
<label className="flex items-center gap-1 text-base">
|
|
53
|
+
Rows
|
|
54
|
+
<input
|
|
55
|
+
className="h-6 w-20 rounded-sm border border-[var(--color-border-default)] bg-[var(--color-bg-input)] px-1 text-right text-base text-[var(--color-text-primary)]"
|
|
56
|
+
aria-label="Row limit"
|
|
57
|
+
aria-invalid={error !== undefined}
|
|
58
|
+
value={draft}
|
|
59
|
+
onChange={(event) => setDraft(event.target.value)}
|
|
60
|
+
onBlur={commit}
|
|
61
|
+
onKeyDown={(event) => {
|
|
62
|
+
if (event.key === 'Enter') {
|
|
63
|
+
commit();
|
|
64
|
+
}
|
|
65
|
+
}}
|
|
66
|
+
/>
|
|
67
|
+
</label>
|
|
68
|
+
{error !== undefined && (
|
|
69
|
+
<span
|
|
70
|
+
className="text-sm text-[var(--color-status-error)]"
|
|
71
|
+
role="alert"
|
|
72
|
+
>
|
|
73
|
+
{error}
|
|
74
|
+
</span>
|
|
75
|
+
)}
|
|
76
|
+
{error === undefined &&
|
|
77
|
+
editorState.rowLimit > ROW_LIMIT_WARNING_THRESHOLD && (
|
|
78
|
+
<span className="text-sm text-[var(--color-status-warn)]">
|
|
79
|
+
A large row limit can slow the page down.
|
|
80
|
+
</span>
|
|
81
|
+
)}
|
|
82
|
+
</div>
|
|
83
|
+
);
|
|
84
|
+
},
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
const CubeGridToolbar = observer(
|
|
88
|
+
(props: {
|
|
89
|
+
editorState: CubeEditorState;
|
|
90
|
+
showSql: boolean;
|
|
91
|
+
onToggleSql: () => void;
|
|
92
|
+
}) => {
|
|
93
|
+
const { editorState, showSql, onToggleSql } = props;
|
|
94
|
+
const { execution, host } = editorState;
|
|
95
|
+
const { applicationStore } = host;
|
|
96
|
+
const { result } = execution;
|
|
97
|
+
const disabledReasons = execution.disabledReasons;
|
|
98
|
+
const execute = (): void => {
|
|
99
|
+
flowResult(execution.execute()).catch(
|
|
100
|
+
applicationStore.alertUnhandledError,
|
|
101
|
+
);
|
|
102
|
+
};
|
|
103
|
+
return (
|
|
104
|
+
<div
|
|
105
|
+
className="flex h-8 shrink-0 items-center gap-3 border-b border-[var(--color-border-default)] bg-[var(--color-bg-panel-header)] px-2"
|
|
106
|
+
data-testid={LEGEND_CUBE_TEST_ID.GRID_TOOLBAR}
|
|
107
|
+
>
|
|
108
|
+
{execution.isRunning ? (
|
|
109
|
+
<CubeButton title="Stop the run" onClick={() => execution.stop()}>
|
|
110
|
+
Stop
|
|
111
|
+
</CubeButton>
|
|
112
|
+
) : (
|
|
113
|
+
<CubeButton
|
|
114
|
+
primary={true}
|
|
115
|
+
disabled={!execution.canExecute}
|
|
116
|
+
title={
|
|
117
|
+
disabledReasons.length
|
|
118
|
+
? `${formatDisabledReasons(disabledReasons)}\n(${EXECUTE_SHORTCUT_LABEL})`
|
|
119
|
+
: `Run the query up to the selected node (${EXECUTE_SHORTCUT_LABEL})`
|
|
120
|
+
}
|
|
121
|
+
onClick={execute}
|
|
122
|
+
>
|
|
123
|
+
Execute
|
|
124
|
+
</CubeButton>
|
|
125
|
+
)}
|
|
126
|
+
<CubeRowLimitInput editorState={editorState} />
|
|
127
|
+
{execution.isRunning && (
|
|
128
|
+
<span className="text-base text-[var(--color-text-secondary)]">
|
|
129
|
+
{CUBE_PENDING_LABEL.EXECUTING_QUERY}
|
|
130
|
+
</span>
|
|
131
|
+
)}
|
|
132
|
+
{execution.isStale && (
|
|
133
|
+
<span
|
|
134
|
+
className="rounded-sm bg-[var(--color-status-warn-bg)] px-1 text-base text-[var(--color-status-warn)]"
|
|
135
|
+
title="The query or the row limit changed since this run"
|
|
136
|
+
>
|
|
137
|
+
Stale: execute again to refresh
|
|
138
|
+
</span>
|
|
139
|
+
)}
|
|
140
|
+
{result && !execution.isRunning && (
|
|
141
|
+
<>
|
|
142
|
+
<span className="text-base text-[var(--color-text-secondary)]">
|
|
143
|
+
{`${result.rows.length.toLocaleString('en-US')} row${result.rows.length === 1 ? '' : 's'} in ${formatDuration(result.durationMs)}`}
|
|
144
|
+
</span>
|
|
145
|
+
{result.limited && (
|
|
146
|
+
<span className="text-base text-[var(--color-status-warn)]">
|
|
147
|
+
{getTruncationMessage(result.rowLimit)}
|
|
148
|
+
</span>
|
|
149
|
+
)}
|
|
150
|
+
{result.sql.length > 0 && (
|
|
151
|
+
<CubeButton aria-expanded={showSql} onClick={onToggleSql}>
|
|
152
|
+
{showSql ? 'Hide SQL' : 'Show SQL'}
|
|
153
|
+
</CubeButton>
|
|
154
|
+
)}
|
|
155
|
+
</>
|
|
156
|
+
)}
|
|
157
|
+
</div>
|
|
158
|
+
);
|
|
159
|
+
},
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
/** The SQL the run's database ran, with Copy */
|
|
163
|
+
const CubeSqlPanel = observer((props: { editorState: CubeEditorState }) => {
|
|
164
|
+
const { editorState } = props;
|
|
165
|
+
const { applicationStore } = editorState.host;
|
|
166
|
+
const sql = editorState.execution.result?.sql ?? [];
|
|
167
|
+
if (!sql.length) {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
const copySql = (): void => {
|
|
171
|
+
applicationStore.clipboardService
|
|
172
|
+
.copyTextToClipboard(sql.join('\n\n'))
|
|
173
|
+
.catch(applicationStore.alertUnhandledError);
|
|
174
|
+
};
|
|
175
|
+
return (
|
|
176
|
+
<div
|
|
177
|
+
className="flex max-h-40 shrink-0 gap-2 overflow-auto border-b border-[var(--color-border-default)] p-2"
|
|
178
|
+
data-testid={LEGEND_CUBE_TEST_ID.SQL_PANEL}
|
|
179
|
+
>
|
|
180
|
+
<div className="min-w-0 flex-1">
|
|
181
|
+
{sql.map((statement, index) => (
|
|
182
|
+
<pre
|
|
183
|
+
// statements hold no state, and the same one may run twice
|
|
184
|
+
// eslint-disable-next-line react/no-array-index-key
|
|
185
|
+
key={index}
|
|
186
|
+
className="whitespace-pre-wrap font-mono text-sm"
|
|
187
|
+
>
|
|
188
|
+
{statement}
|
|
189
|
+
</pre>
|
|
190
|
+
))}
|
|
191
|
+
</div>
|
|
192
|
+
<CubeButton onClick={copySql}>Copy SQL</CubeButton>
|
|
193
|
+
</div>
|
|
194
|
+
);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
const CubeExecutionError = observer(
|
|
198
|
+
(props: { editorState: CubeEditorState }) => {
|
|
199
|
+
const error = props.editorState.execution.error;
|
|
200
|
+
if (!error) {
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
const isCut = error.firstLine.length > MAX_ERROR_LINE_LENGTH;
|
|
204
|
+
const firstLine = isCut
|
|
205
|
+
? `${error.firstLine.slice(0, MAX_ERROR_LINE_LENGTH)}…`
|
|
206
|
+
: error.firstLine;
|
|
207
|
+
return (
|
|
208
|
+
<div
|
|
209
|
+
className="m-2 rounded-sm border border-[var(--color-status-error)] bg-[var(--color-status-error-bg)] p-2 text-base"
|
|
210
|
+
data-testid={LEGEND_CUBE_TEST_ID.EXECUTION_ERROR}
|
|
211
|
+
role="alert"
|
|
212
|
+
>
|
|
213
|
+
<div
|
|
214
|
+
className="font-medium text-[var(--color-status-error)]"
|
|
215
|
+
title={isCut ? error.firstLine : undefined}
|
|
216
|
+
>
|
|
217
|
+
{firstLine}
|
|
218
|
+
</div>
|
|
219
|
+
{(isCut || error.detail !== error.firstLine) && (
|
|
220
|
+
<details className="mt-1">
|
|
221
|
+
<summary className="cursor-pointer text-[var(--color-text-secondary)]">
|
|
222
|
+
Details
|
|
223
|
+
</summary>
|
|
224
|
+
<pre className="mt-1 max-h-60 overflow-auto whitespace-pre-wrap text-sm">
|
|
225
|
+
{error.detail}
|
|
226
|
+
</pre>
|
|
227
|
+
</details>
|
|
228
|
+
)}
|
|
229
|
+
</div>
|
|
230
|
+
);
|
|
231
|
+
},
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
/** The results: Execute, the row limit and the run's stats above the grid (PLAN §9) */
|
|
235
|
+
export const CubeGridRegion = observer(
|
|
236
|
+
(props: { editorState: CubeEditorState }) => {
|
|
237
|
+
const { editorState } = props;
|
|
238
|
+
const { execution, host } = editorState;
|
|
239
|
+
const [showSql, setShowSql] = useState(false);
|
|
240
|
+
const darkMode =
|
|
241
|
+
!host.applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled;
|
|
242
|
+
return (
|
|
243
|
+
<div
|
|
244
|
+
className="flex h-full flex-col bg-[var(--color-bg-panel)]"
|
|
245
|
+
data-testid={LEGEND_CUBE_TEST_ID.GRID_REGION}
|
|
246
|
+
>
|
|
247
|
+
<PanelLoadingIndicator isLoading={execution.isRunning} />
|
|
248
|
+
<CubeGridToolbar
|
|
249
|
+
editorState={editorState}
|
|
250
|
+
showSql={showSql}
|
|
251
|
+
onToggleSql={() => setShowSql(!showSql)}
|
|
252
|
+
/>
|
|
253
|
+
{showSql && !execution.isRunning && (
|
|
254
|
+
<CubeSqlPanel editorState={editorState} />
|
|
255
|
+
)}
|
|
256
|
+
<div className="min-h-0 flex-1">
|
|
257
|
+
{execution.error ? (
|
|
258
|
+
<CubeExecutionError editorState={editorState} />
|
|
259
|
+
) : execution.result ? (
|
|
260
|
+
<CubeResultGrid
|
|
261
|
+
key={execution.result.id}
|
|
262
|
+
result={execution.result}
|
|
263
|
+
darkMode={darkMode}
|
|
264
|
+
/>
|
|
265
|
+
) : (
|
|
266
|
+
<div className="flex h-full items-center justify-center text-base text-[var(--color-text-secondary)]">
|
|
267
|
+
Execute the query to see its rows.
|
|
268
|
+
</div>
|
|
269
|
+
)}
|
|
270
|
+
</div>
|
|
271
|
+
</div>
|
|
272
|
+
);
|
|
273
|
+
},
|
|
274
|
+
);
|