@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
|
@@ -0,0 +1,316 @@
|
|
|
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 { CaretDownIcon, CaretRightIcon, clsx } from '@finos/legend-art';
|
|
18
|
+
import { type SchemaColumn, TypeFamily } from '@finos/legend-cube';
|
|
19
|
+
import { flowResult } from 'mobx';
|
|
20
|
+
import { observer } from 'mobx-react-lite';
|
|
21
|
+
import {
|
|
22
|
+
CUBE_JSON_FIELDS_TITLE,
|
|
23
|
+
getCubeJsonFieldsStatus,
|
|
24
|
+
} from '../../__lib__/LegendCubeLabels.js';
|
|
25
|
+
import type { CubeJsonFieldsState } from '../../stores/variant/CubeJsonFieldsState.js';
|
|
26
|
+
import {
|
|
27
|
+
type CubeJsonField,
|
|
28
|
+
getCubeJsonFieldDefault,
|
|
29
|
+
getCubeJsonOfferExpression,
|
|
30
|
+
} from '../../stores/variant/CubeJsonOffers.js';
|
|
31
|
+
import { CubeButton } from '../CubeButton.js';
|
|
32
|
+
|
|
33
|
+
/** Whether a column holds JSON, whose fields an expression editor can open */
|
|
34
|
+
export const isCubeJsonColumn = (column: SchemaColumn): boolean =>
|
|
35
|
+
column.type.family === TypeFamily.VARIANT;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The caret beside a Variant column's chip, which opens or closes its fields;
|
|
39
|
+
* greyed, with the reason, on a database without JSON functions
|
|
40
|
+
*/
|
|
41
|
+
export const CubeJsonFieldsToggle = observer(
|
|
42
|
+
(props: {
|
|
43
|
+
fields: CubeJsonFieldsState;
|
|
44
|
+
column: SchemaColumn;
|
|
45
|
+
disabled: boolean;
|
|
46
|
+
}) => {
|
|
47
|
+
const { fields, column, disabled } = props;
|
|
48
|
+
const open = fields.isOpen(column.name);
|
|
49
|
+
const problem = fields.databaseProblem;
|
|
50
|
+
return (
|
|
51
|
+
<button
|
|
52
|
+
type="button"
|
|
53
|
+
className={clsx(
|
|
54
|
+
'flex h-full items-center rounded-sm border px-0.5 disabled:text-[var(--color-text-disabled)]',
|
|
55
|
+
open
|
|
56
|
+
? 'border-[var(--color-accent)] text-[var(--color-accent)]'
|
|
57
|
+
: 'border-[var(--color-border-subtle)] hover:bg-[var(--color-bg-hover)]',
|
|
58
|
+
)}
|
|
59
|
+
aria-label={`Fields of ${column.name}`}
|
|
60
|
+
aria-expanded={open}
|
|
61
|
+
title={problem ?? (open ? undefined : CUBE_JSON_FIELDS_TITLE.OPEN)}
|
|
62
|
+
disabled={disabled || problem !== undefined}
|
|
63
|
+
onClick={() => {
|
|
64
|
+
// a failed read shows its message under the chips
|
|
65
|
+
flowResult(fields.toggle(column)).catch(() => undefined);
|
|
66
|
+
}}
|
|
67
|
+
>
|
|
68
|
+
<span className="font-mono text-xs">{'{}'}</span>
|
|
69
|
+
{open ? <CaretDownIcon /> : <CaretRightIcon />}
|
|
70
|
+
</button>
|
|
71
|
+
);
|
|
72
|
+
},
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
/** A field of the tree: its key, kind and share, what a click writes, its other forms, then its fields */
|
|
76
|
+
const CubeJsonFieldsRow = observer(
|
|
77
|
+
(props: {
|
|
78
|
+
fields: CubeJsonFieldsState;
|
|
79
|
+
column: string;
|
|
80
|
+
field: CubeJsonField;
|
|
81
|
+
depth: number;
|
|
82
|
+
disabled: boolean;
|
|
83
|
+
onInsert: (expression: string) => void;
|
|
84
|
+
onAddColumn: ((name: string, code: string) => void) | undefined;
|
|
85
|
+
}) => {
|
|
86
|
+
const { fields, column, field, depth, disabled, onInsert, onAddColumn } =
|
|
87
|
+
props;
|
|
88
|
+
const label = [column, ...field.path].join('.');
|
|
89
|
+
const share = Math.round(field.presence * 100);
|
|
90
|
+
const folded = fields.isFolded(field.path);
|
|
91
|
+
const byDefault = getCubeJsonFieldDefault(column, field);
|
|
92
|
+
const defaultKey = field.offers.find(
|
|
93
|
+
({ key }) => key === 'value' || key === 'json',
|
|
94
|
+
)?.key;
|
|
95
|
+
const forms = field.offers.filter((offer) => offer.key !== defaultKey);
|
|
96
|
+
const formsShown = fields.areFormsShown(field.path);
|
|
97
|
+
return (
|
|
98
|
+
<li>
|
|
99
|
+
<div
|
|
100
|
+
className="flex items-center gap-1 py-0.5 pr-1"
|
|
101
|
+
style={{ paddingLeft: `${4 + depth * 14}px` }}
|
|
102
|
+
>
|
|
103
|
+
{field.children.length ? (
|
|
104
|
+
<button
|
|
105
|
+
type="button"
|
|
106
|
+
className="flex w-4 shrink-0 items-center justify-center text-[var(--color-text-secondary)]"
|
|
107
|
+
aria-label={`${folded ? 'Unfold' : 'Fold'} ${label}`}
|
|
108
|
+
onClick={() => fields.toggleFolded(field.path)}
|
|
109
|
+
>
|
|
110
|
+
{folded ? <CaretRightIcon /> : <CaretDownIcon />}
|
|
111
|
+
</button>
|
|
112
|
+
) : (
|
|
113
|
+
<span className="w-4 shrink-0" />
|
|
114
|
+
)}
|
|
115
|
+
<button
|
|
116
|
+
type="button"
|
|
117
|
+
className="flex min-w-0 flex-1 items-baseline gap-2 rounded-sm px-1 text-left enabled:hover:bg-[var(--color-bg-hover)] disabled:text-[var(--color-text-disabled)]"
|
|
118
|
+
aria-label={`Write ${label}`}
|
|
119
|
+
title={`Write ${byDefault.expression}`}
|
|
120
|
+
disabled={disabled}
|
|
121
|
+
onClick={() => onInsert(byDefault.expression)}
|
|
122
|
+
>
|
|
123
|
+
<span className="break-all font-mono text-sm">
|
|
124
|
+
{field.path.at(-1) ?? column}
|
|
125
|
+
</span>
|
|
126
|
+
<span className="shrink-0 text-xs text-[var(--color-text-secondary)]">
|
|
127
|
+
{field.description}
|
|
128
|
+
</span>
|
|
129
|
+
{share < 100 && (
|
|
130
|
+
<span className="ml-auto shrink-0 text-xs text-[var(--color-text-muted)]">
|
|
131
|
+
{share}%
|
|
132
|
+
</span>
|
|
133
|
+
)}
|
|
134
|
+
</button>
|
|
135
|
+
{onAddColumn && byDefault.column && (
|
|
136
|
+
<button
|
|
137
|
+
type="button"
|
|
138
|
+
className="shrink-0 rounded-sm border border-[var(--color-border-subtle)] px-1 text-sm enabled:hover:bg-[var(--color-bg-hover)] disabled:text-[var(--color-text-disabled)]"
|
|
139
|
+
aria-label={`Add ${label} as a column`}
|
|
140
|
+
title={`A new column ${byDefault.column.name}: ${byDefault.column.code}`}
|
|
141
|
+
disabled={disabled}
|
|
142
|
+
onClick={() => {
|
|
143
|
+
if (byDefault.column) {
|
|
144
|
+
onAddColumn(byDefault.column.name, byDefault.column.code);
|
|
145
|
+
}
|
|
146
|
+
}}
|
|
147
|
+
>
|
|
148
|
+
+ column
|
|
149
|
+
</button>
|
|
150
|
+
)}
|
|
151
|
+
{forms.length > 0 && (
|
|
152
|
+
<button
|
|
153
|
+
type="button"
|
|
154
|
+
className={clsx(
|
|
155
|
+
'shrink-0 rounded-sm border px-1 text-sm enabled:hover:bg-[var(--color-bg-hover)]',
|
|
156
|
+
formsShown
|
|
157
|
+
? 'border-[var(--color-accent)]'
|
|
158
|
+
: 'border-[var(--color-border-subtle)]',
|
|
159
|
+
)}
|
|
160
|
+
aria-label={`Other forms of ${label}`}
|
|
161
|
+
aria-expanded={formsShown}
|
|
162
|
+
title={CUBE_JSON_FIELDS_TITLE.FORMS}
|
|
163
|
+
onClick={() => fields.toggleForms(field.path)}
|
|
164
|
+
>
|
|
165
|
+
···
|
|
166
|
+
</button>
|
|
167
|
+
)}
|
|
168
|
+
</div>
|
|
169
|
+
{formsShown && (
|
|
170
|
+
<ul
|
|
171
|
+
aria-label={`Forms of ${label}`}
|
|
172
|
+
className="my-0.5 rounded-sm border border-[var(--color-border-subtle)]"
|
|
173
|
+
style={{ marginLeft: `${22 + depth * 14}px` }}
|
|
174
|
+
>
|
|
175
|
+
{forms.map((offer) => {
|
|
176
|
+
const expression = getCubeJsonOfferExpression(offer);
|
|
177
|
+
return (
|
|
178
|
+
<li key={offer.key}>
|
|
179
|
+
<button
|
|
180
|
+
type="button"
|
|
181
|
+
className="flex w-full items-baseline gap-2 px-2 py-0.5 text-left text-sm enabled:hover:bg-[var(--color-bg-hover)] disabled:text-[var(--color-text-disabled)]"
|
|
182
|
+
title={
|
|
183
|
+
expression === undefined
|
|
184
|
+
? CUBE_JSON_FIELDS_TITLE.EXPLODE
|
|
185
|
+
: `Write ${expression}`
|
|
186
|
+
}
|
|
187
|
+
disabled={disabled || expression === undefined}
|
|
188
|
+
onClick={() => {
|
|
189
|
+
if (expression !== undefined) {
|
|
190
|
+
onInsert(expression);
|
|
191
|
+
}
|
|
192
|
+
}}
|
|
193
|
+
>
|
|
194
|
+
{offer.label}
|
|
195
|
+
<span className="text-xs text-[var(--color-text-muted)]">
|
|
196
|
+
{offer.explode ? 'Explode Array' : offer.type}
|
|
197
|
+
</span>
|
|
198
|
+
</button>
|
|
199
|
+
</li>
|
|
200
|
+
);
|
|
201
|
+
})}
|
|
202
|
+
</ul>
|
|
203
|
+
)}
|
|
204
|
+
{!folded && field.children.length > 0 && (
|
|
205
|
+
<ul>
|
|
206
|
+
{field.children.map((child) => (
|
|
207
|
+
<CubeJsonFieldsRow
|
|
208
|
+
key={JSON.stringify(child.path)}
|
|
209
|
+
{...props}
|
|
210
|
+
field={child}
|
|
211
|
+
depth={depth + 1}
|
|
212
|
+
/>
|
|
213
|
+
))}
|
|
214
|
+
</ul>
|
|
215
|
+
)}
|
|
216
|
+
</li>
|
|
217
|
+
);
|
|
218
|
+
},
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* A Variant input column's fields in an expression editor (PLAN §11.20, V.9),
|
|
223
|
+
* under the input columns while its caret is open: what was read, a search,
|
|
224
|
+
* and the field tree. A field clicked writes its expression where the
|
|
225
|
+
* editor's chips write; "+ column", where the editor has rows, adds one; its
|
|
226
|
+
* other forms write theirs, one row per element being a step of its own.
|
|
227
|
+
*/
|
|
228
|
+
export const CubeJsonFieldsTree = observer(
|
|
229
|
+
(props: {
|
|
230
|
+
fields: CubeJsonFieldsState;
|
|
231
|
+
disabled: boolean;
|
|
232
|
+
onInsert: (expression: string) => void;
|
|
233
|
+
onAddColumn?: ((name: string, code: string) => void) | undefined;
|
|
234
|
+
}) => {
|
|
235
|
+
const { fields, disabled, onInsert, onAddColumn } = props;
|
|
236
|
+
const { column } = fields;
|
|
237
|
+
if (!column) {
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
const sample =
|
|
241
|
+
fields.sample?.column === column.name ? fields.sample.sample : undefined;
|
|
242
|
+
const shown = fields.shownFields;
|
|
243
|
+
return (
|
|
244
|
+
<div
|
|
245
|
+
aria-label={`Fields of ${column.name}, read`}
|
|
246
|
+
className="flex flex-col gap-1 rounded-sm border border-[var(--color-accent)] p-1"
|
|
247
|
+
>
|
|
248
|
+
<div className="flex items-center gap-2 text-sm text-[var(--color-text-secondary)]">
|
|
249
|
+
<span role="status" className="min-w-0 flex-1">
|
|
250
|
+
{fields.isReading
|
|
251
|
+
? `Reading ${column.name}…`
|
|
252
|
+
: sample
|
|
253
|
+
? getCubeJsonFieldsStatus(sample)
|
|
254
|
+
: ''}
|
|
255
|
+
</span>
|
|
256
|
+
{fields.isReading ? (
|
|
257
|
+
<CubeButton onClick={() => fields.stop()}>Stop</CubeButton>
|
|
258
|
+
) : (
|
|
259
|
+
sample && (
|
|
260
|
+
<CubeButton
|
|
261
|
+
title={
|
|
262
|
+
sample.complete
|
|
263
|
+
? CUBE_JSON_FIELDS_TITLE.READ_AGAIN
|
|
264
|
+
: CUBE_JSON_FIELDS_TITLE.EVERY_ROW
|
|
265
|
+
}
|
|
266
|
+
onClick={() => {
|
|
267
|
+
flowResult(fields.read(!sample.complete)).catch(
|
|
268
|
+
() => undefined,
|
|
269
|
+
);
|
|
270
|
+
}}
|
|
271
|
+
>
|
|
272
|
+
{sample.complete ? 'Read again' : 'Read every row'}
|
|
273
|
+
</CubeButton>
|
|
274
|
+
)
|
|
275
|
+
)}
|
|
276
|
+
</div>
|
|
277
|
+
{fields.error && (
|
|
278
|
+
<div
|
|
279
|
+
role="alert"
|
|
280
|
+
className="break-words text-sm text-[var(--color-status-error)]"
|
|
281
|
+
>
|
|
282
|
+
{fields.error}
|
|
283
|
+
</div>
|
|
284
|
+
)}
|
|
285
|
+
{fields.fields && (
|
|
286
|
+
<>
|
|
287
|
+
<input
|
|
288
|
+
className="h-6 rounded-sm border border-[var(--color-border-default)] bg-[var(--color-bg-input)] px-1 text-sm"
|
|
289
|
+
placeholder="Find a field"
|
|
290
|
+
aria-label="Find a field"
|
|
291
|
+
value={fields.search}
|
|
292
|
+
onChange={(event) => fields.setSearch(event.target.value)}
|
|
293
|
+
/>
|
|
294
|
+
{shown ? (
|
|
295
|
+
<ul aria-label={`Fields of ${column.name}`}>
|
|
296
|
+
<CubeJsonFieldsRow
|
|
297
|
+
fields={fields}
|
|
298
|
+
column={column.name}
|
|
299
|
+
field={shown}
|
|
300
|
+
depth={0}
|
|
301
|
+
disabled={disabled}
|
|
302
|
+
onInsert={onInsert}
|
|
303
|
+
onAddColumn={onAddColumn}
|
|
304
|
+
/>
|
|
305
|
+
</ul>
|
|
306
|
+
) : (
|
|
307
|
+
<div className="px-1 text-sm text-[var(--color-text-muted)]">
|
|
308
|
+
No field matches.
|
|
309
|
+
</div>
|
|
310
|
+
)}
|
|
311
|
+
</>
|
|
312
|
+
)}
|
|
313
|
+
</div>
|
|
314
|
+
);
|
|
315
|
+
},
|
|
316
|
+
);
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
Drop,
|
|
24
24
|
ExpressionFilter,
|
|
25
25
|
Pivot,
|
|
26
|
+
Explode,
|
|
26
27
|
Extend,
|
|
27
28
|
Filter,
|
|
28
29
|
FunctionSource,
|
|
@@ -42,6 +43,7 @@ import type { CubeEditorState } from '../../stores/CubeEditorState.js';
|
|
|
42
43
|
import type { CubeNodeDraft } from '../../stores/editors/CubeNodeDraft.js';
|
|
43
44
|
import { CubeConcatEditor } from './CubeConcatEditor.js';
|
|
44
45
|
import { CubeDataProductSourceEditor } from './CubeDataProductSourceEditor.js';
|
|
46
|
+
import { CubeExplodeEditor } from './CubeExplodeEditor.js';
|
|
45
47
|
import { CubeExpressionFilterEditor } from './CubeExpressionFilterEditor.js';
|
|
46
48
|
import { CubePivotEditor } from './CubePivotEditor.js';
|
|
47
49
|
import { CubeDifferenceEditor } from './CubeDifferenceEditor.js';
|
|
@@ -100,6 +102,7 @@ export const CUBE_NODE_EDITORS: ReadonlyMap<
|
|
|
100
102
|
[Filter.TYPE, CubeFilterEditor],
|
|
101
103
|
[ExpressionFilter.TYPE, CubeExpressionFilterEditor],
|
|
102
104
|
[Pivot.TYPE, CubePivotEditor],
|
|
105
|
+
[Explode.TYPE, CubeExplodeEditor],
|
|
103
106
|
[Restrict.TYPE, CubeRestrictEditor],
|
|
104
107
|
[Rename.TYPE, CubeRenameEditor],
|
|
105
108
|
[Distinct.TYPE, CubeDistinctEditor],
|
|
@@ -28,6 +28,10 @@ import {
|
|
|
28
28
|
ROW_LIMIT_WARNING_THRESHOLD,
|
|
29
29
|
} from '../../__lib__/LegendCubeLabels.js';
|
|
30
30
|
import { LEGEND_CUBE_TEST_ID } from '../../__lib__/LegendCubeTesting.js';
|
|
31
|
+
import {
|
|
32
|
+
CUBE_TOUR_ANCHOR,
|
|
33
|
+
cubeTourAnchor,
|
|
34
|
+
} from '../../__lib__/LegendCubeTour.js';
|
|
31
35
|
import {
|
|
32
36
|
CubeDataProductRunErrorKind,
|
|
33
37
|
type CubeDataProductRuntimeState,
|
|
@@ -63,7 +67,10 @@ const CubeRowLimitInput = observer(
|
|
|
63
67
|
}
|
|
64
68
|
};
|
|
65
69
|
return (
|
|
66
|
-
<div
|
|
70
|
+
<div
|
|
71
|
+
className="flex items-center gap-1"
|
|
72
|
+
{...cubeTourAnchor(CUBE_TOUR_ANCHOR.EXECUTE)}
|
|
73
|
+
>
|
|
67
74
|
<label className="flex items-center gap-1 text-base">
|
|
68
75
|
Rows
|
|
69
76
|
<input
|
|
@@ -123,6 +130,7 @@ const CubeGridModeToggle = observer(
|
|
|
123
130
|
className="flex overflow-hidden rounded-sm border border-[var(--color-border-default)]"
|
|
124
131
|
role="group"
|
|
125
132
|
aria-label="Grid mode"
|
|
133
|
+
{...cubeTourAnchor(CUBE_TOUR_ANCHOR.GRID_MODE)}
|
|
126
134
|
>
|
|
127
135
|
{option(
|
|
128
136
|
CubeGridMode.SERVER_SIDE,
|
|
@@ -156,12 +164,17 @@ const CubeGridToolbar = observer(
|
|
|
156
164
|
data-testid={LEGEND_CUBE_TEST_ID.GRID_TOOLBAR}
|
|
157
165
|
>
|
|
158
166
|
{execution.isRunning ? (
|
|
159
|
-
<CubeButton
|
|
167
|
+
<CubeButton
|
|
168
|
+
title="Stop the run"
|
|
169
|
+
{...cubeTourAnchor(CUBE_TOUR_ANCHOR.EXECUTE)}
|
|
170
|
+
onClick={() => execution.stop()}
|
|
171
|
+
>
|
|
160
172
|
Stop
|
|
161
173
|
</CubeButton>
|
|
162
174
|
) : (
|
|
163
175
|
<CubeButton
|
|
164
176
|
primary={true}
|
|
177
|
+
{...cubeTourAnchor(CUBE_TOUR_ANCHOR.EXECUTE)}
|
|
165
178
|
disabled={!execution.canExecute}
|
|
166
179
|
title={
|
|
167
180
|
disabledReasons.length
|
|
@@ -224,7 +237,11 @@ const CubeGridToolbar = observer(
|
|
|
224
237
|
</span>
|
|
225
238
|
)}
|
|
226
239
|
{result.sql.length > 0 && (
|
|
227
|
-
<CubeButton
|
|
240
|
+
<CubeButton
|
|
241
|
+
aria-haspopup="dialog"
|
|
242
|
+
{...cubeTourAnchor(CUBE_TOUR_ANCHOR.SHOW_SQL)}
|
|
243
|
+
onClick={onShowSql}
|
|
244
|
+
>
|
|
228
245
|
Show SQL
|
|
229
246
|
</CubeButton>
|
|
230
247
|
)}
|
|
@@ -233,6 +250,7 @@ const CubeGridToolbar = observer(
|
|
|
233
250
|
<span className="flex-1" />
|
|
234
251
|
<CubeButton
|
|
235
252
|
aria-expanded={showRuns}
|
|
253
|
+
{...cubeTourAnchor(CUBE_TOUR_ANCHOR.RUNS)}
|
|
236
254
|
title="The latest runs, fetches and exports, with their SQL"
|
|
237
255
|
onClick={onToggleRuns}
|
|
238
256
|
>
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
TypeFamily,
|
|
24
24
|
} from '@finos/legend-cube';
|
|
25
25
|
import type { CubeResultValue } from '../../graph-manager/CubeEngine.js';
|
|
26
|
+
import { formatCubeJsonLine } from '../../stores/variant/CubeJson.js';
|
|
26
27
|
|
|
27
28
|
// How the grid shows and sorts values (PLAN §9, Settled before M1.8).
|
|
28
29
|
// Integer and Decimal values arrive as their exact text and are never turned
|
|
@@ -153,9 +154,11 @@ export const groupThousands = (text: string): string => {
|
|
|
153
154
|
* How the grid shows a value of a column of the family (PLAN §9, user,
|
|
154
155
|
* 2026-10-10): Decimal and Number values keep every digit,
|
|
155
156
|
* their thousands grouped; Float values are rounded to
|
|
156
|
-
* `CUBE_GRID_FLOAT_DECIMAL_PLACES` places, grouped too;
|
|
157
|
-
*
|
|
158
|
-
*
|
|
157
|
+
* `CUBE_GRID_FLOAT_DECIMAL_PLACES` places, grouped too; a Variant's JSON on
|
|
158
|
+
* one line, `{name: ann, tier: gold}` (`formatCubeJsonLine`, PLAN §11.20);
|
|
159
|
+
* Integer values, and everything else, show as they are, so ids and years get
|
|
160
|
+
* no commas. The value itself is unchanged, so copying a cell copies its
|
|
161
|
+
* exact value.
|
|
159
162
|
*/
|
|
160
163
|
export const formatCellValue = (
|
|
161
164
|
value: Exclude<CubeResultValue, null>,
|
|
@@ -181,6 +184,8 @@ export const formatCellValue = (
|
|
|
181
184
|
Number(rounded) === 0 ? rounded.replace(/^-/u, '') : rounded,
|
|
182
185
|
);
|
|
183
186
|
}
|
|
187
|
+
case TypeFamily.VARIANT:
|
|
188
|
+
return formatCubeJsonLine(text);
|
|
184
189
|
default:
|
|
185
190
|
return text;
|
|
186
191
|
}
|