elements-kit 0.27.4 → 0.27.6
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/README.md +1 -0
- package/dist/attributes.d.mts +1 -1
- package/dist/await.d.mts +1 -1
- package/dist/await.mjs +1 -1
- package/dist/{children-_cMdyX7Q.d.mts → children-CjlmAzxx.d.mts} +3 -3
- package/dist/chunk-CfYAbeIz.mjs +13 -0
- package/dist/{custom-elements-C7oiqw-y.d.mts → custom-elements-CTqSkWI7.d.mts} +1 -1
- package/dist/custom-elements.d.mts +1 -1
- package/dist/direction-EwIAAn1f.d.mts +29 -0
- package/dist/{element-DgQvp-49.mjs → element-bMvwF1ue.mjs} +2 -2
- package/dist/for.d.mts +1 -1
- package/dist/for.mjs +2 -2
- package/dist/{fragment-BJfcQdsL.mjs → fragment-DV0hrE_A.mjs} +2 -2
- package/dist/{hot-Zl0s17uq.mjs → hot-kRKLQS6B.mjs} +2 -2
- package/dist/hydrate/index.mjs +1 -1
- package/dist/{hydrate-ICXw1FIY.mjs → hydrate-Vx_iBMqJ.mjs} +3 -3
- package/dist/integrations/astro-client.mjs +2 -2
- package/dist/integrations/astro-server.mjs +3 -3
- package/dist/integrations/astro-slots.mjs +2 -2
- package/dist/integrations/hmr-runtime.mjs +2 -2
- package/dist/integrations/react.d.mts +1 -1
- package/dist/integrations/react.mjs +2 -2
- package/dist/jsx-runtime/dev.d.mts +2 -2
- package/dist/jsx-runtime/dev.mjs +2 -2
- package/dist/jsx-runtime/index.d.mts +2 -2
- package/dist/jsx-runtime/index.mjs +2 -2
- package/dist/render.mjs +1 -1
- package/dist/{scope-C1vELXqP.mjs → scope-B1kDu4YX.mjs} +1 -1
- package/dist/server/index.mjs +1 -1
- package/dist/{server-Qm9JTZ-J.mjs → server-DxiXDjQU.mjs} +3 -3
- package/dist/signals/index.d.mts +1 -1
- package/dist/signals/index.mjs +2 -2
- package/dist/slot.mjs +1 -1
- package/dist/ui/otp-input/index.d.mts +2 -2
- package/dist/ui/otp-input/index.mjs +1 -1
- package/dist/ui/overlay/handle.css +134 -0
- package/dist/ui/overlay/index.css +13 -88
- package/dist/ui/overlay/index.d.mts +232 -347
- package/dist/ui/overlay/index.mjs +676 -1550
- package/dist/ui/overlay/overlay.css +74 -135
- package/dist/utilities/_observe.mjs +1 -1
- package/dist/utilities/active-element.d.mts +1 -1
- package/dist/utilities/active-element.mjs +1 -1
- package/dist/utilities/async.d.mts +7 -1
- package/dist/utilities/async.mjs +13 -1
- package/dist/utilities/context.mjs +1 -1
- package/dist/utilities/debounced.d.mts +1 -1
- package/dist/utilities/debounced.mjs +1 -1
- package/dist/utilities/direction.d.mts +2 -0
- package/dist/utilities/direction.mjs +42 -0
- package/dist/utilities/dom-lifecycle.bench.mjs +2 -2
- package/dist/utilities/dom-lifecycle.d.mts +1 -1
- package/dist/utilities/dom-lifecycle.mjs +1 -1
- package/dist/utilities/element-rect.d.mts +1 -1
- package/dist/utilities/element-rect.mjs +1 -1
- package/dist/utilities/element-scroll.d.mts +1 -1
- package/dist/utilities/environment.d.mts +1 -1
- package/dist/utilities/event-driven.d.mts +1 -1
- package/dist/utilities/event-driven.mjs +1 -1
- package/dist/utilities/event-listener.d.mts +1 -1
- package/dist/utilities/event-listener.mjs +1 -1
- package/dist/utilities/focus-within.d.mts +1 -1
- package/dist/utilities/focus-within.mjs +1 -1
- package/dist/utilities/hover.d.mts +1 -1
- package/dist/utilities/hover.mjs +1 -1
- package/dist/utilities/interval.d.mts +1 -1
- package/dist/utilities/interval.mjs +1 -1
- package/dist/utilities/location.d.mts +1 -1
- package/dist/utilities/location.mjs +1 -1
- package/dist/utilities/media-devices.d.mts +1 -1
- package/dist/utilities/media-devices.mjs +1 -1
- package/dist/utilities/media-player.d.mts +1 -1
- package/dist/utilities/media-query.d.mts +1 -1
- package/dist/utilities/media-query.mjs +1 -1
- package/dist/utilities/network.d.mts +1 -1
- package/dist/utilities/network.mjs +1 -1
- package/dist/utilities/orientation.d.mts +1 -1
- package/dist/utilities/orientation.mjs +1 -1
- package/dist/utilities/previous.d.mts +1 -1
- package/dist/utilities/previous.mjs +1 -1
- package/dist/utilities/promise.d.mts +1 -1
- package/dist/utilities/promise.mjs +1 -1
- package/dist/utilities/retry.mjs +1 -1
- package/dist/utilities/routing.d.mts +1 -1
- package/dist/utilities/routing.mjs +1 -1
- package/dist/utilities/search-params.d.mts +1 -1
- package/dist/utilities/storage.d.mts +1 -1
- package/dist/utilities/storage.mjs +1 -1
- package/dist/utilities/throttled.d.mts +1 -1
- package/dist/utilities/throttled.mjs +1 -1
- package/dist/utilities/timeout.d.mts +1 -1
- package/dist/utilities/timeout.mjs +1 -1
- package/dist/utilities/window-focus.d.mts +1 -1
- package/dist/utilities/window-focus.mjs +1 -1
- package/dist/utilities/window-size.d.mts +1 -1
- package/dist/utilities/window-size.mjs +1 -1
- package/package.json +9 -6
- /package/dist/{attributes-3r7Diua4.d.mts → attributes-YQZNO15D.d.mts} +0 -0
- /package/dist/{environment-c2ftMYT0.d.mts → environment-EQugnOcg.d.mts} +0 -0
- /package/dist/{index-BrVMftR8.d.mts → index-BSAqa0-0.d.mts} +0 -0
- /package/dist/{lib-DYypKhxk.mjs → lib-Dw0g-vex.mjs} +0 -0
- /package/dist/{polyfill-CdZVCxdo.mjs → polyfill-B1lNNcum.mjs} +0 -0
- /package/dist/{polyfill-BVNd6ogU.d.mts → polyfill-_7qvybvt.d.mts} +0 -0
|
@@ -1,1598 +1,724 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import "../../
|
|
1
|
+
import { t as __exportAll } from "../../chunk-CfYAbeIz.mjs";
|
|
2
|
+
import { c as batch, u as effect, x as signal } from "../../lib-Dw0g-vex.mjs";
|
|
3
|
+
import { reactive } from "../../signals/index.mjs";
|
|
4
|
+
import { t as scope } from "../../scope-B1kDu4YX.mjs";
|
|
5
|
+
import { direction } from "../../utilities/direction.mjs";
|
|
3
6
|
import { createElementRect } from "../../utilities/element-rect.mjs";
|
|
4
|
-
import {
|
|
5
|
-
//#region src/ui/overlay/
|
|
7
|
+
import { windowSize } from "../../utilities/window-size.mjs";
|
|
8
|
+
//#region src/ui/overlay/area.ts
|
|
9
|
+
/** One axis of a {@link Region.place}: where a box `n` long, currently at
|
|
10
|
+
* `own`, starts once `pin` is honoured. */
|
|
11
|
+
function placeAxis(pin, own, n) {
|
|
12
|
+
if (!pin) return own;
|
|
13
|
+
if (pin.align === "start") return pin.at;
|
|
14
|
+
if (pin.align === "end") return pin.at - n;
|
|
15
|
+
return pin.at - n / 2;
|
|
16
|
+
}
|
|
17
|
+
/** The inset a pin yields on one edge: its coordinate if it pins that edge. */
|
|
18
|
+
function inset(pin, edge) {
|
|
19
|
+
return pin?.align === edge ? pin.at : null;
|
|
20
|
+
}
|
|
6
21
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* the live viewport (read at call time, not observed).
|
|
22
|
+
* A {@link Region} driven by writes — for gestures, where the box grows away
|
|
23
|
+
* from the edge the user is not dragging. Assigning an inset pins that edge
|
|
24
|
+
* and releases its opposite; assigning `null` frees the axis.
|
|
11
25
|
*/
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
26
|
+
var MutableRegion = class {
|
|
27
|
+
#x = signal(null);
|
|
28
|
+
#y = signal(null);
|
|
29
|
+
constructor(boundary) {
|
|
30
|
+
const { left, right, top, bottom } = boundary;
|
|
31
|
+
if (left !== void 0) this.left = left;
|
|
32
|
+
if (right !== void 0) this.right = right;
|
|
33
|
+
if (top !== void 0) this.top = top;
|
|
34
|
+
if (bottom !== void 0) this.bottom = bottom;
|
|
35
|
+
}
|
|
36
|
+
get left() {
|
|
37
|
+
return inset(this.#x(), "start");
|
|
38
|
+
}
|
|
39
|
+
get right() {
|
|
40
|
+
return inset(this.#x(), "end");
|
|
41
|
+
}
|
|
42
|
+
get top() {
|
|
43
|
+
return inset(this.#y(), "start");
|
|
44
|
+
}
|
|
45
|
+
get bottom() {
|
|
46
|
+
return inset(this.#y(), "end");
|
|
47
|
+
}
|
|
48
|
+
set left(v) {
|
|
49
|
+
this.#x(v === null ? null : {
|
|
50
|
+
align: "start",
|
|
51
|
+
at: v
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
set right(v) {
|
|
55
|
+
this.#x(v === null ? null : {
|
|
56
|
+
align: "end",
|
|
57
|
+
at: v
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
set top(v) {
|
|
61
|
+
this.#y(v === null ? null : {
|
|
62
|
+
align: "start",
|
|
63
|
+
at: v
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
set bottom(v) {
|
|
67
|
+
this.#y(v === null ? null : {
|
|
68
|
+
align: "end",
|
|
69
|
+
at: v
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
place(box) {
|
|
22
73
|
return {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
74
|
+
x: placeAxis(this.#x(), box.x, box.w),
|
|
75
|
+
y: placeAxis(this.#y(), box.y, box.h),
|
|
76
|
+
w: box.w,
|
|
77
|
+
h: box.h
|
|
27
78
|
};
|
|
28
79
|
}
|
|
29
|
-
|
|
30
|
-
top: () => source.top,
|
|
31
|
-
left: () => source.left,
|
|
32
|
-
width: () => source.width,
|
|
33
|
-
height: () => source.height
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Resolves a custom property holding a length to pixels. Plain `px` values
|
|
38
|
-
* parse directly; anything else (`svh` / `calc()` / fractions of the
|
|
39
|
-
* constraint) resolves natively by measuring a hidden probe. `fallback` is
|
|
40
|
-
* a CSS length expression used when the property is unset.
|
|
41
|
-
*/
|
|
42
|
-
function resolveVarPx(overlay, name, axis, fallback) {
|
|
43
|
-
const raw = getComputedStyle(overlay).getPropertyValue(name).trim();
|
|
44
|
-
if (/^-?\d+(\.\d+)?px$/.test(raw)) return parseFloat(raw);
|
|
45
|
-
const probe = document.createElement("div");
|
|
46
|
-
probe.style.position = "absolute";
|
|
47
|
-
probe.style.visibility = "hidden";
|
|
48
|
-
probe.style[axis] = fallback ? `var(${name}, ${fallback})` : `var(${name})`;
|
|
49
|
-
overlay.appendChild(probe);
|
|
50
|
-
const px = probe.getBoundingClientRect()[axis];
|
|
51
|
-
probe.remove();
|
|
52
|
-
return px;
|
|
53
|
-
}
|
|
54
|
-
/** Resolves the constraint rect every gesture bound derives from. */
|
|
55
|
-
function resolveConstraint(overlay) {
|
|
56
|
-
return {
|
|
57
|
-
top: resolveVarPx(overlay, "--overlay-constraint-top", "height"),
|
|
58
|
-
left: resolveVarPx(overlay, "--overlay-constraint-left", "width"),
|
|
59
|
-
width: resolveVarPx(overlay, "--overlay-constraint-width", "width"),
|
|
60
|
-
height: resolveVarPx(overlay, "--overlay-constraint-height", "height")
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Confines an overlay to a {@link Region} by syncing it into the
|
|
65
|
-
* `--overlay-constraint-*` variables. Every location clamp and gesture
|
|
66
|
-
* bound derives from those variables, so the overlay re-clamps when the
|
|
67
|
-
* region changes.
|
|
68
|
-
*
|
|
69
|
-
* Caveat: an element region observes size changes (`ResizeObserver`) — a
|
|
70
|
-
* container that moves without resizing (e.g. page scroll) does not
|
|
71
|
-
* retrigger the sync.
|
|
72
|
-
*
|
|
73
|
-
* Registers its cleanup with the current scope (`onCleanup`) and also
|
|
74
|
-
* returns it as `dispose` / `Symbol.dispose`; disposing removes the
|
|
75
|
-
* variables, restoring the viewport constraint.
|
|
76
|
-
*
|
|
77
|
-
* @example
|
|
78
|
-
* ```ts
|
|
79
|
-
* import { constraint, confine } from "elements-kit/ui/overlay";
|
|
80
|
-
*
|
|
81
|
-
* const panel = document.querySelector("dialog.x-overlay")!;
|
|
82
|
-
* confine(panel, constraint(document.querySelector("main")!));
|
|
83
|
-
* ```
|
|
84
|
-
*/
|
|
85
|
-
function confine(overlay, region) {
|
|
86
|
-
const stop = effect(() => {
|
|
87
|
-
overlay.style.setProperty("--overlay-constraint-top", `${region.top()}px`);
|
|
88
|
-
overlay.style.setProperty("--overlay-constraint-left", `${region.left()}px`);
|
|
89
|
-
overlay.style.setProperty("--overlay-constraint-width", `${region.width()}px`);
|
|
90
|
-
overlay.style.setProperty("--overlay-constraint-height", `${region.height()}px`);
|
|
91
|
-
});
|
|
92
|
-
const dispose = () => {
|
|
93
|
-
stop();
|
|
94
|
-
overlay.style.removeProperty("--overlay-constraint-top");
|
|
95
|
-
overlay.style.removeProperty("--overlay-constraint-left");
|
|
96
|
-
overlay.style.removeProperty("--overlay-constraint-width");
|
|
97
|
-
overlay.style.removeProperty("--overlay-constraint-height");
|
|
98
|
-
};
|
|
99
|
-
onCleanup(dispose);
|
|
100
|
-
return {
|
|
101
|
-
dispose,
|
|
102
|
-
[Symbol.dispose]: dispose
|
|
103
|
-
};
|
|
104
|
-
}
|
|
80
|
+
};
|
|
105
81
|
//#endregion
|
|
106
|
-
//#region src/ui/overlay/
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const hostRect = host.getBoundingClientRect();
|
|
124
|
-
const rect = pin.getBoundingClientRect();
|
|
125
|
-
host.remove();
|
|
126
|
-
pin.remove();
|
|
127
|
-
if (Math.abs(hostRect.top - 85) > 1) return true;
|
|
128
|
-
return Math.abs(rect.top - 85) < 1 && Math.abs(rect.left - 70) < 1 && Math.abs(rect.width - 40) < 1;
|
|
129
|
-
}
|
|
130
|
-
/** The follow pin needs more than the placement gate — the chain rule
|
|
131
|
-
* mirrors the followed box with `anchor()`/`anchor-size()`. A browser
|
|
132
|
-
* that places but can't chain falls back to the JS rect copy. */
|
|
133
|
-
const chainSupport = () => {
|
|
134
|
-
if (!nativeAnchorSupport() || !CSS.supports("top: anchor(top)") || !CSS.supports("width: anchor-size(width)")) return false;
|
|
135
|
-
chainMeasured ??= probeChain();
|
|
136
|
-
return chainMeasured;
|
|
82
|
+
//#region src/ui/overlay/box.ts
|
|
83
|
+
var WindowBox = class {
|
|
84
|
+
get x() {
|
|
85
|
+
return 0;
|
|
86
|
+
}
|
|
87
|
+
get y() {
|
|
88
|
+
return 0;
|
|
89
|
+
}
|
|
90
|
+
get w() {
|
|
91
|
+
return windowSize.width();
|
|
92
|
+
}
|
|
93
|
+
get h() {
|
|
94
|
+
return windowSize.height();
|
|
95
|
+
}
|
|
96
|
+
get direction() {
|
|
97
|
+
return direction();
|
|
98
|
+
}
|
|
137
99
|
};
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Maps a `position-area` value to the Floating UI placement. Spanning
|
|
145
|
-
* toward an edge leaves the box flush with the *opposite* edge, so
|
|
146
|
-
* `span-*-end` is a `-start` alignment (and vice versa) — Floating UI
|
|
147
|
-
* resolves `-start`/`-end` logically, so the span tokens need no dir
|
|
148
|
-
* check; only the physical inline sides do. Unknown values fall back to
|
|
149
|
-
* `bottom` (the CSS default, `block-end`).
|
|
150
|
-
*/
|
|
151
|
-
function areaToPlacement(area, rtl = false) {
|
|
152
|
-
const tokens = area.trim().split(/\s+/);
|
|
153
|
-
const main = tokens.find((t) => !t.startsWith("span-"));
|
|
154
|
-
const span = tokens.find((t) => t.startsWith("span-")) ?? "";
|
|
155
|
-
return `${main === "block-start" ? "top" : main === "inline-start" ? rtl ? "right" : "left" : main === "inline-end" ? rtl ? "left" : "right" : "bottom"}${span.endsWith("-end") ? "-start" : span.endsWith("-start") ? "-end" : ""}`;
|
|
156
|
-
}
|
|
157
|
-
/**
|
|
158
|
-
* Gives `overlay` its anchor element and returns it. The overlay follows
|
|
159
|
-
* the anchor for life; the anchor follows `follow` (element or rect)
|
|
160
|
-
* until something moves it. Pass the returned element to `draggable()`
|
|
161
|
-
* to make the composition tearable — dragging moves the anchor.
|
|
162
|
-
*
|
|
163
|
-
* `follow` may be a getter reading a signal — re-pinning on change is
|
|
164
|
-
* how a shared popover slides between nav triggers: the native chain
|
|
165
|
-
* glides there on the anchor element's CSS transition; the channel
|
|
166
|
-
* engine lets that one write animate.
|
|
167
|
-
*
|
|
168
|
-
* Registers all cleanup with the current scope (`onCleanup`): the anchor
|
|
169
|
-
* element, the wiring, and the stamped attributes are removed together.
|
|
170
|
-
*
|
|
171
|
-
* @example
|
|
172
|
-
* ```ts
|
|
173
|
-
* import { anchor, draggable, rubber } from "elements-kit/ui/overlay";
|
|
174
|
-
*
|
|
175
|
-
* const a = anchor(panel, trigger);
|
|
176
|
-
* draggable(a, undefined, rubber()).attach(panel);
|
|
177
|
-
*
|
|
178
|
-
* // shared nav popover — re-anchors (and glides) when the signal changes
|
|
179
|
-
* const active = signal(firstTrigger);
|
|
180
|
-
* anchor(menu, () => active(), { arrow: true });
|
|
181
|
-
* ```
|
|
182
|
-
*/
|
|
183
|
-
function anchor(overlay, follow, opts = {}) {
|
|
184
|
-
const native = nativeAnchorSupport() && !opts.within && !opts.arrow;
|
|
185
|
-
let disposed = false;
|
|
186
|
-
const resolveFollow = () => (typeof follow === "function" ? follow() : follow) ?? void 0;
|
|
187
|
-
const el = document.createElement("span");
|
|
188
|
-
el.className = "x-overlay-anchor";
|
|
189
|
-
el.setAttribute("aria-hidden", "true");
|
|
190
|
-
if (!native) el.style.transitionProperty = "none";
|
|
191
|
-
document.body.append(el);
|
|
192
|
-
const proxyName = `--overlay-anchor-${anchorNames++}`;
|
|
193
|
-
const followName = `--overlay-follow-${anchorNames++}`;
|
|
194
|
-
const placeAtRect = (rect) => {
|
|
195
|
-
el.style.top = `${rect.top}px`;
|
|
196
|
-
el.style.left = `${rect.left}px`;
|
|
197
|
-
el.style.width = `${rect.width}px`;
|
|
198
|
-
el.style.height = `${rect.height}px`;
|
|
199
|
-
};
|
|
200
|
-
/** Pin the anchor element to a follow target (the `data-follow`
|
|
201
|
-
* contract — `draggable()` tears it on the first pointer-down).
|
|
202
|
-
* Re-pinning to another element keeps the SAME `position-anchor` name,
|
|
203
|
-
* so under the native chain the proxy's `anchor()` insets resolve to
|
|
204
|
-
* the new box and its CSS transition GLIDES there — the nav slide. */
|
|
205
|
-
let pinnedEl;
|
|
206
|
-
let hasPinned = false;
|
|
207
|
-
let stopFollowSync;
|
|
208
|
-
const releasePinMachinery = () => {
|
|
209
|
-
stopFollowSync?.();
|
|
210
|
-
stopFollowSync = void 0;
|
|
211
|
-
pinnedEl?.style.removeProperty("anchor-name");
|
|
212
|
-
pinnedEl = void 0;
|
|
213
|
-
};
|
|
214
|
-
const pin = (target) => {
|
|
215
|
-
const repositioning = hasPinned;
|
|
216
|
-
const flip = repositioning && target instanceof Element && chainSupport();
|
|
217
|
-
if (flip) placeAtRect(el.getBoundingClientRect());
|
|
218
|
-
releasePinMachinery();
|
|
219
|
-
if (!target) {
|
|
220
|
-
el.style.top = "50vh";
|
|
221
|
-
el.style.left = "50vw";
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
hasPinned = true;
|
|
225
|
-
if (!(target instanceof Element)) {
|
|
226
|
-
el.removeAttribute("data-follow");
|
|
227
|
-
placeAtRect(target);
|
|
228
|
-
return;
|
|
229
|
-
}
|
|
230
|
-
el.setAttribute("data-follow", "");
|
|
231
|
-
if (chainSupport()) {
|
|
232
|
-
target.style?.setProperty("anchor-name", followName);
|
|
233
|
-
pinnedEl = target;
|
|
234
|
-
el.style.setProperty("position-anchor", followName);
|
|
235
|
-
if (flip) el.offsetTop;
|
|
236
|
-
el.style.removeProperty("top");
|
|
237
|
-
el.style.removeProperty("left");
|
|
238
|
-
el.style.removeProperty("width");
|
|
239
|
-
el.style.removeProperty("height");
|
|
240
|
-
} else {
|
|
241
|
-
let first = !repositioning;
|
|
242
|
-
const sync = () => {
|
|
243
|
-
if (first) {
|
|
244
|
-
first = false;
|
|
245
|
-
const prev = el.style.transitionProperty;
|
|
246
|
-
el.style.transitionProperty = "none";
|
|
247
|
-
placeAtRect(target.getBoundingClientRect());
|
|
248
|
-
el.offsetTop;
|
|
249
|
-
if (prev) el.style.transitionProperty = prev;
|
|
250
|
-
else el.style.removeProperty("transition-property");
|
|
251
|
-
return;
|
|
252
|
-
}
|
|
253
|
-
placeAtRect(target.getBoundingClientRect());
|
|
254
|
-
};
|
|
255
|
-
stopFollowSync = autoUpdate(target, el, sync);
|
|
256
|
-
}
|
|
257
|
-
};
|
|
258
|
-
const unpinCleanup = () => releasePinMachinery();
|
|
259
|
-
const followObserver = new MutationObserver(() => {
|
|
260
|
-
if (!el.hasAttribute("data-follow")) unpinCleanup();
|
|
261
|
-
});
|
|
262
|
-
followObserver.observe(el, {
|
|
263
|
-
attributes: true,
|
|
264
|
-
attributeFilter: ["data-follow"]
|
|
265
|
-
});
|
|
266
|
-
const stampPlacedHint = () => {
|
|
267
|
-
const style = getComputedStyle(overlay);
|
|
268
|
-
const hint = areaToPlacement(style.getPropertyValue("--overlay-area"), style.direction === "rtl");
|
|
269
|
-
overlay.setAttribute("data-placed", hint.split("-")[0]);
|
|
270
|
-
};
|
|
271
|
-
const isOpen = () => {
|
|
272
|
-
if (overlay.hasAttribute("open")) return true;
|
|
273
|
-
try {
|
|
274
|
-
return overlay.matches(":popover-open");
|
|
275
|
-
} catch {
|
|
276
|
-
return false;
|
|
277
|
-
}
|
|
278
|
-
};
|
|
279
|
-
let haltLoop;
|
|
280
|
-
let engineDispose;
|
|
281
|
-
/** Channel engine only: reposition against a re-pinned target, letting
|
|
282
|
-
* that one write animate (the morph to the new anchor). */
|
|
283
|
-
let onRepoint;
|
|
284
|
-
if (native) {
|
|
285
|
-
overlay.setAttribute("data-anchor", "element");
|
|
286
|
-
el.style.setProperty("anchor-name", proxyName);
|
|
287
|
-
overlay.style.setProperty("position-anchor", proxyName);
|
|
288
|
-
engineDispose = () => {
|
|
289
|
-
const rect = overlay.getBoundingClientRect();
|
|
290
|
-
if (isOpen() && rect.width > 0) {
|
|
291
|
-
const c = resolveConstraint(overlay);
|
|
292
|
-
overlay.style.setProperty("--overlay-x", `${rect.left + rect.width / 2 - c.left}px`);
|
|
293
|
-
overlay.style.setProperty("--overlay-y", `${rect.top + rect.height / 2 - c.top}px`);
|
|
294
|
-
}
|
|
295
|
-
overlay.style.removeProperty("position-anchor");
|
|
296
|
-
overlay.removeAttribute("data-anchor");
|
|
297
|
-
};
|
|
298
|
-
} else {
|
|
299
|
-
let arrowEl = null;
|
|
300
|
-
let arrowOwned = false;
|
|
301
|
-
if (opts.arrow) {
|
|
302
|
-
arrowEl = overlay.querySelector(":scope > .x-overlay-arrow");
|
|
303
|
-
if (!arrowEl) {
|
|
304
|
-
arrowEl = document.createElement("span");
|
|
305
|
-
arrowEl.className = "x-overlay-arrow";
|
|
306
|
-
arrowEl.setAttribute("aria-hidden", "true");
|
|
307
|
-
overlay.append(arrowEl);
|
|
308
|
-
arrowOwned = true;
|
|
309
|
-
}
|
|
310
|
-
if (typeof opts.arrow === "number") overlay.style.setProperty("--overlay-arrow-size", `${opts.arrow}px`);
|
|
311
|
-
}
|
|
312
|
-
const boundaryRect = () => {
|
|
313
|
-
const w = opts.within;
|
|
314
|
-
if (!w) return void 0;
|
|
315
|
-
return {
|
|
316
|
-
x: w.left(),
|
|
317
|
-
y: w.top(),
|
|
318
|
-
width: w.width(),
|
|
319
|
-
height: w.height()
|
|
320
|
-
};
|
|
321
|
-
};
|
|
322
|
-
const update = async () => {
|
|
323
|
-
const style = getComputedStyle(overlay);
|
|
324
|
-
const placementHint = areaToPlacement(style.getPropertyValue("--overlay-area"), style.direction === "rtl");
|
|
325
|
-
const gapPx = resolveVarPx(overlay, "--overlay-gap", "width", "var(--space-2, 8px)");
|
|
326
|
-
const boundary = boundaryRect();
|
|
327
|
-
const middleware = [
|
|
328
|
-
offset(gapPx),
|
|
329
|
-
flip(boundary ? { boundary } : void 0),
|
|
330
|
-
shift(boundary ? { boundary } : void 0)
|
|
331
|
-
];
|
|
332
|
-
if (arrowEl) middleware.push(arrow({ element: arrowEl }));
|
|
333
|
-
const { x, y, placement, middlewareData } = await computePosition(el, overlay, {
|
|
334
|
-
strategy: "fixed",
|
|
335
|
-
placement: placementHint,
|
|
336
|
-
middleware
|
|
337
|
-
});
|
|
338
|
-
if (disposed) return;
|
|
339
|
-
const constraint = resolveConstraint(overlay);
|
|
340
|
-
overlay.style.setProperty("--overlay-x", `${x + overlay.offsetWidth / 2 - constraint.left}px`);
|
|
341
|
-
overlay.style.setProperty("--overlay-y", `${y + overlay.offsetHeight / 2 - constraint.top}px`);
|
|
342
|
-
if (placement) overlay.setAttribute("data-placed", placement.split("-")[0]);
|
|
343
|
-
const a = middlewareData.arrow;
|
|
344
|
-
if (a) {
|
|
345
|
-
if (a.x !== void 0) overlay.style.setProperty("--overlay-arrow-x", `${a.x}px`);
|
|
346
|
-
if (a.y !== void 0) overlay.style.setProperty("--overlay-arrow-y", `${a.y}px`);
|
|
347
|
-
}
|
|
348
|
-
};
|
|
349
|
-
let stop;
|
|
350
|
-
let morphIn = isOpen();
|
|
351
|
-
const suppress = () => overlay.style.setProperty("transition-property", TRACKING_TRANSITIONS);
|
|
352
|
-
const release = () => overlay.style.removeProperty("transition-property");
|
|
353
|
-
const startLoop = () => {
|
|
354
|
-
if (stop) return;
|
|
355
|
-
const instant = !morphIn;
|
|
356
|
-
morphIn = false;
|
|
357
|
-
if (instant) suppress();
|
|
358
|
-
else if (!overlay.style.getPropertyValue("--overlay-x")) {
|
|
359
|
-
const rect = overlay.getBoundingClientRect();
|
|
360
|
-
const c = resolveConstraint(overlay);
|
|
361
|
-
overlay.style.setProperty("--overlay-x", `${rect.left + rect.width / 2 - c.left}px`);
|
|
362
|
-
overlay.style.setProperty("--overlay-y", `${rect.top + rect.height / 2 - c.top}px`);
|
|
363
|
-
}
|
|
364
|
-
let pending = instant;
|
|
365
|
-
stop = autoUpdate(el, overlay, () => update().then(() => {
|
|
366
|
-
if (pending && !disposed) {
|
|
367
|
-
pending = false;
|
|
368
|
-
release();
|
|
369
|
-
}
|
|
370
|
-
}));
|
|
371
|
-
};
|
|
372
|
-
haltLoop = () => {
|
|
373
|
-
stop?.();
|
|
374
|
-
stop = void 0;
|
|
375
|
-
release();
|
|
376
|
-
};
|
|
377
|
-
onRepoint = () => {
|
|
378
|
-
if (stop) update();
|
|
379
|
-
};
|
|
380
|
-
const sync = () => {
|
|
381
|
-
if (disposed) return;
|
|
382
|
-
if (isOpen()) startLoop();
|
|
383
|
-
else haltLoop?.();
|
|
384
|
-
};
|
|
385
|
-
const onDragMove = () => {
|
|
386
|
-
suppress();
|
|
387
|
-
update();
|
|
388
|
-
};
|
|
389
|
-
const onDragEnd = () => {
|
|
390
|
-
if (disposed || !stop) return;
|
|
391
|
-
release();
|
|
392
|
-
update();
|
|
393
|
-
};
|
|
394
|
-
overlay.addEventListener("toggle", sync);
|
|
395
|
-
overlay.addEventListener("close", sync);
|
|
396
|
-
el.addEventListener("dragmove", onDragMove);
|
|
397
|
-
el.addEventListener("dragend", onDragEnd);
|
|
398
|
-
const openObserver = new MutationObserver(sync);
|
|
399
|
-
openObserver.observe(overlay, {
|
|
400
|
-
attributes: true,
|
|
401
|
-
attributeFilter: ["open"]
|
|
402
|
-
});
|
|
403
|
-
sync();
|
|
404
|
-
engineDispose = () => {
|
|
405
|
-
haltLoop?.();
|
|
406
|
-
openObserver.disconnect();
|
|
407
|
-
overlay.removeEventListener("toggle", sync);
|
|
408
|
-
overlay.removeEventListener("close", sync);
|
|
409
|
-
el.removeEventListener("dragmove", onDragMove);
|
|
410
|
-
el.removeEventListener("dragend", onDragEnd);
|
|
411
|
-
overlay.style.removeProperty("--overlay-arrow-x");
|
|
412
|
-
overlay.style.removeProperty("--overlay-arrow-y");
|
|
413
|
-
if (typeof opts.arrow === "number") overlay.style.removeProperty("--overlay-arrow-size");
|
|
414
|
-
if (arrowOwned) arrowEl?.remove();
|
|
415
|
-
};
|
|
100
|
+
const WINDOW_BOX = new WindowBox();
|
|
101
|
+
var ElementBox = class {
|
|
102
|
+
#rect;
|
|
103
|
+
constructor(el) {
|
|
104
|
+
this.#rect = createElementRect(el);
|
|
416
105
|
}
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
repinObserver.observe(overlay, {
|
|
434
|
-
attributes: true,
|
|
435
|
-
attributeFilter: ["open"]
|
|
436
|
-
});
|
|
437
|
-
stampPlacedHint();
|
|
438
|
-
let stopFollowEffect;
|
|
439
|
-
if (typeof follow === "function") {
|
|
440
|
-
let first = true;
|
|
441
|
-
stopFollowEffect = effect(() => {
|
|
442
|
-
pin(resolveFollow());
|
|
443
|
-
if (!first) onRepoint?.();
|
|
444
|
-
first = false;
|
|
445
|
-
});
|
|
446
|
-
} else pin(resolveFollow());
|
|
447
|
-
const dispose = () => {
|
|
448
|
-
disposed = true;
|
|
449
|
-
stopFollowEffect?.();
|
|
450
|
-
engineDispose?.();
|
|
451
|
-
unpinCleanup();
|
|
452
|
-
followObserver.disconnect();
|
|
453
|
-
repinObserver.disconnect();
|
|
454
|
-
overlay.removeEventListener("toggle", repin);
|
|
455
|
-
overlay.removeAttribute("data-placed");
|
|
456
|
-
el.remove();
|
|
457
|
-
};
|
|
458
|
-
onCleanup(dispose);
|
|
459
|
-
return el;
|
|
460
|
-
}
|
|
461
|
-
//#endregion
|
|
462
|
-
//#region src/ui/overlay/resize-strategy.ts
|
|
463
|
-
/**
|
|
464
|
-
* Resize strategies — pluggable policy for a resize drag's live bounds
|
|
465
|
-
* and resting size. Pure (no DOM); the gesture injects the context.
|
|
466
|
-
* Built-in: `freeResize` (default). `detents()` (detents.ts) quantizes a
|
|
467
|
-
* constraint region and doubles as a strategy.
|
|
468
|
-
*/
|
|
469
|
-
/** How far (ms) a release velocity is projected when picking a rest. */
|
|
470
|
-
const PROJECTION_MS = 160;
|
|
471
|
-
/** Clamp `value` into `[min, max]`. */
|
|
472
|
-
function clamp(value, min, max) {
|
|
473
|
-
return Math.min(Math.max(value, min), max);
|
|
474
|
-
}
|
|
475
|
-
/**
|
|
476
|
-
* Picks the index of the detent closest to the released size, projected
|
|
477
|
-
* along the release velocity (px/ms, positive = shrinking). Returns `-1`
|
|
478
|
-
* when the gesture should dismiss instead.
|
|
479
|
-
*/
|
|
480
|
-
function closestDetent(sizePx, detentsPx, velocityPxPerMs = 0, dismissible = false, velocityThreshold = .5) {
|
|
481
|
-
const projected = sizePx - velocityPxPerMs * 160;
|
|
482
|
-
if (dismissible) {
|
|
483
|
-
const smallest = detentsPx[0] ?? 0;
|
|
484
|
-
if (projected < smallest / 2) return -1;
|
|
485
|
-
if (sizePx < smallest && velocityPxPerMs > velocityThreshold) return -1;
|
|
486
|
-
}
|
|
487
|
-
let best = 0;
|
|
488
|
-
for (let i = 1; i < detentsPx.length; i++) if (Math.abs(detentsPx[i] - projected) < Math.abs(detentsPx[best] - projected)) best = i;
|
|
489
|
-
return best;
|
|
490
|
-
}
|
|
491
|
-
/**
|
|
492
|
-
* Free resize: drag to any size within the room; a flick or shrink past
|
|
493
|
-
* the minimum dismisses. The default strategy.
|
|
494
|
-
*/
|
|
495
|
-
function freeResize(opts) {
|
|
496
|
-
return {
|
|
497
|
-
bounds: (ctx) => [opts?.min ?? ctx.min, ctx.max],
|
|
498
|
-
rest: (ctx) => {
|
|
499
|
-
const lo = opts?.min ?? ctx.min;
|
|
500
|
-
const projected = ctx.size - ctx.velocity * 160;
|
|
501
|
-
if (ctx.dismissible && (projected < lo / 2 || ctx.size < lo && ctx.velocity > ctx.velocityThreshold)) return null;
|
|
502
|
-
return clamp(ctx.size, lo, ctx.max);
|
|
503
|
-
}
|
|
504
|
-
};
|
|
505
|
-
}
|
|
106
|
+
[Symbol.dispose]() {
|
|
107
|
+
this.#rect[Symbol.dispose]();
|
|
108
|
+
}
|
|
109
|
+
get x() {
|
|
110
|
+
return this.#rect().left;
|
|
111
|
+
}
|
|
112
|
+
get y() {
|
|
113
|
+
return this.#rect().top;
|
|
114
|
+
}
|
|
115
|
+
get w() {
|
|
116
|
+
return this.#rect().width;
|
|
117
|
+
}
|
|
118
|
+
get h() {
|
|
119
|
+
return this.#rect().height;
|
|
120
|
+
}
|
|
121
|
+
};
|
|
506
122
|
//#endregion
|
|
507
|
-
//#region src/ui/overlay/
|
|
508
|
-
/**
|
|
509
|
-
* Quantizes a region: each stop is a fraction of the region along the
|
|
510
|
-
* axis (number `0–1`) or a CSS length (string, resize-only). Flick-aware
|
|
511
|
-
* on release; shrinking past the smallest stop dismisses (when the
|
|
512
|
-
* consumer allows dismissal).
|
|
513
|
-
*
|
|
514
|
-
* @example
|
|
515
|
-
* ```ts
|
|
516
|
-
* import { constraint, detents } from "elements-kit/ui/overlay";
|
|
517
|
-
*
|
|
518
|
-
* const d = detents(constraint(), [0.25, 0.6, 0.9]); // of the viewport
|
|
519
|
-
* ```
|
|
520
|
-
*/
|
|
521
|
-
function detents(region, stops) {
|
|
522
|
-
const resolved = (ctx) => stops.map((s) => clamp(ctx.resolve(s), ctx.min, ctx.max)).sort((a, b) => a - b);
|
|
523
|
-
return {
|
|
524
|
-
region,
|
|
525
|
-
stops,
|
|
526
|
-
positions(axis) {
|
|
527
|
-
const size = axis === "width" ? region.width() : region.height();
|
|
528
|
-
return stops.filter((s) => typeof s === "number").map((s) => s * size).sort((a, b) => a - b);
|
|
529
|
-
},
|
|
530
|
-
bounds(ctx) {
|
|
531
|
-
const s = resolved(ctx);
|
|
532
|
-
return [s[0] ?? ctx.min, s[s.length - 1] ?? ctx.max];
|
|
533
|
-
},
|
|
534
|
-
rest(ctx) {
|
|
535
|
-
const s = resolved(ctx);
|
|
536
|
-
const i = closestDetent(ctx.size, s, ctx.velocity, ctx.dismissible, ctx.velocityThreshold);
|
|
537
|
-
return i === -1 ? null : s[i];
|
|
538
|
-
}
|
|
539
|
-
};
|
|
540
|
-
}
|
|
541
|
-
/** Clamp with rubber-band resistance past either bound. */
|
|
542
|
-
function resist(value, min, max, resistance = 3) {
|
|
543
|
-
if (value > max) return max + (value - max) / resistance;
|
|
544
|
-
if (value < min) return min - (min - value) / resistance;
|
|
545
|
-
return value;
|
|
546
|
-
}
|
|
123
|
+
//#region src/ui/overlay/anchor.ts
|
|
547
124
|
/**
|
|
548
|
-
* The
|
|
549
|
-
*
|
|
550
|
-
*
|
|
551
|
-
* (`end-end`, `start-end`).
|
|
125
|
+
* The reactive `anchor()` function: the viewport coordinate of an anchor line
|
|
126
|
+
* on `box`, in the context of the inset property being computed. Reads the
|
|
127
|
+
* box's reactive geometry, so calling it inside an `effect` tracks the anchor.
|
|
552
128
|
*/
|
|
553
|
-
function
|
|
554
|
-
const
|
|
555
|
-
const
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
129
|
+
function anchor_length(box, inset, side) {
|
|
130
|
+
const physical = resolveInset(inset);
|
|
131
|
+
const block = physical === "top" || physical === "bottom";
|
|
132
|
+
const lo = block ? box.y : box.x;
|
|
133
|
+
const size = block ? box.h : box.w;
|
|
134
|
+
return lo + fraction(box, block, physical, side) * size;
|
|
135
|
+
}
|
|
136
|
+
/** A side, as a fraction of the axis measured from its top/left edge. */
|
|
137
|
+
function fraction(box, block, inset, side) {
|
|
138
|
+
switch (side) {
|
|
139
|
+
case "top":
|
|
140
|
+
case "left": return 0;
|
|
141
|
+
case "bottom":
|
|
142
|
+
case "right": return 1;
|
|
143
|
+
case "inside": return inset === "top" || inset === "left" ? 0 : 1;
|
|
144
|
+
case "outside": return inset === "top" || inset === "left" ? 1 : 0;
|
|
145
|
+
case "center": return .5;
|
|
146
|
+
}
|
|
147
|
+
const logical = typeof side === "number" ? side : side.endsWith("end") ? 1 : 0;
|
|
148
|
+
const rtl = side === "self-start" || side === "self-end" ? (box.direction ?? rootDirection()) === "rtl" : rootDirection() === "rtl";
|
|
149
|
+
return !block && rtl ? 1 - logical : logical;
|
|
150
|
+
}
|
|
151
|
+
/** The document root's direction — the fallback when a box has no own. */
|
|
152
|
+
function rootDirection() {
|
|
153
|
+
return getComputedStyle(document.documentElement).direction === "rtl" ? "rtl" : "ltr";
|
|
154
|
+
}
|
|
155
|
+
/** A logical inset longhand → its physical side, through the containing
|
|
156
|
+
* block's direction (horizontal writing modes — only inline flips). */
|
|
157
|
+
function resolveInset(inset) {
|
|
158
|
+
switch (inset) {
|
|
159
|
+
case "inset-block-start": return "top";
|
|
160
|
+
case "inset-block-end": return "bottom";
|
|
161
|
+
case "inset-inline-start": return getComputedStyle(document.documentElement).direction === "rtl" ? "right" : "left";
|
|
162
|
+
case "inset-inline-end": return getComputedStyle(document.documentElement).direction === "rtl" ? "left" : "right";
|
|
163
|
+
default: return inset;
|
|
164
|
+
}
|
|
574
165
|
}
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
166
|
+
const B = (region, physical = false) => ({
|
|
167
|
+
axis: "block",
|
|
168
|
+
region,
|
|
169
|
+
physical,
|
|
170
|
+
self: false
|
|
171
|
+
});
|
|
172
|
+
const I = (region, physical = false) => ({
|
|
173
|
+
axis: "inline",
|
|
174
|
+
region,
|
|
175
|
+
physical,
|
|
176
|
+
self: false
|
|
177
|
+
});
|
|
178
|
+
const A = (region, self = false) => ({
|
|
179
|
+
axis: "ambiguous",
|
|
180
|
+
region,
|
|
181
|
+
physical: false,
|
|
182
|
+
self
|
|
183
|
+
});
|
|
184
|
+
/** The full `position-area` keyword grammar → axis + physical region. */
|
|
185
|
+
const KEYWORDS = {
|
|
186
|
+
top: B("start", true),
|
|
187
|
+
bottom: B("end", true),
|
|
188
|
+
"span-top": B("span-start", true),
|
|
189
|
+
"span-bottom": B("span-end", true),
|
|
190
|
+
left: I("start", true),
|
|
191
|
+
right: I("end", true),
|
|
192
|
+
"span-left": I("span-start", true),
|
|
193
|
+
"span-right": I("span-end", true),
|
|
194
|
+
center: {
|
|
195
|
+
axis: "ambiguous",
|
|
196
|
+
region: "center",
|
|
197
|
+
physical: true,
|
|
198
|
+
self: false
|
|
199
|
+
},
|
|
200
|
+
"span-all": {
|
|
201
|
+
axis: "ambiguous",
|
|
202
|
+
region: "span-all",
|
|
203
|
+
physical: true,
|
|
204
|
+
self: false
|
|
205
|
+
},
|
|
206
|
+
"block-start": B("start"),
|
|
207
|
+
"block-end": B("end"),
|
|
208
|
+
"span-block-start": B("span-start"),
|
|
209
|
+
"span-block-end": B("span-end"),
|
|
210
|
+
"y-start": B("start"),
|
|
211
|
+
"y-end": B("end"),
|
|
212
|
+
"span-y-start": B("span-start"),
|
|
213
|
+
"span-y-end": B("span-end"),
|
|
214
|
+
"inline-start": I("start"),
|
|
215
|
+
"inline-end": I("end"),
|
|
216
|
+
"span-inline-start": I("span-start"),
|
|
217
|
+
"span-inline-end": I("span-end"),
|
|
218
|
+
"x-start": I("start"),
|
|
219
|
+
"x-end": I("end"),
|
|
220
|
+
"span-x-start": I("span-start"),
|
|
221
|
+
"span-x-end": I("span-end"),
|
|
222
|
+
start: A("start"),
|
|
223
|
+
end: A("end"),
|
|
224
|
+
"span-start": A("span-start"),
|
|
225
|
+
"span-end": A("span-end"),
|
|
226
|
+
"self-start": A("start", true),
|
|
227
|
+
"self-end": A("end", true),
|
|
228
|
+
"span-self-start": A("span-start", true),
|
|
229
|
+
"span-self-end": A("span-end", true)
|
|
230
|
+
};
|
|
231
|
+
const SPAN_ALL = KEYWORDS["span-all"];
|
|
232
|
+
function flipRegion(r) {
|
|
233
|
+
return r === "start" ? "end" : r === "end" ? "start" : r === "span-start" ? "span-end" : r === "span-end" ? "span-start" : r;
|
|
584
234
|
}
|
|
585
|
-
/**
|
|
586
|
-
*
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
const { block, inline, draggable, rect, pointer, dir } = args;
|
|
592
|
-
const corner = block !== null && inline !== null;
|
|
593
|
-
const handleRight = inline === "end" === (dir === 1);
|
|
594
|
-
if (corner && Math.abs(pointer.x - (handleRight ? rect.right : rect.left)) <= 28 && Math.abs(pointer.y - (block === "end" ? rect.bottom : rect.top)) <= 28) return "resize";
|
|
595
|
-
const topCenterResize = block === "start" && inline === null;
|
|
596
|
-
if (draggable && pointer.y - rect.top >= 0 && pointer.y - rect.top <= 28 && (!topCenterResize || Math.abs(pointer.x - (dir === 1 ? rect.left : rect.right)) <= 28)) return "move";
|
|
597
|
-
if (block !== null && inline === null) return "block";
|
|
598
|
-
if (inline !== null && block === null) return "inline";
|
|
599
|
-
return null;
|
|
235
|
+
/** Resolve a keyword to a physical region: block never flips; inline flips
|
|
236
|
+
* in RTL unless the keyword is physical. */
|
|
237
|
+
function toPhysical(kw, axis, dir, selfDir) {
|
|
238
|
+
const r = kw.region;
|
|
239
|
+
if (r === "center" || r === "span-all" || axis === "block" || kw.physical) return r;
|
|
240
|
+
return (kw.self ? selfDir : dir) === "rtl" ? flipRegion(r) : r;
|
|
600
241
|
}
|
|
601
242
|
/**
|
|
602
|
-
*
|
|
603
|
-
*
|
|
604
|
-
*
|
|
605
|
-
* holds it, skip the shift).
|
|
243
|
+
* Parse a `position-area` value into a physical {@link Area}. One or two
|
|
244
|
+
* keywords, order-independent; an axis-specific single keyword spans the
|
|
245
|
+
* other axis, an ambiguous one applies to both. Unknown/empty → `block-end`.
|
|
606
246
|
*/
|
|
607
|
-
function
|
|
608
|
-
const
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
247
|
+
function resolveArea(area, dir = "ltr", selfDir = dir) {
|
|
248
|
+
const tokens = area.trim().toLowerCase().split(/\s+/).filter(Boolean);
|
|
249
|
+
const fallback = () => ({
|
|
250
|
+
block: "end",
|
|
251
|
+
inline: "center"
|
|
252
|
+
});
|
|
253
|
+
if (tokens.length === 0 || tokens.length > 2) return fallback();
|
|
254
|
+
const kws = [];
|
|
255
|
+
for (const token of tokens) {
|
|
256
|
+
const keyword = KEYWORDS[token];
|
|
257
|
+
if (!keyword) return fallback();
|
|
258
|
+
kws.push(keyword);
|
|
259
|
+
}
|
|
260
|
+
const [first, second] = kws;
|
|
261
|
+
if (first && second && first.axis !== "ambiguous" && first.axis === second.axis) return fallback();
|
|
262
|
+
let block;
|
|
263
|
+
let inline;
|
|
264
|
+
if (kws.length === 1) {
|
|
265
|
+
const k = kws[0];
|
|
266
|
+
if (k.axis === "block") block = k, inline = SPAN_ALL;
|
|
267
|
+
else if (k.axis === "inline") inline = k, block = SPAN_ALL;
|
|
268
|
+
else block = k, inline = k;
|
|
269
|
+
} else if (kws.length === 2) {
|
|
270
|
+
for (const k of kws) if (k.axis === "block") block = k;
|
|
271
|
+
else if (k.axis === "inline") inline = k;
|
|
272
|
+
const ambs = kws.filter((k) => k.axis === "ambiguous");
|
|
273
|
+
if (!block && !inline) block = ambs[0], inline = ambs[1] ?? ambs[0];
|
|
274
|
+
else if (!block) block = ambs[0];
|
|
275
|
+
else if (!inline) inline = ambs[0];
|
|
618
276
|
}
|
|
619
|
-
const handleRight = side === "end" === (dir === 1);
|
|
620
|
-
const anchorEdge = handleRight ? rect.left : rect.right;
|
|
621
|
-
const constraintEdge = handleRight ? constraint.left : constraint.left + constraint.width;
|
|
622
277
|
return {
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
278
|
+
block: block ? toPhysical(block, "block", dir, selfDir) : "end",
|
|
279
|
+
inline: inline ? toPhysical(inline, "inline", dir, selfDir) : "center"
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
/** One axis of the position-area containing block, as `[lo, hi]` viewport
|
|
283
|
+
* coordinates. The anchor's two edges cut `[bLo, bHi]` into before/over/after. */
|
|
284
|
+
function axisSpan(lo, hi, bLo, bHi, region) {
|
|
285
|
+
switch (region) {
|
|
286
|
+
case "start": return [bLo, lo];
|
|
287
|
+
case "end": return [hi, bHi];
|
|
288
|
+
case "center": return [lo, hi];
|
|
289
|
+
case "span-start": return [bLo, hi];
|
|
290
|
+
case "span-end": return [lo, bHi];
|
|
291
|
+
default: return [bLo, bHi];
|
|
292
|
+
}
|
|
627
293
|
}
|
|
628
294
|
/**
|
|
629
|
-
*
|
|
630
|
-
*
|
|
631
|
-
*
|
|
295
|
+
* The reactive `position-area` property: the region of the viewport an
|
|
296
|
+
* overlay anchored to `anchor` may occupy, with the region's default
|
|
297
|
+
* self-alignment as {@link Region.place} — outward regions hug the anchor,
|
|
298
|
+
* spans go flush against its far edge, center is `anchor-center`. Nothing is
|
|
299
|
+
* written; the caller takes what it wants from `place`:
|
|
632
300
|
*
|
|
633
|
-
*
|
|
634
|
-
* (
|
|
635
|
-
* box cannot grow — `max-width`/`-height` caps it and the location clamp
|
|
636
|
-
* would shove the *anchored* edge inward — so the size pins to `max` and
|
|
637
|
-
* the resisted overshoot rides the unclamped slide instead, translating
|
|
638
|
-
* the whole surface past the edge (handle-side rubber-band). On release the
|
|
639
|
-
* strategy rests at the room and the slide clears, snapping it back in.
|
|
640
|
-
* - Below `lo` the size pins to `lo` and the surface slides toward its edge
|
|
641
|
-
* (the dismiss preview).
|
|
301
|
+
* const area = new PositionArea(a, "top span-left");
|
|
302
|
+
* effect(() => { overlay.x = area.place(overlay).x; });
|
|
642
303
|
*
|
|
643
|
-
*
|
|
644
|
-
*
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
};
|
|
653
|
-
return {
|
|
654
|
-
size: target,
|
|
655
|
-
slide: null
|
|
656
|
-
};
|
|
657
|
-
}
|
|
658
|
-
/**
|
|
659
|
-
* Cap a resisted size at the room `max`; route the resisted overshoot beyond
|
|
660
|
-
* it to the slide channel (translating the surface past the edge instead of
|
|
661
|
-
* letting the CSS clamp shove the anchored edge). `null` slide within the room.
|
|
662
|
-
* The handle-side rubber-band shared by the edge ({@link edgeDrag}) and corner
|
|
663
|
-
* resizes — both can only grow until the handle reaches the constraint.
|
|
664
|
-
*/
|
|
665
|
-
function slidePastRoom(resisted, max, sign) {
|
|
666
|
-
if (resisted <= max) return {
|
|
667
|
-
size: resisted,
|
|
668
|
-
slide: null
|
|
669
|
-
};
|
|
670
|
-
return {
|
|
671
|
-
size: max,
|
|
672
|
-
slide: sign * (resisted - max)
|
|
673
|
-
};
|
|
674
|
-
}
|
|
675
|
-
/**
|
|
676
|
-
* Pin the opposite edge along one axis: shift the center-anchored location
|
|
677
|
-
* by half the size change. Returns the rect-relative center coordinate, or
|
|
678
|
-
* `null` when docked (the CSS clamp already holds that edge). The single
|
|
679
|
-
* source of anchoring truth for block, inline, and corner resizes.
|
|
304
|
+
* The value is parsed once, at construction; the geometry reads the anchor
|
|
305
|
+
* and window on every access, so reading it inside an `effect` tracks both.
|
|
306
|
+
*
|
|
307
|
+
* Bounded by the window. A tighter bound (a `Constraint`, a scroll container)
|
|
308
|
+
* is an intersection with the region, so it composes afterwards rather than
|
|
309
|
+
* being a parameter here.
|
|
310
|
+
*
|
|
311
|
+
* There is no gap parameter, for the same reason CSS has none: the offset off
|
|
312
|
+
* the anchor is the overlay's own `margin`.
|
|
680
313
|
*/
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
const
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
314
|
+
var PositionArea = class {
|
|
315
|
+
anchor;
|
|
316
|
+
#area;
|
|
317
|
+
constructor(anchor, area) {
|
|
318
|
+
this.anchor = anchor;
|
|
319
|
+
this.#area = resolveArea(area, rootDirection());
|
|
320
|
+
}
|
|
321
|
+
/** The inline axis as `[lo, hi]`, re-read from the anchor and window. */
|
|
322
|
+
get #ix() {
|
|
323
|
+
const a = this.anchor;
|
|
324
|
+
const w = WINDOW_BOX;
|
|
325
|
+
return axisSpan(a.x, a.x + a.w, w.x, w.x + w.w, this.#area.inline);
|
|
326
|
+
}
|
|
327
|
+
/** The block axis as `[lo, hi]`. */
|
|
328
|
+
get #iy() {
|
|
329
|
+
const a = this.anchor;
|
|
330
|
+
const w = WINDOW_BOX;
|
|
331
|
+
return axisSpan(a.y, a.y + a.h, w.y, w.y + w.h, this.#area.block);
|
|
332
|
+
}
|
|
333
|
+
get #px() {
|
|
334
|
+
const a = this.anchor;
|
|
335
|
+
return pinOf(this.#area.inline, a.x, a.x + a.w);
|
|
336
|
+
}
|
|
337
|
+
get #py() {
|
|
338
|
+
const a = this.anchor;
|
|
339
|
+
return pinOf(this.#area.block, a.y, a.y + a.h);
|
|
340
|
+
}
|
|
341
|
+
get x() {
|
|
342
|
+
return this.#ix[0];
|
|
343
|
+
}
|
|
344
|
+
get y() {
|
|
345
|
+
return this.#iy[0];
|
|
346
|
+
}
|
|
347
|
+
get w() {
|
|
348
|
+
const [lo, hi] = this.#ix;
|
|
349
|
+
return hi - lo;
|
|
350
|
+
}
|
|
351
|
+
get h() {
|
|
352
|
+
const [lo, hi] = this.#iy;
|
|
353
|
+
return hi - lo;
|
|
354
|
+
}
|
|
355
|
+
get left() {
|
|
356
|
+
return inset(this.#px, "start");
|
|
357
|
+
}
|
|
358
|
+
get right() {
|
|
359
|
+
return inset(this.#px, "end");
|
|
360
|
+
}
|
|
361
|
+
get top() {
|
|
362
|
+
return inset(this.#py, "start");
|
|
363
|
+
}
|
|
364
|
+
get bottom() {
|
|
365
|
+
return inset(this.#py, "end");
|
|
366
|
+
}
|
|
367
|
+
place(box) {
|
|
721
368
|
return {
|
|
722
|
-
|
|
723
|
-
|
|
369
|
+
x: placeAxis(this.#px, box.x, box.w),
|
|
370
|
+
y: placeAxis(this.#py, box.y, box.h),
|
|
371
|
+
w: box.w,
|
|
372
|
+
h: box.h
|
|
724
373
|
};
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
if (space && isSpace(space)) {
|
|
736
|
-
const stops = space.positions(axis === "x" ? "width" : "height");
|
|
737
|
-
if (stops.length && region) {
|
|
738
|
-
const origin = axis === "x" ? region.left() : region.top();
|
|
739
|
-
const projected = v + velocity * 160;
|
|
740
|
-
let best = origin + stops[0];
|
|
741
|
-
for (const stop of stops) {
|
|
742
|
-
const candidate = origin + stop;
|
|
743
|
-
if (Math.abs(candidate - projected) < Math.abs(best - projected)) best = candidate;
|
|
744
|
-
}
|
|
745
|
-
v = best;
|
|
746
|
-
}
|
|
747
|
-
}
|
|
748
|
-
return v;
|
|
749
|
-
};
|
|
750
|
-
const attachments = [];
|
|
751
|
-
const attach = (handle) => {
|
|
752
|
-
let engaged = null;
|
|
753
|
-
const onDown = (event) => {
|
|
754
|
-
if (engaged || event.button !== 0) return;
|
|
755
|
-
if (event.target?.closest("button, a, label, input, select, textarea, [contenteditable]")) return;
|
|
756
|
-
engaged = {
|
|
757
|
-
start: takeOver(),
|
|
758
|
-
down: {
|
|
759
|
-
x: event.clientX,
|
|
760
|
-
y: event.clientY
|
|
761
|
-
},
|
|
762
|
-
tracker: {
|
|
763
|
-
prev: {
|
|
764
|
-
x: event.clientX,
|
|
765
|
-
y: event.clientY
|
|
766
|
-
},
|
|
767
|
-
lastTime: event.timeStamp,
|
|
768
|
-
velocity: {
|
|
769
|
-
x: 0,
|
|
770
|
-
y: 0
|
|
771
|
-
}
|
|
772
|
-
},
|
|
773
|
-
transition: target.style.transitionProperty
|
|
774
|
-
};
|
|
775
|
-
target.style.transitionProperty = "none";
|
|
776
|
-
handle.setPointerCapture(event.pointerId);
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
/** A region's default self-alignment as a pin on the anchor's `[lo, hi]` —
|
|
377
|
+
* outward regions hug the anchor's near edge, spans its far edge; `center`
|
|
378
|
+
* and `span-all` are `anchor-center`, which may overflow the rect. */
|
|
379
|
+
function pinOf(region, lo, hi) {
|
|
380
|
+
switch (region) {
|
|
381
|
+
case "start": return {
|
|
382
|
+
align: "end",
|
|
383
|
+
at: lo
|
|
777
384
|
};
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
x: event.clientX,
|
|
782
|
-
y: event.clientY
|
|
783
|
-
};
|
|
784
|
-
engaged.tracker.velocity = updateVelocity(engaged.tracker, client, event.timeStamp);
|
|
785
|
-
engaged.tracker.prev = client;
|
|
786
|
-
engaged.tracker.lastTime = event.timeStamp;
|
|
787
|
-
const x = during(engaged.start.left + (client.x - engaged.down.x), "x");
|
|
788
|
-
const y = during(engaged.start.top + (client.y - engaged.down.y), "y");
|
|
789
|
-
write(x, y);
|
|
790
|
-
emit("dragmove", {
|
|
791
|
-
x,
|
|
792
|
-
y
|
|
793
|
-
});
|
|
385
|
+
case "span-start": return {
|
|
386
|
+
align: "end",
|
|
387
|
+
at: hi
|
|
794
388
|
};
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
engaged = null;
|
|
799
|
-
target.style.transitionProperty = transition;
|
|
800
|
-
const raw = {
|
|
801
|
-
x: start.left + (event.clientX - down.x),
|
|
802
|
-
y: start.top + (event.clientY - down.y)
|
|
803
|
-
};
|
|
804
|
-
const x = settleAxis(raw.x, tracker.velocity.x, "x");
|
|
805
|
-
const y = settleAxis(raw.y, tracker.velocity.y, "y");
|
|
806
|
-
const rest = x === null || y === null ? null : {
|
|
807
|
-
x,
|
|
808
|
-
y
|
|
809
|
-
};
|
|
810
|
-
if (rest) write(rest.x, rest.y);
|
|
811
|
-
emit("dragend", {
|
|
812
|
-
...raw,
|
|
813
|
-
velocity: tracker.velocity,
|
|
814
|
-
rest
|
|
815
|
-
});
|
|
389
|
+
case "end": return {
|
|
390
|
+
align: "start",
|
|
391
|
+
at: hi
|
|
816
392
|
};
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
handle.addEventListener("pointercancel", onUp);
|
|
821
|
-
const dispose = () => {
|
|
822
|
-
handle.removeEventListener("pointerdown", onDown);
|
|
823
|
-
handle.removeEventListener("pointermove", onMove);
|
|
824
|
-
handle.removeEventListener("pointerup", onUp);
|
|
825
|
-
handle.removeEventListener("pointercancel", onUp);
|
|
393
|
+
case "span-end": return {
|
|
394
|
+
align: "start",
|
|
395
|
+
at: lo
|
|
826
396
|
};
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
const x = during(point.x, "x");
|
|
833
|
-
const y = during(point.y, "y");
|
|
834
|
-
write(x, y);
|
|
835
|
-
emit("dragmove", {
|
|
836
|
-
x,
|
|
837
|
-
y
|
|
838
|
-
});
|
|
839
|
-
};
|
|
840
|
-
const dispose = () => {
|
|
841
|
-
for (const detach of attachments.splice(0)) detach();
|
|
842
|
-
};
|
|
843
|
-
onCleanup(dispose);
|
|
844
|
-
return {
|
|
845
|
-
update,
|
|
846
|
-
attach,
|
|
847
|
-
dispose,
|
|
848
|
-
[Symbol.dispose]: dispose
|
|
849
|
-
};
|
|
850
|
-
}
|
|
851
|
-
//#endregion
|
|
852
|
-
//#region src/ui/overlay/move-session.ts
|
|
853
|
-
/** Move bounds on the box center — the same clamp the stylesheet applies to
|
|
854
|
-
* the persisted location point (center floor = half the box). */
|
|
855
|
-
function moveBounds(args) {
|
|
856
|
-
const { rect, constraint } = args;
|
|
857
|
-
const minX = constraint.left + rect.width / 2;
|
|
858
|
-
const minY = constraint.top + rect.height / 2;
|
|
859
|
-
return {
|
|
860
|
-
minX,
|
|
861
|
-
minY,
|
|
862
|
-
maxX: Math.max(constraint.left + constraint.width - rect.width / 2, minX),
|
|
863
|
-
maxY: Math.max(constraint.top + constraint.height - rect.height / 2, minY)
|
|
864
|
-
};
|
|
865
|
-
}
|
|
866
|
-
/** Live move offset (`--overlay-dx/-dy`): 1:1 inside the bounds, rubber-band
|
|
867
|
-
* resistance beyond them. */
|
|
868
|
-
function moveOffset(args) {
|
|
869
|
-
const { center0, client, start, bounds } = args;
|
|
870
|
-
return {
|
|
871
|
-
dx: resist(center0.x + (client.x - start.x), bounds.minX, bounds.maxX) - center0.x,
|
|
872
|
-
dy: resist(center0.y + (client.y - start.y), bounds.minY, bounds.maxY) - center0.y
|
|
873
|
-
};
|
|
874
|
-
}
|
|
875
|
-
/** Rested move location (rect-relative), clamped inside the bounds. */
|
|
876
|
-
function moveRest(args) {
|
|
877
|
-
const { center0, client, start, bounds, constraint } = args;
|
|
878
|
-
const cx = clamp(center0.x + (client.x - start.x), bounds.minX, bounds.maxX);
|
|
879
|
-
const cy = clamp(center0.y + (client.y - start.y), bounds.minY, bounds.maxY);
|
|
880
|
-
return {
|
|
881
|
-
x: cx - constraint.left,
|
|
882
|
-
y: cy - constraint.top
|
|
883
|
-
};
|
|
884
|
-
}
|
|
885
|
-
/** Whether a flicked move's projected center leaves the constraint (close). */
|
|
886
|
-
function projectedOutOfBounds(args) {
|
|
887
|
-
const { rect, velocity, constraint } = args;
|
|
888
|
-
const px = rect.left + rect.width / 2 + velocity.x * 160;
|
|
889
|
-
const py = rect.top + rect.height / 2 + velocity.y * 160;
|
|
890
|
-
return px < constraint.left || px > constraint.left + constraint.width || py < constraint.top || py > constraint.top + constraint.height;
|
|
891
|
-
}
|
|
892
|
-
/**
|
|
893
|
-
* Window move — x/y drag from the top strip, flick off the constraint to
|
|
894
|
-
* dismiss. Doesn't resize: writes only `offset` live and the rested x/y on
|
|
895
|
-
* release (so the I/O layer fires no `resizechange`).
|
|
896
|
-
*/
|
|
897
|
-
function moveSession(snap, deps, io) {
|
|
898
|
-
const bounds = moveBounds({
|
|
899
|
-
rect: snap.rect,
|
|
900
|
-
constraint: snap.constraint
|
|
901
|
-
});
|
|
902
|
-
return {
|
|
903
|
-
move(p) {
|
|
904
|
-
const { dx, dy } = moveOffset({
|
|
905
|
-
center0: snap.center0,
|
|
906
|
-
client: p.current,
|
|
907
|
-
start: p.start,
|
|
908
|
-
bounds
|
|
909
|
-
});
|
|
910
|
-
io.sync({ offset: {
|
|
911
|
-
dx,
|
|
912
|
-
dy
|
|
913
|
-
} });
|
|
914
|
-
},
|
|
915
|
-
release(p) {
|
|
916
|
-
if (deps.dismissible && projectedOutOfBounds({
|
|
917
|
-
rect: deps.liveRect(),
|
|
918
|
-
velocity: p.velocity,
|
|
919
|
-
constraint: snap.constraint
|
|
920
|
-
})) return io.dismiss();
|
|
921
|
-
const r = moveRest({
|
|
922
|
-
center0: snap.center0,
|
|
923
|
-
client: p.current,
|
|
924
|
-
start: p.start,
|
|
925
|
-
bounds,
|
|
926
|
-
constraint: snap.constraint
|
|
927
|
-
});
|
|
928
|
-
io.commit({
|
|
929
|
-
x: r.x,
|
|
930
|
-
y: r.y
|
|
931
|
-
});
|
|
932
|
-
},
|
|
933
|
-
cancel() {
|
|
934
|
-
io.revert([]);
|
|
935
|
-
}
|
|
936
|
-
};
|
|
397
|
+
default: return {
|
|
398
|
+
align: "center",
|
|
399
|
+
at: (lo + hi) / 2
|
|
400
|
+
};
|
|
401
|
+
}
|
|
937
402
|
}
|
|
938
403
|
//#endregion
|
|
939
|
-
//#region src/ui/overlay/overlay
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
* their output back to the `--overlay-*` channels (`sync` / `commit`) or
|
|
947
|
-
* reverts/dismisses.
|
|
948
|
-
* - `createGestureRecognizer`: generic single-pointer drag plumbing
|
|
949
|
-
* (capture, velocity, the touch-scroll block) that drives a {@link Session}.
|
|
950
|
-
*
|
|
951
|
-
* Everything between — the mode reducers and their math — stays pure and
|
|
952
|
-
* element-free (`gesture-model` + the mode files).
|
|
953
|
-
*/
|
|
954
|
-
const CHANNEL = {
|
|
955
|
-
x: "--overlay-x",
|
|
956
|
-
y: "--overlay-y",
|
|
957
|
-
w: "--overlay-w",
|
|
958
|
-
h: "--overlay-h"
|
|
404
|
+
//#region src/ui/overlay/overlay.ts
|
|
405
|
+
var __create = Object.create;
|
|
406
|
+
var __defProp = Object.defineProperty;
|
|
407
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
408
|
+
var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name);
|
|
409
|
+
var __typeError = (msg) => {
|
|
410
|
+
throw TypeError(msg);
|
|
959
411
|
};
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
for (const k of [
|
|
1007
|
-
"x",
|
|
1008
|
-
"y",
|
|
1009
|
-
"w",
|
|
1010
|
-
"h"
|
|
1011
|
-
]) {
|
|
1012
|
-
const v = frame[k];
|
|
1013
|
-
if (v !== void 0) setLen(CHANNEL[k], v);
|
|
1014
|
-
}
|
|
1015
|
-
};
|
|
1016
|
-
const applyOffset = (name, v) => {
|
|
1017
|
-
if (v === void 0) return;
|
|
1018
|
-
if (v === null) overlay.style.removeProperty(name);
|
|
1019
|
-
else setLen(name, v);
|
|
1020
|
-
};
|
|
1021
|
-
return {
|
|
1022
|
-
engage() {
|
|
1023
|
-
const constraint = resolveConstraint(overlay);
|
|
1024
|
-
const rect = overlay.getBoundingClientRect();
|
|
1025
|
-
prev = {
|
|
1026
|
-
x: getProp("--overlay-x"),
|
|
1027
|
-
y: getProp("--overlay-y"),
|
|
1028
|
-
w: getProp("--overlay-w"),
|
|
1029
|
-
h: getProp("--overlay-h")
|
|
1030
|
-
};
|
|
1031
|
-
const dir = getComputedStyle(overlay).direction === "rtl" ? -1 : 1;
|
|
1032
|
-
const ctx = (q) => ({
|
|
1033
|
-
...q,
|
|
1034
|
-
dismissible,
|
|
1035
|
-
velocityThreshold,
|
|
1036
|
-
resolve: (value) => typeof value === "number" ? value * (q.axis === "width" ? constraint.width : constraint.height) : probeLength(value, q.axis)
|
|
1037
|
-
});
|
|
1038
|
-
return {
|
|
1039
|
-
snapshot: {
|
|
1040
|
-
constraint,
|
|
1041
|
-
rect,
|
|
1042
|
-
center0: {
|
|
1043
|
-
x: rect.left + rect.width / 2,
|
|
1044
|
-
y: rect.top + rect.height / 2
|
|
1045
|
-
},
|
|
1046
|
-
dir
|
|
1047
|
-
},
|
|
1048
|
-
resizer: {
|
|
1049
|
-
bounds: (a) => strategy.bounds ? strategy.bounds(ctx({
|
|
1050
|
-
...a,
|
|
1051
|
-
size: a.startSize,
|
|
1052
|
-
velocity: 0
|
|
1053
|
-
})) : [a.min, a.max],
|
|
1054
|
-
rest: (a, drag) => strategy.rest(ctx({
|
|
1055
|
-
...a,
|
|
1056
|
-
...drag
|
|
1057
|
-
}))
|
|
1058
|
-
},
|
|
1059
|
-
move: {
|
|
1060
|
-
dismissible,
|
|
1061
|
-
liveRect: () => overlay.getBoundingClientRect()
|
|
1062
|
-
}
|
|
1063
|
-
};
|
|
1064
|
-
},
|
|
1065
|
-
sync(patch) {
|
|
1066
|
-
overlay.style.transition = "none";
|
|
1067
|
-
if (patch.frame) {
|
|
1068
|
-
const f = patch.frame;
|
|
1069
|
-
if (f.w !== void 0) overlay.style.width = `${f.w}px`;
|
|
1070
|
-
if (f.h !== void 0) overlay.style.height = `${f.h}px`;
|
|
1071
|
-
if (f.x !== void 0) setLen("--overlay-x", f.x);
|
|
1072
|
-
if (f.y !== void 0) setLen("--overlay-y", f.y);
|
|
1073
|
-
}
|
|
1074
|
-
if (patch.offset) {
|
|
1075
|
-
applyOffset("--overlay-dx", patch.offset.dx);
|
|
1076
|
-
applyOffset("--overlay-dy", patch.offset.dy);
|
|
1077
|
-
}
|
|
1078
|
-
},
|
|
1079
|
-
commit(frame) {
|
|
1080
|
-
clearDrag();
|
|
1081
|
-
writeFrame(frame);
|
|
1082
|
-
if (frame.w !== void 0 || frame.h !== void 0) emitResize();
|
|
1083
|
-
},
|
|
1084
|
-
dismiss() {
|
|
1085
|
-
clearDrag();
|
|
1086
|
-
restoreChannel("--overlay-x", prev.x);
|
|
1087
|
-
restoreChannel("--overlay-y", prev.y);
|
|
1088
|
-
restoreChannel("--overlay-w", prev.w);
|
|
1089
|
-
restoreChannel("--overlay-h", prev.h);
|
|
1090
|
-
if (overlay instanceof HTMLDialogElement && overlay.open) overlay.close();
|
|
1091
|
-
else overlay.hidePopover?.();
|
|
412
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, {
|
|
413
|
+
enumerable: true,
|
|
414
|
+
configurable: true,
|
|
415
|
+
writable: true,
|
|
416
|
+
value
|
|
417
|
+
}) : obj[key] = value;
|
|
418
|
+
var __name = (target, value) => __defProp(target, "name", {
|
|
419
|
+
value,
|
|
420
|
+
configurable: true
|
|
421
|
+
});
|
|
422
|
+
var __decoratorStart = (base) => [
|
|
423
|
+
,
|
|
424
|
+
,
|
|
425
|
+
,
|
|
426
|
+
__create(base?.[__knownSymbol("metadata")] ?? null)
|
|
427
|
+
];
|
|
428
|
+
var __decoratorStrings = [
|
|
429
|
+
"class",
|
|
430
|
+
"method",
|
|
431
|
+
"getter",
|
|
432
|
+
"setter",
|
|
433
|
+
"accessor",
|
|
434
|
+
"field",
|
|
435
|
+
"value",
|
|
436
|
+
"get",
|
|
437
|
+
"set"
|
|
438
|
+
];
|
|
439
|
+
var __expectFn = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError("Function expected") : fn;
|
|
440
|
+
var __decoratorContext = (kind, name, done, metadata, fns) => ({
|
|
441
|
+
kind: __decoratorStrings[kind],
|
|
442
|
+
name,
|
|
443
|
+
metadata,
|
|
444
|
+
addInitializer: (fn) => done._ ? __typeError("Already initialized") : fns.push(__expectFn(fn || null))
|
|
445
|
+
});
|
|
446
|
+
var __decoratorMetadata = (array, target) => __defNormalProp(target, __knownSymbol("metadata"), array[3]);
|
|
447
|
+
var __runInitializers = (array, flags, self, value) => {
|
|
448
|
+
for (var i = 0, fns = array[flags >> 1], n = fns && fns.length; i < n; i++) flags & 1 ? fns[i].call(self) : value = fns[i].call(self, value);
|
|
449
|
+
return value;
|
|
450
|
+
};
|
|
451
|
+
var __decorateElement = (array, flags, name, decorators, target, extra) => {
|
|
452
|
+
var fn, it, done, ctx, access, k = flags & 7, s = !!(flags & 8), p = !!(flags & 16);
|
|
453
|
+
var j = k > 3 ? array.length + 1 : k ? s ? 1 : 2 : 0, key = __decoratorStrings[k + 5];
|
|
454
|
+
var initializers = k > 3 && (array[j - 1] = []), extraInitializers = array[j] || (array[j] = []);
|
|
455
|
+
var desc = k && (!p && !s && (target = target.prototype), k < 5 && (k > 3 || !p) && __getOwnPropDesc(k < 4 ? target : {
|
|
456
|
+
get [name]() {
|
|
457
|
+
return __privateGet(this, extra);
|
|
1092
458
|
},
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
for (const k of keys) restoreChannel(CHANNEL[k], prev[k]);
|
|
459
|
+
set [name](x) {
|
|
460
|
+
return __privateSet(this, extra, x);
|
|
1096
461
|
}
|
|
1097
|
-
};
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
* recognizer knows nothing about channels.
|
|
1106
|
-
*/
|
|
1107
|
-
function createGestureRecognizer(target, { canEngage, engage }) {
|
|
1108
|
-
let active = null;
|
|
1109
|
-
let pointer = null;
|
|
1110
|
-
const onPointerDown = (event) => {
|
|
1111
|
-
if (active) return;
|
|
1112
|
-
if (!canEngage(event)) return;
|
|
1113
|
-
const session = engage(event);
|
|
1114
|
-
if (!session) return;
|
|
1115
|
-
active = session;
|
|
1116
|
-
const c = {
|
|
1117
|
-
x: event.clientX,
|
|
1118
|
-
y: event.clientY
|
|
1119
|
-
};
|
|
1120
|
-
pointer = {
|
|
1121
|
-
start: { ...c },
|
|
1122
|
-
prev: { ...c },
|
|
1123
|
-
current: { ...c },
|
|
1124
|
-
lastTime: event.timeStamp,
|
|
1125
|
-
velocity: {
|
|
1126
|
-
x: 0,
|
|
1127
|
-
y: 0
|
|
1128
|
-
}
|
|
1129
|
-
};
|
|
1130
|
-
target.style.userSelect = "none";
|
|
1131
|
-
target.style.setProperty("-webkit-user-select", "none");
|
|
1132
|
-
target.setPointerCapture?.(event.pointerId);
|
|
1133
|
-
};
|
|
1134
|
-
const onPointerMove = (event) => {
|
|
1135
|
-
if (!active || !pointer) return;
|
|
1136
|
-
const c = {
|
|
1137
|
-
x: event.clientX,
|
|
1138
|
-
y: event.clientY
|
|
1139
|
-
};
|
|
1140
|
-
pointer.velocity = updateVelocity(pointer, c, event.timeStamp);
|
|
1141
|
-
pointer.prev = c;
|
|
1142
|
-
pointer.lastTime = event.timeStamp;
|
|
1143
|
-
pointer.current = c;
|
|
1144
|
-
active.move(pointer);
|
|
1145
|
-
};
|
|
1146
|
-
const onPointerUp = (event) => {
|
|
1147
|
-
if (!active || !pointer) return;
|
|
1148
|
-
pointer.current = {
|
|
1149
|
-
x: event.clientX,
|
|
1150
|
-
y: event.clientY
|
|
1151
|
-
};
|
|
1152
|
-
const session = active;
|
|
1153
|
-
active = null;
|
|
1154
|
-
target.releasePointerCapture?.(event.pointerId);
|
|
1155
|
-
session.release(pointer);
|
|
1156
|
-
pointer = null;
|
|
1157
|
-
};
|
|
1158
|
-
const onPointerCancel = () => {
|
|
1159
|
-
if (!active) return;
|
|
1160
|
-
const session = active;
|
|
1161
|
-
active = null;
|
|
1162
|
-
pointer = null;
|
|
1163
|
-
session.cancel();
|
|
1164
|
-
};
|
|
1165
|
-
const blockScroll = (event) => {
|
|
1166
|
-
if (active) event.preventDefault();
|
|
1167
|
-
};
|
|
1168
|
-
target.addEventListener("pointerdown", onPointerDown);
|
|
1169
|
-
target.addEventListener("pointermove", onPointerMove);
|
|
1170
|
-
target.addEventListener("pointerup", onPointerUp);
|
|
1171
|
-
target.addEventListener("pointercancel", onPointerCancel);
|
|
1172
|
-
target.addEventListener("touchmove", blockScroll, { passive: false });
|
|
1173
|
-
return { dispose() {
|
|
1174
|
-
target.removeEventListener("pointerdown", onPointerDown);
|
|
1175
|
-
target.removeEventListener("pointermove", onPointerMove);
|
|
1176
|
-
target.removeEventListener("pointerup", onPointerUp);
|
|
1177
|
-
target.removeEventListener("pointercancel", onPointerCancel);
|
|
1178
|
-
target.removeEventListener("touchmove", blockScroll);
|
|
1179
|
-
} };
|
|
1180
|
-
}
|
|
1181
|
-
//#endregion
|
|
1182
|
-
//#region src/ui/overlay/resize-height-session.ts
|
|
1183
|
-
/**
|
|
1184
|
-
* Height resize — the block-axis edge handle (sheets). One edge moves toward
|
|
1185
|
-
* the pointer; the opposite edge is pinned by the location shift, or held by
|
|
1186
|
-
* the CSS clamp when docked. Below the lower bound the surface slides away via
|
|
1187
|
-
* `--overlay-dy`. Self-contained: the height-axis wiring lives here.
|
|
1188
|
-
*/
|
|
1189
|
-
function resizeHeightSession(snap, resizer, io, side) {
|
|
1190
|
-
const startSize = snap.rect.height;
|
|
1191
|
-
const { sign, anchorSign, docked } = edgeSetup({
|
|
1192
|
-
axis: "block",
|
|
1193
|
-
side,
|
|
1194
|
-
rect: snap.rect,
|
|
1195
|
-
constraint: snap.constraint,
|
|
1196
|
-
dir: snap.dir
|
|
1197
|
-
});
|
|
1198
|
-
const hardMax = anchorSign > 0 ? snap.constraint.top + snap.constraint.height - snap.rect.top : snap.rect.bottom - snap.constraint.top;
|
|
1199
|
-
const ax = {
|
|
1200
|
-
axis: "height",
|
|
1201
|
-
startSize,
|
|
1202
|
-
min: 0,
|
|
1203
|
-
max: hardMax
|
|
1204
|
-
};
|
|
1205
|
-
const anchorAt = (size) => anchor$1({
|
|
1206
|
-
axis: "height",
|
|
1207
|
-
center0: snap.center0,
|
|
1208
|
-
constraint: snap.constraint,
|
|
1209
|
-
anchorSign,
|
|
1210
|
-
startSize,
|
|
1211
|
-
size,
|
|
1212
|
-
docked
|
|
1213
|
-
});
|
|
1214
|
-
return {
|
|
1215
|
-
move(p) {
|
|
1216
|
-
const target = startSize + sign * (p.current.y - p.start.y);
|
|
1217
|
-
const [lo, hi] = resizer.bounds(ax);
|
|
1218
|
-
const { size, slide } = edgeDrag({
|
|
1219
|
-
target,
|
|
1220
|
-
lo,
|
|
1221
|
-
hi,
|
|
1222
|
-
sign,
|
|
1223
|
-
max: hardMax
|
|
1224
|
-
});
|
|
1225
|
-
const a = anchorAt(size);
|
|
1226
|
-
io.sync({
|
|
1227
|
-
frame: {
|
|
1228
|
-
h: size,
|
|
1229
|
-
...a !== null ? { y: a } : {}
|
|
1230
|
-
},
|
|
1231
|
-
offset: { dy: slide }
|
|
1232
|
-
});
|
|
1233
|
-
},
|
|
1234
|
-
release(p) {
|
|
1235
|
-
const target = startSize + sign * (p.current.y - p.start.y);
|
|
1236
|
-
const r = resizer.rest(ax, {
|
|
1237
|
-
size: target,
|
|
1238
|
-
velocity: -sign * p.velocity.y
|
|
1239
|
-
});
|
|
1240
|
-
if (r === null) return io.dismiss();
|
|
1241
|
-
const a = anchorAt(r);
|
|
1242
|
-
io.commit({
|
|
1243
|
-
h: r,
|
|
1244
|
-
...a !== null ? { y: a } : {}
|
|
1245
|
-
});
|
|
1246
|
-
},
|
|
1247
|
-
cancel() {
|
|
1248
|
-
io.revert(["x", "y"]);
|
|
462
|
+
}, name));
|
|
463
|
+
k ? p && k < 4 && __name(extra, (k > 2 ? "set " : k > 1 ? "get " : "") + name) : __name(target, name);
|
|
464
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
465
|
+
ctx = __decoratorContext(k, name, done = {}, array[3], extraInitializers);
|
|
466
|
+
if (k) {
|
|
467
|
+
ctx.static = s, ctx.private = p, access = ctx.access = { has: p ? (x) => __privateIn(target, x) : (x) => name in x };
|
|
468
|
+
if (k ^ 3) access.get = p ? (x) => (k ^ 1 ? __privateGet : __privateMethod)(x, target, k ^ 4 ? extra : desc.get) : (x) => x[name];
|
|
469
|
+
if (k > 2) access.set = p ? (x, y) => __privateSet(x, target, y, k ^ 4 ? extra : desc.set) : (x, y) => x[name] = y;
|
|
1249
470
|
}
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
}
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
}
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
471
|
+
it = (0, decorators[i])(k ? k < 4 ? p ? extra : desc[key] : k > 4 ? void 0 : {
|
|
472
|
+
get: desc.get,
|
|
473
|
+
set: desc.set
|
|
474
|
+
} : target, ctx), done._ = 1;
|
|
475
|
+
if (k ^ 4 || it === void 0) __expectFn(it) && (k > 4 ? initializers.unshift(it) : k ? p ? extra = it : desc[key] = it : target = it);
|
|
476
|
+
else if (typeof it !== "object" || it === null) __typeError("Object expected");
|
|
477
|
+
else __expectFn(fn = it.get) && (desc.get = fn), __expectFn(fn = it.set) && (desc.set = fn), __expectFn(fn = it.init) && initializers.unshift(fn);
|
|
478
|
+
}
|
|
479
|
+
return k || __decoratorMetadata(array, target), desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target;
|
|
480
|
+
};
|
|
481
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
482
|
+
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
483
|
+
var __privateIn = (member, obj) => Object(obj) !== obj ? __typeError("Cannot use the \"in\" operator on this value") : member.has(obj);
|
|
484
|
+
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
485
|
+
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
486
|
+
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
487
|
+
var _h_dec, _w_dec, _y_dec, _x_dec = [reactive()], _init;
|
|
488
|
+
_y_dec = [reactive()], _w_dec = [reactive()], _h_dec = [reactive()];
|
|
489
|
+
var PartialBox = class {
|
|
490
|
+
constructor(x = void 0, y = void 0, w = void 0, h = void 0) {
|
|
491
|
+
__publicField(this, "x", __runInitializers(_init, 8, this)), __runInitializers(_init, 11, this);
|
|
492
|
+
__publicField(this, "y", __runInitializers(_init, 12, this)), __runInitializers(_init, 15, this);
|
|
493
|
+
__publicField(this, "w", __runInitializers(_init, 16, this)), __runInitializers(_init, 19, this);
|
|
494
|
+
__publicField(this, "h", __runInitializers(_init, 20, this)), __runInitializers(_init, 23, this);
|
|
495
|
+
this.x = x;
|
|
496
|
+
this.y = y;
|
|
497
|
+
this.w = w;
|
|
498
|
+
this.h = h;
|
|
499
|
+
}
|
|
500
|
+
};
|
|
501
|
+
_init = __decoratorStart(null);
|
|
502
|
+
__decorateElement(_init, 5, "x", _x_dec, PartialBox);
|
|
503
|
+
__decorateElement(_init, 5, "y", _y_dec, PartialBox);
|
|
504
|
+
__decorateElement(_init, 5, "w", _w_dec, PartialBox);
|
|
505
|
+
__decorateElement(_init, 5, "h", _h_dec, PartialBox);
|
|
506
|
+
__decoratorMetadata(_init, PartialBox);
|
|
507
|
+
var TransformableBox = class {
|
|
508
|
+
transform;
|
|
509
|
+
displacement;
|
|
510
|
+
constructor(transform = new PartialBox()) {
|
|
511
|
+
this.transform = transform;
|
|
512
|
+
this.displacement = new Displacement(this);
|
|
513
|
+
}
|
|
514
|
+
get x() {
|
|
515
|
+
return (this.transform.x ?? 0) + (this.displacement.x ?? 0);
|
|
516
|
+
}
|
|
517
|
+
set x(value) {
|
|
518
|
+
this.transform.x = value;
|
|
519
|
+
}
|
|
520
|
+
get y() {
|
|
521
|
+
return (this.transform.y ?? 0) + (this.displacement.y ?? 0);
|
|
522
|
+
}
|
|
523
|
+
set y(value) {
|
|
524
|
+
this.transform.y = value;
|
|
525
|
+
}
|
|
526
|
+
get w() {
|
|
527
|
+
return (this.transform.w ?? 0) + (this.displacement.w ?? 0);
|
|
528
|
+
}
|
|
529
|
+
set w(value) {
|
|
530
|
+
this.transform.w = value;
|
|
531
|
+
}
|
|
532
|
+
get h() {
|
|
533
|
+
return (this.transform.h ?? 0) + (this.displacement.h ?? 0);
|
|
534
|
+
}
|
|
535
|
+
set h(value) {
|
|
536
|
+
this.transform.h = value;
|
|
537
|
+
}
|
|
538
|
+
};
|
|
539
|
+
var Displacement = class extends PartialBox {
|
|
540
|
+
box;
|
|
541
|
+
constructor(box) {
|
|
542
|
+
super();
|
|
543
|
+
this.box = box;
|
|
544
|
+
}
|
|
545
|
+
apply() {
|
|
546
|
+
batch(() => {
|
|
547
|
+
this.box.transform.x = (this.box.transform.x ?? 0) + (this.x ?? 0);
|
|
548
|
+
this.box.transform.y = (this.box.transform.y ?? 0) + (this.y ?? 0);
|
|
549
|
+
this.box.transform.w = (this.box.transform.w ?? 0) + (this.w ?? 0);
|
|
550
|
+
this.box.transform.h = (this.box.transform.h ?? 0) + (this.h ?? 0);
|
|
551
|
+
this.clear();
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
clear() {
|
|
555
|
+
batch(() => {
|
|
556
|
+
this.x = void 0;
|
|
557
|
+
this.y = void 0;
|
|
558
|
+
this.w = void 0;
|
|
559
|
+
this.h = void 0;
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
};
|
|
563
|
+
var OverlayBox = class extends TransformableBox {
|
|
564
|
+
element;
|
|
565
|
+
#rect;
|
|
566
|
+
constructor(element) {
|
|
567
|
+
super();
|
|
568
|
+
this.element = element;
|
|
569
|
+
this.#rect = createElementRect(element);
|
|
570
|
+
element.style.setProperty("top", "0");
|
|
571
|
+
element.style.setProperty("left", "0");
|
|
572
|
+
element.style.setProperty("translate", "calc(var(--x, 0px) + var(--dx, 0px) + var(--_ex, 0px)) calc(var(--y, 0px) + var(--dy, 0px) + var(--_ey, 0px))");
|
|
573
|
+
const [, stop] = scope(() => {
|
|
574
|
+
effect(() => {
|
|
575
|
+
this.#project("--x", this.transform.x);
|
|
1340
576
|
});
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
const w = resizer.rest(ax, {
|
|
1344
|
-
size: startW + signX * (p.current.x - p.start.x),
|
|
1345
|
-
velocity: -p.velocity.x * signX
|
|
577
|
+
effect(() => {
|
|
578
|
+
this.#project("--y", this.transform.y);
|
|
1346
579
|
});
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
io.commit(frameFor(w, h));
|
|
1350
|
-
},
|
|
1351
|
-
cancel() {
|
|
1352
|
-
io.revert([
|
|
1353
|
-
"x",
|
|
1354
|
-
"y",
|
|
1355
|
-
"w",
|
|
1356
|
-
"h"
|
|
1357
|
-
]);
|
|
1358
|
-
}
|
|
1359
|
-
};
|
|
1360
|
-
}
|
|
1361
|
-
//#endregion
|
|
1362
|
-
//#region src/ui/overlay/resize-width-session.ts
|
|
1363
|
-
/**
|
|
1364
|
-
* Width resize — the inline-axis edge handle (drawers). Mirror of
|
|
1365
|
-
* `resizeHeightSession` on the width axis (the RTL sign flip is folded into
|
|
1366
|
-
* `edgeSetup`); the opposite edge is pinned, and below the lower bound the
|
|
1367
|
-
* surface slides via `--overlay-dx`. Self-contained: the width-axis wiring.
|
|
1368
|
-
*/
|
|
1369
|
-
function resizeWidthSession(snap, resizer, io, side) {
|
|
1370
|
-
const startSize = snap.rect.width;
|
|
1371
|
-
const { sign, anchorSign, docked } = edgeSetup({
|
|
1372
|
-
axis: "inline",
|
|
1373
|
-
side,
|
|
1374
|
-
rect: snap.rect,
|
|
1375
|
-
constraint: snap.constraint,
|
|
1376
|
-
dir: snap.dir
|
|
1377
|
-
});
|
|
1378
|
-
const hardMax = anchorSign > 0 ? snap.constraint.left + snap.constraint.width - snap.rect.left : snap.rect.right - snap.constraint.left;
|
|
1379
|
-
const ax = {
|
|
1380
|
-
axis: "width",
|
|
1381
|
-
startSize,
|
|
1382
|
-
min: 0,
|
|
1383
|
-
max: hardMax
|
|
1384
|
-
};
|
|
1385
|
-
const anchorAt = (size) => anchor$1({
|
|
1386
|
-
axis: "width",
|
|
1387
|
-
center0: snap.center0,
|
|
1388
|
-
constraint: snap.constraint,
|
|
1389
|
-
anchorSign,
|
|
1390
|
-
startSize,
|
|
1391
|
-
size,
|
|
1392
|
-
docked
|
|
1393
|
-
});
|
|
1394
|
-
return {
|
|
1395
|
-
move(p) {
|
|
1396
|
-
const target = startSize + sign * (p.current.x - p.start.x);
|
|
1397
|
-
const [lo, hi] = resizer.bounds(ax);
|
|
1398
|
-
const { size, slide } = edgeDrag({
|
|
1399
|
-
target,
|
|
1400
|
-
lo,
|
|
1401
|
-
hi,
|
|
1402
|
-
sign,
|
|
1403
|
-
max: hardMax
|
|
580
|
+
effect(() => {
|
|
581
|
+
this.#project("--w", this.transform.w, this.displacement.w);
|
|
1404
582
|
});
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
frame: {
|
|
1408
|
-
w: size,
|
|
1409
|
-
...a !== null ? { x: a } : {}
|
|
1410
|
-
},
|
|
1411
|
-
offset: { dx: slide }
|
|
583
|
+
effect(() => {
|
|
584
|
+
this.#project("--h", this.transform.h, this.displacement.h);
|
|
1412
585
|
});
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
const target = startSize + sign * (p.current.x - p.start.x);
|
|
1416
|
-
const r = resizer.rest(ax, {
|
|
1417
|
-
size: target,
|
|
1418
|
-
velocity: -sign * p.velocity.x
|
|
586
|
+
effect(() => {
|
|
587
|
+
this.#project("--dx", this.displacement.x);
|
|
1419
588
|
});
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
io.commit({
|
|
1423
|
-
w: r,
|
|
1424
|
-
...a !== null ? { x: a } : {}
|
|
589
|
+
effect(() => {
|
|
590
|
+
this.#project("--dy", this.displacement.y);
|
|
1425
591
|
});
|
|
1426
|
-
}
|
|
1427
|
-
|
|
1428
|
-
|
|
592
|
+
});
|
|
593
|
+
this.dispose = () => {
|
|
594
|
+
stop();
|
|
595
|
+
this.#rect[Symbol.dispose]();
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
/** Write a size channel, or unset it when the axis is AUTO (NaN) so the
|
|
599
|
+
* element falls back to content sizing (`var(--w, auto)`). */
|
|
600
|
+
#project(name, ...value) {
|
|
601
|
+
if (value.every((v) => v === void 0 || Number.isNaN(v))) {
|
|
602
|
+
this.element.style.removeProperty(name);
|
|
603
|
+
return;
|
|
1429
604
|
}
|
|
1430
|
-
|
|
1431
|
-
}
|
|
605
|
+
const sum = value.reduce((a, b) => a + (b ?? 0), 0);
|
|
606
|
+
this.element.style.setProperty(name, `${sum}px`);
|
|
607
|
+
}
|
|
608
|
+
get w() {
|
|
609
|
+
return this.#rect().width;
|
|
610
|
+
}
|
|
611
|
+
set w(value) {
|
|
612
|
+
this.transform.w = value;
|
|
613
|
+
}
|
|
614
|
+
get h() {
|
|
615
|
+
return this.#rect().height;
|
|
616
|
+
}
|
|
617
|
+
set h(value) {
|
|
618
|
+
this.transform.h = value;
|
|
619
|
+
}
|
|
620
|
+
get x() {
|
|
621
|
+
return this.#rect().left;
|
|
622
|
+
}
|
|
623
|
+
set x(value) {
|
|
624
|
+
this.transform.x = value;
|
|
625
|
+
}
|
|
626
|
+
get y() {
|
|
627
|
+
return this.#rect().top;
|
|
628
|
+
}
|
|
629
|
+
set y(value) {
|
|
630
|
+
this.transform.y = value;
|
|
631
|
+
}
|
|
632
|
+
/** The element's computed direction, read at call time. */
|
|
633
|
+
get direction() {
|
|
634
|
+
return getComputedStyle(this.element).direction === "rtl" ? "rtl" : "ltr";
|
|
635
|
+
}
|
|
636
|
+
dispose;
|
|
637
|
+
[Symbol.dispose]() {
|
|
638
|
+
this.dispose();
|
|
639
|
+
}
|
|
640
|
+
};
|
|
1432
641
|
//#endregion
|
|
1433
642
|
//#region src/ui/overlay/gestures.ts
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
*
|
|
1447
|
-
*
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
* I/O). This function is the wiring — it picks a pure `Session` from
|
|
1463
|
-
* `detectEngagement` and adapts it to the recognizer through the io.
|
|
1464
|
-
* The `resize` strategy (`freeResize` by default, or `detents`) decides the
|
|
1465
|
-
* rested size, written to the public `--overlay-w`/`--overlay-h` channels;
|
|
1466
|
-
* CSS renders and animates them. JS never touches `translate`/`top`/`left`.
|
|
1467
|
-
*
|
|
1468
|
-
* Registers its cleanup with the current scope (`onCleanup`) and also
|
|
1469
|
-
* returns it as `dispose` / `Symbol.dispose`.
|
|
1470
|
-
*
|
|
1471
|
-
* @example
|
|
1472
|
-
* ```ts
|
|
1473
|
-
* import { constraint, createOverlayGestures, detents } from "elements-kit/ui/overlay";
|
|
1474
|
-
*
|
|
1475
|
-
* const el = document.querySelector("dialog.x-overlay")!;
|
|
1476
|
-
* createOverlayGestures(el, { resize: detents(constraint(), [0.25, 0.6, 0.9]) });
|
|
1477
|
-
* el.addEventListener("resizechange", (e) => console.log(e.detail));
|
|
1478
|
-
* ```
|
|
1479
|
-
*/
|
|
1480
|
-
function createOverlayGestures(overlay, options) {
|
|
1481
|
-
const io = createFrameIO(overlay, {
|
|
1482
|
-
strategy: options?.resize ?? freeResize(),
|
|
1483
|
-
dismissible: options?.dismissible ?? true,
|
|
1484
|
-
velocityThreshold: options?.velocityThreshold ?? .5
|
|
1485
|
-
});
|
|
1486
|
-
const canEngage = (event) => {
|
|
1487
|
-
const resize = overlay.getAttribute("data-resize") ?? "";
|
|
1488
|
-
const draggable = overlay.hasAttribute("data-draggable");
|
|
1489
|
-
if (!resize && !draggable) return false;
|
|
1490
|
-
if (overlay.getAttribute("data-anchor") === "element") return false;
|
|
1491
|
-
const target = event.target;
|
|
1492
|
-
if (target?.closest("button, a, label, input, select, textarea, [contenteditable]")) return false;
|
|
1493
|
-
for (let el = target; el !== null && el !== overlay; el = el.parentElement) if (el.scrollTop > 0) return false;
|
|
1494
|
-
return true;
|
|
1495
|
-
};
|
|
1496
|
-
const engage = (event) => {
|
|
1497
|
-
const parsed = parseResize(overlay.getAttribute("data-resize") ?? "");
|
|
1498
|
-
const draggable = overlay.hasAttribute("data-draggable");
|
|
1499
|
-
const { snapshot, resizer, move } = io.engage();
|
|
1500
|
-
const key = detectEngagement({
|
|
1501
|
-
...parsed,
|
|
1502
|
-
draggable,
|
|
1503
|
-
rect: snapshot.rect,
|
|
1504
|
-
pointer: {
|
|
1505
|
-
x: event.clientX,
|
|
1506
|
-
y: event.clientY
|
|
1507
|
-
},
|
|
1508
|
-
dir: snapshot.dir
|
|
1509
|
-
});
|
|
1510
|
-
if (!key) return null;
|
|
1511
|
-
return selectSession(key, parsed, snapshot, resizer, move, io);
|
|
1512
|
-
};
|
|
1513
|
-
const recognizer = createGestureRecognizer(overlay, {
|
|
1514
|
-
canEngage,
|
|
1515
|
-
engage
|
|
1516
|
-
});
|
|
1517
|
-
const dispose = () => recognizer.dispose();
|
|
1518
|
-
onCleanup(dispose);
|
|
1519
|
-
return {
|
|
1520
|
-
resize(size) {
|
|
1521
|
-
const key = resizeKey(parseResize(overlay.getAttribute("data-resize") ?? ""));
|
|
1522
|
-
io.commit({ [key]: size });
|
|
1523
|
-
},
|
|
1524
|
-
dispose,
|
|
1525
|
-
[Symbol.dispose]: dispose
|
|
1526
|
-
};
|
|
643
|
+
var gestures_exports = /* @__PURE__ */ __exportAll({
|
|
644
|
+
detent: () => detent,
|
|
645
|
+
nearest: () => nearest,
|
|
646
|
+
rubber: () => rubber,
|
|
647
|
+
snap: () => snap
|
|
648
|
+
});
|
|
649
|
+
/** iOS rubber-band curve: sub-linear overshoot, asymptotes to
|
|
650
|
+
* `dimension * constant` — pull but never escape. */
|
|
651
|
+
function resist(overshoot, dimension, constant) {
|
|
652
|
+
return overshoot * dimension * constant / (dimension + constant * overshoot);
|
|
653
|
+
}
|
|
654
|
+
/** Elastic resistance past `[min, max]`. The true value stays in `Motion`, so
|
|
655
|
+
* release settles back cleanly. `dimension` = axis extent; `constant` = iOS
|
|
656
|
+
* tension (higher = looser). */
|
|
657
|
+
function rubber(min, max, dimension, constant = .55) {
|
|
658
|
+
return (x) => x < min ? min - resist(min - x, dimension, constant) : x > max ? max + resist(x - max, dimension, constant) : x;
|
|
659
|
+
}
|
|
660
|
+
/** The nearest detent to `value`. */
|
|
661
|
+
function nearest(value, points) {
|
|
662
|
+
return points.reduce((a, b) => Math.abs(b - value) < Math.abs(a - value) ? b : a);
|
|
663
|
+
}
|
|
664
|
+
/** Magnetic pull toward the nearest detent during a drag (0 = free, 1 = snap). */
|
|
665
|
+
function detent(points, strength = 0) {
|
|
666
|
+
return (x) => x + (nearest(x, points) - x) * strength;
|
|
667
|
+
}
|
|
668
|
+
/** Release target: project by velocity (`reach` ms of carry), then nearest detent. */
|
|
669
|
+
function snap(value, velocity, points, reach = 150) {
|
|
670
|
+
return nearest(value + velocity * reach, points);
|
|
1527
671
|
}
|
|
1528
672
|
//#endregion
|
|
1529
|
-
//#region src/ui/overlay/
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
return
|
|
1561
|
-
}
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
}
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
onCleanup(dispose);
|
|
1580
|
-
return {
|
|
1581
|
-
update,
|
|
1582
|
-
attach,
|
|
1583
|
-
dispose,
|
|
1584
|
-
[Symbol.dispose]: dispose
|
|
1585
|
-
};
|
|
1586
|
-
}
|
|
1587
|
-
//#endregion
|
|
1588
|
-
//#region src/ui/overlay/rubber.ts
|
|
1589
|
-
/**
|
|
1590
|
-
* Edge resistance — values past the bounds move at a fraction of the
|
|
1591
|
-
* pointer (the iOS rubber band). Pure `resist()` under the hood; no
|
|
1592
|
-
* channels involved, so it applies to anything a service moves.
|
|
1593
|
-
*/
|
|
1594
|
-
function rubber() {
|
|
1595
|
-
return { during: (value, [lo, hi]) => resist(value, lo, hi) };
|
|
1596
|
-
}
|
|
673
|
+
//#region src/ui/overlay/motion.ts
|
|
674
|
+
var Motion = class {
|
|
675
|
+
#initial;
|
|
676
|
+
#time;
|
|
677
|
+
#value;
|
|
678
|
+
#delta;
|
|
679
|
+
#velocity;
|
|
680
|
+
constructor(value = 0) {
|
|
681
|
+
this.#initial = value;
|
|
682
|
+
this.#time = performance.now();
|
|
683
|
+
this.#value = signal(value);
|
|
684
|
+
this.#delta = signal(0);
|
|
685
|
+
this.#velocity = signal(0);
|
|
686
|
+
}
|
|
687
|
+
get value() {
|
|
688
|
+
return this.#value();
|
|
689
|
+
}
|
|
690
|
+
set value(value) {
|
|
691
|
+
const now = performance.now();
|
|
692
|
+
const prev = this.#value();
|
|
693
|
+
const dt = now - this.#time;
|
|
694
|
+
const delta = value - prev;
|
|
695
|
+
this.#delta(delta);
|
|
696
|
+
if (dt > 0) this.#velocity(delta / dt);
|
|
697
|
+
this.#value(value);
|
|
698
|
+
this.#time = now;
|
|
699
|
+
}
|
|
700
|
+
get delta() {
|
|
701
|
+
return this.#delta();
|
|
702
|
+
}
|
|
703
|
+
get velocity() {
|
|
704
|
+
return this.#velocity();
|
|
705
|
+
}
|
|
706
|
+
/** How far the value has moved from its initial (seed) — reactive.
|
|
707
|
+
* The displacement column: `value − initial`, starts at 0. */
|
|
708
|
+
get displacement() {
|
|
709
|
+
return this.#value() - this.#initial;
|
|
710
|
+
}
|
|
711
|
+
/** Nudge the value by a displacement — delta/velocity fall out of the setter. */
|
|
712
|
+
move(delta) {
|
|
713
|
+
this.value = this.#value() + delta;
|
|
714
|
+
}
|
|
715
|
+
abort(initial) {
|
|
716
|
+
if (initial !== void 0) this.#initial = initial;
|
|
717
|
+
this.#time = performance.now();
|
|
718
|
+
this.#value(this.#initial);
|
|
719
|
+
this.#delta(0);
|
|
720
|
+
this.#velocity(0);
|
|
721
|
+
}
|
|
722
|
+
};
|
|
1597
723
|
//#endregion
|
|
1598
|
-
export {
|
|
724
|
+
export { ElementBox, gestures_exports as Gestures, Motion, MutableRegion, OverlayBox, PositionArea, WINDOW_BOX, WindowBox, anchor_length, inset, placeAxis };
|