@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.
- package/CHANGELOG.md +72 -3
- package/README.md +137 -4
- package/dist/{chunk-NFQ4LIIR.js → chunk-C4M62FUT.js} +1 -1
- package/dist/chunk-C4M62FUT.js.map +1 -0
- package/dist/{chunk-LCTGXGOI.js → chunk-CM6WVNDH.js} +13 -61
- package/dist/chunk-CM6WVNDH.js.map +1 -0
- package/dist/chunk-WQQ2E25M.js +212 -0
- package/dist/chunk-WQQ2E25M.js.map +1 -0
- package/dist/drag.d.ts +202 -0
- package/dist/drag.js +151 -0
- package/dist/drag.js.map +1 -0
- package/dist/gestures.d.ts +1 -1
- package/dist/gestures.js +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +17 -3
- package/dist/layout.d.ts +258 -0
- package/dist/layout.js +312 -0
- package/dist/layout.js.map +1 -0
- package/dist/reveal-my7U6UyE.d.ts +51 -0
- package/dist/testing.d.ts +87 -0
- package/dist/testing.js +93 -0
- package/dist/testing.js.map +1 -0
- package/dist/values.d.ts +80 -4
- package/dist/values.js +15 -1
- package/package.json +13 -1
- package/dist/chunk-LCTGXGOI.js.map +0 -1
- package/dist/chunk-NFQ4LIIR.js.map +0 -1
package/dist/gestures.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export { C as ClockPort, F as FrameSource, T as TimerSource } from './port-CmlUc
|
|
|
2
2
|
export { FakeClock, FakeClockOptions, createFakeClock } from './clock.js';
|
|
3
3
|
export { F as FrameInfo, a as FrameLoop, b as FrameLoopOptions, c as FrameStats, d as FrameTask, P as PHASES, e as Phase, R as RepeatingTask, S as ScheduleOptions, f as createFrameLoop } from './frame-loop-C88dc9xw.js';
|
|
4
4
|
export { EffectCleanup, EffectOptions, Equals, ReadonlySignal, Signal, SignalOptions, batch, computed, effect, isSignal, signal, untracked } from './signals.js';
|
|
5
|
-
export { Animator, AnimatorValue, Easing, MotionOptions, MotionValue, SPRING_DEFAULTS, SetOptions, Spring, SpringConfig, SpringParams, SpringState, Tween, TweenConfig, createSpring, createTween, cubicBezier, ease, easeIn, easeInOut, easeInOutCubic, easeOut, easeOutCubic, linear, motionValue, resolveSpring, springStep, springValue, tweenValue } from './values.js';
|
|
5
|
+
export { Animator, AnimatorValue, Easing, MotionOptions, MotionValue, SETTLE_DEFAULTS, SETTLE_TEMPO, SPRING_DEFAULTS, SetOptions, Settle, SettleConfig, Spring, SpringConfig, SpringParams, SpringState, Tween, TweenConfig, createSettle, createSpring, createTween, cubicBezier, ease, easeIn, easeInOut, easeInOutCubic, easeOut, easeOutCubic, linear, motionValue, resolveSpring, settleValue, springStep, springValue, tweenValue } from './values.js';
|
|
6
|
+
export { P as PopIn, R as RevealFrame, p as popIn, r as reveal } from './reveal-my7U6UyE.js';
|
|
6
7
|
export { CancelReason, CoalescerOptions, DWELL_DEFAULTS, DWELL_IDLE, Deadline, DwellConfig, DwellInput, DwellMachineOptions, DwellState, FrameCoalescer, InputGesture, InputGestureType, Machine, MachineOptions, ModifierDrag, ModifierKey, Modifiers, NO_MODIFIERS, Platform, PointerKind, RECOGNIZER_DEFAULTS, RawInputEvent, RawPointerEvent, RawPointerType, RawSafariGestureEvent, RawWheelEvent, Recognizer, RecognizerOptions, RecognizerTimers, SurfacePointer, WHEEL_DEFAULTS, WHEEL_PAN_DEFAULTS, WheelIntent, WheelOptions, WheelPanOptions, classifyWheel, createDwellMachine, createFrameCoalescer, createMachine, createRecognizer, dampWheelPan, defaultDragModifier, dwellDeadline, dwellReducer, isHeld, modHeld, platformButton, resolveDragModifier, toModifiers, wheelDeltaPx } from './gestures.js';
|
|
7
8
|
export { A as Animatable, P as Point, R as Rect, V as Vector, W as Widen, s as shapeEquals } from './shapes-EbQlwbCu.js';
|
package/dist/index.js
CHANGED
|
@@ -18,11 +18,11 @@ import {
|
|
|
18
18
|
linear,
|
|
19
19
|
motionValue,
|
|
20
20
|
resolveSpring,
|
|
21
|
-
|
|
21
|
+
settleValue,
|
|
22
22
|
springStep,
|
|
23
23
|
springValue,
|
|
24
24
|
tweenValue
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-CM6WVNDH.js";
|
|
26
26
|
import {
|
|
27
27
|
DWELL_DEFAULTS,
|
|
28
28
|
DWELL_IDLE,
|
|
@@ -45,7 +45,7 @@ import {
|
|
|
45
45
|
resolveDragModifier,
|
|
46
46
|
toModifiers,
|
|
47
47
|
wheelDeltaPx
|
|
48
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-C4M62FUT.js";
|
|
49
49
|
import {
|
|
50
50
|
batch,
|
|
51
51
|
computed,
|
|
@@ -54,12 +54,22 @@ import {
|
|
|
54
54
|
signal,
|
|
55
55
|
untracked
|
|
56
56
|
} from "./chunk-OOKRBVMB.js";
|
|
57
|
+
import {
|
|
58
|
+
SETTLE_DEFAULTS,
|
|
59
|
+
SETTLE_TEMPO,
|
|
60
|
+
createSettle,
|
|
61
|
+
popIn,
|
|
62
|
+
reveal,
|
|
63
|
+
shapeEquals
|
|
64
|
+
} from "./chunk-WQQ2E25M.js";
|
|
57
65
|
export {
|
|
58
66
|
DWELL_DEFAULTS,
|
|
59
67
|
DWELL_IDLE,
|
|
60
68
|
NO_MODIFIERS,
|
|
61
69
|
PHASES,
|
|
62
70
|
RECOGNIZER_DEFAULTS,
|
|
71
|
+
SETTLE_DEFAULTS,
|
|
72
|
+
SETTLE_TEMPO,
|
|
63
73
|
SPRING_DEFAULTS,
|
|
64
74
|
WHEEL_DEFAULTS,
|
|
65
75
|
WHEEL_PAN_DEFAULTS,
|
|
@@ -72,6 +82,7 @@ export {
|
|
|
72
82
|
createFrameLoop,
|
|
73
83
|
createMachine,
|
|
74
84
|
createRecognizer,
|
|
85
|
+
createSettle,
|
|
75
86
|
createSpring,
|
|
76
87
|
createTween,
|
|
77
88
|
cubicBezier,
|
|
@@ -92,8 +103,11 @@ export {
|
|
|
92
103
|
modHeld,
|
|
93
104
|
motionValue,
|
|
94
105
|
platformButton,
|
|
106
|
+
popIn,
|
|
95
107
|
resolveDragModifier,
|
|
96
108
|
resolveSpring,
|
|
109
|
+
reveal,
|
|
110
|
+
settleValue,
|
|
97
111
|
shapeEquals,
|
|
98
112
|
signal,
|
|
99
113
|
springStep,
|
package/dist/layout.d.ts
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import { F as FrameInfo, a as FrameLoop, e as Phase } from './frame-loop-C88dc9xw.js';
|
|
2
|
+
import { R as Rect, P as Point } from './shapes-EbQlwbCu.js';
|
|
3
|
+
import { R as RevealFrame, P as PopIn } from './reveal-my7U6UyE.js';
|
|
4
|
+
import './port-CmlUctqx.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* What a {@link Drawable} draws on a frame: where the item is drawn, the box its content is laid out in, how to draw
|
|
8
|
+
* the one at the other without stretching ({@link reveal}), and its appearance ({@link popIn}).
|
|
9
|
+
*/
|
|
10
|
+
interface DrawFrame {
|
|
11
|
+
/** Where the item is drawn this frame (its frame: background, border, shadow). */
|
|
12
|
+
readonly drawn: Rect;
|
|
13
|
+
/** The box its content is laid out in: where it is heading, or (held) where it was laid out last. */
|
|
14
|
+
readonly box: Rect;
|
|
15
|
+
/** How to draw `box`'s content at `drawn`: a move, the frame at the drawn size, the content clipped. */
|
|
16
|
+
readonly reveal: RevealFrame;
|
|
17
|
+
/** Its uniform appearance: scale about its centre and opacity. */
|
|
18
|
+
readonly pop: PopIn;
|
|
19
|
+
/** How shown it is, 0 (gone) to 1. */
|
|
20
|
+
readonly shown: number;
|
|
21
|
+
/** All the way gone: hidden and at rest (out of sight and out of the way of the pointer). */
|
|
22
|
+
readonly gone: boolean;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* How an item reaches the screen. The layout motion never measures or touches anything itself: it lays an item's
|
|
26
|
+
* content out once per new box and draws it once per frame through this. {@link domDrawable} writes an element's
|
|
27
|
+
* style; a canvas or a test can draw anything else.
|
|
28
|
+
*/
|
|
29
|
+
interface Drawable {
|
|
30
|
+
/** Lay the content out in `box` (its position and size): called once per new box, never per frame. */
|
|
31
|
+
layOut(box: Rect): void;
|
|
32
|
+
/** Draw the item for this frame. */
|
|
33
|
+
draw(frame: DrawFrame): void;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* One item the layout motion moves: give it a new place and it settles there from wherever it is drawn.
|
|
37
|
+
*
|
|
38
|
+
* - {@link LayoutItem.moveTo}: a settle to the new place, retargeted from the drawn rect (no jump, no overshoot);
|
|
39
|
+
* the content is laid out at the new box at once and the item redrawn at once, so no frame shows the old offset
|
|
40
|
+
* from the new box.
|
|
41
|
+
* - A size change is revealed: the content is laid out at the target size and the frame clipped, never scaled.
|
|
42
|
+
* - {@link LayoutItem.show}: appear or disappear with a uniform-scale fade.
|
|
43
|
+
*/
|
|
44
|
+
interface LayoutItem {
|
|
45
|
+
readonly id: string;
|
|
46
|
+
/** Where it is drawn now. */
|
|
47
|
+
readonly drawn: Rect;
|
|
48
|
+
/** Where it is heading (its settle's target). */
|
|
49
|
+
readonly target: Rect;
|
|
50
|
+
/** The box its content is laid out in. */
|
|
51
|
+
readonly box: Rect;
|
|
52
|
+
/** At rest on its target. */
|
|
53
|
+
readonly settled: boolean;
|
|
54
|
+
/** How shown it is, 0 to 1, and whether it is heading for shown. */
|
|
55
|
+
readonly shown: number;
|
|
56
|
+
readonly showing: boolean;
|
|
57
|
+
/** Its appearance is at rest (fully shown, or gone). */
|
|
58
|
+
readonly shownSettled: boolean;
|
|
59
|
+
/** Settle to `to` from where it is drawn; lays the content out at `to` and redraws at once. */
|
|
60
|
+
moveTo(to: Rect): void;
|
|
61
|
+
/** Be at `to` now, at rest: no glide. */
|
|
62
|
+
jumpTo(to: Rect): void;
|
|
63
|
+
/** Drawn at `from` now, then settle to `to` (a FLIP's invert and play: `from` in the item's own coordinates). */
|
|
64
|
+
glideFrom(from: Rect, to: Rect): void;
|
|
65
|
+
/** Exactly at `r`, at rest (what a pointer holds); the content stays laid out in its box, moved with it. */
|
|
66
|
+
hold(r: Rect): void;
|
|
67
|
+
/** Appear (true) or disappear (false) with a uniform-scale fade; `now`: without the fade. */
|
|
68
|
+
show(on: boolean, options?: {
|
|
69
|
+
readonly now?: boolean;
|
|
70
|
+
}): void;
|
|
71
|
+
/** Draw it again now (after its drawable's element was laid out anew from outside). */
|
|
72
|
+
redraw(): void;
|
|
73
|
+
}
|
|
74
|
+
/** A task the layout motion runs on every frame before it steps the items; true keeps the frames coming. */
|
|
75
|
+
type LayoutTask = (frame: FrameInfo) => boolean | void;
|
|
76
|
+
interface LayoutMotionOptions {
|
|
77
|
+
/** The frame loop every item steps on (one loop for all of them). */
|
|
78
|
+
readonly loop: FrameLoop;
|
|
79
|
+
/** The settle tempo, per second ({@link SETTLE_TEMPO}); default `SETTLE_TEMPO.default`. */
|
|
80
|
+
readonly omega?: number;
|
|
81
|
+
/** The frame phase the items step and draw in; default `"layout"`. */
|
|
82
|
+
readonly phase?: Phase;
|
|
83
|
+
/** Where an appearing item grows from, as a share of its size ({@link popIn}'s `from`); default 0.87. */
|
|
84
|
+
readonly popFrom?: number;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Layout motion: items with an id and a target rect, each settled to its place on one shared frame loop.
|
|
88
|
+
*
|
|
89
|
+
* Give an item a new place ({@link LayoutItem.moveTo}) and it glides there from wherever it is drawn: the window
|
|
90
|
+
* motion rules of `values/` (settle, reveal, popIn) applied to every item at once. The loop runs only while
|
|
91
|
+
* something moves, or a task asks for frames, and stops by itself.
|
|
92
|
+
*
|
|
93
|
+
* It only animates the rects it is given: which items move where, and when, is the caller's layout.
|
|
94
|
+
*/
|
|
95
|
+
interface LayoutMotion {
|
|
96
|
+
/** Add an item drawn by `drawable`, at `at`, shown (or gone: `{ shown: false }`). Its content is laid out at once. */
|
|
97
|
+
add(id: string, drawable: Drawable, at: Rect, options?: {
|
|
98
|
+
readonly shown?: boolean;
|
|
99
|
+
}): LayoutItem;
|
|
100
|
+
get(id: string): LayoutItem | undefined;
|
|
101
|
+
/** Stop moving an item (its drawable is not touched again). */
|
|
102
|
+
remove(id: string): void;
|
|
103
|
+
readonly items: ReadonlyMap<string, LayoutItem>;
|
|
104
|
+
/** Run `task` on every frame, before the items step; returns how to stop it. */
|
|
105
|
+
task(task: LayoutTask): () => void;
|
|
106
|
+
/** Call `listener` on every frame, after the items have stepped and been drawn; returns how to stop it. */
|
|
107
|
+
onFrame(listener: (frame: FrameInfo) => void): () => void;
|
|
108
|
+
/** Call `listener` each time the frames stop (nothing moves); returns how to stop it. */
|
|
109
|
+
onIdle(listener: () => void): () => void;
|
|
110
|
+
/** Make sure at least one more frame runs (a task has something to do). */
|
|
111
|
+
wake(): void;
|
|
112
|
+
/** Frames are running. */
|
|
113
|
+
readonly running: boolean;
|
|
114
|
+
/** Set the tempo for every item (and the ones added later). */
|
|
115
|
+
configure(config: {
|
|
116
|
+
readonly omega?: number;
|
|
117
|
+
}): void;
|
|
118
|
+
/** Resolves once nothing moves (at once if nothing does). */
|
|
119
|
+
idle(): Promise<void>;
|
|
120
|
+
/** Stop the frames and forget every item. */
|
|
121
|
+
dispose(): void;
|
|
122
|
+
}
|
|
123
|
+
/** Create the {@link LayoutMotion} for one surface. */
|
|
124
|
+
declare function createLayoutMotion(options: LayoutMotionOptions): LayoutMotion;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* An item held by a pointer: the real item (no copy) follows the pointer 1:1 from where it was grabbed, at the size
|
|
128
|
+
* it was lifted at; its place stays reserved (that is the caller's layout). Then:
|
|
129
|
+
*
|
|
130
|
+
* - {@link HeldDrag.snapTo}: glide into a slot (a snap preview); `null` leaves the slot and glides back to the
|
|
131
|
+
* pointer, following it as it moves ({@link HeldDrag.follow} once a frame), until it is under it again.
|
|
132
|
+
* - {@link HeldDrag.land}: let go into a slot: the landing settles from where it was let go (the release rect), never
|
|
133
|
+
* from its old home. Snapped into that slot already, it simply finishes that glide.
|
|
134
|
+
* - {@link HeldDrag.cancel}: let go with no drop: it glides home from where it was let go.
|
|
135
|
+
* - {@link HeldDrag.preview}: the three drop previews, per frame: indicator and gap keep the item under the pointer
|
|
136
|
+
* (gap with the caller's room open), snap glides it into the slot; leaving glides it back to the pointer.
|
|
137
|
+
* - {@link HeldDrag.resize}: the item takes a new size mid-drag (its content laid out anew by the caller): it settles
|
|
138
|
+
* to it from where it is drawn, the size revealed (never stretched), and is held 1:1 again once there.
|
|
139
|
+
*/
|
|
140
|
+
interface HeldDrag {
|
|
141
|
+
/** Where the pointer is (null before the first move). */
|
|
142
|
+
readonly pointer: Point | null;
|
|
143
|
+
/** Where the pointer holds the item: the pointer less the grab offset, at the held size (lifted, or resized). */
|
|
144
|
+
readonly held: Rect | null;
|
|
145
|
+
/** The slot it sits in (a snap), or null. */
|
|
146
|
+
readonly snapped: Rect | null;
|
|
147
|
+
/** It left a slot (or took a new size) and is gliding back to the pointer. */
|
|
148
|
+
readonly returning: boolean;
|
|
149
|
+
/** The pointer moved: held 1:1 (unless it sits in a slot or is on its way back). */
|
|
150
|
+
move(pointer: Point): void;
|
|
151
|
+
/**
|
|
152
|
+
* Into `slot` (a glide from where it is drawn; the same slot again does nothing), or out of the slot it sits in
|
|
153
|
+
* (`null`: back to the pointer). Returns what changed: `"snapped"`, `"left"`, or null.
|
|
154
|
+
*/
|
|
155
|
+
snapTo(slot: Rect | null): "snapped" | "left" | null;
|
|
156
|
+
/** Once a frame: on its way back, retarget to the pointer; true on the frame it is under the pointer again. */
|
|
157
|
+
follow(): boolean;
|
|
158
|
+
/**
|
|
159
|
+
* Once a frame, a drop preview's part for the item (`plan`: `@n-uf/motion/drag`'s `previewPlan`, or any
|
|
160
|
+
* `{ snapItem }`): with `snapItem` it glides into `slot` (snap); else it stays under the pointer, or glides back to
|
|
161
|
+
* it from a slot it left (indicator, gap). The room, if the plan makes any, is the caller's layout. In place of
|
|
162
|
+
* {@link HeldDrag.snapTo} and {@link HeldDrag.follow}; returns what changed.
|
|
163
|
+
*/
|
|
164
|
+
preview(plan: {
|
|
165
|
+
readonly snapItem: boolean;
|
|
166
|
+
}, slot: Rect | null): "snapped" | "left" | null;
|
|
167
|
+
/**
|
|
168
|
+
* Hold it at `size` from now on (the caller has laid its content out for that size). The grab point keeps its share
|
|
169
|
+
* of the item, so the pointer stays on the same spot of it; the item settles from where it is drawn to its new held
|
|
170
|
+
* rect (its size revealed, never stretched), then is held 1:1 again ({@link HeldDrag.follow} once a frame). In a
|
|
171
|
+
* slot it stays there, taking the new size when it leaves. The same size again does nothing; returns true on a
|
|
172
|
+
* change.
|
|
173
|
+
*/
|
|
174
|
+
resize(size: {
|
|
175
|
+
readonly width: number;
|
|
176
|
+
readonly height: number;
|
|
177
|
+
}): boolean;
|
|
178
|
+
/** Let go into `slot`: returns the release rect, where the landing starts. */
|
|
179
|
+
land(slot: Rect): Rect;
|
|
180
|
+
/** Let go with no drop: glides `home` from the release rect, which it returns. */
|
|
181
|
+
cancel(home: Rect): Rect;
|
|
182
|
+
}
|
|
183
|
+
/** Hold `item` from the pointer at `grab` (the pointer's offset from its top-left corner), at its drawn size now. */
|
|
184
|
+
declare function holdItem(item: LayoutItem, grab: Point): HeldDrag;
|
|
185
|
+
|
|
186
|
+
/** A rect as a DOM rect reports it (`getBoundingClientRect()`): left, top, width, height. */
|
|
187
|
+
interface RectLike {
|
|
188
|
+
readonly left: number;
|
|
189
|
+
readonly top: number;
|
|
190
|
+
readonly width: number;
|
|
191
|
+
readonly height: number;
|
|
192
|
+
}
|
|
193
|
+
/** Anything that can say where it is drawn: a DOM node, or a test's stand-in. */
|
|
194
|
+
interface Measurable {
|
|
195
|
+
getBoundingClientRect(): RectLike;
|
|
196
|
+
}
|
|
197
|
+
/** A {@link RectLike} as a {@link Rect}. */
|
|
198
|
+
declare const rectOf: (r: RectLike) => Rect;
|
|
199
|
+
/** One member of a {@link flip}: an item, the node it draws, and where it goes once the DOM has changed. */
|
|
200
|
+
interface FlipEntry {
|
|
201
|
+
readonly item: LayoutItem;
|
|
202
|
+
readonly node: Measurable;
|
|
203
|
+
/**
|
|
204
|
+
* Once the DOM has changed: the old rect (as measured before, in page coordinates) in the item's own coordinates
|
|
205
|
+
* now, and its new place there.
|
|
206
|
+
*/
|
|
207
|
+
readonly place: (before: Rect) => {
|
|
208
|
+
readonly from: Rect;
|
|
209
|
+
readonly to: Rect;
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* FLIP for DOM groups, on settle: record where each node is drawn (first), change the DOM (`change`: move nodes to
|
|
214
|
+
* other parents, reorder them), then each item is drawn at its old rect and settles to its new place (last, invert,
|
|
215
|
+
* play). No transform tween: a settle from the rect it was drawn at, so it never jumps or overshoots, and its size
|
|
216
|
+
* changes through reveal.
|
|
217
|
+
*/
|
|
218
|
+
declare function flip(entries: readonly FlipEntry[], change: () => void): void;
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* The style properties the layout motion writes, as strings (a DOM node's `style` has them all). Structural: the
|
|
222
|
+
* library needs no DOM types.
|
|
223
|
+
*/
|
|
224
|
+
interface StyleLike {
|
|
225
|
+
left: string;
|
|
226
|
+
top: string;
|
|
227
|
+
width: string;
|
|
228
|
+
height: string;
|
|
229
|
+
transform: string;
|
|
230
|
+
opacity: string;
|
|
231
|
+
clipPath: string;
|
|
232
|
+
visibility: string;
|
|
233
|
+
}
|
|
234
|
+
/** Something with a writable {@link StyleLike} `style`: a DOM node. */
|
|
235
|
+
interface Styled {
|
|
236
|
+
readonly style: StyleLike;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* The nodes of a drawn box: `node` is laid out at the box and moved to the drawn rect; `frame` (background, border,
|
|
240
|
+
* shadow) is drawn at the drawn size; `content` is clipped to it. With no `frame` and `content`, `node` itself is
|
|
241
|
+
* clipped (its content laid out at the box, never stretched).
|
|
242
|
+
*/
|
|
243
|
+
interface BoxNodes {
|
|
244
|
+
readonly node: Styled;
|
|
245
|
+
readonly frame?: Styled;
|
|
246
|
+
readonly content?: Styled;
|
|
247
|
+
}
|
|
248
|
+
/** Lay `node` out at `box`: its position and size, in its offset parent's coordinates. */
|
|
249
|
+
declare function layOutBox(node: Styled, box: Rect): void;
|
|
250
|
+
/**
|
|
251
|
+
* Draw one frame of a box laid out by {@link layOutBox}: one transform (the move to the drawn rect, and the uniform
|
|
252
|
+
* appearance about its centre), the frame at the drawn size, the content clipped (reveal); hidden when gone.
|
|
253
|
+
*/
|
|
254
|
+
declare function drawBox(nodes: BoxNodes, frame: DrawFrame): void;
|
|
255
|
+
/** A {@link Drawable} for DOM nodes: {@link layOutBox} on a new box, {@link drawBox} on every frame. */
|
|
256
|
+
declare function domDrawable(nodes: BoxNodes): Drawable;
|
|
257
|
+
|
|
258
|
+
export { type BoxNodes, type DrawFrame, type Drawable, type FlipEntry, type HeldDrag, type LayoutItem, type LayoutMotion, type LayoutMotionOptions, type LayoutTask, type Measurable, type RectLike, type StyleLike, type Styled, createLayoutMotion, domDrawable, drawBox, flip, holdItem, layOutBox, rectOf };
|
package/dist/layout.js
ADDED
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SETTLE_TEMPO,
|
|
3
|
+
createSettle,
|
|
4
|
+
popIn,
|
|
5
|
+
reveal
|
|
6
|
+
} from "./chunk-WQQ2E25M.js";
|
|
7
|
+
|
|
8
|
+
// src/layout/motion.ts
|
|
9
|
+
var same = (a, b) => Math.abs(a.x - b.x) < 0.01 && Math.abs(a.y - b.y) < 0.01 && Math.abs(a.width - b.width) < 0.01 && Math.abs(a.height - b.height) < 0.01;
|
|
10
|
+
var copy = (r) => ({ x: r.x, y: r.y, width: r.width, height: r.height });
|
|
11
|
+
function createLayoutMotion(options) {
|
|
12
|
+
const { loop } = options;
|
|
13
|
+
const phase = options.phase ?? "layout";
|
|
14
|
+
const popFrom = options.popFrom ?? 0.87;
|
|
15
|
+
let omega = options.omega ?? SETTLE_TEMPO.default;
|
|
16
|
+
const items = /* @__PURE__ */ new Map();
|
|
17
|
+
const steps = /* @__PURE__ */ new Map();
|
|
18
|
+
const tasks = /* @__PURE__ */ new Set();
|
|
19
|
+
const frames = /* @__PURE__ */ new Set();
|
|
20
|
+
const idles = /* @__PURE__ */ new Set();
|
|
21
|
+
const tempos = /* @__PURE__ */ new Set();
|
|
22
|
+
const removers = /* @__PURE__ */ new Map();
|
|
23
|
+
let waiters = [];
|
|
24
|
+
let stop = null;
|
|
25
|
+
let woken = false;
|
|
26
|
+
const wake = () => {
|
|
27
|
+
woken = true;
|
|
28
|
+
if (stop) return;
|
|
29
|
+
stop = loop.repeat(phase, tick);
|
|
30
|
+
};
|
|
31
|
+
function tick(frame) {
|
|
32
|
+
let moving = woken;
|
|
33
|
+
woken = false;
|
|
34
|
+
for (const task of [...tasks]) if (task(frame)) moving = true;
|
|
35
|
+
for (const step of [...steps.values()]) if (step(frame.delta)) moving = true;
|
|
36
|
+
for (const listener of [...frames]) listener(frame);
|
|
37
|
+
if (moving || woken) return true;
|
|
38
|
+
stop = null;
|
|
39
|
+
const resolve = waiters;
|
|
40
|
+
waiters = [];
|
|
41
|
+
for (const r of resolve) r();
|
|
42
|
+
for (const listener of [...idles]) listener();
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
function add(id, drawable, at, opts = {}) {
|
|
46
|
+
if (items.has(id)) throw new Error(`layout motion: item "${id}" is already added (remove it first)`);
|
|
47
|
+
const rect = createSettle(copy(at), { omega });
|
|
48
|
+
const pop = createSettle(opts.shown === false ? 0 : 1, { omega, restDelta: 1e-3 });
|
|
49
|
+
let box = copy(at);
|
|
50
|
+
const frameOf = () => {
|
|
51
|
+
const drawn = rect.value;
|
|
52
|
+
return { drawn, box, reveal: reveal(drawn, box), pop: popIn(pop.value, popFrom), shown: pop.value, gone: pop.settled && pop.value === 0 };
|
|
53
|
+
};
|
|
54
|
+
const draw = () => drawable.draw(frameOf());
|
|
55
|
+
const layOut = (to) => {
|
|
56
|
+
box = copy(to);
|
|
57
|
+
drawable.layOut(box);
|
|
58
|
+
};
|
|
59
|
+
const tempo = () => {
|
|
60
|
+
rect.configure({ omega });
|
|
61
|
+
pop.configure({ omega });
|
|
62
|
+
};
|
|
63
|
+
const item = {
|
|
64
|
+
id,
|
|
65
|
+
get drawn() {
|
|
66
|
+
return rect.value;
|
|
67
|
+
},
|
|
68
|
+
get target() {
|
|
69
|
+
return rect.target;
|
|
70
|
+
},
|
|
71
|
+
get box() {
|
|
72
|
+
return box;
|
|
73
|
+
},
|
|
74
|
+
get settled() {
|
|
75
|
+
return rect.settled;
|
|
76
|
+
},
|
|
77
|
+
get shown() {
|
|
78
|
+
return pop.value;
|
|
79
|
+
},
|
|
80
|
+
get showing() {
|
|
81
|
+
return pop.target === 1;
|
|
82
|
+
},
|
|
83
|
+
get shownSettled() {
|
|
84
|
+
return pop.settled;
|
|
85
|
+
},
|
|
86
|
+
moveTo(to) {
|
|
87
|
+
if (same(box, to) && same(rect.target, to)) return;
|
|
88
|
+
layOut(to);
|
|
89
|
+
draw();
|
|
90
|
+
if (same(rect.target, to)) return;
|
|
91
|
+
rect.setTarget(copy(to));
|
|
92
|
+
wake();
|
|
93
|
+
},
|
|
94
|
+
jumpTo(to) {
|
|
95
|
+
layOut(to);
|
|
96
|
+
rect.jumpTo(copy(to));
|
|
97
|
+
draw();
|
|
98
|
+
},
|
|
99
|
+
glideFrom(from, to) {
|
|
100
|
+
rect.jumpTo(copy(from));
|
|
101
|
+
layOut(to);
|
|
102
|
+
draw();
|
|
103
|
+
rect.setTarget(copy(to));
|
|
104
|
+
wake();
|
|
105
|
+
},
|
|
106
|
+
hold(r) {
|
|
107
|
+
rect.hold(copy(r));
|
|
108
|
+
draw();
|
|
109
|
+
},
|
|
110
|
+
show(on, o = {}) {
|
|
111
|
+
const want = on ? 1 : 0;
|
|
112
|
+
if (o.now) pop.jumpTo(want);
|
|
113
|
+
else if (pop.target !== want) {
|
|
114
|
+
pop.setTarget(want);
|
|
115
|
+
wake();
|
|
116
|
+
}
|
|
117
|
+
draw();
|
|
118
|
+
},
|
|
119
|
+
redraw: draw
|
|
120
|
+
};
|
|
121
|
+
drawable.layOut(box);
|
|
122
|
+
draw();
|
|
123
|
+
items.set(id, item);
|
|
124
|
+
tempos.add(tempo);
|
|
125
|
+
steps.set(id, (dt) => {
|
|
126
|
+
const a = rect.step(dt);
|
|
127
|
+
const b = pop.step(dt);
|
|
128
|
+
draw();
|
|
129
|
+
return a || b;
|
|
130
|
+
});
|
|
131
|
+
removers.set(id, () => tempos.delete(tempo));
|
|
132
|
+
return item;
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
add,
|
|
136
|
+
get: (id) => items.get(id),
|
|
137
|
+
remove(id) {
|
|
138
|
+
items.delete(id);
|
|
139
|
+
steps.delete(id);
|
|
140
|
+
removers.get(id)?.();
|
|
141
|
+
removers.delete(id);
|
|
142
|
+
},
|
|
143
|
+
items,
|
|
144
|
+
task(task) {
|
|
145
|
+
tasks.add(task);
|
|
146
|
+
wake();
|
|
147
|
+
return () => tasks.delete(task);
|
|
148
|
+
},
|
|
149
|
+
onFrame(listener) {
|
|
150
|
+
frames.add(listener);
|
|
151
|
+
return () => frames.delete(listener);
|
|
152
|
+
},
|
|
153
|
+
onIdle(listener) {
|
|
154
|
+
idles.add(listener);
|
|
155
|
+
return () => idles.delete(listener);
|
|
156
|
+
},
|
|
157
|
+
wake,
|
|
158
|
+
get running() {
|
|
159
|
+
return stop !== null;
|
|
160
|
+
},
|
|
161
|
+
configure(config) {
|
|
162
|
+
if (config.omega !== void 0) omega = config.omega;
|
|
163
|
+
for (const t of tempos) t();
|
|
164
|
+
},
|
|
165
|
+
idle: () => stop ? new Promise((r) => waiters.push(r)) : Promise.resolve(),
|
|
166
|
+
dispose() {
|
|
167
|
+
stop?.();
|
|
168
|
+
stop = null;
|
|
169
|
+
items.clear();
|
|
170
|
+
steps.clear();
|
|
171
|
+
tasks.clear();
|
|
172
|
+
frames.clear();
|
|
173
|
+
idles.clear();
|
|
174
|
+
tempos.clear();
|
|
175
|
+
removers.clear();
|
|
176
|
+
const resolve = waiters;
|
|
177
|
+
waiters = [];
|
|
178
|
+
for (const r of resolve) r();
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// src/layout/held.ts
|
|
184
|
+
var CAUGHT = 0.5;
|
|
185
|
+
function holdItem(item, grab) {
|
|
186
|
+
let size = { width: item.drawn.width, height: item.drawn.height };
|
|
187
|
+
let at = { x: grab.x, y: grab.y };
|
|
188
|
+
let pointer = null;
|
|
189
|
+
let snapped = null;
|
|
190
|
+
let returning = false;
|
|
191
|
+
const heldRect = () => pointer ? { x: pointer.x - at.x, y: pointer.y - at.y, width: size.width, height: size.height } : null;
|
|
192
|
+
const same2 = (a, b) => Math.max(Math.abs(a.x - b.x), Math.abs(a.y - b.y), Math.abs(a.width - b.width), Math.abs(a.height - b.height)) < 0.01;
|
|
193
|
+
const snapTo = (slot) => {
|
|
194
|
+
if (slot) {
|
|
195
|
+
if (snapped && same2(snapped, slot)) return null;
|
|
196
|
+
snapped = { ...slot };
|
|
197
|
+
returning = false;
|
|
198
|
+
item.moveTo(slot);
|
|
199
|
+
return "snapped";
|
|
200
|
+
}
|
|
201
|
+
if (!snapped) return null;
|
|
202
|
+
snapped = null;
|
|
203
|
+
returning = true;
|
|
204
|
+
return "left";
|
|
205
|
+
};
|
|
206
|
+
const follow = () => {
|
|
207
|
+
const goal = heldRect();
|
|
208
|
+
if (!returning || !goal) return false;
|
|
209
|
+
item.moveTo(goal);
|
|
210
|
+
const d = item.drawn;
|
|
211
|
+
if (Math.max(Math.abs(d.x - goal.x), Math.abs(d.y - goal.y), Math.abs(d.width - goal.width), Math.abs(d.height - goal.height)) >= CAUGHT) return false;
|
|
212
|
+
returning = false;
|
|
213
|
+
item.hold(goal);
|
|
214
|
+
return true;
|
|
215
|
+
};
|
|
216
|
+
const letGo = (to) => {
|
|
217
|
+
const release = { ...item.drawn };
|
|
218
|
+
snapped = null;
|
|
219
|
+
returning = false;
|
|
220
|
+
item.moveTo(to);
|
|
221
|
+
return release;
|
|
222
|
+
};
|
|
223
|
+
return {
|
|
224
|
+
get pointer() {
|
|
225
|
+
return pointer;
|
|
226
|
+
},
|
|
227
|
+
get held() {
|
|
228
|
+
return heldRect();
|
|
229
|
+
},
|
|
230
|
+
get snapped() {
|
|
231
|
+
return snapped;
|
|
232
|
+
},
|
|
233
|
+
get returning() {
|
|
234
|
+
return returning;
|
|
235
|
+
},
|
|
236
|
+
move(p) {
|
|
237
|
+
pointer = { x: p.x, y: p.y };
|
|
238
|
+
if (snapped || returning) return;
|
|
239
|
+
item.hold(heldRect());
|
|
240
|
+
},
|
|
241
|
+
snapTo,
|
|
242
|
+
follow,
|
|
243
|
+
preview(plan, slot) {
|
|
244
|
+
const change = snapTo(plan.snapItem && slot ? slot : null);
|
|
245
|
+
follow();
|
|
246
|
+
return change;
|
|
247
|
+
},
|
|
248
|
+
resize(next) {
|
|
249
|
+
if (Math.abs(next.width - size.width) < 0.01 && Math.abs(next.height - size.height) < 0.01) return false;
|
|
250
|
+
at = { x: size.width ? at.x / size.width * next.width : 0, y: size.height ? at.y / size.height * next.height : 0 };
|
|
251
|
+
size = { width: next.width, height: next.height };
|
|
252
|
+
if (!snapped && pointer) {
|
|
253
|
+
returning = true;
|
|
254
|
+
follow();
|
|
255
|
+
}
|
|
256
|
+
return true;
|
|
257
|
+
},
|
|
258
|
+
land: letGo,
|
|
259
|
+
cancel: letGo
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// src/layout/flip.ts
|
|
264
|
+
var rectOf = (r) => ({ x: r.left, y: r.top, width: r.width, height: r.height });
|
|
265
|
+
function flip(entries, change) {
|
|
266
|
+
const before = entries.map((e) => rectOf(e.node.getBoundingClientRect()));
|
|
267
|
+
change();
|
|
268
|
+
entries.forEach((e, i) => {
|
|
269
|
+
const { from, to } = e.place(before[i]);
|
|
270
|
+
e.item.glideFrom(from, to);
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// src/layout/dom.ts
|
|
275
|
+
function layOutBox(node, box) {
|
|
276
|
+
const s = node.style;
|
|
277
|
+
s.left = `${box.x}px`;
|
|
278
|
+
s.top = `${box.y}px`;
|
|
279
|
+
s.width = `${box.width}px`;
|
|
280
|
+
s.height = `${box.height}px`;
|
|
281
|
+
}
|
|
282
|
+
function drawBox(nodes, frame) {
|
|
283
|
+
const { reveal: r, pop } = frame;
|
|
284
|
+
const cx = frame.drawn.width / 2;
|
|
285
|
+
const cy = frame.drawn.height / 2;
|
|
286
|
+
const s = nodes.node.style;
|
|
287
|
+
s.transform = pop.scale === 1 ? `translate(${r.dx}px, ${r.dy}px)` : `translate(${r.dx}px, ${r.dy}px) translate(${cx}px, ${cy}px) scale(${pop.scale}) translate(${-cx}px, ${-cy}px)`;
|
|
288
|
+
s.opacity = pop.opacity === 1 ? "" : String(pop.opacity);
|
|
289
|
+
if (nodes.frame) {
|
|
290
|
+
nodes.frame.style.width = `${r.width}px`;
|
|
291
|
+
nodes.frame.style.height = `${r.height}px`;
|
|
292
|
+
}
|
|
293
|
+
const clipped = nodes.content ?? (nodes.frame ? null : nodes.node);
|
|
294
|
+
if (clipped) clipped.style.clipPath = r.clipRight || r.clipBottom ? `inset(0 ${r.clipRight}px ${r.clipBottom}px 0)` : "";
|
|
295
|
+
s.visibility = frame.gone ? "hidden" : "";
|
|
296
|
+
}
|
|
297
|
+
function domDrawable(nodes) {
|
|
298
|
+
return {
|
|
299
|
+
layOut: (box) => layOutBox(nodes.node, box),
|
|
300
|
+
draw: (frame) => drawBox(nodes, frame)
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
export {
|
|
304
|
+
createLayoutMotion,
|
|
305
|
+
domDrawable,
|
|
306
|
+
drawBox,
|
|
307
|
+
flip,
|
|
308
|
+
holdItem,
|
|
309
|
+
layOutBox,
|
|
310
|
+
rectOf
|
|
311
|
+
};
|
|
312
|
+
//# sourceMappingURL=layout.js.map
|