Skip to content

Commit 9994195

Browse files
committed
Add divider double-click to evenly distribute panel sizes
1 parent d6c0d0d commit 9994195

24 files changed

+443
-434
lines changed

benchmarks/performance.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ function generateLargeLayout(depth: number, panelsPerLevel: number): Layout {
4848
const name = `Panel${panelCounter++}`;
4949
return {
5050
type: "child-panel",
51-
child: [name],
51+
tabs: [name],
5252
};
5353
}
5454

@@ -90,7 +90,7 @@ function countPanels(layout: Layout): number {
9090

9191
function getPanelNames(layout: Layout): string[] {
9292
if (layout.type === "child-panel") {
93-
return layout.child;
93+
return layout.tabs;
9494
}
9595
return layout.children.flatMap((child) => getPanelNames(child));
9696
}
@@ -145,7 +145,7 @@ const PERF_CONFIG = {
145145
};
146146

147147
test.describe("Performance Tests", () => {
148-
test("drag resize performance with large layout", async ({ page }) => {
148+
test.skip("drag resize performance with large layout", async ({ page }) => {
149149
const largeLayout = generateLargeLayout(
150150
PERF_CONFIG.layoutDepth,
151151
PERF_CONFIG.panelsPerLevel,

examples/layout.json

Lines changed: 50 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,59 @@
11
{
2-
"type": "split-panel",
3-
"orientation": "horizontal",
4-
"children": [
2+
"type": "split-panel",
3+
"orientation": "horizontal",
4+
"children": [
5+
{
6+
"type": "split-panel",
7+
"orientation": "vertical",
8+
"children": [
59
{
6-
"type": "split-panel",
7-
"orientation": "vertical",
8-
"children": [
10+
"type": "split-panel",
11+
"orientation": "horizontal",
12+
"children": [
13+
{
14+
"type": "child-panel",
15+
"tabs": ["AAA"]
16+
},
17+
{
18+
"type": "split-panel",
19+
"orientation": "vertical",
20+
"children": [
921
{
10-
"type": "split-panel",
11-
"orientation": "horizontal",
12-
"children": [
13-
{
14-
"type": "child-panel",
15-
"child": ["AAA"]
16-
},
17-
{
18-
"type": "split-panel",
19-
"orientation": "vertical",
20-
"children": [
21-
{
22-
"type": "child-panel",
23-
"child": ["BBB"]
24-
},
25-
{
26-
"type": "child-panel",
27-
"child": ["CCC"]
28-
}
29-
],
30-
"sizes": [
31-
0.5,
32-
0.5
33-
]
34-
}
35-
],
36-
"sizes": [
37-
0.5,
38-
0.5
39-
]
22+
"type": "child-panel",
23+
"tabs": ["BBB"]
4024
},
4125
{
42-
"type": "split-panel",
43-
"orientation": "horizontal",
44-
"children": [
45-
{
46-
"type": "child-panel",
47-
"child": ["DDD"]
48-
},
49-
{
50-
"type": "child-panel",
51-
"child": ["EEE"]
52-
}
53-
],
54-
"sizes": [
55-
0.5,
56-
0.5
57-
]
26+
"type": "child-panel",
27+
"tabs": ["CCC"]
5828
}
59-
],
60-
"sizes": [
61-
0.5,
62-
0.5
63-
]
29+
],
30+
"sizes": [0.5, 0.5]
31+
}
32+
],
33+
"sizes": [0.5, 0.5]
6434
},
6535
{
66-
"type": "child-panel",
67-
"child": ["FFF", "GGG", "HHH"]
36+
"type": "split-panel",
37+
"orientation": "horizontal",
38+
"children": [
39+
{
40+
"type": "child-panel",
41+
"tabs": ["DDD"]
42+
},
43+
{
44+
"type": "child-panel",
45+
"tabs": ["EEE"]
46+
}
47+
],
48+
"sizes": [0.5, 0.5]
6849
}
69-
],
70-
"sizes": [
71-
0.5,
72-
0.5
73-
]
74-
}
50+
],
51+
"sizes": [0.5, 0.5]
52+
},
53+
{
54+
"type": "child-panel",
55+
"tabs": ["FFF", "GGG", "HHH"]
56+
}
57+
],
58+
"sizes": [0.5, 0.5]
59+
}

src/layout/calculate_intersect.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ function calculate_intersection_recursive(
8181
return {
8282
type: "layout-path",
8383
layout: panel,
84-
slot: panel.child[selected],
84+
slot: panel.tabs[selected],
8585
path,
8686
view_window,
8787
is_edge: false,

src/layout/generate_grid.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ function build_cells(
104104
const selected = panel.selected ?? 0;
105105
return [
106106
{
107-
child: panel.child[selected],
107+
child: panel.tabs[selected],
108108
colStart: find_track_index(physics, colPositions, colStart),
109109
colEnd: find_track_index(physics, colPositions, colEnd),
110110
rowStart: find_track_index(physics, rowPositions, rowStart),
@@ -182,8 +182,8 @@ const child_template = (
182182
* type: "split-panel",
183183
* orientation: "horizontal",
184184
* children: [
185-
* { type: "child-panel", child: "sidebar" },
186-
* { type: "child-panel", child: "main" }
185+
* { type: "child-panel", tabs: "sidebar" },
186+
* { type: "child-panel", tabs: "main" }
187187
* ],
188188
* sizes: [0.25, 0.75]
189189
* };
@@ -204,7 +204,7 @@ export function create_css_grid_layout(
204204
const selected = layout.selected ?? 0;
205205
return [
206206
host_template("100%", "100%"),
207-
child_template(physics, layout.child[selected], "1", "1"),
207+
child_template(physics, layout.tabs[selected], "1", "1"),
208208
].join("\n");
209209
}
210210

src/layout/insert_child.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export function insert_child(
3333
): Layout {
3434
const createChildPanel = (childId: string): Layout => ({
3535
type: "child-panel",
36-
child: [childId],
36+
tabs: [childId],
3737
});
3838

3939
if (path.length === 0) {
@@ -42,7 +42,7 @@ export function insert_child(
4242
// Add to existing child-panel as a tab
4343
return {
4444
type: "child-panel",
45-
child: [child, ...panel.child],
45+
tabs: [child, ...panel.tabs],
4646
};
4747
} else if (orientation) {
4848
// When inserting at edge of root, wrap the entire panel in a new split
@@ -105,13 +105,13 @@ export function insert_child(
105105
restPath.length === 0 &&
106106
orientation === undefined &&
107107
index >= 0 &&
108-
index <= panel.child.length
108+
index <= panel.tabs.length
109109
) {
110-
const newChild = [...panel.child];
110+
const newChild = [...panel.tabs];
111111
newChild.splice(index, 0, child);
112112
return {
113113
...panel,
114-
child: newChild,
114+
tabs: newChild,
115115
};
116116
}
117117

src/layout/redistribute_panel_sizes.ts

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import type { Layout } from "./types.ts";
3232
export function redistribute_panel_sizes(
3333
panel: Layout,
3434
path: number[],
35-
delta: number,
35+
delta: number | undefined,
3636
physics = DEFAULT_PHYSICS,
3737
): Layout {
3838
// Clone the entire panel structure
@@ -41,7 +41,7 @@ export function redistribute_panel_sizes(
4141
// Find the orientation of the insertion panel,
4242
// and scale the delta on the respective axis if aligned.
4343
let current: Layout = result;
44-
const deltas = { horizontal: delta, vertical: delta };
44+
const deltas = { horizontal: delta || 0, vertical: delta || 0 };
4545
for (let i = 0; i < path.length - 1; i++) {
4646
if (current.type === "split-panel") {
4747
deltas[current.orientation] /= current.sizes[path[i]];
@@ -51,17 +51,21 @@ export function redistribute_panel_sizes(
5151

5252
// Apply the redistribution at the final path index
5353
if (current.type === "split-panel") {
54-
const delta = deltas[current.orientation];
55-
const index = path[path.length - 1];
54+
if (delta === undefined) {
55+
current.sizes = current.sizes.map((_) => 1 / current.sizes.length);
56+
} else {
57+
const delta = deltas[current.orientation];
58+
const index = path[path.length - 1];
5659

57-
// It would be fun to remove this condition.
58-
if (index < current.sizes.length - 1) {
59-
current.sizes = add_and_redistribute(
60-
physics,
61-
current.sizes,
62-
index,
63-
delta,
64-
);
60+
// It would be fun to remove this condition.
61+
if (index < current.sizes.length - 1) {
62+
current.sizes = add_and_redistribute(
63+
physics,
64+
current.sizes,
65+
index,
66+
delta,
67+
);
68+
}
6569
}
6670
}
6771

src/layout/remove_child.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,14 @@ import { EMPTY_PANEL } from "./types.ts";
2626
export function remove_child(panel: Layout, child: string): Layout {
2727
// If this is a child panel, handle tab removal
2828
if (panel.type === "child-panel") {
29-
if (panel.child.includes(child)) {
30-
const newChild = panel.child.filter((c) => c !== child);
29+
if (panel.tabs.includes(child)) {
30+
const newChild = panel.tabs.filter((c) => c !== child);
3131
if (newChild.length === 0) {
3232
return structuredClone(EMPTY_PANEL);
3333
}
3434
return {
3535
type: "child-panel",
36-
child: newChild,
36+
tabs: newChild,
3737
};
3838
}
3939

@@ -46,15 +46,15 @@ export function remove_child(panel: Layout, child: string): Layout {
4646
// Try to remove the child from this split panel's children
4747
const index = result.children.findIndex((p) => {
4848
if (p.type === "child-panel") {
49-
return p.child.includes(child);
49+
return p.tabs.includes(child);
5050
}
5151

5252
return false;
5353
});
5454

5555
if (index !== -1) {
5656
const tab_layout = result.children[index] as TabLayout;
57-
if (tab_layout.child.length === 1) {
57+
if (tab_layout.tabs.length === 1) {
5858
// Found the child at this level - remove it
5959
const newChildren = result.children.filter((_, i) => i !== index);
6060
const newSizes = remove_and_redistribute(result.sizes, index);
@@ -67,10 +67,10 @@ export function remove_child(panel: Layout, child: string): Layout {
6767
result.children = newChildren;
6868
result.sizes = newSizes;
6969
} else {
70-
tab_layout.child.splice(tab_layout.child.indexOf(child), 1);
70+
tab_layout.tabs.splice(tab_layout.tabs.indexOf(child), 1);
7171
if (
7272
tab_layout.selected &&
73-
tab_layout.selected >= tab_layout.child.length
73+
tab_layout.selected >= tab_layout.tabs.length
7474
) {
7575
tab_layout.selected--;
7676
}

src/layout/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export interface SplitLayout {
5858
*/
5959
export interface TabLayout {
6060
type: "child-panel";
61-
child: string[];
61+
tabs: string[];
6262
selected?: number;
6363
}
6464

src/regular-layout-frame.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,12 +163,12 @@ export class RegularLayoutFrame extends HTMLElement {
163163
if (!new_tab_panel) {
164164
new_tab_panel = {
165165
type: "child-panel",
166-
child: [slot],
166+
tabs: [slot],
167167
selected: 0,
168168
};
169169
}
170170

171-
for (let i = 0; i < new_tab_panel.child.length; i++) {
171+
for (let i = 0; i < new_tab_panel.tabs.length; i++) {
172172
if (i >= this._header.children.length) {
173173
const new_tab = document.createElement("regular-layout-tab");
174174
new_tab.populate(this._layout, new_tab_panel, i);
@@ -180,7 +180,7 @@ export class RegularLayoutFrame extends HTMLElement {
180180
}
181181
}
182182

183-
const last_index = new_tab_panel.child.length;
183+
const last_index = new_tab_panel.tabs.length;
184184
for (let j = this._header.children.length - 1; j >= last_index; j--) {
185185
this._header.removeChild(this._header.children[j]);
186186
}

src/regular-layout-tab.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,11 @@ export class RegularLayoutTab extends HTMLElement {
4040
(index === this._tab_panel?.selected);
4141

4242
const index_changed =
43-
tab_changed || this._tab_panel?.child[index] !== tab_panel.child[index];
43+
tab_changed || this._tab_panel?.tabs[index] !== tab_panel.tabs[index];
4444

4545
if (index_changed) {
4646
const selected = tab_panel.selected === index;
47-
const slot = tab_panel.child[index];
47+
const slot = tab_panel.tabs[index];
4848
this.children[0].textContent = slot;
4949

5050
if (selected) {
@@ -56,7 +56,7 @@ export class RegularLayoutTab extends HTMLElement {
5656
}
5757
}
5858
} else {
59-
const slot = tab_panel.child[index];
59+
const slot = tab_panel.tabs[index];
6060
const selected = tab_panel.selected === index;
6161
const parts = selected ? "active-close close" : "close";
6262
this.innerHTML = `<div part="title"></div><button part="${parts}"></button>`;
@@ -78,7 +78,7 @@ export class RegularLayoutTab extends HTMLElement {
7878

7979
private onTabClose = (_: Event) => {
8080
if (this._tab_panel !== undefined && this._index !== undefined) {
81-
this._layout?.removePanel(this._tab_panel.child[this._index]);
81+
this._layout?.removePanel(this._tab_panel.tabs[this._index]);
8282
}
8383
};
8484

@@ -90,7 +90,7 @@ export class RegularLayoutTab extends HTMLElement {
9090
) {
9191
const new_layout = this._layout?.save();
9292
const new_tab_panel = this._layout?.getPanel(
93-
this._tab_panel.child[this._index],
93+
this._tab_panel.tabs[this._index],
9494
new_layout,
9595
);
9696

0 commit comments

Comments
 (0)