@bug-on/m3-expressive 1.2.6 → 1.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/buttons.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { Slot } from '@radix-ui/react-slot';
3
- import { m, LazyMotion, domMax, useMotionValue, animate, useAnimationFrame, AnimatePresence, useReducedMotion, useSpring, useTransform } from 'motion/react';
3
+ import { m, useAnimationFrame, LazyMotion, domMax, useMotionValue, animate, AnimatePresence, useReducedMotion, useSpring, useTransform } from 'motion/react';
4
4
  import * as React16 from 'react';
5
5
  import { clsx } from 'clsx';
6
6
  import { twMerge } from 'tailwind-merge';
@@ -256,7 +256,7 @@ function generateWavyCircularPath(center, radius, amplitude, wavelength) {
256
256
  if (i === 0) d += `M ${xAt(t0).toFixed(2)} ${yAt(t0).toFixed(2)}`;
257
257
  d += ` C ${cp1x.toFixed(2)} ${cp1y.toFixed(2)}, ${cp2x.toFixed(2)} ${cp2y.toFixed(2)}, ${xAt(t1).toFixed(2)} ${yAt(t1).toFixed(2)}`;
258
258
  }
259
- return d;
259
+ return `${d} Z`;
260
260
  }
261
261
  function getSinePath(startX, endX, phase, wl, amp) {
262
262
  if (startX >= endX) return "";
@@ -288,6 +288,14 @@ function getSinePath(startX, endX, phase, wl, amp) {
288
288
  }
289
289
  return d;
290
290
  }
