@finos/legend-cube-builder 0.0.2 → 0.0.3
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/README.md +60 -1
- package/docs/adding-an-operation.md +92 -0
- package/docs/hosting.md +79 -0
- package/docs/testing.md +83 -0
- package/lib/__lib__/LegendCubeHelpText.d.ts.map +1 -1
- package/lib/__lib__/LegendCubeHelpText.js +11 -3
- package/lib/__lib__/LegendCubeHelpText.js.map +1 -1
- package/lib/__lib__/LegendCubeLabels.d.ts +19 -0
- package/lib/__lib__/LegendCubeLabels.d.ts.map +1 -1
- package/lib/__lib__/LegendCubeLabels.js +19 -0
- package/lib/__lib__/LegendCubeLabels.js.map +1 -1
- package/lib/__test-utils__/CubeEngineTestSupport.d.ts +6 -0
- package/lib/__test-utils__/CubeEngineTestSupport.d.ts.map +1 -1
- package/lib/__test-utils__/CubeEngineTestSupport.js +30 -0
- package/lib/__test-utils__/CubeEngineTestSupport.js.map +1 -1
- package/lib/__test-utils__/CubeOperationsTestUtils.d.ts +42 -0
- package/lib/__test-utils__/CubeOperationsTestUtils.d.ts.map +1 -0
- package/lib/__test-utils__/CubeOperationsTestUtils.js +92 -0
- package/lib/__test-utils__/CubeOperationsTestUtils.js.map +1 -0
- package/lib/__test-utils__/FakeCubeEngine.d.ts.map +1 -1
- package/lib/__test-utils__/FakeCubeEngine.js +7 -1
- package/lib/__test-utils__/FakeCubeEngine.js.map +1 -1
- package/lib/components/CubeNodeIcon.d.ts.map +1 -1
- package/lib/components/CubeNodeIcon.js +12 -2
- package/lib/components/CubeNodeIcon.js.map +1 -1
- package/lib/components/canvas/CubeCanvas.d.ts.map +1 -1
- package/lib/components/canvas/CubeCanvas.js +5 -3
- package/lib/components/canvas/CubeCanvas.js.map +1 -1
- package/lib/components/canvas/CubeCanvasElements.d.ts +2 -0
- package/lib/components/canvas/CubeCanvasElements.d.ts.map +1 -1
- package/lib/components/canvas/CubeCanvasElements.js +2 -1
- package/lib/components/canvas/CubeCanvasElements.js.map +1 -1
- package/lib/components/canvas/CubeCanvasNode.d.ts.map +1 -1
- package/lib/components/canvas/CubeCanvasNode.js +5 -2
- package/lib/components/canvas/CubeCanvasNode.js.map +1 -1
- package/lib/components/editors/CubeColumnPicker.d.ts +9 -2
- package/lib/components/editors/CubeColumnPicker.d.ts.map +1 -1
- package/lib/components/editors/CubeColumnPicker.js +9 -4
- package/lib/components/editors/CubeColumnPicker.js.map +1 -1
- package/lib/components/editors/CubeDistinctEditor.d.ts +23 -0
- package/lib/components/editors/CubeDistinctEditor.d.ts.map +1 -0
- package/lib/components/editors/CubeDistinctEditor.js +24 -0
- package/lib/components/editors/CubeDistinctEditor.js.map +1 -0
- package/lib/components/editors/CubeIntegerField.d.ts +29 -0
- package/lib/components/editors/CubeIntegerField.d.ts.map +1 -0
- package/lib/components/editors/CubeIntegerField.js +30 -0
- package/lib/components/editors/CubeIntegerField.js.map +1 -0
- package/lib/components/editors/CubeJoinEditor.d.ts.map +1 -1
- package/lib/components/editors/CubeJoinEditor.js +11 -3
- package/lib/components/editors/CubeJoinEditor.js.map +1 -1
- package/lib/components/editors/CubeNodeEditorPanel.d.ts.map +1 -1
- package/lib/components/editors/CubeNodeEditorPanel.js +3 -2
- package/lib/components/editors/CubeNodeEditorPanel.js.map +1 -1
- package/lib/components/editors/CubeNodeEditorRegistry.d.ts.map +1 -1
- package/lib/components/editors/CubeNodeEditorRegistry.js +14 -1
- package/lib/components/editors/CubeNodeEditorRegistry.js.map +1 -1
- package/lib/components/editors/CubeRenameEditor.d.ts +26 -0
- package/lib/components/editors/CubeRenameEditor.d.ts.map +1 -0
- package/lib/components/editors/CubeRenameEditor.js +58 -0
- package/lib/components/editors/CubeRenameEditor.js.map +1 -0
- package/lib/components/editors/CubeRestrictEditor.d.ts +26 -0
- package/lib/components/editors/CubeRestrictEditor.d.ts.map +1 -0
- package/lib/components/editors/CubeRestrictEditor.js +23 -0
- package/lib/components/editors/CubeRestrictEditor.js.map +1 -0
- package/lib/components/editors/CubeRowCountEditor.d.ts +25 -0
- package/lib/components/editors/CubeRowCountEditor.d.ts.map +1 -0
- package/lib/components/editors/CubeRowCountEditor.js +38 -0
- package/lib/components/editors/CubeRowCountEditor.js.map +1 -0
- package/lib/components/editors/CubeSliceEditor.d.ts +25 -0
- package/lib/components/editors/CubeSliceEditor.d.ts.map +1 -0
- package/lib/components/editors/CubeSliceEditor.js +40 -0
- package/lib/components/editors/CubeSliceEditor.js.map +1 -0
- package/lib/components/editors/CubeSortEditor.d.ts +27 -0
- package/lib/components/editors/CubeSortEditor.d.ts.map +1 -0
- package/lib/components/editors/CubeSortEditor.js +72 -0
- package/lib/components/editors/CubeSortEditor.js.map +1 -0
- package/lib/components/editors/CubeSourceEditor.d.ts.map +1 -1
- package/lib/components/editors/CubeSourceEditor.js +1 -2
- package/lib/components/editors/CubeSourceEditor.js.map +1 -1
- package/lib/components/editors/CubeValueEditor.d.ts.map +1 -1
- package/lib/components/editors/CubeValueEditor.js +2 -1
- package/lib/components/editors/CubeValueEditor.js.map +1 -1
- package/lib/components/grid/CubeGridRegion.d.ts.map +1 -1
- package/lib/components/grid/CubeGridRegion.js +1 -1
- package/lib/components/grid/CubeGridRegion.js.map +1 -1
- package/lib/components/grid/CubeResultGrid.d.ts +14 -1
- package/lib/components/grid/CubeResultGrid.d.ts.map +1 -1
- package/lib/components/grid/CubeResultGrid.js +41 -5
- package/lib/components/grid/CubeResultGrid.js.map +1 -1
- package/lib/graph-manager/CubeEngine.d.ts +13 -0
- package/lib/graph-manager/CubeEngine.d.ts.map +1 -1
- package/lib/graph-manager/CubeEngine.js.map +1 -1
- package/lib/graph-manager/CubeModelOutlineHelper.d.ts +8 -0
- package/lib/graph-manager/CubeModelOutlineHelper.d.ts.map +1 -1
- package/lib/graph-manager/CubeModelOutlineHelper.js +21 -0
- package/lib/graph-manager/CubeModelOutlineHelper.js.map +1 -1
- package/lib/graph-manager/protocol/pure/v1/V1_CubeModelOutlineBuilder.d.ts.map +1 -1
- package/lib/graph-manager/protocol/pure/v1/V1_CubeModelOutlineBuilder.js +56 -3
- package/lib/graph-manager/protocol/pure/v1/V1_CubeModelOutlineBuilder.js.map +1 -1
- package/lib/graph-manager/protocol/pure/v1/__test-utils__/V1_CubeEngineTestUtils.d.ts.map +1 -1
- package/lib/graph-manager/protocol/pure/v1/__test-utils__/V1_CubeEngineTestUtils.js +3 -3
- package/lib/graph-manager/protocol/pure/v1/__test-utils__/V1_CubeEngineTestUtils.js.map +1 -1
- package/lib/index.css +1 -1
- package/lib/stores/CubeEditorState.d.ts +34 -4
- package/lib/stores/CubeEditorState.d.ts.map +1 -1
- package/lib/stores/CubeEditorState.js +87 -6
- package/lib/stores/CubeEditorState.js.map +1 -1
- package/lib/stores/CubeExecutionState.d.ts.map +1 -1
- package/lib/stores/CubeExecutionState.js +19 -3
- package/lib/stores/CubeExecutionState.js.map +1 -1
- package/lib/stores/CubeGridQuickActions.d.ts +40 -0
- package/lib/stores/CubeGridQuickActions.d.ts.map +1 -0
- package/lib/stores/CubeGridQuickActions.js +86 -0
- package/lib/stores/CubeGridQuickActions.js.map +1 -0
- package/lib/stores/CubeNodeEditorState.d.ts +19 -1
- package/lib/stores/CubeNodeEditorState.d.ts.map +1 -1
- package/lib/stores/CubeNodeEditorState.js +55 -0
- package/lib/stores/CubeNodeEditorState.js.map +1 -1
- package/lib/stores/CubeShowPureState.d.ts +4 -1
- package/lib/stores/CubeShowPureState.d.ts.map +1 -1
- package/lib/stores/CubeShowPureState.js +22 -5
- package/lib/stores/CubeShowPureState.js.map +1 -1
- package/lib/stores/editors/CubeIntegerText.d.ts +24 -0
- package/lib/stores/editors/CubeIntegerText.d.ts.map +1 -0
- package/lib/stores/editors/CubeIntegerText.js +33 -0
- package/lib/stores/editors/CubeIntegerText.js.map +1 -0
- package/lib/stores/editors/CubeJoinDraft.d.ts +13 -5
- package/lib/stores/editors/CubeJoinDraft.d.ts.map +1 -1
- package/lib/stores/editors/CubeJoinDraft.js +21 -12
- package/lib/stores/editors/CubeJoinDraft.js.map +1 -1
- package/lib/stores/editors/CubeNodeDraftRegistry.d.ts +6 -0
- package/lib/stores/editors/CubeNodeDraftRegistry.d.ts.map +1 -1
- package/lib/stores/editors/CubeNodeDraftRegistry.js +23 -1
- package/lib/stores/editors/CubeNodeDraftRegistry.js.map +1 -1
- package/lib/stores/editors/CubeRenameDraft.d.ts +44 -0
- package/lib/stores/editors/CubeRenameDraft.d.ts.map +1 -0
- package/lib/stores/editors/CubeRenameDraft.js +79 -0
- package/lib/stores/editors/CubeRenameDraft.js.map +1 -0
- package/lib/stores/editors/CubeRestrictDraft.d.ts +37 -0
- package/lib/stores/editors/CubeRestrictDraft.d.ts.map +1 -0
- package/lib/stores/editors/CubeRestrictDraft.js +73 -0
- package/lib/stores/editors/CubeRestrictDraft.js.map +1 -0
- package/lib/stores/editors/CubeRowCountDraft.d.ts +42 -0
- package/lib/stores/editors/CubeRowCountDraft.d.ts.map +1 -0
- package/lib/stores/editors/CubeRowCountDraft.js +55 -0
- package/lib/stores/editors/CubeRowCountDraft.js.map +1 -0
- package/lib/stores/editors/CubeSliceDraft.d.ts +42 -0
- package/lib/stores/editors/CubeSliceDraft.d.ts.map +1 -0
- package/lib/stores/editors/CubeSliceDraft.js +72 -0
- package/lib/stores/editors/CubeSliceDraft.js.map +1 -0
- package/lib/stores/editors/CubeSortDraft.d.ts +49 -0
- package/lib/stores/editors/CubeSortDraft.d.ts.map +1 -0
- package/lib/stores/editors/CubeSortDraft.js +99 -0
- package/lib/stores/editors/CubeSortDraft.js.map +1 -0
- package/package.json +8 -8
- package/src/__lib__/LegendCubeHelpText.ts +22 -2
- package/src/__lib__/LegendCubeLabels.ts +30 -0
- package/src/__test-utils__/CubeEngineTestSupport.ts +35 -0
- package/src/__test-utils__/CubeOperationsTestUtils.ts +177 -0
- package/src/__test-utils__/FakeCubeEngine.ts +7 -1
- package/src/components/CubeNodeIcon.tsx +18 -1
- package/src/components/canvas/CubeCanvas.tsx +5 -3
- package/src/components/canvas/CubeCanvasElements.ts +4 -1
- package/src/components/canvas/CubeCanvasNode.tsx +9 -1
- package/src/components/editors/CubeColumnPicker.tsx +36 -12
- package/src/components/editors/CubeDistinctEditor.tsx +28 -0
- package/src/components/editors/CubeIntegerField.tsx +51 -0
- package/src/components/editors/CubeJoinEditor.tsx +26 -3
- package/src/components/editors/CubeNodeEditorPanel.tsx +12 -1
- package/src/components/editors/CubeNodeEditorRegistry.ts +20 -0
- package/src/components/editors/CubeRenameEditor.tsx +143 -0
- package/src/components/editors/CubeRestrictEditor.tsx +103 -0
- package/src/components/editors/CubeRowCountEditor.tsx +53 -0
- package/src/components/editors/CubeSliceEditor.tsx +69 -0
- package/src/components/editors/CubeSortEditor.tsx +206 -0
- package/src/components/editors/CubeSourceEditor.tsx +0 -10
- package/src/components/editors/CubeValueEditor.tsx +2 -1
- package/src/components/grid/CubeGridRegion.tsx +1 -0
- package/src/components/grid/CubeResultGrid.tsx +59 -5
- package/src/graph-manager/CubeEngine.ts +14 -0
- package/src/graph-manager/CubeModelOutlineHelper.ts +26 -0
- package/src/graph-manager/protocol/pure/v1/V1_CubeModelOutlineBuilder.ts +89 -3
- package/src/graph-manager/protocol/pure/v1/__test-utils__/V1_CubeEngineTestUtils.ts +6 -3
- package/src/stores/CubeEditorState.ts +110 -5
- package/src/stores/CubeExecutionState.ts +36 -3
- package/src/stores/CubeGridQuickActions.ts +129 -0
- package/src/stores/CubeNodeEditorState.ts +70 -1
- package/src/stores/CubeShowPureState.ts +27 -5
- package/src/stores/editors/CubeIntegerText.ts +34 -0
- package/src/stores/editors/CubeJoinDraft.ts +43 -16
- package/src/stores/editors/CubeNodeDraftRegistry.ts +35 -1
- package/src/stores/editors/CubeRenameDraft.ts +110 -0
- package/src/stores/editors/CubeRestrictDraft.ts +83 -0
- package/src/stores/editors/CubeRowCountDraft.ts +69 -0
- package/src/stores/editors/CubeSliceDraft.ts +87 -0
- package/src/stores/editors/CubeSortDraft.ts +135 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,143 @@
|
|
|
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 { type Schema, validateRenameMapping } from '@finos/legend-cube';
|
|
19
|
+
import { guaranteeType } from '@finos/legend-shared';
|
|
20
|
+
import { observer } from 'mobx-react-lite';
|
|
21
|
+
import { COLUMN_NAME_RULES_HINT } from '../../__lib__/LegendCubeLabels.js';
|
|
22
|
+
import {
|
|
23
|
+
type CubeRenameRow,
|
|
24
|
+
CubeRenameDraft,
|
|
25
|
+
} from '../../stores/editors/CubeRenameDraft.js';
|
|
26
|
+
import { CubeButton } from '../CubeButton.js';
|
|
27
|
+
import { CubeColumnPicker } from './CubeColumnPicker.js';
|
|
28
|
+
import type { CubeNodeEditorProps } from './CubeNodeEditorRegistry.js';
|
|
29
|
+
|
|
30
|
+
const CubeRenameRowEditor = observer(
|
|
31
|
+
(props: {
|
|
32
|
+
draft: CubeRenameDraft;
|
|
33
|
+
row: CubeRenameRow;
|
|
34
|
+
position: number;
|
|
35
|
+
schema: Schema;
|
|
36
|
+
problem: string | undefined;
|
|
37
|
+
readOnly: boolean;
|
|
38
|
+
}) => {
|
|
39
|
+
const { draft, row, position, schema, problem, readOnly } = props;
|
|
40
|
+
return (
|
|
41
|
+
<li className="flex flex-col gap-1 border-b border-[var(--color-border-subtle)] py-1">
|
|
42
|
+
<div className="grid grid-cols-[1fr_auto_1fr_auto] items-center gap-1">
|
|
43
|
+
<CubeColumnPicker
|
|
44
|
+
label={`Old column ${position}`}
|
|
45
|
+
schema={schema}
|
|
46
|
+
value={row.from}
|
|
47
|
+
disabled={readOnly}
|
|
48
|
+
onChange={(name) => draft.setFrom(row.key, name)}
|
|
49
|
+
/>
|
|
50
|
+
<span className="text-[var(--color-text-secondary)]">→</span>
|
|
51
|
+
<input
|
|
52
|
+
aria-label={`New column name ${position}`}
|
|
53
|
+
aria-invalid={problem !== undefined}
|
|
54
|
+
title={problem}
|
|
55
|
+
className={clsx(
|
|
56
|
+
'h-6 min-w-0 rounded-sm border bg-[var(--color-bg-input)] px-1 text-base',
|
|
57
|
+
problem
|
|
58
|
+
? 'border-[var(--color-status-error)]'
|
|
59
|
+
: 'border-[var(--color-border-default)]',
|
|
60
|
+
)}
|
|
61
|
+
type="text"
|
|
62
|
+
spellCheck={false}
|
|
63
|
+
value={row.to}
|
|
64
|
+
disabled={readOnly}
|
|
65
|
+
onChange={(event) => draft.setTo(row.key, event.target.value)}
|
|
66
|
+
/>
|
|
67
|
+
<button
|
|
68
|
+
className="flex h-6 w-6 items-center justify-center text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] disabled:text-[var(--color-text-disabled)]"
|
|
69
|
+
aria-label={`Remove rename ${position}`}
|
|
70
|
+
title="Remove this rename"
|
|
71
|
+
disabled={readOnly}
|
|
72
|
+
onClick={() => draft.removeRow(row.key)}
|
|
73
|
+
>
|
|
74
|
+
<TimesIcon />
|
|
75
|
+
</button>
|
|
76
|
+
</div>
|
|
77
|
+
{problem && (
|
|
78
|
+
<div className="text-sm text-[var(--color-status-error)]">
|
|
79
|
+
{problem}
|
|
80
|
+
</div>
|
|
81
|
+
)}
|
|
82
|
+
</li>
|
|
83
|
+
);
|
|
84
|
+
},
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* The Rename editor (spec §17.6): rows of an old column, picked from the
|
|
89
|
+
* input, and its new name, typed. Each row shows its first problem, as the
|
|
90
|
+
* node checks it, and the rule for new names stays shown under the rows. The
|
|
91
|
+
* rows scroll on their own, so the editor fits wherever it is shown.
|
|
92
|
+
*/
|
|
93
|
+
export const CubeRenameEditor = observer((props: CubeNodeEditorProps) => {
|
|
94
|
+
const { readOnly } = props;
|
|
95
|
+
const draft = guaranteeType(props.draft, CubeRenameDraft);
|
|
96
|
+
const [schema] = props.inputSchemas as [Schema];
|
|
97
|
+
const { mappings } = draft;
|
|
98
|
+
// a row's problem, as the node will judge it; a blank row has none
|
|
99
|
+
let mappingIndex = 0;
|
|
100
|
+
const problems = draft.rows.map((row) => {
|
|
101
|
+
if (!row.from && !row.to) {
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
const errors: string[] = [];
|
|
105
|
+
validateRenameMapping(mappings, mappingIndex, schema, errors);
|
|
106
|
+
mappingIndex += 1;
|
|
107
|
+
return errors[0];
|
|
108
|
+
});
|
|
109
|
+
const canAddRow = !readOnly && draft.rows.length < schema.columns.length;
|
|
110
|
+
return (
|
|
111
|
+
<div className="flex flex-col gap-2 text-base">
|
|
112
|
+
<ul aria-label="Column renames" className="max-h-80 overflow-auto">
|
|
113
|
+
{draft.rows.map((row, index) => (
|
|
114
|
+
<CubeRenameRowEditor
|
|
115
|
+
key={row.key}
|
|
116
|
+
draft={draft}
|
|
117
|
+
row={row}
|
|
118
|
+
position={index + 1}
|
|
119
|
+
schema={schema}
|
|
120
|
+
problem={problems[index]}
|
|
121
|
+
readOnly={readOnly}
|
|
122
|
+
/>
|
|
123
|
+
))}
|
|
124
|
+
</ul>
|
|
125
|
+
<div>
|
|
126
|
+
<CubeButton
|
|
127
|
+
title={
|
|
128
|
+
canAddRow
|
|
129
|
+
? 'Add a column to rename'
|
|
130
|
+
: 'Every column of the input already has a row'
|
|
131
|
+
}
|
|
132
|
+
disabled={!canAddRow}
|
|
133
|
+
onClick={() => draft.addRow()}
|
|
134
|
+
>
|
|
135
|
+
Add column to rename
|
|
136
|
+
</CubeButton>
|
|
137
|
+
</div>
|
|
138
|
+
<div className="text-sm text-[var(--color-text-secondary)]">
|
|
139
|
+
{COLUMN_NAME_RULES_HINT}
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
);
|
|
143
|
+
});
|
|
@@ -0,0 +1,103 @@
|
|
|
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 type { Schema } from '@finos/legend-cube';
|
|
18
|
+
import { guaranteeType } from '@finos/legend-shared';
|
|
19
|
+
import { observer } from 'mobx-react-lite';
|
|
20
|
+
import { getColumnTypeLabel } from '../../__lib__/LegendCubeLabels.js';
|
|
21
|
+
import { CubeRestrictDraft } from '../../stores/editors/CubeRestrictDraft.js';
|
|
22
|
+
import { CubeButton } from '../CubeButton.js';
|
|
23
|
+
import { CubeColumnTypeIcon } from './CubeColumnPicker.js';
|
|
24
|
+
import type { CubeNodeEditorProps } from './CubeNodeEditorRegistry.js';
|
|
25
|
+
|
|
26
|
+
const KEEP = 'Columns to keep';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The Restrict editor (spec §17.6): the input's columns, in its order, each
|
|
30
|
+
* ticked to keep it. A saved column the input doesn't have stays listed, last
|
|
31
|
+
* and ticked, until it is unticked. The list scrolls on its own, so the
|
|
32
|
+
* editor fits wherever it is shown.
|
|
33
|
+
*/
|
|
34
|
+
export const CubeRestrictEditor = observer((props: CubeNodeEditorProps) => {
|
|
35
|
+
const { readOnly } = props;
|
|
36
|
+
const draft = guaranteeType(props.draft, CubeRestrictDraft);
|
|
37
|
+
const [schema] = props.inputSchemas as [Schema];
|
|
38
|
+
// each once, though a saved restrict may repeat one (validation reports it)
|
|
39
|
+
const missing = Array.from(
|
|
40
|
+
new Set(draft.columns.filter((name) => !schema.lookup(name))),
|
|
41
|
+
);
|
|
42
|
+
return (
|
|
43
|
+
<div className="flex flex-col gap-2 text-base">
|
|
44
|
+
<div className="flex items-center gap-2">
|
|
45
|
+
<span>{KEEP}</span>
|
|
46
|
+
<span className="flex-1" />
|
|
47
|
+
<CubeButton
|
|
48
|
+
title="Keep every column of the input"
|
|
49
|
+
disabled={readOnly}
|
|
50
|
+
onClick={() => draft.selectAll(schema)}
|
|
51
|
+
>
|
|
52
|
+
All
|
|
53
|
+
</CubeButton>
|
|
54
|
+
<CubeButton
|
|
55
|
+
title="Untick every column"
|
|
56
|
+
disabled={readOnly}
|
|
57
|
+
onClick={() => draft.clear()}
|
|
58
|
+
>
|
|
59
|
+
None
|
|
60
|
+
</CubeButton>
|
|
61
|
+
</div>
|
|
62
|
+
<ul
|
|
63
|
+
aria-label={KEEP}
|
|
64
|
+
className="max-h-80 overflow-auto rounded-sm border border-[var(--color-border-subtle)]"
|
|
65
|
+
>
|
|
66
|
+
{schema.columns.map((column) => (
|
|
67
|
+
<li key={column.name}>
|
|
68
|
+
<label className="flex items-center gap-2 px-1 py-0.5">
|
|
69
|
+
<input
|
|
70
|
+
type="checkbox"
|
|
71
|
+
checked={draft.columns.includes(column.name)}
|
|
72
|
+
disabled={readOnly}
|
|
73
|
+
onChange={() => draft.toggleColumn(column.name, schema)}
|
|
74
|
+
/>
|
|
75
|
+
<CubeColumnTypeIcon
|
|
76
|
+
type={column.type}
|
|
77
|
+
className="text-[var(--color-text-secondary)]"
|
|
78
|
+
/>
|
|
79
|
+
<span className="min-w-0 truncate">{column.name}</span>
|
|
80
|
+
<span className="shrink-0 text-sm text-[var(--color-text-muted)]">
|
|
81
|
+
{getColumnTypeLabel(column)}
|
|
82
|
+
</span>
|
|
83
|
+
</label>
|
|
84
|
+
</li>
|
|
85
|
+
))}
|
|
86
|
+
{missing.map((name) => (
|
|
87
|
+
<li key={`missing:${name}`}>
|
|
88
|
+
<label className="flex items-center gap-2 px-1 py-0.5 text-[var(--color-status-error)]">
|
|
89
|
+
<input
|
|
90
|
+
type="checkbox"
|
|
91
|
+
checked={true}
|
|
92
|
+
disabled={readOnly}
|
|
93
|
+
onChange={() => draft.toggleColumn(name, schema)}
|
|
94
|
+
/>
|
|
95
|
+
<span className="min-w-0 truncate">{name || '(blank)'}</span>
|
|
96
|
+
<span className="shrink-0 text-sm">(not in the input)</span>
|
|
97
|
+
</label>
|
|
98
|
+
</li>
|
|
99
|
+
))}
|
|
100
|
+
</ul>
|
|
101
|
+
</div>
|
|
102
|
+
);
|
|
103
|
+
});
|
|
@@ -0,0 +1,53 @@
|
|
|
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 { Drop, isPositiveWholeNumber, Limit } from '@finos/legend-cube';
|
|
18
|
+
import { guaranteeType } from '@finos/legend-shared';
|
|
19
|
+
import { observer } from 'mobx-react-lite';
|
|
20
|
+
import { CubeRowCountDraft } from '../../stores/editors/CubeRowCountDraft.js';
|
|
21
|
+
import { CubeIntegerField } from './CubeIntegerField.js';
|
|
22
|
+
import type { CubeNodeEditorProps } from './CubeNodeEditorRegistry.js';
|
|
23
|
+
|
|
24
|
+
/** The field's label for each node type the editor serves */
|
|
25
|
+
const ROW_COUNT_LABELS: Readonly<Record<string, string>> = {
|
|
26
|
+
[Drop.TYPE]: 'Rows to drop',
|
|
27
|
+
[Limit.TYPE]: 'Rows to keep',
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The editor of a node whose one setting is a number of rows, a Limit or a
|
|
32
|
+
* Drop (spec §17.6: one integer field). Clearing the field leaves the size empty,
|
|
33
|
+
* which the panel reports; it never falls back to the default.
|
|
34
|
+
*/
|
|
35
|
+
export const CubeRowCountEditor = observer((props: CubeNodeEditorProps) => {
|
|
36
|
+
const { readOnly } = props;
|
|
37
|
+
const draft = guaranteeType(props.draft, CubeRowCountDraft);
|
|
38
|
+
const label = ROW_COUNT_LABELS[draft.original.type] ?? 'Rows';
|
|
39
|
+
return (
|
|
40
|
+
<div className="flex flex-col gap-2 text-base">
|
|
41
|
+
<label className="flex items-center gap-2">
|
|
42
|
+
<span>{label}</span>
|
|
43
|
+
<CubeIntegerField
|
|
44
|
+
label={label}
|
|
45
|
+
text={draft.sizeText}
|
|
46
|
+
invalid={!isPositiveWholeNumber(draft.size)}
|
|
47
|
+
disabled={readOnly}
|
|
48
|
+
onChange={(text) => draft.setSizeText(text)}
|
|
49
|
+
/>
|
|
50
|
+
</label>
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
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 { isRowIndex } from '@finos/legend-cube';
|
|
18
|
+
import { guaranteeType } from '@finos/legend-shared';
|
|
19
|
+
import { observer } from 'mobx-react-lite';
|
|
20
|
+
import { SLICE_RANGE_HINT } from '../../__lib__/LegendCubeLabels.js';
|
|
21
|
+
import { CubeSliceDraft } from '../../stores/editors/CubeSliceDraft.js';
|
|
22
|
+
import { CubeIntegerField } from './CubeIntegerField.js';
|
|
23
|
+
import type { CubeNodeEditorProps } from './CubeNodeEditorRegistry.js';
|
|
24
|
+
|
|
25
|
+
const START = 'Start row index';
|
|
26
|
+
const STOP = 'Stop row index';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The Slice editor (spec §17.6): the start and stop row indexes, counting
|
|
30
|
+
* from 0, the start row kept and the stop row not (D5). A cleared field
|
|
31
|
+
* leaves its bound empty, which the panel reports.
|
|
32
|
+
*/
|
|
33
|
+
export const CubeSliceEditor = observer((props: CubeNodeEditorProps) => {
|
|
34
|
+
const { readOnly } = props;
|
|
35
|
+
const draft = guaranteeType(props.draft, CubeSliceDraft);
|
|
36
|
+
const { start, stop } = draft;
|
|
37
|
+
const validStart = isRowIndex(start);
|
|
38
|
+
const validStop = isRowIndex(stop);
|
|
39
|
+
// the stop is marked when it doesn't come after the start, too
|
|
40
|
+
const outOfOrder =
|
|
41
|
+
validStart && validStop && (start as number) >= (stop as number);
|
|
42
|
+
return (
|
|
43
|
+
<div className="flex flex-col gap-2 text-base">
|
|
44
|
+
<label className="flex items-center gap-2">
|
|
45
|
+
<span className="w-28">{START}</span>
|
|
46
|
+
<CubeIntegerField
|
|
47
|
+
label={START}
|
|
48
|
+
text={draft.startText}
|
|
49
|
+
invalid={!validStart}
|
|
50
|
+
disabled={readOnly}
|
|
51
|
+
onChange={(text) => draft.setStartText(text)}
|
|
52
|
+
/>
|
|
53
|
+
</label>
|
|
54
|
+
<label className="flex items-center gap-2">
|
|
55
|
+
<span className="w-28">{STOP}</span>
|
|
56
|
+
<CubeIntegerField
|
|
57
|
+
label={STOP}
|
|
58
|
+
text={draft.stopText}
|
|
59
|
+
invalid={!validStop || outOfOrder}
|
|
60
|
+
disabled={readOnly}
|
|
61
|
+
onChange={(text) => draft.setStopText(text)}
|
|
62
|
+
/>
|
|
63
|
+
</label>
|
|
64
|
+
<div className="text-sm text-[var(--color-text-secondary)]">
|
|
65
|
+
{SLICE_RANGE_HINT}
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
);
|
|
69
|
+
});
|
|
@@ -0,0 +1,206 @@
|
|
|
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 { ArrowDownIcon, ArrowUpIcon, TimesIcon } from '@finos/legend-art';
|
|
18
|
+
import {
|
|
19
|
+
isSortableType,
|
|
20
|
+
isSortDirection,
|
|
21
|
+
MESSAGE_CANNOT_HAVE_DUPLICATES,
|
|
22
|
+
type Schema,
|
|
23
|
+
type SchemaColumn,
|
|
24
|
+
SORT_DIRECTION_DESCRIPTIONS,
|
|
25
|
+
SORT_DIRECTIONS,
|
|
26
|
+
validateSortKey,
|
|
27
|
+
} from '@finos/legend-cube';
|
|
28
|
+
import { guaranteeType } from '@finos/legend-shared';
|
|
29
|
+
import { observer } from 'mobx-react-lite';
|
|
30
|
+
import { CUBE_SORT_COLUMN_DISABLED_REASON } from '../../__lib__/LegendCubeLabels.js';
|
|
31
|
+
import {
|
|
32
|
+
type CubeSortRow,
|
|
33
|
+
CubeSortDraft,
|
|
34
|
+
} from '../../stores/editors/CubeSortDraft.js';
|
|
35
|
+
import { CubeButton } from '../CubeButton.js';
|
|
36
|
+
import { CubeColumnPicker } from './CubeColumnPicker.js';
|
|
37
|
+
import type { CubeNodeEditorProps } from './CubeNodeEditorRegistry.js';
|
|
38
|
+
|
|
39
|
+
const ROW_BUTTON_CLASS =
|
|
40
|
+
'flex h-6 w-6 items-center justify-center text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] disabled:text-[var(--color-text-disabled)]';
|
|
41
|
+
|
|
42
|
+
const CubeSortRowEditor = observer(
|
|
43
|
+
(props: {
|
|
44
|
+
draft: CubeSortDraft;
|
|
45
|
+
row: CubeSortRow;
|
|
46
|
+
position: number;
|
|
47
|
+
count: number;
|
|
48
|
+
schema: Schema;
|
|
49
|
+
/** The columns of the other rows, which this row can't take */
|
|
50
|
+
takenColumns: ReadonlySet<string>;
|
|
51
|
+
problem: string | undefined;
|
|
52
|
+
readOnly: boolean;
|
|
53
|
+
}) => {
|
|
54
|
+
const {
|
|
55
|
+
draft,
|
|
56
|
+
row,
|
|
57
|
+
position,
|
|
58
|
+
count,
|
|
59
|
+
schema,
|
|
60
|
+
takenColumns,
|
|
61
|
+
problem,
|
|
62
|
+
readOnly,
|
|
63
|
+
} = props;
|
|
64
|
+
const isColumnDisabled = (column: SchemaColumn): string | undefined =>
|
|
65
|
+
!isSortableType(column.type)
|
|
66
|
+
? CUBE_SORT_COLUMN_DISABLED_REASON.NOT_SORTABLE
|
|
67
|
+
: takenColumns.has(column.name)
|
|
68
|
+
? CUBE_SORT_COLUMN_DISABLED_REASON.TAKEN
|
|
69
|
+
: undefined;
|
|
70
|
+
return (
|
|
71
|
+
<li className="flex flex-col gap-1 border-b border-[var(--color-border-subtle)] py-1">
|
|
72
|
+
<div className="grid grid-cols-[1fr_auto_auto_auto_auto] items-center gap-1">
|
|
73
|
+
<CubeColumnPicker
|
|
74
|
+
label={`Sort column ${position}`}
|
|
75
|
+
schema={schema}
|
|
76
|
+
value={row.column}
|
|
77
|
+
disabled={readOnly}
|
|
78
|
+
invalid={problem !== undefined}
|
|
79
|
+
isColumnDisabled={isColumnDisabled}
|
|
80
|
+
onChange={(name) => draft.setColumn(row.key, name)}
|
|
81
|
+
/>
|
|
82
|
+
<select
|
|
83
|
+
aria-label={`Sort direction ${position}`}
|
|
84
|
+
className="h-6 rounded-sm border border-[var(--color-border-default)] bg-[var(--color-bg-input)] px-1 text-base"
|
|
85
|
+
value={row.direction}
|
|
86
|
+
disabled={readOnly}
|
|
87
|
+
onChange={(event) => {
|
|
88
|
+
const direction = event.target.value;
|
|
89
|
+
if (isSortDirection(direction)) {
|
|
90
|
+
draft.setDirection(row.key, direction);
|
|
91
|
+
}
|
|
92
|
+
}}
|
|
93
|
+
>
|
|
94
|
+
{SORT_DIRECTIONS.map((direction) => (
|
|
95
|
+
<option key={direction} value={direction}>
|
|
96
|
+
{SORT_DIRECTION_DESCRIPTIONS[direction]}
|
|
97
|
+
</option>
|
|
98
|
+
))}
|
|
99
|
+
</select>
|
|
100
|
+
<button
|
|
101
|
+
className={ROW_BUTTON_CLASS}
|
|
102
|
+
aria-label={`Move sort column ${position} up`}
|
|
103
|
+
title="Sort by this column before the one above"
|
|
104
|
+
disabled={readOnly || position === 1}
|
|
105
|
+
onClick={() => draft.moveRow(row.key, -1)}
|
|
106
|
+
>
|
|
107
|
+
<ArrowUpIcon />
|
|
108
|
+
</button>
|
|
109
|
+
<button
|
|
110
|
+
className={ROW_BUTTON_CLASS}
|
|
111
|
+
aria-label={`Move sort column ${position} down`}
|
|
112
|
+
title="Sort by this column after the one below"
|
|
113
|
+
disabled={readOnly || position === count}
|
|
114
|
+
onClick={() => draft.moveRow(row.key, 1)}
|
|
115
|
+
>
|
|
116
|
+
<ArrowDownIcon />
|
|
117
|
+
</button>
|
|
118
|
+
<button
|
|
119
|
+
className={ROW_BUTTON_CLASS}
|
|
120
|
+
aria-label={`Remove sort column ${position}`}
|
|
121
|
+
title="Remove this sort column"
|
|
122
|
+
disabled={readOnly}
|
|
123
|
+
onClick={() => draft.removeRow(row.key)}
|
|
124
|
+
>
|
|
125
|
+
<TimesIcon />
|
|
126
|
+
</button>
|
|
127
|
+
</div>
|
|
128
|
+
{problem && (
|
|
129
|
+
<div className="text-sm text-[var(--color-status-error)]">
|
|
130
|
+
{problem}
|
|
131
|
+
</div>
|
|
132
|
+
)}
|
|
133
|
+
</li>
|
|
134
|
+
);
|
|
135
|
+
},
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* The Sort editor (spec §17.6: an ordered list of column and direction rows,
|
|
140
|
+
* added, removed and reordered). The first row sorts first. A column that
|
|
141
|
+
* can't be sorted, or that another row has, can't be picked; a saved one
|
|
142
|
+
* stays shown with its problem. The rows scroll on their own, so the editor
|
|
143
|
+
* fits wherever it is shown.
|
|
144
|
+
*/
|
|
145
|
+
export const CubeSortEditor = observer((props: CubeNodeEditorProps) => {
|
|
146
|
+
const { readOnly } = props;
|
|
147
|
+
const draft = guaranteeType(props.draft, CubeSortDraft);
|
|
148
|
+
const [schema] = props.inputSchemas as [Schema];
|
|
149
|
+
const { rows } = draft;
|
|
150
|
+
// a row's problem, as the node will judge it; a blank row has none
|
|
151
|
+
const problems = rows.map((row, index) => {
|
|
152
|
+
if (!row.column) {
|
|
153
|
+
return undefined;
|
|
154
|
+
}
|
|
155
|
+
const errors: string[] = [];
|
|
156
|
+
validateSortKey(row, schema, errors);
|
|
157
|
+
return (
|
|
158
|
+
errors[0] ??
|
|
159
|
+
(rows.slice(0, index).some(({ column }) => column === row.column)
|
|
160
|
+
? MESSAGE_CANNOT_HAVE_DUPLICATES('Sort columns')
|
|
161
|
+
: undefined)
|
|
162
|
+
);
|
|
163
|
+
});
|
|
164
|
+
const sortable = schema.columns.filter((column) =>
|
|
165
|
+
isSortableType(column.type),
|
|
166
|
+
);
|
|
167
|
+
const canAddRow = !readOnly && rows.length < sortable.length;
|
|
168
|
+
return (
|
|
169
|
+
<div className="flex flex-col gap-2 text-base">
|
|
170
|
+
<ul aria-label="Sort columns" className="max-h-80 overflow-auto">
|
|
171
|
+
{rows.map((row, index) => (
|
|
172
|
+
<CubeSortRowEditor
|
|
173
|
+
key={row.key}
|
|
174
|
+
draft={draft}
|
|
175
|
+
row={row}
|
|
176
|
+
position={index + 1}
|
|
177
|
+
count={rows.length}
|
|
178
|
+
schema={schema}
|
|
179
|
+
takenColumns={
|
|
180
|
+
new Set(
|
|
181
|
+
rows
|
|
182
|
+
.filter((other) => other.key !== row.key && other.column)
|
|
183
|
+
.map(({ column }) => column),
|
|
184
|
+
)
|
|
185
|
+
}
|
|
186
|
+
problem={problems[index]}
|
|
187
|
+
readOnly={readOnly}
|
|
188
|
+
/>
|
|
189
|
+
))}
|
|
190
|
+
</ul>
|
|
191
|
+
<div>
|
|
192
|
+
<CubeButton
|
|
193
|
+
title={
|
|
194
|
+
canAddRow
|
|
195
|
+
? 'Add a column to sort by'
|
|
196
|
+
: 'Every column that can be sorted already has a row'
|
|
197
|
+
}
|
|
198
|
+
disabled={!canAddRow}
|
|
199
|
+
onClick={() => draft.addRow()}
|
|
200
|
+
>
|
|
201
|
+
Add sort column
|
|
202
|
+
</CubeButton>
|
|
203
|
+
</div>
|
|
204
|
+
</div>
|
|
205
|
+
);
|
|
206
|
+
});
|
|
@@ -40,7 +40,6 @@ export const CubeSourceEditor = observer((props: CubeNodeEditorProps) => {
|
|
|
40
40
|
const source = guaranteeType(props.draft.original, RelationalTableSource);
|
|
41
41
|
const refreshing = editorState.isPendingSource(source);
|
|
42
42
|
const { resolution } = source;
|
|
43
|
-
const warnings = editorState.warnings.get(source.key) ?? [];
|
|
44
43
|
return (
|
|
45
44
|
<div className="relative flex flex-col gap-2">
|
|
46
45
|
<dl className="grid grid-cols-[max-content_1fr] gap-x-3 gap-y-1 text-base">
|
|
@@ -70,15 +69,6 @@ export const CubeSourceEditor = observer((props: CubeNodeEditorProps) => {
|
|
|
70
69
|
)}
|
|
71
70
|
</div>
|
|
72
71
|
<PanelLoadingIndicator isLoading={refreshing} />
|
|
73
|
-
{warnings.map((warning) => (
|
|
74
|
-
<div
|
|
75
|
-
key={warning}
|
|
76
|
-
className="text-sm text-[var(--color-status-warn)]"
|
|
77
|
-
role="status"
|
|
78
|
-
>
|
|
79
|
-
{warning}
|
|
80
|
-
</div>
|
|
81
|
-
))}
|
|
82
72
|
{resolution.kind === 'resolved' ? (
|
|
83
73
|
<table className="w-full text-base" aria-label="Columns">
|
|
84
74
|
<thead>
|
|
@@ -283,7 +283,8 @@ export const CubeValueEditor: React.FC<{
|
|
|
283
283
|
return (
|
|
284
284
|
<ul aria-label={label} className="flex min-w-0 flex-1 flex-col gap-1">
|
|
285
285
|
{items.map((item, index) => (
|
|
286
|
-
// values can repeat
|
|
286
|
+
// values can repeat and have no id. A row's only state is its text
|
|
287
|
+
// while typed, which blur commits before another row can be removed
|
|
287
288
|
// eslint-disable-next-line react/no-array-index-key
|
|
288
289
|
<li key={index} className="flex items-center gap-1">
|
|
289
290
|
<CubeSingleValueEditor
|