pulse-js-framework 1.10.4 → 1.11.1
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 +11 -0
- package/cli/build.js +13 -3
- package/compiler/directives.js +356 -0
- package/compiler/lexer.js +18 -3
- package/compiler/parser/core.js +6 -0
- package/compiler/parser/view.js +2 -6
- package/compiler/preprocessor.js +43 -23
- package/compiler/sourcemap.js +3 -1
- package/compiler/transformer/actions.js +329 -0
- package/compiler/transformer/export.js +7 -0
- package/compiler/transformer/expressions.js +85 -33
- package/compiler/transformer/imports.js +3 -0
- package/compiler/transformer/index.js +2 -0
- package/compiler/transformer/store.js +1 -1
- package/compiler/transformer/style.js +45 -16
- package/compiler/transformer/view.js +23 -2
- package/loader/rollup-plugin-server-components.js +391 -0
- package/loader/vite-plugin-server-components.js +420 -0
- package/loader/webpack-loader-server-components.js +356 -0
- package/package.json +124 -82
- package/runtime/async.js +4 -0
- package/runtime/context.js +16 -3
- package/runtime/dom-adapter.js +5 -3
- package/runtime/dom-virtual-list.js +2 -1
- package/runtime/form.js +8 -3
- package/runtime/graphql/cache.js +1 -1
- package/runtime/graphql/client.js +22 -0
- package/runtime/graphql/hooks.js +12 -6
- package/runtime/graphql/subscriptions.js +2 -0
- package/runtime/hmr.js +6 -3
- package/runtime/http.js +1 -0
- package/runtime/i18n.js +2 -0
- package/runtime/lru-cache.js +3 -1
- package/runtime/native.js +46 -20
- package/runtime/pulse.js +3 -0
- package/runtime/router/core.js +5 -1
- package/runtime/router/index.js +17 -1
- package/runtime/router/psc-integration.js +301 -0
- package/runtime/security.js +58 -29
- package/runtime/server-components/actions-server.js +798 -0
- package/runtime/server-components/actions.js +389 -0
- package/runtime/server-components/client.js +447 -0
- package/runtime/server-components/error-sanitizer.js +438 -0
- package/runtime/server-components/index.js +275 -0
- package/runtime/server-components/security-csrf.js +593 -0
- package/runtime/server-components/security-errors.js +227 -0
- package/runtime/server-components/security-ratelimit.js +733 -0
- package/runtime/server-components/security-validation.js +467 -0
- package/runtime/server-components/security.js +598 -0
- package/runtime/server-components/serializer.js +617 -0
- package/runtime/server-components/server.js +382 -0
- package/runtime/server-components/types.js +383 -0
- package/runtime/server-components/utils/mutex.js +60 -0
- package/runtime/server-components/utils/path-sanitizer.js +109 -0
- package/runtime/ssr.js +2 -1
- package/runtime/store.js +19 -10
- package/runtime/utils.js +12 -128
- package/types/animation.d.ts +300 -0
- package/types/i18n.d.ts +283 -0
- package/types/persistence.d.ts +267 -0
- package/types/sse.d.ts +248 -0
- package/types/sw.d.ts +150 -0
- package/runtime/a11y.js.original +0 -1844
- package/runtime/graphql.js.original +0 -1326
- package/runtime/router.js.original +0 -1605
package/runtime/utils.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { createLogger } from './logger.js';
|
|
9
|
-
import { sanitizeHtml } from './security.js';
|
|
9
|
+
import { sanitizeHtml, DANGEROUS_KEYS, escapeHtml, sanitizeUrl } from './security.js';
|
|
10
10
|
|
|
11
11
|
const log = createLogger('Security');
|
|
12
12
|
|
|
@@ -14,43 +14,9 @@ const log = createLogger('Security');
|
|
|
14
14
|
// XSS Prevention
|
|
15
15
|
// ============================================================================
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
*/
|
|
21
|
-
const HTML_ESCAPES = {
|
|
22
|
-
'&': '&',
|
|
23
|
-
'<': '<',
|
|
24
|
-
'>': '>',
|
|
25
|
-
'"': '"',
|
|
26
|
-
"'": '''
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Regex for HTML special characters (auto-generated from HTML_ESCAPES keys)
|
|
31
|
-
* @private
|
|
32
|
-
*/
|
|
33
|
-
const HTML_ESCAPE_REGEX = new RegExp(`[${Object.keys(HTML_ESCAPES).join('')}]`, 'g');
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Escape HTML special characters to prevent XSS attacks.
|
|
37
|
-
* Use this when inserting untrusted content into HTML.
|
|
38
|
-
*
|
|
39
|
-
* @param {*} str - Value to escape (will be converted to string)
|
|
40
|
-
* @returns {string} Escaped string safe for HTML insertion
|
|
41
|
-
*
|
|
42
|
-
* @example
|
|
43
|
-
* escapeHtml('<script>alert("xss")</script>')
|
|
44
|
-
* // Returns: '<script>alert("xss")</script>'
|
|
45
|
-
*
|
|
46
|
-
* @example
|
|
47
|
-
* // Safe to insert into HTML
|
|
48
|
-
* element.innerHTML = `<div>${escapeHtml(userInput)}</div>`;
|
|
49
|
-
*/
|
|
50
|
-
export function escapeHtml(str) {
|
|
51
|
-
if (str == null) return '';
|
|
52
|
-
return String(str).replace(HTML_ESCAPE_REGEX, char => HTML_ESCAPES[char]);
|
|
53
|
-
}
|
|
17
|
+
// escapeHtml and sanitizeUrl are imported from security.js (single source of truth)
|
|
18
|
+
// and re-exported below for backward compatibility.
|
|
19
|
+
export { escapeHtml, sanitizeUrl };
|
|
54
20
|
|
|
55
21
|
/**
|
|
56
22
|
* Unescape HTML entities back to their original characters.
|
|
@@ -101,6 +67,12 @@ export function dangerouslySetInnerHTML(element, html, options = {}) {
|
|
|
101
67
|
if (sanitize) {
|
|
102
68
|
element.innerHTML = sanitizeHtml(html, sanitizeOptions);
|
|
103
69
|
} else {
|
|
70
|
+
if (typeof process === 'undefined' || process.env?.NODE_ENV !== 'production') {
|
|
71
|
+
log.warn(
|
|
72
|
+
'dangerouslySetInnerHTML() called without sanitization. ' +
|
|
73
|
+
'Pass { sanitize: true } to enable HTML sanitization and prevent XSS.'
|
|
74
|
+
);
|
|
75
|
+
}
|
|
104
76
|
element.innerHTML = html;
|
|
105
77
|
}
|
|
106
78
|
}
|
|
@@ -249,95 +221,7 @@ export function safeSetAttribute(element, name, value, options = {}, domAdapter
|
|
|
249
221
|
return true;
|
|
250
222
|
}
|
|
251
223
|
|
|
252
|
-
//
|
|
253
|
-
// URL Validation
|
|
254
|
-
// ============================================================================
|
|
255
|
-
|
|
256
|
-
/**
|
|
257
|
-
* Validate and sanitize a URL to prevent javascript: and data: XSS.
|
|
258
|
-
*
|
|
259
|
-
* Security protections:
|
|
260
|
-
* - Blocks javascript: URLs (including encoded variants)
|
|
261
|
-
* - Blocks data: URLs by default (can be enabled)
|
|
262
|
-
* - Blocks blob: URLs by default (can be enabled)
|
|
263
|
-
* - Blocks vbscript: URLs
|
|
264
|
-
* - Decodes URL before checking to prevent encoding bypass
|
|
265
|
-
*
|
|
266
|
-
* @param {string} url - URL to validate
|
|
267
|
-
* @param {Object} [options] - Validation options
|
|
268
|
-
* @param {boolean} [options.allowData=false] - Allow data: URLs
|
|
269
|
-
* @param {boolean} [options.allowBlob=false] - Allow blob: URLs
|
|
270
|
-
* @param {boolean} [options.allowRelative=true] - Allow relative URLs
|
|
271
|
-
* @returns {string|null} Sanitized URL or null if invalid
|
|
272
|
-
*
|
|
273
|
-
* @example
|
|
274
|
-
* const safeUrl = sanitizeUrl(userProvidedUrl);
|
|
275
|
-
* if (safeUrl) {
|
|
276
|
-
* link.href = safeUrl;
|
|
277
|
-
* }
|
|
278
|
-
*/
|
|
279
|
-
export function sanitizeUrl(url, options = {}) {
|
|
280
|
-
const { allowData = false, allowBlob = false, allowRelative = true } = options;
|
|
281
|
-
|
|
282
|
-
if (url == null || url === '') return null;
|
|
283
|
-
|
|
284
|
-
const trimmed = String(url).trim();
|
|
285
|
-
|
|
286
|
-
// Decode URL to catch encoded attacks like javascript:
|
|
287
|
-
// Also handles %6A%61%76%61%73%63%72%69%70%74 encoding
|
|
288
|
-
let decoded = trimmed;
|
|
289
|
-
try {
|
|
290
|
-
// Decode HTML entities first (j -> j)
|
|
291
|
-
decoded = decoded.replace(/&#x([0-9a-f]+);?/gi, (_, hex) => String.fromCharCode(parseInt(hex, 16)));
|
|
292
|
-
decoded = decoded.replace(/&#(\d+);?/g, (_, dec) => String.fromCharCode(parseInt(dec, 10)));
|
|
293
|
-
// Then decode URI encoding (%6A -> j)
|
|
294
|
-
decoded = decodeURIComponent(decoded);
|
|
295
|
-
} catch {
|
|
296
|
-
// If decoding fails, use original (malformed URLs will be blocked anyway)
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
// Normalize: lowercase and remove whitespace for protocol check
|
|
300
|
-
const normalized = decoded.toLowerCase().replace(/[\s\x00-\x1f]/g, '');
|
|
301
|
-
|
|
302
|
-
// Block dangerous protocols
|
|
303
|
-
const dangerousProtocols = ['javascript:', 'vbscript:', 'file:'];
|
|
304
|
-
for (const protocol of dangerousProtocols) {
|
|
305
|
-
if (normalized.startsWith(protocol)) {
|
|
306
|
-
return null;
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
// Check for data: protocol
|
|
311
|
-
if (normalized.startsWith('data:')) {
|
|
312
|
-
return allowData ? trimmed : null;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// Check for blob: protocol
|
|
316
|
-
if (normalized.startsWith('blob:')) {
|
|
317
|
-
return allowBlob ? trimmed : null;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
// Allow relative URLs (must start with / or . to prevent //evil.com attacks)
|
|
321
|
-
if (allowRelative) {
|
|
322
|
-
if (trimmed.startsWith('/') && !trimmed.startsWith('//')) {
|
|
323
|
-
return trimmed;
|
|
324
|
-
}
|
|
325
|
-
if (trimmed.startsWith('./') || trimmed.startsWith('../')) {
|
|
326
|
-
return trimmed;
|
|
327
|
-
}
|
|
328
|
-
// URLs without protocol that don't start with // are relative
|
|
329
|
-
if (!trimmed.includes(':') && !trimmed.startsWith('//')) {
|
|
330
|
-
return trimmed;
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
// Only allow http: and https: protocols
|
|
335
|
-
if (trimmed.startsWith('http://') || trimmed.startsWith('https://')) {
|
|
336
|
-
return trimmed;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
return null;
|
|
340
|
-
}
|
|
224
|
+
// sanitizeUrl is imported from security.js and re-exported at the top of this file.
|
|
341
225
|
|
|
342
226
|
// ============================================================================
|
|
343
227
|
// CSS Sanitization
|
|
@@ -515,7 +399,7 @@ export function deepClone(obj) {
|
|
|
515
399
|
|
|
516
400
|
const clone = {};
|
|
517
401
|
for (const key in obj) {
|
|
518
|
-
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
402
|
+
if (Object.prototype.hasOwnProperty.call(obj, key) && !DANGEROUS_KEYS.has(key)) {
|
|
519
403
|
clone[key] = deepClone(obj[key]);
|
|
520
404
|
}
|
|
521
405
|
}
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pulse Animation Module Type Definitions
|
|
3
|
+
* @module pulse-js-framework/runtime/animation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Pulse } from './pulse';
|
|
7
|
+
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// Configuration
|
|
10
|
+
// ============================================================================
|
|
11
|
+
|
|
12
|
+
/** Global animation configuration options */
|
|
13
|
+
export interface AnimationConfig {
|
|
14
|
+
/** Respect the user's prefers-reduced-motion setting (default: true) */
|
|
15
|
+
respectReducedMotion?: boolean;
|
|
16
|
+
|
|
17
|
+
/** Default animation duration in ms (default: 300) */
|
|
18
|
+
defaultDuration?: number;
|
|
19
|
+
|
|
20
|
+
/** Default easing function (default: 'ease-out') */
|
|
21
|
+
defaultEasing?: string;
|
|
22
|
+
|
|
23
|
+
/** Kill switch to disable all animations globally (default: false) */
|
|
24
|
+
disabled?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Configure global animation settings.
|
|
29
|
+
*
|
|
30
|
+
* @param options Configuration options
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* configureAnimations({
|
|
34
|
+
* respectReducedMotion: true,
|
|
35
|
+
* defaultDuration: 200,
|
|
36
|
+
* defaultEasing: 'ease-in-out',
|
|
37
|
+
* disabled: false,
|
|
38
|
+
* });
|
|
39
|
+
*/
|
|
40
|
+
export declare function configureAnimations(options?: AnimationConfig): void;
|
|
41
|
+
|
|
42
|
+
// ============================================================================
|
|
43
|
+
// animate()
|
|
44
|
+
// ============================================================================
|
|
45
|
+
|
|
46
|
+
/** Options for the animate() function */
|
|
47
|
+
export interface AnimateOptions {
|
|
48
|
+
/** Duration in ms (default: global defaultDuration) */
|
|
49
|
+
duration?: number;
|
|
50
|
+
|
|
51
|
+
/** Easing function (default: global defaultEasing) */
|
|
52
|
+
easing?: string;
|
|
53
|
+
|
|
54
|
+
/** Fill mode (default: 'none') */
|
|
55
|
+
fill?: FillMode;
|
|
56
|
+
|
|
57
|
+
/** Delay in ms before animation starts (default: 0) */
|
|
58
|
+
delay?: number;
|
|
59
|
+
|
|
60
|
+
/** Number of iterations (default: 1) */
|
|
61
|
+
iterations?: number;
|
|
62
|
+
|
|
63
|
+
/** Playback direction (default: 'normal') */
|
|
64
|
+
direction?: PlaybackDirection;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Animation control returned by animate() */
|
|
68
|
+
export interface AnimationControl {
|
|
69
|
+
/** Reactive playing state */
|
|
70
|
+
isPlaying: Pulse<boolean>;
|
|
71
|
+
|
|
72
|
+
/** Reactive progress (0 to 1) */
|
|
73
|
+
progress: Pulse<number>;
|
|
74
|
+
|
|
75
|
+
/** Promise that resolves when the animation finishes */
|
|
76
|
+
finished: Promise<void>;
|
|
77
|
+
|
|
78
|
+
/** Resume or start playback */
|
|
79
|
+
play(): void;
|
|
80
|
+
|
|
81
|
+
/** Pause the animation */
|
|
82
|
+
pause(): void;
|
|
83
|
+
|
|
84
|
+
/** Reverse the animation direction */
|
|
85
|
+
reverse(): void;
|
|
86
|
+
|
|
87
|
+
/** Cancel the animation and reset progress to 0 */
|
|
88
|
+
cancel(): void;
|
|
89
|
+
|
|
90
|
+
/** Finish the animation immediately (progress set to 1) */
|
|
91
|
+
finish(): void;
|
|
92
|
+
|
|
93
|
+
/** Dispose the animation and clean up resources */
|
|
94
|
+
dispose(): void;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Animate an element using the Web Animations API.
|
|
99
|
+
* Automatically respects reduced motion preferences and SSR environments.
|
|
100
|
+
* Returns a no-op control when animations are disabled.
|
|
101
|
+
*
|
|
102
|
+
* @param element Element to animate
|
|
103
|
+
* @param keyframes Keyframes in WAAPI format (array of keyframe objects or a single keyframe object)
|
|
104
|
+
* @param options Animation options
|
|
105
|
+
* @returns AnimationControl with reactive state and playback methods
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* const ctrl = animate(element, [
|
|
109
|
+
* { opacity: 0, transform: 'translateY(-10px)' },
|
|
110
|
+
* { opacity: 1, transform: 'translateY(0)' },
|
|
111
|
+
* ], { duration: 300, easing: 'ease-out' });
|
|
112
|
+
*
|
|
113
|
+
* effect(() => {
|
|
114
|
+
* console.log('Progress:', ctrl.progress.get());
|
|
115
|
+
* });
|
|
116
|
+
*
|
|
117
|
+
* await ctrl.finished;
|
|
118
|
+
*/
|
|
119
|
+
export declare function animate(
|
|
120
|
+
element: HTMLElement,
|
|
121
|
+
keyframes: Keyframe[] | PropertyIndexedKeyframes,
|
|
122
|
+
options?: AnimateOptions
|
|
123
|
+
): AnimationControl;
|
|
124
|
+
|
|
125
|
+
// ============================================================================
|
|
126
|
+
// useTransition()
|
|
127
|
+
// ============================================================================
|
|
128
|
+
|
|
129
|
+
/** Options for the useTransition() hook */
|
|
130
|
+
export interface TransitionOptions {
|
|
131
|
+
/** Enter animation keyframes (default: { opacity: [0, 1] }) */
|
|
132
|
+
enter?: Keyframe[] | PropertyIndexedKeyframes;
|
|
133
|
+
|
|
134
|
+
/** Leave animation keyframes (default: { opacity: [1, 0] }) */
|
|
135
|
+
leave?: Keyframe[] | PropertyIndexedKeyframes;
|
|
136
|
+
|
|
137
|
+
/** Animation duration in ms */
|
|
138
|
+
duration?: number;
|
|
139
|
+
|
|
140
|
+
/** Easing function */
|
|
141
|
+
easing?: string;
|
|
142
|
+
|
|
143
|
+
/** Template factory called when entering (condition becomes true) */
|
|
144
|
+
onEnter?: (() => Node | Node[] | null) | null;
|
|
145
|
+
|
|
146
|
+
/** Template factory called when leaving (condition becomes false) */
|
|
147
|
+
onLeave?: (() => Node | Node[] | null) | null;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** Return type of useTransition() */
|
|
151
|
+
export interface TransitionResult {
|
|
152
|
+
/** Document fragment container for the transition content */
|
|
153
|
+
container: DocumentFragment;
|
|
154
|
+
|
|
155
|
+
/** Reactive flag: true while enter animation is playing */
|
|
156
|
+
isEntering: Pulse<boolean>;
|
|
157
|
+
|
|
158
|
+
/** Reactive flag: true while leave animation is playing */
|
|
159
|
+
isLeaving: Pulse<boolean>;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Reactive enter/leave transition hook.
|
|
164
|
+
* Animates content in and out based on a reactive condition.
|
|
165
|
+
* Automatically cleans up when the enclosing effect is disposed.
|
|
166
|
+
*
|
|
167
|
+
* @param condition Reactive condition function (or static boolean)
|
|
168
|
+
* @param options Transition configuration
|
|
169
|
+
* @returns Transition result with container and reactive state
|
|
170
|
+
*
|
|
171
|
+
* @example
|
|
172
|
+
* const { container, isEntering, isLeaving } = useTransition(
|
|
173
|
+
* () => showModal.get(),
|
|
174
|
+
* {
|
|
175
|
+
* enter: { opacity: [0, 1], transform: ['scale(0.9)', 'scale(1)'] },
|
|
176
|
+
* leave: { opacity: [1, 0], transform: ['scale(1)', 'scale(0.9)'] },
|
|
177
|
+
* duration: 200,
|
|
178
|
+
* onEnter: () => el('.modal', 'Hello!'),
|
|
179
|
+
* }
|
|
180
|
+
* );
|
|
181
|
+
*/
|
|
182
|
+
export declare function useTransition(
|
|
183
|
+
condition: (() => boolean) | boolean,
|
|
184
|
+
options?: TransitionOptions
|
|
185
|
+
): TransitionResult;
|
|
186
|
+
|
|
187
|
+
// ============================================================================
|
|
188
|
+
// useSpring()
|
|
189
|
+
// ============================================================================
|
|
190
|
+
|
|
191
|
+
/** Options for the useSpring() hook */
|
|
192
|
+
export interface SpringOptions {
|
|
193
|
+
/** Spring stiffness (default: 170) */
|
|
194
|
+
stiffness?: number;
|
|
195
|
+
|
|
196
|
+
/** Damping coefficient (default: 26) */
|
|
197
|
+
damping?: number;
|
|
198
|
+
|
|
199
|
+
/** Mass (default: 1) */
|
|
200
|
+
mass?: number;
|
|
201
|
+
|
|
202
|
+
/** Precision threshold for settling (default: 0.01) */
|
|
203
|
+
precision?: number;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** Return type of useSpring() */
|
|
207
|
+
export interface SpringResult {
|
|
208
|
+
/** Reactive current value of the spring */
|
|
209
|
+
value: Pulse<number>;
|
|
210
|
+
|
|
211
|
+
/** Reactive flag: true while the spring is animating */
|
|
212
|
+
isAnimating: Pulse<boolean>;
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Set a new target value for the spring to animate towards
|
|
216
|
+
* @param newTarget New target value
|
|
217
|
+
*/
|
|
218
|
+
set(newTarget: number): void;
|
|
219
|
+
|
|
220
|
+
/** Dispose the spring animation and clean up resources */
|
|
221
|
+
dispose(): void;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Spring-based animation using a damped harmonic oscillator.
|
|
226
|
+
* Supports both static and reactive target values.
|
|
227
|
+
* Automatically cleans up when the enclosing effect is disposed.
|
|
228
|
+
*
|
|
229
|
+
* @param target Target value (number) or reactive function returning a number
|
|
230
|
+
* @param options Spring physics configuration
|
|
231
|
+
* @returns Spring result with reactive value and controls
|
|
232
|
+
*
|
|
233
|
+
* @example
|
|
234
|
+
* // Static target
|
|
235
|
+
* const spring = useSpring(100, { stiffness: 170, damping: 26 });
|
|
236
|
+
* spring.set(200); // Animate to 200
|
|
237
|
+
*
|
|
238
|
+
* // Reactive target
|
|
239
|
+
* const x = pulse(0);
|
|
240
|
+
* const spring = useSpring(() => x.get(), { stiffness: 300 });
|
|
241
|
+
* x.set(100); // Spring automatically animates to 100
|
|
242
|
+
*
|
|
243
|
+
* effect(() => {
|
|
244
|
+
* element.style.transform = `translateX(${spring.value.get()}px)`;
|
|
245
|
+
* });
|
|
246
|
+
*/
|
|
247
|
+
export declare function useSpring(
|
|
248
|
+
target: number | (() => number),
|
|
249
|
+
options?: SpringOptions
|
|
250
|
+
): SpringResult;
|
|
251
|
+
|
|
252
|
+
// ============================================================================
|
|
253
|
+
// stagger()
|
|
254
|
+
// ============================================================================
|
|
255
|
+
|
|
256
|
+
/** Options for the stagger() function */
|
|
257
|
+
export interface StaggerOptions extends AnimateOptions {
|
|
258
|
+
/** Delay between each element's animation start in ms (default: 50) */
|
|
259
|
+
staggerDelay?: number;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Stagger an animation across multiple elements.
|
|
264
|
+
* Each element starts its animation after a configurable delay from the previous one.
|
|
265
|
+
*
|
|
266
|
+
* @param elements Array of elements to animate
|
|
267
|
+
* @param keyframes Keyframes in WAAPI format
|
|
268
|
+
* @param options Stagger and animation options
|
|
269
|
+
* @returns Array of AnimationControl objects, one per element
|
|
270
|
+
*
|
|
271
|
+
* @example
|
|
272
|
+
* const items = document.querySelectorAll('.list-item');
|
|
273
|
+
* const controls = stagger(
|
|
274
|
+
* Array.from(items),
|
|
275
|
+
* [{ opacity: 0, transform: 'translateY(20px)' }, { opacity: 1, transform: 'translateY(0)' }],
|
|
276
|
+
* { duration: 300, staggerDelay: 50, easing: 'ease-out' }
|
|
277
|
+
* );
|
|
278
|
+
*
|
|
279
|
+
* // Wait for all animations to finish
|
|
280
|
+
* await Promise.all(controls.map(c => c.finished));
|
|
281
|
+
*/
|
|
282
|
+
export declare function stagger(
|
|
283
|
+
elements: HTMLElement[],
|
|
284
|
+
keyframes: Keyframe[] | PropertyIndexedKeyframes,
|
|
285
|
+
options?: StaggerOptions
|
|
286
|
+
): AnimationControl[];
|
|
287
|
+
|
|
288
|
+
// ============================================================================
|
|
289
|
+
// Default Export
|
|
290
|
+
// ============================================================================
|
|
291
|
+
|
|
292
|
+
declare const _default: {
|
|
293
|
+
animate: typeof animate;
|
|
294
|
+
useTransition: typeof useTransition;
|
|
295
|
+
useSpring: typeof useSpring;
|
|
296
|
+
stagger: typeof stagger;
|
|
297
|
+
configureAnimations: typeof configureAnimations;
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
export default _default;
|