@marimo-team/islands 0.25.3-dev196 → 0.25.3-dev198
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/dist/{ConnectedDataExplorerComponent-BRuwLOIx.js → ConnectedDataExplorerComponent-BsWiIPFk.js} +4 -4
- package/dist/{ErrorBoundary-C3Xv3yzV.js → ErrorBoundary-D1Z3KAf_.js} +2 -2
- package/dist/{abnfDiagram-VCTEODGH-LlJTOOU_.js → abnfDiagram-VCTEODGH-I2x4Yu25.js} +2 -2
- package/dist/{architectureDiagram-5GKGNRK7-9PQtGLLZ.js → architectureDiagram-5GKGNRK7-1bSQMKzK.js} +1 -1
- package/dist/{arrays-DA6KkLfq.js → arrays-DoQ-AILx.js} +7 -21
- package/dist/{capabilities-CvWU-ey_.js → capabilities-C2QUNRXi.js} +1 -1
- package/dist/{chat-ui-Bl2euMBD.js → chat-ui-BAyHM_GD.js} +524 -524
- package/dist/{chunk-IMKFNOWR-DcXv-BD5.js → chunk-IMKFNOWR-fsCGAJ9G.js} +1 -1
- package/dist/{chunk-SHT3W25Y-VwnELN23.js → chunk-SHT3W25Y-D2STW4hj.js} +1 -1
- package/dist/{chunk-SVP7TREG-DgQpI5Zi.js → chunk-SVP7TREG-CiY7cQG-.js} +1 -1
- package/dist/{chunk-TICWLB2K-CTg6VTJT.js → chunk-TICWLB2K-C_t5RBp9.js} +1 -1
- package/dist/{classDiagram-ZZMXUADV-UzmcFrnW.js → classDiagram-ZZMXUADV-B2JvZAP4.js} +2 -2
- package/dist/{classDiagram-v2-VYDZK3BY-DhjiI6IQ.js → classDiagram-v2-VYDZK3BY-L8VsZG5A.js} +2 -2
- package/dist/{code-visibility-BCcqmP58.js → code-visibility-CInEQd1e.js} +19 -19
- package/dist/{common-DrNB2bY2.js → common-W5HfVJPN.js} +4362 -4362
- package/dist/{cynefinDiagram-5FMLGOSQ-O4ShIbJX.js → cynefinDiagram-5FMLGOSQ-DdllC264.js} +1 -1
- package/dist/{dagre-GXQ25YYZ-dtHhhcPu.js → dagre-GXQ25YYZ-BaWHw4_f.js} +1 -1
- package/dist/{data-grid-overlay-editor-TGXV_Hf4.js → data-grid-overlay-editor-BQCaV0fI.js} +1 -1
- package/dist/{diagram-S7CK7UJ4-ppPYVZ6W.js → diagram-S7CK7UJ4-pFt-tUDe.js} +1 -1
- package/dist/{diagram-UQ7AKVKN-CMZDf--k.js → diagram-UQ7AKVKN-DqGJiBp4.js} +1 -1
- package/dist/{diagram-VX7I27RA-CFvJdx-C.js → diagram-VX7I27RA-Dr-yzMjf.js} +1 -1
- package/dist/{diagram-Z3DM3KII-Dt6_uFbv.js → diagram-Z3DM3KII-8xzJzv-w.js} +1 -1
- package/dist/{dropdown-menu-CeaOsjWZ.js → dropdown-menu-NlnsmNA6.js} +2 -2
- package/dist/{ebnfDiagram-PWID7BFC-DpRFrKK-.js → ebnfDiagram-PWID7BFC-Br04Wy-E.js} +2 -2
- package/dist/{erDiagram-RLTQ6QDP-CqAU4b0u.js → erDiagram-RLTQ6QDP-D5-XVgvv.js} +1 -1
- package/dist/{error-entries-CYGwr5-O.js → error-entries-BacCWMWj.js} +4678 -5028
- package/dist/{feedback-button-PTiPn2GP.js → feedback-button-DLXUcQ7l.js} +113 -113
- package/dist/{flowDiagram-HODETNUW-i8-Gw8HO.js → flowDiagram-HODETNUW-CVl2kREF.js} +2 -2
- package/dist/{formats-BtoHHy42.js → formats-BCYthwyc.js} +1 -1
- package/dist/{glide-data-editor-BSyexjkB.js → glide-data-editor-D5YaHPKx.js} +5 -5
- package/dist/{globals-B7gkJAxm.js → globals-DjX1uxzy.js} +2 -2
- package/dist/{html-to-image-Cy5UDpQJ.js → html-to-image-DAmgSo5P.js} +624 -573
- package/dist/{infoDiagram-27XIBGKW-DpfOKdLL.js → infoDiagram-27XIBGKW-BJ4vNcZE.js} +1 -1
- package/dist/{ishikawaDiagram-5VMMS53U-Dg7mrMG7.js → ishikawaDiagram-5VMMS53U-B_Jy1Dsm.js} +1 -1
- package/dist/{kanban-definition-UXKFOSKX-QB6e3Jrj.js → kanban-definition-UXKFOSKX-C6MkS9vw.js} +1 -1
- package/dist/main.js +1117 -1117
- package/dist/{mermaid-CeWG4Egr.js → mermaid-Bh918qrq.js} +34 -34
- package/dist/{mindmap-definition-YA3MSWOX-BTz3uTGe.js → mindmap-definition-YA3MSWOX-BP3ZTuir.js} +1 -1
- package/dist/{number-overlay-editor-Bb91yQ46.js → number-overlay-editor-3C_K6w_0.js} +1 -1
- package/dist/{once-DWGO2mQ2.js → once-NnpX_HnX.js} +1 -1
- package/dist/{pegDiagram-XKGWAZYB-3ZIEa3mB.js → pegDiagram-XKGWAZYB-nNGOQtnt.js} +2 -2
- package/dist/{pieDiagram-E7YTZNPT-BJAlk046.js → pieDiagram-E7YTZNPT-CpRz5BZz.js} +1 -1
- package/dist/{process-output-q9MTAIMX.js → process-output-DweUmAmV.js} +3 -3
- package/dist/{railroadDiagram-O6MQD6OU-Dn3bsSRa.js → railroadDiagram-O6MQD6OU-BqOEN4I0.js} +2 -2
- package/dist/{requirementDiagram-BXWQKSXE-wcOmQQCL.js → requirementDiagram-BXWQKSXE-CEfg6vu9.js} +1 -1
- package/dist/{reveal-component-vTdjiyDv.js → reveal-component-JjJRTT9d.js} +17 -17
- package/dist/{sequenceDiagram-WJ2MYXX4-DSh_qxoF.js → sequenceDiagram-WJ2MYXX4-IOmpQcOi.js} +1 -1
- package/dist/{spec-C7Fos-If.js → spec-Dkp64vVQ.js} +1 -1
- package/dist/{stateDiagram-D77RDMKH-DH_H8NMH.js → stateDiagram-D77RDMKH-tgU-EY9_.js} +2 -2
- package/dist/{stateDiagram-v2-MP3YSRHH-D_JM7MEK.js → stateDiagram-v2-MP3YSRHH-BofhG6q1.js} +2 -2
- package/dist/{swimlanes-42K2YHIH-CfoTJO-9.js → swimlanes-42K2YHIH-DW4vWDBd.js} +1 -1
- package/dist/{swimlanesDiagram-VR7AAH4N-Z_QhvK0G.js → swimlanesDiagram-VR7AAH4N-BJcrqUJr.js} +2 -2
- package/dist/{swiper-component-D2geL1Ku.js → swiper-component-BVfnhbnt.js} +1 -1
- package/dist/{timeline-definition-24CTP7MA-BFtdScaL.js → timeline-definition-24CTP7MA-D_QU3NGs.js} +1 -1
- package/dist/{toDate-CQAFSwos.js → toDate-BNDEXbfV.js} +30 -30
- package/dist/{useAsyncData-BeyZyjso.js → useAsyncData-ASDni9vH.js} +1 -1
- package/dist/{useDeepCompareMemoize-DOQgae0W.js → useDeepCompareMemoize-B1Ogyf53.js} +1 -1
- package/dist/{useIframeCapabilities-DNvaj9wM.js → useIframeCapabilities-6a_yDNXp.js} +1 -1
- package/dist/{useLifecycle-n2lnCFS8.js → useLifecycle-Bfpa2m7k.js} +1 -1
- package/dist/{useTheme-CY777LXN.js → useTheme-5TwyOh3B.js} +66 -66
- package/dist/{vega-component-0nQzA-I8.js → vega-component-BHS9UOtC.js} +19 -19
- package/dist/{vennDiagram-4TSXK5OY-BXY8IGCA.js → vennDiagram-4TSXK5OY-YFYsNIAh.js} +1 -1
- package/dist/{wardleyDiagram-VM6X3IG4-D0tFe6eC.js → wardleyDiagram-VM6X3IG4-i_n8mAWC.js} +1 -1
- package/dist/{xychartDiagram-S5SC5T6Z-Dx8wFM-y.js → xychartDiagram-S5SC5T6Z-BYGN95wW.js} +1 -1
- package/package.json +1 -1
- package/src/__mocks__/document.tsx +13 -4
- package/src/__mocks__/notebook.ts +1 -2
- package/src/components/datasources/datasources.tsx +1 -1
- package/src/components/dependency-graph/custom-node.tsx +1 -1
- package/src/components/dependency-graph/minimap-content.tsx +12 -14
- package/src/components/editor/actions/useSetCodeVisibility.ts +1 -1
- package/src/components/editor/actions/useSetOutputsExpanded.ts +1 -1
- package/src/components/editor/alerts/stdin-blocking-alert.tsx +1 -1
- package/src/components/editor/cell/code/cell-editor.tsx +1 -1
- package/src/components/editor/cell/useSplitCell.tsx +8 -5
- package/src/components/editor/chrome/panels/dependency-graph-panel.tsx +1 -1
- package/src/components/editor/chrome/panels/session-panel.tsx +1 -1
- package/src/components/editor/chrome/panels/variable-panel.tsx +1 -5
- package/src/components/editor/chrome/wrapper/minimap-state.ts +1 -1
- package/src/components/editor/columns/__tests__/cell-column.test.tsx +1 -1
- package/src/components/editor/columns/cell-column.tsx +1 -1
- package/src/components/editor/columns/sortable-column.tsx +1 -1
- package/src/components/editor/links/__tests__/cell-link.test.tsx +1 -1
- package/src/components/editor/links/cell-link-list.tsx +1 -1
- package/src/components/editor/links/cell-link.tsx +1 -1
- package/src/components/editor/navigation/__tests__/navigation.test.ts +20 -6
- package/src/components/editor/navigation/__tests__/selection.test.ts +1 -2
- package/src/components/editor/navigation/clipboard.ts +5 -3
- package/src/components/editor/renderers/cell-array.tsx +7 -12
- package/src/components/editor/renderers/slides-layout/slides-layout.tsx +1 -1
- package/src/components/editor/renderers/vertical-layout/__tests__/useFocusFirstEditor.test.ts +4 -4
- package/src/components/editor/renderers/vertical-layout/useFocusFirstEditor.ts +1 -1
- package/src/components/editor/stdin-blocking-alert.tsx +1 -1
- package/src/components/slides/__tests__/minimap-actions.test.tsx +13 -4
- package/src/components/slides/__tests__/minimap.test.ts +38 -19
- package/src/components/slides/minimap.tsx +11 -10
- package/src/components/sort/SortableCellsProvider.tsx +2 -21
- package/src/components/tracing/tracing.tsx +1 -1
- package/src/core/ai/__tests__/staged-cells.test.ts +1 -1
- package/src/core/ai/context/providers/__tests__/cell-output.test.ts +8 -12
- package/src/core/ai/context/providers/cell-output.ts +2 -2
- package/src/core/ai/tools/__tests__/edit-notebook-tool.test.ts +34 -66
- package/src/core/ai/tools/edit-notebook-tool.ts +12 -10
- package/src/core/cells/__tests__/actions.test.ts +64 -25
- package/src/core/cells/__tests__/arrange.test.ts +156 -4
- package/src/core/cells/__tests__/cells.test.ts +12 -290
- package/src/core/cells/__tests__/document.test.ts +2 -46
- package/src/core/cells/__tests__/harness.ts +28 -4
- package/src/core/cells/__tests__/layout.test.ts +42 -54
- package/src/core/cells/__tests__/session.test.ts +0 -2
- package/src/core/cells/__tests__/utils.test.ts +9 -107
- package/src/core/cells/actions.ts +112 -26
- package/src/core/cells/add-missing-import.ts +1 -1
- package/src/core/cells/arrange.ts +206 -13
- package/src/core/cells/cells.ts +28 -256
- package/src/core/cells/document.ts +18 -122
- package/src/core/cells/layout.ts +79 -10
- package/src/core/cells/pending-delete-service.ts +1 -1
- package/src/core/cells/utils.ts +4 -4
- package/src/core/codemirror/copilot/__tests__/getCodes.test.ts +6 -7
- package/src/core/codemirror/copilot/getCodes.ts +2 -2
- package/src/core/codemirror/lsp/__tests__/notebook-lsp.test.ts +1 -2
- package/src/core/codemirror/lsp/utils.ts +1 -1
- package/src/core/documentation/doc-lookup.ts +1 -1
- package/src/core/edit-app.tsx +2 -5
- package/src/core/errors/error-entries.ts +1 -1
- package/src/core/islands/__tests__/bootstrap.test.ts +2 -6
- package/src/core/islands/components/web-components.tsx +1 -1
- package/src/core/notebook/document/__tests__/codemirror.test.ts +8 -0
- package/src/core/session/__tests__/useNotebookSession.hook.test.tsx +1 -1
- package/src/stories/cell.stories.tsx +1 -2
- package/src/stories/layout/vertical/one-column.stories.tsx +1 -2
- package/src/stories/runtime-story.tsx +2 -3
- package/src/utils/__tests__/id-tree.test.ts +0 -617
- package/src/utils/id-tree.tsx +0 -609
package/src/utils/id-tree.tsx
DELETED
|
@@ -1,609 +0,0 @@
|
|
|
1
|
-
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
-
|
|
3
|
-
import { Memoize } from "typescript-memoize";
|
|
4
|
-
import { reorderColumnSizes } from "@/components/editor/columns/storage";
|
|
5
|
-
import { SETUP_CELL_ID } from "@/core/cells/ids";
|
|
6
|
-
import { arrayDelete, arrayInsert, arrayMove } from "./arrays";
|
|
7
|
-
import { Logger } from "./Logger";
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Branded number to help with type safety
|
|
11
|
-
*/
|
|
12
|
-
export type CellColumnIndex = number & { __brand: "CellColumnIndex" };
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Branded string to help with type safety
|
|
16
|
-
*/
|
|
17
|
-
export type CellColumnId = string & { __brand: "CellColumnId" };
|
|
18
|
-
/**
|
|
19
|
-
* Weakly-branded number, since making `__brand` required causes type errors
|
|
20
|
-
*/
|
|
21
|
-
export type CellIndex = number & { __brand?: "CellIndex" };
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Tree data structure for handling ids with nested children
|
|
25
|
-
*/
|
|
26
|
-
export class TreeNode<T> {
|
|
27
|
-
public value: T;
|
|
28
|
-
|
|
29
|
-
public constructor(value: T) {
|
|
30
|
-
this.value = value;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
public toString(): string {
|
|
34
|
-
return String(this.value);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
public equals(other: TreeNode<T>): boolean {
|
|
38
|
-
return this.value === other.value;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
let uniqueId = 0;
|
|
43
|
-
|
|
44
|
-
export class CollapsibleTree<T> {
|
|
45
|
-
public readonly nodes: TreeNode<T>[];
|
|
46
|
-
public readonly id: CellColumnId;
|
|
47
|
-
|
|
48
|
-
private constructor(nodes: TreeNode<T>[], id: CellColumnId) {
|
|
49
|
-
this.nodes = nodes;
|
|
50
|
-
this.id = id;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/** A column of `ids`, keeping `id` when a column is rebuilt. */
|
|
54
|
-
public static from<T>(ids: T[], id?: CellColumnId): CollapsibleTree<T> {
|
|
55
|
-
return new CollapsibleTree(
|
|
56
|
-
ids.map((id) => new TreeNode(id)),
|
|
57
|
-
id ?? (`tree_${uniqueId++}` as CellColumnId),
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
public withNodes(nodes: TreeNode<T>[]): CollapsibleTree<T> {
|
|
62
|
-
return new CollapsibleTree(nodes, this.id);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
@Memoize()
|
|
66
|
-
public get topLevelIds(): T[] {
|
|
67
|
-
return this.nodes.map((n) => n.value);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
public get inOrderIds(): T[] {
|
|
71
|
-
return this.topLevelIds;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
@Memoize()
|
|
75
|
-
public get idSet(): Set<T> {
|
|
76
|
-
return new Set(this.inOrderIds);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
public get length(): number {
|
|
80
|
-
return this.nodes.length;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
@Memoize()
|
|
84
|
-
public get _nodeMap(): Map<T, TreeNode<T>> {
|
|
85
|
-
const result = new Map<T, TreeNode<T>>();
|
|
86
|
-
for (const node of this.nodes) {
|
|
87
|
-
result.set(node.value, node);
|
|
88
|
-
}
|
|
89
|
-
return result;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Get the index of the given node, or throw
|
|
94
|
-
*/
|
|
95
|
-
public indexOfOrThrow(id: T): CellIndex {
|
|
96
|
-
const index = this.nodes.findIndex((n) => n.value === id);
|
|
97
|
-
if (index === -1) {
|
|
98
|
-
throw new Error(
|
|
99
|
-
`Node ${id} not found in tree. Valid ids: ${this.topLevelIds}`,
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
return index as CellIndex;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Get the top level nodes in the given range.
|
|
107
|
-
* This does not include descendants.
|
|
108
|
-
*/
|
|
109
|
-
public slice(start: number, end: number): T[] {
|
|
110
|
-
return this.nodes.slice(start, end).map((n) => n.value);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Get the node at the given index
|
|
115
|
-
*/
|
|
116
|
-
public at(index: number): T | undefined {
|
|
117
|
-
return this.nodes.at(index)?.value;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Get the node at the given index
|
|
122
|
-
*/
|
|
123
|
-
public atOrThrow(index: number): T {
|
|
124
|
-
const node = this.nodes.at(index);
|
|
125
|
-
if (node === undefined) {
|
|
126
|
-
throw new Error(`Node at index ${index} not found in tree`);
|
|
127
|
-
}
|
|
128
|
-
return node.value;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Get the first node, or throw
|
|
133
|
-
*/
|
|
134
|
-
public first(): T {
|
|
135
|
-
return this.atOrThrow(0);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Get the last node, or throw
|
|
140
|
-
*/
|
|
141
|
-
public last(): T {
|
|
142
|
-
return this.atOrThrow(this.nodes.length - 1);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Get the next node after the given node, does not wrap.
|
|
147
|
-
*/
|
|
148
|
-
public after(id: T): T | undefined {
|
|
149
|
-
const index = this.indexOfOrThrow(id);
|
|
150
|
-
if (index === this.nodes.length - 1) {
|
|
151
|
-
return undefined;
|
|
152
|
-
}
|
|
153
|
-
return this.at(index + 1);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Get the previous node before the given node, does not wrap.
|
|
158
|
-
*/
|
|
159
|
-
public before(id: T): T | undefined {
|
|
160
|
-
const index = this.indexOfOrThrow(id);
|
|
161
|
-
if (index === 0) {
|
|
162
|
-
return undefined;
|
|
163
|
-
}
|
|
164
|
-
return this.at(index - 1);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Insert a node at the given index
|
|
169
|
-
*/
|
|
170
|
-
public insert(id: T, index: number): CollapsibleTree<T> {
|
|
171
|
-
return this.withNodes(arrayInsert(this.nodes, index, new TreeNode(id)));
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Insert a node at the end
|
|
176
|
-
*/
|
|
177
|
-
public insertAtEnd(id: T): CollapsibleTree<T> {
|
|
178
|
-
return this.insert(id, this.nodes.length);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* Insert a node at the start
|
|
183
|
-
*/
|
|
184
|
-
public insertAtStart(id: T): CollapsibleTree<T> {
|
|
185
|
-
return this.insert(id, 0);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
public deleteAtIndex(idx: number): CollapsibleTree<T> {
|
|
189
|
-
this.atOrThrow(idx);
|
|
190
|
-
return this.withNodes(arrayDelete(this.nodes, idx));
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
public delete(id: T): CollapsibleTree<T> {
|
|
194
|
-
const index = this.indexOfOrThrow(id);
|
|
195
|
-
return this.deleteAtIndex(index);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Split the tree into two trees
|
|
200
|
-
* @param id the id of the node to split at
|
|
201
|
-
* @returns a tuple of the left and right trees
|
|
202
|
-
*/
|
|
203
|
-
public split(id: T): [CollapsibleTree<T>, CollapsibleTree<T> | undefined] {
|
|
204
|
-
const index = this.nodes.findIndex((n) => n.value === id);
|
|
205
|
-
if (index === -1) {
|
|
206
|
-
throw new Error(`Node ${id} not found in tree`);
|
|
207
|
-
}
|
|
208
|
-
const leftNodes = this.nodes.slice(0, index);
|
|
209
|
-
const rightNodes = this.nodes.slice(index);
|
|
210
|
-
if (leftNodes.length === 0) {
|
|
211
|
-
return [CollapsibleTree.from<T>([]), this.withNodes(rightNodes)];
|
|
212
|
-
}
|
|
213
|
-
const left = this.withNodes(leftNodes);
|
|
214
|
-
const right = CollapsibleTree.from<T>([]).withNodes(rightNodes);
|
|
215
|
-
return [left, right];
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
public equals(other: CollapsibleTree<T>): boolean {
|
|
219
|
-
return (
|
|
220
|
-
this.nodes.length === other.nodes.length &&
|
|
221
|
-
this.nodes.every((n, i) => n.value === other.nodes[i].value)
|
|
222
|
-
);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
public toString(): string {
|
|
226
|
-
return this.nodes.map((node) => `${node.toString()}\n`).join("");
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
export class MultiColumn<T> {
|
|
231
|
-
private readonly columns: readonly CollapsibleTree<T>[];
|
|
232
|
-
|
|
233
|
-
public constructor(columns: readonly CollapsibleTree<T>[]) {
|
|
234
|
-
this.columns = columns;
|
|
235
|
-
|
|
236
|
-
// Ensure there is always at least one column
|
|
237
|
-
if (columns.length === 0) {
|
|
238
|
-
this.columns = [CollapsibleTree.from([])];
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
public static from<T>(idsList: T[][]): MultiColumn<T> {
|
|
243
|
-
return new MultiColumn(idsList.map((ids) => CollapsibleTree.from(ids)));
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* Create a new MultiColumn from idsList,
|
|
248
|
-
* attempting to preserve structure from previous MultiColumn if possible.
|
|
249
|
-
*/
|
|
250
|
-
public static fromWithPreviousShape<T>(
|
|
251
|
-
idsList: T[],
|
|
252
|
-
previousShape: MultiColumn<T>,
|
|
253
|
-
): MultiColumn<T> {
|
|
254
|
-
if (!previousShape) {
|
|
255
|
-
return MultiColumn.from([idsList]);
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// Get previous columns for reference
|
|
259
|
-
const previousColumns = previousShape.getColumns();
|
|
260
|
-
|
|
261
|
-
// Split the ids into their respective columns
|
|
262
|
-
const splitIds: T[][] = Array.from(
|
|
263
|
-
{ length: previousColumns.length },
|
|
264
|
-
() => [],
|
|
265
|
-
);
|
|
266
|
-
let lastUsedColumnIdx = 0;
|
|
267
|
-
for (const id of idsList) {
|
|
268
|
-
const column = previousColumns.findIndex((c) => c.idSet.has(id));
|
|
269
|
-
if (column === -1) {
|
|
270
|
-
// No column found, use the last used column
|
|
271
|
-
splitIds[lastUsedColumnIdx].push(id);
|
|
272
|
-
} else {
|
|
273
|
-
lastUsedColumnIdx = column;
|
|
274
|
-
splitIds[column].push(id);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
const newColumns = splitIds.map((ids, idx) =>
|
|
279
|
-
CollapsibleTree.from(ids, previousColumns[idx].id),
|
|
280
|
-
);
|
|
281
|
-
|
|
282
|
-
return new MultiColumn(newColumns);
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
@Memoize()
|
|
286
|
-
public isEmpty(): boolean {
|
|
287
|
-
if (this.columns.length === 0) {
|
|
288
|
-
return true;
|
|
289
|
-
}
|
|
290
|
-
return this.columns.every((c) => c.nodes.length === 0);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
public static fromIdsAndColumns<T>(
|
|
294
|
-
idAndColumns: [T, number | undefined | null][],
|
|
295
|
-
): MultiColumn<T> {
|
|
296
|
-
// If column is undefined, use the previous column
|
|
297
|
-
// Ensure there is always at least one column
|
|
298
|
-
const numColumns = Math.max(
|
|
299
|
-
1,
|
|
300
|
-
...idAndColumns.map(([, column]) => (column ?? 0) + 1),
|
|
301
|
-
);
|
|
302
|
-
const idsList: T[][] = Array.from({ length: numColumns }, () => []);
|
|
303
|
-
|
|
304
|
-
let prevColumn = 0;
|
|
305
|
-
for (const [id, column] of idAndColumns) {
|
|
306
|
-
// Avoid negative column indices
|
|
307
|
-
if (column === undefined || column === null || column < 0) {
|
|
308
|
-
idsList[prevColumn].push(id);
|
|
309
|
-
} else {
|
|
310
|
-
idsList[column].push(id);
|
|
311
|
-
prevColumn = column;
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
return MultiColumn.from(idsList);
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
@Memoize()
|
|
319
|
-
public get topLevelIds(): T[][] {
|
|
320
|
-
return this.columns.map((c) => c.topLevelIds);
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
public get iterateTopLevelIds(): Iterable<T> {
|
|
324
|
-
const columns = this.columns;
|
|
325
|
-
|
|
326
|
-
function* iter() {
|
|
327
|
-
for (const column of columns) {
|
|
328
|
-
for (const id of column.topLevelIds) {
|
|
329
|
-
yield id;
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
return iter();
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
@Memoize()
|
|
338
|
-
public get inOrderIds(): T[] {
|
|
339
|
-
return this.columns.flatMap((c) => c.inOrderIds);
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
public get colLength(): number {
|
|
343
|
-
return this.columns.length;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
@Memoize()
|
|
347
|
-
public get idLength(): number {
|
|
348
|
-
return this.columns.reduce((acc, c) => acc + c.nodes.length, 0);
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
@Memoize()
|
|
352
|
-
public get _columnMap(): Map<CellColumnId, CollapsibleTree<T>> {
|
|
353
|
-
return new Map(this.columns.map((c) => [c.id, c]));
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
public at(idx: number): CollapsibleTree<T> | undefined {
|
|
357
|
-
return this.columns[idx];
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
public get(columnId: CellColumnId): CollapsibleTree<T> | undefined {
|
|
361
|
-
return this._columnMap.get(columnId);
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
public atOrThrow(idx: number): CollapsibleTree<T> {
|
|
365
|
-
const column = this.columns[idx];
|
|
366
|
-
if (!column) {
|
|
367
|
-
throw new Error(`Column ${idx} not found`);
|
|
368
|
-
}
|
|
369
|
-
return column;
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
public hasOnlyOneColumn(): boolean {
|
|
373
|
-
return this.columns.length === 1;
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
public getColumns(): readonly CollapsibleTree<T>[] {
|
|
377
|
-
return this.columns;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
public hasOnlyOneId(): boolean {
|
|
381
|
-
return this.idLength === 1;
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
@Memoize()
|
|
385
|
-
public getColumnIds(): CellColumnId[] {
|
|
386
|
-
return this.columns.map((c) => c.id);
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
public indexOf(column: CollapsibleTree<T>): number {
|
|
390
|
-
return this.columns.indexOf(column);
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
public addColumn(
|
|
394
|
-
columnId: CellColumnId,
|
|
395
|
-
initialIds: T[] = [],
|
|
396
|
-
): MultiColumn<T> {
|
|
397
|
-
return new MultiColumn(
|
|
398
|
-
this.columns.flatMap((c) => {
|
|
399
|
-
if (c.id === columnId) {
|
|
400
|
-
return [c, CollapsibleTree.from(initialIds)];
|
|
401
|
-
}
|
|
402
|
-
return [c];
|
|
403
|
-
}),
|
|
404
|
-
);
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
public insertBreakpoint(cellId: T): MultiColumn<T> {
|
|
408
|
-
const column = this.findWithId(cellId);
|
|
409
|
-
const [left, right] = column.split(cellId);
|
|
410
|
-
const newColumns = this.columns.flatMap((c) => {
|
|
411
|
-
if (c === column) {
|
|
412
|
-
return [left, right].filter(Boolean);
|
|
413
|
-
}
|
|
414
|
-
return [c];
|
|
415
|
-
});
|
|
416
|
-
return new MultiColumn(newColumns);
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
public delete(columnId: CellColumnId): MultiColumn<T> {
|
|
420
|
-
// Move cells to preceding column
|
|
421
|
-
// If its the first column, move the cells to the next column
|
|
422
|
-
|
|
423
|
-
// Noop if there is only one column
|
|
424
|
-
if (this.columns.length <= 1) {
|
|
425
|
-
return this;
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
const columnIndex = this.indexOfOrThrow(columnId);
|
|
429
|
-
const targetColumnIndex = columnIndex === 0 ? 1 : columnIndex - 1;
|
|
430
|
-
|
|
431
|
-
const columns = [...this.columns];
|
|
432
|
-
const column = columns[columnIndex];
|
|
433
|
-
columns[targetColumnIndex] = column.withNodes([
|
|
434
|
-
...columns[targetColumnIndex].nodes,
|
|
435
|
-
...column.nodes,
|
|
436
|
-
]);
|
|
437
|
-
columns.splice(columnIndex, 1);
|
|
438
|
-
return new MultiColumn(columns);
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
public mergeAllColumns(): MultiColumn<T> {
|
|
442
|
-
if (this.columns.length <= 1) {
|
|
443
|
-
return this;
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
const nodes = this.columns.flatMap((c) => c.nodes);
|
|
447
|
-
const firstColumn = this.columns[0];
|
|
448
|
-
return new MultiColumn([firstColumn.withNodes(nodes)]);
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
public indexOfOrThrow(id: CellColumnId): number {
|
|
452
|
-
const index = this.columns.findIndex((c) => c.id === id);
|
|
453
|
-
if (index === -1) {
|
|
454
|
-
throw new Error(
|
|
455
|
-
`Column ${id} not found. Possible values: ${this.getColumnIds().join(", ")}`,
|
|
456
|
-
);
|
|
457
|
-
}
|
|
458
|
-
return index;
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
public moveColumn(
|
|
462
|
-
fromCol: CellColumnId,
|
|
463
|
-
toCol: CellColumnId | "_left_" | "_right_",
|
|
464
|
-
): MultiColumn<T> {
|
|
465
|
-
if (fromCol === toCol) {
|
|
466
|
-
return this;
|
|
467
|
-
}
|
|
468
|
-
const fromIdx = this.indexOfOrThrow(fromCol);
|
|
469
|
-
const toIdx =
|
|
470
|
-
toCol === "_left_"
|
|
471
|
-
? fromIdx - 1
|
|
472
|
-
: toCol === "_right_"
|
|
473
|
-
? fromIdx + 1
|
|
474
|
-
: this.indexOfOrThrow(toCol);
|
|
475
|
-
|
|
476
|
-
reorderColumnSizes(fromIdx, toIdx);
|
|
477
|
-
return new MultiColumn(arrayMove([...this.columns], fromIdx, toIdx));
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
public findWithId(id: T): CollapsibleTree<T> {
|
|
481
|
-
const found = this.columns.find((c) => {
|
|
482
|
-
return c.inOrderIds.includes(id);
|
|
483
|
-
});
|
|
484
|
-
if (!found) {
|
|
485
|
-
Logger.log(
|
|
486
|
-
`Possible values: ${this.columns.map((c) => c.inOrderIds).join(", ")}`,
|
|
487
|
-
);
|
|
488
|
-
throw new Error(`Cell ${id} not found in any column`);
|
|
489
|
-
}
|
|
490
|
-
return found;
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
/**
|
|
494
|
-
* Transform the column containing the given cell id
|
|
495
|
-
* @param id the id of the cell to transform
|
|
496
|
-
* @param fn the function to transform the column
|
|
497
|
-
* @returns new MultiColumn with the transformed column
|
|
498
|
-
* If the column was not updated, we return the object.
|
|
499
|
-
*/
|
|
500
|
-
public transformWithCellId(
|
|
501
|
-
id: T,
|
|
502
|
-
fn: (tree: CollapsibleTree<T>) => CollapsibleTree<T>,
|
|
503
|
-
): MultiColumn<T> {
|
|
504
|
-
let didChange = false;
|
|
505
|
-
|
|
506
|
-
const columns = this.columns.map((c) => {
|
|
507
|
-
if (c.inOrderIds.includes(id)) {
|
|
508
|
-
const newColumn = fn(c);
|
|
509
|
-
if (c !== newColumn) {
|
|
510
|
-
didChange = true;
|
|
511
|
-
}
|
|
512
|
-
return newColumn;
|
|
513
|
-
}
|
|
514
|
-
return c;
|
|
515
|
-
});
|
|
516
|
-
|
|
517
|
-
// Avoid unnecessary re-renders
|
|
518
|
-
if (!didChange) {
|
|
519
|
-
return this;
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
return new MultiColumn(columns);
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
public setupCellExists(): boolean {
|
|
526
|
-
return this.columns.some((c) => c.topLevelIds.includes(SETUP_CELL_ID as T));
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
public insertId(id: T, col: CellColumnId, index: CellIndex): MultiColumn<T> {
|
|
530
|
-
return this.transform(col, (c) => c.insert(id, index));
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
public compact(): MultiColumn<T> {
|
|
534
|
-
// Don't compact if there's only one column
|
|
535
|
-
if (this.columns.length === 1) {
|
|
536
|
-
return this;
|
|
537
|
-
}
|
|
538
|
-
// If no need to compact, return the same tree
|
|
539
|
-
// to avoid unnecessary re-renders
|
|
540
|
-
const someEmpty = this.columns.some((c) => c.nodes.length === 0);
|
|
541
|
-
if (!someEmpty) {
|
|
542
|
-
return this;
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
return new MultiColumn(this.columns.filter((c) => c.nodes.length > 0));
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
/**
|
|
549
|
-
* Transform the column with the given column id
|
|
550
|
-
*/
|
|
551
|
-
public transform(
|
|
552
|
-
columnId: CellColumnId,
|
|
553
|
-
fn: (tree: CollapsibleTree<T>) => CollapsibleTree<T>,
|
|
554
|
-
): MultiColumn<T> {
|
|
555
|
-
// Use columnMap for faster lookup
|
|
556
|
-
const column = this._columnMap.get(columnId);
|
|
557
|
-
if (!column) {
|
|
558
|
-
Logger.warn(`Column ${columnId} not found`);
|
|
559
|
-
return this;
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
const newColumn = fn(column);
|
|
563
|
-
if (column === newColumn) {
|
|
564
|
-
return this;
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
const newColumns = this.columns.map((c) => {
|
|
568
|
-
if (c.id === columnId) {
|
|
569
|
-
return newColumn;
|
|
570
|
-
}
|
|
571
|
-
return c;
|
|
572
|
-
});
|
|
573
|
-
|
|
574
|
-
return new MultiColumn(newColumns);
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
/**
|
|
578
|
-
* Apply a transformation function to all columns
|
|
579
|
-
* @param fn The function to transform each column
|
|
580
|
-
* @returns A new MultiColumn if any changes were made, otherwise this
|
|
581
|
-
*/
|
|
582
|
-
public transformAll(
|
|
583
|
-
fn: (tree: CollapsibleTree<T>) => CollapsibleTree<T>,
|
|
584
|
-
): MultiColumn<T> {
|
|
585
|
-
let didChange = false;
|
|
586
|
-
|
|
587
|
-
// Apply the transformation to all columns
|
|
588
|
-
const newColumns = this.columns.map((column) => {
|
|
589
|
-
const newColumn = fn(column);
|
|
590
|
-
if (!column.equals(newColumn)) {
|
|
591
|
-
didChange = true;
|
|
592
|
-
}
|
|
593
|
-
return newColumn;
|
|
594
|
-
});
|
|
595
|
-
|
|
596
|
-
// Avoid unnecessary re-renders if nothing changed
|
|
597
|
-
if (!didChange) {
|
|
598
|
-
return this;
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
return new MultiColumn(newColumns);
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
export const visibleForTesting = {
|
|
606
|
-
reset: () => {
|
|
607
|
-
uniqueId = 0;
|
|
608
|
-
},
|
|
609
|
-
};
|