@marimo-team/islands 0.25.3-dev192 → 0.25.3-dev196
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-BBbOElk3.js → ConnectedDataExplorerComponent-BRuwLOIx.js} +9 -9
- package/dist/{ErrorBoundary-Dw6uRKVk.js → ErrorBoundary-C3Xv3yzV.js} +2 -2
- package/dist/{abnfDiagram-VCTEODGH-DhMT6Uhd.js → abnfDiagram-VCTEODGH-LlJTOOU_.js} +2 -2
- package/dist/{architectureDiagram-5GKGNRK7-BVbEiJLq.js → architectureDiagram-5GKGNRK7-9PQtGLLZ.js} +1 -1
- package/dist/{arrays-R6JsImiT.js → arrays-DA6KkLfq.js} +6 -12
- package/dist/assets/__vite-browser-external-BCJsx8QS.js +1 -0
- package/dist/assets/{worker-C6e9ERyd.js → worker-DodjWT_Z.js} +2 -2
- package/dist/{capabilities-CI4xUPsp.js → capabilities-CvWU-ey_.js} +1 -1
- package/dist/{chat-ui-BguxTQEL.js → chat-ui-Bl2euMBD.js} +93 -93
- package/dist/{chunk-IMKFNOWR-By5hSbJJ.js → chunk-IMKFNOWR-DcXv-BD5.js} +1 -1
- package/dist/{chunk-SHT3W25Y-DFrEYG54.js → chunk-SHT3W25Y-VwnELN23.js} +1 -1
- package/dist/{chunk-SVP7TREG-DFXGc9ew.js → chunk-SVP7TREG-DgQpI5Zi.js} +1 -1
- package/dist/{chunk-TICWLB2K-DZdNIpoZ.js → chunk-TICWLB2K-CTg6VTJT.js} +1 -1
- package/dist/{classDiagram-ZZMXUADV-Cgj_eEQA.js → classDiagram-ZZMXUADV-UzmcFrnW.js} +2 -2
- package/dist/{classDiagram-v2-VYDZK3BY-DLAUrSqj.js → classDiagram-v2-VYDZK3BY-DhjiI6IQ.js} +2 -2
- package/dist/{code-visibility-v_7ijLtx.js → code-visibility-BCcqmP58.js} +38 -42
- package/dist/{common-BUwWHpnK.js → common-DrNB2bY2.js} +849 -849
- package/dist/{cynefinDiagram-5FMLGOSQ-Cikf9AKa.js → cynefinDiagram-5FMLGOSQ-O4ShIbJX.js} +1 -1
- package/dist/{dagre-GXQ25YYZ-C5KEi5_l.js → dagre-GXQ25YYZ-dtHhhcPu.js} +1 -1
- package/dist/{data-grid-overlay-editor-4IZymnXZ.js → data-grid-overlay-editor-TGXV_Hf4.js} +1 -1
- package/dist/{diagram-S7CK7UJ4-BaoBTQjR.js → diagram-S7CK7UJ4-ppPYVZ6W.js} +1 -1
- package/dist/{diagram-UQ7AKVKN-DDufBsUA.js → diagram-UQ7AKVKN-CMZDf--k.js} +1 -1
- package/dist/{diagram-VX7I27RA-CY6RALJ8.js → diagram-VX7I27RA-CFvJdx-C.js} +1 -1
- package/dist/{diagram-Z3DM3KII-Bc-LHsD6.js → diagram-Z3DM3KII-Dt6_uFbv.js} +1 -1
- package/dist/{dropdown-menu-drDjxJyV.js → dropdown-menu-CeaOsjWZ.js} +2 -2
- package/dist/{ebnfDiagram-PWID7BFC-Cz0J-tcC.js → ebnfDiagram-PWID7BFC-DpRFrKK-.js} +2 -2
- package/dist/{erDiagram-RLTQ6QDP-DTXTDgFu.js → erDiagram-RLTQ6QDP-CqAU4b0u.js} +1 -1
- package/dist/{error-entries-CZ1NQ6Kv.js → error-entries-CYGwr5-O.js} +5596 -6012
- package/dist/feedback-button-PTiPn2GP.js +374 -0
- package/dist/{flowDiagram-HODETNUW-RbwWf2zc.js → flowDiagram-HODETNUW-i8-Gw8HO.js} +2 -2
- package/dist/{formats-DbXnijtH.js → formats-BtoHHy42.js} +1 -1
- package/dist/{glide-data-editor-DU8_EDG5.js → glide-data-editor-BSyexjkB.js} +5 -5
- package/dist/{globals-BeBDOk22.js → globals-B7gkJAxm.js} +2 -2
- package/dist/{html-to-image-D_4_hQ8U.js → html-to-image-Cy5UDpQJ.js} +56 -56
- package/dist/{infoDiagram-27XIBGKW-DlmzZjmI.js → infoDiagram-27XIBGKW-DpfOKdLL.js} +1 -1
- package/dist/{ishikawaDiagram-5VMMS53U-EN3TYP_q.js → ishikawaDiagram-5VMMS53U-Dg7mrMG7.js} +1 -1
- package/dist/{kanban-definition-UXKFOSKX-CK_-Ru7Q.js → kanban-definition-UXKFOSKX-QB6e3Jrj.js} +1 -1
- package/dist/main.js +5875 -5876
- package/dist/{mermaid-DU9ZRmkG.js → mermaid-CeWG4Egr.js} +48 -48
- package/dist/{mindmap-definition-YA3MSWOX-C-hqgUvF.js → mindmap-definition-YA3MSWOX-BTz3uTGe.js} +1 -1
- package/dist/{number-overlay-editor-BoIkyh6H.js → number-overlay-editor-Bb91yQ46.js} +1 -1
- package/dist/{once-eG8Vmvjd.js → once-DWGO2mQ2.js} +1 -1
- package/dist/{pegDiagram-XKGWAZYB-BiMejYPB.js → pegDiagram-XKGWAZYB-3ZIEa3mB.js} +2 -2
- package/dist/{pieDiagram-E7YTZNPT-B1zCZ1eq.js → pieDiagram-E7YTZNPT-BJAlk046.js} +1 -1
- package/dist/{process-output-DxSCHBWp.js → process-output-q9MTAIMX.js} +2 -2
- package/dist/{railroadDiagram-O6MQD6OU-BLcfaASl.js → railroadDiagram-O6MQD6OU-Dn3bsSRa.js} +2 -2
- package/dist/{requirementDiagram-BXWQKSXE-DtBOf52-.js → requirementDiagram-BXWQKSXE-wcOmQQCL.js} +1 -1
- package/dist/{reveal-component-5M3a2rpj.js → reveal-component-vTdjiyDv.js} +17 -17
- package/dist/{sequenceDiagram-WJ2MYXX4-DIwF19_n.js → sequenceDiagram-WJ2MYXX4-DSh_qxoF.js} +1 -1
- package/dist/{spec-DZDnnlgT.js → spec-C7Fos-If.js} +1 -1
- package/dist/{stateDiagram-D77RDMKH-sWk1AdYL.js → stateDiagram-D77RDMKH-DH_H8NMH.js} +2 -2
- package/dist/{stateDiagram-v2-MP3YSRHH-CiNo-OI7.js → stateDiagram-v2-MP3YSRHH-D_JM7MEK.js} +2 -2
- package/dist/style.css +1 -1
- package/dist/{swimlanes-42K2YHIH-Bj3hiOY5.js → swimlanes-42K2YHIH-CfoTJO-9.js} +1 -1
- package/dist/{swimlanesDiagram-VR7AAH4N-BzEo2r_N.js → swimlanesDiagram-VR7AAH4N-Z_QhvK0G.js} +2 -2
- package/dist/{swiper-component-BkCm-kLg.js → swiper-component-D2geL1Ku.js} +1 -1
- package/dist/{timeline-definition-24CTP7MA-CrPQk42H.js → timeline-definition-24CTP7MA-BFtdScaL.js} +1 -1
- package/dist/{toDate-Cu2pIZcC.js → toDate-CQAFSwos.js} +24 -24
- package/dist/{useAsyncData-DEhLdRIu.js → useAsyncData-BeyZyjso.js} +1 -1
- package/dist/{useDeepCompareMemoize-RjZGVhBV.js → useDeepCompareMemoize-DOQgae0W.js} +1 -1
- package/dist/{useIframeCapabilities-CSdWn6NP.js → useIframeCapabilities-DNvaj9wM.js} +1 -1
- package/dist/{useLifecycle-DtcB2s1z.js → useLifecycle-n2lnCFS8.js} +1 -1
- package/dist/{useTheme-CoEtr4mK.js → useTheme-CY777LXN.js} +303 -99
- package/dist/{vega-component-BhjTrP9-.js → vega-component-0nQzA-I8.js} +6 -6
- package/dist/{vennDiagram-4TSXK5OY-BaYMoIpj.js → vennDiagram-4TSXK5OY-BXY8IGCA.js} +1 -1
- package/dist/{wardleyDiagram-VM6X3IG4-DjHeuj8p.js → wardleyDiagram-VM6X3IG4-D0tFe6eC.js} +1 -1
- package/dist/{xychartDiagram-S5SC5T6Z-Bc_hcuXS.js → xychartDiagram-S5SC5T6Z-Dx8wFM-y.js} +1 -1
- package/package.json +1 -1
- package/src/__mocks__/notebook.ts +0 -1
- package/src/__mocks__/partial.ts +26 -0
- package/src/__tests__/main.test.tsx +24 -1
- package/src/components/app-config/app-config-form.tsx +5 -9
- package/src/components/editor/__tests__/data-attributes.test.tsx +0 -1
- package/src/components/editor/__tests__/output-persistence.test.tsx +0 -1
- package/src/components/editor/actions/useCellActionButton.tsx +6 -5
- package/src/components/editor/actions/useConfigActions.tsx +3 -4
- package/src/components/editor/actions/useNotebookActions.tsx +5 -4
- package/src/components/editor/cell/code/cell-editor.tsx +13 -6
- package/src/components/editor/cell/collapse.tsx +6 -1
- package/src/components/editor/chrome/components/feedback-button.tsx +1 -7
- package/src/components/editor/controls/Controls.tsx +1 -1
- package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +8 -6
- package/src/components/editor/navigation/__tests__/navigation.test.ts +15 -15
- package/src/components/editor/navigation/__tests__/selection.test.ts +44 -21
- package/src/components/editor/navigation/focus-utils.ts +19 -0
- package/src/components/editor/navigation/multi-cell-action-toolbar.tsx +6 -13
- package/src/components/editor/navigation/navigation.ts +52 -43
- package/src/components/editor/navigation/selection.ts +30 -29
- package/src/components/editor/notebook-cell.tsx +10 -18
- package/src/components/editor/renderers/cell-array.tsx +6 -5
- package/src/components/editor/renderers/slides-layout/__tests__/plugin.test.ts +0 -1
- package/src/components/editor/renderers/vertical-layout/vertical-layout.tsx +1 -1
- package/src/components/pages/edit-page.tsx +0 -13
- package/src/components/scratchpad/scratchpad.tsx +0 -1
- package/src/components/slides/reveal-component.tsx +1 -1
- package/src/components/slides/slide-cell-view.tsx +0 -1
- package/src/components/sort/SortableCellsProvider.tsx +6 -5
- package/src/core/MarimoApp.tsx +1 -1
- package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +0 -37
- package/src/core/cells/__tests__/actions.test.ts +190 -80
- package/src/core/cells/__tests__/arrange.test.ts +263 -0
- package/src/core/cells/__tests__/cells.test.ts +11 -1101
- package/src/core/cells/__tests__/document.test.ts +3 -32
- package/src/core/cells/__tests__/editor.test.ts +57 -0
- package/src/core/cells/__tests__/harness.ts +82 -0
- package/src/core/cells/__tests__/layout.test.ts +189 -0
- package/src/core/cells/__tests__/notebook-outline.test.ts +7 -1
- package/src/core/cells/__tests__/session.test.ts +329 -174
- package/src/core/cells/__tests__/utils.test.ts +0 -1
- package/src/core/cells/actions.ts +247 -82
- package/src/core/cells/arrange.ts +341 -0
- package/src/core/cells/cells.ts +27 -516
- package/src/core/cells/document.ts +6 -5
- package/src/core/cells/edit.ts +30 -0
- package/src/core/cells/editor.ts +82 -0
- package/src/core/cells/layout.ts +152 -0
- package/src/core/cells/session.ts +75 -52
- package/src/core/cells/types.ts +0 -5
- package/src/core/cells/utils.ts +6 -4
- package/src/core/config/__tests__/app-config.test.ts +64 -0
- package/src/core/config/__tests__/config-schema.test.ts +1 -1
- package/src/core/config/config-schema.ts +6 -2
- package/src/core/config/config.ts +26 -12
- package/src/core/islands/bootstrap.ts +0 -1
- package/src/core/kernel/handlers.ts +1 -13
- package/src/core/meta/__tests__/code-visibility.test.tsx +78 -70
- package/src/core/meta/code-visibility.ts +18 -9
- package/src/core/notebook/__tests__/operations.test.ts +51 -0
- package/src/core/notebook/document/__tests__/codemirror.test.ts +33 -7
- package/src/core/notebook/index.ts +2 -1
- package/src/core/notebook/operations.ts +30 -0
- package/src/core/saving/__tests__/state.test.ts +111 -0
- package/src/core/saving/save-component.tsx +19 -33
- package/src/core/saving/state.ts +38 -39
- package/src/core/saving/useAutoSave.ts +8 -35
- package/src/core/session/__tests__/backend.test.ts +1 -0
- package/src/core/session/__tests__/useNotebookReplica.hook.test.tsx +1 -0
- package/src/core/session/active-notebook.ts +11 -0
- package/src/core/session/backend.ts +17 -19
- package/src/core/session/embedded.ts +7 -0
- package/src/core/session/useNotebookReplica.ts +3 -1
- package/src/core/session/useNotebookSession.tsx +7 -8
- package/src/core/transports/__tests__/static.test.ts +139 -8
- package/src/core/transports/static.ts +98 -16
- package/src/core/wasm/__tests__/bridge.test.ts +1 -0
- package/src/mount.tsx +19 -10
- package/src/stories/cell.stories.tsx +0 -2
- package/src/stories/layout/vertical/one-column.stories.tsx +0 -2
- package/src/utils/__tests__/id-tree.test.ts +20 -1548
- package/src/utils/id-tree.tsx +12 -537
- package/dist/assets/__vite-browser-external-C8gQho07.js +0 -1
- package/dist/feedback-button-MpeoIn1D.js +0 -379
- package/src/components/editor/navigation/__tests__/state.test.ts +0 -81
- package/src/components/editor/navigation/state.ts +0 -65
package/src/utils/id-tree.tsx
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { Memoize } from "typescript-memoize";
|
|
4
4
|
import { reorderColumnSizes } from "@/components/editor/columns/storage";
|
|
5
5
|
import { SETUP_CELL_ID } from "@/core/cells/ids";
|
|
6
|
-
import { arrayDelete, arrayInsert,
|
|
6
|
+
import { arrayDelete, arrayInsert, arrayMove } from "./arrays";
|
|
7
7
|
import { Logger } from "./Logger";
|
|
8
8
|
|
|
9
9
|
/**
|
|
@@ -25,79 +25,12 @@ export type CellIndex = number & { __brand?: "CellIndex" };
|
|
|
25
25
|
*/
|
|
26
26
|
export class TreeNode<T> {
|
|
27
27
|
public value: T;
|
|
28
|
-
public isCollapsed: boolean;
|
|
29
|
-
public children: TreeNode<T>[];
|
|
30
28
|
|
|
31
|
-
public constructor(value: T
|
|
29
|
+
public constructor(value: T) {
|
|
32
30
|
this.value = value;
|
|
33
|
-
this.isCollapsed = isCollapsed;
|
|
34
|
-
this.children = children;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Recursively count the number of nodes in the tree
|
|
39
|
-
*/
|
|
40
|
-
@Memoize()
|
|
41
|
-
public geDescendantCount(): number {
|
|
42
|
-
let count = 0;
|
|
43
|
-
const stack = [...this.children];
|
|
44
|
-
|
|
45
|
-
while (stack.length > 0) {
|
|
46
|
-
// oxlint-disable-next-line typescript/no-non-null-assertion
|
|
47
|
-
const node = stack.pop()!;
|
|
48
|
-
count++;
|
|
49
|
-
|
|
50
|
-
// Add children to stack
|
|
51
|
-
for (let i = node.children.length - 1; i >= 0; i--) {
|
|
52
|
-
stack.push(node.children[i]);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return count;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
@Memoize()
|
|
60
|
-
public getDescendants(): T[] {
|
|
61
|
-
const result: T[] = [];
|
|
62
|
-
const stack = [...this.children];
|
|
63
|
-
|
|
64
|
-
while (stack.length > 0) {
|
|
65
|
-
// oxlint-disable-next-line typescript/no-non-null-assertion
|
|
66
|
-
const node = stack.pop()!;
|
|
67
|
-
result.push(node.value);
|
|
68
|
-
|
|
69
|
-
// Add children to stack in reverse order to maintain correct traversal order
|
|
70
|
-
for (let i = node.children.length - 1; i >= 0; i--) {
|
|
71
|
-
stack.push(node.children[i]);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return result;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
@Memoize()
|
|
79
|
-
public get inOrderIds(): T[] {
|
|
80
|
-
const result: T[] = [];
|
|
81
|
-
|
|
82
|
-
// Use depth-first traversal to preserve logical document order
|
|
83
|
-
const traverse = (node: TreeNode<T>) => {
|
|
84
|
-
result.push(node.value);
|
|
85
|
-
for (const child of node.children) {
|
|
86
|
-
traverse(child);
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
for (const child of this.children) {
|
|
91
|
-
traverse(child);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return result;
|
|
95
31
|
}
|
|
96
32
|
|
|
97
33
|
public toString(): string {
|
|
98
|
-
if (this.isCollapsed) {
|
|
99
|
-
return `${this.value} (collapsed)`;
|
|
100
|
-
}
|
|
101
34
|
return String(this.value);
|
|
102
35
|
}
|
|
103
36
|
|
|
@@ -117,52 +50,14 @@ export class CollapsibleTree<T> {
|
|
|
117
50
|
this.id = id;
|
|
118
51
|
}
|
|
119
52
|
|
|
120
|
-
|
|
121
|
-
|
|
53
|
+
/** A column of `ids`, keeping `id` when a column is rebuilt. */
|
|
54
|
+
public static from<T>(ids: T[], id?: CellColumnId): CollapsibleTree<T> {
|
|
122
55
|
return new CollapsibleTree(
|
|
123
|
-
ids.map((id) => new TreeNode(id
|
|
124
|
-
id,
|
|
56
|
+
ids.map((id) => new TreeNode(id)),
|
|
57
|
+
id ?? (`tree_${uniqueId++}` as CellColumnId),
|
|
125
58
|
);
|
|
126
59
|
}
|
|
127
60
|
|
|
128
|
-
/**
|
|
129
|
-
* Create a new tree from ids, preserving structure from previous tree if possible
|
|
130
|
-
*/
|
|
131
|
-
public static fromWithPreviousShape<T>(
|
|
132
|
-
ids: T[],
|
|
133
|
-
previousTree?: CollapsibleTree<T>,
|
|
134
|
-
): CollapsibleTree<T> {
|
|
135
|
-
if (!previousTree) {
|
|
136
|
-
return CollapsibleTree.from(ids);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Reuse the previous tree's id if possible
|
|
140
|
-
const id = previousTree.id;
|
|
141
|
-
|
|
142
|
-
// Create new tree with nothing collapsed
|
|
143
|
-
let newTree = new CollapsibleTree(
|
|
144
|
-
ids.map((id) => new TreeNode(id, false, [])),
|
|
145
|
-
id,
|
|
146
|
-
);
|
|
147
|
-
|
|
148
|
-
// Collapse nodes that were collapsed in the previous tree
|
|
149
|
-
for (const id of ids) {
|
|
150
|
-
if (previousTree.isCollapsed(id)) {
|
|
151
|
-
const children = previousTree._nodeMap.get(id)?.children ?? [];
|
|
152
|
-
// Find the first child that is also in the new tree, going backwards
|
|
153
|
-
for (let i = children.length - 1; i >= 0; i--) {
|
|
154
|
-
const child = children[i];
|
|
155
|
-
if (newTree._nodeMap.has(child.value)) {
|
|
156
|
-
newTree = newTree.collapse(id, child.value);
|
|
157
|
-
break;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
return newTree;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
61
|
public withNodes(nodes: TreeNode<T>[]): CollapsibleTree<T> {
|
|
167
62
|
return new CollapsibleTree(nodes, this.id);
|
|
168
63
|
}
|
|
@@ -172,9 +67,8 @@ export class CollapsibleTree<T> {
|
|
|
172
67
|
return this.nodes.map((n) => n.value);
|
|
173
68
|
}
|
|
174
69
|
|
|
175
|
-
@Memoize()
|
|
176
70
|
public get inOrderIds(): T[] {
|
|
177
|
-
return this.
|
|
71
|
+
return this.topLevelIds;
|
|
178
72
|
}
|
|
179
73
|
|
|
180
74
|
@Memoize()
|
|
@@ -195,38 +89,6 @@ export class CollapsibleTree<T> {
|
|
|
195
89
|
return result;
|
|
196
90
|
}
|
|
197
91
|
|
|
198
|
-
/**
|
|
199
|
-
* Get the descendants of the given node
|
|
200
|
-
*
|
|
201
|
-
* Only works for the top-level nodes
|
|
202
|
-
*/
|
|
203
|
-
public getDescendants(id: T): T[] {
|
|
204
|
-
const node = this._nodeMap.get(id);
|
|
205
|
-
if (!node) {
|
|
206
|
-
Logger.warn(
|
|
207
|
-
`Node ${id} not found in tree. Valid ids: ${this.topLevelIds}`,
|
|
208
|
-
);
|
|
209
|
-
return [];
|
|
210
|
-
}
|
|
211
|
-
return node.getDescendants();
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* Check if the given node is collapsed
|
|
216
|
-
*
|
|
217
|
-
* Only works for the top-level nodes
|
|
218
|
-
*/
|
|
219
|
-
public isCollapsed(id: T): boolean {
|
|
220
|
-
const node = this._nodeMap.get(id);
|
|
221
|
-
if (!node) {
|
|
222
|
-
Logger.warn(
|
|
223
|
-
`Node ${id} not found in tree. Valid ids: ${this.topLevelIds}`,
|
|
224
|
-
);
|
|
225
|
-
return false;
|
|
226
|
-
}
|
|
227
|
-
return node.isCollapsed;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
92
|
/**
|
|
231
93
|
* Get the index of the given node, or throw
|
|
232
94
|
*/
|
|
@@ -248,173 +110,6 @@ export class CollapsibleTree<T> {
|
|
|
248
110
|
return this.nodes.slice(start, end).map((n) => n.value);
|
|
249
111
|
}
|
|
250
112
|
|
|
251
|
-
/**
|
|
252
|
-
* Move the given node to the front
|
|
253
|
-
*/
|
|
254
|
-
public moveToFront(id: T): CollapsibleTree<T> {
|
|
255
|
-
const index = this.indexOfOrThrow(id);
|
|
256
|
-
return this.withNodes(arrayMove(this.nodes, index, 0));
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Move the given node to the back
|
|
261
|
-
*/
|
|
262
|
-
public moveToBack(id: T): CollapsibleTree<T> {
|
|
263
|
-
const index = this.indexOfOrThrow(id);
|
|
264
|
-
return this.withNodes(arrayMove(this.nodes, index, this.nodes.length - 1));
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/**
|
|
268
|
-
* Collapse everything past the given node @param id
|
|
269
|
-
* until @param until or the end of the tree
|
|
270
|
-
*/
|
|
271
|
-
public collapse(id: T, until: T | undefined): CollapsibleTree<T> {
|
|
272
|
-
const nodeIndex = this.nodes.findIndex((n) => n.value === id);
|
|
273
|
-
if (nodeIndex === -1) {
|
|
274
|
-
throw new Error(
|
|
275
|
-
`Node ${id} not found in tree. Valid ids: ${this.topLevelIds}`,
|
|
276
|
-
);
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
const untilIndex =
|
|
280
|
-
until === undefined
|
|
281
|
-
? this.nodes.length
|
|
282
|
-
: this.nodes.findIndex((n) => n.value === until);
|
|
283
|
-
|
|
284
|
-
if (untilIndex === -1) {
|
|
285
|
-
throw new Error(`Node ${until} not found in tree`);
|
|
286
|
-
}
|
|
287
|
-
if (untilIndex < nodeIndex) {
|
|
288
|
-
throw new Error(`Node ${until} is before node ${id}`);
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
const nodes = [...this.nodes];
|
|
292
|
-
const node = nodes[nodeIndex];
|
|
293
|
-
if (node.isCollapsed) {
|
|
294
|
-
throw new Error(`Node ${id} is already collapsed`);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// Fold the next nodes into the current node
|
|
298
|
-
const children = nodes.splice(nodeIndex + 1, untilIndex - nodeIndex);
|
|
299
|
-
nodes[nodeIndex] = new TreeNode(node.value, true, children);
|
|
300
|
-
|
|
301
|
-
return this.withNodes(nodes);
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
/**
|
|
305
|
-
* Collapse all nodes in the tree, including nested ones
|
|
306
|
-
*
|
|
307
|
-
* Only works for the top-level nodes
|
|
308
|
-
* Does not collapse the children of already collapsed nodes
|
|
309
|
-
*/
|
|
310
|
-
public collapseAll(
|
|
311
|
-
collapseRanges: ({ id: T; until: T | undefined } | null)[],
|
|
312
|
-
): CollapsibleTree<T> {
|
|
313
|
-
const nodes = [...this.nodes];
|
|
314
|
-
if (collapseRanges.length === 0) {
|
|
315
|
-
throw new Error("No collapse ranges provided");
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
if (collapseRanges.length !== nodes.length) {
|
|
319
|
-
throw new Error(
|
|
320
|
-
`Collapse ranges length ${collapseRanges.length} does not match tree length ${nodes.length}`,
|
|
321
|
-
);
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
// Start from the end of the list and collapse nodes children first
|
|
325
|
-
let nodeIndex = nodes.length - 1;
|
|
326
|
-
while (nodeIndex >= 0) {
|
|
327
|
-
const node = nodes[nodeIndex];
|
|
328
|
-
const range = collapseRanges[nodeIndex];
|
|
329
|
-
|
|
330
|
-
if (!node.isCollapsed && range) {
|
|
331
|
-
const { id, until } = range;
|
|
332
|
-
if (id !== node.value) {
|
|
333
|
-
throw new Error(
|
|
334
|
-
`Node ${node.value} does not match collapse range id ${id}`,
|
|
335
|
-
);
|
|
336
|
-
}
|
|
337
|
-
const untilIndex =
|
|
338
|
-
until === undefined
|
|
339
|
-
? nodes.length
|
|
340
|
-
: nodes.findIndex((n) => n.value === until);
|
|
341
|
-
|
|
342
|
-
if (untilIndex === -1) {
|
|
343
|
-
throw new Error(`Node ${until} not found in tree`);
|
|
344
|
-
}
|
|
345
|
-
if (untilIndex < nodeIndex) {
|
|
346
|
-
throw new Error(`Node ${until} is before node ${id}`);
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
// Fold the next nodes into the current node
|
|
350
|
-
const children = nodes.splice(nodeIndex + 1, untilIndex - nodeIndex);
|
|
351
|
-
nodes[nodeIndex] = new TreeNode(node.value, true, children);
|
|
352
|
-
nodeIndex--;
|
|
353
|
-
} else {
|
|
354
|
-
// Move to the next node
|
|
355
|
-
nodeIndex--;
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
return this.withNodes(nodes);
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
/**
|
|
363
|
-
* Expand a node and all of its children.
|
|
364
|
-
* If the node is already expanded, returns the same tree (no-op).
|
|
365
|
-
*/
|
|
366
|
-
public expand(id: T): CollapsibleTree<T> {
|
|
367
|
-
const nodeIndex = this.nodes.findIndex((n) => n.value === id);
|
|
368
|
-
if (nodeIndex === -1) {
|
|
369
|
-
throw new Error(
|
|
370
|
-
`Node ${id} not found in tree. Valid ids: ${this.topLevelIds}`,
|
|
371
|
-
);
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
let nodes = [...this.nodes];
|
|
375
|
-
const node = nodes[nodeIndex];
|
|
376
|
-
if (!node.isCollapsed) {
|
|
377
|
-
// Already expanded, no-op
|
|
378
|
-
return this;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
nodes[nodeIndex] = new TreeNode(node.value, false, []);
|
|
382
|
-
nodes = arrayInsertMany(nodes, nodeIndex + 1, node.children);
|
|
383
|
-
|
|
384
|
-
return this.withNodes(nodes);
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
/**
|
|
388
|
-
* Expand all collapsed nodes in the tree, including nested ones
|
|
389
|
-
*/
|
|
390
|
-
public expandAll(): CollapsibleTree<T> {
|
|
391
|
-
let nodes = [...this.nodes];
|
|
392
|
-
let nodeIndex = 0;
|
|
393
|
-
|
|
394
|
-
while (nodeIndex < nodes.length) {
|
|
395
|
-
const node = nodes[nodeIndex];
|
|
396
|
-
if (node.isCollapsed) {
|
|
397
|
-
// Replace the collapsed node with an expanded one
|
|
398
|
-
nodes[nodeIndex] = new TreeNode(node.value, false, []);
|
|
399
|
-
// Add the children of the collapsed node to the list
|
|
400
|
-
nodes = arrayInsertMany(nodes, nodeIndex + 1, node.children);
|
|
401
|
-
nodeIndex++;
|
|
402
|
-
} else {
|
|
403
|
-
// Move to the next node
|
|
404
|
-
nodeIndex++;
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
return this.withNodes(nodes);
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
/**
|
|
412
|
-
* Move a node from one index to another
|
|
413
|
-
*/
|
|
414
|
-
public move(fromIdx: number, toIdx: number): CollapsibleTree<T> {
|
|
415
|
-
return this.withNodes(arrayMove(this.nodes, fromIdx, toIdx));
|
|
416
|
-
}
|
|
417
|
-
|
|
418
113
|
/**
|
|
419
114
|
* Get the node at the given index
|
|
420
115
|
*/
|
|
@@ -473,9 +168,7 @@ export class CollapsibleTree<T> {
|
|
|
473
168
|
* Insert a node at the given index
|
|
474
169
|
*/
|
|
475
170
|
public insert(id: T, index: number): CollapsibleTree<T> {
|
|
476
|
-
return this.withNodes(
|
|
477
|
-
arrayInsert(this.nodes, index, new TreeNode(id, false, [])),
|
|
478
|
-
);
|
|
171
|
+
return this.withNodes(arrayInsert(this.nodes, index, new TreeNode(id)));
|
|
479
172
|
}
|
|
480
173
|
|
|
481
174
|
/**
|
|
@@ -492,14 +185,9 @@ export class CollapsibleTree<T> {
|
|
|
492
185
|
return this.insert(id, 0);
|
|
493
186
|
}
|
|
494
187
|
|
|
495
|
-
/**
|
|
496
|
-
* Delete a node, expand if it was collapsed
|
|
497
|
-
*/
|
|
498
188
|
public deleteAtIndex(idx: number): CollapsibleTree<T> {
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
const tree = this.expand(id);
|
|
502
|
-
return tree.withNodes(arrayDelete(tree.nodes, idx));
|
|
189
|
+
this.atOrThrow(idx);
|
|
190
|
+
return this.withNodes(arrayDelete(this.nodes, idx));
|
|
503
191
|
}
|
|
504
192
|
|
|
505
193
|
public delete(id: T): CollapsibleTree<T> {
|
|
@@ -507,49 +195,6 @@ export class CollapsibleTree<T> {
|
|
|
507
195
|
return this.deleteAtIndex(index);
|
|
508
196
|
}
|
|
509
197
|
|
|
510
|
-
/**
|
|
511
|
-
* Get the number of nodes in the tree, not-including the given node
|
|
512
|
-
*/
|
|
513
|
-
public getCount(id: T): number {
|
|
514
|
-
return this._nodeMap.get(id)?.geDescendantCount() ?? 0;
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
/**
|
|
518
|
-
* Find and expand the node and all of its children
|
|
519
|
-
*/
|
|
520
|
-
public findAndExpandDeep(id: T): CollapsibleTree<T> {
|
|
521
|
-
const found = this.find(id);
|
|
522
|
-
if (found.length === 0) {
|
|
523
|
-
return this;
|
|
524
|
-
}
|
|
525
|
-
return found.reduce<CollapsibleTree<T>>(
|
|
526
|
-
(acc, node) => acc.expand(node),
|
|
527
|
-
this,
|
|
528
|
-
);
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
/**
|
|
532
|
-
* Find a node, returning the path to it
|
|
533
|
-
* With the last element being the node itself
|
|
534
|
-
*/
|
|
535
|
-
public find(id: T): T[] {
|
|
536
|
-
// We need to recursively find the node
|
|
537
|
-
function findNode(nodes: TreeNode<T>[], path: T[]): T[] {
|
|
538
|
-
for (const node of nodes) {
|
|
539
|
-
if (node.value === id) {
|
|
540
|
-
return [...path, id];
|
|
541
|
-
}
|
|
542
|
-
const result = findNode(node.children, [...path, node.value]);
|
|
543
|
-
if (result.length > 0) {
|
|
544
|
-
return result;
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
return [];
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
return findNode(this.nodes, []);
|
|
551
|
-
}
|
|
552
|
-
|
|
553
198
|
/**
|
|
554
199
|
* Split the tree into two trees
|
|
555
200
|
* @param id the id of the node to split at
|
|
@@ -578,18 +223,7 @@ export class CollapsibleTree<T> {
|
|
|
578
223
|
}
|
|
579
224
|
|
|
580
225
|
public toString(): string {
|
|
581
|
-
|
|
582
|
-
let result = "";
|
|
583
|
-
const asString = (nodes: TreeNode<T>[]) => {
|
|
584
|
-
for (const node of nodes) {
|
|
585
|
-
result += `${" ".repeat(depth * 2)}${node.toString()}\n`;
|
|
586
|
-
depth += 1;
|
|
587
|
-
asString(node.children);
|
|
588
|
-
depth -= 1;
|
|
589
|
-
}
|
|
590
|
-
};
|
|
591
|
-
asString(this.nodes);
|
|
592
|
-
return result;
|
|
226
|
+
return this.nodes.map((node) => `${node.toString()}\n`).join("");
|
|
593
227
|
}
|
|
594
228
|
}
|
|
595
229
|
|
|
@@ -642,7 +276,7 @@ export class MultiColumn<T> {
|
|
|
642
276
|
}
|
|
643
277
|
|
|
644
278
|
const newColumns = splitIds.map((ids, idx) =>
|
|
645
|
-
CollapsibleTree.
|
|
279
|
+
CollapsibleTree.from(ids, previousColumns[idx].id),
|
|
646
280
|
);
|
|
647
281
|
|
|
648
282
|
return new MultiColumn(newColumns);
|
|
@@ -814,117 +448,6 @@ export class MultiColumn<T> {
|
|
|
814
448
|
return new MultiColumn([firstColumn.withNodes(nodes)]);
|
|
815
449
|
}
|
|
816
450
|
|
|
817
|
-
public moveWithinColumn(
|
|
818
|
-
col: CellColumnId,
|
|
819
|
-
fromIdx: CellIndex,
|
|
820
|
-
toIdx: CellIndex,
|
|
821
|
-
): MultiColumn<T> {
|
|
822
|
-
return this.transform(col, (c) => {
|
|
823
|
-
return c.move(fromIdx, toIdx);
|
|
824
|
-
});
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
public moveAcrossColumns(
|
|
828
|
-
fromCol: CellColumnId,
|
|
829
|
-
fromId: T,
|
|
830
|
-
toCol: CellColumnId,
|
|
831
|
-
toId: T | undefined,
|
|
832
|
-
): MultiColumn<T> {
|
|
833
|
-
// Find indices once to avoid repeated lookups
|
|
834
|
-
const fromColumnIndex = this.indexOfOrThrow(fromCol);
|
|
835
|
-
const toColumnIndex = this.indexOfOrThrow(toCol);
|
|
836
|
-
|
|
837
|
-
// Only copy the columns we're modifying
|
|
838
|
-
const newColumns = [...this.columns];
|
|
839
|
-
const fromColumn = newColumns[fromColumnIndex];
|
|
840
|
-
|
|
841
|
-
// Find the node once
|
|
842
|
-
const nodeIndex = fromColumn.nodes.findIndex((n) => n.value === fromId);
|
|
843
|
-
if (nodeIndex === -1) {
|
|
844
|
-
throw new Error(`Node ${fromId} not found in column ${fromCol}`);
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
const node = fromColumn.nodes[nodeIndex];
|
|
848
|
-
|
|
849
|
-
// Create new columns with the changes
|
|
850
|
-
newColumns[fromColumnIndex] = fromColumn.withNodes(
|
|
851
|
-
fromColumn.nodes.filter((_, i) => i !== nodeIndex),
|
|
852
|
-
);
|
|
853
|
-
|
|
854
|
-
const toColumn = newColumns[toColumnIndex];
|
|
855
|
-
if (toId) {
|
|
856
|
-
const toIndex = toColumn.indexOfOrThrow(toId);
|
|
857
|
-
newColumns[toColumnIndex] = toColumn.withNodes(
|
|
858
|
-
arrayInsert(toColumn.nodes, toIndex, node),
|
|
859
|
-
);
|
|
860
|
-
} else {
|
|
861
|
-
newColumns[toColumnIndex] = toColumn.withNodes([node, ...toColumn.nodes]);
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
return new MultiColumn(newColumns);
|
|
865
|
-
}
|
|
866
|
-
|
|
867
|
-
/**
|
|
868
|
-
* Move multiple cells to be immediately before (or after) a target cell.
|
|
869
|
-
* Cells are inserted in their original order.
|
|
870
|
-
*
|
|
871
|
-
* @throws Error if any cellId is not found in any column.
|
|
872
|
-
* If targetId is among the moved cells, cells are inserted at the end of the target column.
|
|
873
|
-
*/
|
|
874
|
-
public moveCellsRelativeTo(
|
|
875
|
-
cellIds: T[],
|
|
876
|
-
targetId: T,
|
|
877
|
-
position: "before" | "after",
|
|
878
|
-
): MultiColumn<T> {
|
|
879
|
-
if (cellIds.length === 0) {
|
|
880
|
-
return this;
|
|
881
|
-
}
|
|
882
|
-
|
|
883
|
-
const cellIdSet = new Set(cellIds);
|
|
884
|
-
const targetColumn = this.findWithId(targetId);
|
|
885
|
-
const targetColIndex = this.indexOfOrThrow(targetColumn.id);
|
|
886
|
-
|
|
887
|
-
// Collect nodes to move
|
|
888
|
-
const nodesToMove: TreeNode<T>[] = [];
|
|
889
|
-
for (const id of cellIds) {
|
|
890
|
-
const col = this.findWithId(id);
|
|
891
|
-
const node = col.nodes.find((n) => n.value === id);
|
|
892
|
-
if (!node) {
|
|
893
|
-
throw new Error(`Node ${id} not found in column ${col.id}`);
|
|
894
|
-
}
|
|
895
|
-
nodesToMove.push(node);
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
// Remove moved cells from all columns
|
|
899
|
-
const columnsWithRemovals = this.columns.map((col) =>
|
|
900
|
-
col.withNodes(col.nodes.filter((n) => !cellIdSet.has(n.value))),
|
|
901
|
-
);
|
|
902
|
-
|
|
903
|
-
// Find target index in the cleaned column
|
|
904
|
-
const cleanedTargetCol = columnsWithRemovals[targetColIndex];
|
|
905
|
-
let insertIndex = cleanedTargetCol.nodes.findIndex(
|
|
906
|
-
(n) => n.value === targetId,
|
|
907
|
-
);
|
|
908
|
-
|
|
909
|
-
// If target was one of the moved cells, insert at end
|
|
910
|
-
if (insertIndex === -1) {
|
|
911
|
-
insertIndex = cleanedTargetCol.nodes.length;
|
|
912
|
-
} else if (position === "after") {
|
|
913
|
-
insertIndex += 1;
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
// Insert all moved nodes at target position
|
|
917
|
-
const newTargetNodes = arrayInsertMany(
|
|
918
|
-
cleanedTargetCol.nodes,
|
|
919
|
-
insertIndex,
|
|
920
|
-
nodesToMove,
|
|
921
|
-
);
|
|
922
|
-
columnsWithRemovals[targetColIndex] =
|
|
923
|
-
cleanedTargetCol.withNodes(newTargetNodes);
|
|
924
|
-
|
|
925
|
-
return new MultiColumn(columnsWithRemovals);
|
|
926
|
-
}
|
|
927
|
-
|
|
928
451
|
public indexOfOrThrow(id: CellColumnId): number {
|
|
929
452
|
const index = this.columns.findIndex((c) => c.id === id);
|
|
930
453
|
if (index === -1) {
|
|
@@ -954,22 +477,6 @@ export class MultiColumn<T> {
|
|
|
954
477
|
return new MultiColumn(arrayMove([...this.columns], fromIdx, toIdx));
|
|
955
478
|
}
|
|
956
479
|
|
|
957
|
-
public moveToNewColumn(cellId: T): MultiColumn<T> {
|
|
958
|
-
const fromColumn = this.findWithId(cellId);
|
|
959
|
-
|
|
960
|
-
// Create new columns array with the cell removed from its original column
|
|
961
|
-
const columns = this.columns.map((c) => {
|
|
962
|
-
if (c.id === fromColumn.id) {
|
|
963
|
-
return c.delete(cellId);
|
|
964
|
-
}
|
|
965
|
-
return c;
|
|
966
|
-
});
|
|
967
|
-
|
|
968
|
-
// Insert into new column
|
|
969
|
-
const newColumn = CollapsibleTree.from([cellId]);
|
|
970
|
-
return new MultiColumn([...columns, newColumn]);
|
|
971
|
-
}
|
|
972
|
-
|
|
973
480
|
public findWithId(id: T): CollapsibleTree<T> {
|
|
974
481
|
const found = this.columns.find((c) => {
|
|
975
482
|
return c.inOrderIds.includes(id);
|
|
@@ -1023,38 +530,6 @@ export class MultiColumn<T> {
|
|
|
1023
530
|
return this.transform(col, (c) => c.insert(id, index));
|
|
1024
531
|
}
|
|
1025
532
|
|
|
1026
|
-
public deleteById(cellId: T): MultiColumn<T> {
|
|
1027
|
-
return this.transformWithCellId(cellId, (c) => c.delete(cellId));
|
|
1028
|
-
}
|
|
1029
|
-
|
|
1030
|
-
/**
|
|
1031
|
-
* Remove the given cells from the tree, then re-insert each at its
|
|
1032
|
-
* (columnId, index). Used to undo a move (e.g. cut-paste) by restoring
|
|
1033
|
-
* cells to their previous positions. Placements are sorted by
|
|
1034
|
-
* (columnId, index) so insert order keeps indices valid.
|
|
1035
|
-
*/
|
|
1036
|
-
public placeCells(
|
|
1037
|
-
placements: Array<{ id: T; columnId: CellColumnId; index: CellIndex }>,
|
|
1038
|
-
): MultiColumn<T> {
|
|
1039
|
-
if (placements.length === 0) {
|
|
1040
|
-
return this;
|
|
1041
|
-
}
|
|
1042
|
-
let result = this.deleteById(placements[0].id);
|
|
1043
|
-
for (const { id } of placements.slice(1)) {
|
|
1044
|
-
result = result.deleteById(id);
|
|
1045
|
-
}
|
|
1046
|
-
const sorted = [...placements].toSorted((a, b) => {
|
|
1047
|
-
if (a.columnId !== b.columnId) {
|
|
1048
|
-
return a.columnId.localeCompare(b.columnId);
|
|
1049
|
-
}
|
|
1050
|
-
return (a.index as number) - (b.index as number);
|
|
1051
|
-
});
|
|
1052
|
-
for (const { id, columnId, index } of sorted) {
|
|
1053
|
-
result = result.insertId(id, columnId, index);
|
|
1054
|
-
}
|
|
1055
|
-
return result;
|
|
1056
|
-
}
|
|
1057
|
-
|
|
1058
533
|
public compact(): MultiColumn<T> {
|
|
1059
534
|
// Don't compact if there's only one column
|
|
1060
535
|
if (this.columns.length === 1) {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{t as e}from"./worker-C6e9ERyd.js";var t=e(((e,t)=>{t.exports={}}));export default t();
|