@bquery/bquery 1.4.0 → 1.5.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 (127) hide show
  1. package/README.md +139 -120
  2. package/dist/component/component.d.ts.map +1 -1
  3. package/dist/component/index.d.ts +2 -0
  4. package/dist/component/index.d.ts.map +1 -1
  5. package/dist/component/library.d.ts +34 -0
  6. package/dist/component/library.d.ts.map +1 -0
  7. package/dist/component/types.d.ts +10 -6
  8. package/dist/component/types.d.ts.map +1 -1
  9. package/dist/component-CY5MVoYN.js +531 -0
  10. package/dist/component-CY5MVoYN.js.map +1 -0
  11. package/dist/component.es.mjs +6 -184
  12. package/dist/config-DRmZZno3.js +40 -0
  13. package/dist/config-DRmZZno3.js.map +1 -0
  14. package/dist/core-CK2Mfpf4.js +648 -0
  15. package/dist/core-CK2Mfpf4.js.map +1 -0
  16. package/dist/core-DPdbItcq.js +112 -0
  17. package/dist/core-DPdbItcq.js.map +1 -0
  18. package/dist/core.es.mjs +45 -1261
  19. package/dist/full.d.ts +6 -6
  20. package/dist/full.d.ts.map +1 -1
  21. package/dist/full.es.mjs +98 -92
  22. package/dist/full.iife.js +173 -3
  23. package/dist/full.iife.js.map +1 -1
  24. package/dist/full.umd.js +173 -3
  25. package/dist/full.umd.js.map +1 -1
  26. package/dist/index.es.mjs +143 -139
  27. package/dist/motion/transition.d.ts +1 -1
  28. package/dist/motion/transition.d.ts.map +1 -1
  29. package/dist/motion/types.d.ts +11 -1
  30. package/dist/motion/types.d.ts.map +1 -1
  31. package/dist/motion-C5DRdPnO.js +415 -0
  32. package/dist/motion-C5DRdPnO.js.map +1 -0
  33. package/dist/motion.es.mjs +25 -361
  34. package/dist/object-qGpWr6-J.js +38 -0
  35. package/dist/object-qGpWr6-J.js.map +1 -0
  36. package/dist/platform/announcer.d.ts +59 -0
  37. package/dist/platform/announcer.d.ts.map +1 -0
  38. package/dist/platform/config.d.ts +92 -0
  39. package/dist/platform/config.d.ts.map +1 -0
  40. package/dist/platform/cookies.d.ts +45 -0
  41. package/dist/platform/cookies.d.ts.map +1 -0
  42. package/dist/platform/index.d.ts +8 -0
  43. package/dist/platform/index.d.ts.map +1 -1
  44. package/dist/platform/meta.d.ts +62 -0
  45. package/dist/platform/meta.d.ts.map +1 -0
  46. package/dist/platform-B7JhGBc7.js +361 -0
  47. package/dist/platform-B7JhGBc7.js.map +1 -0
  48. package/dist/platform.es.mjs +11 -248
  49. package/dist/reactive/async-data.d.ts +114 -0
  50. package/dist/reactive/async-data.d.ts.map +1 -0
  51. package/dist/reactive/index.d.ts +2 -2
  52. package/dist/reactive/index.d.ts.map +1 -1
  53. package/dist/reactive/signal.d.ts +2 -0
  54. package/dist/reactive/signal.d.ts.map +1 -1
  55. package/dist/reactive-BDya-ia8.js +253 -0
  56. package/dist/reactive-BDya-ia8.js.map +1 -0
  57. package/dist/reactive.es.mjs +18 -34
  58. package/dist/router-CijiICxt.js +188 -0
  59. package/dist/router-CijiICxt.js.map +1 -0
  60. package/dist/router.es.mjs +11 -200
  61. package/dist/sanitize-jyJ2ryE2.js +302 -0
  62. package/dist/sanitize-jyJ2ryE2.js.map +1 -0
  63. package/dist/security/constants.d.ts.map +1 -1
  64. package/dist/security.es.mjs +10 -56
  65. package/dist/store-CPK9E62U.js +262 -0
  66. package/dist/store-CPK9E62U.js.map +1 -0
  67. package/dist/store.es.mjs +12 -25
  68. package/dist/view-Cdi0g-qo.js +396 -0
  69. package/dist/view-Cdi0g-qo.js.map +1 -0
  70. package/dist/view.es.mjs +10 -430
  71. package/package.json +15 -11
  72. package/src/component/component.ts +319 -289
  73. package/src/component/index.ts +42 -40
  74. package/src/component/library.ts +504 -0
  75. package/src/component/types.ts +91 -85
  76. package/src/core/collection.ts +628 -628
  77. package/src/core/element.ts +774 -774
  78. package/src/core/index.ts +48 -48
  79. package/src/core/utils/function.ts +151 -151
  80. package/src/full.ts +223 -187
  81. package/src/motion/animate.ts +113 -113
  82. package/src/motion/flip.ts +176 -176
  83. package/src/motion/scroll.ts +57 -57
  84. package/src/motion/spring.ts +150 -150
  85. package/src/motion/timeline.ts +246 -246
  86. package/src/motion/transition.ts +53 -7
  87. package/src/motion/types.ts +208 -198
  88. package/src/platform/announcer.ts +208 -0
  89. package/src/platform/config.ts +163 -0
  90. package/src/platform/cookies.ts +165 -0
  91. package/src/platform/index.ts +39 -18
  92. package/src/platform/meta.ts +168 -0
  93. package/src/platform/storage.ts +215 -215
  94. package/src/reactive/async-data.ts +486 -0
  95. package/src/reactive/core.ts +114 -114
  96. package/src/reactive/effect.ts +54 -54
  97. package/src/reactive/index.ts +37 -23
  98. package/src/reactive/internals.ts +122 -122
  99. package/src/reactive/signal.ts +29 -20
  100. package/src/security/constants.ts +211 -209
  101. package/src/security/sanitize-core.ts +364 -364
  102. package/src/view/evaluate.ts +290 -290
  103. package/dist/batch-x7b2eZST.js +0 -13
  104. package/dist/batch-x7b2eZST.js.map +0 -1
  105. package/dist/component.es.mjs.map +0 -1
  106. package/dist/core-BhpuvPhy.js +0 -170
  107. package/dist/core-BhpuvPhy.js.map +0 -1
  108. package/dist/core.es.mjs.map +0 -1
  109. package/dist/full.es.mjs.map +0 -1
  110. package/dist/index.es.mjs.map +0 -1
  111. package/dist/motion.es.mjs.map +0 -1
  112. package/dist/persisted-DHoi3uEs.js +0 -278
  113. package/dist/persisted-DHoi3uEs.js.map +0 -1
  114. package/dist/platform.es.mjs.map +0 -1
  115. package/dist/reactive.es.mjs.map +0 -1
  116. package/dist/router.es.mjs.map +0 -1
  117. package/dist/sanitize-Cxvxa-DX.js +0 -283
  118. package/dist/sanitize-Cxvxa-DX.js.map +0 -1
  119. package/dist/security.es.mjs.map +0 -1
  120. package/dist/store.es.mjs.map +0 -1
  121. package/dist/type-guards-BdKlYYlS.js +0 -32
  122. package/dist/type-guards-BdKlYYlS.js.map +0 -1
  123. package/dist/untrack-DNnnqdlR.js +0 -6
  124. package/dist/untrack-DNnnqdlR.js.map +0 -1
  125. package/dist/view.es.mjs.map +0 -1
  126. package/dist/watch-DXXv3iAI.js +0 -58
  127. package/dist/watch-DXXv3iAI.js.map +0 -1
