@bug-on/m3-expressive 1.3.5 → 1.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/buttons.d.mts +3 -3
  3. package/dist/buttons.d.ts +3 -3
  4. package/dist/buttons.js +110 -91
  5. package/dist/buttons.js.map +1 -1
  6. package/dist/buttons.mjs +110 -91
  7. package/dist/buttons.mjs.map +1 -1
  8. package/dist/{core-Cckt8qEm.d.ts → core-DamhB5Yd.d.ts} +1 -1
  9. package/dist/{core-DDfG4pym.d.mts → core-Wsfqn9_N.d.mts} +1 -1
  10. package/dist/core.d.mts +2 -2
  11. package/dist/core.d.ts +2 -2
  12. package/dist/core.js +1 -1
  13. package/dist/core.js.map +1 -1
  14. package/dist/core.mjs +1 -1
  15. package/dist/core.mjs.map +1 -1
  16. package/dist/{fading-blur-mask--uZbBKLj.d.ts → fading-blur-mask-CX-bwjNU.d.mts} +6 -1
  17. package/dist/{fading-blur-mask--uZbBKLj.d.mts → fading-blur-mask-D57UMguz.d.ts} +6 -1
  18. package/dist/feedback.d.mts +17 -5
  19. package/dist/feedback.d.ts +17 -5
  20. package/dist/feedback.js +1 -1
  21. package/dist/feedback.js.map +1 -1
  22. package/dist/feedback.mjs +1 -1
  23. package/dist/feedback.mjs.map +1 -1
  24. package/dist/forms.d.mts +3 -3
  25. package/dist/forms.d.ts +3 -3
  26. package/dist/forms.js +16 -6
  27. package/dist/forms.js.map +1 -1
  28. package/dist/forms.mjs +16 -6
  29. package/dist/forms.mjs.map +1 -1
  30. package/dist/{icon-button-CxyJv7UV.d.mts → icon-button-BpX9_fwZ.d.mts} +1 -1
  31. package/dist/{icon-button-Di8VX6ou.d.ts → icon-button-DFXp9hze.d.ts} +1 -1
  32. package/dist/index.d.mts +11 -10
  33. package/dist/index.d.ts +11 -10
  34. package/dist/index.js +1790 -1133
  35. package/dist/index.js.map +1 -1
  36. package/dist/index.mjs +1640 -988
  37. package/dist/index.mjs.map +1 -1
  38. package/dist/layout.d.mts +92 -23
  39. package/dist/layout.d.ts +92 -23
  40. package/dist/layout.js +118 -13
  41. package/dist/layout.js.map +1 -1
  42. package/dist/layout.mjs +118 -15
  43. package/dist/layout.mjs.map +1 -1
  44. package/dist/{md3-expressive-shapes-CPcfl_Hf.d.mts → md3-expressive-shapes-wk_98LJh.d.mts} +20 -1
  45. package/dist/{md3-expressive-shapes-CPcfl_Hf.d.ts → md3-expressive-shapes-wk_98LJh.d.ts} +20 -1
  46. package/dist/md3-xBl4wgND.d.mts +44 -0
  47. package/dist/md3-xBl4wgND.d.ts +44 -0
  48. package/dist/navigation.d.mts +5 -5
  49. package/dist/navigation.d.ts +5 -5
  50. package/dist/navigation.js +74 -21
  51. package/dist/navigation.js.map +1 -1
  52. package/dist/navigation.mjs +74 -21
  53. package/dist/navigation.mjs.map +1 -1
  54. package/dist/overlays.d.mts +4 -2
  55. package/dist/overlays.d.ts +4 -2
  56. package/dist/overlays.js +85 -44
  57. package/dist/overlays.js.map +1 -1
  58. package/dist/overlays.mjs +85 -44
  59. package/dist/overlays.mjs.map +1 -1
  60. package/dist/pickers.js +89 -56
  61. package/dist/pickers.js.map +1 -1
  62. package/dist/pickers.mjs +89 -56
  63. package/dist/pickers.mjs.map +1 -1
  64. package/dist/shapes.d.mts +150 -17
  65. package/dist/shapes.d.ts +150 -17
  66. package/dist/shapes.js +618 -126
  67. package/dist/shapes.js.map +1 -1
  68. package/dist/shapes.mjs +614 -127
  69. package/dist/shapes.mjs.map +1 -1
  70. package/dist/{side-sheet-modal-Bd5Qqvp9.d.ts → side-sheet-modal-BfdgYjd3.d.ts} +70 -0
  71. package/dist/{side-sheet-modal-64FGhDxL.d.mts → side-sheet-modal-kLfHINFZ.d.mts} +70 -0
  72. package/dist/{split-button-trailing-uncheckable-CGzk7KcG.d.ts → split-button-trailing-uncheckable-BpyIlOdn.d.ts} +7 -3
  73. package/dist/{split-button-trailing-uncheckable-gAz6OAIi.d.mts → split-button-trailing-uncheckable-CpT8iPpr.d.mts} +7 -3
  74. package/dist/{text-field-DARNdj14.d.ts → text-field-Ct-LT_2T.d.ts} +23 -4
  75. package/dist/{text-field-4OlT9o8s.d.mts → text-field-D5xiGn-q.d.mts} +23 -4
  76. package/llms-full.txt +25 -3
  77. package/llms.txt +4 -1
  78. package/package.json +1 -1
  79. package/dist/md3-BQhRygSi.d.mts +0 -31
  80. package/dist/md3-BQhRygSi.d.ts +0 -31
package/dist/shapes.mjs CHANGED
@@ -36,6 +36,13 @@ function transformFeature(feature, fn) {
36
36
  }
37
37
  return edgeFeature(cubics);
38
38
  }
39
+ function reverseFeature(feature) {
40
+ const cubics = feature.cubics.slice().reverse().map((c) => c.reverse());
41
+ if (feature.type === "corner") {
42
+ return cornerFeature(cubics, feature.convex);
43
+ }
44
+ return edgeFeature(cubics);
45
+ }
39
46
 
40
47
  // src/shapes/math/corner-rounding.ts
