@bquery/bquery 1.4.0 → 1.6.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 (164) hide show
  1. package/README.md +586 -527
  2. package/dist/component/component.d.ts +13 -5
  3. package/dist/component/component.d.ts.map +1 -1
  4. package/dist/component/html.d.ts +40 -3
  5. package/dist/component/html.d.ts.map +1 -1
  6. package/dist/component/index.d.ts +4 -2
  7. package/dist/component/index.d.ts.map +1 -1
  8. package/dist/component/library.d.ts +34 -0
  9. package/dist/component/library.d.ts.map +1 -0
  10. package/dist/component/types.d.ts +132 -13
  11. package/dist/component/types.d.ts.map +1 -1
  12. package/dist/component-BEQgt5hl.js +600 -0
  13. package/dist/component-BEQgt5hl.js.map +1 -0
  14. package/dist/component.es.mjs +7 -184
  15. package/dist/config-DRmZZno3.js +40 -0
  16. package/dist/config-DRmZZno3.js.map +1 -0
  17. package/dist/core-BGQJVw0-.js +35 -0
  18. package/dist/core-BGQJVw0-.js.map +1 -0
  19. package/dist/core-CCEabVHl.js +648 -0
  20. package/dist/core-CCEabVHl.js.map +1 -0
  21. package/dist/core.es.mjs +45 -1261
  22. package/dist/effect-AFRW_Plg.js +84 -0
  23. package/dist/effect-AFRW_Plg.js.map +1 -0
  24. package/dist/full.d.ts +8 -8
  25. package/dist/full.d.ts.map +1 -1
  26. package/dist/full.es.mjs +101 -91
  27. package/dist/full.iife.js +173 -3
  28. package/dist/full.iife.js.map +1 -1
  29. package/dist/full.umd.js +173 -3
  30. package/dist/full.umd.js.map +1 -1
  31. package/dist/index.es.mjs +147 -139
  32. package/dist/motion/transition.d.ts +1 -1
  33. package/dist/motion/transition.d.ts.map +1 -1
  34. package/dist/motion/types.d.ts +11 -1
  35. package/dist/motion/types.d.ts.map +1 -1
  36. package/dist/motion-D9TcHxOF.js +415 -0
  37. package/dist/motion-D9TcHxOF.js.map +1 -0
  38. package/dist/motion.es.mjs +25 -361
  39. package/dist/object-qGpWr6-J.js +38 -0
  40. package/dist/object-qGpWr6-J.js.map +1 -0
  41. package/dist/platform/announcer.d.ts +59 -0
  42. package/dist/platform/announcer.d.ts.map +1 -0
  43. package/dist/platform/config.d.ts +92 -0
  44. package/dist/platform/config.d.ts.map +1 -0
  45. package/dist/platform/cookies.d.ts +45 -0
  46. package/dist/platform/cookies.d.ts.map +1 -0
  47. package/dist/platform/index.d.ts +8 -0
  48. package/dist/platform/index.d.ts.map +1 -1
  49. package/dist/platform/meta.d.ts +62 -0
  50. package/dist/platform/meta.d.ts.map +1 -0
  51. package/dist/platform-Dr9b6fsq.js +362 -0
  52. package/dist/platform-Dr9b6fsq.js.map +1 -0
  53. package/dist/platform.es.mjs +11 -248
  54. package/dist/reactive/async-data.d.ts +114 -0
  55. package/dist/reactive/async-data.d.ts.map +1 -0
  56. package/dist/reactive/index.d.ts +2 -2
  57. package/dist/reactive/index.d.ts.map +1 -1
  58. package/dist/reactive/signal.d.ts +2 -0
  59. package/dist/reactive/signal.d.ts.map +1 -1
  60. package/dist/reactive-DSkct0dO.js +254 -0
  61. package/dist/reactive-DSkct0dO.js.map +1 -0
  62. package/dist/reactive.es.mjs +18 -32
  63. package/dist/router-CbDhl8rS.js +188 -0
  64. package/dist/router-CbDhl8rS.js.map +1 -0
  65. package/dist/router.es.mjs +11 -200
  66. package/dist/sanitize-Bs2dkMby.js +313 -0
  67. package/dist/sanitize-Bs2dkMby.js.map +1 -0
  68. package/dist/security/constants.d.ts.map +1 -1
  69. package/dist/security/index.d.ts +4 -2
  70. package/dist/security/index.d.ts.map +1 -1
  71. package/dist/security/sanitize.d.ts +4 -1
  72. package/dist/security/sanitize.d.ts.map +1 -1
  73. package/dist/security/trusted-html.d.ts +53 -0
  74. package/dist/security/trusted-html.d.ts.map +1 -0
  75. package/dist/security.es.mjs +11 -56
  76. package/dist/store/define-store.d.ts +1 -1
  77. package/dist/store/define-store.d.ts.map +1 -1
  78. package/dist/store/mapping.d.ts +1 -1
  79. package/dist/store/mapping.d.ts.map +1 -1
  80. package/dist/store/persisted.d.ts +1 -1
  81. package/dist/store/persisted.d.ts.map +1 -1
  82. package/dist/store/types.d.ts +2 -2
  83. package/dist/store/types.d.ts.map +1 -1
  84. package/dist/store/watch.d.ts +1 -1
  85. package/dist/store/watch.d.ts.map +1 -1
  86. package/dist/store-BwDvI45q.js +263 -0
  87. package/dist/store-BwDvI45q.js.map +1 -0
  88. package/dist/store.es.mjs +12 -25
  89. package/dist/storybook/index.d.ts +37 -0
  90. package/dist/storybook/index.d.ts.map +1 -0
  91. package/dist/storybook.es.mjs +151 -0
  92. package/dist/storybook.es.mjs.map +1 -0
  93. package/dist/untrack-B0rVscTc.js +7 -0
  94. package/dist/untrack-B0rVscTc.js.map +1 -0
  95. package/dist/view-C70lA3vf.js +397 -0
  96. package/dist/view-C70lA3vf.js.map +1 -0
  97. package/dist/view.es.mjs +11 -430
  98. package/package.json +141 -132
  99. package/src/component/component.ts +524 -289
  100. package/src/component/html.ts +153 -53
  101. package/src/component/index.ts +50 -40
  102. package/src/component/library.ts +518 -0
  103. package/src/component/types.ts +256 -85
  104. package/src/core/collection.ts +628 -628
  105. package/src/core/element.ts +774 -774
  106. package/src/core/index.ts +48 -48
  107. package/src/core/utils/function.ts +151 -151
  108. package/src/full.ts +229 -187
  109. package/src/motion/animate.ts +113 -113
  110. package/src/motion/flip.ts +176 -176
  111. package/src/motion/scroll.ts +57 -57
  112. package/src/motion/spring.ts +150 -150
  113. package/src/motion/timeline.ts +246 -246
  114. package/src/motion/transition.ts +97 -51
  115. package/src/motion/types.ts +11 -1
  116. package/src/platform/announcer.ts +208 -0
  117. package/src/platform/config.ts +163 -0
  118. package/src/platform/cookies.ts +165 -0
  119. package/src/platform/index.ts +21 -0
  120. package/src/platform/meta.ts +168 -0
  121. package/src/platform/storage.ts +215 -215
  122. package/src/reactive/async-data.ts +486 -0
  123. package/src/reactive/core.ts +114 -114
  124. package/src/reactive/effect.ts +54 -54
  125. package/src/reactive/index.ts +15 -1
  126. package/src/reactive/internals.ts +122 -122
  127. package/src/reactive/signal.ts +9 -0
  128. package/src/security/constants.ts +3 -1
  129. package/src/security/index.ts +17 -10
  130. package/src/security/sanitize-core.ts +364 -364
  131. package/src/security/sanitize.ts +70 -66
  132. package/src/security/trusted-html.ts +71 -0
  133. package/src/store/define-store.ts +49 -48
  134. package/src/store/mapping.ts +74 -73
  135. package/src/store/persisted.ts +62 -61
  136. package/src/store/types.ts +92 -94
  137. package/src/store/watch.ts +53 -52
  138. package/src/storybook/index.ts +479 -0
  139. package/src/view/evaluate.ts +290 -290
  140. package/dist/batch-x7b2eZST.js +0 -13
  141. package/dist/batch-x7b2eZST.js.map +0 -1
  142. package/dist/component.es.mjs.map +0 -1
  143. package/dist/core-BhpuvPhy.js +0 -170
  144. package/dist/core-BhpuvPhy.js.map +0 -1
  145. package/dist/core.es.mjs.map +0 -1
  146. package/dist/full.es.mjs.map +0 -1
  147. package/dist/index.es.mjs.map +0 -1
  148. package/dist/motion.es.mjs.map +0 -1
  149. package/dist/persisted-DHoi3uEs.js +0 -278
  150. package/dist/persisted-DHoi3uEs.js.map +0 -1
  151. package/dist/platform.es.mjs.map +0 -1
  152. package/dist/reactive.es.mjs.map +0 -1
  153. package/dist/router.es.mjs.map +0 -1
  154. package/dist/sanitize-Cxvxa-DX.js +0 -283
  155. package/dist/sanitize-Cxvxa-DX.js.map +0 -1
  156. package/dist/security.es.mjs.map +0 -1
  157. package/dist/store.es.mjs.map +0 -1
  158. package/dist/type-guards-BdKlYYlS.js +0 -32
  159. package/dist/type-guards-BdKlYYlS.js.map +0 -1
  160. package/dist/untrack-DNnnqdlR.js +0 -6
  161. package/dist/untrack-DNnnqdlR.js.map +0 -1
  162. package/dist/view.es.mjs.map +0 -1
  163. package/dist/watch-DXXv3iAI.js +0 -58
  164. package/dist/watch-DXXv3iAI.js.map +0 -1
