Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions benchmarks/performance.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ function generateLargeLayout(depth: number, panelsPerLevel: number): Layout {
function generatePanel(): TabLayout {
const name = `Panel${panelCounter++}`;
return {
type: "child-panel",
type: "tab-layout",
tabs: [name],
};
}
Expand All @@ -71,7 +71,7 @@ function generateLargeLayout(depth: number, panelsPerLevel: number): Layout {
}

return {
type: "split-panel",
type: "split-layout",
orientation,
children,
sizes,
Expand All @@ -82,14 +82,14 @@ function generateLargeLayout(depth: number, panelsPerLevel: number): Layout {
}

function countPanels(layout: Layout): number {
if (layout.type === "child-panel") {
if (layout.type === "tab-layout") {
return 1;
}
return layout.children.reduce((sum, child) => sum + countPanels(child), 0);
}

function getPanelNames(layout: Layout): string[] {
if (layout.type === "child-panel") {
if (layout.type === "tab-layout") {
return layout.tabs;
}
return layout.children.flatMap((child) => getPanelNames(child));
Expand Down
22 changes: 11 additions & 11 deletions examples/layout.json
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
{
"type": "split-panel",
"type": "split-layout",
"orientation": "horizontal",
"children": [
{
"type": "split-panel",
"type": "split-layout",
"orientation": "vertical",
"children": [
{
"type": "split-panel",
"type": "split-layout",
"orientation": "horizontal",
"children": [
{
"type": "child-panel",
"type": "tab-layout",
"tabs": ["AAA"]
},
{
"type": "split-panel",
"type": "split-layout",
"orientation": "vertical",
"children": [
{
"type": "child-panel",
"type": "tab-layout",
"tabs": ["BBB"]
},
{
"type": "child-panel",
"type": "tab-layout",
"tabs": ["CCC"]
}
],
Expand All @@ -33,15 +33,15 @@
"sizes": [0.5, 0.5]
},
{
"type": "split-panel",
"type": "split-layout",
"orientation": "horizontal",
"children": [
{
"type": "child-panel",
"type": "tab-layout",
"tabs": ["DDD"]
},
{
"type": "child-panel",
"type": "tab-layout",
"tabs": ["EEE"]
}
],
Expand All @@ -51,7 +51,7 @@
"sizes": [0.5, 0.5]
},
{
"type": "child-panel",
"type": "tab-layout",
"tabs": ["FFF", "GGG", "HHH"]
}
],
Expand Down
23 changes: 16 additions & 7 deletions src/layout/calculate_edge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,25 @@

import { DEFAULT_PHYSICS, type Physics } from "./constants";
import { insert_child } from "./insert_child";
import type { Layout, LayoutPath, Orientation, ViewWindow } from "./types";
import type {
Layout,
LayoutPath,
LayoutPathTraversal,
Orientation,
ViewWindow,
} from "./types";

/**
* Calculates an insertion point (which may involve splitting a single
* `"child-panel"` into a new `"split-panel"`), based on the cursor position.
* `"tab-layout"` into a new `"split-layout"`), based on the cursor position.
* *
* @param col - The cursor column.
* @param row - The cursor row.
* @param panel - The `Layout` to insert into.
* @param slot - The slot identifier where the insert should occur
* @param drop_target - The `LayoutPath` (from `calculateIntersect`) of the
* panel to either insert next to, or split by.
* @returns A new `LayoutPath` reflecting the updated (maybe) `"split-panel"`,
* @returns A new `LayoutPath` reflecting the updated (maybe) `"split-layout"`,
* which is enough to draw the overlay.
*/
export function calculate_edge(
Expand Down Expand Up @@ -133,7 +139,7 @@ function insert_root_edge(
panel: Layout,
slot: string,
drop_target: LayoutPath,
path: number[],
path: LayoutPathTraversal,
is_before: boolean,
orientation: Orientation,
): LayoutPath {
Expand All @@ -153,7 +159,7 @@ function insert_axis(
is_before: boolean,
axis_orientation: Orientation,
): LayoutPath {
let result_path: number[];
let result_path: LayoutPathTraversal;

if (drop_target.orientation === axis_orientation) {
// Same orientation - insert into existing split
Expand Down Expand Up @@ -183,7 +189,10 @@ function insert_axis(
};
}

function calculate_view_window(panel: Layout, path: number[]): ViewWindow {
function calculate_view_window(
panel: Layout,
path: LayoutPathTraversal,
): ViewWindow {
let view_window: ViewWindow = {
row_start: 0,
row_end: 1,
Expand All @@ -193,7 +202,7 @@ function calculate_view_window(panel: Layout, path: number[]): ViewWindow {

let current_panel = panel;
for (const step of path) {
if (current_panel.type === "child-panel") {
if (current_panel.type === "tab-layout") {
break;
}

Expand Down
12 changes: 9 additions & 3 deletions src/layout/calculate_intersect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@
// ┃ * [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). * ┃
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

import type { LayoutPath, LayoutDivider, Layout, ViewWindow } from "./types.ts";
import type {
LayoutPath,
LayoutDivider,
Layout,
ViewWindow,
LayoutPathTraversal,
} from "./types.ts";

const VIEW_WINDOW = {
row_start: 0,
Expand Down Expand Up @@ -67,14 +73,14 @@ function calculate_intersection_recursive(
check_dividers: { rect: DOMRect; size: number } | null,
parent_orientation: "horizontal" | "vertical" | null = null,
view_window: ViewWindow = structuredClone(VIEW_WINDOW),
path: number[] = [],
path: LayoutPathTraversal = [],
): LayoutPath | null | LayoutDivider {
if (column < 0 || row < 0 || column > 1 || row > 1) {
return null;
}

// Base case: if this is a child panel, return its name
if (panel.type === "child-panel") {
if (panel.type === "tab-layout") {
const selected = panel.selected ?? 0;
const col_width = view_window.col_end - view_window.col_start;
const row_height = view_window.row_end - view_window.row_start;
Expand Down
6 changes: 3 additions & 3 deletions src/layout/calculate_path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
// ┃ * [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). * ┃
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

import type { Layout } from "./types.ts";
import type { Layout, LayoutPathTraversal } from "./types.ts";

/**
* Calculates the index path for a panel with the given name.
Expand All @@ -28,9 +28,9 @@ export function calculate_path(name: string, layout: Layout): number[] | null {
function calculate_path_recursive(
name: string,
panel: Layout,
path: number[],
path: LayoutPathTraversal,
): number[] | null {
if (panel.type === "child-panel") {
if (panel.type === "tab-layout") {
if (!panel.tabs.includes(name)) {
return null;
}
Expand Down
6 changes: 3 additions & 3 deletions src/layout/flatten.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import type { Layout } from "./types.ts";
* @returns A new flattened layout tree (original is not mutated).
*/
export function flatten(layout: Layout): Layout {
if (layout.type === "child-panel") {
if (layout.type === "tab-layout") {
layout.selected = layout.selected || 0;
return layout;
}
Expand All @@ -36,7 +36,7 @@ export function flatten(layout: Layout): Layout {
const flattenedChild = flatten(child);

if (
flattenedChild.type === "split-panel" &&
flattenedChild.type === "split-layout" &&
flattenedChild.orientation === layout.orientation
) {
for (let j = 0; j < flattenedChild.children.length; j++) {
Expand All @@ -54,7 +54,7 @@ export function flatten(layout: Layout): Layout {
}

return {
type: "split-panel",
type: "split-layout",
orientation: layout.orientation,
children: flattenedChildren,
sizes: flattenedSizes,
Expand Down
12 changes: 6 additions & 6 deletions src/layout/generate_grid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ function collect_track_positions(
end: number,
physics: Physics,
): number[] {
if (panel.type === "child-panel") {
if (panel.type === "tab-layout") {
return [start, end];
}

Expand Down Expand Up @@ -100,7 +100,7 @@ function build_cells(
rowEnd: number,
physics: Physics,
): GridCell[] {
if (panel.type === "child-panel") {
if (panel.type === "tab-layout") {
const selected = panel.selected ?? 0;
return [
{
Expand Down Expand Up @@ -179,11 +179,11 @@ const child_template = (
* @example
* ```typescript
* const layout = {
* type: "split-panel",
* type: "split-layout",
* orientation: "horizontal",
* children: [
* { type: "child-panel", tabs: "sidebar" },
* { type: "child-panel", tabs: "main" }
* { type: "tab-layout", tabs: "sidebar" },
* { type: "tab-layout", tabs: "main" }
* ],
* sizes: [0.25, 0.75]
* };
Expand All @@ -200,7 +200,7 @@ export function create_css_grid_layout(
overlay?: [string, string],
physics: Physics = DEFAULT_PHYSICS,
): string {
if (layout.type === "child-panel") {
if (layout.type === "tab-layout") {
const selected = layout.selected ?? 0;
return [
host_template("100%", "100%"),
Expand Down
32 changes: 16 additions & 16 deletions src/layout/insert_child.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
// ┃ * [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). * ┃
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

import type { Layout } from "./types.ts";
import type { Layout, LayoutPathTraversal } from "./types.ts";

/**
* Inserts a new child panel into the layout tree at a specified location.
Expand All @@ -28,32 +28,32 @@ import type { Layout } from "./types.ts";
export function insert_child(
panel: Layout,
child: string,
path: number[],
path: LayoutPathTraversal,
orientation?: "horizontal" | "vertical",
): Layout {
const createChildPanel = (childId: string): Layout => ({
type: "child-panel",
type: "tab-layout",
tabs: [childId],
});

if (path.length === 0) {
// Insert at root level
if (panel.type === "child-panel") {
// Add to existing child-panel as a tab
if (panel.type === "tab-layout") {
// Add to existing tab-layout as a tab
return {
type: "child-panel",
type: "tab-layout",
tabs: [child, ...panel.tabs],
};
} else if (orientation) {
// When inserting at edge of root, wrap the entire panel in a new split
return {
type: "split-panel",
type: "split-layout",
orientation: orientation,
children: [createChildPanel(child), panel],
sizes: [0.5, 0.5],
};
} else {
// Append to existing split-panel
// Append to existing split-layout
const newChildren = [...panel.children, createChildPanel(child)];
const newSizes = [...panel.sizes, 1 / (newChildren.length - 1)];
return {
Expand All @@ -69,8 +69,8 @@ export function insert_child(

// Special case: when orientation is provided and restPath is empty, handle edge insertion
if (orientation && restPath.length === 0) {
// If panel is a split-panel with the same orientation, insert into its children
if (panel.type === "split-panel" && panel.orientation === orientation) {
// If panel is a split-layout with the same orientation, insert into its children
if (panel.type === "split-layout" && panel.orientation === orientation) {
const newChildren = [...panel.children];
newChildren.splice(index, 0, createChildPanel(child));
const newSizes = [...panel.sizes];
Expand All @@ -89,14 +89,14 @@ export function insert_child(
: [panel, createChildPanel(child)];

return {
type: "split-panel",
type: "split-layout",
orientation: orientation,
children,
sizes: [0.5, 0.5],
};
}

if (panel.type === "child-panel") {
if (panel.type === "tab-layout") {
// Stack into child array only when ALL of these conditions are met:
// 1. Path has exactly one element (restPath is empty)
// 2. Orientation was NOT explicitly provided (orientation is undefined)
Expand All @@ -117,7 +117,7 @@ export function insert_child(

// Otherwise, wrap in a split panel and recurse
const newPanel: Layout = {
type: "split-panel",
type: "split-layout",
orientation: orientation || "horizontal",
children: [panel],
sizes: [1],
Expand All @@ -130,7 +130,7 @@ export function insert_child(
if (orientation && panel.children[index]) {
// When inserting at an edge, create a split panel with the new child and existing child
const newSplitPanel: Layout = {
type: "split-panel",
type: "split-layout",
orientation: orientation,
children: [createChildPanel(child), panel.children[index]],
sizes: [0.5, 0.5],
Expand Down Expand Up @@ -159,9 +159,9 @@ export function insert_child(

const targetChild = panel.children[index];

// Determine the orientation to pass down when navigating into a child-panel
// Determine the orientation to pass down when navigating into a tab-layout
const childOrientation =
targetChild.type === "child-panel" &&
targetChild.type === "tab-layout" &&
restPath.length > 0 &&
orientation !== undefined
? panel.orientation === "horizontal"
Expand Down
Loading
Loading