@bquery/bquery 1.2.0 → 1.3.0
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 +501 -427
- package/dist/batch-4LAvfLE7.js +13 -0
- package/dist/batch-4LAvfLE7.js.map +1 -0
- package/dist/component/component.d.ts +69 -0
- package/dist/component/component.d.ts.map +1 -0
- package/dist/component/html.d.ts +35 -0
- package/dist/component/html.d.ts.map +1 -0
- package/dist/component/index.d.ts +3 -126
- package/dist/component/index.d.ts.map +1 -1
- package/dist/component/props.d.ts +18 -0
- package/dist/component/props.d.ts.map +1 -0
- package/dist/component/types.d.ts +77 -0
- package/dist/component/types.d.ts.map +1 -0
- package/dist/component.es.mjs +90 -59
- package/dist/component.es.mjs.map +1 -1
- package/dist/core/collection.d.ts +36 -0
- package/dist/core/collection.d.ts.map +1 -1
- package/dist/core/dom.d.ts +6 -0
- package/dist/core/dom.d.ts.map +1 -0
- package/dist/core/element.d.ts +8 -0
- package/dist/core/element.d.ts.map +1 -1
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/utils/array.d.ts +74 -0
- package/dist/core/utils/array.d.ts.map +1 -0
- package/dist/core/utils/function.d.ts +70 -0
- package/dist/core/utils/function.d.ts.map +1 -0
- package/dist/core/utils/index.d.ts +70 -0
- package/dist/core/utils/index.d.ts.map +1 -0
- package/dist/core/utils/misc.d.ts +63 -0
- package/dist/core/utils/misc.d.ts.map +1 -0
- package/dist/core/utils/number.d.ts +65 -0
- package/dist/core/utils/number.d.ts.map +1 -0
- package/dist/core/utils/object.d.ts +133 -0
- package/dist/core/utils/object.d.ts.map +1 -0
- package/dist/core/utils/string.d.ts +80 -0
- package/dist/core/utils/string.d.ts.map +1 -0
- package/dist/core/utils/type-guards.d.ts +79 -0
- package/dist/core/utils/type-guards.d.ts.map +1 -0
- package/dist/core-COenAZjD.js +145 -0
- package/dist/core-COenAZjD.js.map +1 -0
- package/dist/core.es.mjs +411 -448
- package/dist/core.es.mjs.map +1 -1
- package/dist/full.d.ts +2 -2
- package/dist/full.d.ts.map +1 -1
- package/dist/full.es.mjs +87 -64
- package/dist/full.es.mjs.map +1 -1
- package/dist/full.iife.js +2 -2
- package/dist/full.iife.js.map +1 -1
- package/dist/full.umd.js +2 -2
- package/dist/full.umd.js.map +1 -1
- package/dist/index.es.mjs +138 -68
- package/dist/index.es.mjs.map +1 -1
- package/dist/motion/animate.d.ts +25 -0
- package/dist/motion/animate.d.ts.map +1 -0
- package/dist/motion/easing.d.ts +30 -0
- package/dist/motion/easing.d.ts.map +1 -0
- package/dist/motion/flip.d.ts +55 -0
- package/dist/motion/flip.d.ts.map +1 -0
- package/dist/motion/index.d.ts +11 -138
- package/dist/motion/index.d.ts.map +1 -1
- package/dist/motion/keyframes.d.ts +21 -0
- package/dist/motion/keyframes.d.ts.map +1 -0
- package/dist/motion/reduced-motion.d.ts +12 -0
- package/dist/motion/reduced-motion.d.ts.map +1 -0
- package/dist/motion/scroll.d.ts +15 -0
- package/dist/motion/scroll.d.ts.map +1 -0
- package/dist/motion/spring.d.ts +42 -0
- package/dist/motion/spring.d.ts.map +1 -0
- package/dist/motion/stagger.d.ts +22 -0
- package/dist/motion/stagger.d.ts.map +1 -0
- package/dist/motion/timeline.d.ts +21 -0
- package/dist/motion/timeline.d.ts.map +1 -0
- package/dist/motion/transition.d.ts +22 -0
- package/dist/motion/transition.d.ts.map +1 -0
- package/dist/motion/types.d.ts +182 -0
- package/dist/motion/types.d.ts.map +1 -0
- package/dist/motion.es.mjs +320 -61
- package/dist/motion.es.mjs.map +1 -1
- package/dist/persisted-Dz_ryNuC.js +278 -0
- package/dist/persisted-Dz_ryNuC.js.map +1 -0
- package/dist/reactive/batch.d.ts +13 -0
- package/dist/reactive/batch.d.ts.map +1 -0
- package/dist/reactive/computed.d.ts +50 -0
- package/dist/reactive/computed.d.ts.map +1 -0
- package/dist/reactive/core.d.ts +60 -0
- package/dist/reactive/core.d.ts.map +1 -0
- package/dist/reactive/effect.d.ts +15 -0
- package/dist/reactive/effect.d.ts.map +1 -0
- package/dist/reactive/index.d.ts +2 -2
- package/dist/reactive/index.d.ts.map +1 -1
- package/dist/reactive/internals.d.ts +36 -0
- package/dist/reactive/internals.d.ts.map +1 -0
- package/dist/reactive/linked.d.ts +36 -0
- package/dist/reactive/linked.d.ts.map +1 -0
- package/dist/reactive/persisted.d.ts +14 -0
- package/dist/reactive/persisted.d.ts.map +1 -0
- package/dist/reactive/readonly.d.ts +26 -0
- package/dist/reactive/readonly.d.ts.map +1 -0
- package/dist/reactive/signal.d.ts +13 -312
- package/dist/reactive/signal.d.ts.map +1 -1
- package/dist/reactive/type-guards.d.ts +20 -0
- package/dist/reactive/type-guards.d.ts.map +1 -0
- package/dist/reactive/untrack.d.ts +29 -0
- package/dist/reactive/untrack.d.ts.map +1 -0
- package/dist/reactive/watch.d.ts +42 -0
- package/dist/reactive/watch.d.ts.map +1 -0
- package/dist/reactive.es.mjs +30 -163
- package/dist/reactive.es.mjs.map +1 -1
- package/dist/router/index.d.ts +6 -252
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/links.d.ts +44 -0
- package/dist/router/links.d.ts.map +1 -0
- package/dist/router/match.d.ts +20 -0
- package/dist/router/match.d.ts.map +1 -0
- package/dist/router/navigation.d.ts +45 -0
- package/dist/router/navigation.d.ts.map +1 -0
- package/dist/router/query.d.ts +16 -0
- package/dist/router/query.d.ts.map +1 -0
- package/dist/router/router.d.ts +34 -0
- package/dist/router/router.d.ts.map +1 -0
- package/dist/router/state.d.ts +27 -0
- package/dist/router/state.d.ts.map +1 -0
- package/dist/router/types.d.ts +88 -0
- package/dist/router/types.d.ts.map +1 -0
- package/dist/router/utils.d.ts +65 -0
- package/dist/router/utils.d.ts.map +1 -0
- package/dist/router.es.mjs +168 -132
- package/dist/router.es.mjs.map +1 -1
- package/dist/sanitize-1FBEPAFH.js +272 -0
- package/dist/sanitize-1FBEPAFH.js.map +1 -0
- package/dist/security/constants.d.ts +42 -0
- package/dist/security/constants.d.ts.map +1 -0
- package/dist/security/csp.d.ts +24 -0
- package/dist/security/csp.d.ts.map +1 -0
- package/dist/security/index.d.ts +4 -2
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/sanitize-core.d.ts +13 -0
- package/dist/security/sanitize-core.d.ts.map +1 -0
- package/dist/security/sanitize.d.ts +5 -57
- package/dist/security/sanitize.d.ts.map +1 -1
- package/dist/security/trusted-types.d.ts +25 -0
- package/dist/security/trusted-types.d.ts.map +1 -0
- package/dist/security/types.d.ts +36 -0
- package/dist/security/types.d.ts.map +1 -0
- package/dist/security.es.mjs +50 -277
- package/dist/security.es.mjs.map +1 -1
- package/dist/store/create-store.d.ts +15 -0
- package/dist/store/create-store.d.ts.map +1 -0
- package/dist/store/define-store.d.ts +28 -0
- package/dist/store/define-store.d.ts.map +1 -0
- package/dist/store/devtools.d.ts +22 -0
- package/dist/store/devtools.d.ts.map +1 -0
- package/dist/store/index.d.ts +10 -286
- package/dist/store/index.d.ts.map +1 -1
- package/dist/store/mapping.d.ts +28 -0
- package/dist/store/mapping.d.ts.map +1 -0
- package/dist/store/persisted.d.ts +13 -0
- package/dist/store/persisted.d.ts.map +1 -0
- package/dist/store/plugins.d.ts +13 -0
- package/dist/store/plugins.d.ts.map +1 -0
- package/dist/store/registry.d.ts +28 -0
- package/dist/store/registry.d.ts.map +1 -0
- package/dist/store/types.d.ts +71 -0
- package/dist/store/types.d.ts.map +1 -0
- package/dist/store/utils.d.ts +28 -0
- package/dist/store/utils.d.ts.map +1 -0
- package/dist/store/watch.d.ts +23 -0
- package/dist/store/watch.d.ts.map +1 -0
- package/dist/store.es.mjs +22 -224
- package/dist/store.es.mjs.map +1 -1
- package/dist/type-guards-DRma3-Kc.js +16 -0
- package/dist/type-guards-DRma3-Kc.js.map +1 -0
- package/dist/untrack-BuEQKH7_.js +6 -0
- package/dist/untrack-BuEQKH7_.js.map +1 -0
- package/dist/view/directives/bind.d.ts +7 -0
- package/dist/view/directives/bind.d.ts.map +1 -0
- package/dist/view/directives/class.d.ts +8 -0
- package/dist/view/directives/class.d.ts.map +1 -0
- package/dist/view/directives/for.d.ts +23 -0
- package/dist/view/directives/for.d.ts.map +1 -0
- package/dist/view/directives/html.d.ts +7 -0
- package/dist/view/directives/html.d.ts.map +1 -0
- package/dist/view/directives/if.d.ts +7 -0
- package/dist/view/directives/if.d.ts.map +1 -0
- package/dist/view/directives/index.d.ts +12 -0
- package/dist/view/directives/index.d.ts.map +1 -0
- package/dist/view/directives/model.d.ts +7 -0
- package/dist/view/directives/model.d.ts.map +1 -0
- package/dist/view/directives/on.d.ts +7 -0
- package/dist/view/directives/on.d.ts.map +1 -0
- package/dist/view/directives/ref.d.ts +7 -0
- package/dist/view/directives/ref.d.ts.map +1 -0
- package/dist/view/directives/show.d.ts +7 -0
- package/dist/view/directives/show.d.ts.map +1 -0
- package/dist/view/directives/style.d.ts +7 -0
- package/dist/view/directives/style.d.ts.map +1 -0
- package/dist/view/directives/text.d.ts +7 -0
- package/dist/view/directives/text.d.ts.map +1 -0
- package/dist/view/evaluate.d.ts +43 -0
- package/dist/view/evaluate.d.ts.map +1 -0
- package/dist/view/index.d.ts +3 -93
- package/dist/view/index.d.ts.map +1 -1
- package/dist/view/mount.d.ts +69 -0
- package/dist/view/mount.d.ts.map +1 -0
- package/dist/view/process.d.ts +26 -0
- package/dist/view/process.d.ts.map +1 -0
- package/dist/view/types.d.ts +36 -0
- package/dist/view/types.d.ts.map +1 -0
- package/dist/view.es.mjs +368 -267
- package/dist/view.es.mjs.map +1 -1
- package/dist/watch-CXyaBC_9.js +58 -0
- package/dist/watch-CXyaBC_9.js.map +1 -0
- package/package.json +132 -132
- package/src/component/component.ts +289 -0
- package/src/component/html.ts +53 -0
- package/src/component/index.ts +40 -414
- package/src/component/props.ts +116 -0
- package/src/component/types.ts +85 -0
- package/src/core/collection.ts +588 -454
- package/src/core/dom.ts +38 -0
- package/src/core/element.ts +746 -740
- package/src/core/index.ts +43 -0
- package/src/core/utils/array.ts +102 -0
- package/src/core/utils/function.ts +110 -0
- package/src/core/utils/index.ts +83 -0
- package/src/core/utils/misc.ts +82 -0
- package/src/core/utils/number.ts +78 -0
- package/src/core/utils/object.ts +206 -0
- package/src/core/utils/string.ts +112 -0
- package/src/core/utils/type-guards.ts +112 -0
- package/src/full.ts +187 -150
- package/src/index.ts +36 -36
- package/src/motion/animate.ts +113 -0
- package/src/motion/easing.ts +40 -0
- package/src/motion/flip.ts +176 -0
- package/src/motion/index.ts +41 -358
- package/src/motion/keyframes.ts +46 -0
- package/src/motion/reduced-motion.ts +17 -0
- package/src/motion/scroll.ts +57 -0
- package/src/motion/spring.ts +150 -0
- package/src/motion/stagger.ts +43 -0
- package/src/motion/timeline.ts +246 -0
- package/src/motion/transition.ts +51 -0
- package/src/motion/types.ts +198 -0
- package/src/reactive/batch.ts +22 -0
- package/src/reactive/computed.ts +92 -0
- package/src/reactive/core.ts +93 -0
- package/src/reactive/effect.ts +43 -0
- package/src/reactive/index.ts +23 -22
- package/src/reactive/internals.ts +105 -0
- package/src/reactive/linked.ts +56 -0
- package/src/reactive/persisted.ts +74 -0
- package/src/reactive/readonly.ts +35 -0
- package/src/reactive/signal.ts +20 -520
- package/src/reactive/type-guards.ts +22 -0
- package/src/reactive/untrack.ts +31 -0
- package/src/reactive/watch.ts +73 -0
- package/src/router/index.ts +41 -718
- package/src/router/links.ts +130 -0
- package/src/router/match.ts +106 -0
- package/src/router/navigation.ts +71 -0
- package/src/router/query.ts +35 -0
- package/src/router/router.ts +211 -0
- package/src/router/state.ts +46 -0
- package/src/router/types.ts +93 -0
- package/src/router/utils.ts +116 -0
- package/src/security/constants.ts +209 -0
- package/src/security/csp.ts +77 -0
- package/src/security/index.ts +4 -12
- package/src/security/sanitize-core.ts +343 -0
- package/src/security/sanitize.ts +66 -625
- package/src/security/trusted-types.ts +69 -0
- package/src/security/types.ts +40 -0
- package/src/store/create-store.ts +329 -0
- package/src/store/define-store.ts +48 -0
- package/src/store/devtools.ts +45 -0
- package/src/store/index.ts +22 -848
- package/src/store/mapping.ts +73 -0
- package/src/store/persisted.ts +61 -0
- package/src/store/plugins.ts +32 -0
- package/src/store/registry.ts +51 -0
- package/src/store/types.ts +94 -0
- package/src/store/utils.ts +141 -0
- package/src/store/watch.ts +52 -0
- package/src/view/directives/bind.ts +23 -0
- package/src/view/directives/class.ts +70 -0
- package/src/view/directives/for.ts +275 -0
- package/src/view/directives/html.ts +19 -0
- package/src/view/directives/if.ts +30 -0
- package/src/view/directives/index.ts +11 -0
- package/src/view/directives/model.ts +56 -0
- package/src/view/directives/on.ts +41 -0
- package/src/view/directives/ref.ts +41 -0
- package/src/view/directives/show.ts +26 -0
- package/src/view/directives/style.ts +47 -0
- package/src/view/directives/text.ts +15 -0
- package/src/view/evaluate.ts +274 -0
- package/src/view/index.ts +112 -1041
- package/src/view/mount.ts +200 -0
- package/src/view/process.ts +92 -0
- package/src/view/types.ts +44 -0
- package/dist/core/utils.d.ts +0 -313
- package/dist/core/utils.d.ts.map +0 -1
- package/src/core/utils.ts +0 -444
package/src/motion/index.ts
CHANGED
|
@@ -5,361 +5,44 @@
|
|
|
5
5
|
* @module bquery/motion
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
export
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
damping?: number;
|
|
50
|
-
/** Mass of the object (default: 1) */
|
|
51
|
-
mass?: number;
|
|
52
|
-
/** Velocity threshold for completion (default: 0.01) */
|
|
53
|
-
precision?: number;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Spring instance for animating values.
|
|
58
|
-
*/
|
|
59
|
-
export interface Spring {
|
|
60
|
-
/** Start animating to target value */
|
|
61
|
-
to(target: number): Promise<void>;
|
|
62
|
-
/** Get current animated value */
|
|
63
|
-
current(): number;
|
|
64
|
-
/** Stop the animation */
|
|
65
|
-
stop(): void;
|
|
66
|
-
/** Subscribe to value changes */
|
|
67
|
-
onChange(callback: (value: number) => void): () => void;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// ============================================================================
|
|
71
|
-
// View Transitions
|
|
72
|
-
// ============================================================================
|
|
73
|
-
|
|
74
|
-
/** Extended document type with View Transitions API */
|
|
75
|
-
type DocumentWithTransition = Document & {
|
|
76
|
-
startViewTransition?: (callback: () => void) => {
|
|
77
|
-
finished: Promise<void>;
|
|
78
|
-
ready: Promise<void>;
|
|
79
|
-
updateCallbackDone: Promise<void>;
|
|
80
|
-
};
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Execute a DOM update with view transition animation.
|
|
85
|
-
* Falls back to immediate update when View Transitions API is unavailable.
|
|
86
|
-
*
|
|
87
|
-
* @param updateOrOptions - Update function or options object
|
|
88
|
-
* @returns Promise that resolves when transition completes
|
|
89
|
-
*
|
|
90
|
-
* @example
|
|
91
|
-
* ```ts
|
|
92
|
-
* await transition(() => {
|
|
93
|
-
* $('#content').text('Updated');
|
|
94
|
-
* });
|
|
95
|
-
* ```
|
|
96
|
-
*/
|
|
97
|
-
export const transition = async (
|
|
98
|
-
updateOrOptions: (() => void) | TransitionOptions
|
|
99
|
-
): Promise<void> => {
|
|
100
|
-
const update = typeof updateOrOptions === 'function' ? updateOrOptions : updateOrOptions.update;
|
|
101
|
-
|
|
102
|
-
const doc = document as DocumentWithTransition;
|
|
103
|
-
|
|
104
|
-
if (doc.startViewTransition) {
|
|
105
|
-
await doc.startViewTransition(() => update()).finished;
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
update();
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
// ============================================================================
|
|
113
|
-
// FLIP Animations
|
|
114
|
-
// ============================================================================
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Capture the current bounds of an element for FLIP animation.
|
|
118
|
-
*
|
|
119
|
-
* @param element - The DOM element to measure
|
|
120
|
-
* @returns The element's current position and size
|
|
121
|
-
*/
|
|
122
|
-
export const capturePosition = (element: Element): ElementBounds => {
|
|
123
|
-
const rect = element.getBoundingClientRect();
|
|
124
|
-
return {
|
|
125
|
-
top: rect.top,
|
|
126
|
-
left: rect.left,
|
|
127
|
-
width: rect.width,
|
|
128
|
-
height: rect.height,
|
|
129
|
-
};
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Perform a FLIP (First, Last, Invert, Play) animation.
|
|
134
|
-
* Animates an element from its captured position to its current position.
|
|
135
|
-
*
|
|
136
|
-
* @param element - The element to animate
|
|
137
|
-
* @param firstBounds - The previously captured bounds
|
|
138
|
-
* @param options - Animation configuration
|
|
139
|
-
* @returns Promise that resolves when animation completes
|
|
140
|
-
*
|
|
141
|
-
* @example
|
|
142
|
-
* ```ts
|
|
143
|
-
* const first = capturePosition(element);
|
|
144
|
-
* // ... DOM changes that move the element ...
|
|
145
|
-
* await flip(element, first, { duration: 300 });
|
|
146
|
-
* ```
|
|
147
|
-
*/
|
|
148
|
-
export const flip = (
|
|
149
|
-
element: Element,
|
|
150
|
-
firstBounds: ElementBounds,
|
|
151
|
-
options: FlipOptions = {}
|
|
152
|
-
): Promise<void> => {
|
|
153
|
-
const { duration = 300, easing = 'ease-out', onComplete } = options;
|
|
154
|
-
|
|
155
|
-
// Last: Get current position
|
|
156
|
-
const lastBounds = capturePosition(element);
|
|
157
|
-
|
|
158
|
-
// Skip animation if element has zero dimensions (avoid division by zero)
|
|
159
|
-
if (lastBounds.width === 0 || lastBounds.height === 0) {
|
|
160
|
-
return Promise.resolve();
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// Invert: Calculate the delta
|
|
164
|
-
const deltaX = firstBounds.left - lastBounds.left;
|
|
165
|
-
const deltaY = firstBounds.top - lastBounds.top;
|
|
166
|
-
const deltaW = firstBounds.width / lastBounds.width;
|
|
167
|
-
const deltaH = firstBounds.height / lastBounds.height;
|
|
168
|
-
|
|
169
|
-
// Skip animation if no change
|
|
170
|
-
if (deltaX === 0 && deltaY === 0 && deltaW === 1 && deltaH === 1) {
|
|
171
|
-
return Promise.resolve();
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
const htmlElement = element as HTMLElement;
|
|
175
|
-
|
|
176
|
-
// Apply inverted transform
|
|
177
|
-
htmlElement.style.transform = `translate(${deltaX}px, ${deltaY}px) scale(${deltaW}, ${deltaH})`;
|
|
178
|
-
htmlElement.style.transformOrigin = 'top left';
|
|
179
|
-
|
|
180
|
-
// Force reflow
|
|
181
|
-
void htmlElement.offsetHeight;
|
|
182
|
-
|
|
183
|
-
// Play: Animate back to current position
|
|
184
|
-
return new Promise((resolve) => {
|
|
185
|
-
const animation = htmlElement.animate(
|
|
186
|
-
[
|
|
187
|
-
{
|
|
188
|
-
transform: `translate(${deltaX}px, ${deltaY}px) scale(${deltaW}, ${deltaH})`,
|
|
189
|
-
},
|
|
190
|
-
{ transform: 'translate(0, 0) scale(1, 1)' },
|
|
191
|
-
],
|
|
192
|
-
{ duration, easing, fill: 'forwards' }
|
|
193
|
-
);
|
|
194
|
-
|
|
195
|
-
animation.onfinish = () => {
|
|
196
|
-
htmlElement.style.transform = '';
|
|
197
|
-
htmlElement.style.transformOrigin = '';
|
|
198
|
-
onComplete?.();
|
|
199
|
-
resolve();
|
|
200
|
-
};
|
|
201
|
-
});
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* FLIP helper for animating a list of elements.
|
|
206
|
-
* Useful for reordering lists with smooth animations.
|
|
207
|
-
*
|
|
208
|
-
* @param elements - Array of elements to animate
|
|
209
|
-
* @param performUpdate - Function that performs the DOM update
|
|
210
|
-
* @param options - Animation configuration
|
|
211
|
-
*
|
|
212
|
-
* @example
|
|
213
|
-
* ```ts
|
|
214
|
-
* await flipList(listItems, () => {
|
|
215
|
-
* container.appendChild(container.firstChild); // Move first to last
|
|
216
|
-
* });
|
|
217
|
-
* ```
|
|
218
|
-
*/
|
|
219
|
-
export const flipList = async (
|
|
220
|
-
elements: Element[],
|
|
221
|
-
performUpdate: () => void,
|
|
222
|
-
options: FlipOptions = {}
|
|
223
|
-
): Promise<void> => {
|
|
224
|
-
// First: Capture all positions
|
|
225
|
-
const positions = new Map<Element, ElementBounds>();
|
|
226
|
-
for (const el of elements) {
|
|
227
|
-
positions.set(el, capturePosition(el));
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// Perform DOM update
|
|
231
|
-
performUpdate();
|
|
232
|
-
|
|
233
|
-
// Animate each element
|
|
234
|
-
const animations = elements.map((el) => {
|
|
235
|
-
const first = positions.get(el);
|
|
236
|
-
if (!first) return Promise.resolve();
|
|
237
|
-
return flip(el, first, options);
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
await Promise.all(animations);
|
|
241
|
-
};
|
|
242
|
-
|
|
243
|
-
// ============================================================================
|
|
244
|
-
// Spring Physics
|
|
245
|
-
// ============================================================================
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* Default spring configuration values.
|
|
249
|
-
*/
|
|
250
|
-
const DEFAULT_SPRING_CONFIG: Required<SpringConfig> = {
|
|
251
|
-
stiffness: 100,
|
|
252
|
-
damping: 10,
|
|
253
|
-
mass: 1,
|
|
254
|
-
precision: 0.01,
|
|
255
|
-
};
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* Create a spring-based animation for smooth, physics-based motion.
|
|
259
|
-
*
|
|
260
|
-
* @param initialValue - Starting value for the spring
|
|
261
|
-
* @param config - Spring physics configuration
|
|
262
|
-
* @returns Spring instance for controlling the animation
|
|
263
|
-
*
|
|
264
|
-
* @example
|
|
265
|
-
* ```ts
|
|
266
|
-
* const x = spring(0, { stiffness: 120, damping: 14 });
|
|
267
|
-
* x.onChange((value) => {
|
|
268
|
-
* element.style.transform = `translateX(${value}px)`;
|
|
269
|
-
* });
|
|
270
|
-
* await x.to(100);
|
|
271
|
-
* ```
|
|
272
|
-
*/
|
|
273
|
-
export const spring = (initialValue: number, config: SpringConfig = {}): Spring => {
|
|
274
|
-
const { stiffness, damping, mass, precision } = {
|
|
275
|
-
...DEFAULT_SPRING_CONFIG,
|
|
276
|
-
...config,
|
|
277
|
-
};
|
|
278
|
-
|
|
279
|
-
let current = initialValue;
|
|
280
|
-
let velocity = 0;
|
|
281
|
-
let target = initialValue;
|
|
282
|
-
let animationFrame: number | null = null;
|
|
283
|
-
let resolvePromise: (() => void) | null = null;
|
|
284
|
-
const listeners = new Set<(value: number) => void>();
|
|
285
|
-
|
|
286
|
-
const notifyListeners = () => {
|
|
287
|
-
for (const listener of listeners) {
|
|
288
|
-
listener(current);
|
|
289
|
-
}
|
|
290
|
-
};
|
|
291
|
-
|
|
292
|
-
const step = () => {
|
|
293
|
-
// Spring physics calculation
|
|
294
|
-
const displacement = current - target;
|
|
295
|
-
const springForce = -stiffness * displacement;
|
|
296
|
-
const dampingForce = -damping * velocity;
|
|
297
|
-
const acceleration = (springForce + dampingForce) / mass;
|
|
298
|
-
|
|
299
|
-
velocity += acceleration * (1 / 60); // Assuming 60fps
|
|
300
|
-
current += velocity * (1 / 60);
|
|
301
|
-
|
|
302
|
-
notifyListeners();
|
|
303
|
-
|
|
304
|
-
// Check if spring has settled
|
|
305
|
-
if (Math.abs(velocity) < precision && Math.abs(displacement) < precision) {
|
|
306
|
-
current = target;
|
|
307
|
-
velocity = 0;
|
|
308
|
-
animationFrame = null;
|
|
309
|
-
notifyListeners();
|
|
310
|
-
resolvePromise?.();
|
|
311
|
-
resolvePromise = null;
|
|
312
|
-
return;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
animationFrame = requestAnimationFrame(step);
|
|
316
|
-
};
|
|
317
|
-
|
|
318
|
-
return {
|
|
319
|
-
to(newTarget: number): Promise<void> {
|
|
320
|
-
target = newTarget;
|
|
321
|
-
|
|
322
|
-
if (animationFrame !== null) {
|
|
323
|
-
cancelAnimationFrame(animationFrame);
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
return new Promise((resolve) => {
|
|
327
|
-
resolvePromise = resolve;
|
|
328
|
-
animationFrame = requestAnimationFrame(step);
|
|
329
|
-
});
|
|
330
|
-
},
|
|
331
|
-
|
|
332
|
-
current(): number {
|
|
333
|
-
return current;
|
|
334
|
-
},
|
|
335
|
-
|
|
336
|
-
stop(): void {
|
|
337
|
-
if (animationFrame !== null) {
|
|
338
|
-
cancelAnimationFrame(animationFrame);
|
|
339
|
-
animationFrame = null;
|
|
340
|
-
}
|
|
341
|
-
velocity = 0;
|
|
342
|
-
resolvePromise?.();
|
|
343
|
-
resolvePromise = null;
|
|
344
|
-
},
|
|
345
|
-
|
|
346
|
-
onChange(callback: (value: number) => void): () => void {
|
|
347
|
-
listeners.add(callback);
|
|
348
|
-
return () => listeners.delete(callback);
|
|
349
|
-
},
|
|
350
|
-
};
|
|
351
|
-
};
|
|
352
|
-
|
|
353
|
-
/**
|
|
354
|
-
* Preset spring configurations for common use cases.
|
|
355
|
-
*/
|
|
356
|
-
export const springPresets = {
|
|
357
|
-
/** Gentle, slow-settling spring */
|
|
358
|
-
gentle: { stiffness: 80, damping: 15 } as SpringConfig,
|
|
359
|
-
/** Responsive, snappy spring */
|
|
360
|
-
snappy: { stiffness: 200, damping: 20 } as SpringConfig,
|
|
361
|
-
/** Bouncy, playful spring */
|
|
362
|
-
bouncy: { stiffness: 300, damping: 8 } as SpringConfig,
|
|
363
|
-
/** Stiff, quick spring with minimal overshoot */
|
|
364
|
-
stiff: { stiffness: 400, damping: 30 } as SpringConfig,
|
|
365
|
-
};
|
|
8
|
+
export type {
|
|
9
|
+
AnimateOptions,
|
|
10
|
+
EasingFunction,
|
|
11
|
+
ElementBounds,
|
|
12
|
+
FlipGroupOptions,
|
|
13
|
+
FlipOptions,
|
|
14
|
+
ScrollAnimateCleanup,
|
|
15
|
+
ScrollAnimateOptions,
|
|
16
|
+
SequenceOptions,
|
|
17
|
+
SequenceStep,
|
|
18
|
+
Spring,
|
|
19
|
+
SpringConfig,
|
|
20
|
+
StaggerFunction,
|
|
21
|
+
StaggerOptions,
|
|
22
|
+
TimelineConfig,
|
|
23
|
+
TimelineControls,
|
|
24
|
+
TimelineStep,
|
|
25
|
+
TransitionOptions,
|
|
26
|
+
} from './types';
|
|
27
|
+
|
|
28
|
+
export { animate } from './animate';
|
|
29
|
+
export {
|
|
30
|
+
easeInCubic,
|
|
31
|
+
easeInOutCubic,
|
|
32
|
+
easeInOutQuad,
|
|
33
|
+
easeInQuad,
|
|
34
|
+
easeOutBack,
|
|
35
|
+
easeOutCubic,
|
|
36
|
+
easeOutExpo,
|
|
37
|
+
easeOutQuad,
|
|
38
|
+
easingPresets,
|
|
39
|
+
linear,
|
|
40
|
+
} from './easing';
|
|
41
|
+
export { capturePosition, flip, flipElements, flipList } from './flip';
|
|
42
|
+
export { keyframePresets } from './keyframes';
|
|
43
|
+
export { prefersReducedMotion } from './reduced-motion';
|
|
44
|
+
export { scrollAnimate } from './scroll';
|
|
45
|
+
export { spring, springPresets } from './spring';
|
|
46
|
+
export { stagger } from './stagger';
|
|
47
|
+
export { sequence, timeline } from './timeline';
|
|
48
|
+
export { transition } from './transition';
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keyframe presets.
|
|
3
|
+
*
|
|
4
|
+
* @module bquery/motion
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Common keyframe presets for quick animations.
|
|
9
|
+
*/
|
|
10
|
+
export const keyframePresets = {
|
|
11
|
+
fadeIn: (from = 0, to = 1): Keyframe[] => [{ opacity: from }, { opacity: to }],
|
|
12
|
+
fadeOut: (from = 1, to = 0): Keyframe[] => [{ opacity: from }, { opacity: to }],
|
|
13
|
+
slideInUp: (distance = 16): Keyframe[] => [
|
|
14
|
+
{ opacity: 0, transform: `translateY(${distance}px)` },
|
|
15
|
+
{ opacity: 1, transform: 'translateY(0)' },
|
|
16
|
+
],
|
|
17
|
+
slideInDown: (distance = 16): Keyframe[] => [
|
|
18
|
+
{ opacity: 0, transform: `translateY(-${distance}px)` },
|
|
19
|
+
{ opacity: 1, transform: 'translateY(0)' },
|
|
20
|
+
],
|
|
21
|
+
slideInLeft: (distance = 16): Keyframe[] => [
|
|
22
|
+
{ opacity: 0, transform: `translateX(${distance}px)` },
|
|
23
|
+
{ opacity: 1, transform: 'translateX(0)' },
|
|
24
|
+
],
|
|
25
|
+
slideInRight: (distance = 16): Keyframe[] => [
|
|
26
|
+
{ opacity: 0, transform: `translateX(-${distance}px)` },
|
|
27
|
+
{ opacity: 1, transform: 'translateX(0)' },
|
|
28
|
+
],
|
|
29
|
+
scaleIn: (from = 0.95, to = 1): Keyframe[] => [
|
|
30
|
+
{ opacity: 0, transform: `scale(${from})` },
|
|
31
|
+
{ opacity: 1, transform: `scale(${to})` },
|
|
32
|
+
],
|
|
33
|
+
scaleOut: (from = 1, to = 0.95): Keyframe[] => [
|
|
34
|
+
{ opacity: 1, transform: `scale(${from})` },
|
|
35
|
+
{ opacity: 0, transform: `scale(${to})` },
|
|
36
|
+
],
|
|
37
|
+
pop: (from = 0.9, mid = 1.02, to = 1): Keyframe[] => [
|
|
38
|
+
{ opacity: 0, transform: `scale(${from})` },
|
|
39
|
+
{ opacity: 1, transform: `scale(${mid})`, offset: 0.6 },
|
|
40
|
+
{ opacity: 1, transform: `scale(${to})` },
|
|
41
|
+
],
|
|
42
|
+
rotateIn: (degrees = 6): Keyframe[] => [
|
|
43
|
+
{ opacity: 0, transform: `rotate(${degrees}deg) scale(0.98)` },
|
|
44
|
+
{ opacity: 1, transform: 'rotate(0deg) scale(1)' },
|
|
45
|
+
],
|
|
46
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reduced motion detection helpers.
|
|
3
|
+
*
|
|
4
|
+
* @module bquery/motion
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Check whether the user prefers reduced motion.
|
|
9
|
+
*
|
|
10
|
+
* @returns true if the user prefers reduced motion, otherwise false
|
|
11
|
+
*/
|
|
12
|
+
export const prefersReducedMotion = (): boolean => {
|
|
13
|
+
if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
return window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
17
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scroll-triggered animation helpers.
|
|
3
|
+
*
|
|
4
|
+
* @module bquery/motion
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { animate } from './animate';
|
|
8
|
+
import type { ScrollAnimateCleanup, ScrollAnimateOptions } from './types';
|
|
9
|
+
|
|
10
|
+
const resolveElements = (elements: Element | Iterable<Element> | ArrayLike<Element>): Element[] => {
|
|
11
|
+
if (typeof Element !== 'undefined' && elements instanceof Element) return [elements];
|
|
12
|
+
return Array.from(elements as Iterable<Element>);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Animate elements when they enter the viewport.
|
|
17
|
+
*
|
|
18
|
+
* @param elements - Target element(s)
|
|
19
|
+
* @param options - Scroll animation configuration
|
|
20
|
+
* @returns Cleanup function to disconnect observers
|
|
21
|
+
*/
|
|
22
|
+
export const scrollAnimate = (
|
|
23
|
+
elements: Element | Iterable<Element> | ArrayLike<Element>,
|
|
24
|
+
options: ScrollAnimateOptions
|
|
25
|
+
): ScrollAnimateCleanup => {
|
|
26
|
+
const targets = resolveElements(elements);
|
|
27
|
+
if (!targets.length) return () => undefined;
|
|
28
|
+
|
|
29
|
+
const { root = null, rootMargin, threshold, once = true, onEnter, ...animationConfig } = options;
|
|
30
|
+
|
|
31
|
+
if (typeof IntersectionObserver === 'undefined') {
|
|
32
|
+
targets.forEach((element) => {
|
|
33
|
+
onEnter?.(element);
|
|
34
|
+
void animate(element, animationConfig);
|
|
35
|
+
});
|
|
36
|
+
return () => undefined;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const observer = new IntersectionObserver(
|
|
40
|
+
(entries) => {
|
|
41
|
+
entries.forEach((entry) => {
|
|
42
|
+
if (!entry.isIntersecting) return;
|
|
43
|
+
const element = entry.target as Element;
|
|
44
|
+
onEnter?.(element);
|
|
45
|
+
void animate(element, animationConfig);
|
|
46
|
+
if (once) {
|
|
47
|
+
observer.unobserve(element);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
},
|
|
51
|
+
{ root, rootMargin, threshold }
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
targets.forEach((element) => observer.observe(element));
|
|
55
|
+
|
|
56
|
+
return () => observer.disconnect();
|
|
57
|
+
};
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spring physics helpers.
|
|
3
|
+
*
|
|
4
|
+
* @module bquery/motion
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { Spring, SpringConfig } from './types';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Default spring configuration values.
|
|
11
|
+
*/
|
|
12
|
+
const DEFAULT_SPRING_CONFIG: Required<SpringConfig> = {
|
|
13
|
+
stiffness: 100,
|
|
14
|
+
damping: 10,
|
|
15
|
+
mass: 1,
|
|
16
|
+
precision: 0.01,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Create a spring-based animation for smooth, physics-based motion.
|
|
21
|
+
*
|
|
22
|
+
* Uses variable frame rate timing based on `requestAnimationFrame` timestamps
|
|
23
|
+
* to ensure consistent animation speed across different devices and frame rates.
|
|
24
|
+
* Large time deltas (e.g., from tab backgrounding) are clamped to maintain
|
|
25
|
+
* simulation stability.
|
|
26
|
+
*
|
|
27
|
+
* @param initialValue - Starting value for the spring
|
|
28
|
+
* @param config - Spring physics configuration
|
|
29
|
+
* @returns Spring instance for controlling the animation
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```ts
|
|
33
|
+
* const x = spring(0, { stiffness: 120, damping: 14 });
|
|
34
|
+
* x.onChange((value) => {
|
|
35
|
+
* element.style.transform = `translateX(${value}px)`;
|
|
36
|
+
* });
|
|
37
|
+
* await x.to(100);
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export const spring = (initialValue: number, config: SpringConfig = {}): Spring => {
|
|
41
|
+
const { stiffness, damping, mass, precision } = {
|
|
42
|
+
...DEFAULT_SPRING_CONFIG,
|
|
43
|
+
...config,
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
let current = initialValue;
|
|
47
|
+
let velocity = 0;
|
|
48
|
+
let target = initialValue;
|
|
49
|
+
let animationFrame: number | null = null;
|
|
50
|
+
let resolvePromise: (() => void) | null = null;
|
|
51
|
+
let lastTime: number | null = null;
|
|
52
|
+
const listeners = new Set<(value: number) => void>();
|
|
53
|
+
|
|
54
|
+
const notifyListeners = () => {
|
|
55
|
+
for (const listener of listeners) {
|
|
56
|
+
listener(current);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const step = (timestamp: number) => {
|
|
61
|
+
// Calculate time delta (in seconds) from last frame
|
|
62
|
+
// If this is the first frame, use a sensible default (1/60s)
|
|
63
|
+
// This ensures the animation speed is independent of frame rate
|
|
64
|
+
const deltaTime = lastTime !== null ? (timestamp - lastTime) / 1000 : 1 / 60;
|
|
65
|
+
// Clamp large deltas to prevent instability (e.g. tab backgrounding)
|
|
66
|
+
// Maximum delta of 1/30s (~33ms) keeps simulation stable
|
|
67
|
+
const clampedDelta = Math.min(deltaTime, 1 / 30);
|
|
68
|
+
lastTime = timestamp;
|
|
69
|
+
|
|
70
|
+
// Spring physics calculation
|
|
71
|
+
const displacement = current - target;
|
|
72
|
+
const springForce = -stiffness * displacement;
|
|
73
|
+
const dampingForce = -damping * velocity;
|
|
74
|
+
const acceleration = (springForce + dampingForce) / mass;
|
|
75
|
+
|
|
76
|
+
velocity += acceleration * clampedDelta;
|
|
77
|
+
current += velocity * clampedDelta;
|
|
78
|
+
|
|
79
|
+
notifyListeners();
|
|
80
|
+
|
|
81
|
+
// Check if spring has settled
|
|
82
|
+
if (Math.abs(velocity) < precision && Math.abs(displacement) < precision) {
|
|
83
|
+
current = target;
|
|
84
|
+
velocity = 0;
|
|
85
|
+
animationFrame = null;
|
|
86
|
+
notifyListeners();
|
|
87
|
+
resolvePromise?.();
|
|
88
|
+
resolvePromise = null;
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
animationFrame = requestAnimationFrame(step);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
to(newTarget: number): Promise<void> {
|
|
97
|
+
target = newTarget;
|
|
98
|
+
|
|
99
|
+
if (animationFrame !== null) {
|
|
100
|
+
cancelAnimationFrame(animationFrame);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Resolve any pending promise from a previous to() call
|
|
104
|
+
// This ensures all returned promises eventually settle
|
|
105
|
+
resolvePromise?.();
|
|
106
|
+
|
|
107
|
+
// Reset lastTime to ensure clean start for new animation
|
|
108
|
+
lastTime = null;
|
|
109
|
+
|
|
110
|
+
return new Promise((resolve) => {
|
|
111
|
+
resolvePromise = resolve;
|
|
112
|
+
animationFrame = requestAnimationFrame(step);
|
|
113
|
+
});
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
current(): number {
|
|
117
|
+
return current;
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
stop(): void {
|
|
121
|
+
if (animationFrame !== null) {
|
|
122
|
+
cancelAnimationFrame(animationFrame);
|
|
123
|
+
animationFrame = null;
|
|
124
|
+
}
|
|
125
|
+
velocity = 0;
|
|
126
|
+
lastTime = null;
|
|
127
|
+
resolvePromise?.();
|
|
128
|
+
resolvePromise = null;
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
onChange(callback: (value: number) => void): () => void {
|
|
132
|
+
listeners.add(callback);
|
|
133
|
+
return () => listeners.delete(callback);
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Preset spring configurations for common use cases.
|
|
140
|
+
*/
|
|
141
|
+
export const springPresets = {
|
|
142
|
+
/** Gentle, slow-settling spring */
|
|
143
|
+
gentle: { stiffness: 80, damping: 15 } as SpringConfig,
|
|
144
|
+
/** Responsive, snappy spring */
|
|
145
|
+
snappy: { stiffness: 200, damping: 20 } as SpringConfig,
|
|
146
|
+
/** Bouncy, playful spring */
|
|
147
|
+
bouncy: { stiffness: 300, damping: 8 } as SpringConfig,
|
|
148
|
+
/** Stiff, quick spring with minimal overshoot */
|
|
149
|
+
stiff: { stiffness: 400, damping: 30 } as SpringConfig,
|
|
150
|
+
};
|