@mateosuarezdev/flash 0.0.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.
@@ -0,0 +1,600 @@
1
+ /**
2
+ * @license
3
+ * Flash
4
+ * Copyright (c) 2025 Mateo Suarez. All rights reserved.
5
+ *
6
+ * Free to use in your own personal or commercial applications and projects.
7
+ * Unauthorized copying, modification, or distribution is strictly prohibited.
8
+ * See LICENSE file for full terms.
9
+ */
10
+
11
+ import { batch } from '@preact/signals-core';
12
+ import { Computed } from '@preact/signals-core';
13
+ import { computed } from '@preact/signals-core';
14
+ import { Effect } from '@preact/signals-core';
15
+ import { effect } from '@preact/signals-core';
16
+ import { Signal } from '@preact/signals-core';
17
+ import { signal } from '@preact/signals-core';
18
+
19
+ export { batch }
20
+
21
+ declare class CancelToken {
22
+ private _cancelled;
23
+ private _cancelCallbacks;
24
+ get cancelled(): boolean;
25
+ cancel(): void;
26
+ onCancel(callback: () => void): void;
27
+ throwIfCancelled(): void;
28
+ }
29
+
30
+ /**
31
+ * Chain configuration - without read phase
32
+ */
33
+ declare interface ChainConfigWithoutRead<U> {
34
+ update: (readData?: any, data?: FrameData) => U;
35
+ render: (data: U, frameData?: FrameData) => void;
36
+ }
37
+
38
+ /**
39
+ * Chain configuration - without update phase
40
+ */
41
+ declare interface ChainConfigWithoutUpdate<R> {
42
+ read: (data?: FrameData) => R;
43
+ render: (data: R, frameData?: FrameData) => void;
44
+ }
45
+
46
+ /**
47
+ * Chain configuration object
48
+ * Must have at least read+render OR read+update+render
49
+ */
50
+ /**
51
+ * Chain configuration - with update phase
52
+ */
53
+ declare interface ChainConfigWithUpdate<R, U> {
54
+ read: (data?: FrameData) => R;
55
+ update: (readData: R, data?: FrameData) => U;
56
+ render: (data: U, frameData?: FrameData) => void;
57
+ }
58
+
59
+ export declare type Child = IntrinsicVNode | VNode | ReactiveVNode | Text | string | number | boolean | null | undefined | (() => Child);
60
+
61
+ export declare type Children = Child | Children[];
62
+
63
+ declare type ComponentFunction<P = Props> = (props: P) => Child;
64
+
65
+ export { Computed }
66
+
67
+ export { computed }
68
+
69
+ declare interface Context<T> {
70
+ _id: Symbol;
71
+ _defaultValue: T;
72
+ provide: (value: T) => void;
73
+ }
74
+
75
+ export declare function createContext<T>(defaultValue: T): Context<T>;
76
+
77
+ /**
78
+ * Factory function for creating gestures - the only exported function
79
+ * Returns an object with just init() and destroy() methods
80
+ */
81
+ export declare function createGesture(el: HTMLElement, options: GestureOptions): Gesture;
82
+
83
+ export { Effect }
84
+
85
+ export { effect }
86
+
87
+ export declare const Fragment: unique symbol;
88
+
89
+ /**
90
+ * Frame scheduler implementation inspired by Framer Motion
91
+ * Provides a high-performance animation loop with read-update-render phases
92
+ * to prevent layout thrashing and optimize DOM operations.
93
+ */
94
+ declare type FrameCallback = (data?: FrameData) => void;
95
+
96
+ declare interface FrameData {
97
+ timestamp: number;
98
+ delta: number;
99
+ }
100
+
101
+ export declare class FrameScheduler {
102
+ private readCallbacks;
103
+ private updateCallbacks;
104
+ private renderCallbacks;
105
+ private readKeepAlive;
106
+ private updateKeepAlive;
107
+ private renderKeepAlive;
108
+ private isProcessing;
109
+ private frameScheduled;
110
+ private frameId;
111
+ private lastTimestamp;
112
+ private currentDelta;
113
+ private readonly trackFPS;
114
+ private readonly fpsHistorySize;
115
+ private fpsHistory;
116
+ constructor(options?: FrameSchedulerOptions);
117
+ /**
118
+ * Schedule a callback to run in the READ phase (for DOM measurements)
119
+ * Use this for: offsetHeight, getBoundingClientRect, getComputedStyle, etc.
120
+ *
121
+ * @param callback - Function to execute during the read phase
122
+ * @param keepAlive - If true, callback runs every frame until cancelled
123
+ * @returns The callback function (can be passed to cancelFrame)
124
+ *
125
+ * @example
126
+ * frame.read(() => {
127
+ * const height = element.offsetHeight;
128
+ * });
129
+ *
130
+ * // With frame data if needed
131
+ * frame.read((data) => {
132
+ * console.log('Delta:', data.delta);
133
+ * });
134
+ */
135
+ read(callback: FrameCallback, keepAlive?: boolean): FrameCallback;
136
+ /**
137
+ * Schedule a callback to run in the UPDATE phase (for calculations)
138
+ * Use this for: computing animation values, business logic, etc.
139
+ *
140
+ * @param callback - Function to execute during the update phase
141
+ * @param keepAlive - If true, callback runs every frame until cancelled
142
+ * @returns The callback function (can be passed to cancelFrame)
143
+ *
144
+ * @example
145
+ * frame.update(() => {
146
+ * position += velocity;
147
+ * });
148
+ *
149
+ * // Delta-time independent animation
150
+ * frame.update((data) => {
151
+ * position += velocity * (data.delta / 1000);
152
+ * }, true);
153
+ */
154
+ update(callback: FrameCallback, keepAlive?: boolean): FrameCallback;
155
+ /**
156
+ * Schedule a callback to run in the RENDER phase (for DOM writes)
157
+ * Use this for: style updates, classList changes, DOM mutations, etc.
158
+ *
159
+ * @param callback - Function to execute during the render phase
160
+ * @param keepAlive - If true, callback runs every frame until cancelled
161
+ * @returns The callback function (can be passed to cancelFrame)
162
+ *
163
+ * @example
164
+ * frame.render(() => {
165
+ * element.style.transform = `translateX(${x}px)`;
166
+ * });
167
+ */
168
+ render(callback: FrameCallback, keepAlive?: boolean): FrameCallback;
169
+ /**
170
+ * Schedule a read-update-render chain with typed data flow
171
+ * Must provide at least read+render or read+update+render
172
+ *
173
+ * @example
174
+ * // With update
175
+ * frame.chain({
176
+ * read: () => element.offsetHeight,
177
+ * update: (height) => height * 2,
178
+ * render: (doubled) => element.style.height = `${doubled}px`
179
+ * });
180
+ *
181
+ * // Without update (render receives read data directly)
182
+ * frame.chain({
183
+ * read: () => element.offsetHeight,
184
+ * render: (height) => element.style.height = `${height}px`
185
+ * });
186
+ */
187
+ chain<R, U>(config: ChainConfigWithUpdate<R, U>): void;
188
+ chain<R>(config: ChainConfigWithoutUpdate<R>): void;
189
+ chain<U>(config: ChainConfigWithoutRead<U>): void;
190
+ /**
191
+ * Cancel a scheduled callback from all phases
192
+ *
193
+ * @param callback - The callback function to cancel
194
+ */
195
+ cancel(callback: FrameCallback): void;
196
+ /**
197
+ * Cancel all scheduled callbacks and stop the frame loop
198
+ */
199
+ cancelAll(): void;
200
+ /**
201
+ * Get current frame data (timestamp and delta)
202
+ */
203
+ get data(): FrameData;
204
+ /**
205
+ * Check if frame loop is currently running
206
+ */
207
+ get isRunning(): boolean;
208
+ /**
209
+ * Get current instantaneous FPS
210
+ * Only available if trackFPS is enabled
211
+ */
212
+ get fps(): number;
213
+ /**
214
+ * Get average FPS over the history window
215
+ * Only available if trackFPS is enabled
216
+ */
217
+ get averageFps(): number;
218
+ /**
219
+ * Get estimated refresh rate (rounded FPS)
220
+ * Only available if trackFPS is enabled
221
+ */
222
+ get refreshRate(): number;
223
+ private scheduleFrame;
224
+ private processFrame;
225
+ private executeCallbacks;
226
+ private hasKeepAliveCallbacks;
227
+ private isKeepAliveSet;
228
+ }
229
+
230
+ declare interface FrameSchedulerOptions {
231
+ /** Enable FPS tracking (adds minimal overhead) */
232
+ trackFPS?: boolean;
233
+ /** Number of frames to track for average FPS calculation */
234
+ fpsHistorySize?: number;
235
+ }
236
+
237
+ /**
238
+ * Main Gesture Class with public and private members
239
+ */
240
+ declare class Gesture {
241
+ private el;
242
+ private options;
243
+ private details;
244
+ private minTouches;
245
+ private maxTouches;
246
+ private threshold;
247
+ private direction;
248
+ private lastMoveTime;
249
+ private prevX;
250
+ private prevY;
251
+ private initialDistance;
252
+ private initialAngle;
253
+ private boundOnStart;
254
+ private boundOnMove;
255
+ private boundOnEnd;
256
+ private passive;
257
+ private hasPassedThreshold;
258
+ private controller;
259
+ private id;
260
+ private name;
261
+ private priority;
262
+ private useThreshold;
263
+ constructor(controller: GestureController, id: number, el: HTMLElement, name: string, priority: number, options: GestureOptions);
264
+ /**
265
+ * Calculate distance between two touch points
266
+ */
267
+ private getDistance;
268
+ /**
269
+ * Calculate angle between two touch points in degrees
270
+ */
271
+ private getAngle;
272
+ /**
273
+ * Get center point of all touches
274
+ */
275
+ private getCenter;
276
+ /**
277
+ * Check if the gesture can start based on touch count
278
+ */
279
+ private canStart;
280
+ /**
281
+ * Check if we've moved enough to pass the threshold
282
+ */
283
+ private checkThreshold;
284
+ private onStart;
285
+ private onMove;
286
+ private tryCapture;
287
+ private onEnd;
288
+ /**
289
+ * Public method to initialize the gesture
290
+ */
291
+ init(): void;
292
+ /**
293
+ * Public method to clean up the gesture
294
+ */
295
+ destroy(): void;
296
+ }
297
+
298
+ /**
299
+ * Gesture Controller - manages and coordinates between multiple gestures
300
+ * This is kept as a private class not exported from the module
301
+ */
302
+ declare class GestureController {
303
+ private gestureId;
304
+ private activeGestures;
305
+ private disabledGestures;
306
+ private capturedId?;
307
+ private gestureWithPriority;
308
+ private maxPriority;
309
+ /**
310
+ * Creates a new gesture ID
311
+ */
312
+ private newId;
313
+ /**
314
+ * Check if a gesture can start
315
+ */
316
+ canStart(gestureName: string): boolean;
317
+ /**
318
+ * Register a gesture as wanting to start
319
+ */
320
+ start(id: number, gestureName: string, priority: number): boolean;
321
+ /**
322
+ * Attempt to capture control with this gesture
323
+ */
324
+ capture(id: number, gestureName: string, priority: number): boolean;
325
+ /**
326
+ * Release a gesture's attempt to capture
327
+ */
328
+ release(id: number): void;
329
+ /**
330
+ * Disable a specific gesture type
331
+ */
332
+ disableGesture(gestureName: string, id: number): void;
333
+ /**
334
+ * Enable a previously disabled gesture type
335
+ */
336
+ enableGesture(gestureName: string, id: number): void;
337
+ /**
338
+ * Create a gesture through this controller
339
+ */
340
+ createGesture(el: HTMLElement, options: GestureOptions): Gesture;
341
+ }
342
+
343
+ declare type GestureDetails = {
344
+ isTracking: boolean;
345
+ startX: number;
346
+ startY: number;
347
+ currentX: number;
348
+ currentY: number;
349
+ deltaX: number;
350
+ deltaY: number;
351
+ velocityX: number;
352
+ velocityY: number;
353
+ touchCount: number;
354
+ scale: number;
355
+ rotation: number;
356
+ type: string;
357
+ event?: TouchEvent | MouseEvent;
358
+ };
359
+
360
+ declare type GestureDirection = "x" | "y" | "all";
361
+
362
+ declare type GestureOptions = {
363
+ name?: string;
364
+ priority?: number;
365
+ threshold?: number;
366
+ direction?: GestureDirection;
367
+ minTouches?: number;
368
+ maxTouches?: number;
369
+ passive?: boolean;
370
+ canStart?: (details: GestureDetails) => boolean;
371
+ onStart?: (details: GestureDetails) => void;
372
+ onMove?: (details: GestureDetails) => void;
373
+ onEnd?: (details: GestureDetails) => void;
374
+ };
375
+
376
+ /**
377
+ * Performs a tracking read of a value.
378
+ * If the input is a Signal, it accesses the value and registers a dependency
379
+ * if called within a reactive context (like effect or computed).
380
+ *
381
+ * @template T
382
+ * @param {HybridSignal<T>} toRead - The signal or static value to subscribe to.
383
+ * @returns {T} The current value.
384
+ */
385
+ export declare function get<T>(toRead: HybridSignal<T>): T;
386
+
387
+ /**
388
+ * Represents a value that is either static or a lazy reactive computation.
389
+ * Use this for props that will be passed directly into JSX boundaries.
390
+ * @template T
391
+ */
392
+ export declare type HybridReactive<T> = T | (() => T);
393
+
394
+ /**
395
+ * Represents a value that is either static or a direct Signal instance.
396
+ * Use this for data sources that need to be read or tracked within component logic.
397
+ * @template T
398
+ */
399
+ export declare type HybridSignal<T> = T | Signal<T>;
400
+
401
+ export declare function hydrate(node: JSX.Element, container: HTMLElement, mode?: HydrationMode): void;
402
+
403
+ declare type HydrationMode = "self" | "children";
404
+
405
+ declare interface IntrinsicVNode {
406
+ type: string;
407
+ kind: "IntrinsicVNode";
408
+ props: Props;
409
+ key?: Key;
410
+ _id: number;
411
+ _dom?: HTMLElement | SVGElement;
412
+ _parent: VNode | ReactiveVNode | IntrinsicVNode | null;
413
+ _renderedChildren: (VNode | ReactiveVNode | IntrinsicVNode)[];
414
+ _onMountCallbacks: (() => void)[];
415
+ _onUnmountCallbacks: (() => void)[];
416
+ _onBeforeExitCallbacks: ((token: CancelToken) => void | Promise<void>)[];
417
+ _lifecycleState?: "mounting" | "mounted" | "exiting" | "exited";
418
+ }
419
+
420
+ declare type Key = string | number;
421
+
422
+ /**
423
+ * Normalizes a value into a format suitable for JSX rendering.
424
+ * Converts Signals into reactive getters and passes through primitives or
425
+ * existing reactive boundaries. This ensures the UI remains fine-grained.
426
+ *
427
+ * @template T
428
+ * @param {HybridSignal<T> | HybridReactive<T>} toBind - The input to normalize.
429
+ * @returns {T | (() => T)} A static value or a reactive function wrapper.
430
+ */
431
+ export declare function link<T>(toBind: HybridSignal<T> | HybridReactive<T>): HybridReactive<T>;
432
+
433
+ /**
434
+ * Register a callback to run before the component exits (for exit animations).
435
+ * Can be sync or async.
436
+ * Receives a CancelToken to handle animation interruption.
437
+ *
438
+ * @example
439
+ * onBeforeExit(async (token) => {
440
+ * const animation = animate(ref, { opacity: 0 });
441
+ *
442
+ * // Handle cancellation - reverse the animation
443
+ * token.onCancel(() => {
444
+ * animation.reverse();
445
+ * });
446
+ *
447
+ * await animation.finished;
448
+ * });
449
+ */
450
+ export declare function onBeforeExit(callback: (token: CancelToken) => void | Promise<void>): void;
451
+
452
+ /**
453
+ * This code will run after page loads
454
+ * on client side only, way before any flash
455
+ * runtime related code, use it for a pure
456
+ * vanilla workflow or to enhance instant
457
+ * hydration experiences, letting flash
458
+ * hydrate later, inside here, flash refs
459
+ * will not exist, but html parsed dom will
460
+ *
461
+ * You can also use it to opt out of flash for client side
462
+ * and add pure standard vanilla interactivity
463
+ * so you get JSX server rendered HTML Templating with
464
+ * all it's advantages, and fine grained per page bundle
465
+ * with vanilla js integrations
466
+ *
467
+ * @example
468
+ * onLoad(() => {
469
+ * const someEl = document.getElementById("element-id")
470
+ *
471
+ * fancyCriticalEnterAnimation(someEL)
472
+ * })
473
+ */
474
+ export declare function onLoad(cb: VoidFunction): void;
475
+
476
+ /**
477
+ * Register a callback to run when the component is mounted.
478
+ */
479
+ export declare function onMount(callback: () => void): void;
480
+
481
+ /**
482
+ * Register a callback to run when the component is unmounted.
483
+ */
484
+ export declare function onUnmount(callback: () => void): void;
485
+
486
+ /**
487
+ * Performs a non-tracking read of a value.
488
+ * If the input is a Signal, it returns the current value without subscribing
489
+ * the caller to future changes.
490
+ *
491
+ * @template T
492
+ * @param {HybridSignal<T>} toRead - The signal or static value to peek.
493
+ * @returns {T} The current raw value.
494
+ */
495
+ export declare function peek<T>(toRead: HybridSignal<T>): T;
496
+
497
+ declare interface Props {
498
+ children?: Children;
499
+ ref?: (element: HTMLElement | SVGElement) => void;
500
+ key?: string | number;
501
+ [key: string]: any;
502
+ }
503
+
504
+ declare interface ReactiveVNode {
505
+ type: "reactive";
506
+ kind: "ReactiveVNode";
507
+ fn: () => Child;
508
+ _id: number;
509
+ _dispose: VoidFunction | null;
510
+ _currentChild: VNode | IntrinsicVNode | ReactiveVNode | null;
511
+ _currentDom: Node | null;
512
+ _placeholder: Comment | null;
513
+ _parent: VNode | ReactiveVNode | IntrinsicVNode | null;
514
+ _renderedChildren: (VNode | ReactiveVNode | IntrinsicVNode)[];
515
+ _listState?: {
516
+ items: any[];
517
+ };
518
+ }
519
+
520
+ export declare function render(node: JSX.Element, container: HTMLElement): void;
521
+
522
+ /**
523
+ * Sets the new value of a signal
524
+ */
525
+ export declare function set<T>(signal: Signal<T>, value: T | (() => T)): void;
526
+
527
+ export { Signal }
528
+
529
+ export { signal }
530
+
531
+ export declare function startViewTransition(cb: VoidFunction): void;
532
+
533
+ /**
534
+ * Creates a suspended component that handles async data fetching
535
+ * with loading and error states.
536
+ *
537
+ * @param asyncFn - Async function that returns a Child (component)
538
+ * @param fallback - Component to show while loading (default: "Loading...")
539
+ * @param errorFallback - Component or function to show on error (default: "Error")
540
+ *
541
+ * @example
542
+ * ```tsx
543
+ * const UserProfile = suspend(
544
+ * async ({ userId }) => {
545
+ * const user = await fetchUser(userId);
546
+ * return <div>{user.name}</div>;
547
+ * },
548
+ * <div>Loading user...</div>,
549
+ * (error) => <div>Error: {error.message}</div>
550
+ * );
551
+ * ```
552
+ */
553
+ export declare function suspend<P = any>(asyncFn: (props: P) => Promise<Child>, fallback?: Child, errorFallback?: Child | ((error: Error) => Child)): SuspendedComponent<P>;
554
+
555
+ /**
556
+ * Component function that always returns a SuspendedVNode
557
+ */
558
+ declare type SuspendedComponent<P = any> = (props: P) => SuspendedVNode;
559
+
560
+ declare interface SuspendedVNode {
561
+ type: "suspended";
562
+ kind: "SuspendedVNode";
563
+ promise: Promise<Child>;
564
+ fallback: Child;
565
+ errorFallback: Child | ((error: Error) => Child);
566
+ _id: number;
567
+ _componentName: string;
568
+ _parent: VNode | ReactiveVNode | IntrinsicVNode | SuspendedVNode | null;
569
+ _renderedChildren: (VNode | ReactiveVNode | IntrinsicVNode | SuspendedVNode)[];
570
+ _currentChild: VNode | IntrinsicVNode | ReactiveVNode | SuspendedVNode | null;
571
+ _state: "pending" | "resolved" | "error";
572
+ }
573
+
574
+ export declare function useContext<T>(context: Context<T>): T;
575
+
576
+ declare interface ViewTransitionOptions {
577
+ duration?: number;
578
+ namePrefix?: string;
579
+ }
580
+
581
+ declare interface VNode {
582
+ type: ComponentFunction;
583
+ kind: "VNode";
584
+ props: Props;
585
+ children: Children;
586
+ key?: Key;
587
+ _id: number;
588
+ _onMountCallbacks: (() => void)[];
589
+ _onUnmountCallbacks: (() => void)[];
590
+ _onBeforeExitCallbacks: ((token: CancelToken) => void | Promise<void>)[];
591
+ _element: Node | null;
592
+ _parent: VNode | ReactiveVNode | IntrinsicVNode | null;
593
+ _renderedChildren: (VNode | ReactiveVNode | IntrinsicVNode)[];
594
+ _lifecycleState?: "mounting" | "mounted" | "exiting" | "exited";
595
+ _contextMap?: Map<Symbol, any>;
596
+ }
597
+
598
+ export declare function withViewTransition(mutate: () => void, elements?: HTMLElement[], options?: ViewTransitionOptions): void;
599
+
600
+ export { }