@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/layout.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { Slot } from '@radix-ui/react-slot';
3
3
  import { cva } from 'class-variance-authority';
4
- import { m, LazyMotion, domMax, useMotionValue, animate, useAnimationFrame, AnimatePresence, useReducedMotion } from 'motion/react';
4
+ import { m, LazyMotion, domMax, useAnimationFrame, useMotionValue, animate, AnimatePresence, useReducedMotion } from 'motion/react';
5
5
  import * as React18 from 'react';
6
6
  import { createContext, useState, useCallback, useMemo, useEffect, useContext, createElement } from 'react';
7
7
  import { clsx } from 'clsx';
@@ -515,7 +515,7 @@ function generateWavyCircularPath(center, radius, amplitude, wavelength) {
515
515
  if (i === 0) d += `M ${xAt(t0).toFixed(2)} ${yAt(t0).toFixed(2)}`;
516
516
  d += ` C ${cp1x.toFixed(2)} ${cp1y.toFixed(2)}, ${cp2x.toFixed(2)} ${cp2y.toFixed(2)}, ${xAt(t1).toFixed(2)} ${yAt(t1).toFixed(2)}`;
517
517
  }
518
- return d;
518
+ return `${d} Z`;
519
519
  }
520
520
  function getSinePath(startX, endX, phase, wl, amp) {
521
521
  if (startX >= endX) return "";
@@ -547,6 +547,14 @@ function getSinePath(startX, endX, phase, wl, amp) {
547
547
  }
548
548
  return d;
549
549
  }
