regular-layout 0.2.0 → 0.2.2
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.
- package/dist/index.js +6 -6
- package/dist/index.js.map +3 -3
- package/dist/layout/calculate_edge.d.ts +2 -1
- package/dist/layout/calculate_intersect.d.ts +8 -2
- package/dist/layout/constants.d.ts +76 -38
- package/dist/layout/generate_grid.d.ts +4 -3
- package/dist/layout/generate_overlay.d.ts +1 -1
- package/dist/layout/redistribute_panel_sizes.d.ts +1 -1
- package/dist/layout/types.d.ts +3 -7
- package/dist/regular-layout-frame.d.ts +10 -3
- package/dist/regular-layout.d.ts +63 -12
- package/package.json +4 -2
- package/src/layout/calculate_edge.ts +26 -16
- package/src/layout/calculate_intersect.ts +11 -9
- package/src/layout/constants.ts +94 -38
- package/src/layout/generate_grid.ts +71 -27
- package/src/layout/generate_overlay.ts +5 -3
- package/src/layout/insert_child.ts +5 -5
- package/src/layout/redistribute_panel_sizes.ts +20 -9
- package/src/layout/remove_child.ts +7 -7
- package/src/layout/types.ts +3 -7
- package/src/regular-layout-frame.ts +53 -65
- package/src/regular-layout-tab.ts +5 -5
- package/src/regular-layout.ts +206 -114
- package/themes/lorax.css +2 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type Physics } from "./constants";
|
|
1
2
|
import type { Layout, LayoutPath } from "./types";
|
|
2
3
|
/**
|
|
3
4
|
* Calculates an insertion point (which may involve splitting a single
|
|
@@ -12,4 +13,4 @@ import type { Layout, LayoutPath } from "./types";
|
|
|
12
13
|
* @returns A new `LayoutPath` reflecting the updated (maybe) `"split-panel"`,
|
|
13
14
|
* which is enough to draw the overlay.
|
|
14
15
|
*/
|
|
15
|
-
export declare function calculate_edge(col: number, row: number, panel: Layout, slot: string, drop_target: LayoutPath, box?: DOMRect): LayoutPath;
|
|
16
|
+
export declare function calculate_edge(col: number, row: number, panel: Layout, slot: string, drop_target: LayoutPath, box?: DOMRect, physics?: Physics): LayoutPath;
|
|
@@ -12,5 +12,11 @@ import type { LayoutPath, LayoutDivider, Layout } from "./types.ts";
|
|
|
12
12
|
* boundary, or null if outside all panels
|
|
13
13
|
*/
|
|
14
14
|
export declare function calculate_intersection(column: number, row: number, layout: Layout, check_dividers?: null): LayoutPath | null;
|
|
15
|
-
export declare function calculate_intersection(column: number, row: number, layout: Layout, check_dividers?:
|
|
16
|
-
|
|
15
|
+
export declare function calculate_intersection(column: number, row: number, layout: Layout, check_dividers?: {
|
|
16
|
+
rect: DOMRect;
|
|
17
|
+
size: number;
|
|
18
|
+
}): LayoutPath | null | LayoutDivider;
|
|
19
|
+
export declare function calculate_intersection(column: number, row: number, layout: Layout, check_dividers?: {
|
|
20
|
+
rect: DOMRect;
|
|
21
|
+
size: number;
|
|
22
|
+
} | null): LayoutPath | null | LayoutDivider;
|
|
@@ -1,43 +1,81 @@
|
|
|
1
1
|
import type { OverlayMode } from "./types";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* Instance-specific constants which define the behavior and rendering details
|
|
4
|
+
* of a `<regular-layout>`.
|
|
5
5
|
*/
|
|
6
|
-
export
|
|
6
|
+
export interface Physics {
|
|
7
|
+
/**
|
|
8
|
+
* The prefix to use for `CustomEvent`s generated by `regular-layout`, e.g.
|
|
9
|
+
* `"regular-layout-before-update"`.
|
|
10
|
+
*/
|
|
11
|
+
CUSTOM_EVENT_NAME_PREFIX: string;
|
|
12
|
+
/**
|
|
13
|
+
* The attribute name to use for matching child `Element`s to grid
|
|
14
|
+
* positions.
|
|
15
|
+
*/
|
|
16
|
+
CHILD_ATTRIBUTE_NAME: string;
|
|
17
|
+
/**
|
|
18
|
+
* The minimum number of pixels the mouse must move to be considered a drag.
|
|
19
|
+
*/
|
|
20
|
+
MIN_DRAG_DISTANCE: number;
|
|
21
|
+
/**
|
|
22
|
+
* Should floating point pixel calculations be rounded. Useful for testing.
|
|
23
|
+
*/
|
|
24
|
+
SHOULD_ROUND: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Class name to use for child elements in overlay position (dragging).
|
|
27
|
+
*/
|
|
28
|
+
OVERLAY_CLASSNAME: string;
|
|
29
|
+
/**
|
|
30
|
+
* The percentage of the maximum resize distance that will be clamped.
|
|
31
|
+
*/
|
|
32
|
+
MINIMUM_REDISTRIBUTION_SIZE_THRESHOLD: number;
|
|
33
|
+
/**
|
|
34
|
+
* Threshold from panel edge that is considered a split vs drop action.
|
|
35
|
+
*/
|
|
36
|
+
SPLIT_EDGE_TOLERANCE: number;
|
|
37
|
+
/**
|
|
38
|
+
* Threshold from _container_ edge that is considered a split action on the root
|
|
39
|
+
* node.
|
|
40
|
+
*/
|
|
41
|
+
SPLIT_ROOT_EDGE_TOLERANCE: number;
|
|
42
|
+
/**
|
|
43
|
+
* Tolerance threshold for considering two grid track positions as identical.
|
|
44
|
+
*
|
|
45
|
+
* When collecting and deduplicating track positions, any positions closer than
|
|
46
|
+
* this value are treated as the same position to avoid redundant grid tracks.
|
|
47
|
+
*/
|
|
48
|
+
GRID_TRACK_COLLAPSE_TOLERANCE: number;
|
|
49
|
+
/**
|
|
50
|
+
* The overlay default behavior.
|
|
51
|
+
*/
|
|
52
|
+
OVERLAY_DEFAULT: OverlayMode;
|
|
53
|
+
/**
|
|
54
|
+
* Width of split panel dividers in pixels (for hit-test purposes).
|
|
55
|
+
*/
|
|
56
|
+
GRID_DIVIDER_SIZE: number;
|
|
57
|
+
/**
|
|
58
|
+
* Whether the grid should trigger column resize if the grid itself is not
|
|
59
|
+
* the `event.target`.
|
|
60
|
+
*/
|
|
61
|
+
GRID_DIVIDER_CHECK_TARGET: boolean;
|
|
62
|
+
}
|
|
7
63
|
/**
|
|
8
|
-
*
|
|
64
|
+
* Like `GlobalPhysics`, but suitable for partial definition for incremental
|
|
65
|
+
* updates.
|
|
9
66
|
*/
|
|
10
|
-
export
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
* Threshold from _container_ edge that is considered a split action on the root
|
|
26
|
-
* node.
|
|
27
|
-
*/
|
|
28
|
-
export declare const SPLIT_ROOT_EDGE_TOLERANCE = 0.01;
|
|
29
|
-
/**
|
|
30
|
-
* Tolerance threshold for considering two grid track positions as identical.
|
|
31
|
-
*
|
|
32
|
-
* When collecting and deduplicating track positions, any positions closer than
|
|
33
|
-
* this value are treated as the same position to avoid redundant grid tracks.
|
|
34
|
-
*/
|
|
35
|
-
export declare const GRID_TRACK_COLLAPSE_TOLERANCE = 0.001;
|
|
36
|
-
/**
|
|
37
|
-
* The overlay default behavior.
|
|
38
|
-
*/
|
|
39
|
-
export declare const OVERLAY_DEFAULT: OverlayMode;
|
|
40
|
-
/**
|
|
41
|
-
* Width of split panel dividers in pixels (for hit-test purposes).
|
|
42
|
-
*/
|
|
43
|
-
export declare const GRID_DIVIDER_SIZE = 6;
|
|
67
|
+
export interface PhysicsUpdate {
|
|
68
|
+
CUSTOM_EVENT_NAME_PREFIX?: string;
|
|
69
|
+
CHILD_ATTRIBUTE_NAME?: string;
|
|
70
|
+
MIN_DRAG_DISTANCE?: number;
|
|
71
|
+
SHOULD_ROUND?: boolean;
|
|
72
|
+
OVERLAY_CLASSNAME?: string;
|
|
73
|
+
MINIMUM_REDISTRIBUTION_SIZE_THRESHOLD?: number;
|
|
74
|
+
SPLIT_EDGE_TOLERANCE?: number;
|
|
75
|
+
SPLIT_ROOT_EDGE_TOLERANCE?: number;
|
|
76
|
+
GRID_TRACK_COLLAPSE_TOLERANCE?: number;
|
|
77
|
+
OVERLAY_DEFAULT?: OverlayMode;
|
|
78
|
+
GRID_DIVIDER_SIZE?: number;
|
|
79
|
+
GRID_DIVIDER_CHECK_TARGET?: boolean;
|
|
80
|
+
}
|
|
81
|
+
export declare const DEFAULT_PHYSICS: Physics;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type Physics } from "./constants.ts";
|
|
1
2
|
import type { Layout } from "./types.ts";
|
|
2
3
|
/**
|
|
3
4
|
* Generates CSS Grid styles to render a layout tree.
|
|
@@ -16,8 +17,8 @@ import type { Layout } from "./types.ts";
|
|
|
16
17
|
* type: "split-panel",
|
|
17
18
|
* orientation: "horizontal",
|
|
18
19
|
* children: [
|
|
19
|
-
* { type: "child-panel",
|
|
20
|
-
* { type: "child-panel",
|
|
20
|
+
* { type: "child-panel", tabs: "sidebar" },
|
|
21
|
+
* { type: "child-panel", tabs: "main" }
|
|
21
22
|
* ],
|
|
22
23
|
* sizes: [0.25, 0.75]
|
|
23
24
|
* };
|
|
@@ -29,4 +30,4 @@ import type { Layout } from "./types.ts";
|
|
|
29
30
|
* // :host ::slotted([name=main]) { grid-column: 2; grid-row: 1; }
|
|
30
31
|
* ```
|
|
31
32
|
*/
|
|
32
|
-
export declare function create_css_grid_layout(layout: Layout,
|
|
33
|
+
export declare function create_css_grid_layout(layout: Layout, overlay?: [string, string], physics?: Physics): string;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { LayoutPath } from "./types";
|
|
2
|
-
export declare function updateOverlaySheet(slot: string, box: DOMRect, style: CSSStyleDeclaration, drag_target: LayoutPath
|
|
2
|
+
export declare function updateOverlaySheet(slot: string, box: DOMRect, style: CSSStyleDeclaration, drag_target: LayoutPath | null, physics?: import("./constants").Physics): string;
|
|
@@ -16,4 +16,4 @@ import type { Layout } from "./types.ts";
|
|
|
16
16
|
* @returns A new layout tree with updated sizes (original is not mutated).
|
|
17
17
|
* ```
|
|
18
18
|
*/
|
|
19
|
-
export declare function redistribute_panel_sizes(panel: Layout, path: number[], delta: number): Layout;
|
|
19
|
+
export declare function redistribute_panel_sizes(panel: Layout, path: number[], delta: number | undefined, physics?: import("./constants.ts").Physics): Layout;
|
package/dist/layout/types.d.ts
CHANGED
|
@@ -42,7 +42,7 @@ export interface SplitLayout {
|
|
|
42
42
|
*/
|
|
43
43
|
export interface TabLayout {
|
|
44
44
|
type: "child-panel";
|
|
45
|
-
|
|
45
|
+
tabs: string[];
|
|
46
46
|
selected?: number;
|
|
47
47
|
}
|
|
48
48
|
/**
|
|
@@ -57,12 +57,8 @@ export interface LayoutDivider {
|
|
|
57
57
|
}
|
|
58
58
|
/**
|
|
59
59
|
* Represents a panel location result from hit detection.
|
|
60
|
-
*
|
|
61
|
-
* Contains both the panel identifier and its grid position in relative units.
|
|
62
|
-
* The generic parameter `T` allows DOM-only properties (e.g. `DOMRect`) to be
|
|
63
|
-
* shared in this cross-platform module.
|
|
64
60
|
*/
|
|
65
|
-
export interface LayoutPath
|
|
61
|
+
export interface LayoutPath {
|
|
66
62
|
type: "layout-path";
|
|
67
63
|
slot: string;
|
|
68
64
|
path: number[];
|
|
@@ -73,7 +69,7 @@ export interface LayoutPath<T = undefined> {
|
|
|
73
69
|
row_offset: number;
|
|
74
70
|
orientation: Orientation;
|
|
75
71
|
is_edge: boolean;
|
|
76
|
-
layout:
|
|
72
|
+
layout: Layout;
|
|
77
73
|
}
|
|
78
74
|
/**
|
|
79
75
|
* An empty `Layout` with no panels.
|
|
@@ -28,15 +28,22 @@ export declare class RegularLayoutFrame extends HTMLElement {
|
|
|
28
28
|
private _container_sheet;
|
|
29
29
|
private _layout;
|
|
30
30
|
private _header;
|
|
31
|
-
private
|
|
32
|
-
private _drag_moved;
|
|
31
|
+
private _drag;
|
|
33
32
|
private _tab_to_index_map;
|
|
34
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Initializes this elements. Override this method and
|
|
35
|
+
* `disconnectedCallback` to modify how this subclass renders the Shadow
|
|
36
|
+
* DOM and registers events.
|
|
37
|
+
*/
|
|
35
38
|
connectedCallback(): void;
|
|
39
|
+
/**
|
|
40
|
+
* Destroys this element.
|
|
41
|
+
*/
|
|
36
42
|
disconnectedCallback(): void;
|
|
37
43
|
private onPointerDown;
|
|
38
44
|
private onPointerMove;
|
|
39
45
|
private onPointerUp;
|
|
46
|
+
private onPointerCancel;
|
|
40
47
|
private onPointerLost;
|
|
41
48
|
private drawTabs;
|
|
42
49
|
}
|
package/dist/regular-layout.d.ts
CHANGED
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import type { LayoutPath, Layout, TabLayout, OverlayMode, Orientation } from "./layout/types.ts";
|
|
2
|
+
import { type PhysicsUpdate, type Physics } from "./layout/constants.ts";
|
|
3
|
+
/**
|
|
4
|
+
* An interface which models the fields of `PointerEvent` that
|
|
5
|
+
* `<regular-layour>` actually uses, making it easier to sub out with an
|
|
6
|
+
* JavaScript object lieral when you don't have a `PointerEvent` handy.
|
|
7
|
+
*/
|
|
8
|
+
export interface PointerEventCoordinates {
|
|
9
|
+
clientX: number;
|
|
10
|
+
clientY: number;
|
|
11
|
+
}
|
|
2
12
|
/**
|
|
3
13
|
* A Web Component that provides a resizable panel layout system.
|
|
4
14
|
* Panels are arranged using CSS Grid and can be resized by dragging dividers.
|
|
@@ -31,6 +41,22 @@ import type { LayoutPath, Layout, TabLayout, OverlayMode, Orientation } from "./
|
|
|
31
41
|
* layout.restore(state);
|
|
32
42
|
* ```
|
|
33
43
|
*
|
|
44
|
+
* @remarks
|
|
45
|
+
*
|
|
46
|
+
* Why does this implementation use a `<slot>` at all? We must use
|
|
47
|
+
* `<slot>` and the Shadow DOM to scope the grid CSS rules to each
|
|
48
|
+
* instance of `<regular-layout>` (without e.g. giving them unique
|
|
49
|
+
* `"id"` and injecting into `document,head`), and we can only select
|
|
50
|
+
* `::slotted` light DOM children from `adoptedStyleSheets` on the
|
|
51
|
+
* `ShadowRoot`.
|
|
52
|
+
*
|
|
53
|
+
* Why does this implementation use a single `<slot>` and the child
|
|
54
|
+
* `"name"` attribute, as opposed to a named `<slot name="my_slot">`
|
|
55
|
+
* and the built-in `"slot"` child attribute? Children with a `"slot"`
|
|
56
|
+
* attribute don't fallback to the un-named `<slot>`, so using the
|
|
57
|
+
* latter implementation would require synchronizing the light DOM
|
|
58
|
+
* and shadow DOM slots/slotted children continuously.
|
|
59
|
+
*
|
|
34
60
|
*/
|
|
35
61
|
export declare class RegularLayout extends HTMLElement {
|
|
36
62
|
private _shadowRoot;
|
|
@@ -40,23 +66,24 @@ export declare class RegularLayout extends HTMLElement {
|
|
|
40
66
|
private _drag_target?;
|
|
41
67
|
private _cursor_override;
|
|
42
68
|
private _dimensions?;
|
|
69
|
+
private _physics;
|
|
43
70
|
constructor();
|
|
44
71
|
connectedCallback(): void;
|
|
45
72
|
disconnectedCallback(): void;
|
|
46
73
|
/**
|
|
47
74
|
* Determines which panel is at a given screen coordinate.
|
|
48
75
|
*
|
|
49
|
-
* @param
|
|
50
|
-
*
|
|
76
|
+
* @param coordinates - `PointerEvent`, `MouseEvent`, or just X and Y
|
|
77
|
+
* coordinates in screen pixels.
|
|
51
78
|
* @returns Panel information if a panel is at that position, null otherwise.
|
|
52
79
|
*/
|
|
53
|
-
calculateIntersect: (
|
|
80
|
+
calculateIntersect: (coordinates: PointerEventCoordinates) => LayoutPath | null;
|
|
54
81
|
/**
|
|
55
82
|
* Sets the visual overlay state during drag-and-drop operations.
|
|
56
83
|
* Displays a preview of where a panel would be placed at the given coordinates.
|
|
57
84
|
*
|
|
58
|
-
* @param
|
|
59
|
-
*
|
|
85
|
+
* @param event - `PointerEvent`, `MouseEvent`, or just X and Y
|
|
86
|
+
* coordinates in screen pixels.
|
|
60
87
|
* @param dragTarget - A `LayoutPath` (presumably from `calculateIntersect`)
|
|
61
88
|
* which points to the drag element in the current layout.
|
|
62
89
|
* @param className - The CSS class name to use for the overlay panel
|
|
@@ -65,12 +92,12 @@ export declare class RegularLayout extends HTMLElement {
|
|
|
65
92
|
* the target, "absolute" positions the panel absolutely. Defaults to
|
|
66
93
|
* "absolute".
|
|
67
94
|
*/
|
|
68
|
-
setOverlayState: (
|
|
95
|
+
setOverlayState: (event: PointerEventCoordinates, { slot }: LayoutPath, className?: string, mode?: OverlayMode) => void;
|
|
69
96
|
/**
|
|
70
97
|
* Clears the overlay state and commits the panel placement.
|
|
71
98
|
*
|
|
72
|
-
* @param
|
|
73
|
-
*
|
|
99
|
+
* @param event - `PointerEvent`, `MouseEvent`, or just X and Y
|
|
100
|
+
* coordinates in screen pixels.
|
|
74
101
|
* @param dragTarget - A `LayoutPath` (presumably from `calculateIntersect`)
|
|
75
102
|
* which points to the drag element in the current layout.
|
|
76
103
|
* @param className - The CSS class name to use for the overlay panel
|
|
@@ -78,7 +105,7 @@ export declare class RegularLayout extends HTMLElement {
|
|
|
78
105
|
* @param mode - Overlay rendering mode that was used, must match the mode
|
|
79
106
|
* passed to `setOverlayState`. Defaults to "absolute".
|
|
80
107
|
*/
|
|
81
|
-
clearOverlayState: (
|
|
108
|
+
clearOverlayState: (event: PointerEventCoordinates | null, { slot, layout }: LayoutPath, className?: string) => void;
|
|
82
109
|
/**
|
|
83
110
|
* Inserts a new panel into the layout at a specified path.
|
|
84
111
|
*
|
|
@@ -135,20 +162,44 @@ export declare class RegularLayout extends HTMLElement {
|
|
|
135
162
|
* ```
|
|
136
163
|
*/
|
|
137
164
|
save: () => Layout;
|
|
165
|
+
/**
|
|
166
|
+
* Override this instance's global constants.
|
|
167
|
+
*
|
|
168
|
+
* @param physics
|
|
169
|
+
*/
|
|
170
|
+
restorePhysics(physics: PhysicsUpdate): void;
|
|
171
|
+
/**
|
|
172
|
+
* Get this instance's constants.
|
|
173
|
+
*
|
|
174
|
+
* @returns The current constants
|
|
175
|
+
*/
|
|
176
|
+
savePhysics(): Physics;
|
|
138
177
|
/**
|
|
139
178
|
* Converts screen coordinates to relative layout coordinates.
|
|
140
179
|
*
|
|
141
180
|
* Transforms absolute pixel positions into normalized coordinates (0-1 range)
|
|
142
181
|
* relative to the layout's bounding box.
|
|
143
182
|
*
|
|
144
|
-
* @param
|
|
145
|
-
*
|
|
183
|
+
* @param coordinates - `PointerEvent`, `MouseEvent`, or just X and Y
|
|
184
|
+
* coordinates in screen pixels.
|
|
146
185
|
* @returns A tuple containing:
|
|
147
186
|
* - col: Normalized X coordinate (0 = left edge, 1 = right edge)
|
|
148
187
|
* - row: Normalized Y coordinate (0 = top edge, 1 = bottom edge)
|
|
149
188
|
* - box: The layout element's bounding rectangle
|
|
150
189
|
*/
|
|
151
|
-
relativeCoordinates: (
|
|
190
|
+
relativeCoordinates: (event: PointerEventCoordinates, recalculate_bounds?: boolean) => [number, number, DOMRect, CSSStyleDeclaration];
|
|
191
|
+
/**
|
|
192
|
+
* Calculates the Euclidean distance in pixels between the current pointer
|
|
193
|
+
* coordinates and a drag target's position within the layout.
|
|
194
|
+
*
|
|
195
|
+
* @param coordinates - The current pointer event coordinates.
|
|
196
|
+
* @param drag_target - The layout path representing the drag target
|
|
197
|
+
* position.
|
|
198
|
+
* @returns The distance in pixels between the coordinates and the drag
|
|
199
|
+
* target.
|
|
200
|
+
*/
|
|
201
|
+
diffCoordinates: (event: PointerEventCoordinates, drag_target: LayoutPath) => number;
|
|
202
|
+
private onDblClick;
|
|
152
203
|
private onPointerDown;
|
|
153
204
|
private onPointerMove;
|
|
154
205
|
private onPointerUp;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "regular-layout",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "A regular CSS `grid` container",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
"browser": "dist/index.js",
|
|
12
12
|
"main": "dist/index.js",
|
|
13
|
+
"types": "dist/index.d.ts",
|
|
13
14
|
"type": "module",
|
|
14
15
|
"files": [
|
|
15
16
|
"dist/**/*",
|
|
@@ -20,7 +21,8 @@
|
|
|
20
21
|
"build": "tsx build.ts",
|
|
21
22
|
"build:watch": "tsx build.ts --watch",
|
|
22
23
|
"clean": "rm -rf dist",
|
|
23
|
-
"test": "playwright test",
|
|
24
|
+
"test": "playwright test tests/integration tests/unit",
|
|
25
|
+
"test:perf": "playwright test --workers=1 ./benchmarks",
|
|
24
26
|
"example": "tsx serve.ts",
|
|
25
27
|
"deploy": "tsx deploy.ts",
|
|
26
28
|
"lint": "biome lint src tests",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// ┃ * [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). * ┃
|
|
10
10
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
11
11
|
|
|
12
|
-
import {
|
|
12
|
+
import { DEFAULT_PHYSICS, type Physics } from "./constants";
|
|
13
13
|
import { insert_child } from "./insert_child";
|
|
14
14
|
import type { Layout, LayoutPath, Orientation, ViewWindow } from "./types";
|
|
15
15
|
|
|
@@ -33,39 +33,47 @@ export function calculate_edge(
|
|
|
33
33
|
slot: string,
|
|
34
34
|
drop_target: LayoutPath,
|
|
35
35
|
box?: DOMRect,
|
|
36
|
+
physics: Physics = DEFAULT_PHYSICS,
|
|
36
37
|
): LayoutPath {
|
|
37
38
|
// Check root edges first
|
|
38
|
-
if (col < SPLIT_ROOT_EDGE_TOLERANCE) {
|
|
39
|
+
if (col < physics.SPLIT_ROOT_EDGE_TOLERANCE) {
|
|
39
40
|
return insert_root_edge(panel, slot, drop_target, [0], true, "horizontal");
|
|
40
41
|
}
|
|
41
42
|
|
|
42
|
-
if (col > 1 - SPLIT_ROOT_EDGE_TOLERANCE) {
|
|
43
|
+
if (col > 1 - physics.SPLIT_ROOT_EDGE_TOLERANCE) {
|
|
43
44
|
return insert_root_edge(
|
|
44
45
|
panel,
|
|
45
46
|
slot,
|
|
46
47
|
drop_target,
|
|
47
|
-
drop_target.path.length > 0 ? drop_target.path : [],
|
|
48
|
+
drop_target.path.length > 0 ? drop_target.path : [1],
|
|
48
49
|
false,
|
|
49
50
|
"horizontal",
|
|
50
51
|
);
|
|
51
52
|
}
|
|
52
53
|
|
|
53
|
-
if (row < SPLIT_ROOT_EDGE_TOLERANCE) {
|
|
54
|
+
if (row < physics.SPLIT_ROOT_EDGE_TOLERANCE) {
|
|
54
55
|
return insert_root_edge(panel, slot, drop_target, [0], true, "vertical");
|
|
55
56
|
}
|
|
56
57
|
|
|
57
|
-
if (row > 1 - SPLIT_ROOT_EDGE_TOLERANCE) {
|
|
58
|
-
return insert_root_edge(
|
|
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
|
+
);
|
|
59
67
|
}
|
|
60
68
|
|
|
61
69
|
// Check panel edges
|
|
62
70
|
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;
|
|
65
73
|
|
|
66
74
|
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;
|
|
69
77
|
|
|
70
78
|
// If both edges triggered, choose closer axis
|
|
71
79
|
if (is_column_edge && is_row_edge) {
|
|
@@ -79,15 +87,17 @@ export function calculate_edge(
|
|
|
79
87
|
(box?.height || 1) *
|
|
80
88
|
(drop_target.view_window.row_end - drop_target.view_window.row_start);
|
|
81
89
|
|
|
82
|
-
const use_column =
|
|
90
|
+
const use_column =
|
|
91
|
+
col_scale / 2 - col_distance * col_scale <
|
|
92
|
+
row_scale / 2 - row_distance * row_scale;
|
|
83
93
|
|
|
84
94
|
return insert_axis(
|
|
85
95
|
panel,
|
|
86
96
|
slot,
|
|
87
97
|
drop_target,
|
|
88
98
|
use_column
|
|
89
|
-
? drop_target.column_offset < SPLIT_EDGE_TOLERANCE
|
|
90
|
-
: drop_target.row_offset < SPLIT_EDGE_TOLERANCE,
|
|
99
|
+
? drop_target.column_offset < physics.SPLIT_EDGE_TOLERANCE
|
|
100
|
+
: drop_target.row_offset < physics.SPLIT_EDGE_TOLERANCE,
|
|
91
101
|
use_column ? "horizontal" : "vertical",
|
|
92
102
|
);
|
|
93
103
|
}
|
|
@@ -97,7 +107,7 @@ export function calculate_edge(
|
|
|
97
107
|
panel,
|
|
98
108
|
slot,
|
|
99
109
|
drop_target,
|
|
100
|
-
drop_target.column_offset < SPLIT_EDGE_TOLERANCE,
|
|
110
|
+
drop_target.column_offset < physics.SPLIT_EDGE_TOLERANCE,
|
|
101
111
|
"horizontal",
|
|
102
112
|
);
|
|
103
113
|
}
|
|
@@ -107,7 +117,7 @@ export function calculate_edge(
|
|
|
107
117
|
panel,
|
|
108
118
|
slot,
|
|
109
119
|
drop_target,
|
|
110
|
-
drop_target.row_offset < SPLIT_EDGE_TOLERANCE,
|
|
120
|
+
drop_target.row_offset < physics.SPLIT_EDGE_TOLERANCE,
|
|
111
121
|
"vertical",
|
|
112
122
|
);
|
|
113
123
|
}
|
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
// ┃ * [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). * ┃
|
|
10
10
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
11
11
|
|
|
12
|
-
import { GRID_DIVIDER_SIZE } from "./constants.ts";
|
|
13
12
|
import type { LayoutPath, LayoutDivider, Layout, ViewWindow } from "./types.ts";
|
|
14
13
|
|
|
15
14
|
const VIEW_WINDOW = {
|
|
@@ -42,21 +41,21 @@ export function calculate_intersection(
|
|
|
42
41
|
column: number,
|
|
43
42
|
row: number,
|
|
44
43
|
layout: Layout,
|
|
45
|
-
check_dividers?: DOMRect,
|
|
44
|
+
check_dividers?: { rect: DOMRect; size: number },
|
|
46
45
|
): LayoutPath | null | LayoutDivider;
|
|
47
46
|
|
|
48
47
|
export function calculate_intersection(
|
|
49
48
|
column: number,
|
|
50
49
|
row: number,
|
|
51
50
|
layout: Layout,
|
|
52
|
-
check_dividers?: DOMRect | null,
|
|
51
|
+
check_dividers?: { rect: DOMRect; size: number } | null,
|
|
53
52
|
): LayoutPath | null | LayoutDivider;
|
|
54
53
|
|
|
55
54
|
export function calculate_intersection(
|
|
56
55
|
column: number,
|
|
57
56
|
row: number,
|
|
58
57
|
layout: Layout,
|
|
59
|
-
check_dividers: DOMRect | null = null,
|
|
58
|
+
check_dividers: { rect: DOMRect; size: number } | null = null,
|
|
60
59
|
): LayoutPath | null | LayoutDivider {
|
|
61
60
|
return calculate_intersection_recursive(column, row, layout, check_dividers);
|
|
62
61
|
}
|
|
@@ -65,7 +64,7 @@ function calculate_intersection_recursive(
|
|
|
65
64
|
column: number,
|
|
66
65
|
row: number,
|
|
67
66
|
panel: Layout,
|
|
68
|
-
check_dividers: DOMRect | null,
|
|
67
|
+
check_dividers: { rect: DOMRect; size: number } | null,
|
|
69
68
|
parent_orientation: "horizontal" | "vertical" | null = null,
|
|
70
69
|
view_window: ViewWindow = structuredClone(VIEW_WINDOW),
|
|
71
70
|
path: number[] = [],
|
|
@@ -81,8 +80,8 @@ function calculate_intersection_recursive(
|
|
|
81
80
|
const row_height = view_window.row_end - view_window.row_start;
|
|
82
81
|
return {
|
|
83
82
|
type: "layout-path",
|
|
84
|
-
layout:
|
|
85
|
-
slot: panel.
|
|
83
|
+
layout: panel,
|
|
84
|
+
slot: panel.tabs[selected],
|
|
86
85
|
path,
|
|
87
86
|
view_window,
|
|
88
87
|
is_edge: false,
|
|
@@ -99,7 +98,10 @@ function calculate_intersection_recursive(
|
|
|
99
98
|
const position = is_vertical ? row : column;
|
|
100
99
|
const start_key = is_vertical ? "row_start" : "col_start";
|
|
101
100
|
const end_key = is_vertical ? "row_end" : "col_end";
|
|
102
|
-
const rect_dim = is_vertical
|
|
101
|
+
const rect_dim = is_vertical
|
|
102
|
+
? check_dividers?.rect?.height
|
|
103
|
+
: check_dividers?.rect?.width;
|
|
104
|
+
|
|
103
105
|
let current_pos = view_window[start_key];
|
|
104
106
|
const total_size = view_window[end_key] - view_window[start_key];
|
|
105
107
|
for (let i = 0; i < panel.children.length; i++) {
|
|
@@ -107,7 +109,7 @@ function calculate_intersection_recursive(
|
|
|
107
109
|
|
|
108
110
|
// Check if position is on a divider
|
|
109
111
|
if (check_dividers && rect_dim) {
|
|
110
|
-
const divider_threshold =
|
|
112
|
+
const divider_threshold = check_dividers.size / rect_dim;
|
|
111
113
|
if (Math.abs(position - next_pos) < divider_threshold) {
|
|
112
114
|
return {
|
|
113
115
|
path: [...path, i],
|