@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.
Files changed (305) hide show
  1. package/README.md +501 -427
  2. package/dist/batch-4LAvfLE7.js +13 -0
  3. package/dist/batch-4LAvfLE7.js.map +1 -0
  4. package/dist/component/component.d.ts +69 -0
  5. package/dist/component/component.d.ts.map +1 -0
  6. package/dist/component/html.d.ts +35 -0
  7. package/dist/component/html.d.ts.map +1 -0
  8. package/dist/component/index.d.ts +3 -126
  9. package/dist/component/index.d.ts.map +1 -1
  10. package/dist/component/props.d.ts +18 -0
  11. package/dist/component/props.d.ts.map +1 -0
  12. package/dist/component/types.d.ts +77 -0
  13. package/dist/component/types.d.ts.map +1 -0
  14. package/dist/component.es.mjs +90 -59
  15. package/dist/component.es.mjs.map +1 -1
  16. package/dist/core/collection.d.ts +36 -0
  17. package/dist/core/collection.d.ts.map +1 -1
  18. package/dist/core/dom.d.ts +6 -0
  19. package/dist/core/dom.d.ts.map +1 -0
  20. package/dist/core/element.d.ts +8 -0
  21. package/dist/core/element.d.ts.map +1 -1
  22. package/dist/core/index.d.ts +1 -0
  23. package/dist/core/index.d.ts.map +1 -1
  24. package/dist/core/utils/array.d.ts +74 -0
  25. package/dist/core/utils/array.d.ts.map +1 -0
  26. package/dist/core/utils/function.d.ts +70 -0
  27. package/dist/core/utils/function.d.ts.map +1 -0
  28. package/dist/core/utils/index.d.ts +70 -0
  29. package/dist/core/utils/index.d.ts.map +1 -0
  30. package/dist/core/utils/misc.d.ts +63 -0
  31. package/dist/core/utils/misc.d.ts.map +1 -0
  32. package/dist/core/utils/number.d.ts +65 -0
  33. package/dist/core/utils/number.d.ts.map +1 -0
  34. package/dist/core/utils/object.d.ts +133 -0
  35. package/dist/core/utils/object.d.ts.map +1 -0
  36. package/dist/core/utils/string.d.ts +80 -0
  37. package/dist/core/utils/string.d.ts.map +1 -0
  38. package/dist/core/utils/type-guards.d.ts +79 -0
  39. package/dist/core/utils/type-guards.d.ts.map +1 -0
  40. package/dist/core-COenAZjD.js +145 -0
  41. package/dist/core-COenAZjD.js.map +1 -0
  42. package/dist/core.es.mjs +411 -448
  43. package/dist/core.es.mjs.map +1 -1
  44. package/dist/full.d.ts +2 -2
  45. package/dist/full.d.ts.map +1 -1
  46. package/dist/full.es.mjs +87 -64
  47. package/dist/full.es.mjs.map +1 -1
  48. package/dist/full.iife.js +2 -2
  49. package/dist/full.iife.js.map +1 -1
  50. package/dist/full.umd.js +2 -2
  51. package/dist/full.umd.js.map +1 -1
  52. package/dist/index.es.mjs +138 -68
  53. package/dist/index.es.mjs.map +1 -1
  54. package/dist/motion/animate.d.ts +25 -0
  55. package/dist/motion/animate.d.ts.map +1 -0
  56. package/dist/motion/easing.d.ts +30 -0
  57. package/dist/motion/easing.d.ts.map +1 -0
  58. package/dist/motion/flip.d.ts +55 -0
  59. package/dist/motion/flip.d.ts.map +1 -0
  60. package/dist/motion/index.d.ts +11 -138
  61. package/dist/motion/index.d.ts.map +1 -1
  62. package/dist/motion/keyframes.d.ts +21 -0
  63. package/dist/motion/keyframes.d.ts.map +1 -0
  64. package/dist/motion/reduced-motion.d.ts +12 -0
  65. package/dist/motion/reduced-motion.d.ts.map +1 -0
  66. package/dist/motion/scroll.d.ts +15 -0
  67. package/dist/motion/scroll.d.ts.map +1 -0
  68. package/dist/motion/spring.d.ts +42 -0
  69. package/dist/motion/spring.d.ts.map +1 -0
  70. package/dist/motion/stagger.d.ts +22 -0
  71. package/dist/motion/stagger.d.ts.map +1 -0
  72. package/dist/motion/timeline.d.ts +21 -0
  73. package/dist/motion/timeline.d.ts.map +1 -0
  74. package/dist/motion/transition.d.ts +22 -0
  75. package/dist/motion/transition.d.ts.map +1 -0
  76. package/dist/motion/types.d.ts +182 -0
  77. package/dist/motion/types.d.ts.map +1 -0
  78. package/dist/motion.es.mjs +320 -61
  79. package/dist/motion.es.mjs.map +1 -1
  80. package/dist/persisted-Dz_ryNuC.js +278 -0
  81. package/dist/persisted-Dz_ryNuC.js.map +1 -0
  82. package/dist/reactive/batch.d.ts +13 -0
  83. package/dist/reactive/batch.d.ts.map +1 -0
  84. package/dist/reactive/computed.d.ts +50 -0
  85. package/dist/reactive/computed.d.ts.map +1 -0
  86. package/dist/reactive/core.d.ts +60 -0
  87. package/dist/reactive/core.d.ts.map +1 -0
  88. package/dist/reactive/effect.d.ts +15 -0
  89. package/dist/reactive/effect.d.ts.map +1 -0
  90. package/dist/reactive/index.d.ts +2 -2
  91. package/dist/reactive/index.d.ts.map +1 -1
  92. package/dist/reactive/internals.d.ts +36 -0
  93. package/dist/reactive/internals.d.ts.map +1 -0
  94. package/dist/reactive/linked.d.ts +36 -0
  95. package/dist/reactive/linked.d.ts.map +1 -0
  96. package/dist/reactive/persisted.d.ts +14 -0
  97. package/dist/reactive/persisted.d.ts.map +1 -0
  98. package/dist/reactive/readonly.d.ts +26 -0
  99. package/dist/reactive/readonly.d.ts.map +1 -0
  100. package/dist/reactive/signal.d.ts +13 -312
  101. package/dist/reactive/signal.d.ts.map +1 -1
  102. package/dist/reactive/type-guards.d.ts +20 -0
  103. package/dist/reactive/type-guards.d.ts.map +1 -0
  104. package/dist/reactive/untrack.d.ts +29 -0
  105. package/dist/reactive/untrack.d.ts.map +1 -0
  106. package/dist/reactive/watch.d.ts +42 -0
  107. package/dist/reactive/watch.d.ts.map +1 -0
  108. package/dist/reactive.es.mjs +30 -163
  109. package/dist/reactive.es.mjs.map +1 -1
  110. package/dist/router/index.d.ts +6 -252
  111. package/dist/router/index.d.ts.map +1 -1
  112. package/dist/router/links.d.ts +44 -0
  113. package/dist/router/links.d.ts.map +1 -0
  114. package/dist/router/match.d.ts +20 -0
  115. package/dist/router/match.d.ts.map +1 -0
  116. package/dist/router/navigation.d.ts +45 -0
  117. package/dist/router/navigation.d.ts.map +1 -0
  118. package/dist/router/query.d.ts +16 -0
  119. package/dist/router/query.d.ts.map +1 -0
  120. package/dist/router/router.d.ts +34 -0
  121. package/dist/router/router.d.ts.map +1 -0
  122. package/dist/router/state.d.ts +27 -0
  123. package/dist/router/state.d.ts.map +1 -0
  124. package/dist/router/types.d.ts +88 -0
  125. package/dist/router/types.d.ts.map +1 -0
  126. package/dist/router/utils.d.ts +65 -0
  127. package/dist/router/utils.d.ts.map +1 -0
  128. package/dist/router.es.mjs +168 -132
  129. package/dist/router.es.mjs.map +1 -1
  130. package/dist/sanitize-1FBEPAFH.js +272 -0
  131. package/dist/sanitize-1FBEPAFH.js.map +1 -0
  132. package/dist/security/constants.d.ts +42 -0
  133. package/dist/security/constants.d.ts.map +1 -0
  134. package/dist/security/csp.d.ts +24 -0
  135. package/dist/security/csp.d.ts.map +1 -0
  136. package/dist/security/index.d.ts +4 -2
  137. package/dist/security/index.d.ts.map +1 -1
  138. package/dist/security/sanitize-core.d.ts +13 -0
  139. package/dist/security/sanitize-core.d.ts.map +1 -0
  140. package/dist/security/sanitize.d.ts +5 -57
  141. package/dist/security/sanitize.d.ts.map +1 -1
  142. package/dist/security/trusted-types.d.ts +25 -0
  143. package/dist/security/trusted-types.d.ts.map +1 -0
  144. package/dist/security/types.d.ts +36 -0
  145. package/dist/security/types.d.ts.map +1 -0
  146. package/dist/security.es.mjs +50 -277
  147. package/dist/security.es.mjs.map +1 -1
  148. package/dist/store/create-store.d.ts +15 -0
  149. package/dist/store/create-store.d.ts.map +1 -0
  150. package/dist/store/define-store.d.ts +28 -0
  151. package/dist/store/define-store.d.ts.map +1 -0
  152. package/dist/store/devtools.d.ts +22 -0
  153. package/dist/store/devtools.d.ts.map +1 -0
  154. package/dist/store/index.d.ts +10 -286
  155. package/dist/store/index.d.ts.map +1 -1
  156. package/dist/store/mapping.d.ts +28 -0
  157. package/dist/store/mapping.d.ts.map +1 -0
  158. package/dist/store/persisted.d.ts +13 -0
  159. package/dist/store/persisted.d.ts.map +1 -0
  160. package/dist/store/plugins.d.ts +13 -0
  161. package/dist/store/plugins.d.ts.map +1 -0
  162. package/dist/store/registry.d.ts +28 -0
  163. package/dist/store/registry.d.ts.map +1 -0
  164. package/dist/store/types.d.ts +71 -0
  165. package/dist/store/types.d.ts.map +1 -0
  166. package/dist/store/utils.d.ts +28 -0
  167. package/dist/store/utils.d.ts.map +1 -0
  168. package/dist/store/watch.d.ts +23 -0
  169. package/dist/store/watch.d.ts.map +1 -0
  170. package/dist/store.es.mjs +22 -224
  171. package/dist/store.es.mjs.map +1 -1
  172. package/dist/type-guards-DRma3-Kc.js +16 -0
  173. package/dist/type-guards-DRma3-Kc.js.map +1 -0
  174. package/dist/untrack-BuEQKH7_.js +6 -0
  175. package/dist/untrack-BuEQKH7_.js.map +1 -0
  176. package/dist/view/directives/bind.d.ts +7 -0
  177. package/dist/view/directives/bind.d.ts.map +1 -0
  178. package/dist/view/directives/class.d.ts +8 -0
  179. package/dist/view/directives/class.d.ts.map +1 -0
  180. package/dist/view/directives/for.d.ts +23 -0
  181. package/dist/view/directives/for.d.ts.map +1 -0
  182. package/dist/view/directives/html.d.ts +7 -0
  183. package/dist/view/directives/html.d.ts.map +1 -0
  184. package/dist/view/directives/if.d.ts +7 -0
  185. package/dist/view/directives/if.d.ts.map +1 -0
  186. package/dist/view/directives/index.d.ts +12 -0
  187. package/dist/view/directives/index.d.ts.map +1 -0
  188. package/dist/view/directives/model.d.ts +7 -0
  189. package/dist/view/directives/model.d.ts.map +1 -0
  190. package/dist/view/directives/on.d.ts +7 -0
  191. package/dist/view/directives/on.d.ts.map +1 -0
  192. package/dist/view/directives/ref.d.ts +7 -0
  193. package/dist/view/directives/ref.d.ts.map +1 -0
  194. package/dist/view/directives/show.d.ts +7 -0
  195. package/dist/view/directives/show.d.ts.map +1 -0
  196. package/dist/view/directives/style.d.ts +7 -0
  197. package/dist/view/directives/style.d.ts.map +1 -0
  198. package/dist/view/directives/text.d.ts +7 -0
  199. package/dist/view/directives/text.d.ts.map +1 -0
  200. package/dist/view/evaluate.d.ts +43 -0
  201. package/dist/view/evaluate.d.ts.map +1 -0
  202. package/dist/view/index.d.ts +3 -93
  203. package/dist/view/index.d.ts.map +1 -1
  204. package/dist/view/mount.d.ts +69 -0
  205. package/dist/view/mount.d.ts.map +1 -0
  206. package/dist/view/process.d.ts +26 -0
  207. package/dist/view/process.d.ts.map +1 -0
  208. package/dist/view/types.d.ts +36 -0
  209. package/dist/view/types.d.ts.map +1 -0
  210. package/dist/view.es.mjs +368 -267
  211. package/dist/view.es.mjs.map +1 -1
  212. package/dist/watch-CXyaBC_9.js +58 -0
  213. package/dist/watch-CXyaBC_9.js.map +1 -0
  214. package/package.json +132 -132
  215. package/src/component/component.ts +289 -0
  216. package/src/component/html.ts +53 -0
  217. package/src/component/index.ts +40 -414
  218. package/src/component/props.ts +116 -0
  219. package/src/component/types.ts +85 -0
  220. package/src/core/collection.ts +588 -454
  221. package/src/core/dom.ts +38 -0
  222. package/src/core/element.ts +746 -740
  223. package/src/core/index.ts +43 -0
  224. package/src/core/utils/array.ts +102 -0
  225. package/src/core/utils/function.ts +110 -0
  226. package/src/core/utils/index.ts +83 -0
  227. package/src/core/utils/misc.ts +82 -0
  228. package/src/core/utils/number.ts +78 -0
  229. package/src/core/utils/object.ts +206 -0
  230. package/src/core/utils/string.ts +112 -0
  231. package/src/core/utils/type-guards.ts +112 -0
  232. package/src/full.ts +187 -150
  233. package/src/index.ts +36 -36
  234. package/src/motion/animate.ts +113 -0
  235. package/src/motion/easing.ts +40 -0
  236. package/src/motion/flip.ts +176 -0
  237. package/src/motion/index.ts +41 -358
  238. package/src/motion/keyframes.ts +46 -0
  239. package/src/motion/reduced-motion.ts +17 -0
  240. package/src/motion/scroll.ts +57 -0
  241. package/src/motion/spring.ts +150 -0
  242. package/src/motion/stagger.ts +43 -0
  243. package/src/motion/timeline.ts +246 -0
  244. package/src/motion/transition.ts +51 -0
  245. package/src/motion/types.ts +198 -0
  246. package/src/reactive/batch.ts +22 -0
  247. package/src/reactive/computed.ts +92 -0
  248. package/src/reactive/core.ts +93 -0
  249. package/src/reactive/effect.ts +43 -0
  250. package/src/reactive/index.ts +23 -22
  251. package/src/reactive/internals.ts +105 -0
  252. package/src/reactive/linked.ts +56 -0
  253. package/src/reactive/persisted.ts +74 -0
  254. package/src/reactive/readonly.ts +35 -0
  255. package/src/reactive/signal.ts +20 -520
  256. package/src/reactive/type-guards.ts +22 -0
  257. package/src/reactive/untrack.ts +31 -0
  258. package/src/reactive/watch.ts +73 -0
  259. package/src/router/index.ts +41 -718
  260. package/src/router/links.ts +130 -0
  261. package/src/router/match.ts +106 -0
  262. package/src/router/navigation.ts +71 -0
  263. package/src/router/query.ts +35 -0
  264. package/src/router/router.ts +211 -0
  265. package/src/router/state.ts +46 -0
  266. package/src/router/types.ts +93 -0
  267. package/src/router/utils.ts +116 -0
  268. package/src/security/constants.ts +209 -0
  269. package/src/security/csp.ts +77 -0
  270. package/src/security/index.ts +4 -12
  271. package/src/security/sanitize-core.ts +343 -0
  272. package/src/security/sanitize.ts +66 -625
  273. package/src/security/trusted-types.ts +69 -0
  274. package/src/security/types.ts +40 -0
  275. package/src/store/create-store.ts +329 -0
  276. package/src/store/define-store.ts +48 -0
  277. package/src/store/devtools.ts +45 -0
  278. package/src/store/index.ts +22 -848
  279. package/src/store/mapping.ts +73 -0
  280. package/src/store/persisted.ts +61 -0
  281. package/src/store/plugins.ts +32 -0
  282. package/src/store/registry.ts +51 -0
  283. package/src/store/types.ts +94 -0
  284. package/src/store/utils.ts +141 -0
  285. package/src/store/watch.ts +52 -0
  286. package/src/view/directives/bind.ts +23 -0
  287. package/src/view/directives/class.ts +70 -0
  288. package/src/view/directives/for.ts +275 -0
  289. package/src/view/directives/html.ts +19 -0
  290. package/src/view/directives/if.ts +30 -0
  291. package/src/view/directives/index.ts +11 -0
  292. package/src/view/directives/model.ts +56 -0
  293. package/src/view/directives/on.ts +41 -0
  294. package/src/view/directives/ref.ts +41 -0
  295. package/src/view/directives/show.ts +26 -0
  296. package/src/view/directives/style.ts +47 -0
  297. package/src/view/directives/text.ts +15 -0
  298. package/src/view/evaluate.ts +274 -0
  299. package/src/view/index.ts +112 -1041
  300. package/src/view/mount.ts +200 -0
  301. package/src/view/process.ts +92 -0
  302. package/src/view/types.ts +44 -0
  303. package/dist/core/utils.d.ts +0 -313
  304. package/dist/core/utils.d.ts.map +0 -1
  305. package/src/core/utils.ts +0 -444
@@ -5,361 +5,44 @@
5
5
  * @module bquery/motion
6
6
  */
7
7
 
8
- // ============================================================================
9
- // Types
10
- // ============================================================================
11
-
12
- /**
13
- * Options for view transitions.
14
- */
15
- export interface TransitionOptions {
16
- /** The DOM update function to execute during transition */
17
- update: () => void;
18
- }
19
-
20
- /**
21
- * Captured element bounds for FLIP animations.
22
- */
23
- export interface ElementBounds {
24
- top: number;
25
- left: number;
26
- width: number;
27
- height: number;
28
- }
29
-
30
- /**
31
- * FLIP animation configuration options.
32
- */
33
- export interface FlipOptions {
34
- /** Animation duration in milliseconds */
35
- duration?: number;
36
- /** CSS easing function */
37
- easing?: string;
38
- /** Callback when animation completes */
39
- onComplete?: () => void;
40
- }
41
-
42
- /**
43
- * Spring physics configuration.
44
- */
45
- export interface SpringConfig {
46
- /** Spring stiffness (default: 100) */
47
- stiffness?: number;
48
- /** Damping coefficient (default: 10) */
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
+ };