regular-layout 0.1.0 → 0.2.1

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.
Files changed (44) hide show
  1. package/LICENSE.md +1 -1
  2. package/README.md +6 -6
  3. package/dist/extensions.d.ts +5 -4
  4. package/dist/index.d.ts +3 -3
  5. package/dist/index.js +15 -10
  6. package/dist/index.js.map +4 -4
  7. package/dist/{common → layout}/calculate_edge.d.ts +3 -2
  8. package/dist/{common → layout}/calculate_intersect.d.ts +13 -9
  9. package/dist/layout/constants.d.ts +81 -0
  10. package/dist/{common → layout}/flatten.d.ts +1 -1
  11. package/dist/{common → layout}/generate_grid.d.ts +5 -4
  12. package/dist/layout/generate_overlay.d.ts +2 -0
  13. package/dist/{common → layout}/insert_child.d.ts +3 -2
  14. package/dist/{common → layout}/redistribute_panel_sizes.d.ts +2 -2
  15. package/dist/{common → layout}/remove_child.d.ts +1 -1
  16. package/dist/{common/layout_config.d.ts → layout/types.d.ts} +6 -10
  17. package/dist/regular-layout-frame.d.ts +1 -4
  18. package/dist/regular-layout-tab.d.ts +26 -0
  19. package/dist/regular-layout.d.ts +37 -18
  20. package/package.json +9 -7
  21. package/src/extensions.ts +10 -4
  22. package/src/index.ts +3 -7
  23. package/src/layout/calculate_edge.ts +217 -0
  24. package/src/{common → layout}/calculate_intersect.ts +61 -101
  25. package/src/layout/constants.ts +119 -0
  26. package/src/{common → layout}/flatten.ts +1 -1
  27. package/src/{common → layout}/generate_grid.ts +120 -106
  28. package/src/{common → layout}/generate_overlay.ts +26 -12
  29. package/src/{common → layout}/insert_child.ts +105 -51
  30. package/src/{common → layout}/redistribute_panel_sizes.ts +11 -4
  31. package/src/{common → layout}/remove_child.ts +2 -2
  32. package/src/{common/layout_config.ts → layout/types.ts} +7 -19
  33. package/src/regular-layout-frame.ts +40 -74
  34. package/src/regular-layout-tab.ts +103 -0
  35. package/src/regular-layout.ts +260 -148
  36. package/themes/chicago.css +89 -0
  37. package/themes/fluxbox.css +110 -0
  38. package/themes/gibson.css +264 -0
  39. package/themes/hotdog.css +88 -0
  40. package/themes/lorax.css +130 -0
  41. package/dist/common/constants.d.ts +0 -29
  42. package/dist/common/generate_overlay.d.ts +0 -2
  43. package/src/common/calculate_edge.ts +0 -104
  44. package/src/common/constants.ts +0 -46
@@ -9,9 +9,8 @@
9
9
  // ┃ * [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). * ┃
10
10
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
11
11
 
12
- import { GRID_TRACK_COLLAPSE_TOLERANCE } from "./constants.ts";
13
- import type { Layout } from "./layout_config.ts";
14
- import { remove_child } from "./remove_child.ts";
12
+ import { DEFAULT_PHYSICS, type Physics } from "./constants.ts";
13
+ import type { Layout } from "./types.ts";
15
14
 