package/src/full.ts CHANGED
@@ -1,187 +1,229 @@
1
- /**
2
- * bQuery.js — Full Bundle
3
- *
4
- * This is the complete bundle containing all modules for CDN usage.
5
- * Use this when you want all features without tree-shaking concerns.
6
- *
7
- * @module bquery/full
8
- *
9
- * @example CDN Usage (ES Modules)
10
- * ```html
11
- * <script type="module">
12
- * import { $, signal, component } from 'https://unpkg.com/bquery@1/dist/full.es.mjs';
13
- *
14
- * const count = signal(0);
15
- * $('#counter').text(count.value);
16
- * </script>
17
- * ```
18
- *
19
- * @example CDN Usage (UMD/Global)
20
- * ```html
21
- * <script src="https://unpkg.com/bquery@1/dist/full.umd.js"></script>
22
- * <script>
23
- * const { $, signal } = bQuery;
24
- * const count = signal(0);
25
- * </script>
26
- * ```
27
- *
28
- * @example CDN Usage (IIFE)
29
- * ```html
30
- * <script src="https://unpkg.com/bquery@1/dist/full.iife.js"></script>
31
- * <script>
32
- * // bQuery is available as a global variable
33
- * const { $, $$ } = bQuery;
34
- * </script>
35
- * ```
36
- */
37
-
38
- // ============================================================================
39
- // Core Module: Selectors, DOM operations, events, utilities
40
- // ============================================================================
41
- export { $, $$, BQueryCollection, BQueryElement, utils } from './core/index';
42
-
43
- // ============================================================================
44
- // Reactive Module: Signals, computed values, effects, batching
45
- // ============================================================================
46
- export {
47
- Computed,
48
- Signal,
49
- batch,
50
- computed,
51
- effect,
52
- isComputed,
53
- isSignal,
54
- persistedSignal,
55
- readonly,
56
- signal,
57
- untrack,
58
- watch,
59
- } from './reactive/index';
60
- export type { CleanupFn, Observer, ReadonlySignal } from './reactive/index';
61
-
62
- // ============================================================================
63
- // Component Module: Web Components helper with Shadow DOM
64
- // ============================================================================
65
- export { component, html, safeHtml } from './component/index';
66
- export type { ComponentDefinition, PropDefinition } from './component/index';
67
-
68
- // ============================================================================
69
- // Motion Module: View transitions, FLIP animations, springs
70
- // ============================================================================
71
- export {
72
- animate,
73
- capturePosition,
74
- easeInCubic,
75
- easeInOutCubic,
76
- easeInOutQuad,
77
- easeInQuad,
78
- easeOutBack,
79
- easeOutCubic,
80
- easeOutExpo,
81
- easeOutQuad,
82
- easingPresets,
83
- flip,
84
- flipElements,
85
- flipList,
86
- keyframePresets,
87
- linear,
88
- prefersReducedMotion,
89
- scrollAnimate,
90
- sequence,
91
- spring,
92
- springPresets,
93
- stagger,
94
- timeline,
95
- transition,
96
- } from './motion/index';
97
- export type {
98
- AnimateOptions,
99
- EasingFunction,
100
- ElementBounds,
101
- FlipGroupOptions,
102
- FlipOptions,
103
- ScrollAnimateCleanup,
104
- ScrollAnimateOptions,
105
- SequenceOptions,
106
- SequenceStep,
107
- Spring,
108
- SpringConfig,
109
- StaggerFunction,
110
- StaggerOptions,
111
- TimelineConfig,
112
- TimelineControls,
113
- TimelineStep,
114
- TransitionOptions,
115
- } from './motion/index';
116
-
117
- // ============================================================================
118
- // Security Module: Sanitization, CSP compatibility, Trusted Types
119
- // ============================================================================
120
- export {
121
- createTrustedHtml,
122
- escapeHtml,
123
- generateNonce,
124
- getTrustedTypesPolicy,
125
- hasCSPDirective,
126
- isTrustedTypesSupported,
127
- sanitize,
128
- sanitizeHtml,
129
- stripTags,
130
- } from './security/index';
131
- export type { SanitizeOptions } from './security/index';
132
-
133
- // ============================================================================
134
- // Platform Module: Storage, buckets, notifications, cache
135
- // ============================================================================
136
- export { buckets, cache, notifications, storage } from './platform/index';
137
- export type {
138
- Bucket,
139
- CacheHandle,
140
- IndexedDBOptions,
141
- NotificationOptions,
142
- StorageAdapter,
143
- } from './platform/index';
144
-
145
- // ============================================================================
146
- // Router Module: SPA routing, navigation guards, lazy loading
147
- // ============================================================================
148
- export {
149
- back,
150
- createRouter,
151
- currentRoute,
152
- forward,
153
- interceptLinks,
154
- isActive,
155
- isActiveSignal,
156
- link,
157
- navigate,
158
- resolve,
159
- } from './router/index';
160
- export type {
161
- NavigationGuard,
162
- Route,
163
- RouteDefinition,
164
- Router,
165
- RouterOptions,
166
- } from './router/index';
167
-
168
- // ============================================================================
169
- // Store Module: Signal-based state management
170
- // ============================================================================
171
- export {
172
- createPersistedStore,
173
- createStore,
174
- destroyStore,
175
- getStore,
176
- listStores,
177
- mapActions,
178
- mapState,
179
- registerPlugin,
180
- } from './store/index';
181
- export type { StateFactory, Store, StoreDefinition, StorePlugin } from './store/index';
182
-
183
- // ============================================================================
184
- // View Module: Declarative DOM bindings without compiler
185
- // ============================================================================
186
- export { createTemplate, mount } from './view/index';
187
- export type { BindingContext, MountOptions, View } from './view/index';
1
+ /**
2
+ * bQuery.js — Full Bundle
3
+ *
4
+ * This is the complete bundle containing all modules for CDN usage.
5
+ * Use this when you want all features without tree-shaking concerns.
6
+ *
7
+ * @module bquery/full
8
+ *
9
+ * @example CDN Usage (ES Modules)
10
+ * ```html
11
+ * <script type="module">
12
+ * import { $, signal, component } from 'https://unpkg.com/bquery@1/dist/full.es.mjs';
13
+ *
14
+ * const count = signal(0);
15
+ * $('#counter').text(count.value);
16
+ * </script>
17
+ * ```
18
+ *
19
+ * @example CDN Usage (UMD/Global)
20
+ * ```html
21
+ * <script src="https://unpkg.com/bquery@1/dist/full.umd.js"></script>
22
+ * <script>
23
+ * const { $, signal } = bQuery;
24
+ * const count = signal(0);
25
+ * </script>
26
+ * ```
27
+ *
28
+ * @example CDN Usage (IIFE)
29
+ * ```html
30
+ * <script src="https://unpkg.com/bquery@1/dist/full.iife.js"></script>
31
+ * <script>
32
+ * // bQuery is available as a global variable
33
+ * const { $, $$ } = bQuery;
34
+ * </script>
35
+ * ```
36
+ */
37
+
38
+ // ============================================================================
39
+ // Core Module: Selectors, DOM operations, events, utilities
40
+ // ============================================================================
41
+ export { $, $$, BQueryCollection, BQueryElement, utils } from './core/index';
42
+
43
+ // ============================================================================
44
+ // Reactive Module: Signals, computed values, effects, batching
45
+ // ============================================================================
46
+ export {
47
+ Computed,
48
+ Signal,
49
+ batch,
50
+ computed,
51
+ createUseFetch,
52
+ effect,
53
+ isComputed,
54
+ isSignal,
55
+ linkedSignal,
56
+ persistedSignal,
57
+ readonly,
58
+ signal,
59
+ useAsyncData,
60
+ useFetch,
61
+ untrack,
62
+ watch,
63
+ } from './reactive/index';
64
+ export type {
65
+ AsyncDataState,
66
+ AsyncDataStatus,
67
+ AsyncWatchSource,
68
+ CleanupFn,
69
+ FetchInput,
70
+ LinkedSignal,
71
+ Observer,
72
+ ReadonlySignal,
73
+ UseAsyncDataOptions,
74
+ UseFetchOptions,
75
+ } from './reactive/index';
76
+
77
+ // ============================================================================
78
+ // Component Module: Web Components helper with Shadow DOM
79
+ // ============================================================================
80
+ export { bool, component, html, registerDefaultComponents, safeHtml } from './component/index';
81
+ export type {
82
+ AttributeChange,
83
+ ComponentDefinition,
84
+ ComponentRenderContext,
85
+ ComponentStateKey,
86
+ ComponentSignalLike,
87
+ ComponentSignals,
88
+ DefaultComponentLibraryOptions,
89
+ PropDefinition,
90
+ RegisteredDefaultComponents,
91
+ } from './component/index';
92
+
93
+ // ============================================================================
94
+ // Motion Module: View transitions, FLIP animations, springs
95
+ // ============================================================================
96
+ export {
97
+ animate,
98
+ capturePosition,
99
+ easeInCubic,
100
+ easeInOutCubic,
101
+ easeInOutQuad,
102
+ easeInQuad,
103
+ easeOutBack,
104
+ easeOutCubic,
105
+ easeOutExpo,
106
+ easeOutQuad,
107
+ easingPresets,
108
+ flip,
109
+ flipElements,
110
+ flipList,
111
+ keyframePresets,
112
+ linear,
113
+ prefersReducedMotion,
114
+ scrollAnimate,
115
+ sequence,
116
+ spring,
117
+ springPresets,
118
+ stagger,
119
+ timeline,
120
+ transition,
121
+ } from './motion/index';
122
+ export type {
123
+ AnimateOptions,
124
+ EasingFunction,
125
+ ElementBounds,
126
+ FlipGroupOptions,
127
+ FlipOptions,
128
+ ScrollAnimateCleanup,
129
+ ScrollAnimateOptions,
130
+ SequenceOptions,
131
+ SequenceStep,
132
+ Spring,
133
+ SpringConfig,
134
+ StaggerFunction,
135
+ StaggerOptions,
136
+ TimelineConfig,
137
+ TimelineControls,
138
+ TimelineStep,
139
+ TransitionOptions,
140
+ } from './motion/index';
141
+
142
+ // ============================================================================
143
+ // Security Module: Sanitization, CSP compatibility, Trusted Types
144
+ // ============================================================================
145
+ export {
146
+ createTrustedHtml,
147
+ escapeHtml,
148
+ generateNonce,
149
+ getTrustedTypesPolicy,
150
+ hasCSPDirective,
151
+ isTrustedTypesSupported,
152
+ sanitize,
153
+ sanitizeHtml,
154
+ stripTags,
155
+ trusted,
156
+ } from './security/index';
157
+ export type { SanitizedHtml, SanitizeOptions, TrustedHtml } from './security/index';
158
+
159
+ // ============================================================================
160
+ // Platform Module: Storage, buckets, notifications, cache
161
+ // ============================================================================
162
+ export {
163
+ buckets,
164
+ cache,
165
+ defineBqueryConfig,
166
+ definePageMeta,
167
+ getBqueryConfig,
168
+ notifications,
169
+ storage,
170
+ useAnnouncer,
171
+ useCookie,
172
+ } from './platform/index';
173
+ export type {
174
+ AnnounceOptions,
175
+ AnnouncerHandle,
176
+ Bucket,
177
+ BqueryConfig,
178
+ CacheHandle,
179
+ IndexedDBOptions,
180
+ NotificationOptions,
181
+ PageMetaDefinition,
182
+ StorageAdapter,
183
+ UseAnnouncerOptions,
184
+ UseCookieOptions,
185
+ } from './platform/index';
186
+
187
+ // ============================================================================
188
+ // Router Module: SPA routing, navigation guards, lazy loading
189
+ // ============================================================================
190
+ export {
191
+ back,
192
+ createRouter,
193
+ currentRoute,
194
+ forward,
195
+ interceptLinks,
196
+ isActive,
197
+ isActiveSignal,
198
+ link,
199
+ navigate,
200
+ resolve,
201
+ } from './router/index';
202
+ export type {
203
+ NavigationGuard,
204
+ Route,
205
+ RouteDefinition,
206
+ Router,
207
+ RouterOptions,
208
+ } from './router/index';
209
+
210
+ // ============================================================================
211
+ // Store Module: Signal-based state management
212
+ // ============================================================================
213
+ export {
214
+ createPersistedStore,
215
+ createStore,
216
+ destroyStore,
217
+ getStore,
218
+ listStores,
219
+ mapActions,
220
+ mapState,
221
+ registerPlugin,
222
+ } from './store/index';
223
+ export type { StateFactory, Store, StoreDefinition, StorePlugin } from './store/index';
224
+
225
+ // ============================================================================
226
+ // View Module: Declarative DOM bindings without compiler
227
+ // ============================================================================
228
+ export { createTemplate, mount } from './view/index';
229
+ export type { BindingContext, MountOptions, View } from './view/index';
@@ -1,113 +1,113 @@
1
- /**
2
- * Web Animations helpers.
3
- *
4
- * @module bquery/motion
5
- */
6
-
7
- import { prefersReducedMotion } from './reduced-motion';
8
- import type { AnimateOptions } from './types';
9
-
10
- /** @internal */
11
- const isStyleValue = (value: unknown): value is string | number =>
12
- typeof value === 'string' || typeof value === 'number';
13
-
14
- /**
15
- * Convert camelCase property names to kebab-case for CSS.
16
- * @internal
17
- */
18
- const toKebabCase = (str: string): string => {
19
- return str.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);
20
- };
21
-
22
- /** @internal */
23
- export const applyFinalKeyframeStyles = (
24
- element: Element,
25
- keyframes: Keyframe[] | PropertyIndexedKeyframes
26
- ): void => {
27
- const htmlElement = element as HTMLElement;
28
- const style = htmlElement.style;
29
-
30
- if (Array.isArray(keyframes)) {
31
- const last = keyframes[keyframes.length - 1];
32
- if (!last) return;
33
- for (const [prop, value] of Object.entries(last)) {
34
- if (prop === 'offset' || prop === 'easing' || prop === 'composite') continue;
35
- if (isStyleValue(value)) {
36
- // Convert camelCase to kebab-case for CSS properties
37
- const cssProp = prop.startsWith('--') ? prop : toKebabCase(prop);
38
- style.setProperty(cssProp, String(value));
39
- }
40
- }
41
- return;
42
- }
43
-
44
- for (const [prop, value] of Object.entries(keyframes)) {
45
- if (prop === 'offset' || prop === 'easing' || prop === 'composite') continue;
46
- const finalValue = Array.isArray(value) ? value[value.length - 1] : value;
47
- if (isStyleValue(finalValue)) {
48
- // Convert camelCase to kebab-case for CSS properties
49
- const cssProp = prop.startsWith('--') ? prop : toKebabCase(prop);
50
- style.setProperty(cssProp, String(finalValue));
51
- }
52
- }
53
- };
54
-
55
- /**
56
- * Animate an element using the Web Animations API with reduced-motion fallback.
57
- *
58
- * @param element - Element to animate
59
- * @param config - Animation configuration
60
- * @returns Promise that resolves when animation completes
61
- *
62
- * @example
63
- * ```ts
64
- * await animate(element, {
65
- * keyframes: [{ opacity: 0 }, { opacity: 1 }],
66
- * options: { duration: 200, easing: 'ease-out' },
67
- * });
68
- * ```
69
- */
70
- export const animate = (element: Element, config: AnimateOptions): Promise<void> => {
71
- const { keyframes, options, commitStyles = true, respectReducedMotion = true, onFinish } = config;
72
-
73
- if (respectReducedMotion && prefersReducedMotion()) {
74
- if (commitStyles) {
75
- applyFinalKeyframeStyles(element, keyframes);
76
- }
77
- onFinish?.();
78
- return Promise.resolve();
79
- }
80
-
81
- const htmlElement = element as HTMLElement;
82
- if (typeof htmlElement.animate !== 'function') {
83
- if (commitStyles) {
84
- applyFinalKeyframeStyles(element, keyframes);
85
- }
86
- onFinish?.();
87
- return Promise.resolve();
88
- }
89
-
90
- return new Promise((resolve) => {
91
- const animation = htmlElement.animate(keyframes, options);
92
- let finalized = false;
93
- const finalize = () => {
94
- if (finalized) return;
95
- finalized = true;
96
- if (commitStyles) {
97
- if (typeof animation.commitStyles === 'function') {
98
- animation.commitStyles();
99
- } else {
100
- applyFinalKeyframeStyles(element, keyframes);
101
- }
102
- }
103
- animation.cancel();
104
- onFinish?.();
105
- resolve();
106
- };
107
-
108
- animation.onfinish = finalize;
109
- if (animation.finished) {
110
- animation.finished.then(finalize).catch(finalize);
111
- }
112
- });
113
- };
1
+ /**
2
+ * Web Animations helpers.
3
+ *
4
+ * @module bquery/motion
5
+ */
6
+
7
+ import { prefersReducedMotion } from './reduced-motion';
8
+ import type { AnimateOptions } from './types';
9
+
10
+ /** @internal */
11
+ const isStyleValue = (value: unknown): value is string | number =>
12
+ typeof value === 'string' || typeof value === 'number';
13
+
14
+ /**
15
+ * Convert camelCase property names to kebab-case for CSS.
16
+ * @internal
17
+ */
18
+ const toKebabCase = (str: string): string => {
19
+ return str.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);
20
+ };
21
+
22
+ /** @internal */
23
+ export const applyFinalKeyframeStyles = (
24
+ element: Element,
25
+ keyframes: Keyframe[] | PropertyIndexedKeyframes
26
+ ): void => {
27
+ const htmlElement = element as HTMLElement;
28
+ const style = htmlElement.style;
29
+
30
+ if (Array.isArray(keyframes)) {
31
+ const last = keyframes[keyframes.length - 1];
32
+ if (!last) return;
33
+ for (const [prop, value] of Object.entries(last)) {
34
+ if (prop === 'offset' || prop === 'easing' || prop === 'composite') continue;
35
+ if (isStyleValue(value)) {
36
+ // Convert camelCase to kebab-case for CSS properties
37
+ const cssProp = prop.startsWith('--') ? prop : toKebabCase(prop);
38
+ style.setProperty(cssProp, String(value));
39
+ }
40
+ }
41
+ return;
42
+ }
43
+
44
+ for (const [prop, value] of Object.entries(keyframes)) {
45
+ if (prop === 'offset' || prop === 'easing' || prop === 'composite') continue;
46
+ const finalValue = Array.isArray(value) ? value[value.length - 1] : value;
47
+ if (isStyleValue(finalValue)) {
48
+ // Convert camelCase to kebab-case for CSS properties
49
+ const cssProp = prop.startsWith('--') ? prop : toKebabCase(prop);
50
+ style.setProperty(cssProp, String(finalValue));
51
+ }
52
+ }
53
+ };
54
+
55
+ /**
56
+ * Animate an element using the Web Animations API with reduced-motion fallback.
57
+ *
58
+ * @param element - Element to animate
59
+ * @param config - Animation configuration
60
+ * @returns Promise that resolves when animation completes
61
+ *
62
+ * @example
63
+ * ```ts
64
+ * await animate(element, {
65
+ * keyframes: [{ opacity: 0 }, { opacity: 1 }],
66
+ * options: { duration: 200, easing: 'ease-out' },
67
+ * });
68
+ * ```
69
+ */
70
+ export const animate = (element: Element, config: AnimateOptions): Promise<void> => {
71
+ const { keyframes, options, commitStyles = true, respectReducedMotion = true, onFinish } = config;
72
+
73
+ if (respectReducedMotion && prefersReducedMotion()) {
74
+ if (commitStyles) {
75
+ applyFinalKeyframeStyles(element, keyframes);
76
+ }
77
+ onFinish?.();
78
+ return Promise.resolve();
79
+ }
80
+
81
+ const htmlElement = element as HTMLElement;
82
+ if (typeof htmlElement.animate !== 'function') {
83
+ if (commitStyles) {
84
+ applyFinalKeyframeStyles(element, keyframes);
85
+ }
86
+ onFinish?.();
87
+ return Promise.resolve();
88
+ }
89
+
90
+ return new Promise((resolve) => {
91
+ const animation = htmlElement.animate(keyframes, options);
92
+ let finalized = false;
93
+ const finalize = () => {
94
+ if (finalized) return;
95
+ finalized = true;
96
+ if (commitStyles) {
97
+ if (typeof animation.commitStyles === 'function') {
98
+ animation.commitStyles();
99
+ } else {
100
+ applyFinalKeyframeStyles(element, keyframes);
101
+ }
102
+ }
103
+ animation.cancel();
104
+ onFinish?.();
105
+ resolve();
106
+ };
107
+
108
+ animation.onfinish = finalize;
109
+ if (animation.finished) {
110
+ animation.finished.then(finalize).catch(finalize);
111
+ }
112
+ });
113
+ };