550
+ var GLOBAL_ROTATION_DURATION = 6e3;
551
+ var GLOBAL_ROTATION_TARGET = 1080;
552
+ var ADDITIONAL_ROTATION_CYCLE = 1500;
553
+ var ADDITIONAL_ROTATION_STEP = 90;
554
+ var ADDITIONAL_ROTATION_EASE_DURATION = 500;
555
+ var DEFAULT_MIN_PROGRESS = 0.1;
556
+ var DEFAULT_MAX_PROGRESS = 0.8;
557
+ var PROGRESS_CYCLE_DURATION = 1500;
550
558
  var CircularProgress = React18.forwardRef(
551
559
  (_a, ref) => {
552
560
  var _b = _a, {
@@ -560,6 +568,10 @@ var CircularProgress = React18.forwardRef(
560
568
  crawlerSpeed = 1,
561
569
  color,
562
570
  trackColor,
571
+ showTrack = "auto",
572
+ minProgress = DEFAULT_MIN_PROGRESS,
573
+ maxProgress = DEFAULT_MAX_PROGRESS,
574
+ amplitudeRange,
563
575
  className,
564
576
  "aria-label": ariaLabel
565
577
  } = _b, restProps = __objRest(_b, [
@@ -573,6 +585,10 @@ var CircularProgress = React18.forwardRef(
573
585
  "crawlerSpeed",
574
586
  "color",
575
587
  "trackColor",
588
+ "showTrack",
589
+ "minProgress",
590
+ "maxProgress",
591
+ "amplitudeRange",
576
592
  "className",
577
593
  "aria-label"
578
594
  ]);
@@ -583,6 +599,7 @@ var CircularProgress = React18.forwardRef(
583
599
  const activeColor = color || "var(--md-sys-color-indicator-active)";
584
600
  const bgTrackColor = trackColor || "var(--md-sys-color-indicator-track)";
585
601
  const isWavy = shape === "wavy";
602
+ const shouldShowIndeterminateTrack = showTrack === "auto" ? isWavy : showTrack;
586
603
  const BASELINE_SIZE = 48;
587
604
  const scaleFactor = size / BASELINE_SIZE;
588
605
  const effectiveAmplitude = React18.useMemo(
@@ -593,6 +610,10 @@ var CircularProgress = React18.forwardRef(
593
610
  () => wavelength != null ? wavelength : 15 * scaleFactor,
594
611
  [wavelength, scaleFactor]
595
612
  );
613
+ const resolvedAmplitudeRange = React18.useMemo(
614
+ () => amplitudeRange != null ? amplitudeRange : [0, effectiveAmplitude],
615
+ [amplitudeRange, effectiveAmplitude]
616
+ );
596
617
  const wavyActivePath = React18.useMemo(
597
618
  () => isWavy ? generateWavyCircularPath(
598
619
  center,
@@ -612,6 +633,145 @@ var CircularProgress = React18.forwardRef(
612
633
  const trackLength = isDeterminate ? Math.max(1e-3, 1 - activeAngularFraction - 2 * gapForTrack) : 1;
613
634
  const ActiveCircleElem = m.circle;
614
635
  const ActivePathElem = m.path;
636
+ const indeterminateSvgRef = React18.useRef(null);
637
+ const indeterminateTrackRef = React18.useRef(null);
638
+ const indeterminateActiveRef = React18.useRef(null);
639
+ const indeterminateWavyTrackPathRef = React18.useRef(null);
640
+ const indeterminateWavyActivePathRef = React18.useRef(null);
641
+ const cachedPathLengthRef = React18.useRef(0);
642
+ React18.useLayoutEffect(() => {
643
+ if (!isWavy || !wavyActivePath) {
644
+ cachedPathLengthRef.current = circumference;
645
+ return;
646
+ }
647
+ const el = indeterminateActiveRef.current;
648
+ if (el && "getTotalLength" in el) {
649
+ try {
650
+ const len = el.getTotalLength();
651
+ if (len > 0) {
652
+ cachedPathLengthRef.current = len;
653
+ return;
654
+ }
655
+ } catch (e) {
656
+ }
657
+ }
658
+ cachedPathLengthRef.current = circumference;
659
+ }, [isWavy, circumference, wavyActivePath]);
660
+ useAnimationFrame((time) => {
661
+ if (isDeterminate) return;
662
+ const safeCrawlerSpeed = Math.max(0.1, crawlerSpeed);
663
+ const scaledTime = time * safeCrawlerSpeed;
664
+ const globalCycle = scaledTime % GLOBAL_ROTATION_DURATION;
665
+ const globalAngle = globalCycle / GLOBAL_ROTATION_DURATION * GLOBAL_ROTATION_TARGET;
666
+ const additionalFullCycles = Math.floor(
667
+ scaledTime / ADDITIONAL_ROTATION_CYCLE
668
+ );
669
+ const additionalCycleTime = scaledTime % ADDITIONAL_ROTATION_CYCLE;
670
+ const additionalEaseT = Math.min(
671
+ 1,
672
+ additionalCycleTime / ADDITIONAL_ROTATION_EASE_DURATION
673
+ );
674
+ const additionalAngle = (additionalFullCycles + easeInOutCubic(additionalEaseT)) * ADDITIONAL_ROTATION_STEP;
675
+ const totalRotation = globalAngle + additionalAngle;
676
+ if (indeterminateSvgRef.current) {
677
+ indeterminateSvgRef.current.style.transform = `rotate(${totalRotation - 90}deg)`;
678
+ }
679
+ const progressFullCycle = PROGRESS_CYCLE_DURATION * 2;
680
+ const progressCycleTime = scaledTime % progressFullCycle;
681
+ let progress;
682
+ if (progressCycleTime < PROGRESS_CYCLE_DURATION) {
683
+ const t = progressCycleTime / PROGRESS_CYCLE_DURATION;
684
+ progress = minProgress + (maxProgress - minProgress) * easeInOutCubic(t);
685
+ } else {
686
+ const t = (progressCycleTime - PROGRESS_CYCLE_DURATION) / PROGRESS_CYCLE_DURATION;
687
+ progress = maxProgress - (maxProgress - minProgress) * easeInOutCubic(t);
688
+ }
689
+ if (isWavy) {
690
+ const amplitudeT = (progress - minProgress) / (maxProgress - minProgress);
691
+ const currentAmplitude = resolvedAmplitudeRange[0] + (resolvedAmplitudeRange[1] - resolvedAmplitudeRange[0]) * amplitudeT;
692
+ const dynamicPath = generateWavyCircularPath(
693
+ center,
694
+ radius,
695
+ currentAmplitude,
696
+ effectiveWavelength
697
+ );
698
+ if (indeterminateWavyActivePathRef.current) {
699
+ indeterminateWavyActivePathRef.current.setAttribute("d", dynamicPath);
700
+ }
701
+ if (indeterminateWavyTrackPathRef.current) {
702
+ indeterminateWavyTrackPathRef.current.setAttribute("d", dynamicPath);
703
+ }
704
+ const pathEl = indeterminateWavyActivePathRef.current;
705
+ let totalLen = cachedPathLengthRef.current || circumference;
706
+ if (pathEl) {
707
+ try {
708
+ const len = pathEl.getTotalLength();
709
+ if (len > 0) totalLen = len;
710
+ } catch (e) {
711
+ }
712
+ }
713
+ const gapFrac = (gapSize + trackHeight) / totalLen;
714
+ const activeLen = totalLen * progress;
715
+ const activeOff = 0;
716
+ if (indeterminateWavyActivePathRef.current) {
717
+ indeterminateWavyActivePathRef.current.setAttribute(
718
+ "stroke-dasharray",
719
+ `${activeLen} ${totalLen}`
720
+ );
721
+ indeterminateWavyActivePathRef.current.setAttribute(
722
+ "stroke-dashoffset",
723
+ `${activeOff}`
724
+ );
725
+ }
726
+ if (shouldShowIndeterminateTrack && indeterminateWavyTrackPathRef.current) {
727
+ const trackStartFrac = progress + gapFrac;
728
+ const trackLen = Math.max(
729
+ 1e-3,
730
+ totalLen * (1 - progress - 2 * gapFrac)
731
+ );
732
+ const trackOff = -totalLen * trackStartFrac;
733
+ indeterminateWavyTrackPathRef.current.setAttribute(
734
+ "stroke-dasharray",
735
+ `${trackLen} ${totalLen}`
736
+ );
737
+ indeterminateWavyTrackPathRef.current.setAttribute(
738
+ "stroke-dashoffset",
739
+ `${trackOff}`
740
+ );
741
+ }
742
+ } else {
743
+ const totalLen = circumference;
744
+ const activeLen = totalLen * progress;
745
+ const activeOff = 0;
746
+ if (indeterminateActiveRef.current) {
747
+ indeterminateActiveRef.current.setAttribute(
748
+ "stroke-dasharray",
749
+ `${activeLen} ${totalLen}`
750
+ );
751
+ indeterminateActiveRef.current.setAttribute(
752
+ "stroke-dashoffset",
753
+ `${activeOff}`
754
+ );
755
+ }
756
+ if (shouldShowIndeterminateTrack && indeterminateTrackRef.current) {
757
+ const gapFrac = (gapSize + trackHeight) / totalLen;
758
+ const trackStartFrac = progress + gapFrac;
759
+ const trackLen = Math.max(
760
+ 1e-3,
761
+ totalLen * (1 - progress - 2 * gapFrac)
762
+ );
763
+ const trackOff = -totalLen * trackStartFrac;
764
+ indeterminateTrackRef.current.setAttribute(
765
+ "stroke-dasharray",
766
+ `${trackLen} ${totalLen}`
767
+ );
768
+ indeterminateTrackRef.current.setAttribute(
769
+ "stroke-dashoffset",
770
+ `${trackOff}`
771
+ );
772
+ }
773
+ }
774
+ });
615
775
  return /* @__PURE__ */ jsx(
616
776
  "div",
617
777
  __spreadProps(__spreadValues({
@@ -688,9 +848,10 @@ var CircularProgress = React18.forwardRef(
688
848
  ]
689
849
  }
690
850
  ),
691
- !isDeterminate && /* @__PURE__ */ jsxs(
692
- m.svg,
851
+ !isDeterminate && /* @__PURE__ */ jsx(
852
+ "svg",
693
853
  {
854
+ ref: indeterminateSvgRef,
694
855
  width: size,
695
856
  height: size,
696
857
  viewBox: `0 0 ${size} ${size}`,
@@ -699,84 +860,60 @@ var CircularProgress = React18.forwardRef(
699
860
  position: "absolute",
700
861
  inset: 0,
701
862
  overflow: "visible",
702
- transformOrigin: "center"
703
- },
704
- animate: { rotate: [0, 360] },
705
- transition: {
706
- duration: 2 / Math.max(0.1, crawlerSpeed),
707
- repeat: Number.POSITIVE_INFINITY,
708
- ease: "linear"
863
+ transformOrigin: "center",
864
+ transform: "rotate(-90deg)"
709
865
  },
710
- children: [
711
- /* @__PURE__ */ jsx(
712
- m.circle,
866
+ children: isWavy ? /* @__PURE__ */ jsxs(Fragment, { children: [
867
+ shouldShowIndeterminateTrack && /* @__PURE__ */ jsx(
868
+ "path",
713
869
  {
714
- cx: center,
715
- cy: center,
716
- r: radius,
870
+ ref: indeterminateWavyTrackPathRef,
871
+ d: wavyActivePath != null ? wavyActivePath : "",
717
872
  fill: "none",
718
873
  stroke: bgTrackColor,
719
874
  strokeWidth: trackHeight,
720
- strokeLinecap: "round",
721
- animate: {
722
- pathLength: [
723
- Math.max(1e-3, 0.9 - 2 * gapForTrack),
724
- Math.max(1e-3, 0.25 - 2 * gapForTrack),
725
- Math.max(1e-3, 0.9 - 2 * gapForTrack)
726
- ],
727
- pathOffset: [
728
- 0.1 + gapForTrack,
729
- 0.75 + gapForTrack,
730
- 0.1 + gapForTrack
731
- ]
732
- },
733
- transition: {
734
- duration: 2 / Math.max(0.1, crawlerSpeed),
735
- repeat: Number.POSITIVE_INFINITY,
736
- ease: [0.4, 0, 0.2, 1]
737
- }
875
+ strokeLinecap: "round"
738
876
  }
739
877
  ),
740
- isWavy ? /* @__PURE__ */ jsx(
741
- ActivePathElem,
878
+ /* @__PURE__ */ jsx(
879
+ "path",
742
880
  {
881
+ ref: indeterminateWavyActivePathRef,
743
882
  d: wavyActivePath != null ? wavyActivePath : "",
744
883
  fill: "none",
745
884
  stroke: activeColor,
746
885
  strokeWidth: trackHeight,
747
- strokeLinecap: "round",
748
- animate: {
749
- pathLength: [0.1, 0.75, 0.1],
750
- pathOffset: [0, 0.65, 1]
751
- },
752
- transition: {
753
- duration: 2 / Math.max(0.1, crawlerSpeed),
754
- repeat: Number.POSITIVE_INFINITY,
755
- ease: [0.4, 0, 0.2, 1]
756
- }
886
+ strokeLinecap: "round"
757
887
  }
758
- ) : /* @__PURE__ */ jsx(
759
- ActiveCircleElem,
888
+ )
889
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
890
+ shouldShowIndeterminateTrack && /* @__PURE__ */ jsx(
891
+ "circle",
892
+ {
893
+ ref: indeterminateTrackRef,
894
+ cx: center,
895
+ cy: center,
896
+ r: radius,
897
+ fill: "none",
898
+ stroke: bgTrackColor,
899
+ strokeWidth: trackHeight,
900
+ strokeLinecap: "round"
901
+ }
902
+ ),
903
+ /* @__PURE__ */ jsx(
904
+ "circle",
760
905
  {
906
+ ref: indeterminateActiveRef,
761
907
  cx: center,
762
908
  cy: center,
763
909
  r: radius,
764
910
  fill: "none",
765
911
  stroke: activeColor,
766
912
  strokeWidth: trackHeight,
767
- strokeLinecap: "round",
768
- animate: {
769
- pathLength: [0.1, 0.75, 0.1],
770
- pathOffset: [0, 0.65, 1]
771
- },
772
- transition: {
773
- duration: 2 / Math.max(0.1, crawlerSpeed),
774
- repeat: Number.POSITIVE_INFINITY,
775
- ease: [0.4, 0, 0.2, 1]
776
- }
913
+ strokeLinecap: "round"
777
914
  }
778
915
  )
779
- ]
916
+ ] })
780
917
  }
781
918
  )
782
919
  ] })
@@ -982,7 +1119,7 @@ var WavyLinearTrack = React18.memo(function WavyLinearTrack2({
982
1119
  currentAmp
983
1120
  );
984
1121
  } else if (fraction === 0) {
985
- activePathD = `M ${capWidth} 0 L ${capWidth + 0.01} 0`;
1122
+ activePathD = "";
986
1123
  }
987
1124
  const trackStart = adjHead + totalGap;
988
1125
  if (trackStart < width - capWidth) {
@@ -1014,20 +1151,20 @@ var WavyLinearTrack = React18.memo(function WavyLinearTrack2({
1014
1151
  activeLines.push({ tail: l1T, head: l1H });
1015
1152
  activeLines.push({ tail: l2T, head: l2H });
1016
1153
  }
1017
- const segments = activeLines.map((line) => {
1018
- const barTail = line.tail * width;
1019
- const barHead = line.head * width;
1154
+ const activeSegments = activeLines.map((line) => {
1155
+ const rawTail = line.tail * width;
1156
+ const rawHead = line.head * width;
1020
1157
  const adjTail = Math.max(
1021
1158
  capWidth,
1022
- Math.min(width - capWidth, barTail)
1159
+ Math.min(width - capWidth, rawTail)
1023
1160
  );
1024
1161
  const adjHead = Math.max(
1025
1162
  capWidth,
1026
- Math.min(width - capWidth, barHead)
1163
+ Math.min(width - capWidth, rawHead)
1027
1164
  );
1028
1165
  return { adjTail, adjHead };
1029
1166
  }).filter((seg) => seg.adjHead - seg.adjTail > 0.1);
1030
- activePathD = segments.map(
1167
+ activePathD = activeSegments.map(
1031
1168
  (seg) => getSinePath(
1032
1169
  seg.adjTail,
1033
1170
  seg.adjHead,
@@ -1036,13 +1173,24 @@ var WavyLinearTrack = React18.memo(function WavyLinearTrack2({
1036
1173
  currentAmp
1037
1174
  )
1038
1175
  ).join(" ");
1176
+ const validActiveLines = activeLines.filter(
1177
+ (line) => line.head > line.tail && line.head * width > 0 && line.tail * width < width
1178
+ ).sort((a, b) => a.tail - b.tail);
1039
1179
  let currentTrackX = capWidth;
1040
- for (const seg of segments) {
1041
- const trackEnd = seg.adjTail - totalGap;
1180
+ for (const line of validActiveLines) {
1181
+ const blockStart = line.tail * width - totalGap;
1182
+ const blockEnd = line.head * width + totalGap;
1183
+ const trackEnd = Math.min(width - capWidth, blockStart);
1042
1184
  if (trackEnd > currentTrackX) {
1043
- trackD += `${getSinePath(currentTrackX, trackEnd, phase, activeWavelength, trackAmp)} `;
1185
+ trackD += `${getSinePath(
1186
+ currentTrackX,
1187
+ trackEnd,
1188
+ phase,
1189
+ activeWavelength,
1190
+ trackAmp
1191
+ )} `;
1044
1192
  }
1045
- currentTrackX = Math.max(currentTrackX, seg.adjHead + totalGap);
1193
+ currentTrackX = Math.max(currentTrackX, blockEnd);
1046
1194
  }
1047
1195
  if (currentTrackX < width - capWidth) {
1048
1196
  trackD += getSinePath(
@@ -1055,9 +1203,9 @@ var WavyLinearTrack = React18.memo(function WavyLinearTrack2({
1055
1203
  }
1056
1204
  }
1057
1205
  if (activePathRef.current)
1058
- activePathRef.current.setAttribute("d", activePathD);
1206
+ activePathRef.current.setAttribute("d", activePathD || "");
1059
1207
  if (trackPathRef.current)
1060
- trackPathRef.current.setAttribute("d", trackD.trim());
1208
+ trackPathRef.current.setAttribute("d", trackD.trim() || "");
1061
1209
  });
1062
1210
  return /* @__PURE__ */ jsx(
1063
1211
  "div",