Skip to content

Commit 676a982

Browse files
authored
Merge pull request #10 from texodus/calculate-path
Add `calculatePath` method
2 parents 789c760 + a5cb32c commit 676a982

3 files changed

Lines changed: 133 additions & 0 deletions

File tree

src/layout/calculate_path.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
2+
// ░░░░░░░░▄▀░█▀▄░█▀▀░█▀▀░█░█░█░░░█▀█░█▀▄░░░░░█░░░█▀█░█░█░█▀█░█░█░▀█▀░▀▄░░░░░░░░
3+
// ░░░░░░░▀▄░░█▀▄░█▀▀░█░█░█░█░█░░░█▀█░█▀▄░▀▀▀░█░░░█▀█░░█░░█░█░█░█░░█░░░▄▀░░░░░░░
4+
// ░░░░░░░░░▀░▀░▀░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀░▀░░░░░▀▀▀░▀░▀░░▀░░▀▀▀░▀▀▀░░▀░░▀░░░░░░░░░
5+
// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
6+
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
7+
// ┃ * Copyright (c) 2026, the Regular Layout Authors. This file is part * ┃
8+
// ┃ * of the Regular Layout library, distributed under the terms of the * ┃
9+
// ┃ * [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). * ┃
10+
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
11+
12+
import type { Layout } from "./types.ts";
13+
14+
/**
15+
* Calculates the index path for a panel with the given name.
16+
*
17+
* Traverses the layout tree to find the named panel and returns the
18+
* index path describing its position in the tree.
19+
*
20+
* @param name - The name of the panel to find.
21+
* @param layout - The layout tree to search.
22+
* @returns The panel's index path if found, `null` otherwise.
23+
*/
24+
export function calculate_path(name: string, layout: Layout): number[] | null {
25+
return calculate_path_recursive(name, layout, []);
26+
}
27+
28+
function calculate_path_recursive(
29+
name: string,
30+
panel: Layout,
31+
path: number[],
32+
): number[] | null {
33+
if (panel.type === "child-panel") {
34+
if (!panel.tabs.includes(name)) {
35+
return null;
36+
}
37+
38+
return path;
39+
}
40+
41+
for (let i = 0; i < panel.children.length; i++) {
42+
const result = calculate_path_recursive(name, panel.children[i], [
43+
...path,
44+
i,
45+
]);
46+
47+
if (result) {
48+
return result;
49+
}
50+
}
51+
52+
return null;
53+
}

src/regular-layout.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import { redistribute_panel_sizes } from "./layout/redistribute_panel_sizes.ts";
3333
import { updateOverlaySheet } from "./layout/generate_overlay.ts";
3434
import { calculate_edge } from "./layout/calculate_edge.ts";
3535
import { flatten } from "./layout/flatten.ts";
36+
import { calculate_path } from "./layout/calculate_path.ts";
3637
import {
3738
DEFAULT_PHYSICS,
3839
type PhysicsUpdate,
@@ -151,6 +152,16 @@ export class RegularLayout extends HTMLElement {
151152
return calculate_intersection(col, row, this._panel);
152153
};
153154

155+
/**
156+
* Calculates the index path for a panel with the given name.
157+
*
158+
* @param name - The name of the panel to find.
159+
* @returns The panel's index path if found, `null` otherwise.
160+
*/
161+
calculatePath = (name: string): number[] | null => {
162+
return calculate_path(name, this._panel);
163+
};
164+
154165
/**
155166
* Sets the visual overlay state during drag-and-drop operations.
156167
* Displays a preview of where a panel would be placed at the given coordinates.

tests/unit/calculate_path.spec.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
2+
// ░░░░░░░░▄▀░█▀▄░█▀▀░█▀▀░█░█░█░░░█▀█░█▀▄░░░░░█░░░█▀█░█░█░█▀█░█░█░▀█▀░▀▄░░░░░░░░
3+
// ░░░░░░░▀▄░░█▀▄░█▀▀░█░█░█░█░█░░░█▀█░█▀▄░▀▀▀░█░░░█▀█░░█░░█░█░█░█░░█░░░▄▀░░░░░░░
4+
// ░░░░░░░░░▀░▀░▀░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀░▀░░░░░▀▀▀░▀░▀░░▀░░▀▀▀░▀▀▀░░▀░░▀░░░░░░░░░
5+
// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
6+
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
7+
// ┃ * Copyright (c) 2026, the Regular Layout Authors. This file is part * ┃
8+
// ┃ * of the Regular Layout library, distributed under the terms of the * ┃
9+
// ┃ * [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). * ┃
10+
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
11+
12+
import { expect, test } from "@playwright/test";
13+
import { calculate_path } from "../../src/layout/calculate_path.ts";
14+
import { LAYOUTS } from "../helpers/fixtures.ts";
15+
16+
test("returns null for a name not in the layout", () => {
17+
const result = calculate_path("ZZZ", LAYOUTS.SINGLE_AAA);
18+
expect(result).toBeNull();
19+
});
20+
21+
test("returns null for an empty layout", () => {
22+
const result = calculate_path("AAA", {
23+
type: "split-panel",
24+
orientation: "horizontal",
25+
sizes: [],
26+
children: [],
27+
});
28+
expect(result).toBeNull();
29+
});
30+
31+
test("finds a single panel", () => {
32+
const result = calculate_path("AAA", LAYOUTS.SINGLE_AAA);
33+
expect(result).toStrictEqual([]);
34+
});
35+
36+
test("finds left panel in horizontal split", () => {
37+
const result = calculate_path("AAA", LAYOUTS.TWO_HORIZONTAL);
38+
expect(result).toStrictEqual([0]);
39+
});
40+
41+
test("finds right panel in horizontal split", () => {
42+
const result = calculate_path("BBB", LAYOUTS.TWO_HORIZONTAL);
43+
expect(result).toStrictEqual([1]);
44+
});
45+
46+
test("finds top panel in vertical split", () => {
47+
const result = calculate_path("AAA", LAYOUTS.TWO_VERTICAL);
48+
expect(result).toStrictEqual([0]);
49+
});
50+
51+
test("finds panel in nested layout", () => {
52+
const result = calculate_path("AAA", LAYOUTS.NESTED_BASIC);
53+
expect(result).toStrictEqual([0, 0]);
54+
});
55+
56+
test("finds deeply nested panel", () => {
57+
const result = calculate_path("BBB", LAYOUTS.DEEPLY_NESTED);
58+
expect(result).toStrictEqual([0, 1]);
59+
});
60+
61+
test("finds non-selected tab by name", () => {
62+
const result = calculate_path("BBB", LAYOUTS.SINGLE_TABS);
63+
expect(result).toStrictEqual([]);
64+
});
65+
66+
test("finds leaf panel in deeply nested alt layout", () => {
67+
const result = calculate_path("DDD", LAYOUTS.DEEPLY_NESTED_ALT);
68+
expect(result).toStrictEqual([1]);
69+
});

0 commit comments

Comments
 (0)