@finos/legend-cube-builder 0.0.5 → 0.0.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/docs/adding-an-operation.md +4 -2
- package/docs/demo/README.md +1 -1
- package/docs/hosting.md +40 -9
- package/lib/__lib__/LegendCubeHelpText.d.ts.map +1 -1
- package/lib/__lib__/LegendCubeHelpText.js +3 -1
- package/lib/__lib__/LegendCubeHelpText.js.map +1 -1
- package/lib/__lib__/LegendCubeLabels.d.ts +34 -5
- package/lib/__lib__/LegendCubeLabels.d.ts.map +1 -1
- package/lib/__lib__/LegendCubeLabels.js +45 -8
- package/lib/__lib__/LegendCubeLabels.js.map +1 -1
- package/lib/__lib__/LegendCubeNavigation.d.ts +23 -0
- package/lib/__lib__/LegendCubeNavigation.d.ts.map +1 -0
- package/lib/__lib__/LegendCubeNavigation.js +33 -0
- package/lib/__lib__/LegendCubeNavigation.js.map +1 -0
- package/lib/__lib__/LegendCubeTesting.d.ts +3 -1
- package/lib/__lib__/LegendCubeTesting.d.ts.map +1 -1
- package/lib/__lib__/LegendCubeTesting.js +2 -0
- package/lib/__lib__/LegendCubeTesting.js.map +1 -1
- package/lib/__lib__/LegendCubeTour.d.ts +62 -0
- package/lib/__lib__/LegendCubeTour.d.ts.map +1 -0
- package/lib/__lib__/LegendCubeTour.js +149 -0
- package/lib/__lib__/LegendCubeTour.js.map +1 -0
- package/lib/__test-utils__/CubeExampleTestUtils.d.ts +34 -0
- package/lib/__test-utils__/CubeExampleTestUtils.d.ts.map +1 -0
- package/lib/__test-utils__/CubeExampleTestUtils.js +72 -0
- package/lib/__test-utils__/CubeExampleTestUtils.js.map +1 -0
- package/lib/__test-utils__/CubeVariantTestUtils.d.ts +16 -0
- package/lib/__test-utils__/CubeVariantTestUtils.d.ts.map +1 -0
- package/lib/__test-utils__/CubeVariantTestUtils.js +66 -0
- package/lib/__test-utils__/CubeVariantTestUtils.js.map +1 -0
- package/lib/components/CubeAddItems.d.ts.map +1 -1
- package/lib/components/CubeAddItems.js +2 -1
- package/lib/components/CubeAddItems.js.map +1 -1
- package/lib/components/CubeEditor.d.ts.map +1 -1
- package/lib/components/CubeEditor.js +11 -5
- package/lib/components/CubeEditor.js.map +1 -1
- package/lib/components/CubeFileMenu.d.ts.map +1 -1
- package/lib/components/CubeFileMenu.js +2 -1
- package/lib/components/CubeFileMenu.js.map +1 -1
- package/lib/components/CubeNodeIcon.d.ts.map +1 -1
- package/lib/components/CubeNodeIcon.js +2 -1
- package/lib/components/CubeNodeIcon.js.map +1 -1
- package/lib/components/canvas/CubeCanvas.d.ts.map +1 -1
- package/lib/components/canvas/CubeCanvas.js +3 -2
- package/lib/components/canvas/CubeCanvas.js.map +1 -1
- package/lib/components/canvas/CubeCanvasNode.d.ts.map +1 -1
- package/lib/components/canvas/CubeCanvasNode.js +4 -1
- package/lib/components/canvas/CubeCanvasNode.js.map +1 -1
- package/lib/components/dev-settings/CubeDevSettings.d.ts +2 -2
- package/lib/components/dev-settings/CubeDevSettings.d.ts.map +1 -1
- package/lib/components/dev-settings/CubeDevSettings.js +12 -8
- package/lib/components/dev-settings/CubeDevSettings.js.map +1 -1
- package/lib/components/editors/CubeColumnPicker.d.ts.map +1 -1
- package/lib/components/editors/CubeColumnPicker.js +3 -1
- package/lib/components/editors/CubeColumnPicker.js.map +1 -1
- package/lib/components/editors/CubeExplodeEditor.d.ts +26 -0
- package/lib/components/editors/CubeExplodeEditor.d.ts.map +1 -0
- package/lib/components/editors/CubeExplodeEditor.js +61 -0
- package/lib/components/editors/CubeExplodeEditor.js.map +1 -0
- package/lib/components/editors/CubeExpressionFilterEditor.d.ts.map +1 -1
- package/lib/components/editors/CubeExpressionFilterEditor.js +8 -5
- package/lib/components/editors/CubeExpressionFilterEditor.js.map +1 -1
- package/lib/components/editors/CubeExtendEditor.d.ts +0 -2
- package/lib/components/editors/CubeExtendEditor.d.ts.map +1 -1
- package/lib/components/editors/CubeExtendEditor.js +12 -11
- package/lib/components/editors/CubeExtendEditor.js.map +1 -1
- package/lib/components/editors/CubeJsonFieldsTree.d.ts +46 -0
- package/lib/components/editors/CubeJsonFieldsTree.d.ts.map +1 -0
- package/lib/components/editors/CubeJsonFieldsTree.js +93 -0
- package/lib/components/editors/CubeJsonFieldsTree.js.map +1 -0
- package/lib/components/editors/CubeNodeEditorRegistry.d.ts.map +1 -1
- package/lib/components/editors/CubeNodeEditorRegistry.js +3 -1
- package/lib/components/editors/CubeNodeEditorRegistry.js.map +1 -1
- package/lib/components/grid/CubeGridRegion.d.ts.map +1 -1
- package/lib/components/grid/CubeGridRegion.js +5 -4
- package/lib/components/grid/CubeGridRegion.js.map +1 -1
- package/lib/components/grid/CubeGridValues.d.ts +5 -3
- package/lib/components/grid/CubeGridValues.d.ts.map +1 -1
- package/lib/components/grid/CubeGridValues.js +8 -3
- package/lib/components/grid/CubeGridValues.js.map +1 -1
- package/lib/components/grid/CubeJsonExplorerDialog.d.ts +27 -0
- package/lib/components/grid/CubeJsonExplorerDialog.d.ts.map +1 -0
- package/lib/components/grid/CubeJsonExplorerDialog.js +102 -0
- package/lib/components/grid/CubeJsonExplorerDialog.js.map +1 -0
- package/lib/components/grid/CubeJsonViewerDialog.d.ts +27 -0
- package/lib/components/grid/CubeJsonViewerDialog.d.ts.map +1 -0
- package/lib/components/grid/CubeJsonViewerDialog.js +68 -0
- package/lib/components/grid/CubeJsonViewerDialog.js.map +1 -0
- package/lib/components/grid/CubeResultGrid.d.ts +15 -3
- package/lib/components/grid/CubeResultGrid.d.ts.map +1 -1
- package/lib/components/grid/CubeResultGrid.js +95 -3
- package/lib/components/grid/CubeResultGrid.js.map +1 -1
- package/lib/components/grid/CubeServerSideGrid.d.ts.map +1 -1
- package/lib/components/grid/CubeServerSideGrid.js +4 -2
- package/lib/components/grid/CubeServerSideGrid.js.map +1 -1
- package/lib/components/palette/CubePalette.d.ts.map +1 -1
- package/lib/components/palette/CubePalette.js +4 -1
- package/lib/components/palette/CubePalette.js.map +1 -1
- package/lib/components/source-picker/CubeIngestTab.d.ts +2 -2
- package/lib/components/source-picker/CubeIngestTab.d.ts.map +1 -1
- package/lib/components/source-picker/CubeIngestTab.js +19 -13
- package/lib/components/source-picker/CubeIngestTab.js.map +1 -1
- package/lib/components/tour/CubeTour.d.ts +36 -0
- package/lib/components/tour/CubeTour.d.ts.map +1 -0
- package/lib/components/tour/CubeTour.js +183 -0
- package/lib/components/tour/CubeTour.js.map +1 -0
- package/lib/index.css +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/stores/CubeEditorState.d.ts +17 -1
- package/lib/stores/CubeEditorState.d.ts.map +1 -1
- package/lib/stores/CubeEditorState.js +35 -5
- package/lib/stores/CubeEditorState.js.map +1 -1
- package/lib/stores/CubeExamples.d.ts.map +1 -1
- package/lib/stores/CubeExamples.js +101 -4
- package/lib/stores/CubeExamples.js.map +1 -1
- package/lib/stores/CubeExamplesState.d.ts.map +1 -1
- package/lib/stores/CubeExamplesState.js +4 -1
- package/lib/stores/CubeExamplesState.js.map +1 -1
- package/lib/stores/CubeGridQuickActions.d.ts +7 -0
- package/lib/stores/CubeGridQuickActions.d.ts.map +1 -1
- package/lib/stores/CubeGridQuickActions.js +20 -11
- package/lib/stores/CubeGridQuickActions.js.map +1 -1
- package/lib/stores/CubePureText.d.ts +20 -0
- package/lib/stores/CubePureText.d.ts.map +1 -0
- package/lib/stores/CubePureText.js +24 -0
- package/lib/stores/CubePureText.js.map +1 -0
- package/lib/stores/CubeTourState.d.ts +49 -0
- package/lib/stores/CubeTourState.d.ts.map +1 -0
- package/lib/stores/CubeTourState.js +92 -0
- package/lib/stores/CubeTourState.js.map +1 -0
- package/lib/stores/LegendCubeHost.d.ts +88 -0
- package/lib/stores/LegendCubeHost.d.ts.map +1 -0
- package/lib/stores/LegendCubeHost.js +149 -0
- package/lib/stores/LegendCubeHost.js.map +1 -0
- package/lib/stores/editors/CubeExplodeDraft.d.ts +87 -0
- package/lib/stores/editors/CubeExplodeDraft.d.ts.map +1 -0
- package/lib/stores/editors/CubeExplodeDraft.js +221 -0
- package/lib/stores/editors/CubeExplodeDraft.js.map +1 -0
- package/lib/stores/editors/CubeExpressionFilterDraft.d.ts +3 -0
- package/lib/stores/editors/CubeExpressionFilterDraft.d.ts.map +1 -1
- package/lib/stores/editors/CubeExpressionFilterDraft.js +4 -0
- package/lib/stores/editors/CubeExpressionFilterDraft.js.map +1 -1
- package/lib/stores/editors/CubeExtendDraft.d.ts +9 -0
- package/lib/stores/editors/CubeExtendDraft.d.ts.map +1 -1
- package/lib/stores/editors/CubeExtendDraft.js +26 -0
- package/lib/stores/editors/CubeExtendDraft.js.map +1 -1
- package/lib/stores/editors/CubeNodeDraftRegistry.d.ts.map +1 -1
- package/lib/stores/editors/CubeNodeDraftRegistry.js +6 -1
- package/lib/stores/editors/CubeNodeDraftRegistry.js.map +1 -1
- package/lib/stores/fixtures/CubeSalesDuckDbModel.d.ts +1 -0
- package/lib/stores/fixtures/CubeSalesDuckDbModel.d.ts.map +1 -1
- package/lib/stores/fixtures/CubeSalesDuckDbModel.js +8 -2
- package/lib/stores/fixtures/CubeSalesDuckDbModel.js.map +1 -1
- package/lib/stores/source-picker/CubeIngestTabState.d.ts +48 -14
- package/lib/stores/source-picker/CubeIngestTabState.d.ts.map +1 -1
- package/lib/stores/source-picker/CubeIngestTabState.js +159 -62
- package/lib/stores/source-picker/CubeIngestTabState.js.map +1 -1
- package/lib/stores/variant/CubeJson.d.ts +48 -0
- package/lib/stores/variant/CubeJson.d.ts.map +1 -0
- package/lib/stores/variant/CubeJson.js +207 -0
- package/lib/stores/variant/CubeJson.js.map +1 -0
- package/lib/stores/variant/CubeJsonExplorerState.d.ts +88 -0
- package/lib/stores/variant/CubeJsonExplorerState.d.ts.map +1 -0
- package/lib/stores/variant/CubeJsonExplorerState.js +297 -0
- package/lib/stores/variant/CubeJsonExplorerState.js.map +1 -0
- package/lib/stores/variant/CubeJsonFieldsState.d.ts +83 -0
- package/lib/stores/variant/CubeJsonFieldsState.d.ts.map +1 -0
- package/lib/stores/variant/CubeJsonFieldsState.js +207 -0
- package/lib/stores/variant/CubeJsonFieldsState.js.map +1 -0
- package/lib/stores/variant/CubeJsonOffers.d.ts +76 -0
- package/lib/stores/variant/CubeJsonOffers.d.ts.map +1 -0
- package/lib/stores/variant/CubeJsonOffers.js +250 -0
- package/lib/stores/variant/CubeJsonOffers.js.map +1 -0
- package/lib/stores/variant/CubeJsonParse.d.ts +28 -0
- package/lib/stores/variant/CubeJsonParse.d.ts.map +1 -0
- package/lib/stores/variant/CubeJsonParse.js +53 -0
- package/lib/stores/variant/CubeJsonParse.js.map +1 -0
- package/lib/stores/variant/CubeJsonSampling.d.ts +41 -0
- package/lib/stores/variant/CubeJsonSampling.d.ts.map +1 -0
- package/lib/stores/variant/CubeJsonSampling.js +59 -0
- package/lib/stores/variant/CubeJsonSampling.js.map +1 -0
- package/lib/stores/variant/CubeJsonShape.d.ts +87 -0
- package/lib/stores/variant/CubeJsonShape.d.ts.map +1 -0
- package/lib/stores/variant/CubeJsonShape.js +209 -0
- package/lib/stores/variant/CubeJsonShape.js.map +1 -0
- package/lib/stores/variant/CubeJsonViewerState.d.ts +33 -0
- package/lib/stores/variant/CubeJsonViewerState.d.ts.map +1 -0
- package/lib/stores/variant/CubeJsonViewerState.js +53 -0
- package/lib/stores/variant/CubeJsonViewerState.js.map +1 -0
- package/package.json +2 -2
- package/src/__lib__/LegendCubeHelpText.ts +4 -0
- package/src/__lib__/LegendCubeLabels.ts +60 -8
- package/src/__lib__/LegendCubeNavigation.ts +48 -0
- package/src/__lib__/LegendCubeTesting.ts +2 -0
- package/src/__lib__/LegendCubeTour.ts +173 -0
- package/src/__test-utils__/CubeExampleTestUtils.ts +119 -0
- package/src/__test-utils__/CubeVariantTestUtils.ts +103 -0
- package/src/components/CubeAddItems.tsx +2 -0
- package/src/components/CubeEditor.tsx +15 -0
- package/src/components/CubeFileMenu.tsx +2 -0
- package/src/components/CubeNodeIcon.tsx +2 -0
- package/src/components/canvas/CubeCanvas.tsx +17 -1
- package/src/components/canvas/CubeCanvasNode.tsx +7 -0
- package/src/components/dev-settings/CubeDevSettings.tsx +17 -2
- package/src/components/editors/CubeColumnPicker.tsx +3 -0
- package/src/components/editors/CubeExplodeEditor.tsx +207 -0
- package/src/components/editors/CubeExpressionFilterEditor.tsx +25 -8
- package/src/components/editors/CubeExtendEditor.tsx +32 -19
- package/src/components/editors/CubeJsonFieldsTree.tsx +316 -0
- package/src/components/editors/CubeNodeEditorRegistry.ts +3 -0
- package/src/components/grid/CubeGridRegion.tsx +21 -3
- package/src/components/grid/CubeGridValues.ts +8 -3
- package/src/components/grid/CubeJsonExplorerDialog.tsx +304 -0
- package/src/components/grid/CubeJsonViewerDialog.tsx +190 -0
- package/src/components/grid/CubeResultGrid.tsx +144 -2
- package/src/components/grid/CubeServerSideGrid.tsx +13 -0
- package/src/components/palette/CubePalette.tsx +9 -0
- package/src/components/source-picker/CubeIngestTab.tsx +62 -30
- package/src/components/tour/CubeTour.tsx +317 -0
- package/src/index.ts +7 -0
- package/src/stores/CubeEditorState.ts +36 -5
- package/src/stores/CubeExamples.ts +120 -2
- package/src/stores/CubeExamplesState.ts +9 -1
- package/src/stores/CubeGridQuickActions.ts +23 -12
- package/src/stores/CubePureText.ts +28 -0
- package/src/stores/CubeTourState.ts +116 -0
- package/src/stores/LegendCubeHost.ts +243 -0
- package/src/stores/editors/CubeExplodeDraft.ts +272 -0
- package/src/stores/editors/CubeExpressionFilterDraft.ts +4 -0
- package/src/stores/editors/CubeExtendDraft.ts +29 -0
- package/src/stores/editors/CubeNodeDraftRegistry.ts +7 -0
- package/src/stores/fixtures/CubeSalesDuckDbModel.ts +8 -2
- package/src/stores/source-picker/CubeIngestTabState.ts +253 -102
- package/src/stores/variant/CubeJson.ts +229 -0
- package/src/stores/variant/CubeJsonExplorerState.ts +378 -0
- package/src/stores/variant/CubeJsonFieldsState.ts +252 -0
- package/src/stores/variant/CubeJsonOffers.ts +359 -0
- package/src/stores/variant/CubeJsonParse.ts +75 -0
- package/src/stores/variant/CubeJsonSampling.ts +90 -0
- package/src/stores/variant/CubeJsonShape.ts +279 -0
- package/src/stores/variant/CubeJsonViewerState.ts +59 -0
- package/tsconfig.json +22 -1
|
@@ -27,6 +27,10 @@ import {
|
|
|
27
27
|
import { memo, useCallback, useEffect, useMemo, useState } from 'react';
|
|
28
28
|
import { NULL_CELL_TEXT } from '../../__lib__/LegendCubeLabels.js';
|
|
29
29
|
import { LEGEND_CUBE_TEST_ID } from '../../__lib__/LegendCubeTesting.js';
|
|
30
|
+
import {
|
|
31
|
+
CUBE_TOUR_ANCHOR,
|
|
32
|
+
cubeTourAnchor,
|
|
33
|
+
} from '../../__lib__/LegendCubeTour.js';
|
|
30
34
|
import type { CubeEditorState } from '../../stores/CubeEditorState.js';
|
|
31
35
|
import type { CubeExecutionResult } from '../../stores/CubeExecutionState.js';
|
|
32
36
|
import type {
|
|
@@ -37,7 +41,9 @@ import { formatCellValue } from './CubeGridValues.js';
|
|
|
37
41
|
import {
|
|
38
42
|
buildCubeColumnDefs,
|
|
39
43
|
CUBE_EXCEL_STYLES,
|
|
44
|
+
type CubeGridHeaderMenuParams,
|
|
40
45
|
getCubeGridContextMenuItems,
|
|
46
|
+
getCubeGridHeaderMenuItems,
|
|
41
47
|
keepCubeColumnWidths,
|
|
42
48
|
} from './CubeResultGrid.js';
|
|
43
49
|
|
|
@@ -102,6 +108,11 @@ export const CubeServerSideGrid = memo(
|
|
|
102
108
|
getCubeGridContextMenuItems(editorState, params),
|
|
103
109
|
[editorState],
|
|
104
110
|
);
|
|
111
|
+
const getMainMenuItems = useCallback(
|
|
112
|
+
(params: CubeGridHeaderMenuParams<CubeGridRowData>) =>
|
|
113
|
+
getCubeGridHeaderMenuItems(editorState, params),
|
|
114
|
+
[editorState],
|
|
115
|
+
);
|
|
105
116
|
const columnDefs = useMemo(
|
|
106
117
|
(): DataGridColumnDefinition<CubeGridRowData>[] =>
|
|
107
118
|
buildCubeColumnDefs<CubeGridRowData>(
|
|
@@ -154,6 +165,7 @@ export const CubeServerSideGrid = memo(
|
|
|
154
165
|
'ag-theme-balham-dark': darkMode,
|
|
155
166
|
})}
|
|
156
167
|
data-testid={LEGEND_CUBE_TEST_ID.RESULT_GRID}
|
|
168
|
+
{...cubeTourAnchor(CUBE_TOUR_ANCHOR.RESULTS)}
|
|
157
169
|
>
|
|
158
170
|
<DataGrid<CubeGridRowData>
|
|
159
171
|
rowModelType="serverSide"
|
|
@@ -163,6 +175,7 @@ export const CubeServerSideGrid = memo(
|
|
|
163
175
|
suppressFieldDotNotation={true}
|
|
164
176
|
tooltipShowDelay={500}
|
|
165
177
|
getContextMenuItems={getContextMenuItems}
|
|
178
|
+
getMainMenuItems={getMainMenuItems}
|
|
166
179
|
serverSideSortAllLevels={true}
|
|
167
180
|
excelStyles={CUBE_EXCEL_STYLES}
|
|
168
181
|
onColumnResized={(event) =>
|
|
@@ -31,6 +31,10 @@ import {
|
|
|
31
31
|
READ_ONLY_CUBE_TITLE,
|
|
32
32
|
} from '../../__lib__/LegendCubeLabels.js';
|
|
33
33
|
import { LEGEND_CUBE_TEST_ID } from '../../__lib__/LegendCubeTesting.js';
|
|
34
|
+
import {
|
|
35
|
+
CUBE_TOUR_ANCHOR,
|
|
36
|
+
cubeTourAnchor,
|
|
37
|
+
} from '../../__lib__/LegendCubeTour.js';
|
|
34
38
|
import {
|
|
35
39
|
addCubeNode,
|
|
36
40
|
getCubeAddDatabaseProblem,
|
|
@@ -109,6 +113,11 @@ const CubePaletteItem = observer(
|
|
|
109
113
|
tabIndex={disabled ? -1 : 0}
|
|
110
114
|
aria-disabled={disabled}
|
|
111
115
|
aria-label={label}
|
|
116
|
+
{...cubeTourAnchor(
|
|
117
|
+
definition.kind === 'source'
|
|
118
|
+
? CUBE_TOUR_ANCHOR.SOURCES
|
|
119
|
+
: CUBE_TOUR_ANCHOR.STEPS,
|
|
120
|
+
)}
|
|
112
121
|
className={clsx(
|
|
113
122
|
'flex h-8 shrink-0 items-center gap-2 px-3 text-base',
|
|
114
123
|
disabled
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
import { clsx } from '@finos/legend-art';
|
|
18
18
|
import { observer } from 'mobx-react-lite';
|
|
19
|
-
import { useId } from 'react';
|
|
19
|
+
import { Fragment, useId } from 'react';
|
|
20
20
|
import { CUBE_DATA_PRODUCT_ENVIRONMENT_LABELS } from '../../__lib__/LegendCubeDataProductLabels.js';
|
|
21
21
|
import { getDroppedDefinitionsLabel } from '../../__lib__/LegendCubeIngestLabels.js';
|
|
22
22
|
import {
|
|
@@ -62,11 +62,16 @@ const CubeIngestTabAlert: React.FC<{
|
|
|
62
62
|
);
|
|
63
63
|
|
|
64
64
|
/**
|
|
65
|
-
*
|
|
66
|
-
* ticked to add
|
|
65
|
+
* The data sets of the opened definitions, as both Ingest screens list them,
|
|
66
|
+
* each picked or ticked to add, under its definition's name when several are
|
|
67
|
+
* opened; the one picked last previewed
|
|
67
68
|
*/
|
|
68
69
|
const CubeIngestDataSets = observer((props: { tab: CubeIngestTabState }) => {
|
|
69
70
|
const { tab } = props;
|
|
71
|
+
const read = tab.definitions.filter(
|
|
72
|
+
(definition) => tab.getDataSets(definition.urn) !== undefined,
|
|
73
|
+
);
|
|
74
|
+
const grouped = tab.definitions.length > 1;
|
|
70
75
|
return (
|
|
71
76
|
<>
|
|
72
77
|
{tab.isDescribing && (
|
|
@@ -74,34 +79,56 @@ const CubeIngestDataSets = observer((props: { tab: CubeIngestTabState }) => {
|
|
|
74
79
|
reading the definition
|
|
75
80
|
</div>
|
|
76
81
|
)}
|
|
77
|
-
{
|
|
82
|
+
{read.length > 0 && (
|
|
78
83
|
<ul
|
|
79
|
-
className={`max-h-
|
|
84
|
+
className={`max-h-48 overflow-auto ${CUBE_PICKER_LIST_CLASS}`}
|
|
80
85
|
aria-label="Data sets"
|
|
81
86
|
>
|
|
82
|
-
{
|
|
83
|
-
<
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
87
|
+
{read.map((definition) => (
|
|
88
|
+
<Fragment key={definition.urn}>
|
|
89
|
+
{grouped && (
|
|
90
|
+
<li className="px-2 pt-1 text-sm text-[var(--color-text-muted)]">
|
|
91
|
+
{definition.name}
|
|
92
|
+
</li>
|
|
93
|
+
)}
|
|
94
|
+
{(tab.getDataSets(definition.urn) ?? []).map((dataSet) => (
|
|
95
|
+
<CubePickerListItem
|
|
96
|
+
key={dataSet.name}
|
|
97
|
+
picked={tab.isDataSetPicked(dataSet.name, definition.urn)}
|
|
98
|
+
disabled={!dataSet.isPickable}
|
|
99
|
+
title={dataSet.disabledReason}
|
|
100
|
+
adds={true}
|
|
101
|
+
onPick={() => tab.selectDataSet(dataSet.name, definition.urn)}
|
|
102
|
+
onToggle={() =>
|
|
103
|
+
tab.toggleDataSet(dataSet.name, definition.urn)
|
|
104
|
+
}
|
|
105
|
+
toggleLabel={
|
|
106
|
+
grouped
|
|
107
|
+
? `${dataSet.name} of ${definition.name}`
|
|
108
|
+
: dataSet.name
|
|
109
|
+
}
|
|
110
|
+
>
|
|
111
|
+
<span className="min-w-0 flex-1 truncate">
|
|
112
|
+
{dataSet.name}
|
|
113
|
+
</span>
|
|
114
|
+
<span className="shrink-0 text-sm text-[var(--color-text-muted)]">
|
|
115
|
+
{dataSet.isPickable
|
|
116
|
+
? `${dataSet.schema?.columns.length ?? 0} columns`
|
|
117
|
+
: dataSet.disabledReason}
|
|
118
|
+
</span>
|
|
119
|
+
</CubePickerListItem>
|
|
120
|
+
))}
|
|
121
|
+
</Fragment>
|
|
100
122
|
))}
|
|
101
123
|
</ul>
|
|
102
124
|
)}
|
|
103
125
|
{tab.dataSet?.schema && (
|
|
104
|
-
<section aria-label="Data set preview">
|
|
126
|
+
<section aria-label="Data set preview" className="flex flex-col gap-1">
|
|
127
|
+
{grouped && (
|
|
128
|
+
<span className="text-sm text-[var(--color-text-muted)]">
|
|
129
|
+
{`${tab.dataSet.name} of ${tab.previewedDefinition?.name ?? ''}`}
|
|
130
|
+
</span>
|
|
131
|
+
)}
|
|
105
132
|
<CubeSchemaColumnsTable schema={tab.dataSet.schema} />
|
|
106
133
|
</section>
|
|
107
134
|
)}
|
|
@@ -111,8 +138,9 @@ const CubeIngestDataSets = observer((props: { tab: CubeIngestTabState }) => {
|
|
|
111
138
|
|
|
112
139
|
/**
|
|
113
140
|
* The Ingest tab on a host reading one project's definitions (Studio): the
|
|
114
|
-
* project's definitions by name, each path muted beside it, then
|
|
115
|
-
*
|
|
141
|
+
* project's definitions by name, each path muted beside it, then the data
|
|
142
|
+
* sets of the one picked, or of several ticked; the runtime its runs use is
|
|
143
|
+
* named, not picked
|
|
116
144
|
*/
|
|
117
145
|
const CubeProjectIngestTab = observer((props: { tab: CubeIngestTabState }) => {
|
|
118
146
|
const { tab } = props;
|
|
@@ -142,9 +170,11 @@ const CubeProjectIngestTab = observer((props: { tab: CubeIngestTabState }) => {
|
|
|
142
170
|
{candidates.map((candidate) => (
|
|
143
171
|
<CubePickerListItem
|
|
144
172
|
key={candidate.urn}
|
|
145
|
-
picked={tab.
|
|
173
|
+
picked={tab.isDefinitionOpened(candidate)}
|
|
146
174
|
title={candidate.definition}
|
|
147
175
|
onPick={() => tab.selectDefinition(candidate)}
|
|
176
|
+
onToggle={() => tab.toggleDefinition(candidate)}
|
|
177
|
+
toggleLabel={candidate.name}
|
|
148
178
|
>
|
|
149
179
|
<span className="shrink-0">{candidate.name}</span>
|
|
150
180
|
<span className="min-w-0 flex-1 truncate text-sm text-[var(--color-text-muted)]">
|
|
@@ -178,8 +208,8 @@ const CubeProjectIngestTab = observer((props: { tab: CubeIngestTabState }) => {
|
|
|
178
208
|
|
|
179
209
|
/**
|
|
180
210
|
* The source dialog's Ingest tab (PLAN §6.7): Mode, the viewer's
|
|
181
|
-
* environment (read-only), a producer deployment, one of its definitions,
|
|
182
|
-
* then
|
|
211
|
+
* environment (read-only), a producer deployment, one of its definitions, or
|
|
212
|
+
* several ticked, then data sets with their columns. The cube's first data set fixes the
|
|
183
213
|
* Mode, the producer deployment and the warehouse.
|
|
184
214
|
*/
|
|
185
215
|
export const CubeIngestTab = observer((props: { tab: CubeIngestTabState }) => {
|
|
@@ -256,13 +286,15 @@ export const CubeIngestTab = observer((props: { tab: CubeIngestTabState }) => {
|
|
|
256
286
|
aria-label="Ingest definitions"
|
|
257
287
|
>
|
|
258
288
|
{tab.shownDefinitions.map((candidate) => {
|
|
259
|
-
const isPicked = tab.
|
|
289
|
+
const isPicked = tab.isDefinitionOpened(candidate);
|
|
260
290
|
return (
|
|
261
291
|
<CubePickerListItem
|
|
262
292
|
key={candidate.urn}
|
|
263
293
|
picked={isPicked}
|
|
264
294
|
title={candidate.definition}
|
|
265
295
|
onPick={() => tab.selectDefinition(candidate)}
|
|
296
|
+
onToggle={() => tab.toggleDefinition(candidate)}
|
|
297
|
+
toggleLabel={candidate.name}
|
|
266
298
|
>
|
|
267
299
|
<span className="min-w-0 flex-1 truncate">
|
|
268
300
|
{candidate.name}
|
|
@@ -0,0 +1,317 @@
|
|
|
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 { BasePopper } from '@finos/legend-art';
|
|
18
|
+
import { noop } from '@finos/legend-shared';
|
|
19
|
+
import { observer } from 'mobx-react-lite';
|
|
20
|
+
import { useEffect, useId, useMemo, useRef, useState } from 'react';
|
|
21
|
+
import { createPortal } from 'react-dom';
|
|
22
|
+
import {
|
|
23
|
+
CUBE_TOUR_ANCHOR_ATTRIBUTE,
|
|
24
|
+
CUBE_TOUR_LABEL,
|
|
25
|
+
type CUBE_TOUR_ANCHOR,
|
|
26
|
+
type CubeTourStep,
|
|
27
|
+
} from '../../__lib__/LegendCubeTour.js';
|
|
28
|
+
import type { CubeEditorState } from '../../stores/CubeEditorState.js';
|
|
29
|
+
import { CubeButton } from '../CubeButton.js';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Above the canvas, the grid and the node editor (1250), below MUI's dialogs
|
|
33
|
+
* (1300); inside the dialog that hosts the page, if one does
|
|
34
|
+
*/
|
|
35
|
+
export const CUBE_TOUR_Z_INDEX = 1290;
|
|
36
|
+
|
|
37
|
+
/** Room around the controls shown, and between them and the card */
|
|
38
|
+
const PADDING = 4;
|
|
39
|
+
const GAP = 12;
|
|
40
|
+
/** The card's width (Legend's root is 62.5%, so it is written in pixels) */
|
|
41
|
+
const CARD_WIDTH = 340;
|
|
42
|
+
const DIM = 'rgba(0, 0, 0, 0.45)';
|
|
43
|
+
|
|
44
|
+
const MODIFIERS = [
|
|
45
|
+
{ name: 'offset', options: { offset: [0, GAP + PADDING] } },
|
|
46
|
+
{ name: 'flip', options: { padding: GAP } },
|
|
47
|
+
{
|
|
48
|
+
name: 'preventOverflow',
|
|
49
|
+
options: { rootBoundary: 'viewport', padding: GAP, altAxis: true },
|
|
50
|
+
},
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
type CubePopperInstance = NonNullable<
|
|
54
|
+
Extract<
|
|
55
|
+
React.ComponentProps<typeof BasePopper>['popperRef'],
|
|
56
|
+
React.RefObject<unknown>
|
|
57
|
+
>['current']
|
|
58
|
+
>;
|
|
59
|
+
|
|
60
|
+
interface Box {
|
|
61
|
+
left: number;
|
|
62
|
+
top: number;
|
|
63
|
+
width: number;
|
|
64
|
+
height: number;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** The page's controls that carry the anchor */
|
|
68
|
+
const findAnchored = (root: Element, anchor: CUBE_TOUR_ANCHOR): Element[] =>
|
|
69
|
+
Array.from(
|
|
70
|
+
root.querySelectorAll(`[${CUBE_TOUR_ANCHOR_ATTRIBUTE}~="${anchor}"]`),
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
/** The box around the anchor's controls that have a size; none if none has */
|
|
74
|
+
const boxOf = (root: Element, anchor: CUBE_TOUR_ANCHOR): Box | undefined =>
|
|
75
|
+
findAnchored(root, anchor)
|
|
76
|
+
.map((element) => element.getBoundingClientRect())
|
|
77
|
+
.filter((rect) => rect.width > 0 || rect.height > 0)
|
|
78
|
+
.reduce<Box | undefined>((box, rect) => {
|
|
79
|
+
if (!box) {
|
|
80
|
+
return {
|
|
81
|
+
left: rect.left,
|
|
82
|
+
top: rect.top,
|
|
83
|
+
width: rect.width,
|
|
84
|
+
height: rect.height,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
const left = Math.min(box.left, rect.left);
|
|
88
|
+
const top = Math.min(box.top, rect.top);
|
|
89
|
+
return {
|
|
90
|
+
left,
|
|
91
|
+
top,
|
|
92
|
+
width: Math.max(box.left + box.width, rect.right) - left,
|
|
93
|
+
height: Math.max(box.top + box.height, rect.bottom) - top,
|
|
94
|
+
};
|
|
95
|
+
}, undefined);
|
|
96
|
+
|
|
97
|
+
const isSameBox = (a: Box | undefined, b: Box | undefined): boolean =>
|
|
98
|
+
a === b ||
|
|
99
|
+
(a !== undefined &&
|
|
100
|
+
b !== undefined &&
|
|
101
|
+
a.left === b.left &&
|
|
102
|
+
a.top === b.top &&
|
|
103
|
+
a.width === b.width &&
|
|
104
|
+
a.height === b.height);
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* The editor's guided tour (PLAN §11.21): the page dimmed but for the
|
|
108
|
+
* current step's controls, and a card beside them, or in the middle of the
|
|
109
|
+
* page for a step that points at nothing. The controls are followed as the
|
|
110
|
+
* page moves them (a panel resized, the grid loaded). Clicks on the page
|
|
111
|
+
* are held while it shows; Esc ends it, the arrow keys move through it.
|
|
112
|
+
*/
|
|
113
|
+
export const CubeTour = observer(
|
|
114
|
+
(props: {
|
|
115
|
+
editorState: CubeEditorState;
|
|
116
|
+
/** The page, whose controls the steps point at */
|
|
117
|
+
root: HTMLElement | null;
|
|
118
|
+
}) => {
|
|
119
|
+
const { editorState, root } = props;
|
|
120
|
+
const { tour } = editorState;
|
|
121
|
+
const { step } = tour;
|
|
122
|
+
const titleId = useId();
|
|
123
|
+
const textId = useId();
|
|
124
|
+
const [box, setBox] = useState<Box | undefined>(undefined);
|
|
125
|
+
const popperRef = useRef<CubePopperInstance>(null);
|
|
126
|
+
const isShown = useMemo(
|
|
127
|
+
() =>
|
|
128
|
+
(candidate: CubeTourStep): boolean =>
|
|
129
|
+
candidate.anchor === undefined ||
|
|
130
|
+
(root !== null && findAnchored(root, candidate.anchor).length > 0),
|
|
131
|
+
[root],
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
// the step's controls followed, frame by frame, while it shows
|
|
135
|
+
const anchor = step?.anchor;
|
|
136
|
+
useEffect(() => {
|
|
137
|
+
if (anchor === undefined || !root) {
|
|
138
|
+
setBox(undefined);
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
141
|
+
let frame = 0;
|
|
142
|
+
const follow = (): void => {
|
|
143
|
+
const next = boxOf(root, anchor);
|
|
144
|
+
setBox((current) => (isSameBox(current, next) ? current : next));
|
|
145
|
+
frame = requestAnimationFrame(follow);
|
|
146
|
+
};
|
|
147
|
+
follow();
|
|
148
|
+
return () => cancelAnimationFrame(frame);
|
|
149
|
+
}, [anchor, root]);
|
|
150
|
+
useEffect(() => {
|
|
151
|
+
popperRef.current?.update().catch(noop());
|
|
152
|
+
}, [box]);
|
|
153
|
+
|
|
154
|
+
// the tour's keys come first: the page's shortcuts wait while it shows
|
|
155
|
+
const isOpen = step !== undefined;
|
|
156
|
+
useEffect(() => {
|
|
157
|
+
if (!isOpen) {
|
|
158
|
+
return undefined;
|
|
159
|
+
}
|
|
160
|
+
const onKeyDown = (event: KeyboardEvent): void => {
|
|
161
|
+
if (event.key === 'Escape') {
|
|
162
|
+
tour.close();
|
|
163
|
+
} else if (event.key === 'ArrowRight') {
|
|
164
|
+
tour.move(1, isShown);
|
|
165
|
+
} else if (event.key === 'ArrowLeft') {
|
|
166
|
+
tour.move(-1, isShown);
|
|
167
|
+
} else {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
event.preventDefault();
|
|
171
|
+
event.stopPropagation();
|
|
172
|
+
};
|
|
173
|
+
window.addEventListener('keydown', onKeyDown, true);
|
|
174
|
+
return () => window.removeEventListener('keydown', onKeyDown, true);
|
|
175
|
+
}, [isOpen, tour, isShown]);
|
|
176
|
+
|
|
177
|
+
const spotlight = useMemo(
|
|
178
|
+
() =>
|
|
179
|
+
box && {
|
|
180
|
+
left: box.left - PADDING,
|
|
181
|
+
top: box.top - PADDING,
|
|
182
|
+
width: box.width + 2 * PADDING,
|
|
183
|
+
height: box.height + 2 * PADDING,
|
|
184
|
+
},
|
|
185
|
+
[box],
|
|
186
|
+
);
|
|
187
|
+
// the popper reads the spotlight wherever it places the card
|
|
188
|
+
const latest = useRef(spotlight);
|
|
189
|
+
latest.current = spotlight;
|
|
190
|
+
const virtualAnchor = useMemo(
|
|
191
|
+
() => ({
|
|
192
|
+
getBoundingClientRect: (): DOMRect => {
|
|
193
|
+
const current = latest.current ?? {
|
|
194
|
+
left: 0,
|
|
195
|
+
top: 0,
|
|
196
|
+
width: 0,
|
|
197
|
+
height: 0,
|
|
198
|
+
};
|
|
199
|
+
return DOMRect.fromRect({
|
|
200
|
+
x: current.left,
|
|
201
|
+
y: current.top,
|
|
202
|
+
width: current.width,
|
|
203
|
+
height: current.height,
|
|
204
|
+
});
|
|
205
|
+
},
|
|
206
|
+
}),
|
|
207
|
+
[],
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
if (!step || !root) {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
// counted over the whole tour, steady while an example loads and adds
|
|
214
|
+
// the controls of later steps; a step skipped skips its number
|
|
215
|
+
const position = tour.index + 1;
|
|
216
|
+
const isLast = tour.steps.filter(isShown).at(-1) === step;
|
|
217
|
+
// a page hosted in a dialog, as Studio hosts Cube, keeps the tour in the
|
|
218
|
+
// dialog's layer, above the page
|
|
219
|
+
const container =
|
|
220
|
+
root.closest<HTMLElement>('.MuiDialog-root') ?? root.ownerDocument.body;
|
|
221
|
+
const card = (
|
|
222
|
+
<div
|
|
223
|
+
// a new card for each step, so its Next button takes the focus
|
|
224
|
+
key={tour.index}
|
|
225
|
+
className="legend-cube__tour flex flex-col gap-2 rounded-md border border-[var(--color-border-default)] bg-[var(--color-bg-elevated)] p-3 text-[var(--color-text-primary)] shadow-lg"
|
|
226
|
+
style={{ width: CARD_WIDTH, maxWidth: `calc(100vw - ${2 * GAP}px)` }}
|
|
227
|
+
role="dialog"
|
|
228
|
+
aria-modal="true"
|
|
229
|
+
aria-labelledby={titleId}
|
|
230
|
+
aria-describedby={textId}
|
|
231
|
+
>
|
|
232
|
+
<div className="flex items-baseline gap-2">
|
|
233
|
+
<h2 id={titleId} className="min-w-0 flex-1 text-lg font-medium">
|
|
234
|
+
{step.title}
|
|
235
|
+
</h2>
|
|
236
|
+
<span className="shrink-0 text-sm text-[var(--color-text-secondary)]">
|
|
237
|
+
{CUBE_TOUR_LABEL.progress(position, tour.steps.length)}
|
|
238
|
+
</span>
|
|
239
|
+
</div>
|
|
240
|
+
<p id={textId} className="text-base leading-snug">
|
|
241
|
+
{step.text}
|
|
242
|
+
{tour.openedExample && tour.index === 0 && (
|
|
243
|
+
<> {CUBE_TOUR_LABEL.EXAMPLE_OPENED}</>
|
|
244
|
+
)}
|
|
245
|
+
</p>
|
|
246
|
+
<div className="flex items-center gap-2">
|
|
247
|
+
{!isLast && (
|
|
248
|
+
<button
|
|
249
|
+
type="button"
|
|
250
|
+
className="text-sm text-[var(--color-text-secondary)] underline hover:text-[var(--color-text-primary)]"
|
|
251
|
+
onClick={() => tour.close()}
|
|
252
|
+
>
|
|
253
|
+
{CUBE_TOUR_LABEL.END}
|
|
254
|
+
</button>
|
|
255
|
+
)}
|
|
256
|
+
<span className="flex-1" />
|
|
257
|
+
{position > 1 && (
|
|
258
|
+
<CubeButton onClick={() => tour.move(-1, isShown)}>
|
|
259
|
+
{CUBE_TOUR_LABEL.BACK}
|
|
260
|
+
</CubeButton>
|
|
261
|
+
)}
|
|
262
|
+
<CubeButton
|
|
263
|
+
primary={true}
|
|
264
|
+
// the keyboard follows the tour: Enter moves on
|
|
265
|
+
autoFocus={true}
|
|
266
|
+
onClick={() => tour.move(1, isShown)}
|
|
267
|
+
>
|
|
268
|
+
{isLast ? CUBE_TOUR_LABEL.DONE : CUBE_TOUR_LABEL.NEXT}
|
|
269
|
+
</CubeButton>
|
|
270
|
+
</div>
|
|
271
|
+
</div>
|
|
272
|
+
);
|
|
273
|
+
return createPortal(
|
|
274
|
+
<div
|
|
275
|
+
className="legend-cube__tour-layer"
|
|
276
|
+
style={{ position: 'fixed', inset: 0, zIndex: CUBE_TOUR_Z_INDEX }}
|
|
277
|
+
// the page underneath takes no clicks, and shows no menu, meanwhile
|
|
278
|
+
onContextMenu={(event) => event.preventDefault()}
|
|
279
|
+
>
|
|
280
|
+
{spotlight ? (
|
|
281
|
+
<>
|
|
282
|
+
<div
|
|
283
|
+
aria-hidden={true}
|
|
284
|
+
style={{
|
|
285
|
+
position: 'fixed',
|
|
286
|
+
...spotlight,
|
|
287
|
+
borderRadius: 6,
|
|
288
|
+
boxShadow: `0 0 0 2px var(--color-accent), 0 0 0 9999px ${DIM}`,
|
|
289
|
+
pointerEvents: 'none',
|
|
290
|
+
}}
|
|
291
|
+
/>
|
|
292
|
+
<BasePopper
|
|
293
|
+
open={true}
|
|
294
|
+
role="presentation"
|
|
295
|
+
popperRef={popperRef}
|
|
296
|
+
anchorEl={virtualAnchor}
|
|
297
|
+
container={container}
|
|
298
|
+
placement={step.placement ?? 'bottom'}
|
|
299
|
+
modifiers={MODIFIERS}
|
|
300
|
+
style={{ zIndex: CUBE_TOUR_Z_INDEX + 1 }}
|
|
301
|
+
>
|
|
302
|
+
{card}
|
|
303
|
+
</BasePopper>
|
|
304
|
+
</>
|
|
305
|
+
) : (
|
|
306
|
+
<div
|
|
307
|
+
className="flex items-center justify-center"
|
|
308
|
+
style={{ position: 'fixed', inset: 0, background: DIM }}
|
|
309
|
+
>
|
|
310
|
+
{card}
|
|
311
|
+
</div>
|
|
312
|
+
)}
|
|
313
|
+
</div>,
|
|
314
|
+
container,
|
|
315
|
+
);
|
|
316
|
+
},
|
|
317
|
+
);
|
package/src/index.ts
CHANGED
|
@@ -63,6 +63,13 @@ export {
|
|
|
63
63
|
isCubeProjectHost,
|
|
64
64
|
isCubeSampleDataOffered,
|
|
65
65
|
} from './stores/CubeHost.js';
|
|
66
|
+
export {
|
|
67
|
+
buildLegendCubeLakehouseServices,
|
|
68
|
+
buildLegendCubeProjectCatalog,
|
|
69
|
+
LegendCubeHost,
|
|
70
|
+
type LegendCubeHostConfig,
|
|
71
|
+
type LegendCubeHostParts,
|
|
72
|
+
} from './stores/LegendCubeHost.js';
|
|
66
73
|
export { getCubeRememberedWarehouse } from './stores/CubeDataProductWarehouse.js';
|
|
67
74
|
export {
|
|
68
75
|
CUBE_LAMBDA_IMPORT_MESSAGE,
|
|
@@ -104,6 +104,7 @@ import {
|
|
|
104
104
|
import { recheckCubeFunctionSources } from './CubeFunctionRecheck.js';
|
|
105
105
|
import { CubeDataProductRuntimeState } from './CubeDataProductRuntimeState.js';
|
|
106
106
|
import { CubeExamplesState } from './CubeExamplesState.js';
|
|
107
|
+
import { CubeTourState } from './CubeTourState.js';
|
|
107
108
|
import { CubeExportState } from './CubeExportState.js';
|
|
108
109
|
import { CubeGridFetcher } from './CubeGridFetcher.js';
|
|
109
110
|
import { CubeGridState } from './CubeGridState.js';
|
|
@@ -123,6 +124,8 @@ import { getCubeExtendUpstream } from './CubeExtendTyping.js';
|
|
|
123
124
|
import { type CubeHost, loadCubeModelOutline } from './CubeHost.js';
|
|
124
125
|
import { CubeNodeEditorState } from './CubeNodeEditorState.js';
|
|
125
126
|
import { CubeShowPureState } from './CubeShowPureState.js';
|
|
127
|
+
import { CubeJsonViewerState } from './variant/CubeJsonViewerState.js';
|
|
128
|
+
import { CubeJsonExplorerState } from './variant/CubeJsonExplorerState.js';
|
|
126
129
|
import { CubeLambdaImportState } from './CubeLambdaImportState.js';
|
|
127
130
|
import { CubeSourcePickerState } from './CubeSourcePickerState.js';
|
|
128
131
|
import { CubeSpecTransferState } from './CubeSpecTransferState.js';
|
|
@@ -199,6 +202,12 @@ export class CubeEditorState implements CommandRegistrar {
|
|
|
199
202
|
readonly specTransfer: CubeSpecTransferState;
|
|
200
203
|
readonly examples: CubeExamplesState;
|
|
201
204
|
readonly showPure: CubeShowPureState;
|
|
205
|
+
/** The editor's guided tour (PLAN §11.21) */
|
|
206
|
+
readonly tour: CubeTourState;
|
|
207
|
+
/** Open JSON: a Variant cell's document as a tree (PLAN §11.20) */
|
|
208
|
+
readonly jsonViewer = new CubeJsonViewerState();
|
|
209
|
+
/** Explore JSON: a Variant column's fields, and the columns they can become (PLAN §11.20) */
|
|
210
|
+
readonly jsonExplorer: CubeJsonExplorerState;
|
|
202
211
|
/** Import Pure: the lambda the page opened on, read into the cube (PLAN §11.18) */
|
|
203
212
|
readonly lambdaImport: CubeLambdaImportState;
|
|
204
213
|
readonly nodeEditor: CubeNodeEditorState;
|
|
@@ -316,6 +325,7 @@ export class CubeEditorState implements CommandRegistrar {
|
|
|
316
325
|
connect: action,
|
|
317
326
|
addNode: action,
|
|
318
327
|
addConfiguredNode: action,
|
|
328
|
+
addConfiguredNodes: action,
|
|
319
329
|
dropNode: action,
|
|
320
330
|
removeNode: action,
|
|
321
331
|
copiedNode: observable.ref,
|
|
@@ -368,6 +378,8 @@ export class CubeEditorState implements CommandRegistrar {
|
|
|
368
378
|
this.specTransfer = new CubeSpecTransferState(this);
|
|
369
379
|
this.examples = new CubeExamplesState(this);
|
|
370
380
|
this.showPure = new CubeShowPureState(this);
|
|
381
|
+
this.tour = new CubeTourState(this);
|
|
382
|
+
this.jsonExplorer = new CubeJsonExplorerState(this);
|
|
371
383
|
this.lambdaImport = new CubeLambdaImportState(this);
|
|
372
384
|
this.nodeEditor = new CubeNodeEditorState(this);
|
|
373
385
|
this.rechecking = reaction(
|
|
@@ -762,7 +774,7 @@ export class CubeEditorState implements CommandRegistrar {
|
|
|
762
774
|
}
|
|
763
775
|
}
|
|
764
776
|
|
|
765
|
-
/** One of Cube's dialogs is open: the source picker, Examples, the group rows' settings, Import or Export, Show Pure */
|
|
777
|
+
/** One of Cube's dialogs is open: the source picker, Examples, the group rows' settings, Import or Export, Show Pure, or the tour */
|
|
766
778
|
get isDialogOpen(): boolean {
|
|
767
779
|
return (
|
|
768
780
|
this.sourcePicker.isOpen ||
|
|
@@ -770,7 +782,8 @@ export class CubeEditorState implements CommandRegistrar {
|
|
|
770
782
|
this.grid.isSettingsShown ||
|
|
771
783
|
this.grid.formatColumn !== undefined ||
|
|
772
784
|
this.specTransfer.mode !== undefined ||
|
|
773
|
-
this.showPure.isOpen
|
|
785
|
+
this.showPure.isOpen ||
|
|
786
|
+
this.tour.isOpen
|
|
774
787
|
);
|
|
775
788
|
}
|
|
776
789
|
|
|
@@ -1505,11 +1518,29 @@ export class CubeEditorState implements CommandRegistrar {
|
|
|
1505
1518
|
* read-only cube, or when the query can't take it.
|
|
1506
1519
|
*/
|
|
1507
1520
|
addConfiguredNode(node: QueryNode, afterId?: string): void {
|
|
1508
|
-
|
|
1509
|
-
|
|
1521
|
+
this.addConfiguredNodes([node], afterId);
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
/**
|
|
1525
|
+
* Adds nodes made elsewhere one after another, the first spliced in after
|
|
1526
|
+
* `afterId`, as one undo step, the last then running when that one did
|
|
1527
|
+
* (Explore JSON's Extend then Explode Array). Does nothing in a read-only
|
|
1528
|
+
* cube, or when the query can't take one of them.
|
|
1529
|
+
*/
|
|
1530
|
+
addConfiguredNodes(nodes: readonly QueryNode[], afterId?: string): void {
|
|
1531
|
+
if (this.readOnly || !nodes.length) {
|
|
1510
1532
|
return;
|
|
1511
1533
|
}
|
|
1512
|
-
this.
|
|
1534
|
+
let query = this.document.query;
|
|
1535
|
+
let after = afterId;
|
|
1536
|
+
for (const node of nodes) {
|
|
1537
|
+
if (!query.canAdd(node, after)) {
|
|
1538
|
+
return;
|
|
1539
|
+
}
|
|
1540
|
+
query = query.add(node, after);
|
|
1541
|
+
after = node.id;
|
|
1542
|
+
}
|
|
1543
|
+
this.applyQuery(query);
|
|
1513
1544
|
}
|
|
1514
1545
|
|
|
1515
1546
|
/** Whether dropping a node on another does anything: connect it, or else move it after it */
|