@n-uf/motion 26.11.2 → 26.11.4

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.
@@ -0,0 +1,212 @@
1
+ // src/shapes.ts
2
+ var SCALAR = 0;
3
+ var VECTOR = 1;
4
+ var POINT = 2;
5
+ var RECT = 3;
6
+ var SCALAR_LAYOUT = { kind: SCALAR, size: 1 };
7
+ var POINT_LAYOUT = { kind: POINT, size: 2 };
8
+ var RECT_LAYOUT = { kind: RECT, size: 4 };
9
+ function layoutOf(value) {
10
+ if (typeof value === "number") return SCALAR_LAYOUT;
11
+ if (Array.isArray(value)) return { kind: VECTOR, size: value.length };
12
+ if ("width" in value) return RECT_LAYOUT;
13
+ return POINT_LAYOUT;
14
+ }
15
+ function sameLayout(a, b) {
16
+ return a.kind === b.kind && a.size === b.size;
17
+ }
18
+ function writeComponents(value, out) {
19
+ if (typeof value === "number") {
20
+ out[0] = value;
21
+ } else if (Array.isArray(value)) {
22
+ for (let i = 0; i < value.length; i++) out[i] = value[i];
23
+ } else if ("width" in value) {
24
+ out[0] = value.x;
25
+ out[1] = value.y;
26
+ out[2] = value.width;
27
+ out[3] = value.height;
28
+ } else {
29
+ const point = value;
30
+ out[0] = point.x;
31
+ out[1] = point.y;
32
+ }
33
+ }
34
+ function makeValue(layout, components) {
35
+ switch (layout.kind) {
36
+ case SCALAR:
37
+ return components[0];
38
+ case POINT:
39
+ return { x: components[0], y: components[1] };
40
+ case RECT:
41
+ return { x: components[0], y: components[1], width: components[2], height: components[3] };
42
+ default:
43
+ return Array.from(components);
44
+ }
45
+ }
46
+ function shapeEquals(a, b) {
47
+ if (typeof a === "number" || typeof b === "number") return a === b;
48
+ if (Array.isArray(a) || Array.isArray(b)) {
49
+ const u = a;
50
+ const v = b;
51
+ if (u.length !== v.length) return false;
52
+ for (let i = 0; i < u.length; i++) if (u[i] !== v[i]) return false;
53
+ return true;
54
+ }
55
+ const p = a;
56
+ const q = b;
57
+ return p.x === q.x && p.y === q.y && p.width === q.width && p.height === q.height;
58
+ }
59
+
60
+ // src/values/settle.ts
61
+ var SETTLE_TEMPO = { calm: 20, default: 25, quick: 37 };
62
+ var SETTLE_DEFAULTS = { omega: SETTLE_TEMPO.default, restDelta: 0.1 };
63
+ function positive(name, value) {
64
+ if (!(value > 0) || !Number.isFinite(value)) throw new RangeError(`settle: ${name} must be a finite number > 0, got ${value}`);
65
+ return value;
66
+ }
67
+ var SettleAnimator = class {
68
+ layout;
69
+ x;
70
+ v;
71
+ goal;
72
+ omega;
73
+ restDelta;
74
+ settled = true;
75
+ cachedValue = null;
76
+ constructor(initial, config) {
77
+ this.layout = layoutOf(initial);
78
+ this.x = new Float64Array(this.layout.size);
79
+ this.v = new Float64Array(this.layout.size);
80
+ this.goal = new Float64Array(this.layout.size);
81
+ writeComponents(initial, this.x);
82
+ writeComponents(initial, this.goal);
83
+ this.omega = positive("omega", config.omega ?? SETTLE_DEFAULTS.omega);
84
+ this.restDelta = positive("restDelta", config.restDelta ?? SETTLE_DEFAULTS.restDelta);
85
+ }
86
+ get value() {
87
+ if (this.cachedValue === null) this.cachedValue = makeValue(this.layout, this.x);
88
+ return this.cachedValue;
89
+ }
90
+ get velocity() {
91
+ return makeValue(this.layout, this.v);
92
+ }
93
+ get target() {
94
+ return makeValue(this.layout, this.goal);
95
+ }
96
+ check(value, what) {
97
+ if (!sameLayout(layoutOf(value), this.layout)) {
98
+ throw new TypeError(`settle: ${what} has a different shape than the settle's value`);
99
+ }
100
+ }
101
+ /** The launch/clamp/carry rule on component `i`: `launch` for a new target, else clamp or carry. */
102
+ rule(i, launch) {
103
+ const d = this.x[i] - this.goal[i];
104
+ const v = this.v[i];
105
+ const limit = d === 0 ? 0 : -this.omega * d;
106
+ if (launch) {
107
+ this.v[i] = limit;
108
+ return;
109
+ }
110
+ if (d === 0) return;
111
+ if (d * v > 0) return;
112
+ if (Math.abs(v) > Math.abs(limit)) this.v[i] = limit;
113
+ }
114
+ updateSettled() {
115
+ for (let i = 0; i < this.x.length; i++) {
116
+ if (this.x[i] !== this.goal[i] || this.v[i] !== 0) {
117
+ this.settled = false;
118
+ return;
119
+ }
120
+ }
121
+ this.settled = true;
122
+ }
123
+ setTarget(target) {
124
+ this.check(target, "the target");
125
+ writeComponents(target, this.goal);
126
+ for (let i = 0; i < this.x.length; i++) this.rule(i, true);
127
+ this.updateSettled();
128
+ }
129
+ setVelocity(velocity) {
130
+ this.check(velocity, "the velocity");
131
+ writeComponents(velocity, this.v);
132
+ for (let i = 0; i < this.x.length; i++) this.rule(i, false);
133
+ this.updateSettled();
134
+ }
135
+ jumpTo(value) {
136
+ this.check(value, "the value");
137
+ writeComponents(value, this.x);
138
+ writeComponents(value, this.goal);
139
+ this.v.fill(0);
140
+ this.cachedValue = null;
141
+ this.settled = true;
142
+ }
143
+ hold(value) {
144
+ this.jumpTo(value);
145
+ }
146
+ snap() {
147
+ this.x.set(this.goal);
148
+ this.v.fill(0);
149
+ this.cachedValue = null;
150
+ this.settled = true;
151
+ }
152
+ configure(config) {
153
+ if (config.omega !== void 0) this.omega = positive("omega", config.omega);
154
+ if (config.restDelta !== void 0) this.restDelta = positive("restDelta", config.restDelta);
155
+ for (let i = 0; i < this.x.length; i++) this.rule(i, false);
156
+ }
157
+ step(dtMs) {
158
+ if (this.settled) return false;
159
+ if (!(dtMs > 0)) return true;
160
+ const w = this.omega;
161
+ const t = dtMs / 1e3;
162
+ const e = Math.exp(-w * t);
163
+ const { x, v, goal, restDelta } = this;
164
+ const restSpeed = w * restDelta * 1.000001;
165
+ let resting = true;
166
+ for (let i = 0; i < x.length; i++) {
167
+ const d = x[i] - goal[i];
168
+ const b = v[i] + w * d;
169
+ const nd = e * (d + b * t);
170
+ v[i] = e * (v[i] - w * b * t);
171
+ x[i] = goal[i] + nd;
172
+ const near = Math.max(restDelta, 4 * Number.EPSILON * Math.abs(goal[i]));
173
+ if (resting && (Math.abs(nd) > near || Math.abs(v[i]) > Math.max(restSpeed, w * near))) resting = false;
174
+ }
175
+ this.cachedValue = null;
176
+ if (resting) this.snap();
177
+ return !this.settled;
178
+ }
179
+ };
180
+ function createSettle(initial, config = {}) {
181
+ return new SettleAnimator(initial, config);
182
+ }
183
+
184
+ // src/values/reveal.ts
185
+ function reveal(drawn, target) {
186
+ return {
187
+ dx: drawn.x - target.x,
188
+ dy: drawn.y - target.y,
189
+ width: Math.max(0, drawn.width),
190
+ height: Math.max(0, drawn.height),
191
+ clipRight: Math.max(0, target.width - drawn.width),
192
+ clipBottom: Math.max(0, target.height - drawn.height)
193
+ };
194
+ }
195
+ function popIn(progress, from = 0.87) {
196
+ const p = Math.min(1, Math.max(0, progress));
197
+ return { scale: from + (1 - from) * p, opacity: p };
198
+ }
199
+
200
+ export {
201
+ layoutOf,
202
+ sameLayout,
203
+ writeComponents,
204
+ makeValue,
205
+ shapeEquals,
206
+ SETTLE_TEMPO,
207
+ SETTLE_DEFAULTS,
208
+ createSettle,
209
+ reveal,
210
+ popIn
211
+ };
212
+ //# sourceMappingURL=chunk-WQQ2E25M.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/shapes.ts","../src/values/settle.ts","../src/values/reveal.ts"],"sourcesContent":["/** A point or a 2D vector. */\nexport interface Point {\n x: number;\n y: number;\n}\n\n/** An axis-aligned rectangle. */\nexport interface Rect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\n/** Any number of components. */\nexport type Vector = readonly number[];\n\n/** The value shapes springs and tweens move: plain numbers, nothing else. */\nexport type Animatable = number | Point | Rect | Vector;\n\n/** The shape type of an {@link Animatable}: literal numbers widen to `number`. */\nexport type Widen<T> = T extends number\n ? number\n : T extends Vector\n ? Vector\n : T extends Rect\n ? Rect\n : T extends Point\n ? Point\n : never;\n\nconst SCALAR = 0;\nconst VECTOR = 1;\nconst POINT = 2;\nconst RECT = 3;\n\n/** How a shape maps onto a flat list of numbers. Internal to values/. */\nexport interface Layout {\n readonly kind: typeof SCALAR | typeof VECTOR | typeof POINT | typeof RECT;\n readonly size: number;\n}\n\nconst SCALAR_LAYOUT: Layout = { kind: SCALAR, size: 1 };\nconst POINT_LAYOUT: Layout = { kind: POINT, size: 2 };\nconst RECT_LAYOUT: Layout = { kind: RECT, size: 4 };\n\nexport function layoutOf(value: Animatable): Layout {\n if (typeof value === \"number\") return SCALAR_LAYOUT;\n if (Array.isArray(value)) return { kind: VECTOR, size: value.length };\n if (\"width\" in value) return RECT_LAYOUT;\n return POINT_LAYOUT;\n}\n\nexport function sameLayout(a: Layout, b: Layout): boolean {\n return a.kind === b.kind && a.size === b.size;\n}\n\n/** Copy `value`'s components into `out`. */\nexport function writeComponents(value: Animatable, out: Float64Array): void {\n if (typeof value === \"number\") {\n out[0] = value;\n } else if (Array.isArray(value)) {\n for (let i = 0; i < value.length; i++) out[i] = value[i]!;\n } else if (\"width\" in value) {\n out[0] = value.x;\n out[1] = value.y;\n out[2] = value.width;\n out[3] = value.height;\n } else {\n const point = value as Point;\n out[0] = point.x;\n out[1] = point.y;\n }\n}\n\n/** A fresh value of `layout`'s shape from `components`. */\nexport function makeValue(layout: Layout, components: Float64Array): Animatable {\n switch (layout.kind) {\n case SCALAR:\n return components[0]!;\n case POINT:\n return { x: components[0]!, y: components[1]! };\n case RECT:\n return { x: components[0]!, y: components[1]!, width: components[2]!, height: components[3]! };\n default:\n return Array.from(components);\n }\n}\n\n/** Component-wise equality of two values of the same shape. */\nexport function shapeEquals(a: Animatable, b: Animatable): boolean {\n if (typeof a === \"number\" || typeof b === \"number\") return a === b;\n if (Array.isArray(a) || Array.isArray(b)) {\n const u = a as Vector;\n const v = b as Vector;\n if (u.length !== v.length) return false;\n for (let i = 0; i < u.length; i++) if (u[i] !== v[i]) return false;\n return true;\n }\n const p = a as Partial<Rect>;\n const q = b as Partial<Rect>;\n return p.x === q.x && p.y === q.y && p.width === q.width && p.height === q.height;\n}\n","import type { Animatable, Layout, Widen } from \"../shapes\";\nimport { layoutOf, makeValue, sameLayout, writeComponents } from \"../shapes\";\nimport type { Animator } from \"./animator\";\n\n/**\n * Tempos for {@link createSettle}, in 1/s: a settle covers 90 % of the way in\n * ln(10)/omega seconds and is within 1 % in twice that.\n *\n * - `calm` 20: 90 % in 115 ms, done in 230 ms.\n * - `default` 25: 90 % in 92 ms, done in 184 ms.\n * - `quick` 37: 90 % in 62 ms, done in 124 ms (a fast Hyprland setup).\n */\nexport const SETTLE_TEMPO = { calm: 20, default: 25, quick: 37 } as const;\n\n/** Settings of {@link createSettle}. */\nexport interface SettleConfig {\n /** The tempo in 1/s (see {@link SETTLE_TEMPO}). Higher is faster. Default 25. */\n readonly omega?: number;\n /** Distance from the target that counts as arrived. Default 0.1 (tuned for pixels). */\n readonly restDelta?: number;\n}\n\n/**\n * The one curve for things that move to a new place: a window's rect between\n * layout targets, a drawer, a camera. Fast at the start, gentle at the end,\n * and it never passes its target.\n *\n * It is a critically damped spring (stiffness omega², damping 2·omega) with\n * one rule on its velocity, per component:\n *\n * - **Launch.** On a new target, each component gets the velocity\n * omega · distance toward it, from wherever it is drawn: standing still,\n * moving toward it, or moving away (it turns at once rather than drifting\n * further first). From there the spring follows `distance · e^(−omega·t)`\n * exactly: most of the move happens at once, the rest eases in.\n * - **Clamp.** That speed is also the most it may have toward the target, so a\n * velocity set by hand (`setVelocity`, a fling) toward the target is capped\n * there instead of shooting past.\n * - **Carry.** A velocity set by hand away from the target is kept, and the\n * value turns around; a critically damped spring turning around never\n * crosses the target either.\n *\n * So after a new target every component goes straight from where it is drawn\n * to the target and never past it: no overshoot on position, no wobble on\n * size, whatever the retargets. A new target starts from the drawn value,\n * never from the old start.\n *\n * Holding (a pointer owns the value): call `hold(value)` on each move. The\n * value sits exactly there, at rest; the next `setTarget` launches from it.\n */\nexport interface Settle<T> extends Animator<T> {\n /** The tempo in 1/s. */\n readonly omega: number;\n /** Put the value at `value`, at rest, with it as the target: a held value follows its owner 1:1. Same as `jumpTo`. */\n hold(value: T): void;\n /** Set the velocity (units per second); a component moving toward the target is clamped as in the launch rule. */\n setVelocity(velocity: T): void;\n /** Change the tempo or rest distance mid-flight; the value and velocity carry on, re-clamped. */\n configure(config: SettleConfig): void;\n}\n\n/** Defaults of {@link SettleConfig}. */\nexport const SETTLE_DEFAULTS = { omega: SETTLE_TEMPO.default, restDelta: 0.1 } as const;\n\nfunction positive(name: string, value: number): number {\n if (!(value > 0) || !Number.isFinite(value)) throw new RangeError(`settle: ${name} must be a finite number > 0, got ${value}`);\n return value;\n}\n\nclass SettleAnimator implements Settle<Animatable> {\n readonly layout: Layout;\n readonly x: Float64Array;\n readonly v: Float64Array;\n readonly goal: Float64Array;\n omega: number;\n restDelta: number;\n settled = true;\n cachedValue: Animatable | null = null;\n\n constructor(initial: Animatable, config: SettleConfig) {\n this.layout = layoutOf(initial);\n this.x = new Float64Array(this.layout.size);\n this.v = new Float64Array(this.layout.size);\n this.goal = new Float64Array(this.layout.size);\n writeComponents(initial, this.x);\n writeComponents(initial, this.goal);\n this.omega = positive(\"omega\", config.omega ?? SETTLE_DEFAULTS.omega);\n this.restDelta = positive(\"restDelta\", config.restDelta ?? SETTLE_DEFAULTS.restDelta);\n }\n\n get value(): Animatable {\n if (this.cachedValue === null) this.cachedValue = makeValue(this.layout, this.x);\n return this.cachedValue;\n }\n\n get velocity(): Animatable {\n return makeValue(this.layout, this.v);\n }\n\n get target(): Animatable {\n return makeValue(this.layout, this.goal);\n }\n\n check(value: Animatable, what: string): void {\n if (!sameLayout(layoutOf(value), this.layout)) {\n throw new TypeError(`settle: ${what} has a different shape than the settle's value`);\n }\n }\n\n /** The launch/clamp/carry rule on component `i`: `launch` for a new target, else clamp or carry. */\n rule(i: number, launch: boolean): void {\n const d = this.x[i]! - this.goal[i]!;\n const v = this.v[i]!;\n const limit = d === 0 ? 0 : -this.omega * d;\n if (launch) {\n this.v[i] = limit;\n return;\n }\n if (d === 0) return;\n // Moving away from the target (d and v the same sign): carry.\n if (d * v > 0) return;\n // Moving toward it: no faster than a launch from here.\n if (Math.abs(v) > Math.abs(limit)) this.v[i] = limit;\n }\n\n updateSettled(): void {\n for (let i = 0; i < this.x.length; i++) {\n if (this.x[i] !== this.goal[i] || this.v[i] !== 0) {\n this.settled = false;\n return;\n }\n }\n this.settled = true;\n }\n\n setTarget(target: Animatable): void {\n this.check(target, \"the target\");\n writeComponents(target, this.goal);\n for (let i = 0; i < this.x.length; i++) this.rule(i, true);\n this.updateSettled();\n }\n\n setVelocity(velocity: Animatable): void {\n this.check(velocity, \"the velocity\");\n writeComponents(velocity, this.v);\n for (let i = 0; i < this.x.length; i++) this.rule(i, false);\n this.updateSettled();\n }\n\n jumpTo(value: Animatable): void {\n this.check(value, \"the value\");\n writeComponents(value, this.x);\n writeComponents(value, this.goal);\n this.v.fill(0);\n this.cachedValue = null;\n this.settled = true;\n }\n\n hold(value: Animatable): void {\n this.jumpTo(value);\n }\n\n snap(): void {\n this.x.set(this.goal);\n this.v.fill(0);\n this.cachedValue = null;\n this.settled = true;\n }\n\n configure(config: SettleConfig): void {\n if (config.omega !== undefined) this.omega = positive(\"omega\", config.omega);\n if (config.restDelta !== undefined) this.restDelta = positive(\"restDelta\", config.restDelta);\n for (let i = 0; i < this.x.length; i++) this.rule(i, false);\n }\n\n step(dtMs: number): boolean {\n if (this.settled) return false;\n if (!(dtMs > 0)) return true;\n const w = this.omega;\n const t = dtMs / 1000;\n const e = Math.exp(-w * t);\n const { x, v, goal, restDelta } = this;\n // At rest when every component is within restDelta and no faster than a launch from there would be.\n const restSpeed = w * restDelta * 1.000001;\n let resting = true;\n for (let i = 0; i < x.length; i++) {\n // The exact critically damped step: d(t) = e^(−wt)·(d + b·t), b = v + w·d.\n const d = x[i]! - goal[i]!;\n const b = v[i]! + w * d;\n const nd = e * (d + b * t);\n v[i] = e * (v[i]! - w * b * t);\n x[i] = goal[i]! + nd;\n // Within a few float steps of the goal counts as arrived too: a restDelta finer than that would never be met.\n const near = Math.max(restDelta, 4 * Number.EPSILON * Math.abs(goal[i]!));\n if (resting && (Math.abs(nd) > near || Math.abs(v[i]!) > Math.max(restSpeed, w * near))) resting = false;\n }\n this.cachedValue = null;\n if (resting) this.snap();\n return !this.settled;\n }\n}\n\n/**\n * A settle starting at rest on `initial` (a number, a vector, a point or a\n * rect; targets and velocities must have the same shape). See {@link Settle}.\n */\nexport function createSettle<T extends Animatable>(initial: T, config: SettleConfig = {}): Settle<Widen<T>> {\n return new SettleAnimator(initial, config) as unknown as Settle<Widen<T>>;\n}\n","import type { Rect } from \"../shapes\";\n\n/**\n * How to draw a window whose rect is gliding without stretching its content.\n *\n * The content is laid out once at the target size and never scaled. The\n * window's element sits at its target in the layout; to draw it at the\n * gliding rect:\n *\n * - move the element by (`dx`, `dy`), so its top-left corner is the drawn rect's;\n * - draw the frame (background, border, shadow) at `width` × `height`: the drawn size;\n * - clip the content by `clipRight` and `clipBottom` so it fits a frame\n * smaller than the target (CSS: `clip-path: inset(0 clipRight clipBottom 0)`).\n *\n * A window that shrinks shows a band of its own background closing in at the\n * right and bottom; one that grows reveals content already laid out at its\n * new size. Text is never stretched. One transform and one clip per frame, no\n * layout per frame.\n */\nexport interface RevealFrame {\n /** Move the element (laid out at the target) right by this much. */\n readonly dx: number;\n /** Move the element (laid out at the target) down by this much. */\n readonly dy: number;\n /** The frame's width: the drawn rect's. */\n readonly width: number;\n /** The frame's height: the drawn rect's. */\n readonly height: number;\n /** Clip this much off the content's right edge (0 when the frame is at least as wide as the target). */\n readonly clipRight: number;\n /** Clip this much off the content's bottom edge (0 when the frame is at least as tall as the target). */\n readonly clipBottom: number;\n}\n\n/** The {@link RevealFrame} for content laid out at `target` and drawn at `drawn`. */\nexport function reveal(drawn: Rect, target: Rect): RevealFrame {\n return {\n dx: drawn.x - target.x,\n dy: drawn.y - target.y,\n width: Math.max(0, drawn.width),\n height: Math.max(0, drawn.height),\n clipRight: Math.max(0, target.width - drawn.width),\n clipBottom: Math.max(0, target.height - drawn.height),\n };\n}\n\n/** A uniform pop-in (or pop-out, run backwards): scale about the centre and opacity. */\nexport interface PopIn {\n /** Scale about the window's centre: the same on both axes, so nothing is stretched. */\n readonly scale: number;\n readonly opacity: number;\n}\n\n/**\n * Opening and closing: the window grows from `from` of its size to full, about\n * its centre, while fading in. `progress` runs 0 to 1 (take it from a settle\n * on a number with `{ restDelta: 0.001 }`, since the default 0.1 is tuned for\n * pixels and would snap the last 10 %; or from a tween); closing runs it from\n * 1 to 0. Hyprland's default is `popin 87%`.\n */\nexport function popIn(progress: number, from = 0.87): PopIn {\n const p = Math.min(1, Math.max(0, progress));\n return { scale: from + (1 - from) * p, opacity: p };\n}\n"],"mappings":"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","names":[]}
package/dist/drag.d.ts ADDED
@@ -0,0 +1,202 @@
1
+ import { R as Rect, P as Point } from './shapes-EbQlwbCu.js';
2
+
3
+ /**
4
+ * The drag intent model's tuning. Distances in px, speeds in px/ms, times in
5
+ * ms; every value can be overridden per intent (see
6
+ * {@link DragIntentOptions.tuning}).
7
+ */
8
+ interface DragTuning {
9
+ /** A mouse press lifts once the pointer has moved this far. */
10
+ readonly lift: number;
11
+ /** A touch press lifts only after it has been held this long… */
12
+ readonly touchHold: number;
13
+ /** …and then moved this far (moving that far before the hold is over is not a drag). */
14
+ readonly touchLift: number;
15
+ /** The pointer's speed is measured over this window and smoothed. */
16
+ readonly speedWindow: number;
17
+ /** Aiming: the smoothed speed is below this… */
18
+ readonly aimSpeed: number;
19
+ /** …or the pointer has stayed within `still` px of one spot for `dwell` ms. */
20
+ readonly still: number;
21
+ readonly dwell: number;
22
+ /** The current target holds while the pointer is within this band of its zone. */
23
+ readonly band: number;
24
+ /** A higher-priority target takes over at once when the pointer is this far inside it. */
25
+ readonly breakthrough: number;
26
+ /** A gap between zones shorter than this keeps the current target (for a pointer that aims). */
27
+ readonly gapGrace: number;
28
+ /** Aiming, the shown target moves to another one only once that one has held this long. */
29
+ readonly switchHold: number;
30
+ /** Room is made once the shown target, and the place in it, have held this long. */
31
+ readonly roomAfter: number;
32
+ /** The pointer has left the item's home once it is outside the home shrunk by this much on every side. */
33
+ readonly homeInset: number;
34
+ }
35
+ /**
36
+ * The defaults: a mouse lifts after 5 px, a touch after a 180 ms hold and
37
+ * 8 px; aiming is slower than 0.45 px/ms over 50 ms, or within 12 px for
38
+ * 90 ms; a 10 px band, a 4 px breakthrough and a 120 ms gap grace; a switch
39
+ * waits 150 ms (the shortest an indicator may show); room after 160 ms.
40
+ */
41
+ declare const DRAG_DEFAULTS: DragTuning;
42
+ /**
43
+ * A drop target: plain data. `hit` is the zone the pointer can be in;
44
+ * `priority` how precise it is (1 beats 2 beats 3; equal priorities go to the
45
+ * smaller zone). `slot` is where the dragged item would land, `label` what to
46
+ * call it; the model reads neither. A `free` target has no slot (the item
47
+ * floats where it is dropped): no room is made for it.
48
+ */
49
+ interface DragTarget {
50
+ readonly id: string;
51
+ readonly priority: 1 | 2 | 3;
52
+ readonly hit: Rect;
53
+ readonly slot?: Rect;
54
+ readonly label?: string;
55
+ readonly free?: boolean;
56
+ }
57
+ /** Whether `point` lies in `rect` grown by `margin` px on every side (shrunk, for a negative margin). */
58
+ declare function inRect(rect: Rect, point: Point, margin?: number): boolean;
59
+ /** The most precise target under `point`: the lowest priority number, then the smallest zone; null when none is. */
60
+ declare function targetAt<T extends DragTarget>(targets: readonly T[], point: Point): T | null;
61
+ /** What a press does so far: not yet a drag (`wait`), a drag (`lift`), or never one (`refuse`: a touch that moved too soon). */
62
+ type LiftDecision = "wait" | "lift" | "refuse";
63
+ /**
64
+ * The lift rule for a press that has moved `distance` px in `elapsed` ms. A
65
+ * mouse (or pen) lifts at `lift` px. A touch lifts only after `touchHold` ms,
66
+ * then at `touchLift` px; moving `touchLift` px before the hold is over
67
+ * refuses the drag (the finger is scrolling or swiping).
68
+ */
69
+ declare function liftDecision(press: {
70
+ readonly touch: boolean;
71
+ readonly distance: number;
72
+ readonly elapsed: number;
73
+ }, tuning?: Partial<DragTuning>): LiftDecision;
74
+ /** One update's answer. */
75
+ interface DragFrame<T extends DragTarget> {
76
+ /** The target the drop indicator shows, or null. */
77
+ readonly shown: T | null;
78
+ /** The others make room for the shown target: it, and the place in it, have held `roomAfter` ms. */
79
+ readonly room: boolean;
80
+ /**
81
+ * The place the room is made for, or null. Within one target it stays on
82
+ * the old place until a new one has held `roomAfter` ms (see
83
+ * {@link DragIntentOptions.placeOf}).
84
+ */
85
+ readonly roomPlace: string | null;
86
+ /** The pointer is aiming: slow, or nearly still for a moment. */
87
+ readonly aiming: boolean;
88
+ }
89
+ /** What a drag intent knows now. */
90
+ interface DragIntentState<T extends DragTarget> {
91
+ /** The pointer's smoothed speed, px/ms. */
92
+ readonly speed: number;
93
+ /** The target under the pointer as the hysteresis holds it, or null. */
94
+ readonly current: T | null;
95
+ /** The target the indicator shows, or null. */
96
+ readonly shown: T | null;
97
+ /** The pointer has left the item's home. */
98
+ readonly leftHome: boolean;
99
+ }
100
+ interface DragIntentOptions<T extends DragTarget> {
101
+ /** Where the item was lifted from: the indicator stays off until the pointer leaves it, and a release there does nothing. */
102
+ readonly home: Rect;
103
+ /** Overrides of {@link DRAG_DEFAULTS}. */
104
+ readonly tuning?: Partial<DragTuning>;
105
+ /**
106
+ * The place within a target the pointer is at (a tab index, a stack
107
+ * position), unique across targets; default: the target's id. Moving between places of the
108
+ * shown target moves the indicator at once but the room only once the new
109
+ * place has held `roomAfter` ms.
110
+ */
111
+ readonly placeOf?: (target: T, point: Point) => string;
112
+ }
113
+ /** A drag's intent: feed it the pointer on every frame; it says what the indicator shows and what a release lands on. */
114
+ interface DragIntent<T extends DragTarget> {
115
+ /**
116
+ * A pointer sample at `time` (ms), with the targets as they stand (they may
117
+ * be rebuilt between calls: the model follows them by id). Call it once a
118
+ * frame while the item is held, also when the pointer doesn't move: a
119
+ * dwell is time passing.
120
+ */
121
+ update(point: Point, time: number, targets: readonly T[]): DragFrame<T>;
122
+ /**
123
+ * What a release at `point` lands on: the shown target whenever one shows
124
+ * (a drop lands where the indicator was), else the target under the
125
+ * pointer, else null (the item goes home). A release over home is null: it
126
+ * does nothing. Feed the release point to `update` first, so a last jump
127
+ * of the pointer counts.
128
+ */
129
+ release(point: Point, targets: readonly T[]): T | null;
130
+ /**
131
+ * Hide the indicator (the view is scrolling under the pointer, say). The
132
+ * aim starts afresh: the next sample never counts as aiming, and a dwell
133
+ * starts again.
134
+ */
135
+ hide(time: number): void;
136
+ /** The item's home moved with the view (it scrolled) by (dx, dy). */
137
+ moveHome(dx: number, dy: number): void;
138
+ readonly state: DragIntentState<T>;
139
+ readonly tuning: DragTuning;
140
+ }
141
+ /**
142
+ * The drag intent model: when the drop indicator shows, which target it
143
+ * shows, when the others make room, and what a release lands on. Pure: fed
144
+ * pointer samples and plain-data targets, no DOM and no clock of its own.
145
+ *
146
+ * - The indicator shows only when the pointer has left the item's home, a
147
+ * target is under it, and it is aiming (slow, or nearly still for a
148
+ * moment). Time spent crossing a big target at speed doesn't count.
149
+ * - The most precise target wins (priority, then the smaller zone).
150
+ * - Hysteresis: the current target holds while the pointer is within `band`
151
+ * px of its zone, unless a more precise one is clearly (`breakthrough` px)
152
+ * under the pointer; for a pointer that aims, a short gap between zones
153
+ * (`gapGrace` ms) keeps it too.
154
+ * - Leaving the shown target: flying, the indicator hides at once; aiming, it
155
+ * moves to the new target once that has held `switchHold` ms (so no
156
+ * indicator shows for less than that), at once for a breakthrough. A target
157
+ * kept only by the gap grace can keep the indicator, never show it.
158
+ * - Room is made once the shown target has held `roomAfter` ms, never for a
159
+ * free target, and closes at once when it changes.
160
+ */
161
+ declare function createDragIntent<T extends DragTarget>(options: DragIntentOptions<T>): DragIntent<T>;
162
+
163
+ /**
164
+ * How a drag previews its drop, from least to most motion while dragging:
165
+ *
166
+ * - `"indicator"`: nothing moves; only the indicator (an outline or an
167
+ * insertion marker) shows the target.
168
+ * - `"gap"`: once the room is made, the others move aside and open the slot;
169
+ * the item stays under the pointer.
170
+ * - `"snap"`: the gap opens and the item itself glides into it (for a target
171
+ * with a slot; one without keeps the indicator).
172
+ *
173
+ * Every mode follows the same intent: what shows, when, and what a release
174
+ * lands on are the drag intent's, so a drop always lands where the preview
175
+ * showed it.
176
+ */
177
+ type DragPreview = "indicator" | "gap" | "snap";
178
+ /** The preview a drag uses unless the host picks another. */
179
+ declare const DRAG_PREVIEW_DEFAULT: DragPreview;
180
+ /** What to draw for one frame of a drag. */
181
+ interface PreviewPlan {
182
+ /** Draw the indicator on the shown target. */
183
+ readonly showIndicator: boolean;
184
+ /** Lay the others out around the room (the gap is open). */
185
+ readonly makeRoom: boolean;
186
+ /** Draw the item in its slot instead of under the pointer. */
187
+ readonly snapItem: boolean;
188
+ }
189
+ /**
190
+ * The plan for a frame of the drag intent under `preview`. `slotted` says
191
+ * whether the shown target's drop has a slot the item can sit in (default: a
192
+ * target that isn't `free`); only such a target snaps, and in `"snap"` it
193
+ * previews with the item rather than the indicator.
194
+ *
195
+ * - `"indicator"`: the indicator, never room, never a snap.
196
+ * - `"gap"`: the indicator, and room when the intent makes it.
197
+ * - `"snap"`: room when the intent makes it, the item snapped into a slotted
198
+ * target's room; the indicator only for a target without a slot.
199
+ */
200
+ declare function previewPlan<T extends DragTarget>(frame: Pick<DragFrame<T>, "shown" | "room">, preview: DragPreview, slotted?: boolean): PreviewPlan;
201
+
202
+ export { DRAG_DEFAULTS, DRAG_PREVIEW_DEFAULT, type DragFrame, type DragIntent, type DragIntentOptions, type DragIntentState, type DragPreview, type DragTarget, type DragTuning, type LiftDecision, type PreviewPlan, createDragIntent, inRect, liftDecision, previewPlan, targetAt };
package/dist/drag.js ADDED
@@ -0,0 +1,151 @@
1
+ // src/drag/intent.ts
2
+ var DRAG_DEFAULTS = {
3
+ lift: 5,
4
+ touchHold: 180,
5
+ touchLift: 8,
6
+ speedWindow: 50,
7
+ aimSpeed: 0.45,
8
+ still: 12,
9
+ dwell: 90,
10
+ band: 10,
11
+ breakthrough: 4,
12
+ gapGrace: 120,
13
+ switchHold: 150,
14
+ roomAfter: 160,
15
+ homeInset: 6
16
+ };
17
+ function inRect(rect, point, margin = 0) {
18
+ return point.x >= rect.x - margin && point.x <= rect.x + rect.width + margin && point.y >= rect.y - margin && point.y <= rect.y + rect.height + margin;
19
+ }
20
+ var area = (r) => r.width * r.height;
21
+ function targetAt(targets, point) {
22
+ let best = null;
23
+ for (const t of targets) {
24
+ if (!inRect(t.hit, point)) continue;
25
+ if (!best || t.priority < best.priority || t.priority === best.priority && area(t.hit) < area(best.hit)) best = t;
26
+ }
27
+ return best;
28
+ }
29
+ function liftDecision(press, tuning = {}) {
30
+ const t = { ...DRAG_DEFAULTS, ...tuning };
31
+ if (!press.touch) return press.distance >= t.lift ? "lift" : "wait";
32
+ if (press.elapsed < t.touchHold) return press.distance >= t.touchLift ? "refuse" : "wait";
33
+ return press.distance >= t.touchLift ? "lift" : "wait";
34
+ }
35
+ function createDragIntent(options) {
36
+ const tuning = { ...DRAG_DEFAULTS, ...options.tuning };
37
+ const placeOf = options.placeOf ?? ((t) => t.id);
38
+ let home = { ...options.home };
39
+ let samples = [];
40
+ let cur = null;
41
+ let curSince = 0;
42
+ let lastInside = 0;
43
+ let shown = null;
44
+ let shownSince = 0;
45
+ let place = null;
46
+ let placeSince = 0;
47
+ let roomPlace = null;
48
+ let leftHome = false;
49
+ let speed = 0;
50
+ let anchor = null;
51
+ let stillSince = 0;
52
+ let restart = false;
53
+ const same = (a, b) => a === null || b === null ? a === b : a.id === b.id;
54
+ const follow = (t, targets) => t ? targets.find((x) => x.id === t.id) ?? t : null;
55
+ return {
56
+ tuning,
57
+ get state() {
58
+ return { speed, current: cur, shown, leftHome };
59
+ },
60
+ update(point, time, targets) {
61
+ const p = point;
62
+ const t = time;
63
+ samples.push({ x: p.x, y: p.y, t });
64
+ while (samples.length > 2 && t - samples[0].t > tuning.speedWindow) samples.shift();
65
+ const a = samples[0];
66
+ speed = speed * 0.5 + Math.hypot(p.x - a.x, p.y - a.y) / Math.max(1, t - a.t) * 0.5;
67
+ if (!anchor || Math.hypot(p.x - anchor.x, p.y - anchor.y) > tuning.still) {
68
+ anchor = { x: p.x, y: p.y };
69
+ stillSince = t;
70
+ }
71
+ if (!inRect(home, p, -tuning.homeInset)) leftHome = true;
72
+ const atHome = inRect(home, p);
73
+ const raw = atHome ? null : targetAt(targets, p);
74
+ cur = follow(cur, targets);
75
+ shown = follow(shown, targets);
76
+ let next = raw;
77
+ if (cur && !atHome && inRect(cur.hit, p, tuning.band)) {
78
+ const breaks = raw !== null && !same(raw, cur) && raw.priority < cur.priority && inRect(raw.hit, p, -tuning.breakthrough);
79
+ next = breaks ? raw : cur;
80
+ }
81
+ if (!next && cur && !atHome && t - lastInside < tuning.gapGrace) next = cur;
82
+ if (next && inRect(next.hit, p)) lastInside = t;
83
+ if (!same(next, cur)) curSince = t;
84
+ cur = next;
85
+ const aiming = !restart && (speed < tuning.aimSpeed || t - stillSince >= tuning.dwell);
86
+ restart = false;
87
+ const show = (s) => {
88
+ if (!same(s, shown)) shownSince = t;
89
+ shown = s;
90
+ };
91
+ const under = !!cur && inRect(cur.hit, p, tuning.band);
92
+ if (!shown) {
93
+ if (cur && under && leftHome && aiming) show(cur);
94
+ } else if (same(cur, shown)) {
95
+ if (!aiming && !under) show(null);
96
+ else shown = cur;
97
+ } else if (!cur || !aiming) {
98
+ show(null);
99
+ } else if (cur.priority < shown.priority && inRect(cur.hit, p, -tuning.breakthrough) || under && t - curSince >= tuning.switchHold) {
100
+ show(cur);
101
+ }
102
+ const at = shown ? placeOf(shown, p) : null;
103
+ if (at !== place) {
104
+ place = at;
105
+ placeSince = t;
106
+ }
107
+ if (!shown || shown.free) roomPlace = null;
108
+ else if (t - shownSince >= tuning.roomAfter && t - placeSince >= tuning.roomAfter) roomPlace = place;
109
+ else if (shownSince === t) roomPlace = null;
110
+ return { shown, room: roomPlace !== null, roomPlace, aiming };
111
+ },
112
+ release(point, targets) {
113
+ if (inRect(home, point)) return null;
114
+ const s = follow(shown, targets);
115
+ if (s) return s;
116
+ return targetAt(targets, point);
117
+ },
118
+ hide(time) {
119
+ shown = null;
120
+ cur = null;
121
+ shownSince = time;
122
+ place = null;
123
+ roomPlace = null;
124
+ samples = [];
125
+ anchor = null;
126
+ restart = true;
127
+ },
128
+ moveHome(dx, dy) {
129
+ home = { ...home, x: home.x + dx, y: home.y + dy };
130
+ }
131
+ };
132
+ }
133
+
134
+ // src/drag/preview.ts
135
+ var DRAG_PREVIEW_DEFAULT = "snap";
136
+ function previewPlan(frame, preview, slotted = !!frame.shown && !frame.shown.free) {
137
+ const shown = !!frame.shown;
138
+ if (preview === "indicator") return { showIndicator: shown, makeRoom: false, snapItem: false };
139
+ if (preview === "gap") return { showIndicator: shown, makeRoom: frame.room, snapItem: false };
140
+ return { showIndicator: shown && !slotted, makeRoom: frame.room, snapItem: frame.room && shown && slotted };
141
+ }
142
+ export {
143
+ DRAG_DEFAULTS,
144
+ DRAG_PREVIEW_DEFAULT,
145
+ createDragIntent,
146
+ inRect,
147
+ liftDecision,
148
+ previewPlan,
149
+ targetAt
150
+ };
151
+ //# sourceMappingURL=drag.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/drag/intent.ts","../src/drag/preview.ts"],"sourcesContent":["import type { Point, Rect } from \"../shapes\";\n\n/**\n * The drag intent model's tuning. Distances in px, speeds in px/ms, times in\n * ms; every value can be overridden per intent (see\n * {@link DragIntentOptions.tuning}).\n */\nexport interface DragTuning {\n /** A mouse press lifts once the pointer has moved this far. */\n readonly lift: number;\n /** A touch press lifts only after it has been held this long… */\n readonly touchHold: number;\n /** …and then moved this far (moving that far before the hold is over is not a drag). */\n readonly touchLift: number;\n /** The pointer's speed is measured over this window and smoothed. */\n readonly speedWindow: number;\n /** Aiming: the smoothed speed is below this… */\n readonly aimSpeed: number;\n /** …or the pointer has stayed within `still` px of one spot for `dwell` ms. */\n readonly still: number;\n readonly dwell: number;\n /** The current target holds while the pointer is within this band of its zone. */\n readonly band: number;\n /** A higher-priority target takes over at once when the pointer is this far inside it. */\n readonly breakthrough: number;\n /** A gap between zones shorter than this keeps the current target (for a pointer that aims). */\n readonly gapGrace: number;\n /** Aiming, the shown target moves to another one only once that one has held this long. */\n readonly switchHold: number;\n /** Room is made once the shown target, and the place in it, have held this long. */\n readonly roomAfter: number;\n /** The pointer has left the item's home once it is outside the home shrunk by this much on every side. */\n readonly homeInset: number;\n}\n\n/**\n * The defaults: a mouse lifts after 5 px, a touch after a 180 ms hold and\n * 8 px; aiming is slower than 0.45 px/ms over 50 ms, or within 12 px for\n * 90 ms; a 10 px band, a 4 px breakthrough and a 120 ms gap grace; a switch\n * waits 150 ms (the shortest an indicator may show); room after 160 ms.\n */\nexport const DRAG_DEFAULTS: DragTuning = {\n lift: 5,\n touchHold: 180,\n touchLift: 8,\n speedWindow: 50,\n aimSpeed: 0.45,\n still: 12,\n dwell: 90,\n band: 10,\n breakthrough: 4,\n gapGrace: 120,\n switchHold: 150,\n roomAfter: 160,\n homeInset: 6,\n};\n\n/**\n * A drop target: plain data. `hit` is the zone the pointer can be in;\n * `priority` how precise it is (1 beats 2 beats 3; equal priorities go to the\n * smaller zone). `slot` is where the dragged item would land, `label` what to\n * call it; the model reads neither. A `free` target has no slot (the item\n * floats where it is dropped): no room is made for it.\n */\nexport interface DragTarget {\n readonly id: string;\n readonly priority: 1 | 2 | 3;\n readonly hit: Rect;\n readonly slot?: Rect;\n readonly label?: string;\n readonly free?: boolean;\n}\n\n/** Whether `point` lies in `rect` grown by `margin` px on every side (shrunk, for a negative margin). */\nexport function inRect(rect: Rect, point: Point, margin = 0): boolean {\n return point.x >= rect.x - margin && point.x <= rect.x + rect.width + margin && point.y >= rect.y - margin && point.y <= rect.y + rect.height + margin;\n}\n\nconst area = (r: Rect): number => r.width * r.height;\n\n/** The most precise target under `point`: the lowest priority number, then the smallest zone; null when none is. */\nexport function targetAt<T extends DragTarget>(targets: readonly T[], point: Point): T | null {\n let best: T | null = null;\n for (const t of targets) {\n if (!inRect(t.hit, point)) continue;\n if (!best || t.priority < best.priority || (t.priority === best.priority && area(t.hit) < area(best.hit))) best = t;\n }\n return best;\n}\n\n/** What a press does so far: not yet a drag (`wait`), a drag (`lift`), or never one (`refuse`: a touch that moved too soon). */\nexport type LiftDecision = \"wait\" | \"lift\" | \"refuse\";\n\n/**\n * The lift rule for a press that has moved `distance` px in `elapsed` ms. A\n * mouse (or pen) lifts at `lift` px. A touch lifts only after `touchHold` ms,\n * then at `touchLift` px; moving `touchLift` px before the hold is over\n * refuses the drag (the finger is scrolling or swiping).\n */\nexport function liftDecision(press: { readonly touch: boolean; readonly distance: number; readonly elapsed: number }, tuning: Partial<DragTuning> = {}): LiftDecision {\n const t = { ...DRAG_DEFAULTS, ...tuning };\n if (!press.touch) return press.distance >= t.lift ? \"lift\" : \"wait\";\n if (press.elapsed < t.touchHold) return press.distance >= t.touchLift ? \"refuse\" : \"wait\";\n return press.distance >= t.touchLift ? \"lift\" : \"wait\";\n}\n\n/** One update's answer. */\nexport interface DragFrame<T extends DragTarget> {\n /** The target the drop indicator shows, or null. */\n readonly shown: T | null;\n /** The others make room for the shown target: it, and the place in it, have held `roomAfter` ms. */\n readonly room: boolean;\n /**\n * The place the room is made for, or null. Within one target it stays on\n * the old place until a new one has held `roomAfter` ms (see\n * {@link DragIntentOptions.placeOf}).\n */\n readonly roomPlace: string | null;\n /** The pointer is aiming: slow, or nearly still for a moment. */\n readonly aiming: boolean;\n}\n\n/** What a drag intent knows now. */\nexport interface DragIntentState<T extends DragTarget> {\n /** The pointer's smoothed speed, px/ms. */\n readonly speed: number;\n /** The target under the pointer as the hysteresis holds it, or null. */\n readonly current: T | null;\n /** The target the indicator shows, or null. */\n readonly shown: T | null;\n /** The pointer has left the item's home. */\n readonly leftHome: boolean;\n}\n\nexport interface DragIntentOptions<T extends DragTarget> {\n /** Where the item was lifted from: the indicator stays off until the pointer leaves it, and a release there does nothing. */\n readonly home: Rect;\n /** Overrides of {@link DRAG_DEFAULTS}. */\n readonly tuning?: Partial<DragTuning>;\n /**\n * The place within a target the pointer is at (a tab index, a stack\n * position), unique across targets; default: the target's id. Moving between places of the\n * shown target moves the indicator at once but the room only once the new\n * place has held `roomAfter` ms.\n */\n readonly placeOf?: (target: T, point: Point) => string;\n}\n\n/** A drag's intent: feed it the pointer on every frame; it says what the indicator shows and what a release lands on. */\nexport interface DragIntent<T extends DragTarget> {\n /**\n * A pointer sample at `time` (ms), with the targets as they stand (they may\n * be rebuilt between calls: the model follows them by id). Call it once a\n * frame while the item is held, also when the pointer doesn't move: a\n * dwell is time passing.\n */\n update(point: Point, time: number, targets: readonly T[]): DragFrame<T>;\n /**\n * What a release at `point` lands on: the shown target whenever one shows\n * (a drop lands where the indicator was), else the target under the\n * pointer, else null (the item goes home). A release over home is null: it\n * does nothing. Feed the release point to `update` first, so a last jump\n * of the pointer counts.\n */\n release(point: Point, targets: readonly T[]): T | null;\n /**\n * Hide the indicator (the view is scrolling under the pointer, say). The\n * aim starts afresh: the next sample never counts as aiming, and a dwell\n * starts again.\n */\n hide(time: number): void;\n /** The item's home moved with the view (it scrolled) by (dx, dy). */\n moveHome(dx: number, dy: number): void;\n readonly state: DragIntentState<T>;\n readonly tuning: DragTuning;\n}\n\n/**\n * The drag intent model: when the drop indicator shows, which target it\n * shows, when the others make room, and what a release lands on. Pure: fed\n * pointer samples and plain-data targets, no DOM and no clock of its own.\n *\n * - The indicator shows only when the pointer has left the item's home, a\n * target is under it, and it is aiming (slow, or nearly still for a\n * moment). Time spent crossing a big target at speed doesn't count.\n * - The most precise target wins (priority, then the smaller zone).\n * - Hysteresis: the current target holds while the pointer is within `band`\n * px of its zone, unless a more precise one is clearly (`breakthrough` px)\n * under the pointer; for a pointer that aims, a short gap between zones\n * (`gapGrace` ms) keeps it too.\n * - Leaving the shown target: flying, the indicator hides at once; aiming, it\n * moves to the new target once that has held `switchHold` ms (so no\n * indicator shows for less than that), at once for a breakthrough. A target\n * kept only by the gap grace can keep the indicator, never show it.\n * - Room is made once the shown target has held `roomAfter` ms, never for a\n * free target, and closes at once when it changes.\n */\nexport function createDragIntent<T extends DragTarget>(options: DragIntentOptions<T>): DragIntent<T> {\n const tuning: DragTuning = { ...DRAG_DEFAULTS, ...options.tuning };\n const placeOf = options.placeOf ?? ((t: T) => t.id);\n let home: Rect = { ...options.home };\n let samples: { x: number; y: number; t: number }[] = [];\n let cur: T | null = null;\n let curSince = 0;\n let lastInside = 0;\n let shown: T | null = null;\n let shownSince = 0;\n let place: string | null = null;\n let placeSince = 0;\n let roomPlace: string | null = null;\n let leftHome = false;\n let speed = 0;\n let anchor: Point | null = null;\n let stillSince = 0;\n let restart = false;\n const same = (a: T | null, b: T | null): boolean => (a === null || b === null ? a === b : a.id === b.id);\n // Targets may be rebuilt between calls: follow the ones held by id.\n const follow = (t: T | null, targets: readonly T[]): T | null => (t ? (targets.find((x) => x.id === t.id) ?? t) : null);\n\n return {\n tuning,\n get state(): DragIntentState<T> {\n return { speed, current: cur, shown, leftHome };\n },\n update(point, time, targets) {\n const p = point;\n const t = time;\n samples.push({ x: p.x, y: p.y, t });\n while (samples.length > 2 && t - samples[0]!.t > tuning.speedWindow) samples.shift();\n const a = samples[0]!;\n speed = speed * 0.5 + (Math.hypot(p.x - a.x, p.y - a.y) / Math.max(1, t - a.t)) * 0.5;\n if (!anchor || Math.hypot(p.x - anchor.x, p.y - anchor.y) > tuning.still) {\n anchor = { x: p.x, y: p.y };\n stillSince = t;\n }\n if (!inRect(home, p, -tuning.homeInset)) leftHome = true;\n const atHome = inRect(home, p);\n const raw = atHome ? null : targetAt(targets, p);\n cur = follow(cur, targets);\n shown = follow(shown, targets);\n let next = raw;\n if (cur && !atHome && inRect(cur.hit, p, tuning.band)) {\n const breaks = raw !== null && !same(raw, cur) && raw.priority < cur.priority && inRect(raw.hit, p, -tuning.breakthrough);\n next = breaks ? raw : cur;\n }\n if (!next && cur && !atHome && t - lastInside < tuning.gapGrace) next = cur;\n if (next && inRect(next.hit, p)) lastInside = t;\n if (!same(next, cur)) curSince = t;\n cur = next;\n const aiming = !restart && (speed < tuning.aimSpeed || t - stillSince >= tuning.dwell);\n restart = false;\n const show = (s: T | null): void => {\n if (!same(s, shown)) shownSince = t;\n shown = s;\n };\n // Under the pointer (within its band), not just kept by the gap grace.\n const under = !!cur && inRect(cur.hit, p, tuning.band);\n if (!shown) {\n if (cur && under && leftHome && aiming) show(cur);\n } else if (same(cur, shown)) {\n // It stays while its target holds; but a pointer flying out past the band hides it at once.\n if (!aiming && !under) show(null);\n else shown = cur;\n } else if (!cur || !aiming) {\n show(null);\n } else if ((cur.priority < shown.priority && inRect(cur.hit, p, -tuning.breakthrough)) || (under && t - curSince >= tuning.switchHold)) {\n show(cur);\n }\n // The place within the shown target, and the room: after the target and the place have both held.\n const at = shown ? placeOf(shown, p) : null;\n if (at !== place) {\n place = at;\n placeSince = t;\n }\n if (!shown || shown.free) roomPlace = null;\n else if (t - shownSince >= tuning.roomAfter && t - placeSince >= tuning.roomAfter) roomPlace = place;\n else if (shownSince === t) roomPlace = null;\n return { shown, room: roomPlace !== null, roomPlace, aiming };\n },\n release(point, targets) {\n if (inRect(home, point)) return null;\n // Drop = shown: a shown target is kept until it hides (flying out past its band hides it at once).\n const s = follow(shown, targets);\n if (s) return s;\n return targetAt(targets, point);\n },\n hide(time) {\n shown = null;\n cur = null;\n shownSince = time;\n place = null;\n roomPlace = null;\n samples = [];\n anchor = null;\n restart = true;\n },\n moveHome(dx, dy) {\n home = { ...home, x: home.x + dx, y: home.y + dy };\n },\n };\n}\n","import type { DragFrame, DragTarget } from \"./intent\";\n\n/**\n * How a drag previews its drop, from least to most motion while dragging:\n *\n * - `\"indicator\"`: nothing moves; only the indicator (an outline or an\n * insertion marker) shows the target.\n * - `\"gap\"`: once the room is made, the others move aside and open the slot;\n * the item stays under the pointer.\n * - `\"snap\"`: the gap opens and the item itself glides into it (for a target\n * with a slot; one without keeps the indicator).\n *\n * Every mode follows the same intent: what shows, when, and what a release\n * lands on are the drag intent's, so a drop always lands where the preview\n * showed it.\n */\nexport type DragPreview = \"indicator\" | \"gap\" | \"snap\";\n\n/** The preview a drag uses unless the host picks another. */\nexport const DRAG_PREVIEW_DEFAULT: DragPreview = \"snap\";\n\n/** What to draw for one frame of a drag. */\nexport interface PreviewPlan {\n /** Draw the indicator on the shown target. */\n readonly showIndicator: boolean;\n /** Lay the others out around the room (the gap is open). */\n readonly makeRoom: boolean;\n /** Draw the item in its slot instead of under the pointer. */\n readonly snapItem: boolean;\n}\n\n/**\n * The plan for a frame of the drag intent under `preview`. `slotted` says\n * whether the shown target's drop has a slot the item can sit in (default: a\n * target that isn't `free`); only such a target snaps, and in `\"snap\"` it\n * previews with the item rather than the indicator.\n *\n * - `\"indicator\"`: the indicator, never room, never a snap.\n * - `\"gap\"`: the indicator, and room when the intent makes it.\n * - `\"snap\"`: room when the intent makes it, the item snapped into a slotted\n * target's room; the indicator only for a target without a slot.\n */\nexport function previewPlan<T extends DragTarget>(\n frame: Pick<DragFrame<T>, \"shown\" | \"room\">,\n preview: DragPreview,\n slotted: boolean = !!frame.shown && !frame.shown.free,\n): PreviewPlan {\n const shown = !!frame.shown;\n if (preview === \"indicator\") return { showIndicator: shown, makeRoom: false, snapItem: false };\n if (preview === \"gap\") return { showIndicator: shown, makeRoom: frame.room, snapItem: false };\n // By design a slotted target draws nothing until the room opens (`roomAfter`); a release then still lands on it.\n return { showIndicator: shown && !slotted, makeRoom: frame.room, snapItem: frame.room && shown && slotted };\n}\n"],"mappings":";AAyCO,IAAM,gBAA4B;AAAA,EACvC,MAAM;AAAA,EACN,WAAW;AAAA,EACX,WAAW;AAAA,EACX,aAAa;AAAA,EACb,UAAU;AAAA,EACV,OAAO;AAAA,EACP,OAAO;AAAA,EACP,MAAM;AAAA,EACN,cAAc;AAAA,EACd,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,WAAW;AACb;AAmBO,SAAS,OAAO,MAAY,OAAc,SAAS,GAAY;AACpE,SAAO,MAAM,KAAK,KAAK,IAAI,UAAU,MAAM,KAAK,KAAK,IAAI,KAAK,QAAQ,UAAU,MAAM,KAAK,KAAK,IAAI,UAAU,MAAM,KAAK,KAAK,IAAI,KAAK,SAAS;AAClJ;AAEA,IAAM,OAAO,CAAC,MAAoB,EAAE,QAAQ,EAAE;AAGvC,SAAS,SAA+B,SAAuB,OAAwB;AAC5F,MAAI,OAAiB;AACrB,aAAW,KAAK,SAAS;AACvB,QAAI,CAAC,OAAO,EAAE,KAAK,KAAK,EAAG;AAC3B,QAAI,CAAC,QAAQ,EAAE,WAAW,KAAK,YAAa,EAAE,aAAa,KAAK,YAAY,KAAK,EAAE,GAAG,IAAI,KAAK,KAAK,GAAG,EAAI,QAAO;AAAA,EACpH;AACA,SAAO;AACT;AAWO,SAAS,aAAa,OAAyF,SAA8B,CAAC,GAAiB;AACpK,QAAM,IAAI,EAAE,GAAG,eAAe,GAAG,OAAO;AACxC,MAAI,CAAC,MAAM,MAAO,QAAO,MAAM,YAAY,EAAE,OAAO,SAAS;AAC7D,MAAI,MAAM,UAAU,EAAE,UAAW,QAAO,MAAM,YAAY,EAAE,YAAY,WAAW;AACnF,SAAO,MAAM,YAAY,EAAE,YAAY,SAAS;AAClD;AA6FO,SAAS,iBAAuC,SAA8C;AACnG,QAAM,SAAqB,EAAE,GAAG,eAAe,GAAG,QAAQ,OAAO;AACjE,QAAM,UAAU,QAAQ,YAAY,CAAC,MAAS,EAAE;AAChD,MAAI,OAAa,EAAE,GAAG,QAAQ,KAAK;AACnC,MAAI,UAAiD,CAAC;AACtD,MAAI,MAAgB;AACpB,MAAI,WAAW;AACf,MAAI,aAAa;AACjB,MAAI,QAAkB;AACtB,MAAI,aAAa;AACjB,MAAI,QAAuB;AAC3B,MAAI,aAAa;AACjB,MAAI,YAA2B;AAC/B,MAAI,WAAW;AACf,MAAI,QAAQ;AACZ,MAAI,SAAuB;AAC3B,MAAI,aAAa;AACjB,MAAI,UAAU;AACd,QAAM,OAAO,CAAC,GAAa,MAA0B,MAAM,QAAQ,MAAM,OAAO,MAAM,IAAI,EAAE,OAAO,EAAE;AAErG,QAAM,SAAS,CAAC,GAAa,YAAqC,IAAK,QAAQ,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,KAAK,IAAK;AAElH,SAAO;AAAA,IACL;AAAA,IACA,IAAI,QAA4B;AAC9B,aAAO,EAAE,OAAO,SAAS,KAAK,OAAO,SAAS;AAAA,IAChD;AAAA,IACA,OAAO,OAAO,MAAM,SAAS;AAC3B,YAAM,IAAI;AACV,YAAM,IAAI;AACV,cAAQ,KAAK,EAAE,GAAG,EAAE,GAAG,GAAG,EAAE,GAAG,EAAE,CAAC;AAClC,aAAO,QAAQ,SAAS,KAAK,IAAI,QAAQ,CAAC,EAAG,IAAI,OAAO,YAAa,SAAQ,MAAM;AACnF,YAAM,IAAI,QAAQ,CAAC;AACnB,cAAQ,QAAQ,MAAO,KAAK,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,IAAI,KAAK,IAAI,GAAG,IAAI,EAAE,CAAC,IAAK;AAClF,UAAI,CAAC,UAAU,KAAK,MAAM,EAAE,IAAI,OAAO,GAAG,EAAE,IAAI,OAAO,CAAC,IAAI,OAAO,OAAO;AACxE,iBAAS,EAAE,GAAG,EAAE,GAAG,GAAG,EAAE,EAAE;AAC1B,qBAAa;AAAA,MACf;AACA,UAAI,CAAC,OAAO,MAAM,GAAG,CAAC,OAAO,SAAS,EAAG,YAAW;AACpD,YAAM,SAAS,OAAO,MAAM,CAAC;AAC7B,YAAM,MAAM,SAAS,OAAO,SAAS,SAAS,CAAC;AAC/C,YAAM,OAAO,KAAK,OAAO;AACzB,cAAQ,OAAO,OAAO,OAAO;AAC7B,UAAI,OAAO;AACX,UAAI,OAAO,CAAC,UAAU,OAAO,IAAI,KAAK,GAAG,OAAO,IAAI,GAAG;AACrD,cAAM,SAAS,QAAQ,QAAQ,CAAC,KAAK,KAAK,GAAG,KAAK,IAAI,WAAW,IAAI,YAAY,OAAO,IAAI,KAAK,GAAG,CAAC,OAAO,YAAY;AACxH,eAAO,SAAS,MAAM;AAAA,MACxB;AACA,UAAI,CAAC,QAAQ,OAAO,CAAC,UAAU,IAAI,aAAa,OAAO,SAAU,QAAO;AACxE,UAAI,QAAQ,OAAO,KAAK,KAAK,CAAC,EAAG,cAAa;AAC9C,UAAI,CAAC,KAAK,MAAM,GAAG,EAAG,YAAW;AACjC,YAAM;AACN,YAAM,SAAS,CAAC,YAAY,QAAQ,OAAO,YAAY,IAAI,cAAc,OAAO;AAChF,gBAAU;AACV,YAAM,OAAO,CAAC,MAAsB;AAClC,YAAI,CAAC,KAAK,GAAG,KAAK,EAAG,cAAa;AAClC,gBAAQ;AAAA,MACV;AAEA,YAAM,QAAQ,CAAC,CAAC,OAAO,OAAO,IAAI,KAAK,GAAG,OAAO,IAAI;AACrD,UAAI,CAAC,OAAO;AACV,YAAI,OAAO,SAAS,YAAY,OAAQ,MAAK,GAAG;AAAA,MAClD,WAAW,KAAK,KAAK,KAAK,GAAG;AAE3B,YAAI,CAAC,UAAU,CAAC,MAAO,MAAK,IAAI;AAAA,YAC3B,SAAQ;AAAA,MACf,WAAW,CAAC,OAAO,CAAC,QAAQ;AAC1B,aAAK,IAAI;AAAA,MACX,WAAY,IAAI,WAAW,MAAM,YAAY,OAAO,IAAI,KAAK,GAAG,CAAC,OAAO,YAAY,KAAO,SAAS,IAAI,YAAY,OAAO,YAAa;AACtI,aAAK,GAAG;AAAA,MACV;AAEA,YAAM,KAAK,QAAQ,QAAQ,OAAO,CAAC,IAAI;AACvC,UAAI,OAAO,OAAO;AAChB,gBAAQ;AACR,qBAAa;AAAA,MACf;AACA,UAAI,CAAC,SAAS,MAAM,KAAM,aAAY;AAAA,eAC7B,IAAI,cAAc,OAAO,aAAa,IAAI,cAAc,OAAO,UAAW,aAAY;AAAA,eACtF,eAAe,EAAG,aAAY;AACvC,aAAO,EAAE,OAAO,MAAM,cAAc,MAAM,WAAW,OAAO;AAAA,IAC9D;AAAA,IACA,QAAQ,OAAO,SAAS;AACtB,UAAI,OAAO,MAAM,KAAK,EAAG,QAAO;AAEhC,YAAM,IAAI,OAAO,OAAO,OAAO;AAC/B,UAAI,EAAG,QAAO;AACd,aAAO,SAAS,SAAS,KAAK;AAAA,IAChC;AAAA,IACA,KAAK,MAAM;AACT,cAAQ;AACR,YAAM;AACN,mBAAa;AACb,cAAQ;AACR,kBAAY;AACZ,gBAAU,CAAC;AACX,eAAS;AACT,gBAAU;AAAA,IACZ;AAAA,IACA,SAAS,IAAI,IAAI;AACf,aAAO,EAAE,GAAG,MAAM,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,IAAI,GAAG;AAAA,IACnD;AAAA,EACF;AACF;;;ACzRO,IAAM,uBAAoC;AAuB1C,SAAS,YACd,OACA,SACA,UAAmB,CAAC,CAAC,MAAM,SAAS,CAAC,MAAM,MAAM,MACpC;AACb,QAAM,QAAQ,CAAC,CAAC,MAAM;AACtB,MAAI,YAAY,YAAa,QAAO,EAAE,eAAe,OAAO,UAAU,OAAO,UAAU,MAAM;AAC7F,MAAI,YAAY,MAAO,QAAO,EAAE,eAAe,OAAO,UAAU,MAAM,MAAM,UAAU,MAAM;AAE5F,SAAO,EAAE,eAAe,SAAS,CAAC,SAAS,UAAU,MAAM,MAAM,UAAU,MAAM,QAAQ,SAAS,QAAQ;AAC5G;","names":[]}
@@ -6,7 +6,7 @@ import { C as ClockPort, F as FrameSource, T as TimerSource } from './port-CmlUc
6
6
  * touches the DOM; the host copies the fields it needs out of PointerEvent /
7
7
  * WheelEvent / Safari's GestureEvent.
8
8
  *
9
- * Ported from the pdfized surface's input layer, whose recogniser this is.
9
+ * Ported from a host app's input layer, whose recogniser this is.
10
10
  * Hit testing and routing (what is under a point, who gets a gesture) stay
11
11
  * with the host.
12
12
  */