package/src/full.ts CHANGED
@@ -1,187 +1,223 @@
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 { component, html, registerDefaultComponents, safeHtml } from './component/index';
81
+ export type {
82
+ ComponentDefinition,
83
+ DefaultComponentLibraryOptions,
84
+ PropDefinition,
85
+ RegisteredDefaultComponents,
86
+ } from './component/index';
87
+
88
+ // ============================================================================
89
+ // Motion Module: View transitions, FLIP animations, springs
90
+ // ============================================================================
91
+ export {
92
+ animate,
93
+ capturePosition,
94
+ easeInCubic,
95
+ easeInOutCubic,
96
+ easeInOutQuad,
97
+ easeInQuad,
98
+ easeOutBack,
99
+ easeOutCubic,
100
+ easeOutExpo,
101
+ easeOutQuad,
102
+ easingPresets,
103
+ flip,
104
+ flipElements,
105
+ flipList,
106
+ keyframePresets,
107
+ linear,
108
+ prefersReducedMotion,
109
+ scrollAnimate,
110
+ sequence,
111
+ spring,
112
+ springPresets,
113
+ stagger,
114
+ timeline,
115
+ transition,
116
+ } from './motion/index';
117
+ export type {
118
+ AnimateOptions,
119
+ EasingFunction,
120
+ ElementBounds,
121
+ FlipGroupOptions,
122
+ FlipOptions,
123
+ ScrollAnimateCleanup,
124
+ ScrollAnimateOptions,
125
+ SequenceOptions,
126
+ SequenceStep,
127
+ Spring,
128
+ SpringConfig,
129
+ StaggerFunction,
130
+ StaggerOptions,
131
+ TimelineConfig,
132
+ TimelineControls,
133
+ TimelineStep,
134
+ TransitionOptions,
135
+ } from './motion/index';
136
+
137
+ // ============================================================================
138
+ // Security Module: Sanitization, CSP compatibility, Trusted Types
139
+ // ============================================================================
140
+ export {
141
+ createTrustedHtml,
142
+ escapeHtml,
143
+ generateNonce,
144
+ getTrustedTypesPolicy,
145
+ hasCSPDirective,
146
+ isTrustedTypesSupported,
147
+ sanitize,
148
+ sanitizeHtml,
149
+ stripTags,
150
+ } from './security/index';
151
+ export type { SanitizeOptions } from './security/index';
152
+
153
+ // ============================================================================
154
+ // Platform Module: Storage, buckets, notifications, cache
155
+ // ============================================================================
156
+ export {
157
+ buckets,
158
+ cache,
159
+ defineBqueryConfig,
160
+ definePageMeta,
161
+ getBqueryConfig,
162
+ notifications,
163
+ storage,
164
+ useAnnouncer,
165
+ useCookie,
166
+ } from './platform/index';
167
+ export type {
168
+ AnnounceOptions,
169
+ AnnouncerHandle,
170
+ Bucket,
171
+ BqueryConfig,
172
+ CacheHandle,
173
+ IndexedDBOptions,
174
+ NotificationOptions,
175
+ PageMetaDefinition,
176
+ StorageAdapter,
177
+ UseAnnouncerOptions,
178
+ UseCookieOptions,
179
+ } from './platform/index';
180
+
181
+ // ============================================================================
182
+ // Router Module: SPA routing, navigation guards, lazy loading
183
+ // ============================================================================
184
+ export {
185
+ back,
186
+ createRouter,
187
+ currentRoute,
188
+ forward,
189
+ interceptLinks,
190
+ isActive,
191
+ isActiveSignal,
192
+ link,
193
+ navigate,
194
+ resolve,
195
+ } from './router/index';
196
+ export type {
197
+ NavigationGuard,
198
+ Route,
199
+ RouteDefinition,
200
+ Router,
201
+ RouterOptions,
202
+ } from './router/index';
203
+
204
+ // ============================================================================
205
+ // Store Module: Signal-based state management
206
+ // ============================================================================
207
+ export {
208
+ createPersistedStore,
209
+ createStore,
210
+ destroyStore,
211
+ getStore,
212
+ listStores,
213
+ mapActions,
214
+ mapState,
215
+ registerPlugin,
216
+ } from './store/index';
217
+ export type { StateFactory, Store, StoreDefinition, StorePlugin } from './store/index';
218
+
219
+ // ============================================================================
220
+ // View Module: Declarative DOM bindings without compiler
221
+ // ============================================================================
222
+ export { createTemplate, mount } from './view/index';
223
+ 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
+ };