@bug-on/m3-expressive 1.3.4 → 1.3.6

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