Skip to content

Commit 5badaac

Browse files
committed
Expose constants and a drop target bug
1 parent b1fb650 commit 5badaac

16 files changed

+388
-149
lines changed

build.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ const browserConfig: esbuild.BuildOptions = {
3030
minify: true,
3131
minifyWhitespace: true,
3232
minifyIdentifiers: true,
33+
mangleProps: /^[_#]/,
3334
outfile: "dist/index.js",
3435
platform: "browser",
3536
format: "esm",

examples/index.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ const add = document.querySelector("#add") as HTMLButtonElement;
1616
const save = document.querySelector("#save") as HTMLButtonElement;
1717
const restore = document.querySelector("#restore") as HTMLButtonElement;
1818
const clear = document.querySelector("#clear") as HTMLButtonElement;
19+
20+
// biome-ignore lint/style/noNonNullAssertion: demo
21+
const layout = document.querySelector("regular-layout")!;
22+
1923
add.addEventListener("click", () => {
2024
// Note: this *demo* implementation leaks `div` elements, because they
2125
// are not removed from the light DOM by `clear` or `restore`. You must
@@ -36,12 +40,11 @@ add.addEventListener("click", () => {
3640

3741
themes.addEventListener("change", (_event) => {
3842
layout.className = themes.value;
39-
})
43+
});
4044

4145
const req = await fetch("./layout.json");
4246
let state = await req.json();
4347

44-
const layout = document.querySelector("regular-layout") as any;
4548
layout.restore(state);
4649
save.addEventListener("click", () => {
4750
state = layout.save();

src/layout/calculate_edge.ts

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
// ┃ * [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). * ┃
1010
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
1111

12-
import { SPLIT_EDGE_TOLERANCE, SPLIT_ROOT_EDGE_TOLERANCE } from "./constants";
12+
import { DEFAULT_PHYSICS, type Physics } from "./constants";
1313
import { insert_child } from "./insert_child";
1414
import type { Layout, LayoutPath, Orientation, ViewWindow } from "./types";
1515

@@ -33,39 +33,47 @@ export function calculate_edge(
3333
slot: string,
3434
drop_target: LayoutPath,
3535
box?: DOMRect,
36+
physics: Physics = DEFAULT_PHYSICS,
3637
): LayoutPath {
3738
// Check root edges first
38-
if (col < SPLIT_ROOT_EDGE_TOLERANCE) {
39+
if (col < physics.SPLIT_ROOT_EDGE_TOLERANCE) {
3940
return insert_root_edge(panel, slot, drop_target, [0], true, "horizontal");
4041
}
4142

42-
if (col > 1 - SPLIT_ROOT_EDGE_TOLERANCE) {
43+
if (col > 1 - physics.SPLIT_ROOT_EDGE_TOLERANCE) {
4344
return insert_root_edge(
4445
panel,
4546
slot,
4647
drop_target,
47-
drop_target.path.length > 0 ? drop_target.path : [],
48+
drop_target.path.length > 0 ? drop_target.path : [1],
4849
false,
4950
"horizontal",
5051
);
5152
}
5253

53-
if (row < SPLIT_ROOT_EDGE_TOLERANCE) {
54+
if (row < physics.SPLIT_ROOT_EDGE_TOLERANCE) {
5455
return insert_root_edge(panel, slot, drop_target, [0], true, "vertical");
5556
}
5657

57-
if (row > 1 - SPLIT_ROOT_EDGE_TOLERANCE) {
58-
return insert_root_edge(panel, slot, drop_target, [], false, "vertical");
58+
if (row > 1 - physics.SPLIT_ROOT_EDGE_TOLERANCE) {
59+
return insert_root_edge(
60+
panel,
61+
slot,
62+
drop_target,
63+
drop_target.path.length > 0 ? drop_target.path : [1],
64+
false,
65+
"vertical",
66+
);
5967
}
6068

6169
// Check panel edges
6270
const is_column_edge =
63-
drop_target.column_offset < SPLIT_EDGE_TOLERANCE ||
64-
drop_target.column_offset > 1 - SPLIT_EDGE_TOLERANCE;
71+
drop_target.column_offset < physics.SPLIT_EDGE_TOLERANCE ||
72+
drop_target.column_offset > 1 - physics.SPLIT_EDGE_TOLERANCE;
6573

6674
const is_row_edge =
67-
drop_target.row_offset < SPLIT_EDGE_TOLERANCE ||
68-
drop_target.row_offset > 1 - SPLIT_EDGE_TOLERANCE;
75+
drop_target.row_offset < physics.SPLIT_EDGE_TOLERANCE ||
76+
drop_target.row_offset > 1 - physics.SPLIT_EDGE_TOLERANCE;
6977

7078
// If both edges triggered, choose closer axis
7179
if (is_column_edge && is_row_edge) {
@@ -86,8 +94,8 @@ export function calculate_edge(
8694
slot,
8795
drop_target,
8896
use_column
89-
? drop_target.column_offset < SPLIT_EDGE_TOLERANCE
90-
: drop_target.row_offset < SPLIT_EDGE_TOLERANCE,
97+
? drop_target.column_offset < physics.SPLIT_EDGE_TOLERANCE
98+
: drop_target.row_offset < physics.SPLIT_EDGE_TOLERANCE,
9199
use_column ? "horizontal" : "vertical",
92100
);
93101
}
@@ -97,7 +105,7 @@ export function calculate_edge(
97105
panel,
98106
slot,
99107
drop_target,
100-
drop_target.column_offset < SPLIT_EDGE_TOLERANCE,
108+
drop_target.column_offset < physics.SPLIT_EDGE_TOLERANCE,
101109
"horizontal",
102110
);
103111
}
@@ -107,7 +115,7 @@ export function calculate_edge(
107115
panel,
108116
slot,
109117
drop_target,
110-
drop_target.row_offset < SPLIT_EDGE_TOLERANCE,
118+
drop_target.row_offset < physics.SPLIT_EDGE_TOLERANCE,
111119
"vertical",
112120
);
113121
}

src/layout/calculate_intersect.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
// ┃ * [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). * ┃
1010
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
1111

12-
import { GRID_DIVIDER_SIZE } from "./constants.ts";
1312
import type { LayoutPath, LayoutDivider, Layout, ViewWindow } from "./types.ts";
1413

1514
const VIEW_WINDOW = {
@@ -42,21 +41,21 @@ export function calculate_intersection(
4241
column: number,
4342
row: number,
4443
layout: Layout,
45-
check_dividers?: DOMRect,
44+
check_dividers?: { rect: DOMRect; size: number },
4645
): LayoutPath | null | LayoutDivider;
4746

4847
export function calculate_intersection(
4948
column: number,
5049
row: number,
5150
layout: Layout,
52-
check_dividers?: DOMRect | null,
51+
check_dividers?: { rect: DOMRect; size: number } | null,
5352
): LayoutPath | null | LayoutDivider;
5453

5554
export function calculate_intersection(
5655
column: number,
5756
row: number,
5857
layout: Layout,
59-
check_dividers: DOMRect | null = null,
58+
check_dividers: { rect: DOMRect; size: number } | null = null,
6059
): LayoutPath | null | LayoutDivider {
6160
return calculate_intersection_recursive(column, row, layout, check_dividers);
6261
}
@@ -65,7 +64,7 @@ function calculate_intersection_recursive(
6564
column: number,
6665
row: number,
6766
panel: Layout,
68-
check_dividers: DOMRect | null,
67+
check_dividers: { rect: DOMRect; size: number } | null,
6968
parent_orientation: "horizontal" | "vertical" | null = null,
7069
view_window: ViewWindow = structuredClone(VIEW_WINDOW),
7170
path: number[] = [],
@@ -99,15 +98,18 @@ function calculate_intersection_recursive(
9998
const position = is_vertical ? row : column;
10099
const start_key = is_vertical ? "row_start" : "col_start";
101100
const end_key = is_vertical ? "row_end" : "col_end";
102-
const rect_dim = is_vertical ? check_dividers?.height : check_dividers?.width;
101+
const rect_dim = is_vertical
102+
? check_dividers?.rect?.height
103+
: check_dividers?.rect?.width;
104+
103105
let current_pos = view_window[start_key];
104106
const total_size = view_window[end_key] - view_window[start_key];
105107
for (let i = 0; i < panel.children.length; i++) {
106108
const next_pos = current_pos + total_size * panel.sizes[i];
107109

108110
// Check if position is on a divider
109111
if (check_dividers && rect_dim) {
110-
const divider_threshold = GRID_DIVIDER_SIZE / rect_dim;
112+
const divider_threshold = check_dividers.size / rect_dim;
111113
if (Math.abs(position - next_pos) < divider_threshold) {
112114
return {
113115
path: [...path, i],

src/layout/constants.ts

Lines changed: 86 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -12,52 +12,100 @@
1212
import type { OverlayMode } from "./types";
1313

1414
/**
15-
* The prefix to use for `CustomEvent`s generated by `regular-layout`, e.g.
16-
* `"regular-layout-before-update"`.
15+
* Instance-specific constants which define the behavior and rendering details
16+
* of a `<regular-layout>`.
1717
*/
18-
export const CUSTOM_EVENT_NAME_PREFIX = "regular-layout";
18+
export interface Physics {
19+
/**
20+
* The prefix to use for `CustomEvent`s generated by `regular-layout`, e.g.
21+
* `"regular-layout-before-update"`.
22+
*/
23+
CUSTOM_EVENT_NAME_PREFIX: string;
1924

20-
/**
21-
* The minimum number of pixels the mouse must move to be considered a drag.
22-
*/
23-
export const MIN_DRAG_DISTANCE = 10;
25+
/**
26+
* The attribute name to use for matching child `Element`s to grid
27+
* positions.
28+
*/
29+
CHILD_ATTRIBUTE_NAME: string;
2430

25-
/**
26-
* Class name to use for child elements in overlay position (dragging).
27-
*/
28-
export const OVERLAY_CLASSNAME = "overlay";
31+
/**
32+
* The minimum number of pixels the mouse must move to be considered a drag.
33+
*/
34+
MIN_DRAG_DISTANCE: number;
2935

30-
/**
31-
* The percentage of the maximum resize distance that will be clamped.
32-
*
33-
*/
34-
export const MINIMUM_REDISTRIBUTION_SIZE_THRESHOLD = 0.15;
36+
/**
37+
* Should floating point pixel calculations be rounded. Useful for testing.
38+
*/
39+
SHOULD_ROUND: boolean;
3540

36-
/**
37-
* Threshold from panel edge that is considered a split vs drop action.
38-
*/
39-
export const SPLIT_EDGE_TOLERANCE = 0.25;
41+
/**
42+
* Class name to use for child elements in overlay position (dragging).
43+
*/
44+
OVERLAY_CLASSNAME: string;
4045

41-
/**
42-
* Threshold from _container_ edge that is considered a split action on the root
43-
* node.
44-
*/
45-
export const SPLIT_ROOT_EDGE_TOLERANCE = 0.01;
46+
/**
47+
* The percentage of the maximum resize distance that will be clamped.
48+
*/
49+
MINIMUM_REDISTRIBUTION_SIZE_THRESHOLD: number;
4650

47-
/**
48-
* Tolerance threshold for considering two grid track positions as identical.
49-
*
50-
* When collecting and deduplicating track positions, any positions closer than
51-
* this value are treated as the same position to avoid redundant grid tracks.
52-
*/
53-
export const GRID_TRACK_COLLAPSE_TOLERANCE = 0.001;
51+
/**
52+
* Threshold from panel edge that is considered a split vs drop action.
53+
*/
54+
SPLIT_EDGE_TOLERANCE: number;
5455

55-
/**
56-
* The overlay default behavior.
57-
*/
58-
export const OVERLAY_DEFAULT: OverlayMode = "absolute";
56+
/**
57+
* Threshold from _container_ edge that is considered a split action on the root
58+
* node.
59+
*/
60+
SPLIT_ROOT_EDGE_TOLERANCE: number;
61+
62+
/**
63+
* Tolerance threshold for considering two grid track positions as identical.
64+
*
65+
* When collecting and deduplicating track positions, any positions closer than
66+
* this value are treated as the same position to avoid redundant grid tracks.
67+
*/
68+
GRID_TRACK_COLLAPSE_TOLERANCE: number;
69+
70+
/**
71+
* The overlay default behavior.
72+
*/
73+
OVERLAY_DEFAULT: OverlayMode;
74+
75+
/**
76+
* Width of split panel dividers in pixels (for hit-test purposes).
77+
*/
78+
GRID_DIVIDER_SIZE: number;
79+
}
5980

6081
/**
61-
* Width of split panel dividers in pixels (for hit-test purposes).
82+
* Like `GlobalPhysics`, but suitable for partial definition for incremental
83+
* updates.
6284
*/
63-
export const GRID_DIVIDER_SIZE = 6;
85+
export interface PhysicsUpdate {
86+
CUSTOM_EVENT_NAME_PREFIX?: string;
87+
CHILD_ATTRIBUTE_NAME?: string;
88+
MIN_DRAG_DISTANCE?: number;
89+
SHOULD_ROUND?: boolean;
90+
OVERLAY_CLASSNAME?: string;
91+
MINIMUM_REDISTRIBUTION_SIZE_THRESHOLD?: number;
92+
SPLIT_EDGE_TOLERANCE?: number;
93+
SPLIT_ROOT_EDGE_TOLERANCE?: number;
94+
GRID_TRACK_COLLAPSE_TOLERANCE?: number;
95+
OVERLAY_DEFAULT?: OverlayMode;
96+
GRID_DIVIDER_SIZE?: number;
97+
}
98+
99+
export const DEFAULT_PHYSICS: Physics = Object.freeze({
100+
CUSTOM_EVENT_NAME_PREFIX: "regular-layout",
101+
CHILD_ATTRIBUTE_NAME: "name",
102+
MIN_DRAG_DISTANCE: 10,
103+
SHOULD_ROUND: false,
104+
OVERLAY_CLASSNAME: "overlay",
105+
MINIMUM_REDISTRIBUTION_SIZE_THRESHOLD: 0.15,
106+
SPLIT_EDGE_TOLERANCE: 0.25,
107+
SPLIT_ROOT_EDGE_TOLERANCE: 0.01,
108+
GRID_TRACK_COLLAPSE_TOLERANCE: 0.001,
109+
OVERLAY_DEFAULT: "absolute",
110+
GRID_DIVIDER_SIZE: 6,
111+
});

0 commit comments

Comments
 (0)