tekiyo-physics 1.0.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.
package/dist/index.js ADDED
@@ -0,0 +1,1916 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+ import { createContext, useContext, useMemo, useRef, useState, useEffect, useCallback, forwardRef } from "react";
5
+ import { jsx } from "react/jsx-runtime";
6
+ class Vector2 {
7
+ constructor(x = 0, y = 0) {
8
+ this.x = x;
9
+ this.y = y;
10
+ }
11
+ static zero() {
12
+ return new Vector2(0, 0);
13
+ }
14
+ static from(x, y) {
15
+ return new Vector2(x, y);
16
+ }
17
+ static fromObject(obj) {
18
+ return new Vector2(obj.x, obj.y);
19
+ }
20
+ add(other) {
21
+ return new Vector2(this.x + other.x, this.y + other.y);
22
+ }
23
+ subtract(other) {
24
+ return new Vector2(this.x - other.x, this.y - other.y);
25
+ }
26
+ multiply(scalar) {
27
+ return new Vector2(this.x * scalar, this.y * scalar);
28
+ }
29
+ divide(scalar) {
30
+ if (scalar === 0) return Vector2.zero();
31
+ return new Vector2(this.x / scalar, this.y / scalar);
32
+ }
33
+ get magnitude() {
34
+ return Math.sqrt(this.x * this.x + this.y * this.y);
35
+ }
36
+ get magnitudeSquared() {
37
+ return this.x * this.x + this.y * this.y;
38
+ }
39
+ normalize() {
40
+ const mag = this.magnitude;
41
+ if (mag === 0) return Vector2.zero();
42
+ return this.divide(mag);
43
+ }
44
+ clamp(maxMagnitude) {
45
+ const mag = this.magnitude;
46
+ if (mag <= maxMagnitude) return this;
47
+ return this.normalize().multiply(maxMagnitude);
48
+ }
49
+ clampComponents(min, max) {
50
+ return new Vector2(
51
+ Math.max(min.x, Math.min(max.x, this.x)),
52
+ Math.max(min.y, Math.min(max.y, this.y))
53
+ );
54
+ }
55
+ lerp(target, t) {
56
+ return new Vector2(
57
+ this.x + (target.x - this.x) * t,
58
+ this.y + (target.y - this.y) * t
59
+ );
60
+ }
61
+ dot(other) {
62
+ return this.x * other.x + this.y * other.y;
63
+ }
64
+ distanceTo(other) {
65
+ return this.subtract(other).magnitude;
66
+ }
67
+ equals(other, epsilon = 1e-4) {
68
+ return Math.abs(this.x - other.x) < epsilon && Math.abs(this.y - other.y) < epsilon;
69
+ }
70
+ isZero(epsilon = 1e-4) {
71
+ return this.magnitude < epsilon;
72
+ }
73
+ negate() {
74
+ return new Vector2(-this.x, -this.y);
75
+ }
76
+ abs() {
77
+ return new Vector2(Math.abs(this.x), Math.abs(this.y));
78
+ }
79
+ toObject() {
80
+ return { x: this.x, y: this.y };
81
+ }
82
+ }
83
+ const DEFAULT_SPRING_CONFIG = {
84
+ tension: 170,
85
+ friction: 26,
86
+ mass: 1,
87
+ precision: 0.01
88
+ };
89
+ class Spring {
90
+ constructor(initialPosition = Vector2.zero(), config = {}) {
91
+ __publicField(this, "position");
92
+ __publicField(this, "velocity");
93
+ __publicField(this, "target");
94
+ __publicField(this, "config");
95
+ this.position = initialPosition;
96
+ this.velocity = Vector2.zero();
97
+ this.target = initialPosition;
98
+ this.config = { ...DEFAULT_SPRING_CONFIG, ...config };
99
+ }
100
+ setTarget(target) {
101
+ this.target = target;
102
+ }
103
+ setPosition(position) {
104
+ this.position = position;
105
+ }
106
+ setVelocity(velocity) {
107
+ this.velocity = velocity;
108
+ }
109
+ updateConfig(config) {
110
+ this.config = { ...this.config, ...config };
111
+ }
112
+ /**
113
+ * Advance the spring simulation by deltaTime seconds
114
+ * Uses semi-implicit Euler for stability
115
+ */
116
+ step(deltaTime) {
117
+ const { tension, friction, mass, precision } = this.config;
118
+ const displacement = this.position.subtract(this.target);
119
+ const springForce = displacement.multiply(-tension);
120
+ const dampingForce = this.velocity.multiply(-friction);
121
+ const totalForce = springForce.add(dampingForce);
122
+ const acceleration = totalForce.divide(mass);
123
+ this.velocity = this.velocity.add(acceleration.multiply(deltaTime));
124
+ this.position = this.position.add(this.velocity.multiply(deltaTime));
125
+ const isSettled = displacement.magnitude < precision && this.velocity.magnitude < precision;
126
+ if (isSettled) {
127
+ this.position = this.target;
128
+ this.velocity = Vector2.zero();
129
+ }
130
+ return {
131
+ position: this.position,
132
+ velocity: this.velocity,
133
+ target: this.target,
134
+ isSettled
135
+ };
136
+ }
137
+ getState() {
138
+ const displacement = this.position.subtract(this.target);
139
+ return {
140
+ position: this.position,
141
+ velocity: this.velocity,
142
+ target: this.target,
143
+ isSettled: displacement.magnitude < this.config.precision && this.velocity.magnitude < this.config.precision
144
+ };
145
+ }
146
+ /**
147
+ * Immediately snap to target
148
+ */
149
+ snapToTarget() {
150
+ this.position = this.target;
151
+ this.velocity = Vector2.zero();
152
+ }
153
+ }
154
+ class Spring1D {
155
+ constructor(initialValue = 0, config = {}) {
156
+ __publicField(this, "position");
157
+ __publicField(this, "velocity");
158
+ __publicField(this, "target");
159
+ __publicField(this, "config");
160
+ this.position = initialValue;
161
+ this.velocity = 0;
162
+ this.target = initialValue;
163
+ this.config = { ...DEFAULT_SPRING_CONFIG, ...config };
164
+ }
165
+ setTarget(target) {
166
+ this.target = target;
167
+ }
168
+ setPosition(position) {
169
+ this.position = position;
170
+ }
171
+ setVelocity(velocity) {
172
+ this.velocity = velocity;
173
+ }
174
+ step(deltaTime) {
175
+ const { tension, friction, mass, precision } = this.config;
176
+ const displacement = this.position - this.target;
177
+ const springForce = -tension * displacement;
178
+ const dampingForce = -friction * this.velocity;
179
+ const acceleration = (springForce + dampingForce) / mass;
180
+ this.velocity += acceleration * deltaTime;
181
+ this.position += this.velocity * deltaTime;
182
+ const isSettled = Math.abs(displacement) < precision && Math.abs(this.velocity) < precision;
183
+ if (isSettled) {
184
+ this.position = this.target;
185
+ this.velocity = 0;
186
+ }
187
+ return {
188
+ value: this.position,
189
+ velocity: this.velocity,
190
+ isSettled
191
+ };
192
+ }
193
+ getValue() {
194
+ return this.position;
195
+ }
196
+ snapToTarget() {
197
+ this.position = this.target;
198
+ this.velocity = 0;
199
+ }
200
+ }
201
+ class VelocityTracker {
202
+ constructor(maxSamples = 8, maxAge = 100) {
203
+ __publicField(this, "samples", []);
204
+ __publicField(this, "maxSamples");
205
+ __publicField(this, "maxAge");
206
+ this.maxSamples = maxSamples;
207
+ this.maxAge = maxAge;
208
+ }
209
+ /**
210
+ * Record a position sample
211
+ */
212
+ addSample(position, timestamp = performance.now()) {
213
+ this.samples.push({ position, timestamp });
214
+ if (this.samples.length > this.maxSamples) {
215
+ this.samples.shift();
216
+ }
217
+ }
218
+ /**
219
+ * Calculate current velocity from sample history
220
+ * Returns smoothed velocity vector in pixels per second
221
+ */
222
+ getVelocity() {
223
+ const now = performance.now();
224
+ this.samples = this.samples.filter(
225
+ (sample) => now - sample.timestamp < this.maxAge
226
+ );
227
+ if (this.samples.length < 2) {
228
+ return Vector2.zero();
229
+ }
230
+ let totalWeight = 0;
231
+ let weightedVelocity = Vector2.zero();
232
+ for (let i = 1; i < this.samples.length; i++) {
233
+ const prev = this.samples[i - 1];
234
+ const curr = this.samples[i];
235
+ const dt = (curr.timestamp - prev.timestamp) / 1e3;
236
+ if (dt <= 0) continue;
237
+ const instantVelocity = curr.position.subtract(prev.position).divide(dt);
238
+ const age = now - curr.timestamp;
239
+ const weight = Math.exp(-age / this.maxAge);
240
+ weightedVelocity = weightedVelocity.add(instantVelocity.multiply(weight));
241
+ totalWeight += weight;
242
+ }
243
+ if (totalWeight === 0) {
244
+ return Vector2.zero();
245
+ }
246
+ return weightedVelocity.divide(totalWeight);
247
+ }
248
+ /**
249
+ * Get velocity magnitude in pixels per second
250
+ */
251
+ getSpeed() {
252
+ return this.getVelocity().magnitude;
253
+ }
254
+ /**
255
+ * Check if current velocity qualifies as a flick gesture
256
+ */
257
+ isFlick(threshold = 500) {
258
+ return this.getSpeed() > threshold;
259
+ }
260
+ /**
261
+ * Get dominant direction of movement
262
+ */
263
+ getDirection() {
264
+ const velocity = this.getVelocity();
265
+ const speed = velocity.magnitude;
266
+ if (speed < 10) return "none";
267
+ if (Math.abs(velocity.x) > Math.abs(velocity.y)) {
268
+ return velocity.x > 0 ? "right" : "left";
269
+ } else {
270
+ return velocity.y > 0 ? "down" : "up";
271
+ }
272
+ }
273
+ /**
274
+ * Clear all samples
275
+ */
276
+ reset() {
277
+ this.samples = [];
278
+ }
279
+ /**
280
+ * Get the last recorded position
281
+ */
282
+ getLastPosition() {
283
+ if (this.samples.length === 0) return null;
284
+ return this.samples[this.samples.length - 1].position;
285
+ }
286
+ }
287
+ class VelocityTracker1D {
288
+ constructor(maxSamples = 8, maxAge = 100) {
289
+ __publicField(this, "samples", []);
290
+ __publicField(this, "maxSamples");
291
+ __publicField(this, "maxAge");
292
+ this.maxSamples = maxSamples;
293
+ this.maxAge = maxAge;
294
+ }
295
+ addSample(value, timestamp = performance.now()) {
296
+ this.samples.push({ value, timestamp });
297
+ if (this.samples.length > this.maxSamples) {
298
+ this.samples.shift();
299
+ }
300
+ }
301
+ getVelocity() {
302
+ const now = performance.now();
303
+ this.samples = this.samples.filter((s) => now - s.timestamp < this.maxAge);
304
+ if (this.samples.length < 2) return 0;
305
+ let totalWeight = 0;
306
+ let weightedVelocity = 0;
307
+ for (let i = 1; i < this.samples.length; i++) {
308
+ const prev = this.samples[i - 1];
309
+ const curr = this.samples[i];
310
+ const dt = (curr.timestamp - prev.timestamp) / 1e3;
311
+ if (dt <= 0) continue;
312
+ const instantVelocity = (curr.value - prev.value) / dt;
313
+ const age = now - curr.timestamp;
314
+ const weight = Math.exp(-age / this.maxAge);
315
+ weightedVelocity += instantVelocity * weight;
316
+ totalWeight += weight;
317
+ }
318
+ return totalWeight > 0 ? weightedVelocity / totalWeight : 0;
319
+ }
320
+ reset() {
321
+ this.samples = [];
322
+ }
323
+ }
324
+ const DEFAULT_FRICTION_CONFIG = {
325
+ decay: 0.95,
326
+ stopThreshold: 0.1,
327
+ maxVelocity: 2e3
328
+ };
329
+ class Momentum {
330
+ constructor(initialPosition = Vector2.zero(), config = {}) {
331
+ __publicField(this, "position");
332
+ __publicField(this, "velocity");
333
+ __publicField(this, "config");
334
+ __publicField(this, "isActive", false);
335
+ this.position = initialPosition;
336
+ this.velocity = Vector2.zero();
337
+ this.config = { ...DEFAULT_FRICTION_CONFIG, ...config };
338
+ }
339
+ /**
340
+ * Start momentum with initial velocity
341
+ */
342
+ start(position, velocity) {
343
+ this.position = position;
344
+ this.velocity = velocity.clamp(this.config.maxVelocity);
345
+ this.isActive = true;
346
+ }
347
+ /**
348
+ * Stop momentum immediately
349
+ */
350
+ stop() {
351
+ this.velocity = Vector2.zero();
352
+ this.isActive = false;
353
+ }
354
+ /**
355
+ * Update momentum simulation
356
+ * @param deltaTime Time in seconds
357
+ */
358
+ step(deltaTime) {
359
+ if (!this.isActive) {
360
+ return {
361
+ position: this.position,
362
+ velocity: Vector2.zero(),
363
+ isActive: false
364
+ };
365
+ }
366
+ this.position = this.position.add(this.velocity.multiply(deltaTime));
367
+ const frameDecay = Math.pow(this.config.decay, deltaTime * 60);
368
+ this.velocity = this.velocity.multiply(frameDecay);
369
+ if (this.velocity.magnitude < this.config.stopThreshold) {
370
+ this.velocity = Vector2.zero();
371
+ this.isActive = false;
372
+ }
373
+ return {
374
+ position: this.position,
375
+ velocity: this.velocity,
376
+ isActive: this.isActive
377
+ };
378
+ }
379
+ /**
380
+ * Apply rubber band effect at boundaries
381
+ */
382
+ applyRubberBand(bounds, rubberBandFactor = 0.55) {
383
+ let newX = this.position.x;
384
+ let newY = this.position.y;
385
+ if (this.position.x < bounds.min.x) {
386
+ const overshoot = bounds.min.x - this.position.x;
387
+ newX = bounds.min.x - overshoot * rubberBandFactor;
388
+ } else if (this.position.x > bounds.max.x) {
389
+ const overshoot = this.position.x - bounds.max.x;
390
+ newX = bounds.max.x + overshoot * rubberBandFactor;
391
+ }
392
+ if (this.position.y < bounds.min.y) {
393
+ const overshoot = bounds.min.y - this.position.y;
394
+ newY = bounds.min.y - overshoot * rubberBandFactor;
395
+ } else if (this.position.y > bounds.max.y) {
396
+ const overshoot = this.position.y - bounds.max.y;
397
+ newY = bounds.max.y + overshoot * rubberBandFactor;
398
+ }
399
+ this.position = new Vector2(newX, newY);
400
+ }
401
+ getPosition() {
402
+ return this.position;
403
+ }
404
+ getVelocity() {
405
+ return this.velocity;
406
+ }
407
+ getIsActive() {
408
+ return this.isActive;
409
+ }
410
+ setPosition(position) {
411
+ this.position = position;
412
+ }
413
+ updateConfig(config) {
414
+ this.config = { ...this.config, ...config };
415
+ }
416
+ }
417
+ class Momentum1D {
418
+ constructor(initialPosition = 0, config = {}) {
419
+ __publicField(this, "position");
420
+ __publicField(this, "velocity");
421
+ __publicField(this, "config");
422
+ __publicField(this, "isActive", false);
423
+ this.position = initialPosition;
424
+ this.velocity = 0;
425
+ this.config = { ...DEFAULT_FRICTION_CONFIG, ...config };
426
+ }
427
+ start(position, velocity) {
428
+ this.position = position;
429
+ this.velocity = Math.sign(velocity) * Math.min(Math.abs(velocity), this.config.maxVelocity);
430
+ this.isActive = true;
431
+ }
432
+ stop() {
433
+ this.velocity = 0;
434
+ this.isActive = false;
435
+ }
436
+ step(deltaTime) {
437
+ if (!this.isActive) {
438
+ return { position: this.position, velocity: 0, isActive: false };
439
+ }
440
+ this.position += this.velocity * deltaTime;
441
+ const frameDecay = Math.pow(this.config.decay, deltaTime * 60);
442
+ this.velocity *= frameDecay;
443
+ if (Math.abs(this.velocity) < this.config.stopThreshold) {
444
+ this.velocity = 0;
445
+ this.isActive = false;
446
+ }
447
+ return {
448
+ position: this.position,
449
+ velocity: this.velocity,
450
+ isActive: this.isActive
451
+ };
452
+ }
453
+ getPosition() {
454
+ return this.position;
455
+ }
456
+ setPosition(position) {
457
+ this.position = position;
458
+ }
459
+ }
460
+ class PhysicsEngineCore {
461
+ constructor() {
462
+ __publicField(this, "bodies", /* @__PURE__ */ new Map());
463
+ __publicField(this, "rafId", null);
464
+ __publicField(this, "lastTime", 0);
465
+ __publicField(this, "isRunning", false);
466
+ // Cap deltaTime to prevent spiral of death (max ~30fps frame time)
467
+ __publicField(this, "maxDelta", 1 / 30);
468
+ /**
469
+ * Main RAF loop
470
+ */
471
+ __publicField(this, "loop", () => {
472
+ if (!this.isRunning) return;
473
+ const currentTime = performance.now();
474
+ let deltaTime = (currentTime - this.lastTime) / 1e3;
475
+ this.lastTime = currentTime;
476
+ deltaTime = Math.min(deltaTime, this.maxDelta);
477
+ const sortedBodies = Array.from(this.bodies.values()).sort(
478
+ (a, b) => b.priority - a.priority
479
+ );
480
+ for (const body of sortedBodies) {
481
+ body.update(deltaTime);
482
+ }
483
+ this.rafId = requestAnimationFrame(this.loop);
484
+ });
485
+ }
486
+ /**
487
+ * Register a physics body to receive updates
488
+ */
489
+ register(id, update, priority = 0) {
490
+ this.bodies.set(id, { id, update, priority });
491
+ this.start();
492
+ }
493
+ /**
494
+ * Unregister a physics body
495
+ */
496
+ unregister(id) {
497
+ this.bodies.delete(id);
498
+ if (this.bodies.size === 0) {
499
+ this.stop();
500
+ }
501
+ }
502
+ /**
503
+ * Check if a body is registered
504
+ */
505
+ has(id) {
506
+ return this.bodies.has(id);
507
+ }
508
+ /**
509
+ * Start the physics loop
510
+ */
511
+ start() {
512
+ if (this.isRunning) return;
513
+ this.isRunning = true;
514
+ this.lastTime = performance.now();
515
+ this.loop();
516
+ }
517
+ /**
518
+ * Stop the physics loop
519
+ */
520
+ stop() {
521
+ if (!this.isRunning) return;
522
+ this.isRunning = false;
523
+ if (this.rafId !== null) {
524
+ cancelAnimationFrame(this.rafId);
525
+ this.rafId = null;
526
+ }
527
+ }
528
+ /**
529
+ * Get current number of active bodies
530
+ */
531
+ getActiveCount() {
532
+ return this.bodies.size;
533
+ }
534
+ /**
535
+ * Check if engine is running
536
+ */
537
+ getIsRunning() {
538
+ return this.isRunning;
539
+ }
540
+ }
541
+ const PhysicsEngine = new PhysicsEngineCore();
542
+ let idCounter = 0;
543
+ function generatePhysicsId(prefix = "physics") {
544
+ return `${prefix}_${++idCounter}_${Date.now().toString(36)}`;
545
+ }
546
+ const ios = {
547
+ name: "ios",
548
+ description: "iOS-like smooth, natural feel",
549
+ spring: {
550
+ tension: 170,
551
+ friction: 26,
552
+ mass: 1,
553
+ precision: 0.01
554
+ },
555
+ friction: {
556
+ decay: 0.95,
557
+ stopThreshold: 0.1,
558
+ maxVelocity: 2e3
559
+ },
560
+ stretch: {
561
+ intensity: 0.08,
562
+ maxVelocity: 1500
563
+ },
564
+ lift: {
565
+ scale: 1.02,
566
+ shadowIntensity: 0.8
567
+ }
568
+ };
569
+ const snappy = {
570
+ name: "snappy",
571
+ description: "Ultra-responsive, instant feedback",
572
+ spring: {
573
+ tension: 400,
574
+ friction: 30,
575
+ mass: 0.8,
576
+ precision: 0.01
577
+ },
578
+ friction: {
579
+ decay: 0.92,
580
+ stopThreshold: 0.5,
581
+ maxVelocity: 3e3
582
+ },
583
+ stretch: {
584
+ intensity: 0.06,
585
+ maxVelocity: 2e3
586
+ },
587
+ lift: {
588
+ scale: 1.015,
589
+ shadowIntensity: 0.6
590
+ }
591
+ };
592
+ const smooth = {
593
+ name: "smooth",
594
+ description: "Slow, luxurious, elegant motion",
595
+ spring: {
596
+ tension: 120,
597
+ friction: 20,
598
+ mass: 1.5,
599
+ precision: 5e-3
600
+ },
601
+ friction: {
602
+ decay: 0.97,
603
+ stopThreshold: 0.05,
604
+ maxVelocity: 1500
605
+ },
606
+ stretch: {
607
+ intensity: 0.1,
608
+ maxVelocity: 1200
609
+ },
610
+ lift: {
611
+ scale: 1.04,
612
+ shadowIntensity: 1.2
613
+ }
614
+ };
615
+ const stiff = {
616
+ name: "stiff",
617
+ description: "Direct, precise, no overshoot",
618
+ spring: {
619
+ tension: 300,
620
+ friction: 35,
621
+ mass: 1,
622
+ precision: 0.01
623
+ },
624
+ friction: {
625
+ decay: 0.9,
626
+ stopThreshold: 0.2,
627
+ maxVelocity: 2500
628
+ },
629
+ stretch: {
630
+ intensity: 0.04,
631
+ maxVelocity: 2500
632
+ },
633
+ lift: {
634
+ scale: 1.01,
635
+ shadowIntensity: 0.5
636
+ }
637
+ };
638
+ const gentle = {
639
+ name: "gentle",
640
+ description: "Soft, forgiving, approachable",
641
+ spring: {
642
+ tension: 150,
643
+ friction: 22,
644
+ mass: 1.2,
645
+ precision: 0.01
646
+ },
647
+ friction: {
648
+ decay: 0.96,
649
+ stopThreshold: 0.1,
650
+ maxVelocity: 1800
651
+ },
652
+ stretch: {
653
+ intensity: 0.12,
654
+ maxVelocity: 1400
655
+ },
656
+ lift: {
657
+ scale: 1.03,
658
+ shadowIntensity: 1
659
+ }
660
+ };
661
+ const presets = {
662
+ ios,
663
+ snappy,
664
+ smooth,
665
+ stiff,
666
+ gentle
667
+ };
668
+ function getPreset(name) {
669
+ return presets[name];
670
+ }
671
+ function getSpringConfig(preset) {
672
+ var _a;
673
+ if (typeof preset === "string") {
674
+ return ((_a = presets[preset]) == null ? void 0 : _a.spring) ?? ios.spring;
675
+ }
676
+ return preset;
677
+ }
678
+ function getFrictionConfig(preset) {
679
+ var _a;
680
+ if (typeof preset === "string") {
681
+ return ((_a = presets[preset]) == null ? void 0 : _a.friction) ?? ios.friction;
682
+ }
683
+ return preset;
684
+ }
685
+ const defaultContext = {
686
+ spring: DEFAULT_SPRING_CONFIG,
687
+ friction: DEFAULT_FRICTION_CONFIG,
688
+ stretch: {
689
+ intensity: 0.12,
690
+ maxVelocity: 2e3
691
+ },
692
+ lift: {
693
+ scale: 1.03,
694
+ shadowIntensity: 1
695
+ }
696
+ };
697
+ const PhysicsContext = createContext(defaultContext);
698
+ function PhysicsProvider({
699
+ children,
700
+ preset = "ios",
701
+ spring,
702
+ friction,
703
+ stretch,
704
+ lift
705
+ }) {
706
+ const value = useMemo(() => {
707
+ const basePreset = typeof preset === "string" ? presets[preset] ?? ios : preset;
708
+ return {
709
+ spring: { ...basePreset.spring, ...spring },
710
+ friction: { ...basePreset.friction, ...friction },
711
+ stretch: { ...basePreset.stretch, ...stretch },
712
+ lift: { ...basePreset.lift, ...lift }
713
+ };
714
+ }, [preset, spring, friction, stretch, lift]);
715
+ return /* @__PURE__ */ jsx(PhysicsContext.Provider, { value, children });
716
+ }
717
+ function usePhysicsContext() {
718
+ return useContext(PhysicsContext);
719
+ }
720
+ function usePhysicsConfig() {
721
+ const context = useContext(PhysicsContext);
722
+ return context.spring;
723
+ }
724
+ function useFrictionConfig() {
725
+ const context = useContext(PhysicsContext);
726
+ return context.friction;
727
+ }
728
+ function useStretchConfig() {
729
+ const context = useContext(PhysicsContext);
730
+ return context.stretch;
731
+ }
732
+ function useLiftConfig() {
733
+ const context = useContext(PhysicsContext);
734
+ return context.lift;
735
+ }
736
+ function useSpring(options) {
737
+ const { to, from, config, onRest, immediate = true } = options;
738
+ const globalConfig = usePhysicsConfig();
739
+ const resolvedConfig = useMemo(() => {
740
+ if (!config) return { ...DEFAULT_SPRING_CONFIG, ...globalConfig };
741
+ if (typeof config === "string") {
742
+ return { ...DEFAULT_SPRING_CONFIG, ...globalConfig };
743
+ }
744
+ return { ...DEFAULT_SPRING_CONFIG, ...globalConfig, ...config };
745
+ }, [config, globalConfig]);
746
+ const springRef = useRef(null);
747
+ const idRef = useRef(generatePhysicsId("spring"));
748
+ const onRestRef = useRef(onRest);
749
+ onRestRef.current = onRest;
750
+ const [state, setState] = useState({
751
+ x: (from == null ? void 0 : from.x) ?? to.x,
752
+ y: (from == null ? void 0 : from.y) ?? to.y,
753
+ isAnimating: false,
754
+ isSettled: true
755
+ });
756
+ useEffect(() => {
757
+ const initialPos = from ? Vector2.from(from.x, from.y) : Vector2.from(to.x, to.y);
758
+ springRef.current = new Spring(initialPos, resolvedConfig);
759
+ if (immediate) {
760
+ springRef.current.setTarget(Vector2.from(to.x, to.y));
761
+ }
762
+ return () => {
763
+ PhysicsEngine.unregister(idRef.current);
764
+ };
765
+ }, []);
766
+ useEffect(() => {
767
+ if (springRef.current && immediate) {
768
+ springRef.current.setTarget(Vector2.from(to.x, to.y));
769
+ if (!PhysicsEngine.has(idRef.current)) {
770
+ PhysicsEngine.register(idRef.current, (deltaTime) => {
771
+ var _a;
772
+ if (!springRef.current) return;
773
+ const result = springRef.current.step(deltaTime);
774
+ setState({
775
+ x: result.position.x,
776
+ y: result.position.y,
777
+ isAnimating: !result.isSettled,
778
+ isSettled: result.isSettled
779
+ });
780
+ if (result.isSettled) {
781
+ PhysicsEngine.unregister(idRef.current);
782
+ (_a = onRestRef.current) == null ? void 0 : _a.call(onRestRef);
783
+ }
784
+ });
785
+ }
786
+ }
787
+ }, [to.x, to.y, immediate]);
788
+ useEffect(() => {
789
+ var _a;
790
+ (_a = springRef.current) == null ? void 0 : _a.updateConfig(resolvedConfig);
791
+ }, [resolvedConfig]);
792
+ const setTarget = useCallback((target) => {
793
+ if (!springRef.current) return;
794
+ springRef.current.setTarget(Vector2.from(target.x, target.y));
795
+ if (!PhysicsEngine.has(idRef.current)) {
796
+ PhysicsEngine.register(idRef.current, (deltaTime) => {
797
+ var _a;
798
+ if (!springRef.current) return;
799
+ const result = springRef.current.step(deltaTime);
800
+ setState({
801
+ x: result.position.x,
802
+ y: result.position.y,
803
+ isAnimating: !result.isSettled,
804
+ isSettled: result.isSettled
805
+ });
806
+ if (result.isSettled) {
807
+ PhysicsEngine.unregister(idRef.current);
808
+ (_a = onRestRef.current) == null ? void 0 : _a.call(onRestRef);
809
+ }
810
+ });
811
+ }
812
+ }, []);
813
+ const snapTo = useCallback((position) => {
814
+ if (!springRef.current) return;
815
+ springRef.current.setPosition(Vector2.from(position.x, position.y));
816
+ springRef.current.setTarget(Vector2.from(position.x, position.y));
817
+ springRef.current.snapToTarget();
818
+ setState({
819
+ x: position.x,
820
+ y: position.y,
821
+ isAnimating: false,
822
+ isSettled: true
823
+ });
824
+ PhysicsEngine.unregister(idRef.current);
825
+ }, []);
826
+ const impulse = useCallback((velocity) => {
827
+ if (!springRef.current) return;
828
+ const currentState = springRef.current.getState();
829
+ springRef.current.setVelocity(
830
+ currentState.velocity.add(Vector2.from(velocity.x, velocity.y))
831
+ );
832
+ if (!PhysicsEngine.has(idRef.current)) {
833
+ PhysicsEngine.register(idRef.current, (deltaTime) => {
834
+ var _a;
835
+ if (!springRef.current) return;
836
+ const result = springRef.current.step(deltaTime);
837
+ setState({
838
+ x: result.position.x,
839
+ y: result.position.y,
840
+ isAnimating: !result.isSettled,
841
+ isSettled: result.isSettled
842
+ });
843
+ if (result.isSettled) {
844
+ PhysicsEngine.unregister(idRef.current);
845
+ (_a = onRestRef.current) == null ? void 0 : _a.call(onRestRef);
846
+ }
847
+ });
848
+ }
849
+ }, []);
850
+ const style = useMemo(
851
+ () => ({
852
+ transform: `translate3d(${state.x}px, ${state.y}px, 0)`,
853
+ willChange: state.isAnimating ? "transform" : "auto"
854
+ }),
855
+ [state.x, state.y, state.isAnimating]
856
+ );
857
+ return {
858
+ x: state.x,
859
+ y: state.y,
860
+ style,
861
+ isAnimating: state.isAnimating,
862
+ isSettled: state.isSettled,
863
+ setTarget,
864
+ snapTo,
865
+ impulse
866
+ };
867
+ }
868
+ function useSpring1D(options) {
869
+ const { to, from, config, onRest, immediate = true } = options;
870
+ const globalConfig = usePhysicsConfig();
871
+ const resolvedConfig = useMemo(
872
+ () => ({ ...DEFAULT_SPRING_CONFIG, ...globalConfig, ...config }),
873
+ [config, globalConfig]
874
+ );
875
+ const springRef = useRef(null);
876
+ const idRef = useRef(generatePhysicsId("spring1d"));
877
+ const onRestRef = useRef(onRest);
878
+ onRestRef.current = onRest;
879
+ const [state, setState] = useState({
880
+ value: from ?? to,
881
+ isAnimating: false,
882
+ isSettled: true
883
+ });
884
+ useEffect(() => {
885
+ springRef.current = new Spring1D(from ?? to, resolvedConfig);
886
+ if (immediate) {
887
+ springRef.current.setTarget(to);
888
+ }
889
+ return () => {
890
+ PhysicsEngine.unregister(idRef.current);
891
+ };
892
+ }, []);
893
+ useEffect(() => {
894
+ if (springRef.current && immediate) {
895
+ springRef.current.setTarget(to);
896
+ if (!PhysicsEngine.has(idRef.current)) {
897
+ PhysicsEngine.register(idRef.current, (deltaTime) => {
898
+ var _a;
899
+ if (!springRef.current) return;
900
+ const result = springRef.current.step(deltaTime);
901
+ setState({
902
+ value: result.value,
903
+ isAnimating: !result.isSettled,
904
+ isSettled: result.isSettled
905
+ });
906
+ if (result.isSettled) {
907
+ PhysicsEngine.unregister(idRef.current);
908
+ (_a = onRestRef.current) == null ? void 0 : _a.call(onRestRef);
909
+ }
910
+ });
911
+ }
912
+ }
913
+ }, [to, immediate]);
914
+ const setTarget = useCallback((target) => {
915
+ if (!springRef.current) return;
916
+ springRef.current.setTarget(target);
917
+ if (!PhysicsEngine.has(idRef.current)) {
918
+ PhysicsEngine.register(idRef.current, (deltaTime) => {
919
+ var _a;
920
+ if (!springRef.current) return;
921
+ const result = springRef.current.step(deltaTime);
922
+ setState({
923
+ value: result.value,
924
+ isAnimating: !result.isSettled,
925
+ isSettled: result.isSettled
926
+ });
927
+ if (result.isSettled) {
928
+ PhysicsEngine.unregister(idRef.current);
929
+ (_a = onRestRef.current) == null ? void 0 : _a.call(onRestRef);
930
+ }
931
+ });
932
+ }
933
+ }, []);
934
+ return {
935
+ value: state.value,
936
+ isAnimating: state.isAnimating,
937
+ isSettled: state.isSettled,
938
+ setTarget
939
+ };
940
+ }
941
+ function useGesture(options) {
942
+ const {
943
+ onDragStart,
944
+ onDrag,
945
+ onDragEnd,
946
+ onFlick,
947
+ onHover,
948
+ onFocus,
949
+ threshold = 3,
950
+ flickThreshold = 500,
951
+ preventTouch = true
952
+ } = options;
953
+ const velocityTracker = useRef(new VelocityTracker());
954
+ const startPosition = useRef(Vector2.zero());
955
+ const lastPosition = useRef(Vector2.zero());
956
+ const isDragging = useRef(false);
957
+ const hasExceededThreshold = useRef(false);
958
+ const pointerId = useRef(null);
959
+ const elementRef = useRef(null);
960
+ const handlersRef = useRef({ onDragStart, onDrag, onDragEnd, onFlick });
961
+ handlersRef.current = { onDragStart, onDrag, onDragEnd, onFlick };
962
+ const createGestureState = useCallback(
963
+ (event, first, last) => {
964
+ const position = Vector2.from(event.clientX, event.clientY);
965
+ const delta = position.subtract(lastPosition.current);
966
+ const offset = position.subtract(startPosition.current);
967
+ const velocity = velocityTracker.current.getVelocity();
968
+ const direction = velocityTracker.current.getDirection();
969
+ return {
970
+ position,
971
+ delta,
972
+ offset,
973
+ velocity,
974
+ direction,
975
+ active: !last,
976
+ first,
977
+ last,
978
+ event
979
+ };
980
+ },
981
+ []
982
+ );
983
+ const handlePointerMove = useCallback(
984
+ (event) => {
985
+ var _a, _b, _c, _d;
986
+ if (!isDragging.current || pointerId.current !== event.pointerId) return;
987
+ const position = Vector2.from(event.clientX, event.clientY);
988
+ velocityTracker.current.addSample(position);
989
+ if (!hasExceededThreshold.current) {
990
+ const distance = position.distanceTo(startPosition.current);
991
+ if (distance < threshold) return;
992
+ hasExceededThreshold.current = true;
993
+ const state2 = createGestureState(event, true, false);
994
+ (_b = (_a = handlersRef.current).onDragStart) == null ? void 0 : _b.call(_a, state2);
995
+ }
996
+ const state = createGestureState(event, false, false);
997
+ (_d = (_c = handlersRef.current).onDrag) == null ? void 0 : _d.call(_c, state);
998
+ lastPosition.current = position;
999
+ },
1000
+ [threshold, createGestureState]
1001
+ );
1002
+ const handlePointerUp = useCallback(
1003
+ (event) => {
1004
+ var _a, _b, _c, _d;
1005
+ if (!isDragging.current || pointerId.current !== event.pointerId) return;
1006
+ isDragging.current = false;
1007
+ pointerId.current = null;
1008
+ document.removeEventListener("pointermove", handlePointerMove);
1009
+ document.removeEventListener("pointerup", handlePointerUp);
1010
+ document.removeEventListener("pointercancel", handlePointerUp);
1011
+ if (hasExceededThreshold.current) {
1012
+ const state = createGestureState(event, false, true);
1013
+ (_b = (_a = handlersRef.current).onDragEnd) == null ? void 0 : _b.call(_a, state);
1014
+ const velocity = velocityTracker.current.getVelocity();
1015
+ if (velocity.magnitude > flickThreshold) {
1016
+ (_d = (_c = handlersRef.current).onFlick) == null ? void 0 : _d.call(_c, { ...state, flickVelocity: velocity });
1017
+ }
1018
+ }
1019
+ velocityTracker.current.reset();
1020
+ hasExceededThreshold.current = false;
1021
+ },
1022
+ [handlePointerMove, createGestureState, flickThreshold]
1023
+ );
1024
+ const onPointerDown = useCallback(
1025
+ (e) => {
1026
+ if (isDragging.current) return;
1027
+ const element = e.currentTarget;
1028
+ elementRef.current = element;
1029
+ element.setPointerCapture(e.pointerId);
1030
+ pointerId.current = e.pointerId;
1031
+ isDragging.current = true;
1032
+ hasExceededThreshold.current = false;
1033
+ const position = Vector2.from(e.clientX, e.clientY);
1034
+ startPosition.current = position;
1035
+ lastPosition.current = position;
1036
+ velocityTracker.current.reset();
1037
+ velocityTracker.current.addSample(position);
1038
+ document.addEventListener("pointermove", handlePointerMove);
1039
+ document.addEventListener("pointerup", handlePointerUp);
1040
+ document.addEventListener("pointercancel", handlePointerUp);
1041
+ },
1042
+ [handlePointerMove, handlePointerUp]
1043
+ );
1044
+ const onPointerEnter = useCallback(() => {
1045
+ onHover == null ? void 0 : onHover(true);
1046
+ }, [onHover]);
1047
+ const onPointerLeave = useCallback(() => {
1048
+ onHover == null ? void 0 : onHover(false);
1049
+ }, [onHover]);
1050
+ const handleFocus = useCallback(() => {
1051
+ onFocus == null ? void 0 : onFocus(true);
1052
+ }, [onFocus]);
1053
+ const handleBlur = useCallback(() => {
1054
+ onFocus == null ? void 0 : onFocus(false);
1055
+ }, [onFocus]);
1056
+ useEffect(() => {
1057
+ return () => {
1058
+ document.removeEventListener("pointermove", handlePointerMove);
1059
+ document.removeEventListener("pointerup", handlePointerUp);
1060
+ document.removeEventListener("pointercancel", handlePointerUp);
1061
+ };
1062
+ }, [handlePointerMove, handlePointerUp]);
1063
+ return {
1064
+ onPointerDown,
1065
+ onPointerEnter,
1066
+ onPointerLeave,
1067
+ onFocus: handleFocus,
1068
+ onBlur: handleBlur,
1069
+ style: preventTouch ? { touchAction: "none" } : {}
1070
+ };
1071
+ }
1072
+ function useDrag(options = {}) {
1073
+ const {
1074
+ initial = { x: 0, y: 0 },
1075
+ bounds,
1076
+ rubberBand = true,
1077
+ rubberBandFactor = 0.55,
1078
+ momentum = true,
1079
+ snapTo,
1080
+ snapThreshold = 50,
1081
+ axis = "both",
1082
+ springConfig,
1083
+ frictionConfig,
1084
+ onDragStart,
1085
+ onDrag,
1086
+ onDragEnd,
1087
+ onRest
1088
+ } = options;
1089
+ const globalConfig = usePhysicsConfig();
1090
+ const resolvedSpringConfig = useMemo(
1091
+ () => ({ ...DEFAULT_SPRING_CONFIG, ...globalConfig, ...springConfig }),
1092
+ [globalConfig, springConfig]
1093
+ );
1094
+ const resolvedFrictionConfig = useMemo(
1095
+ () => ({ ...DEFAULT_FRICTION_CONFIG, ...frictionConfig }),
1096
+ [frictionConfig]
1097
+ );
1098
+ const [state, setState] = useState({
1099
+ x: initial.x,
1100
+ y: initial.y,
1101
+ isDragging: false,
1102
+ isMomentum: false
1103
+ });
1104
+ const positionRef = useRef(Vector2.from(initial.x, initial.y));
1105
+ const startPositionRef = useRef(Vector2.zero());
1106
+ const dragOffsetRef = useRef(Vector2.zero());
1107
+ const velocityTracker = useRef(new VelocityTracker());
1108
+ const springRef = useRef(null);
1109
+ const momentumRef = useRef(null);
1110
+ const idRef = useRef(generatePhysicsId("drag"));
1111
+ const pointerIdRef = useRef(null);
1112
+ const handlersRef = useRef({ onDragStart, onDrag, onDragEnd, onRest });
1113
+ handlersRef.current = { onDragStart, onDrag, onDragEnd, onRest };
1114
+ const boundsVec = useMemo(() => {
1115
+ if (!bounds) return null;
1116
+ return {
1117
+ min: Vector2.from(bounds.left ?? -Infinity, bounds.top ?? -Infinity),
1118
+ max: Vector2.from(bounds.right ?? Infinity, bounds.bottom ?? Infinity)
1119
+ };
1120
+ }, [bounds]);
1121
+ const applyRubberBand = useCallback(
1122
+ (pos) => {
1123
+ if (!boundsVec || !rubberBand) return pos;
1124
+ let x = pos.x;
1125
+ let y = pos.y;
1126
+ if (pos.x < boundsVec.min.x) {
1127
+ const over = boundsVec.min.x - pos.x;
1128
+ x = boundsVec.min.x - over * rubberBandFactor;
1129
+ } else if (pos.x > boundsVec.max.x) {
1130
+ const over = pos.x - boundsVec.max.x;
1131
+ x = boundsVec.max.x + over * rubberBandFactor;
1132
+ }
1133
+ if (pos.y < boundsVec.min.y) {
1134
+ const over = boundsVec.min.y - pos.y;
1135
+ y = boundsVec.min.y - over * rubberBandFactor;
1136
+ } else if (pos.y > boundsVec.max.y) {
1137
+ const over = pos.y - boundsVec.max.y;
1138
+ y = boundsVec.max.y + over * rubberBandFactor;
1139
+ }
1140
+ return Vector2.from(x, y);
1141
+ },
1142
+ [boundsVec, rubberBand, rubberBandFactor]
1143
+ );
1144
+ const clampToBounds = useCallback(
1145
+ (pos) => {
1146
+ if (!boundsVec) return pos;
1147
+ return pos.clampComponents(boundsVec.min, boundsVec.max);
1148
+ },
1149
+ [boundsVec]
1150
+ );
1151
+ const findSnapPoint = useCallback(
1152
+ (pos, velocity) => {
1153
+ if (!snapTo || snapTo.length === 0) return null;
1154
+ const projectedPos = pos.add(velocity.multiply(0.1));
1155
+ let nearestPoint = null;
1156
+ let nearestDistance = Infinity;
1157
+ for (const point of snapTo) {
1158
+ const snapVec = Vector2.from(point.x, point.y);
1159
+ const distance = projectedPos.distanceTo(snapVec);
1160
+ if (distance < nearestDistance && distance < snapThreshold) {
1161
+ nearestDistance = distance;
1162
+ nearestPoint = snapVec;
1163
+ }
1164
+ }
1165
+ return nearestPoint;
1166
+ },
1167
+ [snapTo, snapThreshold]
1168
+ );
1169
+ const animateToTarget = useCallback(
1170
+ (target) => {
1171
+ springRef.current = new Spring(positionRef.current, resolvedSpringConfig);
1172
+ springRef.current.setTarget(target);
1173
+ PhysicsEngine.register(idRef.current, (deltaTime) => {
1174
+ var _a, _b;
1175
+ if (!springRef.current) return;
1176
+ const result = springRef.current.step(deltaTime);
1177
+ positionRef.current = result.position;
1178
+ setState((prev) => ({
1179
+ ...prev,
1180
+ x: result.position.x,
1181
+ y: result.position.y,
1182
+ isMomentum: false
1183
+ }));
1184
+ if (result.isSettled) {
1185
+ PhysicsEngine.unregister(idRef.current);
1186
+ (_b = (_a = handlersRef.current).onRest) == null ? void 0 : _b.call(_a, result.position);
1187
+ }
1188
+ });
1189
+ },
1190
+ [resolvedSpringConfig]
1191
+ );
1192
+ const handlePointerMove = useCallback(
1193
+ (e) => {
1194
+ var _a, _b;
1195
+ if (pointerIdRef.current !== e.pointerId) return;
1196
+ const currentPos = Vector2.from(e.clientX, e.clientY);
1197
+ velocityTracker.current.addSample(currentPos);
1198
+ let newPos = currentPos.subtract(dragOffsetRef.current);
1199
+ if (axis === "x") {
1200
+ newPos = Vector2.from(newPos.x, startPositionRef.current.y);
1201
+ } else if (axis === "y") {
1202
+ newPos = Vector2.from(startPositionRef.current.x, newPos.y);
1203
+ }
1204
+ newPos = applyRubberBand(newPos);
1205
+ positionRef.current = newPos;
1206
+ setState((prev) => ({
1207
+ ...prev,
1208
+ x: newPos.x,
1209
+ y: newPos.y
1210
+ }));
1211
+ (_b = (_a = handlersRef.current).onDrag) == null ? void 0 : _b.call(_a, newPos);
1212
+ },
1213
+ [axis, applyRubberBand]
1214
+ );
1215
+ const handlePointerUp = useCallback(
1216
+ (e) => {
1217
+ var _a, _b, _c, _d;
1218
+ if (pointerIdRef.current !== e.pointerId) return;
1219
+ pointerIdRef.current = null;
1220
+ document.removeEventListener("pointermove", handlePointerMove);
1221
+ document.removeEventListener("pointerup", handlePointerUp);
1222
+ document.removeEventListener("pointercancel", handlePointerUp);
1223
+ const velocity = velocityTracker.current.getVelocity();
1224
+ velocityTracker.current.reset();
1225
+ setState((prev) => ({ ...prev, isDragging: false }));
1226
+ (_b = (_a = handlersRef.current).onDragEnd) == null ? void 0 : _b.call(_a, positionRef.current, velocity);
1227
+ const snapPoint = findSnapPoint(positionRef.current, velocity);
1228
+ if (snapPoint) {
1229
+ animateToTarget(snapPoint);
1230
+ return;
1231
+ }
1232
+ if (boundsVec) {
1233
+ const clamped = clampToBounds(positionRef.current);
1234
+ if (!clamped.equals(positionRef.current)) {
1235
+ animateToTarget(clamped);
1236
+ return;
1237
+ }
1238
+ }
1239
+ if (momentum && velocity.magnitude > 100) {
1240
+ setState((prev) => ({ ...prev, isMomentum: true }));
1241
+ momentumRef.current = new Momentum(positionRef.current, resolvedFrictionConfig);
1242
+ momentumRef.current.start(positionRef.current, velocity);
1243
+ PhysicsEngine.register(idRef.current, (deltaTime) => {
1244
+ var _a2, _b2;
1245
+ if (!momentumRef.current) return;
1246
+ const result = momentumRef.current.step(deltaTime);
1247
+ let newPos = result.position;
1248
+ if (boundsVec) {
1249
+ const clamped = clampToBounds(newPos);
1250
+ if (!clamped.equals(newPos)) {
1251
+ momentumRef.current.stop();
1252
+ PhysicsEngine.unregister(idRef.current);
1253
+ positionRef.current = newPos;
1254
+ animateToTarget(clamped);
1255
+ return;
1256
+ }
1257
+ }
1258
+ positionRef.current = newPos;
1259
+ setState((prev) => ({
1260
+ ...prev,
1261
+ x: newPos.x,
1262
+ y: newPos.y,
1263
+ isMomentum: result.isActive
1264
+ }));
1265
+ if (!result.isActive) {
1266
+ PhysicsEngine.unregister(idRef.current);
1267
+ (_b2 = (_a2 = handlersRef.current).onRest) == null ? void 0 : _b2.call(_a2, newPos);
1268
+ }
1269
+ });
1270
+ } else {
1271
+ (_d = (_c = handlersRef.current).onRest) == null ? void 0 : _d.call(_c, positionRef.current);
1272
+ }
1273
+ },
1274
+ [
1275
+ handlePointerMove,
1276
+ momentum,
1277
+ boundsVec,
1278
+ clampToBounds,
1279
+ findSnapPoint,
1280
+ animateToTarget,
1281
+ resolvedFrictionConfig
1282
+ ]
1283
+ );
1284
+ const onPointerDown = useCallback(
1285
+ (e) => {
1286
+ var _a, _b;
1287
+ if (pointerIdRef.current !== null) return;
1288
+ PhysicsEngine.unregister(idRef.current);
1289
+ pointerIdRef.current = e.pointerId;
1290
+ const element = e.currentTarget;
1291
+ element.setPointerCapture(e.pointerId);
1292
+ const pointerPos = Vector2.from(e.clientX, e.clientY);
1293
+ dragOffsetRef.current = pointerPos.subtract(positionRef.current);
1294
+ startPositionRef.current = positionRef.current;
1295
+ velocityTracker.current.reset();
1296
+ velocityTracker.current.addSample(pointerPos);
1297
+ setState((prev) => ({ ...prev, isDragging: true, isMomentum: false }));
1298
+ (_b = (_a = handlersRef.current).onDragStart) == null ? void 0 : _b.call(_a, positionRef.current);
1299
+ document.addEventListener("pointermove", handlePointerMove);
1300
+ document.addEventListener("pointerup", handlePointerUp);
1301
+ document.addEventListener("pointercancel", handlePointerUp);
1302
+ },
1303
+ [handlePointerMove, handlePointerUp]
1304
+ );
1305
+ const setPosition = useCallback((position) => {
1306
+ PhysicsEngine.unregister(idRef.current);
1307
+ positionRef.current = Vector2.from(position.x, position.y);
1308
+ setState((prev) => ({
1309
+ ...prev,
1310
+ x: position.x,
1311
+ y: position.y,
1312
+ isDragging: false,
1313
+ isMomentum: false
1314
+ }));
1315
+ }, []);
1316
+ const snapToPosition = useCallback(
1317
+ (position) => {
1318
+ animateToTarget(Vector2.from(position.x, position.y));
1319
+ },
1320
+ [animateToTarget]
1321
+ );
1322
+ useEffect(() => {
1323
+ return () => {
1324
+ PhysicsEngine.unregister(idRef.current);
1325
+ document.removeEventListener("pointermove", handlePointerMove);
1326
+ document.removeEventListener("pointerup", handlePointerUp);
1327
+ document.removeEventListener("pointercancel", handlePointerUp);
1328
+ };
1329
+ }, [handlePointerMove, handlePointerUp]);
1330
+ const style = useMemo(
1331
+ () => ({
1332
+ transform: `translate3d(${state.x}px, ${state.y}px, 0)`,
1333
+ willChange: state.isDragging || state.isMomentum ? "transform" : "auto",
1334
+ cursor: state.isDragging ? "grabbing" : "grab"
1335
+ }),
1336
+ [state.x, state.y, state.isDragging, state.isMomentum]
1337
+ );
1338
+ return {
1339
+ x: state.x,
1340
+ y: state.y,
1341
+ isDragging: state.isDragging,
1342
+ isMomentum: state.isMomentum,
1343
+ style,
1344
+ bind: {
1345
+ onPointerDown,
1346
+ style: { touchAction: "none" }
1347
+ },
1348
+ setPosition,
1349
+ snapToPosition
1350
+ };
1351
+ }
1352
+ function useStretch(options) {
1353
+ const {
1354
+ velocity,
1355
+ axis = "both",
1356
+ intensity = 0.12,
1357
+ maxVelocity = 2e3
1358
+ } = options;
1359
+ return useMemo(() => {
1360
+ const vel = velocity instanceof Vector2 ? velocity : Vector2.from(velocity.x, velocity.y);
1361
+ const normalizedX = Math.min(Math.abs(vel.x) / maxVelocity, 1);
1362
+ const normalizedY = Math.min(Math.abs(vel.y) / maxVelocity, 1);
1363
+ const easedX = easeOutQuad(normalizedX);
1364
+ const easedY = easeOutQuad(normalizedY);
1365
+ let scaleX = 1;
1366
+ let scaleY = 1;
1367
+ if (axis === "x" || axis === "both") {
1368
+ const stretchX = 1 + easedX * intensity;
1369
+ const compressY = 1 - easedX * intensity * 0.5;
1370
+ if (axis === "x") {
1371
+ scaleX = stretchX;
1372
+ scaleY = compressY;
1373
+ } else {
1374
+ scaleX = stretchX;
1375
+ }
1376
+ }
1377
+ if (axis === "y" || axis === "both") {
1378
+ const stretchY = 1 + easedY * intensity;
1379
+ const compressX = 1 - easedY * intensity * 0.5;
1380
+ if (axis === "y") {
1381
+ scaleY = stretchY;
1382
+ scaleX = compressX;
1383
+ } else {
1384
+ scaleY = stretchY;
1385
+ }
1386
+ }
1387
+ if (axis === "both" && normalizedX > 0 && normalizedY > 0) {
1388
+ if (normalizedX > normalizedY) {
1389
+ scaleX = 1 + easedX * intensity;
1390
+ scaleY = 1 - easedX * intensity * 0.3 + easedY * intensity * 0.5;
1391
+ } else {
1392
+ scaleY = 1 + easedY * intensity;
1393
+ scaleX = 1 - easedY * intensity * 0.3 + easedX * intensity * 0.5;
1394
+ }
1395
+ }
1396
+ scaleX = Math.max(0.85, Math.min(1.15, scaleX));
1397
+ scaleY = Math.max(0.85, Math.min(1.15, scaleY));
1398
+ return {
1399
+ scaleX,
1400
+ scaleY,
1401
+ style: {
1402
+ transform: `scale3d(${scaleX}, ${scaleY}, 1)`,
1403
+ willChange: scaleX !== 1 || scaleY !== 1 ? "transform" : "auto"
1404
+ }
1405
+ };
1406
+ }, [velocity, axis, intensity, maxVelocity]);
1407
+ }
1408
+ function easeOutQuad(t) {
1409
+ return t * (2 - t);
1410
+ }
1411
+ function combineStretchStyle(positionStyle, stretchStyle) {
1412
+ const positionTransform = positionStyle.transform || "";
1413
+ const stretchTransform = stretchStyle.transform || "";
1414
+ return {
1415
+ ...positionStyle,
1416
+ transform: `${positionTransform} ${stretchTransform}`.trim(),
1417
+ willChange: positionStyle.willChange === "transform" || stretchStyle.willChange === "transform" ? "transform" : "auto"
1418
+ };
1419
+ }
1420
+ function useLift(options = {}) {
1421
+ const {
1422
+ trigger = "hover",
1423
+ scale: targetScale = 1.03,
1424
+ shadow = true,
1425
+ shadowIntensity = 1,
1426
+ springConfig,
1427
+ onLift
1428
+ } = options;
1429
+ const [isLifted, setIsLifted] = useState(false);
1430
+ const { value: scale } = useSpring1D({
1431
+ to: isLifted ? targetScale : 1,
1432
+ config: {
1433
+ tension: 300,
1434
+ friction: 20,
1435
+ ...springConfig
1436
+ }
1437
+ });
1438
+ const handleLift = useCallback(
1439
+ (lifted) => {
1440
+ setIsLifted(lifted);
1441
+ onLift == null ? void 0 : onLift(lifted);
1442
+ },
1443
+ [onLift]
1444
+ );
1445
+ const bind = useMemo(() => {
1446
+ switch (trigger) {
1447
+ case "hover":
1448
+ return {
1449
+ onPointerEnter: () => handleLift(true),
1450
+ onPointerLeave: () => handleLift(false)
1451
+ };
1452
+ case "drag":
1453
+ return {
1454
+ onPointerDown: () => handleLift(true),
1455
+ onPointerUp: () => handleLift(false)
1456
+ };
1457
+ case "focus":
1458
+ return {
1459
+ onFocus: () => handleLift(true),
1460
+ onBlur: () => handleLift(false)
1461
+ };
1462
+ case "active":
1463
+ return {
1464
+ onPointerDown: () => handleLift(true),
1465
+ onPointerUp: () => handleLift(false),
1466
+ onPointerLeave: () => handleLift(false)
1467
+ };
1468
+ default:
1469
+ return {};
1470
+ }
1471
+ }, [trigger, handleLift]);
1472
+ const shadowStyle = useMemo(() => {
1473
+ if (!shadow) return {};
1474
+ const liftAmount = (scale - 1) / (targetScale - 1);
1475
+ const clampedLift = Math.max(0, Math.min(1, liftAmount));
1476
+ const blur = 8 + clampedLift * 24 * shadowIntensity;
1477
+ const spread = clampedLift * 4 * shadowIntensity;
1478
+ const yOffset = 2 + clampedLift * 12 * shadowIntensity;
1479
+ const opacity = 0.08 + clampedLift * 0.12 * shadowIntensity;
1480
+ return {
1481
+ boxShadow: `0 ${yOffset}px ${blur}px ${spread}px rgba(0, 0, 0, ${opacity})`
1482
+ };
1483
+ }, [scale, targetScale, shadow, shadowIntensity]);
1484
+ const style = useMemo(
1485
+ () => ({
1486
+ transform: `scale3d(${scale}, ${scale}, 1)`,
1487
+ transformOrigin: "center center",
1488
+ willChange: scale !== 1 ? "transform, box-shadow" : "auto",
1489
+ ...shadowStyle
1490
+ }),
1491
+ [scale, shadowStyle]
1492
+ );
1493
+ return {
1494
+ isLifted,
1495
+ scale,
1496
+ style,
1497
+ bind,
1498
+ setLifted: handleLift
1499
+ };
1500
+ }
1501
+ function combineLiftStyle(baseStyle, liftStyle) {
1502
+ const baseTransform = baseStyle.transform || "";
1503
+ const liftTransform = liftStyle.transform || "";
1504
+ return {
1505
+ ...baseStyle,
1506
+ ...liftStyle,
1507
+ transform: `${baseTransform} ${liftTransform}`.trim()
1508
+ };
1509
+ }
1510
+ function useFlick(options = {}) {
1511
+ const {
1512
+ initial = { x: 0, y: 0 },
1513
+ threshold = 500,
1514
+ friction,
1515
+ axis = "both",
1516
+ onFlickStart,
1517
+ onFlick,
1518
+ onFlickEnd
1519
+ } = options;
1520
+ const resolvedFriction = useMemo(
1521
+ () => ({ ...DEFAULT_FRICTION_CONFIG, ...friction }),
1522
+ [friction]
1523
+ );
1524
+ const [state, setState] = useState({
1525
+ x: initial.x,
1526
+ y: initial.y,
1527
+ isFlicking: false,
1528
+ velocity: Vector2.zero()
1529
+ });
1530
+ const positionRef = useRef(Vector2.from(initial.x, initial.y));
1531
+ const momentumRef = useRef(null);
1532
+ const velocityTracker = useRef(new VelocityTracker());
1533
+ const idRef = useRef(generatePhysicsId("flick"));
1534
+ const handlersRef = useRef({ onFlickStart, onFlick, onFlickEnd });
1535
+ handlersRef.current = { onFlickStart, onFlick, onFlickEnd };
1536
+ const startMomentum = useCallback(
1537
+ (velocity) => {
1538
+ var _a, _b;
1539
+ let constrainedVelocity = velocity;
1540
+ if (axis === "x") {
1541
+ constrainedVelocity = Vector2.from(velocity.x, 0);
1542
+ } else if (axis === "y") {
1543
+ constrainedVelocity = Vector2.from(0, velocity.y);
1544
+ }
1545
+ momentumRef.current = new Momentum(positionRef.current, resolvedFriction);
1546
+ momentumRef.current.start(positionRef.current, constrainedVelocity);
1547
+ setState((prev) => ({
1548
+ ...prev,
1549
+ isFlicking: true,
1550
+ velocity: constrainedVelocity
1551
+ }));
1552
+ (_b = (_a = handlersRef.current).onFlickStart) == null ? void 0 : _b.call(_a, constrainedVelocity);
1553
+ PhysicsEngine.register(idRef.current, (deltaTime) => {
1554
+ var _a2, _b2, _c, _d;
1555
+ if (!momentumRef.current) return;
1556
+ const result = momentumRef.current.step(deltaTime);
1557
+ let newPos = result.position;
1558
+ if (axis === "x") {
1559
+ newPos = Vector2.from(newPos.x, positionRef.current.y);
1560
+ } else if (axis === "y") {
1561
+ newPos = Vector2.from(positionRef.current.x, newPos.y);
1562
+ }
1563
+ positionRef.current = newPos;
1564
+ setState({
1565
+ x: newPos.x,
1566
+ y: newPos.y,
1567
+ isFlicking: result.isActive,
1568
+ velocity: result.velocity
1569
+ });
1570
+ (_b2 = (_a2 = handlersRef.current).onFlick) == null ? void 0 : _b2.call(_a2, newPos, result.velocity);
1571
+ if (!result.isActive) {
1572
+ PhysicsEngine.unregister(idRef.current);
1573
+ (_d = (_c = handlersRef.current).onFlickEnd) == null ? void 0 : _d.call(_c, newPos);
1574
+ }
1575
+ });
1576
+ },
1577
+ [axis, resolvedFriction]
1578
+ );
1579
+ const flick = useCallback(
1580
+ (velocity) => {
1581
+ PhysicsEngine.unregister(idRef.current);
1582
+ velocityTracker.current.reset();
1583
+ startMomentum(Vector2.from(velocity.x, velocity.y));
1584
+ },
1585
+ [startMomentum]
1586
+ );
1587
+ const stop = useCallback(() => {
1588
+ var _a;
1589
+ PhysicsEngine.unregister(idRef.current);
1590
+ (_a = momentumRef.current) == null ? void 0 : _a.stop();
1591
+ velocityTracker.current.reset();
1592
+ setState((prev) => ({
1593
+ ...prev,
1594
+ isFlicking: false,
1595
+ velocity: Vector2.zero()
1596
+ }));
1597
+ }, []);
1598
+ const setPosition = useCallback(
1599
+ (position) => {
1600
+ stop();
1601
+ positionRef.current = Vector2.from(position.x, position.y);
1602
+ setState({
1603
+ x: position.x,
1604
+ y: position.y,
1605
+ isFlicking: false,
1606
+ velocity: Vector2.zero()
1607
+ });
1608
+ },
1609
+ [stop]
1610
+ );
1611
+ const recordSample = useCallback((position) => {
1612
+ velocityTracker.current.addSample(Vector2.from(position.x, position.y));
1613
+ positionRef.current = Vector2.from(position.x, position.y);
1614
+ setState((prev) => ({
1615
+ ...prev,
1616
+ x: position.x,
1617
+ y: position.y
1618
+ }));
1619
+ }, []);
1620
+ const getCurrentVelocity = useCallback(() => {
1621
+ return velocityTracker.current.getVelocity();
1622
+ }, []);
1623
+ const release = useCallback(
1624
+ (position) => {
1625
+ positionRef.current = Vector2.from(position.x, position.y);
1626
+ const velocity = velocityTracker.current.getVelocity();
1627
+ velocityTracker.current.reset();
1628
+ if (velocity.magnitude >= threshold) {
1629
+ startMomentum(velocity);
1630
+ return true;
1631
+ }
1632
+ setState((prev) => ({
1633
+ ...prev,
1634
+ x: position.x,
1635
+ y: position.y,
1636
+ isFlicking: false,
1637
+ velocity: Vector2.zero()
1638
+ }));
1639
+ return false;
1640
+ },
1641
+ [threshold, startMomentum]
1642
+ );
1643
+ useEffect(() => {
1644
+ return () => {
1645
+ PhysicsEngine.unregister(idRef.current);
1646
+ };
1647
+ }, []);
1648
+ const style = useMemo(
1649
+ () => ({
1650
+ transform: `translate3d(${state.x}px, ${state.y}px, 0)`,
1651
+ willChange: state.isFlicking ? "transform" : "auto"
1652
+ }),
1653
+ [state.x, state.y, state.isFlicking]
1654
+ );
1655
+ return {
1656
+ x: state.x,
1657
+ y: state.y,
1658
+ isFlicking: state.isFlicking,
1659
+ velocity: state.velocity,
1660
+ style,
1661
+ flick,
1662
+ stop,
1663
+ setPosition,
1664
+ recordSample,
1665
+ getCurrentVelocity,
1666
+ release
1667
+ };
1668
+ }
1669
+ const Draggable = forwardRef(
1670
+ ({
1671
+ children,
1672
+ initial = { x: 0, y: 0 },
1673
+ bounds,
1674
+ rubberBand = true,
1675
+ momentum = true,
1676
+ lift: enableLift = true,
1677
+ stretch: enableStretch = true,
1678
+ snapTo,
1679
+ axis = "both",
1680
+ springConfig,
1681
+ className,
1682
+ style: customStyle,
1683
+ onDragStart,
1684
+ onDrag,
1685
+ onDragEnd,
1686
+ onRest
1687
+ }, ref) => {
1688
+ const physicsContext = usePhysicsContext();
1689
+ const {
1690
+ isDragging,
1691
+ isMomentum,
1692
+ style: dragStyle,
1693
+ bind
1694
+ } = useDrag({
1695
+ initial,
1696
+ bounds,
1697
+ rubberBand,
1698
+ momentum,
1699
+ snapTo,
1700
+ axis,
1701
+ springConfig: { ...physicsContext.spring, ...springConfig },
1702
+ onDragStart: (pos) => onDragStart == null ? void 0 : onDragStart(pos.toObject()),
1703
+ onDrag: (pos) => onDrag == null ? void 0 : onDrag(pos.toObject()),
1704
+ onDragEnd: (pos, vel) => onDragEnd == null ? void 0 : onDragEnd(pos.toObject(), vel.toObject()),
1705
+ onRest: (pos) => onRest == null ? void 0 : onRest(pos.toObject())
1706
+ });
1707
+ const { style: liftStyle, bind: liftBind } = useLift({
1708
+ trigger: "drag",
1709
+ scale: physicsContext.lift.scale,
1710
+ shadowIntensity: physicsContext.lift.shadowIntensity
1711
+ });
1712
+ const velocity = useMemo(() => {
1713
+ if (!isDragging && !isMomentum) return Vector2.zero();
1714
+ return Vector2.from(0, 0);
1715
+ }, [isDragging, isMomentum]);
1716
+ const { style: stretchStyle } = useStretch({
1717
+ velocity,
1718
+ intensity: physicsContext.stretch.intensity,
1719
+ maxVelocity: physicsContext.stretch.maxVelocity
1720
+ });
1721
+ const combinedStyle = useMemo(() => {
1722
+ let result = { ...dragStyle };
1723
+ if (enableLift && isDragging) {
1724
+ result = combineLiftStyle(result, liftStyle);
1725
+ }
1726
+ if (enableStretch) {
1727
+ result = combineStretchStyle(result, stretchStyle);
1728
+ }
1729
+ return {
1730
+ ...result,
1731
+ ...customStyle,
1732
+ position: "relative",
1733
+ userSelect: "none"
1734
+ };
1735
+ }, [dragStyle, liftStyle, stretchStyle, enableLift, enableStretch, isDragging, customStyle]);
1736
+ const combinedBind = useMemo(() => {
1737
+ return {
1738
+ ...bind,
1739
+ onPointerDown: (e) => {
1740
+ var _a;
1741
+ bind.onPointerDown(e);
1742
+ if (enableLift) {
1743
+ (_a = liftBind.onPointerDown) == null ? void 0 : _a.call(liftBind);
1744
+ }
1745
+ },
1746
+ onPointerUp: () => {
1747
+ var _a;
1748
+ if (enableLift) {
1749
+ (_a = liftBind.onPointerUp) == null ? void 0 : _a.call(liftBind);
1750
+ }
1751
+ }
1752
+ };
1753
+ }, [bind, liftBind, enableLift]);
1754
+ return /* @__PURE__ */ jsx(
1755
+ "div",
1756
+ {
1757
+ ref,
1758
+ className,
1759
+ ...combinedBind,
1760
+ style: combinedStyle,
1761
+ children
1762
+ }
1763
+ );
1764
+ }
1765
+ );
1766
+ Draggable.displayName = "Draggable";
1767
+ const Card = forwardRef(
1768
+ ({
1769
+ children,
1770
+ lift: enableLift = true,
1771
+ pressable = true,
1772
+ liftScale,
1773
+ pressScale = 0.98,
1774
+ glass = false,
1775
+ glassBlur = 20,
1776
+ springConfig,
1777
+ className,
1778
+ style: customStyle,
1779
+ onClick,
1780
+ onHover,
1781
+ onPress
1782
+ }, ref) => {
1783
+ const physicsContext = usePhysicsContext();
1784
+ const [isPressed, setIsPressed] = useState(false);
1785
+ const resolvedLiftScale = liftScale ?? physicsContext.lift.scale;
1786
+ const { style: liftStyle, bind: liftBind, isLifted } = useLift({
1787
+ trigger: "hover",
1788
+ scale: resolvedLiftScale,
1789
+ shadowIntensity: physicsContext.lift.shadowIntensity,
1790
+ springConfig: { ...physicsContext.spring, ...springConfig },
1791
+ onLift: onHover
1792
+ });
1793
+ const { value: pressScaleValue } = useSpring1D({
1794
+ to: isPressed ? pressScale : 1,
1795
+ config: {
1796
+ tension: 400,
1797
+ friction: 25,
1798
+ ...springConfig
1799
+ }
1800
+ });
1801
+ const handlePointerDown = useCallback(() => {
1802
+ if (pressable) {
1803
+ setIsPressed(true);
1804
+ onPress == null ? void 0 : onPress(true);
1805
+ }
1806
+ }, [pressable, onPress]);
1807
+ const handlePointerUp = useCallback(() => {
1808
+ if (pressable) {
1809
+ setIsPressed(false);
1810
+ onPress == null ? void 0 : onPress(false);
1811
+ }
1812
+ }, [pressable, onPress]);
1813
+ const handlePointerLeave = useCallback(() => {
1814
+ if (isPressed) {
1815
+ setIsPressed(false);
1816
+ onPress == null ? void 0 : onPress(false);
1817
+ }
1818
+ }, [isPressed, onPress]);
1819
+ const glassStyles = glass ? {
1820
+ background: "rgba(255, 255, 255, 0.1)",
1821
+ backdropFilter: `blur(${glassBlur}px)`,
1822
+ WebkitBackdropFilter: `blur(${glassBlur}px)`,
1823
+ border: "1px solid rgba(255, 255, 255, 0.18)"
1824
+ } : {};
1825
+ const combinedStyle = useMemo(() => {
1826
+ const liftTransform = enableLift ? liftStyle.transform || "" : "";
1827
+ const pressTransform = pressable ? `scale(${pressScaleValue})` : "";
1828
+ const transform = `${liftTransform} ${pressTransform}`.trim() || void 0;
1829
+ return {
1830
+ ...glassStyles,
1831
+ ...liftStyle,
1832
+ ...customStyle,
1833
+ transform,
1834
+ transformOrigin: "center center",
1835
+ cursor: onClick ? "pointer" : "default",
1836
+ willChange: isLifted || isPressed ? "transform, box-shadow" : "auto"
1837
+ };
1838
+ }, [
1839
+ liftStyle,
1840
+ pressScaleValue,
1841
+ enableLift,
1842
+ pressable,
1843
+ glassStyles,
1844
+ customStyle,
1845
+ onClick,
1846
+ isLifted,
1847
+ isPressed
1848
+ ]);
1849
+ return /* @__PURE__ */ jsx(
1850
+ "div",
1851
+ {
1852
+ ref,
1853
+ className,
1854
+ style: combinedStyle,
1855
+ onClick,
1856
+ onPointerEnter: liftBind.onPointerEnter,
1857
+ onPointerLeave: () => {
1858
+ var _a;
1859
+ (_a = liftBind.onPointerLeave) == null ? void 0 : _a.call(liftBind);
1860
+ handlePointerLeave();
1861
+ },
1862
+ onPointerDown: handlePointerDown,
1863
+ onPointerUp: handlePointerUp,
1864
+ children
1865
+ }
1866
+ );
1867
+ }
1868
+ );
1869
+ Card.displayName = "Card";
1870
+ const cardBaseStyles = {
1871
+ padding: "24px",
1872
+ borderRadius: "16px",
1873
+ background: "white",
1874
+ boxShadow: "0 4px 12px rgba(0, 0, 0, 0.08)"
1875
+ };
1876
+ export {
1877
+ Card,
1878
+ DEFAULT_FRICTION_CONFIG,
1879
+ DEFAULT_SPRING_CONFIG,
1880
+ Draggable,
1881
+ Momentum,
1882
+ Momentum1D,
1883
+ PhysicsEngine,
1884
+ PhysicsProvider,
1885
+ Spring,
1886
+ Spring1D,
1887
+ Vector2,
1888
+ VelocityTracker,
1889
+ VelocityTracker1D,
1890
+ cardBaseStyles,
1891
+ combineLiftStyle,
1892
+ combineStretchStyle,
1893
+ generatePhysicsId,
1894
+ gentle,
1895
+ getFrictionConfig,
1896
+ getPreset,
1897
+ getSpringConfig,
1898
+ ios,
1899
+ presets,
1900
+ smooth,
1901
+ snappy,
1902
+ stiff,
1903
+ useDrag,
1904
+ useFlick,
1905
+ useFrictionConfig,
1906
+ useGesture,
1907
+ useLift,
1908
+ useLiftConfig,
1909
+ usePhysicsConfig,
1910
+ usePhysicsContext,
1911
+ useSpring,
1912
+ useSpring1D,
1913
+ useStretch,
1914
+ useStretchConfig
1915
+ };
1916
+ //# sourceMappingURL=index.js.map