291
+ var GLOBAL_ROTATION_DURATION = 6e3;
292
+ var GLOBAL_ROTATION_TARGET = 1080;
293
+ var ADDITIONAL_ROTATION_CYCLE = 1500;
294
+ var ADDITIONAL_ROTATION_STEP = 90;
295
+ var ADDITIONAL_ROTATION_EASE_DURATION = 500;
296
+ var DEFAULT_MIN_PROGRESS = 0.1;
297
+ var DEFAULT_MAX_PROGRESS = 0.8;
298
+ var PROGRESS_CYCLE_DURATION = 1500;
291
299
  var CircularProgress = React16.forwardRef(
292
300
  (_a, ref) => {
293
301
  var _b = _a, {
@@ -301,6 +309,10 @@ var CircularProgress = React16.forwardRef(
301
309
  crawlerSpeed = 1,
302
310
  color,
303
311
  trackColor,
312
+ showTrack = "auto",
313
+ minProgress = DEFAULT_MIN_PROGRESS,
314
+ maxProgress = DEFAULT_MAX_PROGRESS,
315
+ amplitudeRange,
304
316
  className,
305
317
  "aria-label": ariaLabel
306
318
  } = _b, restProps = __objRest(_b, [
@@ -314,6 +326,10 @@ var CircularProgress = React16.forwardRef(
314
326
  "crawlerSpeed",
315
327
  "color",
316
328
  "trackColor",
329
+ "showTrack",
330
+ "minProgress",
331
+ "maxProgress",
332
+ "amplitudeRange",
317
333
  "className",
318
334
  "aria-label"
319
335
  ]);
@@ -324,6 +340,7 @@ var CircularProgress = React16.forwardRef(
324
340
  const activeColor = color || "var(--md-sys-color-indicator-active)";
325
341
  const bgTrackColor = trackColor || "var(--md-sys-color-indicator-track)";
326
342
  const isWavy = shape === "wavy";
343
+ const shouldShowIndeterminateTrack = showTrack === "auto" ? isWavy : showTrack;
327
344
  const BASELINE_SIZE = 48;
328
345
  const scaleFactor = size / BASELINE_SIZE;
329
346
  const effectiveAmplitude = React16.useMemo(
@@ -334,6 +351,10 @@ var CircularProgress = React16.forwardRef(
334
351
  () => wavelength != null ? wavelength : 15 * scaleFactor,
335
352
  [wavelength, scaleFactor]
336
353
  );
354
+ const resolvedAmplitudeRange = React16.useMemo(
355
+ () => amplitudeRange != null ? amplitudeRange : [0, effectiveAmplitude],
356
+ [amplitudeRange, effectiveAmplitude]
357
+ );
337
358
  const wavyActivePath = React16.useMemo(
338
359
  () => isWavy ? generateWavyCircularPath(
339
360
  center,
@@ -353,6 +374,145 @@ var CircularProgress = React16.forwardRef(
353
374
  const trackLength = isDeterminate ? Math.max(1e-3, 1 - activeAngularFraction - 2 * gapForTrack) : 1;
354
375
  const ActiveCircleElem = m.circle;
355
376
  const ActivePathElem = m.path;
377
+ const indeterminateSvgRef = React16.useRef(null);
378
+ const indeterminateTrackRef = React16.useRef(null);
379
+ const indeterminateActiveRef = React16.useRef(null);
380
+ const indeterminateWavyTrackPathRef = React16.useRef(null);
381
+ const indeterminateWavyActivePathRef = React16.useRef(null);
382
+ const cachedPathLengthRef = React16.useRef(0);
383
+ React16.useLayoutEffect(() => {
384
+ if (!isWavy || !wavyActivePath) {
385
+ cachedPathLengthRef.current = circumference;
386
+ return;
387
+ }
388
+ const el = indeterminateActiveRef.current;
389
+ if (el && "getTotalLength" in el) {
390
+ try {
391
+ const len = el.getTotalLength();
392
+ if (len > 0) {
393
+ cachedPathLengthRef.current = len;
394
+ return;
395
+ }
396
+ } catch (e) {
397
+ }
398
+ }
399
+ cachedPathLengthRef.current = circumference;
400
+ }, [isWavy, circumference, wavyActivePath]);
401
+ useAnimationFrame((time) => {
402
+ if (isDeterminate) return;
403
+ const safeCrawlerSpeed = Math.max(0.1, crawlerSpeed);
404
+ const scaledTime = time * safeCrawlerSpeed;
405
+ const globalCycle = scaledTime % GLOBAL_ROTATION_DURATION;
406
+ const globalAngle = globalCycle / GLOBAL_ROTATION_DURATION * GLOBAL_ROTATION_TARGET;
407
+ const additionalFullCycles = Math.floor(
408
+ scaledTime / ADDITIONAL_ROTATION_CYCLE
409
+ );
410
+ const additionalCycleTime = scaledTime % ADDITIONAL_ROTATION_CYCLE;
411
+ const additionalEaseT = Math.min(
412
+ 1,
413
+ additionalCycleTime / ADDITIONAL_ROTATION_EASE_DURATION
414
+ );
415
+ const additionalAngle = (additionalFullCycles + easeInOutCubic(additionalEaseT)) * ADDITIONAL_ROTATION_STEP;
416
+ const totalRotation = globalAngle + additionalAngle;
417
+ if (indeterminateSvgRef.current) {
418
+ indeterminateSvgRef.current.style.transform = `rotate(${totalRotation - 90}deg)`;
419
+ }
420
+ const progressFullCycle = PROGRESS_CYCLE_DURATION * 2;
421
+ const progressCycleTime = scaledTime % progressFullCycle;
422
+ let progress;
423
+ if (progressCycleTime < PROGRESS_CYCLE_DURATION) {
424
+ const t = progressCycleTime / PROGRESS_CYCLE_DURATION;
425
+ progress = minProgress + (maxProgress - minProgress) * easeInOutCubic(t);
426
+ } else {
427
+ const t = (progressCycleTime - PROGRESS_CYCLE_DURATION) / PROGRESS_CYCLE_DURATION;
428
+ progress = maxProgress - (maxProgress - minProgress) * easeInOutCubic(t);
429
+ }
430
+ if (isWavy) {
431
+ const amplitudeT = (progress - minProgress) / (maxProgress - minProgress);
432
+ const currentAmplitude = resolvedAmplitudeRange[0] + (resolvedAmplitudeRange[1] - resolvedAmplitudeRange[0]) * amplitudeT;
433
+ const dynamicPath = generateWavyCircularPath(
434
+ center,
435
+ radius,
436
+ currentAmplitude,
437
+ effectiveWavelength
438
+ );
439
+ if (indeterminateWavyActivePathRef.current) {
440
+ indeterminateWavyActivePathRef.current.setAttribute("d", dynamicPath);
441
+ }
442
+ if (indeterminateWavyTrackPathRef.current) {
443
+ indeterminateWavyTrackPathRef.current.setAttribute("d", dynamicPath);
444
+ }
445
+ const pathEl = indeterminateWavyActivePathRef.current;
446
+ let totalLen = cachedPathLengthRef.current || circumference;
447
+ if (pathEl) {
448
+ try {
449
+ const len = pathEl.getTotalLength();
450
+ if (len > 0) totalLen = len;
451
+ } catch (e) {
452
+ }
453
+ }
454
+ const gapFrac = (gapSize + trackHeight) / totalLen;
455
+ const activeLen = totalLen * progress;
456
+ const activeOff = 0;
457
+ if (indeterminateWavyActivePathRef.current) {
458
+ indeterminateWavyActivePathRef.current.setAttribute(
459
+ "stroke-dasharray",
460
+ `${activeLen} ${totalLen}`
461
+ );
462
+ indeterminateWavyActivePathRef.current.setAttribute(
463
+ "stroke-dashoffset",
464
+ `${activeOff}`
465
+ );
466
+ }
467
+ if (shouldShowIndeterminateTrack && indeterminateWavyTrackPathRef.current) {
468
+ const trackStartFrac = progress + gapFrac;
469
+ const trackLen = Math.max(
470
+ 1e-3,
471
+ totalLen * (1 - progress - 2 * gapFrac)
472
+ );
473
+ const trackOff = -totalLen * trackStartFrac;
474
+ indeterminateWavyTrackPathRef.current.setAttribute(
475
+ "stroke-dasharray",
476
+ `${trackLen} ${totalLen}`
477
+ );
478
+ indeterminateWavyTrackPathRef.current.setAttribute(
479
+ "stroke-dashoffset",
480
+ `${trackOff}`
481
+ );
482
+ }
483
+ } else {
484
+ const totalLen = circumference;
485
+ const activeLen = totalLen * progress;
486
+ const activeOff = 0;
487
+ if (indeterminateActiveRef.current) {
488
+ indeterminateActiveRef.current.setAttribute(
489
+ "stroke-dasharray",
490
+ `${activeLen} ${totalLen}`
491
+ );
492
+ indeterminateActiveRef.current.setAttribute(
493
+ "stroke-dashoffset",
494
+ `${activeOff}`
495
+ );
496
+ }
497
+ if (shouldShowIndeterminateTrack && indeterminateTrackRef.current) {
498
+ const gapFrac = (gapSize + trackHeight) / totalLen;
499
+ const trackStartFrac = progress + gapFrac;
500
+ const trackLen = Math.max(
501
+ 1e-3,
502
+ totalLen * (1 - progress - 2 * gapFrac)
503
+ );
504
+ const trackOff = -totalLen * trackStartFrac;
505
+ indeterminateTrackRef.current.setAttribute(
506
+ "stroke-dasharray",
507
+ `${trackLen} ${totalLen}`
508
+ );
509
+ indeterminateTrackRef.current.setAttribute(
510
+ "stroke-dashoffset",
511
+ `${trackOff}`
512
+ );
513
+ }
514
+ }
515
+ });
356
516
  return /* @__PURE__ */ jsx(
357
517
  "div",
358
518
  __spreadProps(__spreadValues({
@@ -429,9 +589,10 @@ var CircularProgress = React16.forwardRef(
429
589
  ]
430
590
  }
431
591
  ),
432
- !isDeterminate && /* @__PURE__ */ jsxs(
433
- m.svg,
592
+ !isDeterminate && /* @__PURE__ */ jsx(
593
+ "svg",
434
594
  {
595
+ ref: indeterminateSvgRef,
435
596
  width: size,
436
597
  height: size,
437
598
  viewBox: `0 0 ${size} ${size}`,
@@ -440,84 +601,60 @@ var CircularProgress = React16.forwardRef(
440
601
  position: "absolute",
441
602
  inset: 0,
442
603
  overflow: "visible",
443
- transformOrigin: "center"
604
+ transformOrigin: "center",
605
+ transform: "rotate(-90deg)"
444
606
  },
445
- animate: { rotate: [0, 360] },
446
- transition: {
447
- duration: 2 / Math.max(0.1, crawlerSpeed),
448
- repeat: Number.POSITIVE_INFINITY,
449
- ease: "linear"
450
- },
451
- children: [
452
- /* @__PURE__ */ jsx(
453
- m.circle,
607
+ children: isWavy ? /* @__PURE__ */ jsxs(Fragment, { children: [
608
+ shouldShowIndeterminateTrack && /* @__PURE__ */ jsx(
609
+ "path",
454
610
  {
455
- cx: center,
456
- cy: center,
457
- r: radius,
611
+ ref: indeterminateWavyTrackPathRef,
612
+ d: wavyActivePath != null ? wavyActivePath : "",
458
613
  fill: "none",
459
614
  stroke: bgTrackColor,
460
615
  strokeWidth: trackHeight,
461
- strokeLinecap: "round",
462
- animate: {
463
- pathLength: [
464
- Math.max(1e-3, 0.9 - 2 * gapForTrack),
465
- Math.max(1e-3, 0.25 - 2 * gapForTrack),
466
- Math.max(1e-3, 0.9 - 2 * gapForTrack)
467
- ],
468
- pathOffset: [
469
- 0.1 + gapForTrack,
470
- 0.75 + gapForTrack,
471
- 0.1 + gapForTrack
472
- ]
473
- },
474
- transition: {
475
- duration: 2 / Math.max(0.1, crawlerSpeed),
476
- repeat: Number.POSITIVE_INFINITY,
477
- ease: [0.4, 0, 0.2, 1]
478
- }
616
+ strokeLinecap: "round"
479
617
  }
480
618
  ),
481
- isWavy ? /* @__PURE__ */ jsx(
482
- ActivePathElem,
619
+ /* @__PURE__ */ jsx(
620
+ "path",
483
621
  {
622
+ ref: indeterminateWavyActivePathRef,
484
623
  d: wavyActivePath != null ? wavyActivePath : "",
485
624
  fill: "none",
486
625
  stroke: activeColor,
487
626
  strokeWidth: trackHeight,
488
- strokeLinecap: "round",
489
- animate: {
490
- pathLength: [0.1, 0.75, 0.1],
491
- pathOffset: [0, 0.65, 1]
492
- },
493
- transition: {
494
- duration: 2 / Math.max(0.1, crawlerSpeed),
495
- repeat: Number.POSITIVE_INFINITY,
496
- ease: [0.4, 0, 0.2, 1]
497
- }
627
+ strokeLinecap: "round"
498
628
  }
499
- ) : /* @__PURE__ */ jsx(
500
- ActiveCircleElem,
629
+ )
630
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
631
+ shouldShowIndeterminateTrack && /* @__PURE__ */ jsx(
632
+ "circle",
501
633
  {
634
+ ref: indeterminateTrackRef,
635
+ cx: center,
636
+ cy: center,
637
+ r: radius,
638
+ fill: "none",
639
+ stroke: bgTrackColor,
640
+ strokeWidth: trackHeight,
641
+ strokeLinecap: "round"
642
+ }
643
+ ),
644
+ /* @__PURE__ */ jsx(
645
+ "circle",
646
+ {
647
+ ref: indeterminateActiveRef,
502
648
  cx: center,
503
649
  cy: center,
504
650
  r: radius,
505
651
  fill: "none",
506
652
  stroke: activeColor,
507
653
  strokeWidth: trackHeight,
508
- strokeLinecap: "round",
509
- animate: {
510
- pathLength: [0.1, 0.75, 0.1],
511
- pathOffset: [0, 0.65, 1]
512
- },
513
- transition: {
514
- duration: 2 / Math.max(0.1, crawlerSpeed),
515
- repeat: Number.POSITIVE_INFINITY,
516
- ease: [0.4, 0, 0.2, 1]
517
- }
654
+ strokeLinecap: "round"
518
655
  }
519
656
  )
520
- ]
657
+ ] })
521
658
  }
522
659
  )
523
660
  ] })
@@ -723,7 +860,7 @@ var WavyLinearTrack = React16.memo(function WavyLinearTrack2({
723
860
  currentAmp
724
861
  );
725
862
  } else if (fraction === 0) {
726
- activePathD = `M ${capWidth} 0 L ${capWidth + 0.01} 0`;
863
+ activePathD = "";
727
864
  }
728
865
  const trackStart = adjHead + totalGap;
729
866
  if (trackStart < width - capWidth) {
@@ -755,20 +892,20 @@ var WavyLinearTrack = React16.memo(function WavyLinearTrack2({
755
892
  activeLines.push({ tail: l1T, head: l1H });
756
893
  activeLines.push({ tail: l2T, head: l2H });
757
894
  }
758
- const segments = activeLines.map((line) => {
759
- const barTail = line.tail * width;
760
- const barHead = line.head * width;
895
+ const activeSegments = activeLines.map((line) => {
896
+ const rawTail = line.tail * width;
897
+ const rawHead = line.head * width;
761
898
  const adjTail = Math.max(
762
899
  capWidth,
763
- Math.min(width - capWidth, barTail)
900
+ Math.min(width - capWidth, rawTail)
764
901
  );
765
902
  const adjHead = Math.max(
766
903
  capWidth,
767
- Math.min(width - capWidth, barHead)
904
+ Math.min(width - capWidth, rawHead)
768
905
  );
769
906
  return { adjTail, adjHead };
770
907
  }).filter((seg) => seg.adjHead - seg.adjTail > 0.1);
771
- activePathD = segments.map(
908
+ activePathD = activeSegments.map(
772
909
  (seg) => getSinePath(
773
910
  seg.adjTail,
774
911
  seg.adjHead,
@@ -777,13 +914,24 @@ var WavyLinearTrack = React16.memo(function WavyLinearTrack2({
777
914
  currentAmp
778
915
  )
779
916
  ).join(" ");
917
+ const validActiveLines = activeLines.filter(
918
+ (line) => line.head > line.tail && line.head * width > 0 && line.tail * width < width
919
+ ).sort((a, b) => a.tail - b.tail);
780
920
  let currentTrackX = capWidth;
781
- for (const seg of segments) {
782
- const trackEnd = seg.adjTail - totalGap;
921
+ for (const line of validActiveLines) {
922
+ const blockStart = line.tail * width - totalGap;
923
+ const blockEnd = line.head * width + totalGap;
924
+ const trackEnd = Math.min(width - capWidth, blockStart);
783
925
  if (trackEnd > currentTrackX) {
784
- trackD += `${getSinePath(currentTrackX, trackEnd, phase, activeWavelength, trackAmp)} `;
926
+ trackD += `${getSinePath(
927
+ currentTrackX,
928
+ trackEnd,
929
+ phase,
930
+ activeWavelength,
931
+ trackAmp
932
+ )} `;
785
933
  }
786
- currentTrackX = Math.max(currentTrackX, seg.adjHead + totalGap);
934
+ currentTrackX = Math.max(currentTrackX, blockEnd);
787
935
  }
788
936
  if (currentTrackX < width - capWidth) {
789
937
  trackD += getSinePath(
@@ -796,9 +944,9 @@ var WavyLinearTrack = React16.memo(function WavyLinearTrack2({
796
944
  }
797
945
  }
798
946
  if (activePathRef.current)
799
- activePathRef.current.setAttribute("d", activePathD);
947
+ activePathRef.current.setAttribute("d", activePathD || "");
800
948
  if (trackPathRef.current)
801
- trackPathRef.current.setAttribute("d", trackD.trim());
949
+ trackPathRef.current.setAttribute("d", trackD.trim() || "");
802
950
  });
803
951
  return /* @__PURE__ */ jsx(
804
952
  "div",