@bug-on/m3-expressive 1.3.5 → 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 (43) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/forms.d.mts +2 -2
  3. package/dist/forms.d.ts +2 -2
  4. package/dist/forms.js +16 -6
  5. package/dist/forms.js.map +1 -1
  6. package/dist/forms.mjs +16 -6
  7. package/dist/forms.mjs.map +1 -1
  8. package/dist/index.d.mts +4 -4
  9. package/dist/index.d.ts +4 -4
  10. package/dist/index.js +722 -187
  11. package/dist/index.js.map +1 -1
  12. package/dist/index.mjs +718 -188
  13. package/dist/index.mjs.map +1 -1
  14. package/dist/{md3-expressive-shapes-CPcfl_Hf.d.mts → md3-expressive-shapes-wk_98LJh.d.mts} +20 -1
  15. package/dist/{md3-expressive-shapes-CPcfl_Hf.d.ts → md3-expressive-shapes-wk_98LJh.d.ts} +20 -1
  16. package/dist/navigation.d.mts +1 -1
  17. package/dist/navigation.d.ts +1 -1
  18. package/dist/navigation.js +73 -20
  19. package/dist/navigation.js.map +1 -1
  20. package/dist/navigation.mjs +73 -20
  21. package/dist/navigation.mjs.map +1 -1
  22. package/dist/overlays.d.mts +4 -2
  23. package/dist/overlays.d.ts +4 -2
  24. package/dist/overlays.js +84 -43
  25. package/dist/overlays.js.map +1 -1
  26. package/dist/overlays.mjs +84 -43
  27. package/dist/overlays.mjs.map +1 -1
  28. package/dist/pickers.js +88 -55
  29. package/dist/pickers.js.map +1 -1
  30. package/dist/pickers.mjs +88 -55
  31. package/dist/pickers.mjs.map +1 -1
  32. package/dist/shapes.d.mts +141 -15
  33. package/dist/shapes.d.ts +141 -15
  34. package/dist/shapes.js +618 -126
  35. package/dist/shapes.js.map +1 -1
  36. package/dist/shapes.mjs +614 -127
  37. package/dist/shapes.mjs.map +1 -1
  38. package/dist/{side-sheet-modal-64FGhDxL.d.mts → side-sheet-modal-BycxrabB.d.mts} +70 -0
  39. package/dist/{side-sheet-modal-Bd5Qqvp9.d.ts → side-sheet-modal-Cw4vemKx.d.ts} +70 -0
  40. package/dist/{text-field-4OlT9o8s.d.mts → text-field-B1fLh5Sh.d.mts} +22 -3
  41. package/dist/{text-field-DARNdj14.d.ts → text-field-C0VQLp8Y.d.ts} +22 -3
  42. package/llms-full.txt +7 -0
  43. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -335,6 +335,13 @@ function transformFeature(feature, fn) {
335
335
  }
336
336
  return edgeFeature(cubics);
337
337
  }
338
+ function reverseFeature(feature) {
339
+ const cubics = feature.cubics.slice().reverse().map((c) => c.reverse());
340
+ if (feature.type === "corner") {
341
+ return cornerFeature(cubics, feature.convex);
342
+ }
343
+ return edgeFeature(cubics);
344
+ }
338
345
 
339
346
  // src/shapes/math/corner-rounding.ts