41
48
  function cornerRounding(radius = 0, smoothing = 0) {
@@ -80,6 +87,10 @@ function convex(prevX, prevY, currX, currY, nextX, nextY) {
80
87
  const v2y = nextY - currY;
81
88
  return v1x * v2y - v1y * v2x > 0;
82
89
  }
90
+ function formatCoordinate(v) {
91
+ const rounded = Math.round(v * 1e4) / 1e4;
92
+ return (rounded === 0 ? 0 : rounded).toString();
93
+ }
83
94
 
84
95
  // src/shapes/math/point.ts
85
96
  function point(x, y) {
@@ -179,9 +190,16 @@ var Cubic = class _Cubic {
179
190
  */
180
191
  pointOnCurve(t) {
181
192
  const u = 1 - t;
193
+ const uu = u * u;
194
+ const tt = t * t;
195
+ const uuu = uu * u;
196
+ const ttt = tt * t;
197
+ const c0 = 3 * t * uu;
198
+ const c1 = 3 * tt * u;
199
+ const p = this.points;
182
200
  return point(
183
- this.anchor0X * (u * u * u) + this.control0X * (3 * t * u * u) + this.control1X * (3 * t * t * u) + this.anchor1X * (t * t * t),
184
- this.anchor0Y * (u * u * u) + this.control0Y * (3 * t * u * u) + this.control1Y * (3 * t * t * u) + this.anchor1Y * (t * t * t)
201
+ p[0] * uuu + p[2] * c0 + p[4] * c1 + p[6] * ttt,
202
+ p[1] * uuu + p[3] * c0 + p[5] * c1 + p[7] * ttt
185
203
  );
186
204
  }
187
205
  /**
@@ -404,13 +422,17 @@ var MutableCubic = class extends Cubic {
404
422
  * @param progress - Interpolation factor [0, 1]
405
423
  */
406
424
  interpolate(c1, c2, progress) {
407
- for (let i = 0; i < 8; i++) {
408
- this.points[i] = interpolate(
409
- c1.points[i],
410
- c2.points[i],
411
- progress
412
- );
413
- }
425
+ const p1 = c1.points;
426
+ const p2 = c2.points;
427
+ const p = this.points;
428
+ p[0] = p1[0] + (p2[0] - p1[0]) * progress;
429
+ p[1] = p1[1] + (p2[1] - p1[1]) * progress;
430
+ p[2] = p1[2] + (p2[2] - p1[2]) * progress;
431
+ p[3] = p1[3] + (p2[3] - p1[3]) * progress;
432
+ p[4] = p1[4] + (p2[4] - p1[4]) * progress;
433
+ p[5] = p1[5] + (p2[5] - p1[5]) * progress;
434
+ p[6] = p1[6] + (p2[6] - p1[6]) * progress;
435
+ p[7] = p1[7] + (p2[7] - p1[7]) * progress;
414
436
  }
415
437
  /**
416
438
  * Mutably applies a PointTransformer to all points.
@@ -822,6 +844,36 @@ var RoundedPolygon = class _RoundedPolygon {
822
844
  this.centerY + d
823
845
  ];
824
846
  }
847
+ /**
848
+ * Calculates the approximate signed area of the polygon using anchor points.
849
+ * Returns > 0 for Clockwise (in screen coords, y down), < 0 for Counter-Clockwise.
850
+ */
851
+ calculateSignedArea() {
852
+ let area = 0;
853
+ const n = this.cubics.length;
854
+ for (let i = 0; i < n; i++) {
855
+ const c = this.cubics[i];
856
+ area += c.anchor0X * c.anchor1Y - c.anchor1X * c.anchor0Y;
857
+ }
858
+ return area / 2;
859
+ }
860
+ /**
861
+ * Returns true if the polygon outline runs in Clockwise order (screen coords).
862
+ */
863
+ isClockwise() {
864
+ return this.calculateSignedArea() > 0;
865
+ }
866
+ /**
867
+ * Returns a reversed copy of the polygon with reversed traversal order.
868
+ * Flips the winding direction (CW ↔ CCW).
869
+ */
870
+ reversed() {
871
+ const reversedFeatures = this.features.slice().reverse().map((f) => reverseFeature(f));
872
+ return new _RoundedPolygon(
873
+ reversedFeatures,
874
+ point(this.centerX, this.centerY)
875
+ );
876
+ }
825
877
  };
826
878
  function buildCubicsList(features) {
827
879
  const result = [];
@@ -890,10 +942,21 @@ function validateContiguity(cubics) {
890
942
 
891
943
  // src/shapes/catalog/md3-expressive-shapes.ts
892
944
  function createShape(pts) {
893
- const cubics = pts.map((p) => new Cubic(p));
894
- return RoundedPolygon.fromFeatures(
895
- cubics.map((c) => cornerFeature([c], false))
896
- );
945
+ let rawArea = 0;
946
+ for (const p of pts) {
947
+ rawArea += p[0] * p[7] - p[6] * p[1];
948
+ }
949
+ const normalizedPts = rawArea < 0 ? pts.slice().reverse().map((p) => [p[6], p[7], p[4], p[5], p[2], p[3], p[0], p[1]]) : pts;
950
+ const cubics = normalizedPts.map((p) => new Cubic(p));
951
+ const n = cubics.length;
952
+ const features = cubics.map((c, i) => {
953
+ const prev = cubics[(i - 1 + n) % n].pointOnCurve(0.5);
954
+ const curr = c.pointOnCurve(0.5);
955
+ const next = cubics[(i + 1) % n].pointOnCurve(0.5);
956
+ const isConvex = convex(prev.x, prev.y, curr.x, curr.y, next.x, next.y);
957
+ return cornerFeature([c], isConvex);
958
+ });
959
+ return RoundedPolygon.fromFeatures(features);
897
960
  }
898
961
  var archShape = createShape([
899
962
  [1, 0.83461, 1, 0.8547, 1, 0.86477, 0.99898, 0.87322],
@@ -1991,6 +2054,94 @@ function resolveShape(ref) {
1991
2054
  return shape;
1992
2055
  }
1993
2056
 
2057
+ // src/shapes/catalog/shape-pairings.ts
2058
+ var MD3_SHAPE_PAIRINGS = {
2059
+ // ─── Organic & Floral ──────────────────────────────────────────
2060
+ circle: "flower",
2061
+ flower: "sunny",
2062
+ sunny: "verySunny",
2063
+ verySunny: "flower",
2064
+ bun: "clamshell",
2065
+ clamshell: "bun",
2066
+ puffy: "circle",
2067
+ // ─── Starburst & Explosive ─────────────────────────────────────
2068
+ burst: "softBurst",
2069
+ softBurst: "boom",
2070
+ boom: "softBoom",
2071
+ softBoom: "burst",
2072
+ cookie12Sided: "softBurst",
2073
+ // ─── Clover, Gem & Diamond ─────────────────────────────────────
2074
+ diamond: "puffyDiamond",
2075
+ puffyDiamond: "diamond",
2076
+ clover4Leaf: "clover8Leaf",
2077
+ clover8Leaf: "clover4Leaf",
2078
+ gem: "diamond",
2079
+ heart: "clover4Leaf",
2080
+ // ─── Cookies & Geometric Multi-sided ───────────────────────────
2081
+ cookie4Sided: "cookie6Sided",
2082
+ cookie6Sided: "cookie9Sided",
2083
+ cookie7Sided: "cookie12Sided",
2084
+ cookie9Sided: "cookie7Sided",
2085
+ square: "cookie4Sided",
2086
+ // ─── Rounded & Architectural ───────────────────────────────────
2087
+ pill: "oval",
2088
+ oval: "arch",
2089
+ arch: "semiCircle",
2090
+ semiCircle: "pill",
2091
+ slanted: "arch",
2092
+ // ─── Polygonal, Directional & Pixel ────────────────────────────
2093
+ triangle: "arrow",
2094
+ arrow: "pentagon",
2095
+ pentagon: "triangle",
2096
+ fan: "ghostish",
2097
+ ghostish: "fan",
2098
+ pixelTriangle: "pixelCircle",
2099
+ pixelCircle: "circle"
2100
+ };
2101
+ var MD3_SHAPE_FAMILIES = {
2102
+ organic: [
2103
+ "circle",
2104
+ "flower",
2105
+ "sunny",
2106
+ "verySunny",
2107
+ "bun",
2108
+ "clamshell",
2109
+ "puffy"
2110
+ ],
2111
+ starburst: ["burst", "softBurst", "boom", "softBoom", "cookie12Sided"],
2112
+ cloverAndGems: [
2113
+ "diamond",
2114
+ "puffyDiamond",
2115
+ "clover4Leaf",
2116
+ "clover8Leaf",
2117
+ "gem",
2118
+ "heart"
2119
+ ],
2120
+ cookiesAndPolygons: [
2121
+ "cookie4Sided",
2122
+ "cookie6Sided",
2123
+ "cookie7Sided",
2124
+ "cookie9Sided",
2125
+ "square"
2126
+ ],
2127
+ roundedContainers: ["pill", "oval", "arch", "semiCircle", "slanted"],
2128
+ directionalAndPixel: [
2129
+ "triangle",
2130
+ "arrow",
2131
+ "pentagon",
2132
+ "fan",
2133
+ "ghostish",
2134
+ "pixelTriangle",
2135
+ "pixelCircle"
2136
+ ]
2137
+ };
2138
+ function getRecommendedMorphShape(shape) {
2139
+ if (typeof shape === "string" && shape in MD3_SHAPE_PAIRINGS) {
2140
+ return MD3_SHAPE_PAIRINGS[shape];
2141
+ }
2142
+ return "circle";
2143
+ }
2144
+
1994
2145
  // src/shapes/catalog/shape-tokens.ts
1995
2146
  var MD3CornerRadius = {
1996
2147
  /** 0dp — Sharp corners */
@@ -2156,6 +2307,56 @@ function pillStarVertices(width, height, numVerticesPerRadius, innerRadiusRatio,
2156
2307
  return result;
2157
2308
  }
2158
2309
 
2310
+ // src/shapes/morph/feature-mapping.ts
2311
+ function angleDiff(a1, a2) {
2312
+ let diff = Math.abs(a1 - a2);
2313
+ if (diff > Math.PI) diff = 2 * Math.PI - diff;
2314
+ return diff;
2315
+ }
2316
+ var TWO_PI_SQ = 4 * Math.PI * Math.PI;
2317
+ function featureMapper(features1, features2) {
2318
+ const convex1 = features1.filter((f) => f.convex);
2319
+ const convex2 = features2.filter((f) => f.convex);
2320
+ const set1 = convex1.length > 0 ? convex1 : features1;
2321
+ const set2 = convex2.length > 0 ? convex2 : features2;
2322
+ if (set1.length === 0 || set2.length === 0) {
2323
+ return { map: (v) => v, mapBack: (v) => v };
2324
+ }
2325
+ let bestShift = 0;
2326
+ let minCost = Number.MAX_VALUE;
2327
+ for (const f1 of set1) {
2328
+ for (const f2 of set2) {
2329
+ const shift = positiveModulo(f2.progress - f1.progress, 1);
2330
+ let cost = 0;
2331
+ for (const c1 of set1) {
2332
+ const expected = positiveModulo(c1.progress + shift, 1);
2333
+ let dMin = Number.MAX_VALUE;
2334
+ let matchedC2 = set2[0];
2335
+ for (const c2 of set2) {
2336
+ const diff = Math.abs(expected - c2.progress);
2337
+ const d = diff > 0.5 ? 1 - diff : diff;
2338
+ if (d < dMin) {
2339
+ dMin = d;
2340
+ matchedC2 = c2;
2341
+ }
2342
+ }
2343
+ const progErrorSq = dMin * dMin * TWO_PI_SQ;
2344
+ const angDiff = c1.angle !== void 0 && matchedC2.angle !== void 0 ? angleDiff(c1.angle, matchedC2.angle) : 0;
2345
+ cost += progErrorSq + angDiff * angDiff;
2346
+ if (cost >= minCost) break;
2347
+ }
2348
+ if (cost < minCost) {
2349
+ minCost = cost;
2350
+ bestShift = shift;
2351
+ }
2352
+ }
2353
+ }
2354
+ return {
2355
+ map: (v) => positiveModulo(v + bestShift, 1),
2356
+ mapBack: (v) => positiveModulo(v - bestShift, 1)
2357
+ };
2358
+ }
2359
+
2159
2360
  // src/shapes/morph/float-mapping.ts
2160
2361
  function mapSegments(value, segments) {
2161
2362
  for (const seg of segments) {
@@ -2200,32 +2401,6 @@ function createDoubleMapper(fromValues, toValues) {
2200
2401
  };
2201
2402
  }
2202
2403
 
2203
- // src/shapes/morph/feature-mapping.ts
2204
- function featureMapper(features1, features2) {
2205
- const convex1 = features1.filter((f) => f.convex);
2206
- const convex2 = features2.filter((f) => f.convex);
2207
- if (convex1.length === 0 || convex2.length === 0) {
2208
- return createDoubleMapper([], []);
2209
- }
2210
- const fromValues = [];
2211
- const toValues = [];
2212
- for (const f1 of convex1) {
2213
- let bestDist = Number.MAX_VALUE;
2214
- let bestProgress = 0;
2215
- for (const f2 of convex2) {
2216
- const diff = Math.abs(f1.progress - f2.progress);
2217
- const dist = Math.min(diff, 1 - diff);
2218
- if (dist < bestDist) {
2219
- bestDist = dist;
2220
- bestProgress = f2.progress;
2221
- }
2222
- }
2223
- fromValues.push(f1.progress);
2224
- toValues.push(bestProgress);
2225
- }
2226
- return createDoubleMapper(fromValues, toValues);
2227
- }
2228
-
2229
2404
  // src/shapes/morph/polygon-measure.ts
2230
2405
  var MeasuredPolygon = class _MeasuredPolygon {
2231
2406
  constructor(measuredCubics, features) {
@@ -2259,9 +2434,8 @@ var MeasuredPolygon = class _MeasuredPolygon {
2259
2434
  const t = segLen > 0 ? (cutPoint - cutCubic.startOutlineProgress) / segLen : 0;
2260
2435
  const [left, right] = cutCubic.cubic.split(t);
2261
2436
  const shift = (p) => {
2262
- let s = p - cutPoint;
2263
- if (s < 0) s += 1;
2264
- return s;
2437
+ const s = p - cutPoint;
2438
+ return s < 0 ? s + 1 : s;
2265
2439
  };
2266
2440
  if (!right.zeroLength()) {
2267
2441
  result.push({
@@ -2270,9 +2444,19 @@ var MeasuredPolygon = class _MeasuredPolygon {
2270
2444
  endOutlineProgress: shift(cutCubic.endOutlineProgress)
2271
2445
  });
2272
2446
  }
2273
- for (let i = 1; i < this.measuredCubics.length; i++) {
2274
- const idx = (cutCubicIdx + i) % this.measuredCubics.length;
2275
- const mc = this.measuredCubics[idx];
2447
+ const totalCubics = this.measuredCubics.length;
2448
+ for (let i = cutCubicIdx + 1; i < totalCubics; i++) {
2449
+ const mc = this.measuredCubics[i];
2450
+ if (!mc.cubic.zeroLength()) {
2451
+ result.push({
2452
+ cubic: mc.cubic,
2453
+ startOutlineProgress: shift(mc.startOutlineProgress),
2454
+ endOutlineProgress: shift(mc.endOutlineProgress)
2455
+ });
2456
+ }
2457
+ }
2458
+ for (let i = 0; i < cutCubicIdx; i++) {
2459
+ const mc = this.measuredCubics[i];
2276
2460
  if (!mc.cubic.zeroLength()) {
2277
2461
  result.push({
2278
2462
  cubic: mc.cubic,
@@ -2354,7 +2538,16 @@ var MeasuredPolygon = class _MeasuredPolygon {
2354
2538
  const mc = measured[Math.min(cubicIdx + middleCubicIdx, measured.length - 1)];
2355
2539
  if (mc) {
2356
2540
  const midProgress = (mc.startOutlineProgress + mc.endOutlineProgress) / 2;
2357
- featureList.push({ progress: midProgress, convex: feature.convex });
2541
+ const pt = mc.cubic.pointOnCurve(0.5);
2542
+ const angle = Math.atan2(
2543
+ pt.y - polygon.centerY,
2544
+ pt.x - polygon.centerX
2545
+ );
2546
+ featureList.push({
2547
+ progress: midProgress,
2548
+ convex: feature.convex,
2549
+ angle
2550
+ });
2358
2551
  }
2359
2552
  }
2360
2553
  cubicIdx += feature.cubics.length;
@@ -2364,18 +2557,30 @@ var MeasuredPolygon = class _MeasuredPolygon {
2364
2557
  };
2365
2558
  function approximateLength(cubic) {
2366
2559
  let len = 0;
2367
- let prevX = cubic.anchor0X, prevY = cubic.anchor0Y;
2560
+ const p = cubic.points;
2561
+ let prevX = p[0];
2562
+ let prevY = p[1];
2368
2563
  const steps = 4;
2369
2564
  for (let i = 1; i <= steps; i++) {
2370
- const p = cubic.pointOnCurve(i / steps);
2371
- len += distance(p.x - prevX, p.y - prevY);
2372
- prevX = p.x;
2373
- prevY = p.y;
2565
+ const t = i / steps;
2566
+ const u = 1 - t;
2567
+ const uu = u * u;
2568
+ const tt = t * t;
2569
+ const uuu = uu * u;
2570
+ const ttt = tt * t;
2571
+ const c0 = 3 * t * uu;
2572
+ const c1 = 3 * tt * u;
2573
+ const px = p[0] * uuu + p[2] * c0 + p[4] * c1 + p[6] * ttt;
2574
+ const py = p[1] * uuu + p[3] * c0 + p[5] * c1 + p[7] * ttt;
2575
+ len += distance(px - prevX, py - prevY);
2576
+ prevX = px;
2577
+ prevY = py;
2374
2578
  }
2375
2579
  return len;
2376
2580
  }
2377
2581
 
2378
2582
  // src/shapes/morph/morph.ts
2583
+ var morphMatchCache = /* @__PURE__ */ new WeakMap();
2379
2584
  var Morph = class _Morph {
2380
2585
  /**
2381
2586
  * Creates a Morph between two shapes.
@@ -2391,23 +2596,79 @@ var Morph = class _Morph {
2391
2596
  * Returns the interpolated shape at the given progress as a list of Cubics.
2392
2597
  *
2393
2598
  * Note: This allocates a new list. For performance-critical animation loops,
2394
- * use {@link forEachCubic} instead.
2599
+ * use {@link toSvgPath} or {@link forEachCubic} instead.
2395
2600
  *
2396
2601
  * @param progress - Value in [0, 1]. 0 = start shape, 1 = end shape.
2397
2602
  * Values outside [0, 1] produce exaggerated shapes (useful for bounce/overshoot).
2398
2603
  * @returns List of interpolated Cubic curves forming the morphed shape
2399
2604
  */
2400
2605
  asCubics(progress) {
2401
- const result = [];
2402
- for (const [c1, c2] of this.morphMatch) {
2403
- result.push(
2404
- new Cubic(
2405
- c1.points.map((v, i) => interpolate(v, c2.points[i], progress))
2406
- )
2407
- );
2606
+ const match = this.morphMatch;
2607
+ const len = match.length;
2608
+ const result = new Array(len);
2609
+ for (let i = 0; i < len; i++) {
2610
+ const [c1, c2] = match[i];
2611
+ const p1 = c1.points;
2612
+ const p2 = c2.points;
2613
+ result[i] = new Cubic([
2614
+ p1[0] + (p2[0] - p1[0]) * progress,
2615
+ p1[1] + (p2[1] - p1[1]) * progress,
2616
+ p1[2] + (p2[2] - p1[2]) * progress,
2617
+ p1[3] + (p2[3] - p1[3]) * progress,
2618
+ p1[4] + (p2[4] - p1[4]) * progress,
2619
+ p1[5] + (p2[5] - p1[5]) * progress,
2620
+ p1[6] + (p2[6] - p1[6]) * progress,
2621
+ p1[7] + (p2[7] - p1[7]) * progress
2622
+ ]);
2408
2623
  }
2409
2624
  return result;
2410
2625
  }
2626
+ /**
2627
+ * Returns an SVG path data string for the morphed shape at the given progress.
2628
+ * Directly evaluates coordinates without allocating intermediate Cubic objects.
2629
+ *
2630
+ * @param progress - Value in [0, 1]
2631
+ * @param width - Target pixel width (default: 1)
2632
+ * @param height - Target pixel height (default: 1)
2633
+ */
2634
+ toSvgPath(progress, width = 1, height = 1) {
2635
+ const match = this.morphMatch;
2636
+ const len = match.length;
2637
+ if (len === 0) return "";
2638
+ const paddingX = width * 0.015;
2639
+ const paddingY = height * 0.015;
2640
+ const sx = width - 2 * paddingX;
2641
+ const sy = height - 2 * paddingY;
2642
+ const [firstC1, firstC2] = match[0];
2643
+ const f1 = firstC1.points;
2644
+ const f2 = firstC2.points;
2645
+ const a0x = f1[0] + (f2[0] - f1[0]) * progress;
2646
+ const a0y = f1[1] + (f2[1] - f1[1]) * progress;
2647
+ let d = `M ${formatCoordinate(paddingX + a0x * sx)} ${formatCoordinate(paddingY + sy * (1 - a0y))}`;
2648
+ for (let i = 0; i < len; i++) {
2649
+ const [c1, c2] = match[i];
2650
+ const p1 = c1.points;
2651
+ const p2 = c2.points;
2652
+ const c0x = p1[2] + (p2[2] - p1[2]) * progress;
2653
+ const c0y = p1[3] + (p2[3] - p1[3]) * progress;
2654
+ const c1x = p1[4] + (p2[4] - p1[4]) * progress;
2655
+ const c1y = p1[5] + (p2[5] - p1[5]) * progress;
2656
+ const a1x = p1[6] + (p2[6] - p1[6]) * progress;
2657
+ const a1y = p1[7] + (p2[7] - p1[7]) * progress;
2658
+ d += ` C ${formatCoordinate(paddingX + c0x * sx)} ${formatCoordinate(paddingY + sy * (1 - c0y))},${formatCoordinate(paddingX + c1x * sx)} ${formatCoordinate(paddingY + sy * (1 - c1y))},${formatCoordinate(paddingX + a1x * sx)} ${formatCoordinate(paddingY + sy * (1 - a1y))}`;
2659
+ }
2660
+ return `${d} Z`;
2661
+ }
2662
+ /**
2663
+ * Returns a CSS clip-path value for the morphed shape at the given progress.
2664
+ *
2665
+ * @param progress - Value in [0, 1]
2666
+ * @param width - Element width in pixels
2667
+ * @param height - Element height in pixels
2668
+ */
2669
+ toClipPath(progress, width, height) {
2670
+ return `path('${this.toSvgPath(progress, width, height)}')`;
2671
+ }
2411
2672
  /**
2412
2673
  * Iterates over the morphed cubics without allocating new Cubic instances.
2413
2674
  * Reuses a single MutableCubic for each callback invocation.
@@ -2481,8 +2742,15 @@ var Morph = class _Morph {
2481
2742
  * @internal
2482
2743
  */
2483
2744
  static match(p1, p2) {
2484
- const measured1 = MeasuredPolygon.measurePolygon(p1);
2485
- const measured2 = MeasuredPolygon.measurePolygon(p2);
2745
+ let cachedMap = morphMatchCache.get(p1);
2746
+ if (cachedMap) {
2747
+ const cached = cachedMap.get(p2);
2748
+ if (cached) return cached;
2749
+ }
2750
+ const poly1 = p1.isClockwise() ? p1 : p1.reversed();
2751
+ const poly2 = p2.isClockwise() ? p2 : p2.reversed();
2752
+ const measured1 = MeasuredPolygon.measurePolygon(poly1);
2753
+ const measured2 = MeasuredPolygon.measurePolygon(poly2);
2486
2754
  const doubleMapper = featureMapper(measured1.features, measured2.features);
2487
2755
  const polygon2CutPoint = doubleMapper.map(0);
2488
2756
  const bs1 = measured1;
@@ -2493,11 +2761,12 @@ var Morph = class _Morph {
2493
2761
  let b2 = bs2.getOrNull(i2++);
2494
2762
  while (b1 !== null && b2 !== null) {
2495
2763
  const b1a = i1 >= bs1.size ? 1 : b1.endOutlineProgress;
2496
- const b2aRaw = i2 >= bs2.size ? 1 : positiveModulo(b2.endOutlineProgress + polygon2CutPoint, 1);
2497
- const b2a = doubleMapper.mapBack(b2aRaw);
2764
+ const b2a = i2 >= bs2.size ? 1 : b2.endOutlineProgress;
2498
2765
  const minb = Math.min(b1a, b2a);
2499
- let seg1 = b1, newb1 = bs1.getOrNull(i1);
2500
- let seg2 = b2, newb2 = bs2.getOrNull(i2);
2766
+ let seg1 = b1;
2767
+ let newb1 = bs1.getOrNull(i1);
2768
+ let seg2 = b2;
2769
+ let newb2 = bs2.getOrNull(i2);
2501
2770
  if (b1a > minb + ANGLE_EPSILON) {
2502
2771
  const [cut, rest] = bs1.cutAtProgress(b1, minb);
2503
2772
  seg1 = cut;
@@ -2506,11 +2775,7 @@ var Morph = class _Morph {
2506
2775
  i1++;
2507
2776
  }
2508
2777
  if (b2a > minb + ANGLE_EPSILON) {
2509
- const targetProgress = positiveModulo(
2510
- doubleMapper.map(minb) - polygon2CutPoint,
2511
- 1
2512
- );
2513
- const [cut, rest] = bs2.cutAtProgress(b2, targetProgress);
2778
+ const [cut, rest] = bs2.cutAtProgress(b2, minb);
2514
2779
  seg2 = cut;
2515
2780
  newb2 = rest;
2516
2781
  } else {
@@ -2520,44 +2785,83 @@ var Morph = class _Morph {
2520
2785
  b1 = newb1;
2521
2786
  b2 = newb2;
2522
2787
  }
2788
+ if (!cachedMap) {
2789
+ cachedMap = /* @__PURE__ */ new WeakMap();
2790
+ morphMatchCache.set(p1, cachedMap);
2791
+ }
2792
+ cachedMap.set(p2, result);
2523
2793
  return result;
2524
2794
  }
2525
2795
  };
2526
2796
 
2527
2797
  // src/shapes/render/shape-rendering.ts
2528
2798
  function toSvgPath(cubics, width = 1, height = 1) {
2529
- if (cubics.length === 0) return "";
2799
+ const len = cubics.length;
2800
+ if (len === 0) return "";
2530
2801
  const paddingX = width * 0.015;
2531
2802
  const paddingY = height * 0.015;
2532
2803
  const sx = width - 2 * paddingX;
2533
2804
  const sy = height - 2 * paddingY;
2534
- const flipY = (y) => paddingY + sy * (1 - y);
2535
- const parts = [
2536
- `M ${fmt(paddingX + cubics[0].anchor0X * sx)} ${fmt(flipY(cubics[0].anchor0Y))}`
2537
- ];
2538
- for (const c of cubics) {
2539
- parts.push(
2540
- `C ${fmt(paddingX + c.control0X * sx)} ${fmt(flipY(c.control0Y))},${fmt(paddingX + c.control1X * sx)} ${fmt(flipY(c.control1Y))},${fmt(paddingX + c.anchor1X * sx)} ${fmt(flipY(c.anchor1Y))}`
2541
- );
2805
+ const c0 = cubics[0];
2806
+ let d = `M ${formatCoordinate(paddingX + c0.anchor0X * sx)} ${formatCoordinate(paddingY + sy * (1 - c0.anchor0Y))}`;
2807
+ for (let i = 0; i < len; i++) {
2808
+ const c = cubics[i];
2809
+ d += ` C ${formatCoordinate(paddingX + c.control0X * sx)} ${formatCoordinate(paddingY + sy * (1 - c.control0Y))},${formatCoordinate(paddingX + c.control1X * sx)} ${formatCoordinate(paddingY + sy * (1 - c.control1Y))},${formatCoordinate(paddingX + c.anchor1X * sx)} ${formatCoordinate(paddingY + sy * (1 - c.anchor1Y))}`;
2542
2810
  }
2543
- parts.push("Z");
2544
- return parts.join(" ");
2811
+ return `${d} Z`;
2545
2812
  }
2546
2813
  function toClipPath(polygon, width, height) {
2547
2814
  const pathData = toSvgPath(polygon.cubics, width, height);
2548
2815
  return `path('${pathData}')`;
2549
2816
  }
2550
2817
  function interpolatePath(morph, progress, width, height) {
2551
- const cubics = morph.asCubics(progress);
2552
- return toClipPath({ cubics }, width, height);
2553
- }
2554
- function fmt(v) {
2555
- return Number(v.toFixed(4)).toString();
2818
+ return morph.toClipPath(progress, width, height);
2556
2819
  }
2820
+
2821
+ // src/ui/shape-media/animation-utils.ts
2557
2822
  function prefersReducedMotion() {
2558
- if (typeof window === "undefined") return false;
2823
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
2824
+ return false;
2825
+ }
2559
2826
  return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
2560
2827
  }
2828
+ function easeCubicInOut(t) {
2829
+ return t < 0.5 ? 4 * t * t * t : 1 - (-2 * t + 2) ** 3 / 2;
2830
+ }
2831
+ function cubicBezier(x1, y1, x2, y2) {
2832
+ return (t) => {
2833
+ if (t === 0 || t === 1) return t;
2834
+ let tParam = t;
2835
+ for (let i = 0; i < 8; i++) {
2836
+ const x = 3 * (1 - tParam) ** 2 * tParam * x1 + 3 * (1 - tParam) * tParam ** 2 * x2 + tParam ** 3;
2837
+ const dx = 3 * (1 - tParam) ** 2 * x1 + 6 * (1 - tParam) * tParam * (x2 - x1) + 3 * tParam ** 2 * (1 - x2);
2838
+ if (Math.abs(dx) < 1e-6) break;
2839
+ tParam -= (x - t) / dx;
2840
+ }
2841
+ return 3 * (1 - tParam) ** 2 * tParam * y1 + 3 * (1 - tParam) * tParam ** 2 * y2 + tParam ** 3;
2842
+ };
2843
+ }
2844
+ function getEasingFunction(easing) {
2845
+ if (Array.isArray(easing)) {
2846
+ if (easing.length === 4) {
2847
+ const [x1, y1, x2, y2] = easing;
2848
+ return cubicBezier(x1, y1, x2, y2);
2849
+ }
2850
+ return easeCubicInOut;
2851
+ }
2852
+ switch (easing) {
2853
+ case "linear":
2854
+ return (t) => t;
2855
+ case "ease-in":
2856
+ return (t) => t * t * t;
2857
+ case "ease-out":
2858
+ return (t) => 1 - (1 - t) ** 3;
2859
+ default:
2860
+ return easeCubicInOut;
2861
+ }
2862
+ }
2863
+
2864
+ // src/ui/shape-media/useShapeMorph.ts
2561
2865
  function useShapeMorph({
2562
2866
  shape,
2563
2867
  morphTo,
@@ -2572,10 +2876,16 @@ function useShapeMorph({
2572
2876
  const duration = (_a = morphOptions == null ? void 0 : morphOptions.duration) != null ? _a : 0.3;
2573
2877
  const easing = (_b = morphOptions == null ? void 0 : morphOptions.easing) != null ? _b : "ease-in-out";
2574
2878
  const easingFunction = useMemo(() => getEasingFunction(easing), [easing]);
2879
+ const resolvedTarget = useMemo(() => {
2880
+ if (morphTo === "auto") {
2881
+ return getRecommendedMorphShape(shape);
2882
+ }
2883
+ return morphTo;
2884
+ }, [morphTo, shape]);
2575
2885
  const startShape = useMemo(() => resolveShape(shape), [shape]);
2576
2886
  const endShape = useMemo(
2577
- () => morphTo ? resolveShape(morphTo) : null,
2578
- [morphTo]
2887
+ () => resolvedTarget ? resolveShape(resolvedTarget) : null,
2888
+ [resolvedTarget]
2579
2889
  );
2580
2890
  const morph = useMemo(
2581
2891
  () => endShape ? new Morph(startShape, endShape) : null,
@@ -2590,7 +2900,45 @@ function useShapeMorph({
2590
2900
  const startProgressRef = useRef(0);
2591
2901
  const targetProgressRef = useRef(0);
2592
2902
  const currentProgressRef = useRef(0);
2903
+ const prevShapeRef = useRef(shape);
2904
+ useEffect(() => {
2905
+ if (prevShapeRef.current === shape) return;
2906
+ const oldShape = prevShapeRef.current;
2907
+ prevShapeRef.current = shape;
2908
+ if (disabled || prefersReducedMotion()) {
2909
+ setClipPath(toClipPath(resolveShape(shape), width, height));
2910
+ return;
2911
+ }
2912
+ try {
2913
+ const transitionMorph = new Morph(
2914
+ resolveShape(oldShape),
2915
+ resolveShape(shape)
2916
+ );
2917
+ const start = performance.now();
2918
+ const durationMs = duration * 1e3;
2919
+ const animateTransition = (timestamp) => {
2920
+ const elapsed = timestamp - start;
2921
+ const rawT = Math.min(elapsed / durationMs, 1);
2922
+ const ease = easingFunction(rawT);
2923
+ setClipPath(interpolatePath(transitionMorph, ease, width, height));
2924
+ if (rawT < 1) {
2925
+ animRef.current = requestAnimationFrame(animateTransition);
2926
+ } else {
2927
+ animRef.current = null;
2928
+ }
2929
+ };
2930
+ if (animRef.current !== null) cancelAnimationFrame(animRef.current);
2931
+ animRef.current = requestAnimationFrame(animateTransition);
2932
+ } catch (e) {
2933
+ setClipPath(toClipPath(resolveShape(shape), width, height));
2934
+ }
2935
+ }, [shape, disabled, duration, easingFunction, width, height]);
2936
+ const prevDimsRef = useRef({ width, height, startShape, morph });
2593
2937
  useEffect(() => {
2938
+ if (prevDimsRef.current.width === width && prevDimsRef.current.height === height && prevDimsRef.current.startShape === startShape && prevDimsRef.current.morph === morph) {
2939
+ return;
2940
+ }
2941
+ prevDimsRef.current = { width, height, startShape, morph };
2594
2942
  if (morph) {
2595
2943
  setClipPath(
2596
2944
  interpolatePath(morph, currentProgressRef.current, width, height)
@@ -2684,41 +3032,6 @@ function useShapeMorph({
2684
3032
  }, [morphTo, disabled, morphOn, activate, deactivate, isActive]);
2685
3033
  return { clipPath, isActive, activate, deactivate, setProgress, handlers };
2686
3034
  }
2687
- function easeCubicInOut(t) {
2688
- return t < 0.5 ? 4 * t * t * t : 1 - (-2 * t + 2) ** 3 / 2;
2689
- }
2690
- function cubicBezier(x1, y1, x2, y2) {
2691
- return (t) => {
2692
- if (t === 0 || t === 1) return t;
2693
- let tParam = t;
2694
- for (let i = 0; i < 8; i++) {
2695
- const x = 3 * (1 - tParam) ** 2 * tParam * x1 + 3 * (1 - tParam) * tParam ** 2 * x2 + tParam ** 3;
2696
- const dx = 3 * (1 - tParam) ** 2 * x1 + 6 * (1 - tParam) * tParam * (x2 - x1) + 3 * tParam ** 2 * (1 - x2);
2697
- if (Math.abs(dx) < 1e-6) break;
2698
- tParam -= (x - t) / dx;
2699
- }
2700
- return 3 * (1 - tParam) ** 2 * tParam * y1 + 3 * (1 - tParam) * tParam ** 2 * y2 + tParam ** 3;
2701
- };
2702
- }
2703
- function getEasingFunction(easing) {
2704
- if (Array.isArray(easing)) {
2705
- if (easing.length === 4) {
2706
- const [x1, y1, x2, y2] = easing;
2707
- return cubicBezier(x1, y1, x2, y2);
2708
- }
2709
- return easeCubicInOut;
2710
- }
2711
- switch (easing) {
2712
- case "linear":
2713
- return (t) => t;
2714
- case "ease-in":
2715
- return (t) => t * t * t;
2716
- case "ease-out":
2717
- return (t) => 1 - (1 - t) ** 3;
2718
- default:
2719
- return easeCubicInOut;
2720
- }
2721
- }
2722
3035
  function ShapeMedia({
2723
3036
  shape,
2724
3037
  morphTo,
@@ -2846,10 +3159,14 @@ function ShapeSvg({
2846
3159
  style,
2847
3160
  "aria-label": ariaLabel
2848
3161
  }) {
3162
+ const resolvedTarget = useMemo(() => {
3163
+ if (morphTo === "auto") return getRecommendedMorphShape(shape);
3164
+ return morphTo;
3165
+ }, [morphTo, shape]);
2849
3166
  const startPolygon = useMemo(() => resolveShape(shape), [shape]);
2850
3167
  const endPolygon = useMemo(
2851
- () => morphTo ? resolveShape(morphTo) : null,
2852
- [morphTo]
3168
+ () => resolvedTarget ? resolveShape(resolvedTarget) : null,
3169
+ [resolvedTarget]
2853
3170
  );
2854
3171
  const morph = useMemo(
2855
3172
  () => endPolygon ? new Morph(startPolygon, endPolygon) : null,
@@ -2883,7 +3200,177 @@ function ShapeSvg({
2883
3200
  }
2884
3201
  );
2885
3202
  }
3203
+ function useShapeSequenceMorph({
3204
+ shapes,
3205
+ duration = 0.6,
3206
+ interval = 2,
3207
+ autoplay = false,
3208
+ loop = true,
3209
+ morphOptions,
3210
+ width,
3211
+ height,
3212
+ disabled = false
3213
+ }) {
3214
+ var _a;
3215
+ const resolvedDuration = (_a = morphOptions == null ? void 0 : morphOptions.duration) != null ? _a : duration;
3216
+ const [currentIndex, setCurrentIndex] = useState(0);
3217
+ const [nextIndex, setNextIndex] = useState(() => shapes.length > 1 ? 1 : 0);
3218
+ const [progress, setProgress] = useState(0);
3219
+ const [isPlaying, setIsPlaying] = useState(autoplay);
3220
+ const animRef = useRef(null);
3221
+ const timerRef = useRef(null);
3222
+ const startTimeRef = useRef(0);
3223
+ const isTransitioningRef = useRef(false);
3224
+ const numShapes = shapes.length;
3225
+ const currentShape = useMemo(() => {
3226
+ if (numShapes === 0) return resolveShape("circle");
3227
+ const safeIdx = (currentIndex % numShapes + numShapes) % numShapes;
3228
+ return resolveShape(shapes[safeIdx]);
3229
+ }, [shapes, currentIndex, numShapes]);
3230
+ const targetShape = useMemo(() => {
3231
+ if (numShapes === 0) return resolveShape("circle");
3232
+ const safeIdx = (nextIndex % numShapes + numShapes) % numShapes;
3233
+ return resolveShape(shapes[safeIdx]);
3234
+ }, [shapes, nextIndex, numShapes]);
3235
+ const morph = useMemo(() => {
3236
+ if (currentShape === targetShape) return null;
3237
+ return new Morph(currentShape, targetShape);
3238
+ }, [currentShape, targetShape]);
3239
+ const [clipPath, setClipPath] = useState(
3240
+ () => toClipPath(currentShape, width, height)
3241
+ );
3242
+ const prevDimsRef = useRef({ width, height, currentShape, morph, progress });
3243
+ useEffect(() => {
3244
+ if (prevDimsRef.current.width === width && prevDimsRef.current.height === height && prevDimsRef.current.currentShape === currentShape && prevDimsRef.current.morph === morph && prevDimsRef.current.progress === progress) {
3245
+ return;
3246
+ }
3247
+ prevDimsRef.current = { width, height, currentShape, morph, progress };
3248
+ if (morph && progress > 0) {
3249
+ setClipPath(interpolatePath(morph, progress, width, height));
3250
+ } else {
3251
+ setClipPath(toClipPath(currentShape, width, height));
3252
+ }
3253
+ }, [width, height, currentShape, morph, progress]);
3254
+ const transitionTo = useCallback(
3255
+ (toIndex) => {
3256
+ if (disabled || numShapes <= 1 || isTransitioningRef.current) return;
3257
+ const targetSafe = (toIndex % numShapes + numShapes) % numShapes;
3258
+ if (targetSafe === currentIndex) return;
3259
+ setNextIndex(targetSafe);
3260
+ if (prefersReducedMotion()) {
3261
+ setCurrentIndex(targetSafe);
3262
+ setNextIndex((targetSafe + 1) % numShapes);
3263
+ setProgress(0);
3264
+ setClipPath(
3265
+ toClipPath(resolveShape(shapes[targetSafe]), width, height)
3266
+ );
3267
+ return;
3268
+ }
3269
+ isTransitioningRef.current = true;
3270
+ startTimeRef.current = performance.now();
3271
+ const fromPoly = resolveShape(shapes[currentIndex]);
3272
+ const toPoly = resolveShape(shapes[targetSafe]);
3273
+ const activeMorph = new Morph(fromPoly, toPoly);
3274
+ const animate = (timestamp) => {
3275
+ const elapsed = timestamp - startTimeRef.current;
3276
+ const durationMs = resolvedDuration * 1e3;
3277
+ const rawT = Math.min(elapsed / durationMs, 1);
3278
+ const ease = easeCubicInOut(rawT);
3279
+ setProgress(rawT);
3280
+ setClipPath(interpolatePath(activeMorph, ease, width, height));
3281
+ if (rawT < 1) {
3282
+ animRef.current = requestAnimationFrame(animate);
3283
+ } else {
3284
+ animRef.current = null;
3285
+ isTransitioningRef.current = false;
3286
+ setCurrentIndex(targetSafe);
3287
+ setNextIndex((targetSafe + 1) % numShapes);
3288
+ setProgress(0);
3289
+ }
3290
+ };
3291
+ if (animRef.current !== null) cancelAnimationFrame(animRef.current);
3292
+ animRef.current = requestAnimationFrame(animate);
3293
+ },
3294
+ [
3295
+ disabled,
3296
+ numShapes,
3297
+ currentIndex,
3298
+ shapes,
3299
+ resolvedDuration,
3300
+ width,
3301
+ height
3302
+ ]
3303
+ );
3304
+ const next = useCallback(() => {
3305
+ if (numShapes <= 1) return;
3306
+ if (!loop && currentIndex >= numShapes - 1) return;
3307
+ transitionTo((currentIndex + 1) % numShapes);
3308
+ }, [numShapes, loop, currentIndex, transitionTo]);
3309
+ const prev = useCallback(() => {
3310
+ if (numShapes <= 1) return;
3311
+ if (!loop && currentIndex <= 0) return;
3312
+ transitionTo((currentIndex - 1 + numShapes) % numShapes);
3313
+ }, [numShapes, loop, currentIndex, transitionTo]);
3314
+ const goTo = useCallback(
3315
+ (index) => {
3316
+ transitionTo(index);
3317
+ },
3318
+ [transitionTo]
3319
+ );
3320
+ const play = useCallback(() => {
3321
+ setIsPlaying(true);
3322
+ }, []);
3323
+ const pause = useCallback(() => {
3324
+ setIsPlaying(false);
3325
+ if (timerRef.current !== null) {
3326
+ clearTimeout(timerRef.current);
3327
+ timerRef.current = null;
3328
+ }
3329
+ }, []);
3330
+ useEffect(() => {
3331
+ if (!isPlaying || disabled || numShapes <= 1) return;
3332
+ timerRef.current = setTimeout(() => {
3333
+ if (!loop && currentIndex >= numShapes - 1) {
3334
+ setIsPlaying(false);
3335
+ return;
3336
+ }
3337
+ next();
3338
+ }, interval * 1e3);
3339
+ return () => {
3340
+ if (timerRef.current !== null) {
3341
+ clearTimeout(timerRef.current);
3342
+ timerRef.current = null;
3343
+ }
3344
+ };
3345
+ }, [isPlaying, disabled, numShapes, loop, currentIndex, interval, next]);
3346
+ useEffect(() => {
3347
+ return () => {
3348
+ if (animRef.current !== null) cancelAnimationFrame(animRef.current);
3349
+ if (timerRef.current !== null) clearTimeout(timerRef.current);
3350
+ };
3351
+ }, []);
3352
+ useEffect(() => {
3353
+ if (currentIndex >= shapes.length) {
3354
+ setCurrentIndex(0);
3355
+ setNextIndex(shapes.length > 1 ? 1 : 0);
3356
+ }
3357
+ }, [shapes.length, currentIndex]);
3358
+ const safeCurrentIndex = numShapes > 0 ? (currentIndex % numShapes + numShapes) % numShapes : 0;
3359
+ const safeNextIndex = numShapes > 0 ? (nextIndex % numShapes + numShapes) % numShapes : 0;
3360
+ return {
3361
+ clipPath,
3362
+ currentIndex: safeCurrentIndex,
3363
+ nextIndex: safeNextIndex,
3364
+ progress,
3365
+ isPlaying,
3366
+ next,
3367
+ prev,
3368
+ goTo,
3369
+ play,
3370
+ pause
3371
+ };
3372
+ }
2886
3373
 
2887
- export { ANGLE_EPSILON, Cubic, DISTANCE_EPSILON, FLOAT_PI, MD3CornerRadius, MD3Shapes, MeasuredPolygon, Morph, MutableCubic, RoundedPolygon, ShapeIcon, ShapeMedia, ShapeMediaServer, ShapeSvg, UNROUNDED, addPoints, circle, clockwise, convex, cornerFeature, cornerRounding, createDoubleMapper, directionVector, distance, distanceSquared, dividePoint, dotProduct, edgeFeature, featureMapper, getDirection, getDistance, getDistanceSquared, interpolate, interpolatePath, lerpPoint, pill, pillStar, point, positiveModulo, radialToCartesian, rectangle, rotate90, scalePoint, square, star, subtractPoints, toClipPath, toSvgPath, transformFeature, transformPoint, useShapeMorph };
3374
+ export { ANGLE_EPSILON, Cubic, DISTANCE_EPSILON, FLOAT_PI, MD3CornerRadius, MD3Shapes, MD3_SHAPE_FAMILIES, MD3_SHAPE_PAIRINGS, MeasuredPolygon, Morph, MutableCubic, RoundedPolygon, ShapeIcon, ShapeMedia, ShapeMediaServer, ShapeSvg, UNROUNDED, addPoints, circle, clockwise, convex, cornerFeature, cornerRounding, createDoubleMapper, directionVector, distance, distanceSquared, dividePoint, dotProduct, edgeFeature, featureMapper, formatCoordinate, getDirection, getDistance, getDistanceSquared, getRecommendedMorphShape, interpolate, interpolatePath, lerpPoint, pill, pillStar, point, positiveModulo, radialToCartesian, rectangle, rotate90, scalePoint, square, star, subtractPoints, toClipPath, toSvgPath, transformFeature, transformPoint, useShapeMorph, useShapeSequenceMorph };
2888
3375
  //# sourceMappingURL=shapes.mjs.map
2889
3376
  //# sourceMappingURL=shapes.mjs.map