16
15
  interface GridCell {
17
16
  child: string;
@@ -21,80 +20,77 @@ interface GridCell {
21
20
  rowEnd: number;
22
21
  }
23
22
 
24
- function dedupePositions(positions: number[]): number[] {
25
- if (positions.length === 0) {
26
- return [];
27
- }
28
-
29
- const sorted = positions.sort((a, b) => a - b);
30
- const result = [sorted[0]];
31
- for (let i = 1; i < sorted.length; i++) {
32
- if (
33
- Math.abs(sorted[i] - result[result.length - 1]) >
34
- GRID_TRACK_COLLAPSE_TOLERANCE
35
- ) {
36
- result.push(sorted[i]);
37
- }
23
+ function dedupe_sort(physics: Physics, result: number[], pos: number) {
24
+ if (
25
+ result.length === 0 ||
26
+ Math.abs(pos - result[result.length - 1]) >
27
+ physics.GRID_TRACK_COLLAPSE_TOLERANCE
28
+ ) {
29
+ result.push(pos);
38
30
  }
39
31
 
40
32
  return result;
41
33
  }
42
34
 
43
- function collectTrackPositions(
35
+ function dedupe_positions(physics: Physics, positions: number[]): number[] {
36
+ const sorted = positions.sort((a, b) => a - b);
37
+ return sorted.reduce(dedupe_sort.bind(undefined, physics), []);
38
+ }
39
+
40
+ function collect_track_positions(
44
41
  panel: Layout,
45
42
  orientation: "horizontal" | "vertical",
46
43
  start: number,
47
44
  end: number,
45
+ physics: Physics,
48
46
  ): number[] {
49
47
  if (panel.type === "child-panel") {
50
48
  return [start, end];
51
49
  }
52
50
 
51
+ const positions: number[] = [start, end];
53
52
  if (panel.orientation === orientation) {
54
- const positions: number[] = [start, end];
55
53
  let current = start;
54
+ const range = end - start;
56
55
  for (let i = 0; i < panel.children.length; i++) {
57
56
  const size = panel.sizes[i];
58
- const childPositions = collectTrackPositions(
59
- panel.children[i],
60
- orientation,
61
- current,
62
- current + size * (end - start),
57
+ const next = current + size * range;
58
+ positions.push(
59
+ ...collect_track_positions(
60
+ panel.children[i],
61
+ orientation,
62
+ current,
63
+ next,
64
+ physics,
65
+ ),
63
66
  );
64
67
 
65
- positions.push(...childPositions);
66
- current = current + size * (end - start);
68
+ current = next;
67
69
  }
68
-
69
- return dedupePositions(positions);
70
70
  } else {
71
- const allPositions: number[] = [start, end];
72
71
  for (const child of panel.children) {
73
- const childPositions = collectTrackPositions(
74
- child,
75
- orientation,
76
- start,
77
- end,
72
+ positions.push(
73
+ ...collect_track_positions(child, orientation, start, end, physics),
78
74
  );
79
-
80
- allPositions.push(...childPositions);
81
75
  }
82
-
83
- return dedupePositions(allPositions);
84
76
  }
77
+
78
+ return dedupe_positions(physics, positions);
85
79
  }
86
80
 
87
- function findTrackIndex(positions: number[], value: number): number {
88
- for (let i = 0; i < positions.length; i++) {
89
- if (Math.abs(positions[i] - value) < GRID_TRACK_COLLAPSE_TOLERANCE) {
90
- return i;
91
- }
92
- }
81
+ function find_track_index(
82
+ physics: Physics,
83
+ positions: number[],
84
+ value: number,
85
+ ): number {
86
+ const index = positions.findIndex(
87
+ (pos) => Math.abs(pos - value) < physics.GRID_TRACK_COLLAPSE_TOLERANCE,
88
+ );
93
89
 
94
- throw new Error(`Position ${value} not found in ${positions}`);
90
+ return index === -1 ? 0 : index;
95
91
  }
96
92
 
97
- function buildCells(
93
+ function build_cells(
98
94
  panel: Layout,
99
95
  colPositions: number[],
100
96
  rowPositions: number[],
@@ -102,28 +98,31 @@ function buildCells(
102
98
  colEnd: number,
103
99
  rowStart: number,
104
100
  rowEnd: number,
101
+ physics: Physics,
105
102
  ): GridCell[] {
106
103
  if (panel.type === "child-panel") {
107
104
  const selected = panel.selected ?? 0;
108
105
  return [
109
106
  {
110
107
  child: panel.child[selected],
111
- colStart: findTrackIndex(colPositions, colStart),
112
- colEnd: findTrackIndex(colPositions, colEnd),
113
- rowStart: findTrackIndex(rowPositions, rowStart),
114
- rowEnd: findTrackIndex(rowPositions, rowEnd),
108
+ colStart: find_track_index(physics, colPositions, colStart),
109
+ colEnd: find_track_index(physics, colPositions, colEnd),
110
+ rowStart: find_track_index(physics, rowPositions, rowStart),
111
+ rowEnd: find_track_index(physics, rowPositions, rowEnd),
115
112
  },
116
113
  ];
117
114
  }
118
115
 
119
- const cells: GridCell[] = [];
120
116
  const { children, sizes, orientation } = panel;
121
- if (orientation === "horizontal") {
122
- let current = colStart;
123
- for (let i = 0; i < children.length; i++) {
124
- const next = current + sizes[i] * (colEnd - colStart);
117
+ const isHorizontal = orientation === "horizontal";
118
+ let current = isHorizontal ? colStart : rowStart;
119
+ const range = isHorizontal ? colEnd - colStart : rowEnd - rowStart;
120
+ const cells: GridCell[] = [];
121
+ for (let i = 0; i < children.length; i++) {
122
+ const next = current + sizes[i] * range;
123
+ if (isHorizontal) {
125
124
  cells.push(
126
- ...buildCells(
125
+ ...build_cells(
127
126
  children[i],
128
127
  colPositions,
129
128
  rowPositions,
@@ -131,17 +130,12 @@ function buildCells(
131
130
  next,
132
131
  rowStart,
133
132
  rowEnd,
133
+ physics,
134
134
  ),
135
135
  );
136
-
137
- current = next;
138
- }
139
- } else {
140
- let current = rowStart;
141
- for (let i = 0; i < children.length; i++) {
142
- const next = current + sizes[i] * (rowEnd - rowStart);
136
+ } else {
143
137
  cells.push(
144
- ...buildCells(
138
+ ...build_cells(
145
139
  children[i],
146
140
  colPositions,
147
141
  rowPositions,
@@ -149,21 +143,27 @@ function buildCells(
149
143
  colEnd,
150
144
  current,
151
145
  next,
146
+ physics,
152
147
  ),
153
148
  );
154
-
155
- current = next;
156
149
  }
150
+
151
+ current = next;
157
152
  }
158
153
 
159
154
  return cells;
160
155
  }
161
156
 
162
157
  const host_template = (rowTemplate: string, colTemplate: string) =>
163
- `:host { display: grid; gap: 0px; grid-template-rows: ${rowTemplate}; grid-template-columns: ${colTemplate}; }`;
158
+ `:host ::slotted(*){display:none}:host{display:grid;grid-template-rows:${rowTemplate};grid-template-columns:${colTemplate}}`;
164
159
 
165
- const child_template = (slot: string, rowPart: string, colPart: string) =>
166
- `:host ::slotted([slot=${slot}]) { grid-column: ${colPart}; grid-row: ${rowPart}; }`;
160
+ const child_template = (
161
+ physics: Physics,
162
+ slot: string,
163
+ rowPart: string,
164
+ colPart: string,
165
+ ) =>
166
+ `:host ::slotted([${physics.CHILD_ATTRIBUTE_NAME}="${slot}"]){display:flex;grid-column:${colPart};grid-row:${rowPart}}`;
167
167
 
168
168
  /**
169
169
  * Generates CSS Grid styles to render a layout tree.
@@ -191,60 +191,74 @@ const child_template = (slot: string, rowPart: string, colPart: string) =>
191
191
  * const css = create_css_grid_layout(layout);
192
192
  * // Returns CSS like:
193
193
  * // :host { display: grid; grid-template-columns: 25% 75%; ... }
194
- * // :host ::slotted([slot=sidebar]) { grid-column: 1; grid-row: 1; }
195
- * // :host ::slotted([slot=main]) { grid-column: 2; grid-row: 1; }
194
+ * // :host ::slotted([name=sidebar]) { grid-column: 1; grid-row: 1; }
195
+ * // :host ::slotted([name=main]) { grid-column: 2; grid-row: 1; }
196
196
  * ```
197
197
  */
198
198
  export function create_css_grid_layout(
199
199
  layout: Layout,
200
- round: boolean = false,
201
200
  overlay?: [string, string],
201
+ physics: Physics = DEFAULT_PHYSICS,
202
202
  ): string {
203
- if (overlay) {
204
- layout = remove_child(layout, overlay[0]);
205
- }
206
-
207
203
  if (layout.type === "child-panel") {
208
204
  const selected = layout.selected ?? 0;
209
- return `${host_template("100%", "100%")}\n${child_template(layout.child[selected], "1", "1")}`;
205
+ return [
206
+ host_template("100%", "100%"),
207
+ child_template(physics, layout.child[selected], "1", "1"),
208
+ ].join("\n");
210
209
  }
211
210
 
212
- const colPositions = collectTrackPositions(layout, "horizontal", 0, 1);
213
- const colSizes: number[] = [];
214
- for (let i = 0; i < colPositions.length - 1; i++) {
215
- colSizes.push(colPositions[i + 1] - colPositions[i]);
216
- }
211
+ const createTemplate = (positions: number[]) => {
212
+ const sizes = positions
213
+ .slice(0, -1)
214
+ .map((pos, i) => positions[i + 1] - pos);
215
+ return sizes
216
+ .map((s) => `${physics.SHOULD_ROUND ? Math.round(s * 100) : s * 100}fr`)
217
+ .join(" ");
218
+ };
217
219
 
218
- const colTemplate = colSizes
219
- .map((s) => `${round ? Math.round(s * 100) : s * 100}%`)
220
- .join(" ");
220
+ const colPositions = collect_track_positions(
221
+ layout,
222
+ "horizontal",
223
+ 0,
224
+ 1,
225
+ physics,
226
+ );
221
227
 
222
- const rowPositions = collectTrackPositions(layout, "vertical", 0, 1);
223
- const rowSizes: number[] = [];
224
- for (let i = 0; i < rowPositions.length - 1; i++) {
225
- rowSizes.push(rowPositions[i + 1] - rowPositions[i]);
226
- }
228
+ const colTemplate = createTemplate(colPositions);
229
+ const rowPositions = collect_track_positions(
230
+ layout,
231
+ "vertical",
232
+ 0,
233
+ 1,
234
+ physics,
235
+ );
227
236
 
228
- const rowTemplate = rowSizes
229
- .map((s) => `${round ? Math.round(s * 100) : s * 100}%`)
230
- .join(" ");
237
+ const rowTemplate = createTemplate(rowPositions);
238
+ const formatGridLine = (start: number, end: number) =>
239
+ end - start === 1 ? `${start + 1}` : `${start + 1} / ${end + 1}`;
240
+
241
+ const cells = build_cells(
242
+ layout,
243
+ colPositions,
244
+ rowPositions,
245
+ 0,
246
+ 1,
247
+ 0,
248
+ 1,
249
+ physics,
250
+ );
231
251
 
232
- const cells = buildCells(layout, colPositions, rowPositions, 0, 1, 0, 1);
233
252
  const css = [host_template(rowTemplate, colTemplate)];
234
253
  for (const cell of cells) {
235
- const colPart =
236
- cell.colEnd - cell.colStart === 1
237
- ? `${cell.colStart + 1}`
238
- : `${cell.colStart + 1} / ${cell.colEnd + 1}`;
239
- const rowPart =
240
- cell.rowEnd - cell.rowStart === 1
241
- ? `${cell.rowStart + 1}`
242
- : `${cell.rowStart + 1} / ${cell.rowEnd + 1}`;
243
-
244
- css.push(`${child_template(cell.child, rowPart, colPart)}`);
254
+ const colPart = formatGridLine(cell.colStart, cell.colEnd);
255
+ const rowPart = formatGridLine(cell.rowStart, cell.rowEnd);
256
+ css.push(child_template(physics, cell.child, rowPart, colPart));
245
257
  if (cell.child === overlay?.[1]) {
246
- css.push(`${child_template(overlay[0], rowPart, colPart)}`);
247
- css.push(`:host ::slotted([slot=${overlay[0]}]) { z-index: 1; }`);
258
+ css.push(child_template(physics, overlay[0], rowPart, colPart));
259
+ css.push(
260
+ `:host ::slotted([${physics.CHILD_ATTRIBUTE_NAME}=${overlay[0]}]){z-index:1}`,
261
+ );
248
262
  }
249
263
  }
250
264
 
@@ -9,20 +9,34 @@
9
9
  // ┃ * [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). * ┃
10
10
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
11
11
 
12
- import type { LayoutPath } from "./layout_config";
12
+ import { DEFAULT_PHYSICS } from "./constants";
13
+ import type { LayoutPath } from "./types";
13
14
 
14
15
  export function updateOverlaySheet(
15
16
  slot: string,
16
- {
17
- view_window: { row_start, row_end, col_start, col_end },
18
- box,
19
- }: LayoutPath<DOMRect>,
17
+ box: DOMRect,
18
+ style: CSSStyleDeclaration,
19
+ drag_target: LayoutPath<undefined> | null,
20
+ physics = DEFAULT_PHYSICS,
20
21
  ) {
21
- const margin = 0;
22
- const top = row_start * box.height + margin / 2;
23
- const left = col_start * box.width + margin / 2;
24
- const height = (row_end - row_start) * box.height - margin;
25
- const width = (col_end - col_start) * box.width - margin;
26
- const css = `position:absolute!important;z-index:1;top:${top}px;left:${left}px;height:${height}px;width:${width}px;`;
27
- return `::slotted([slot="${slot}"]){${css}}`;
22
+ if (!drag_target) {
23
+ return `:host ::slotted([${physics.CHILD_ATTRIBUTE_NAME}="${slot}"]){display:none;}`;
24
+ }
25
+
26
+ const {
27
+ view_window: { row_start, row_end, col_start, col_end },
28
+ } = drag_target;
29
+
30
+ const box_height =
31
+ box.height - parseFloat(style.paddingTop) - parseFloat(style.paddingBottom);
32
+
33
+ const box_width =
34
+ box.width - parseFloat(style.paddingLeft) - parseFloat(style.paddingRight);
35
+
36
+ const top = row_start * box_height + parseFloat(style.paddingTop);
37
+ const left = col_start * box_width + parseFloat(style.paddingLeft);
38
+ const height = (row_end - row_start) * box_height;
39
+ const width = (col_end - col_start) * box_width;
40
+ const css = `display:flex;position:absolute!important;z-index:1;top:${top}px;left:${left}px;height:${height}px;width:${width}px;`;
41
+ return `::slotted([${physics.CHILD_ATTRIBUTE_NAME}="${slot}"]){${css}}`;
28
42
  }
@@ -9,7 +9,7 @@
9
9
  // ┃ * [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). * ┃
10
10
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
11
11
 
12
- import type { Layout } from "./layout_config.ts";
12
+ import type { Layout } from "./types.ts";
13
13
 
14
14
  /**
15
15
  * Inserts a new child panel into the layout tree at a specified location.
@@ -22,15 +22,20 @@ import type { Layout } from "./layout_config.ts";
22
22
  * at root level.
23
23
  * @param orientation - Orientation for newly created split panels. Defaults to
24
24
  * "horizontal".
25
+ * @param is_edge - If true, create the split at the parent level.
25
26
  * @returns A new layout tree with the child inserted (original is not mutated).
26
27
  */
27
28
  export function insert_child(
28
29
  panel: Layout,
29
30
  child: string,
30
31
  path: number[],
31
- orientation: "horizontal" | "vertical" = "horizontal",
32
- is_edge?: boolean,
32
+ orientation?: "horizontal" | "vertical",
33
33
  ): Layout {
34
+ const createChildPanel = (childId: string): Layout => ({
35
+ type: "child-panel",
36
+ child: [childId],
37
+ });
38
+
34
39
  if (path.length === 0) {
35
40
  // Insert at root level
36
41
  if (panel.type === "child-panel") {
@@ -39,92 +44,136 @@ export function insert_child(
39
44
  type: "child-panel",
40
45
  child: [child, ...panel.child],
41
46
  };
47
+ } else if (orientation) {
48
+ // When inserting at edge of root, wrap the entire panel in a new split
49
+ return {
50
+ type: "split-panel",
51
+ orientation: orientation,
52
+ children: [createChildPanel(child), panel],
53
+ sizes: [0.5, 0.5],
54
+ };
42
55
  } else {
43
56
  // Append to existing split-panel
44
- const newChildren = [
45
- ...panel.children,
46
- {
47
- type: "child-panel",
48
- child: [child],
49
- } as Layout,
50
- ];
51
-
52
- const numChildren = newChildren.length;
53
- const newSizes = Array(numChildren).fill(1 / numChildren);
57
+ const newChildren = [...panel.children, createChildPanel(child)];
58
+ const newSizes = [...panel.sizes, 1 / (newChildren.length - 1)];
54
59
  return {
55
60
  ...panel,
56
61
  children: newChildren,
57
- sizes: newSizes,
62
+ sizes: redistribute(newSizes),
58
63
  };
59
64
  }
60
65
  }
61
66
 
62
67
  // Navigate down the path
63
68
  const [index, ...restPath] = path;
69
+
70
+ // Special case: when orientation is provided and restPath is empty, handle edge insertion
71
+ if (orientation && restPath.length === 0) {
72
+ // If panel is a split-panel with the same orientation, insert into its children
73
+ if (panel.type === "split-panel" && panel.orientation === orientation) {
74
+ const newChildren = [...panel.children];
75
+ newChildren.splice(index, 0, createChildPanel(child));
76
+ const newSizes = [...panel.sizes];
77
+ newSizes.splice(index, 0, 1 / (newChildren.length - 1));
78
+ return {
79
+ ...panel,
80
+ children: newChildren,
81
+ sizes: redistribute(newSizes),
82
+ };
83
+ }
84
+
85
+ // Otherwise, wrap the entire panel in a new split at the edge
86
+ const children =
87
+ index === 0
88
+ ? [createChildPanel(child), panel]
89
+ : [panel, createChildPanel(child)];
90
+
91
+ return {
92
+ type: "split-panel",
93
+ orientation: orientation,
94
+ children,
95
+ sizes: [0.5, 0.5],
96
+ };
97
+ }
98
+
64
99
  if (panel.type === "child-panel") {
65
- // This shouldn't happen if path.length > 0, but handle it gracefully
66
- // We need to split this child-panel
100
+ // Stack into child array only when ALL of these conditions are met:
101
+ // 1. Path has exactly one element (restPath is empty)
102
+ // 2. Orientation was NOT explicitly provided (orientation is undefined)
103
+ // 3. Index is within the valid stacking range [0, child.length]
104
+ if (
105
+ restPath.length === 0 &&
106
+ orientation === undefined &&
107
+ index >= 0 &&
108
+ index <= panel.child.length
109
+ ) {
110
+ const newChild = [...panel.child];
111
+ newChild.splice(index, 0, child);
112
+ return {
113
+ ...panel,
114
+ child: newChild,
115
+ };
116
+ }
117
+
118
+ // Otherwise, wrap in a split panel and recurse
67
119
  const newPanel: Layout = {
68
120
  type: "split-panel",
69
- orientation,
121
+ orientation: orientation || "horizontal",
70
122
  children: [panel],
71
123
  sizes: [1],
72
124
  };
73
125
 
74
- return insert_child(newPanel, child, path, orientation, is_edge);
126
+ return insert_child(newPanel, child, path, orientation);
75
127
  }
76
128
 
77
129
  if (restPath.length === 0 || index === panel.children.length) {
78
- if (is_edge && panel.children[index]?.type === "child-panel") {
79
- panel.children[index].child.unshift(child);
80
- panel.children[index].selected = 0;
81
- return panel;
130
+ if (orientation && panel.children[index]) {
131
+ // When inserting at an edge, create a split panel with the new child and existing child
132
+ const newSplitPanel: Layout = {
133
+ type: "split-panel",
134
+ orientation: orientation,
135
+ children: [createChildPanel(child), panel.children[index]],
136
+ sizes: [0.5, 0.5],
137
+ };
138
+
139
+ const newChildren = [...panel.children];
140
+ newChildren[index] = newSplitPanel;
141
+ return {
142
+ ...panel,
143
+ children: newChildren,
144
+ sizes: redistribute(panel.sizes),
145
+ };
82
146
  }
83
147
 
84
148
  // Insert at this level at the specified index
85
149
  const newChildren = [...panel.children];
86
- newChildren.splice(index, 0, {
87
- type: "child-panel",
88
- child: [child],
89
- });
90
-
91
- const numChildren = newChildren.length;
92
- const newSizes = Array(numChildren).fill(1 / numChildren);
150
+ newChildren.splice(index, 0, createChildPanel(child));
151
+ const newSizes = [...panel.sizes];
152
+ newSizes.splice(index, 0, 1 / (newChildren.length - 1));
93
153
  return {
94
154
  ...panel,
95
155
  children: newChildren,
96
- sizes: newSizes,
156
+ sizes: redistribute(newSizes),
97
157
  };
98
158
  }
99
159
 
100
160
  const targetChild = panel.children[index];
101
- if (targetChild.type === "child-panel" && restPath.length > 0) {
102
- // Need to split this child-panel
103
- const oppositeOrientation =
104
- panel.orientation === "horizontal" ? "vertical" : "horizontal";
105
-
106
- const newSplitPanel = insert_child(
107
- targetChild,
108
- child,
109
- restPath,
110
- oppositeOrientation,
111
- is_edge,
112
- );
113
161
 
114
- const newChildren = [...panel.children];
115
- newChildren[index] = newSplitPanel;
116
- return {
117
- ...panel,
118
- children: newChildren,
119
- };
120
- }
162
+ // Determine the orientation to pass down when navigating into a child-panel
163
+ const childOrientation =
164
+ targetChild.type === "child-panel" &&
165
+ restPath.length > 0 &&
166
+ orientation !== undefined
167
+ ? panel.orientation === "horizontal"
168
+ ? "vertical"
169
+ : "horizontal"
170
+ : orientation;
121
171
 
122
172
  const updatedChild = insert_child(
123
173
  targetChild,
124
174
  child,
125
175
  restPath,
126
- orientation,
127
- is_edge,
176
+ childOrientation,
128
177
  );
129
178
 
130
179
  const newChildren = [...panel.children];
@@ -134,3 +183,8 @@ export function insert_child(
134
183
  children: newChildren,
135
184
  };
136
185
  }
186
+
187
+ function redistribute(arr: number[]): number[] {
188
+ const total = arr.reduce((sum, val) => sum + val, 0);
189
+ return arr.map((val) => val / total);
190
+ }
@@ -9,8 +9,8 @@
9
9
  // ┃ * [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). * ┃
10
10
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
11
11
 
12
- import { MINIMUM_REDISTRIBUTION_SIZE_THRESHOLD } from "./constants.ts";
13
- import type { Layout } from "./layout_config.ts";
12
+ import { DEFAULT_PHYSICS } from "./constants.ts";
13
+ import type { Layout } from "./types.ts";
14
14
 
15
15
  /**
16
16
  * Adjusts panel sizes during a drag operation on a divider.
@@ -33,6 +33,7 @@ export function redistribute_panel_sizes(
33
33
  panel: Layout,
34
34
  path: number[],
35
35
  delta: number,
36
+ physics = DEFAULT_PHYSICS,
36
37
  ): Layout {
37
38
  // Clone the entire panel structure
38
39
  const result = structuredClone(panel);
@@ -55,7 +56,12 @@ export function redistribute_panel_sizes(
55
56
 
56
57
  // It would be fun to remove this condition.
57
58
  if (index < current.sizes.length - 1) {
58
- current.sizes = add_and_redistribute(current.sizes, index, delta);
59
+ current.sizes = add_and_redistribute(
60
+ physics,
61
+ current.sizes,
62
+ index,
63
+ delta,
64
+ );
59
65
  }
60
66
  }
61
67
 
@@ -63,6 +69,7 @@ export function redistribute_panel_sizes(
63
69
  }
64
70
 
65
71
  function add_and_redistribute(
72
+ physics: typeof DEFAULT_PHYSICS,
66
73
  arr: number[],
67
74
  index: number,
68
75
  delta: number,
@@ -83,7 +90,7 @@ function add_and_redistribute(
83
90
  Math.sign(delta) *
84
91
  Math.min(
85
92
  Math.abs(delta),
86
- (1 - MINIMUM_REDISTRIBUTION_SIZE_THRESHOLD) *
93
+ (1 - physics.MINIMUM_REDISTRIBUTION_SIZE_THRESHOLD) *
87
94
  (delta > 0 ? before_total : after_total),
88
95
  );
89
96
 
@@ -9,8 +9,8 @@
9
9
  // ┃ * [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). * ┃
10
10
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
11
11
 
12
- import type { Layout, TabLayout } from "./layout_config.ts";
13
- import { EMPTY_PANEL } from "./layout_config.ts";
12
+ import type { Layout, TabLayout } from "./types.ts";
13
+ import { EMPTY_PANEL } from "./types.ts";
14
14
 
15
15
  /**
16
16
  * Removes a child panel from the layout tree by its name.