340
347
  function cornerRounding(radius = 0, smoothing = 0) {
@@ -379,6 +386,10 @@ function convex(prevX, prevY, currX, currY, nextX, nextY) {
379
386
  const v2y = nextY - currY;
380
387
  return v1x * v2y - v1y * v2x > 0;
381
388
  }
389
+ function formatCoordinate(v) {
390
+ const rounded = Math.round(v * 1e4) / 1e4;
391
+ return (rounded === 0 ? 0 : rounded).toString();
392
+ }
382
393
 
383
394
  // src/shapes/math/point.ts
384
395
  function point(x, y) {
@@ -478,9 +489,16 @@ var Cubic = class _Cubic {
478
489
  */
479
490
  pointOnCurve(t) {
480
491
  const u = 1 - t;
492
+ const uu = u * u;
493
+ const tt = t * t;
494
+ const uuu = uu * u;
495
+ const ttt = tt * t;
496
+ const c0 = 3 * t * uu;
497
+ const c1 = 3 * tt * u;
498
+ const p = this.points;
481
499
  return point(
482
- this.anchor0X * (u * u * u) + this.control0X * (3 * t * u * u) + this.control1X * (3 * t * t * u) + this.anchor1X * (t * t * t),
483
- this.anchor0Y * (u * u * u) + this.control0Y * (3 * t * u * u) + this.control1Y * (3 * t * t * u) + this.anchor1Y * (t * t * t)
500
+ p[0] * uuu + p[2] * c0 + p[4] * c1 + p[6] * ttt,
501
+ p[1] * uuu + p[3] * c0 + p[5] * c1 + p[7] * ttt
484
502
  );
485
503
  }
486
504
  /**
@@ -703,13 +721,17 @@ var MutableCubic = class extends Cubic {
703
721
  * @param progress - Interpolation factor [0, 1]
704
722
  */
705
723
  interpolate(c1, c2, progress) {
706
- for (let i = 0; i < 8; i++) {
707
- this.points[i] = interpolate(
708
- c1.points[i],
709
- c2.points[i],
710
- progress
711
- );
712
- }
724
+ const p1 = c1.points;
725
+ const p2 = c2.points;
726
+ const p = this.points;
727
+ p[0] = p1[0] + (p2[0] - p1[0]) * progress;
728
+ p[1] = p1[1] + (p2[1] - p1[1]) * progress;
729
+ p[2] = p1[2] + (p2[2] - p1[2]) * progress;
730
+ p[3] = p1[3] + (p2[3] - p1[3]) * progress;
731
+ p[4] = p1[4] + (p2[4] - p1[4]) * progress;
732
+ p[5] = p1[5] + (p2[5] - p1[5]) * progress;
733
+ p[6] = p1[6] + (p2[6] - p1[6]) * progress;
734
+ p[7] = p1[7] + (p2[7] - p1[7]) * progress;
713
735
  }
714
736
  /**
715
737
  * Mutably applies a PointTransformer to all points.
@@ -1121,6 +1143,36 @@ var RoundedPolygon = class _RoundedPolygon {
1121
1143
  this.centerY + d
1122
1144
  ];
1123
1145
  }
1146
+ /**
1147
+ * Calculates the approximate signed area of the polygon using anchor points.
1148
+ * Returns > 0 for Clockwise (in screen coords, y down), < 0 for Counter-Clockwise.
1149
+ */
1150
+ calculateSignedArea() {
1151
+ let area = 0;
1152
+ const n = this.cubics.length;
1153
+ for (let i = 0; i < n; i++) {
1154
+ const c = this.cubics[i];
1155
+ area += c.anchor0X * c.anchor1Y - c.anchor1X * c.anchor0Y;
1156
+ }
1157
+ return area / 2;
1158
+ }
1159
+ /**
1160
+ * Returns true if the polygon outline runs in Clockwise order (screen coords).
1161
+ */
1162
+ isClockwise() {
1163
+ return this.calculateSignedArea() > 0;
1164
+ }
1165
+ /**
1166
+ * Returns a reversed copy of the polygon with reversed traversal order.
1167
+ * Flips the winding direction (CW ↔ CCW).
1168
+ */
1169
+ reversed() {
1170
+ const reversedFeatures = this.features.slice().reverse().map((f) => reverseFeature(f));
1171
+ return new _RoundedPolygon(
1172
+ reversedFeatures,
1173
+ point(this.centerX, this.centerY)
1174
+ );
1175
+ }
1124
1176
  };
1125
1177
  function buildCubicsList(features) {
1126
1178
  const result = [];
@@ -1189,10 +1241,21 @@ function validateContiguity(cubics) {
1189
1241
 
1190
1242
  // src/shapes/catalog/md3-expressive-shapes.ts
1191
1243
  function createShape(pts) {
1192
- const cubics = pts.map((p) => new Cubic(p));
1193
- return RoundedPolygon.fromFeatures(
1194
- cubics.map((c) => cornerFeature([c], false))
1195
- );
1244
+ let rawArea = 0;
1245
+ for (const p of pts) {
1246
+ rawArea += p[0] * p[7] - p[6] * p[1];
1247
+ }
1248
+ 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;
1249
+ const cubics = normalizedPts.map((p) => new Cubic(p));
1250
+ const n = cubics.length;
1251
+ const features = cubics.map((c, i) => {
1252
+ const prev = cubics[(i - 1 + n) % n].pointOnCurve(0.5);
1253
+ const curr = c.pointOnCurve(0.5);
1254
+ const next = cubics[(i + 1) % n].pointOnCurve(0.5);
1255
+ const isConvex = convex(prev.x, prev.y, curr.x, curr.y, next.x, next.y);
1256
+ return cornerFeature([c], isConvex);
1257
+ });
1258
+ return RoundedPolygon.fromFeatures(features);
1196
1259
  }
1197
1260
  var archShape = createShape([
1198
1261
  [1, 0.83461, 1, 0.8547, 1, 0.86477, 0.99898, 0.87322],
@@ -2290,6 +2353,94 @@ function resolveShape(ref) {
2290
2353
  return shape;
2291
2354
  }
2292
2355
 
2356
+ // src/shapes/catalog/shape-pairings.ts
2357
+ var MD3_SHAPE_PAIRINGS = {
2358
+ // ─── Organic & Floral ──────────────────────────────────────────
2359
+ circle: "flower",
2360
+ flower: "sunny",
2361
+ sunny: "verySunny",
2362
+ verySunny: "flower",
2363
+ bun: "clamshell",
2364
+ clamshell: "bun",
2365
+ puffy: "circle",
2366
+ // ─── Starburst & Explosive ─────────────────────────────────────
2367
+ burst: "softBurst",
2368
+ softBurst: "boom",
2369
+ boom: "softBoom",
2370
+ softBoom: "burst",
2371
+ cookie12Sided: "softBurst",
2372
+ // ─── Clover, Gem & Diamond ─────────────────────────────────────
2373
+ diamond: "puffyDiamond",
2374
+ puffyDiamond: "diamond",
2375
+ clover4Leaf: "clover8Leaf",
2376
+ clover8Leaf: "clover4Leaf",
2377
+ gem: "diamond",
2378
+ heart: "clover4Leaf",
2379
+ // ─── Cookies & Geometric Multi-sided ───────────────────────────
2380
+ cookie4Sided: "cookie6Sided",
2381
+ cookie6Sided: "cookie9Sided",
2382
+ cookie7Sided: "cookie12Sided",
2383
+ cookie9Sided: "cookie7Sided",
2384
+ square: "cookie4Sided",
2385
+ // ─── Rounded & Architectural ───────────────────────────────────
2386
+ pill: "oval",
2387
+ oval: "arch",
2388
+ arch: "semiCircle",
2389
+ semiCircle: "pill",
2390
+ slanted: "arch",
2391
+ // ─── Polygonal, Directional & Pixel ────────────────────────────
2392
+ triangle: "arrow",
2393
+ arrow: "pentagon",
2394
+ pentagon: "triangle",
2395
+ fan: "ghostish",
2396
+ ghostish: "fan",
2397
+ pixelTriangle: "pixelCircle",
2398
+ pixelCircle: "circle"
2399
+ };
2400
+ var MD3_SHAPE_FAMILIES = {
2401
+ organic: [
2402
+ "circle",
2403
+ "flower",
2404
+ "sunny",
2405
+ "verySunny",
2406
+ "bun",
2407
+ "clamshell",
2408
+ "puffy"
2409
+ ],
2410
+ starburst: ["burst", "softBurst", "boom", "softBoom", "cookie12Sided"],
2411
+ cloverAndGems: [
2412
+ "diamond",
2413
+ "puffyDiamond",
2414
+ "clover4Leaf",
2415
+ "clover8Leaf",
2416
+ "gem",
2417
+ "heart"
2418
+ ],
2419
+ cookiesAndPolygons: [
2420
+ "cookie4Sided",
2421
+ "cookie6Sided",
2422
+ "cookie7Sided",
2423
+ "cookie9Sided",
2424
+ "square"
2425
+ ],
2426
+ roundedContainers: ["pill", "oval", "arch", "semiCircle", "slanted"],
2427
+ directionalAndPixel: [
2428
+ "triangle",
2429
+ "arrow",
2430
+ "pentagon",
2431
+ "fan",
2432
+ "ghostish",
2433
+ "pixelTriangle",
2434
+ "pixelCircle"
2435
+ ]
2436
+ };
2437
+ function getRecommendedMorphShape(shape) {
2438
+ if (typeof shape === "string" && shape in MD3_SHAPE_PAIRINGS) {
2439
+ return MD3_SHAPE_PAIRINGS[shape];
2440
+ }
2441
+ return "circle";
2442
+ }
2443
+
2293
2444
  // src/shapes/catalog/shape-tokens.ts
2294
2445
  var MD3CornerRadius = {
2295
2446
  /** 0dp — Sharp corners */
@@ -2455,6 +2606,56 @@ function pillStarVertices(width, height, numVerticesPerRadius, innerRadiusRatio,
2455
2606
  return result;
2456
2607
  }
2457
2608
 
2609
+ // src/shapes/morph/feature-mapping.ts
2610
+ function angleDiff(a1, a2) {
2611
+ let diff = Math.abs(a1 - a2);
2612
+ if (diff > Math.PI) diff = 2 * Math.PI - diff;
2613
+ return diff;
2614
+ }
2615
+ var TWO_PI_SQ = 4 * Math.PI * Math.PI;
2616
+ function featureMapper(features1, features2) {
2617
+ const convex1 = features1.filter((f) => f.convex);
2618
+ const convex2 = features2.filter((f) => f.convex);
2619
+ const set1 = convex1.length > 0 ? convex1 : features1;
2620
+ const set2 = convex2.length > 0 ? convex2 : features2;
2621
+ if (set1.length === 0 || set2.length === 0) {
2622
+ return { map: (v) => v, mapBack: (v) => v };
2623
+ }
2624
+ let bestShift = 0;
2625
+ let minCost = Number.MAX_VALUE;
2626
+ for (const f1 of set1) {
2627
+ for (const f2 of set2) {
2628
+ const shift = positiveModulo(f2.progress - f1.progress, 1);
2629
+ let cost = 0;
2630
+ for (const c1 of set1) {
2631
+ const expected = positiveModulo(c1.progress + shift, 1);
2632
+ let dMin = Number.MAX_VALUE;
2633
+ let matchedC2 = set2[0];
2634
+ for (const c2 of set2) {
2635
+ const diff = Math.abs(expected - c2.progress);
2636
+ const d = diff > 0.5 ? 1 - diff : diff;
2637
+ if (d < dMin) {
2638
+ dMin = d;
2639
+ matchedC2 = c2;
2640
+ }
2641
+ }
2642
+ const progErrorSq = dMin * dMin * TWO_PI_SQ;
2643
+ const angDiff = c1.angle !== void 0 && matchedC2.angle !== void 0 ? angleDiff(c1.angle, matchedC2.angle) : 0;
2644
+ cost += progErrorSq + angDiff * angDiff;
2645
+ if (cost >= minCost) break;
2646
+ }
2647
+ if (cost < minCost) {
2648
+ minCost = cost;
2649
+ bestShift = shift;
2650
+ }
2651
+ }
2652
+ }
2653
+ return {
2654
+ map: (v) => positiveModulo(v + bestShift, 1),
2655
+ mapBack: (v) => positiveModulo(v - bestShift, 1)
2656
+ };
2657
+ }
2658
+
2458
2659
  // src/shapes/morph/float-mapping.ts
2459
2660
  function mapSegments(value, segments) {
2460
2661
  for (const seg of segments) {
@@ -2499,32 +2700,6 @@ function createDoubleMapper(fromValues, toValues) {
2499
2700
  };
2500
2701
  }
2501
2702
 
2502
- // src/shapes/morph/feature-mapping.ts
2503
- function featureMapper(features1, features2) {
2504
- const convex1 = features1.filter((f) => f.convex);
2505
- const convex2 = features2.filter((f) => f.convex);
2506
- if (convex1.length === 0 || convex2.length === 0) {
2507
- return createDoubleMapper([], []);
2508
- }
2509
- const fromValues = [];
2510
- const toValues = [];
2511
- for (const f1 of convex1) {
2512
- let bestDist = Number.MAX_VALUE;
2513
- let bestProgress = 0;
2514
- for (const f2 of convex2) {
2515
- const diff = Math.abs(f1.progress - f2.progress);
2516
- const dist = Math.min(diff, 1 - diff);
2517
- if (dist < bestDist) {
2518
- bestDist = dist;
2519
- bestProgress = f2.progress;
2520
- }
2521
- }
2522
- fromValues.push(f1.progress);
2523
- toValues.push(bestProgress);
2524
- }
2525
- return createDoubleMapper(fromValues, toValues);
2526
- }
2527
-
2528
2703
  // src/shapes/morph/polygon-measure.ts
2529
2704
  var MeasuredPolygon = class _MeasuredPolygon {
2530
2705
  constructor(measuredCubics, features) {
@@ -2558,9 +2733,8 @@ var MeasuredPolygon = class _MeasuredPolygon {
2558
2733
  const t = segLen > 0 ? (cutPoint - cutCubic.startOutlineProgress) / segLen : 0;
2559
2734
  const [left, right] = cutCubic.cubic.split(t);
2560
2735
  const shift = (p) => {
2561
- let s = p - cutPoint;
2562
- if (s < 0) s += 1;
2563
- return s;
2736
+ const s = p - cutPoint;
2737
+ return s < 0 ? s + 1 : s;
2564
2738
  };
2565
2739
  if (!right.zeroLength()) {
2566
2740
  result.push({
@@ -2569,9 +2743,19 @@ var MeasuredPolygon = class _MeasuredPolygon {
2569
2743
  endOutlineProgress: shift(cutCubic.endOutlineProgress)
2570
2744
  });
2571
2745
  }
2572
- for (let i = 1; i < this.measuredCubics.length; i++) {
2573
- const idx = (cutCubicIdx + i) % this.measuredCubics.length;
2574
- const mc = this.measuredCubics[idx];
2746
+ const totalCubics = this.measuredCubics.length;
2747
+ for (let i = cutCubicIdx + 1; i < totalCubics; i++) {
2748
+ const mc = this.measuredCubics[i];
2749
+ if (!mc.cubic.zeroLength()) {
2750
+ result.push({
2751
+ cubic: mc.cubic,
2752
+ startOutlineProgress: shift(mc.startOutlineProgress),
2753
+ endOutlineProgress: shift(mc.endOutlineProgress)
2754
+ });
2755
+ }
2756
+ }
2757
+ for (let i = 0; i < cutCubicIdx; i++) {
2758
+ const mc = this.measuredCubics[i];
2575
2759
  if (!mc.cubic.zeroLength()) {
2576
2760
  result.push({
2577
2761
  cubic: mc.cubic,
@@ -2653,7 +2837,16 @@ var MeasuredPolygon = class _MeasuredPolygon {
2653
2837
  const mc = measured[Math.min(cubicIdx + middleCubicIdx, measured.length - 1)];
2654
2838
  if (mc) {
2655
2839
  const midProgress = (mc.startOutlineProgress + mc.endOutlineProgress) / 2;
2656
- featureList.push({ progress: midProgress, convex: feature.convex });
2840
+ const pt = mc.cubic.pointOnCurve(0.5);
2841
+ const angle = Math.atan2(
2842
+ pt.y - polygon.centerY,
2843
+ pt.x - polygon.centerX
2844
+ );
2845
+ featureList.push({
2846
+ progress: midProgress,
2847
+ convex: feature.convex,
2848
+ angle
2849
+ });
2657
2850
  }
2658
2851
  }
2659
2852
  cubicIdx += feature.cubics.length;
@@ -2663,18 +2856,30 @@ var MeasuredPolygon = class _MeasuredPolygon {
2663
2856
  };
2664
2857
  function approximateLength(cubic) {
2665
2858
  let len = 0;
2666
- let prevX = cubic.anchor0X, prevY = cubic.anchor0Y;
2859
+ const p = cubic.points;
2860
+ let prevX = p[0];
2861
+ let prevY = p[1];
2667
2862
  const steps = 4;
2668
2863
  for (let i = 1; i <= steps; i++) {
2669
- const p = cubic.pointOnCurve(i / steps);
2670
- len += distance(p.x - prevX, p.y - prevY);
2671
- prevX = p.x;
2672
- prevY = p.y;
2864
+ const t = i / steps;
2865
+ const u = 1 - t;
2866
+ const uu = u * u;
2867
+ const tt = t * t;
2868
+ const uuu = uu * u;
2869
+ const ttt = tt * t;
2870
+ const c0 = 3 * t * uu;
2871
+ const c1 = 3 * tt * u;
2872
+ const px = p[0] * uuu + p[2] * c0 + p[4] * c1 + p[6] * ttt;
2873
+ const py = p[1] * uuu + p[3] * c0 + p[5] * c1 + p[7] * ttt;
2874
+ len += distance(px - prevX, py - prevY);
2875
+ prevX = px;
2876
+ prevY = py;
2673
2877
  }
2674
2878
  return len;
2675
2879
  }
2676
2880
 
2677
2881
  // src/shapes/morph/morph.ts
2882
+ var morphMatchCache = /* @__PURE__ */ new WeakMap();
2678
2883
  var Morph = class _Morph {
2679
2884
  /**
2680
2885
  * Creates a Morph between two shapes.
@@ -2690,23 +2895,79 @@ var Morph = class _Morph {
2690
2895
  * Returns the interpolated shape at the given progress as a list of Cubics.
2691
2896
  *
2692
2897
  * Note: This allocates a new list. For performance-critical animation loops,
2693
- * use {@link forEachCubic} instead.
2898
+ * use {@link toSvgPath} or {@link forEachCubic} instead.
2694
2899
  *
2695
2900
  * @param progress - Value in [0, 1]. 0 = start shape, 1 = end shape.
2696
2901
  * Values outside [0, 1] produce exaggerated shapes (useful for bounce/overshoot).
2697
2902
  * @returns List of interpolated Cubic curves forming the morphed shape
2698
2903
  */
2699
2904
  asCubics(progress) {
2700
- const result = [];
2701
- for (const [c1, c2] of this.morphMatch) {
2702
- result.push(
2703
- new Cubic(
2704
- c1.points.map((v, i) => interpolate(v, c2.points[i], progress))
2705
- )
2706
- );
2905
+ const match = this.morphMatch;
2906
+ const len = match.length;
2907
+ const result = new Array(len);
2908
+ for (let i = 0; i < len; i++) {
2909
+ const [c1, c2] = match[i];
2910
+ const p1 = c1.points;
2911
+ const p2 = c2.points;
2912
+ result[i] = new Cubic([
2913
+ p1[0] + (p2[0] - p1[0]) * progress,
2914
+ p1[1] + (p2[1] - p1[1]) * progress,
2915
+ p1[2] + (p2[2] - p1[2]) * progress,
2916
+ p1[3] + (p2[3] - p1[3]) * progress,
2917
+ p1[4] + (p2[4] - p1[4]) * progress,
2918
+ p1[5] + (p2[5] - p1[5]) * progress,
2919
+ p1[6] + (p2[6] - p1[6]) * progress,
2920
+ p1[7] + (p2[7] - p1[7]) * progress
2921
+ ]);
2707
2922
  }
2708
2923
  return result;
2709
2924
  }
2925
+ /**
2926
+ * Returns an SVG path data string for the morphed shape at the given progress.
2927
+ * Directly evaluates coordinates without allocating intermediate Cubic objects.
2928
+ *
2929
+ * @param progress - Value in [0, 1]
2930
+ * @param width - Target pixel width (default: 1)
2931
+ * @param height - Target pixel height (default: 1)
2932
+ */
2933
+ toSvgPath(progress, width = 1, height = 1) {
2934
+ const match = this.morphMatch;
2935
+ const len = match.length;
2936
+ if (len === 0) return "";
2937
+ const paddingX = width * 0.015;
2938
+ const paddingY = height * 0.015;
2939
+ const sx = width - 2 * paddingX;
2940
+ const sy = height - 2 * paddingY;
2941
+ const [firstC1, firstC2] = match[0];
2942
+ const f1 = firstC1.points;
2943
+ const f2 = firstC2.points;
2944
+ const a0x = f1[0] + (f2[0] - f1[0]) * progress;
2945
+ const a0y = f1[1] + (f2[1] - f1[1]) * progress;
2946
+ let d = `M ${formatCoordinate(paddingX + a0x * sx)} ${formatCoordinate(paddingY + sy * (1 - a0y))}`;
2947
+ for (let i = 0; i < len; i++) {
2948
+ const [c1, c2] = match[i];
2949
+ const p1 = c1.points;
2950
+ const p2 = c2.points;
2951
+ const c0x = p1[2] + (p2[2] - p1[2]) * progress;
2952
+ const c0y = p1[3] + (p2[3] - p1[3]) * progress;
2953
+ const c1x = p1[4] + (p2[4] - p1[4]) * progress;
2954
+ const c1y = p1[5] + (p2[5] - p1[5]) * progress;
2955
+ const a1x = p1[6] + (p2[6] - p1[6]) * progress;
2956
+ const a1y = p1[7] + (p2[7] - p1[7]) * progress;
2957
+ 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))}`;
2958
+ }
2959
+ return `${d} Z`;
2960
+ }
2961
+ /**
2962
+ * Returns a CSS clip-path value for the morphed shape at the given progress.
2963
+ *
2964
+ * @param progress - Value in [0, 1]
2965
+ * @param width - Element width in pixels
2966
+ * @param height - Element height in pixels
2967
+ */
2968
+ toClipPath(progress, width, height) {
2969
+ return `path('${this.toSvgPath(progress, width, height)}')`;
2970
+ }
2710
2971
  /**
2711
2972
  * Iterates over the morphed cubics without allocating new Cubic instances.
2712
2973
  * Reuses a single MutableCubic for each callback invocation.
@@ -2780,8 +3041,15 @@ var Morph = class _Morph {
2780
3041
  * @internal
2781
3042
  */
2782
3043
  static match(p1, p2) {
2783
- const measured1 = MeasuredPolygon.measurePolygon(p1);
2784
- const measured2 = MeasuredPolygon.measurePolygon(p2);
3044
+ let cachedMap = morphMatchCache.get(p1);
3045
+ if (cachedMap) {
3046
+ const cached = cachedMap.get(p2);
3047
+ if (cached) return cached;
3048
+ }
3049
+ const poly1 = p1.isClockwise() ? p1 : p1.reversed();
3050
+ const poly2 = p2.isClockwise() ? p2 : p2.reversed();
3051
+ const measured1 = MeasuredPolygon.measurePolygon(poly1);
3052
+ const measured2 = MeasuredPolygon.measurePolygon(poly2);
2785
3053
  const doubleMapper = featureMapper(measured1.features, measured2.features);
2786
3054
  const polygon2CutPoint = doubleMapper.map(0);
2787
3055
  const bs1 = measured1;
@@ -2792,11 +3060,12 @@ var Morph = class _Morph {
2792
3060
  let b2 = bs2.getOrNull(i2++);
2793
3061
  while (b1 !== null && b2 !== null) {
2794
3062
  const b1a = i1 >= bs1.size ? 1 : b1.endOutlineProgress;
2795
- const b2aRaw = i2 >= bs2.size ? 1 : positiveModulo(b2.endOutlineProgress + polygon2CutPoint, 1);
2796
- const b2a = doubleMapper.mapBack(b2aRaw);
3063
+ const b2a = i2 >= bs2.size ? 1 : b2.endOutlineProgress;
2797
3064
  const minb = Math.min(b1a, b2a);
2798
- let seg1 = b1, newb1 = bs1.getOrNull(i1);
2799
- let seg2 = b2, newb2 = bs2.getOrNull(i2);
3065
+ let seg1 = b1;
3066
+ let newb1 = bs1.getOrNull(i1);
3067
+ let seg2 = b2;
3068
+ let newb2 = bs2.getOrNull(i2);
2800
3069
  if (b1a > minb + ANGLE_EPSILON) {
2801
3070
  const [cut, rest] = bs1.cutAtProgress(b1, minb);
2802
3071
  seg1 = cut;
@@ -2805,11 +3074,7 @@ var Morph = class _Morph {
2805
3074
  i1++;
2806
3075
  }
2807
3076
  if (b2a > minb + ANGLE_EPSILON) {
2808
- const targetProgress = positiveModulo(
2809
- doubleMapper.map(minb) - polygon2CutPoint,
2810
- 1
2811
- );
2812
- const [cut, rest] = bs2.cutAtProgress(b2, targetProgress);
3077
+ const [cut, rest] = bs2.cutAtProgress(b2, minb);
2813
3078
  seg2 = cut;
2814
3079
  newb2 = rest;
2815
3080
  } else {
@@ -2819,39 +3084,37 @@ var Morph = class _Morph {
2819
3084
  b1 = newb1;
2820
3085
  b2 = newb2;
2821
3086
  }
3087
+ if (!cachedMap) {
3088
+ cachedMap = /* @__PURE__ */ new WeakMap();
3089
+ morphMatchCache.set(p1, cachedMap);
3090
+ }
3091
+ cachedMap.set(p2, result);
2822
3092
  return result;
2823
3093
  }
2824
3094
  };
2825
3095
 
2826
3096
  // src/shapes/render/shape-rendering.ts
2827
3097
  function toSvgPath(cubics, width = 1, height = 1) {
2828
- if (cubics.length === 0) return "";
3098
+ const len = cubics.length;
3099
+ if (len === 0) return "";
2829
3100
  const paddingX = width * 0.015;
2830
3101
  const paddingY = height * 0.015;
2831
3102
  const sx = width - 2 * paddingX;
2832
3103
  const sy = height - 2 * paddingY;
2833
- const flipY = (y) => paddingY + sy * (1 - y);
2834
- const parts = [
2835
- `M ${fmt(paddingX + cubics[0].anchor0X * sx)} ${fmt(flipY(cubics[0].anchor0Y))}`
2836
- ];
2837
- for (const c of cubics) {
2838
- parts.push(
2839
- `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))}`
2840
- );
3104
+ const c0 = cubics[0];
3105
+ let d = `M ${formatCoordinate(paddingX + c0.anchor0X * sx)} ${formatCoordinate(paddingY + sy * (1 - c0.anchor0Y))}`;
3106
+ for (let i = 0; i < len; i++) {
3107
+ const c = cubics[i];
3108
+ 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))}`;
2841
3109
  }
2842
- parts.push("Z");
2843
- return parts.join(" ");
3110
+ return `${d} Z`;
2844
3111
  }
2845
3112
  function toClipPath(polygon, width, height) {
2846
3113
  const pathData = toSvgPath(polygon.cubics, width, height);
2847
3114
  return `path('${pathData}')`;
2848
3115
  }
2849
3116
  function interpolatePath(morph, progress, width, height) {
2850
- const cubics = morph.asCubics(progress);
2851
- return toClipPath({ cubics }, width, height);
2852
- }
2853
- function fmt(v) {
2854
- return Number(v.toFixed(4)).toString();
3117
+ return morph.toClipPath(progress, width, height);
2855
3118
  }
2856
3119
  function cn(...inputs) {
2857
3120
  return tailwindMerge.twMerge(clsx.clsx(inputs));
@@ -14035,45 +14298,64 @@ var DialogOverlay = React67__namespace.forwardRef((_a, ref) => {
14035
14298
  ) }));
14036
14299
  });
14037
14300
  DialogOverlay.displayName = "DialogOverlay";
14038
- var DialogContent = React67__namespace.forwardRef((_a, ref) => {
14039
- var _b = _a, { className, children, hideCloseButton = false } = _b, props = __objRest(_b, ["className", "children", "hideCloseButton"]);
14040
- return /* @__PURE__ */ jsxRuntime.jsx(
14041
- RadixDialog__namespace.Content,
14042
- __spreadProps(__spreadValues({
14043
- ref,
14044
- asChild: true,
14045
- "aria-describedby": void 0
14046
- }, props), {
14047
- children: /* @__PURE__ */ jsxRuntime.jsxs(
14048
- react.m.div,
14049
- __spreadProps(__spreadValues({
14050
- className: cn(
14051
- "fixed left-1/2 top-1/2 z-50 -translate-x-1/2 -translate-y-1/2",
14052
- "w-[calc(100%-2rem)] max-w-140",
14053
- "rounded-[28px] bg-m3-surface-container-high p-6",
14054
- "shadow-lg outline-none focus-visible:ring-2 focus-visible:ring-m3-primary",
14055
- className
14056
- ),
14057
- role: "dialog"
14058
- }, MD3_CONTENT_ANIM), {
14059
- children: [
14060
- children,
14061
- !hideCloseButton && /* @__PURE__ */ jsxRuntime.jsx(RadixDialog__namespace.Close, { asChild: true, "aria-label": "Close dialog", children: /* @__PURE__ */ jsxRuntime.jsx(
14062
- IconButton,
14063
- {
14064
- size: "sm",
14065
- colorStyle: "filled",
14066
- className: "absolute right-4 top-4",
14067
- "aria-label": "Close",
14068
- children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "close", "aria-hidden": "true" })
14069
- }
14070
- ) })
14071
- ]
14072
- })
14073
- )
14074
- })
14075
- );
14076
- });
14301
+ var DEFAULT_CLOSE_BTN_PROPS = {
14302
+ size: "sm",
14303
+ colorStyle: "standard",
14304
+ className: "absolute right-4 top-4",
14305
+ "aria-label": "Close"
14306
+ };
14307
+ var DialogContent = React67__namespace.forwardRef(
14308
+ (_a, ref) => {
14309
+ var _b = _a, {
14310
+ className,
14311
+ children,
14312
+ hideCloseButton = false,
14313
+ closeButtonProps,
14314
+ closeButton
14315
+ } = _b, props = __objRest(_b, [
14316
+ "className",
14317
+ "children",
14318
+ "hideCloseButton",
14319
+ "closeButtonProps",
14320
+ "closeButton"
14321
+ ]);
14322
+ const closeAriaLabel = (() => {
14323
+ var _a2, _b2, _c;
14324
+ if (closeButton == null) {
14325
+ return (_a2 = closeButtonProps == null ? void 0 : closeButtonProps["aria-label"]) != null ? _a2 : DEFAULT_CLOSE_BTN_PROPS["aria-label"];
14326
+ }
14327
+ const el = closeButton;
14328
+ return (_c = (_b2 = el == null ? void 0 : el.props) == null ? void 0 : _b2["aria-label"]) != null ? _c : "Close";
14329
+ })();
14330
+ return /* @__PURE__ */ jsxRuntime.jsx(
14331
+ RadixDialog__namespace.Content,
14332
+ __spreadProps(__spreadValues({
14333
+ ref,
14334
+ asChild: true,
14335
+ "aria-describedby": void 0
14336
+ }, props), {
14337
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
14338
+ react.m.div,
14339
+ __spreadProps(__spreadValues({
14340
+ className: cn(
14341
+ "fixed left-1/2 top-1/2 z-50 -translate-x-1/2 -translate-y-1/2",
14342
+ "w-[calc(100%-2rem)] max-w-140",
14343
+ "rounded-[28px] bg-m3-surface-container-high p-6",
14344
+ "shadow-lg outline-none focus-visible:ring-2 focus-visible:ring-m3-primary",
14345
+ className
14346
+ ),
14347
+ role: "dialog"
14348
+ }, MD3_CONTENT_ANIM), {
14349
+ children: [
14350
+ children,
14351
+ closeButton != null ? /* @__PURE__ */ jsxRuntime.jsx(RadixDialog__namespace.Close, { asChild: true, "aria-label": closeAriaLabel, children: closeButton }) : !hideCloseButton ? /* @__PURE__ */ jsxRuntime.jsx(RadixDialog__namespace.Close, { asChild: true, "aria-label": closeAriaLabel, children: /* @__PURE__ */ jsxRuntime.jsx(IconButton, __spreadProps(__spreadValues(__spreadValues({}, DEFAULT_CLOSE_BTN_PROPS), closeButtonProps), { children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "close", "aria-hidden": "true" }) })) }) : null
14352
+ ]
14353
+ })
14354
+ )
14355
+ })
14356
+ );
14357
+ }
14358
+ );
14077
14359
  DialogContent.displayName = "DialogContent";
14078
14360
  var DialogIcon = React67__namespace.forwardRef((_a, ref) => {
14079
14361
  var _b = _a, { className, children } = _b, props = __objRest(_b, ["className", "children"]);
@@ -14164,6 +14446,8 @@ var DialogFullScreenContent = React67__namespace.forwardRef(
14164
14446
  title,
14165
14447
  actionLabel,
14166
14448
  onAction,
14449
+ actionButtonProps,
14450
+ closeButtonProps,
14167
14451
  showDivider
14168
14452
  } = _b, props = __objRest(_b, [
14169
14453
  "className",
@@ -14171,8 +14455,11 @@ var DialogFullScreenContent = React67__namespace.forwardRef(
14171
14455
  "title",
14172
14456
  "actionLabel",
14173
14457
  "onAction",
14458
+ "actionButtonProps",
14459
+ "closeButtonProps",
14174
14460
  "showDivider"
14175
14461
  ]);
14462
+ var _a2;
14176
14463
  return /* @__PURE__ */ jsxRuntime.jsx(
14177
14464
  RadixDialog__namespace.Content,
14178
14465
  __spreadProps(__spreadValues({
@@ -14192,16 +14479,33 @@ var DialogFullScreenContent = React67__namespace.forwardRef(
14192
14479
  }, MD3_FULLSCREEN_ANIM), {
14193
14480
  children: [
14194
14481
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex shrink-0 items-center px-4 h-14 gap-2 bg-m3-surface", children: [
14195
- /* @__PURE__ */ jsxRuntime.jsx(RadixDialog__namespace.Close, { asChild: true, "aria-label": "Close dialog", children: /* @__PURE__ */ jsxRuntime.jsx(IconButton, { size: "sm", colorStyle: "filled", "aria-label": "Close", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "close", "aria-hidden": "true" }) }) }),
14482
+ /* @__PURE__ */ jsxRuntime.jsx(
14483
+ RadixDialog__namespace.Close,
14484
+ {
14485
+ asChild: true,
14486
+ "aria-label": (_a2 = closeButtonProps == null ? void 0 : closeButtonProps["aria-label"]) != null ? _a2 : "Close",
14487
+ children: /* @__PURE__ */ jsxRuntime.jsx(
14488
+ IconButton,
14489
+ __spreadProps(__spreadValues({
14490
+ size: "sm",
14491
+ colorStyle: "standard",
14492
+ "aria-label": "Close"
14493
+ }, closeButtonProps), {
14494
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "close", "aria-hidden": "true" })
14495
+ })
14496
+ )
14497
+ }
14498
+ ),
14196
14499
  title && /* @__PURE__ */ jsxRuntime.jsx(DialogTitle, { className: "flex-1 text-[22px] leading-7 font-medium truncate pr-2", children: title }),
14197
14500
  actionLabel && onAction && /* @__PURE__ */ jsxRuntime.jsx(
14198
14501
  "button",
14199
- {
14502
+ __spreadProps(__spreadValues({
14200
14503
  type: "button",
14201
14504
  onClick: onAction,
14202
- className: "text-sm font-medium text-m3-primary px-3 py-2 rounded-full hover:bg-m3-primary/8 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-m3-primary transition-colors whitespace-nowrap",
14505
+ className: "text-sm font-medium text-m3-primary px-3 py-2 rounded-full hover:bg-m3-primary/8 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-m3-primary transition-colors whitespace-nowrap disabled:opacity-[0.38] disabled:pointer-events-none"
14506
+ }, actionButtonProps), {
14203
14507
  children: actionLabel
14204
- }
14508
+ })
14205
14509
  )
14206
14510
  ] }),
14207
14511
  showDivider && /* @__PURE__ */ jsxRuntime.jsx("hr", { className: "border-m3-outline-variant w-full shrink-0 m-0" }),
@@ -15197,21 +15501,13 @@ var normalizeAngle = (angle) => {
15197
15501
  return a;
15198
15502
  };
15199
15503
  var angleToHour12 = (angle) => {
15200
- const hourOffset = RADIANS_PER_HOUR / 2;
15201
- const totalOffset = hourOffset + QUARTER_CIRCLE;
15202
- const hour = Math.round((angle + totalOffset) / RADIANS_PER_HOUR) % 12;
15504
+ const normalized = normalizeAngle(angle + QUARTER_CIRCLE);
15505
+ const hour = Math.round(normalized / RADIANS_PER_HOUR) % 12;
15203
15506
  return hour === 0 ? 12 : hour;
15204
15507
  };
15205
- var angleToMinute = (angle, snap = true) => {
15206
- if (snap) {
15207
- const minuteOffset = RADIANS_PER_MINUTE / 2;
15208
- const totalOffset = minuteOffset + QUARTER_CIRCLE;
15209
- const raw2 = Math.round((angle + totalOffset) / RADIANS_PER_MINUTE) % 60;
15210
- return raw2 < 0 ? raw2 + 60 : raw2;
15211
- }
15508
+ var angleToMinute = (angle, _snap = true) => {
15212
15509
  const normalized = normalizeAngle(angle + QUARTER_CIRCLE);
15213
- const raw = Math.round(normalized / FULL_CIRCLE * 60) % 60;
15214
- return raw < 0 ? raw + 60 : raw;
15510
+ return Math.round(normalized / RADIANS_PER_MINUTE) % 60;
15215
15511
  };
15216
15512
  var getSelectorPosition = (angle, selection, is24hour, isPm) => {
15217
15513
  const useInner = is24hour && isPm && selection === "hour";
@@ -20492,12 +20788,13 @@ var TextFieldComponent = React67__namespace.forwardRef(
20492
20788
  if (autoResize) {
20493
20789
  textarea.style.height = "auto";
20494
20790
  textarea.style.height = `${textarea.scrollHeight}px`;
20495
- if (maxRows) {
20496
- textarea.style.maxHeight = `${maxRows * LINE_HEIGHT_PX}px`;
20497
- }
20791
+ textarea.style.overflowY = "hidden";
20792
+ } else {
20793
+ textarea.style.height = "";
20794
+ textarea.style.maxHeight = "";
20795
+ textarea.style.overflowY = "";
20498
20796
  }
20499
- textarea.style.overflowY = "hidden";
20500
- }, [type, autoResize, maxRows, currentValue]);
20797
+ }, [type, autoResize, currentValue]);
20501
20798
  const handleValueChange = React67__namespace.useCallback(
20502
20799
  (newValue) => {
20503
20800
  var _a2, _b;
@@ -20720,7 +21017,7 @@ var TextFieldComponent = React67__namespace.forwardRef(
20720
21017
  className: cn(
20721
21018
  inputClass,
20722
21019
  "resize-none mt-2",
20723
- autoResize ? "h-auto" : "h-full"
21020
+ autoResize ? "h-auto" : "min-h-full overflow-y-auto"
20724
21021
  ),
20725
21022
  style: { direction: textDirection || void 0 }
20726
21023
  }, inputProps)
@@ -20859,6 +21156,15 @@ var Select = React67__namespace.forwardRef(
20859
21156
  openRef.current = open;
20860
21157
  const [searchQuery, setSearchQuery] = React67__namespace.useState("");
20861
21158
  const [scrollViewport, setScrollViewport] = React67__namespace.useState(null);
21159
+ React67__namespace.useEffect(() => {
21160
+ var _a3, _b3;
21161
+ const isDev = ((_b3 = (_a3 = globalThis.process) == null ? void 0 : _a3.env) == null ? void 0 : _b3.NODE_ENV) !== "production";
21162
+ if (isDev && onSearchChange && !searchable) {
21163
+ console.warn(
21164
+ "[Select] `onSearchChange` was provided but `searchable` is false. The callback will never be called unless you also set `searchable={true}`."
21165
+ );
21166
+ }
21167
+ }, [onSearchChange, searchable]);
20862
21168
  const selectedOption = React67__namespace.useMemo(
20863
21169
  () => options.find((opt) => opt.value === currentValue),
20864
21170
  [options, currentValue]
@@ -21084,10 +21390,51 @@ function SelectEmpty(_a) {
21084
21390
  })
21085
21391
  );
21086
21392
  }
21393
+
21394
+ // src/ui/shape-media/animation-utils.ts
21087
21395
  function prefersReducedMotion() {
21088
- if (typeof window === "undefined") return false;
21396
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
21397
+ return false;
21398
+ }
21089
21399
  return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
21090
21400
  }
21401
+ function easeCubicInOut(t) {
21402
+ return t < 0.5 ? 4 * t * t * t : 1 - (-2 * t + 2) ** 3 / 2;
21403
+ }
21404
+ function cubicBezier(x1, y1, x2, y2) {
21405
+ return (t) => {
21406
+ if (t === 0 || t === 1) return t;
21407
+ let tParam = t;
21408
+ for (let i = 0; i < 8; i++) {
21409
+ const x = 3 * (1 - tParam) ** 2 * tParam * x1 + 3 * (1 - tParam) * tParam ** 2 * x2 + tParam ** 3;
21410
+ const dx = 3 * (1 - tParam) ** 2 * x1 + 6 * (1 - tParam) * tParam * (x2 - x1) + 3 * tParam ** 2 * (1 - x2);
21411
+ if (Math.abs(dx) < 1e-6) break;
21412
+ tParam -= (x - t) / dx;
21413
+ }
21414
+ return 3 * (1 - tParam) ** 2 * tParam * y1 + 3 * (1 - tParam) * tParam ** 2 * y2 + tParam ** 3;
21415
+ };
21416
+ }
21417
+ function getEasingFunction(easing) {
21418
+ if (Array.isArray(easing)) {
21419
+ if (easing.length === 4) {
21420
+ const [x1, y1, x2, y2] = easing;
21421
+ return cubicBezier(x1, y1, x2, y2);
21422
+ }
21423
+ return easeCubicInOut;
21424
+ }
21425
+ switch (easing) {
21426
+ case "linear":
21427
+ return (t) => t;
21428
+ case "ease-in":
21429
+ return (t) => t * t * t;
21430
+ case "ease-out":
21431
+ return (t) => 1 - (1 - t) ** 3;
21432
+ default:
21433
+ return easeCubicInOut;
21434
+ }
21435
+ }
21436
+
21437
+ // src/ui/shape-media/useShapeMorph.ts
21091
21438
  function useShapeMorph({
21092
21439
  shape,
21093
21440
  morphTo,
@@ -21102,10 +21449,16 @@ function useShapeMorph({
21102
21449
  const duration = (_a = morphOptions == null ? void 0 : morphOptions.duration) != null ? _a : 0.3;
21103
21450
  const easing = (_b = morphOptions == null ? void 0 : morphOptions.easing) != null ? _b : "ease-in-out";
21104
21451
  const easingFunction = React67.useMemo(() => getEasingFunction(easing), [easing]);
21452
+ const resolvedTarget = React67.useMemo(() => {
21453
+ if (morphTo === "auto") {
21454
+ return getRecommendedMorphShape(shape);
21455
+ }
21456
+ return morphTo;
21457
+ }, [morphTo, shape]);
21105
21458
  const startShape = React67.useMemo(() => resolveShape(shape), [shape]);
21106
21459
  const endShape = React67.useMemo(
21107
- () => morphTo ? resolveShape(morphTo) : null,
21108
- [morphTo]
21460
+ () => resolvedTarget ? resolveShape(resolvedTarget) : null,
21461
+ [resolvedTarget]
21109
21462
  );
21110
21463
  const morph = React67.useMemo(
21111
21464
  () => endShape ? new Morph(startShape, endShape) : null,
@@ -21120,7 +21473,45 @@ function useShapeMorph({
21120
21473
  const startProgressRef = React67.useRef(0);
21121
21474
  const targetProgressRef = React67.useRef(0);
21122
21475
  const currentProgressRef = React67.useRef(0);
21476
+ const prevShapeRef = React67.useRef(shape);
21477
+ React67.useEffect(() => {
21478
+ if (prevShapeRef.current === shape) return;
21479
+ const oldShape = prevShapeRef.current;
21480
+ prevShapeRef.current = shape;
21481
+ if (disabled || prefersReducedMotion()) {
21482
+ setClipPath(toClipPath(resolveShape(shape), width, height));
21483
+ return;
21484
+ }
21485
+ try {
21486
+ const transitionMorph = new Morph(
21487
+ resolveShape(oldShape),
21488
+ resolveShape(shape)
21489
+ );
21490
+ const start = performance.now();
21491
+ const durationMs = duration * 1e3;
21492
+ const animateTransition = (timestamp) => {
21493
+ const elapsed = timestamp - start;
21494
+ const rawT = Math.min(elapsed / durationMs, 1);
21495
+ const ease = easingFunction(rawT);
21496
+ setClipPath(interpolatePath(transitionMorph, ease, width, height));
21497
+ if (rawT < 1) {
21498
+ animRef.current = requestAnimationFrame(animateTransition);
21499
+ } else {
21500
+ animRef.current = null;
21501
+ }
21502
+ };
21503
+ if (animRef.current !== null) cancelAnimationFrame(animRef.current);
21504
+ animRef.current = requestAnimationFrame(animateTransition);
21505
+ } catch (e) {
21506
+ setClipPath(toClipPath(resolveShape(shape), width, height));
21507
+ }
21508
+ }, [shape, disabled, duration, easingFunction, width, height]);
21509
+ const prevDimsRef = React67.useRef({ width, height, startShape, morph });
21123
21510
  React67.useEffect(() => {
21511
+ if (prevDimsRef.current.width === width && prevDimsRef.current.height === height && prevDimsRef.current.startShape === startShape && prevDimsRef.current.morph === morph) {
21512
+ return;
21513
+ }
21514
+ prevDimsRef.current = { width, height, startShape, morph };
21124
21515
  if (morph) {
21125
21516
  setClipPath(
21126
21517
  interpolatePath(morph, currentProgressRef.current, width, height)
@@ -21214,41 +21605,6 @@ function useShapeMorph({
21214
21605
  }, [morphTo, disabled, morphOn, activate, deactivate, isActive]);
21215
21606
  return { clipPath, isActive, activate, deactivate, setProgress, handlers };
21216
21607
  }
21217
- function easeCubicInOut(t) {
21218
- return t < 0.5 ? 4 * t * t * t : 1 - (-2 * t + 2) ** 3 / 2;
21219
- }
21220
- function cubicBezier(x1, y1, x2, y2) {
21221
- return (t) => {
21222
- if (t === 0 || t === 1) return t;
21223
- let tParam = t;
21224
- for (let i = 0; i < 8; i++) {
21225
- const x = 3 * (1 - tParam) ** 2 * tParam * x1 + 3 * (1 - tParam) * tParam ** 2 * x2 + tParam ** 3;
21226
- const dx = 3 * (1 - tParam) ** 2 * x1 + 6 * (1 - tParam) * tParam * (x2 - x1) + 3 * tParam ** 2 * (1 - x2);
21227
- if (Math.abs(dx) < 1e-6) break;
21228
- tParam -= (x - t) / dx;
21229
- }
21230
- return 3 * (1 - tParam) ** 2 * tParam * y1 + 3 * (1 - tParam) * tParam ** 2 * y2 + tParam ** 3;
21231
- };
21232
- }
21233
- function getEasingFunction(easing) {
21234
- if (Array.isArray(easing)) {
21235
- if (easing.length === 4) {
21236
- const [x1, y1, x2, y2] = easing;
21237
- return cubicBezier(x1, y1, x2, y2);
21238
- }
21239
- return easeCubicInOut;
21240
- }
21241
- switch (easing) {
21242
- case "linear":
21243
- return (t) => t;
21244
- case "ease-in":
21245
- return (t) => t * t * t;
21246
- case "ease-out":
21247
- return (t) => 1 - (1 - t) ** 3;
21248
- default:
21249
- return easeCubicInOut;
21250
- }
21251
- }
21252
21608
  function ShapeMedia({
21253
21609
  shape,
21254
21610
  morphTo,
@@ -21376,10 +21732,14 @@ function ShapeSvg({
21376
21732
  style,
21377
21733
  "aria-label": ariaLabel
21378
21734
  }) {
21735
+ const resolvedTarget = React67.useMemo(() => {
21736
+ if (morphTo === "auto") return getRecommendedMorphShape(shape);
21737
+ return morphTo;
21738
+ }, [morphTo, shape]);
21379
21739
  const startPolygon = React67.useMemo(() => resolveShape(shape), [shape]);
21380
21740
  const endPolygon = React67.useMemo(
21381
- () => morphTo ? resolveShape(morphTo) : null,
21382
- [morphTo]
21741
+ () => resolvedTarget ? resolveShape(resolvedTarget) : null,
21742
+ [resolvedTarget]
21383
21743
  );
21384
21744
  const morph = React67.useMemo(
21385
21745
  () => endPolygon ? new Morph(startPolygon, endPolygon) : null,
@@ -21413,6 +21773,176 @@ function ShapeSvg({
21413
21773
  }
21414
21774
  );
21415
21775
  }
21776
+ function useShapeSequenceMorph({
21777
+ shapes,
21778
+ duration = 0.6,
21779
+ interval = 2,
21780
+ autoplay = false,
21781
+ loop = true,
21782
+ morphOptions,
21783
+ width,
21784
+ height,
21785
+ disabled = false
21786
+ }) {
21787
+ var _a;
21788
+ const resolvedDuration = (_a = morphOptions == null ? void 0 : morphOptions.duration) != null ? _a : duration;
21789
+ const [currentIndex, setCurrentIndex] = React67.useState(0);
21790
+ const [nextIndex, setNextIndex] = React67.useState(() => shapes.length > 1 ? 1 : 0);
21791
+ const [progress, setProgress] = React67.useState(0);
21792
+ const [isPlaying, setIsPlaying] = React67.useState(autoplay);
21793
+ const animRef = React67.useRef(null);
21794
+ const timerRef = React67.useRef(null);
21795
+ const startTimeRef = React67.useRef(0);
21796
+ const isTransitioningRef = React67.useRef(false);
21797
+ const numShapes = shapes.length;
21798
+ const currentShape = React67.useMemo(() => {
21799
+ if (numShapes === 0) return resolveShape("circle");
21800
+ const safeIdx = (currentIndex % numShapes + numShapes) % numShapes;
21801
+ return resolveShape(shapes[safeIdx]);
21802
+ }, [shapes, currentIndex, numShapes]);
21803
+ const targetShape = React67.useMemo(() => {
21804
+ if (numShapes === 0) return resolveShape("circle");
21805
+ const safeIdx = (nextIndex % numShapes + numShapes) % numShapes;
21806
+ return resolveShape(shapes[safeIdx]);
21807
+ }, [shapes, nextIndex, numShapes]);
21808
+ const morph = React67.useMemo(() => {
21809
+ if (currentShape === targetShape) return null;
21810
+ return new Morph(currentShape, targetShape);
21811
+ }, [currentShape, targetShape]);
21812
+ const [clipPath, setClipPath] = React67.useState(
21813
+ () => toClipPath(currentShape, width, height)
21814
+ );
21815
+ const prevDimsRef = React67.useRef({ width, height, currentShape, morph, progress });
21816
+ React67.useEffect(() => {
21817
+ if (prevDimsRef.current.width === width && prevDimsRef.current.height === height && prevDimsRef.current.currentShape === currentShape && prevDimsRef.current.morph === morph && prevDimsRef.current.progress === progress) {
21818
+ return;
21819
+ }
21820
+ prevDimsRef.current = { width, height, currentShape, morph, progress };
21821
+ if (morph && progress > 0) {
21822
+ setClipPath(interpolatePath(morph, progress, width, height));
21823
+ } else {
21824
+ setClipPath(toClipPath(currentShape, width, height));
21825
+ }
21826
+ }, [width, height, currentShape, morph, progress]);
21827
+ const transitionTo = React67.useCallback(
21828
+ (toIndex) => {
21829
+ if (disabled || numShapes <= 1 || isTransitioningRef.current) return;
21830
+ const targetSafe = (toIndex % numShapes + numShapes) % numShapes;
21831
+ if (targetSafe === currentIndex) return;
21832
+ setNextIndex(targetSafe);
21833
+ if (prefersReducedMotion()) {
21834
+ setCurrentIndex(targetSafe);
21835
+ setNextIndex((targetSafe + 1) % numShapes);
21836
+ setProgress(0);
21837
+ setClipPath(
21838
+ toClipPath(resolveShape(shapes[targetSafe]), width, height)
21839
+ );
21840
+ return;
21841
+ }
21842
+ isTransitioningRef.current = true;
21843
+ startTimeRef.current = performance.now();
21844
+ const fromPoly = resolveShape(shapes[currentIndex]);
21845
+ const toPoly = resolveShape(shapes[targetSafe]);
21846
+ const activeMorph = new Morph(fromPoly, toPoly);
21847
+ const animate7 = (timestamp) => {
21848
+ const elapsed = timestamp - startTimeRef.current;
21849
+ const durationMs = resolvedDuration * 1e3;
21850
+ const rawT = Math.min(elapsed / durationMs, 1);
21851
+ const ease = easeCubicInOut(rawT);
21852
+ setProgress(rawT);
21853
+ setClipPath(interpolatePath(activeMorph, ease, width, height));
21854
+ if (rawT < 1) {
21855
+ animRef.current = requestAnimationFrame(animate7);
21856
+ } else {
21857
+ animRef.current = null;
21858
+ isTransitioningRef.current = false;
21859
+ setCurrentIndex(targetSafe);
21860
+ setNextIndex((targetSafe + 1) % numShapes);
21861
+ setProgress(0);
21862
+ }
21863
+ };
21864
+ if (animRef.current !== null) cancelAnimationFrame(animRef.current);
21865
+ animRef.current = requestAnimationFrame(animate7);
21866
+ },
21867
+ [
21868
+ disabled,
21869
+ numShapes,
21870
+ currentIndex,
21871
+ shapes,
21872
+ resolvedDuration,
21873
+ width,
21874
+ height
21875
+ ]
21876
+ );
21877
+ const next = React67.useCallback(() => {
21878
+ if (numShapes <= 1) return;
21879
+ if (!loop && currentIndex >= numShapes - 1) return;
21880
+ transitionTo((currentIndex + 1) % numShapes);
21881
+ }, [numShapes, loop, currentIndex, transitionTo]);
21882
+ const prev = React67.useCallback(() => {
21883
+ if (numShapes <= 1) return;
21884
+ if (!loop && currentIndex <= 0) return;
21885
+ transitionTo((currentIndex - 1 + numShapes) % numShapes);
21886
+ }, [numShapes, loop, currentIndex, transitionTo]);
21887
+ const goTo = React67.useCallback(
21888
+ (index) => {
21889
+ transitionTo(index);
21890
+ },
21891
+ [transitionTo]
21892
+ );
21893
+ const play = React67.useCallback(() => {
21894
+ setIsPlaying(true);
21895
+ }, []);
21896
+ const pause = React67.useCallback(() => {
21897
+ setIsPlaying(false);
21898
+ if (timerRef.current !== null) {
21899
+ clearTimeout(timerRef.current);
21900
+ timerRef.current = null;
21901
+ }
21902
+ }, []);
21903
+ React67.useEffect(() => {
21904
+ if (!isPlaying || disabled || numShapes <= 1) return;
21905
+ timerRef.current = setTimeout(() => {
21906
+ if (!loop && currentIndex >= numShapes - 1) {
21907
+ setIsPlaying(false);
21908
+ return;
21909
+ }
21910
+ next();
21911
+ }, interval * 1e3);
21912
+ return () => {
21913
+ if (timerRef.current !== null) {
21914
+ clearTimeout(timerRef.current);
21915
+ timerRef.current = null;
21916
+ }
21917
+ };
21918
+ }, [isPlaying, disabled, numShapes, loop, currentIndex, interval, next]);
21919
+ React67.useEffect(() => {
21920
+ return () => {
21921
+ if (animRef.current !== null) cancelAnimationFrame(animRef.current);
21922
+ if (timerRef.current !== null) clearTimeout(timerRef.current);
21923
+ };
21924
+ }, []);
21925
+ React67.useEffect(() => {
21926
+ if (currentIndex >= shapes.length) {
21927
+ setCurrentIndex(0);
21928
+ setNextIndex(shapes.length > 1 ? 1 : 0);
21929
+ }
21930
+ }, [shapes.length, currentIndex]);
21931
+ const safeCurrentIndex = numShapes > 0 ? (currentIndex % numShapes + numShapes) % numShapes : 0;
21932
+ const safeNextIndex = numShapes > 0 ? (nextIndex % numShapes + numShapes) % numShapes : 0;
21933
+ return {
21934
+ clipPath,
21935
+ currentIndex: safeCurrentIndex,
21936
+ nextIndex: safeNextIndex,
21937
+ progress,
21938
+ isPlaying,
21939
+ next,
21940
+ prev,
21941
+ goTo,
21942
+ play,
21943
+ pause
21944
+ };
21945
+ }
21416
21946
  var INDICATOR = "h-1 w-8 rounded-full bg-m3-on-surface-variant/40";
21417
21947
  var DragHandle = React67__namespace.forwardRef(
21418
21948
  ({ className, onCycle, "aria-label": ariaLabel }, ref) => {
@@ -27549,6 +28079,8 @@ exports.MD3CornerRadius = MD3CornerRadius;
27549
28079
  exports.MD3Shapes = MD3Shapes;
27550
28080
  exports.MD3ThemeProvider = MD3ThemeProvider;
27551
28081
  exports.MD3_EXPRESSIVE_FONT_VARIATION = MD3_EXPRESSIVE_FONT_VARIATION;
28082
+ exports.MD3_SHAPE_FAMILIES = MD3_SHAPE_FAMILIES;
28083
+ exports.MD3_SHAPE_PAIRINGS = MD3_SHAPE_PAIRINGS;
27552
28084
  exports.MENU_CHECK_ICON_SIZE = MENU_CHECK_ICON_SIZE;
27553
28085
  exports.MENU_CONTAINER_VARIANTS = MENU_CONTAINER_VARIANTS;
27554
28086
  exports.MENU_GROUP_GAP = MENU_GROUP_GAP;
@@ -27688,12 +28220,14 @@ exports.dividePoint = dividePoint;
27688
28220
  exports.dotProduct = dotProduct;
27689
28221
  exports.edgeFeature = edgeFeature;
27690
28222
  exports.featureMapper = featureMapper;
28223
+ exports.formatCoordinate = formatCoordinate;
27691
28224
  exports.generateM3Theme = generateM3Theme;
27692
28225
  exports.getDirection = getDirection;
27693
28226
  exports.getDistance = getDistance;
27694
28227
  exports.getDistanceSquared = getDistanceSquared;
27695
28228
  exports.getExpressiveShape = getExpressiveShape;
27696
28229
  exports.getListItemHeight = getListItemHeight;
28230
+ exports.getRecommendedMorphShape = getRecommendedMorphShape;
27697
28231
  exports.getToolbarColors = getToolbarColors;
27698
28232
  exports.interpolate = interpolate;
27699
28233
  exports.interpolatePath = interpolatePath;
@@ -27734,6 +28268,7 @@ exports.useRipple = useRipple2;
27734
28268
  exports.useRippleState = useRippleState;
27735
28269
  exports.useSearchKeyboard = useSearchKeyboard;
27736
28270
  exports.useShapeMorph = useShapeMorph;
28271
+ exports.useShapeSequenceMorph = useShapeSequenceMorph;
27737
28272
  exports.useSnackbar = useSnackbar;
27738
28273
  exports.useSnackbarState = useSnackbarState;
27739
28274
  exports.useTheme = useTheme;