@titan-design/react-ui 0.7.0 → 0.8.0
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/index.d.mts +109 -12
- package/dist/index.d.ts +109 -12
- package/dist/index.js +648 -231
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +648 -232
- package/dist/index.mjs.map +1 -1
- package/dist/{pages-8u_4WbL-.d.mts → pages-DCFBqp79.d.mts} +16 -7
- package/dist/{pages-DaWiBOGa.d.ts → pages-_fI3-x7Z.d.ts} +16 -7
- package/dist/pages.d.mts +1 -1
- package/dist/pages.d.ts +1 -1
- package/dist/pages.js +495 -92
- package/dist/pages.js.map +1 -1
- package/dist/pages.mjs +496 -93
- package/dist/pages.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/custom/CircularTimer/CircularTimer.stories.tsx +90 -0
- package/src/components/custom/CircularTimer/CircularTimer.test.tsx +62 -0
- package/src/components/custom/CircularTimer/CircularTimer.tsx +112 -0
- package/src/components/custom/CircularTimer/index.ts +1 -0
- package/src/components/custom/Workout/FatigueMeter.stories.tsx +2 -2
- package/src/components/custom/Workout/FatigueMeter.test.tsx +12 -0
- package/src/components/custom/Workout/FatigueMeter.tsx +7 -3
- package/src/components/custom/Workout/README.md +47 -0
- package/src/components/custom/Workout/RestTimer.stories.tsx +132 -2
- package/src/components/custom/Workout/RestTimer.test.tsx +73 -0
- package/src/components/custom/Workout/RestTimer.tsx +141 -50
- package/src/components/custom/Workout/TempoBar.stories.tsx +69 -0
- package/src/components/custom/Workout/TempoBar.test.tsx +119 -4
- package/src/components/custom/Workout/TempoBar.tsx +107 -22
- package/src/components/custom/Workout/VelocityStrip.stories.tsx +152 -9
- package/src/components/custom/Workout/VelocityStrip.test.tsx +64 -0
- package/src/components/custom/Workout/VelocityStrip.tsx +287 -40
- package/src/components/custom/Workout/ZoneTrack.stories.tsx +2 -3
- package/src/components/custom/Workout/ZoneTrack.test.tsx +34 -0
- package/src/components/custom/Workout/ZoneTrack.tsx +76 -18
- package/src/components/custom/index.ts +1 -0
- package/src/components/ui/alert/Alert.stories.tsx +85 -2
- package/src/components/ui/alert/Alert.test.tsx +52 -0
- package/src/components/ui/alert/Alert.tsx +54 -20
- package/src/components/ui/progress/Progress.tsx +47 -22
package/dist/pages.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useState, useMemo, useEffect, Fragment as Fragment$1, useSyncExternalStore, useCallback } from 'react';
|
|
2
|
-
import { Easing, Platform, View, Text,
|
|
2
|
+
import { Easing, Platform, View, Text, TextInput, Pressable, Animated, ScrollView } from 'react-native';
|
|
3
3
|
import { Fragment, jsx as jsx$1, jsxs as jsxs$1 } from 'react/jsx-runtime';
|
|
4
4
|
import { clsx } from 'clsx';
|
|
5
5
|
import { twMerge } from 'tailwind-merge';
|
|
@@ -33,6 +33,8 @@ var primitiveColors = {
|
|
|
33
33
|
900: "#111827"},
|
|
34
34
|
// Charcoal scale (dark backgrounds)
|
|
35
35
|
charcoal: {
|
|
36
|
+
0: "#6E6E6E",
|
|
37
|
+
100: "#4C4C4C",
|
|
36
38
|
200: "#3C3C3C",
|
|
37
39
|
300: "#2C2C2C",
|
|
38
40
|
400: "#1F1F1F",
|
|
@@ -604,6 +606,14 @@ var EXPANDED_ENCODED_PCT = 45;
|
|
|
604
606
|
var EXPANDED_HEIGHT = 60;
|
|
605
607
|
var FIXED_MAX_VELOCITY = 1.15;
|
|
606
608
|
var BARE_STUB_HEIGHT = 3;
|
|
609
|
+
var HERO_HEIGHT = 220;
|
|
610
|
+
var HERO_LABEL_HEADROOM = 20;
|
|
611
|
+
var HERO_BAR_GAP = 8;
|
|
612
|
+
var HERO_BAR_MAX_WIDTH = 52;
|
|
613
|
+
var HERO_BAR_RADIUS = 5;
|
|
614
|
+
var HERO_MIN_BAR_HEIGHT = 4;
|
|
615
|
+
var HERO_PENDING_COLOR = primitiveColors.charcoal[100];
|
|
616
|
+
var HERO_REFERENCE_COLOR = primitiveColors.charcoal[0];
|
|
607
617
|
function scaleDenominator(scale, maxVelocity) {
|
|
608
618
|
return scale === "fixed" ? FIXED_MAX_VELOCITY : maxVelocity * 1.15;
|
|
609
619
|
}
|
|
@@ -631,6 +641,183 @@ function usePrefersReducedMotion() {
|
|
|
631
641
|
var ANIMATION_DURATION = 400;
|
|
632
642
|
var ANIMATION_EASING = Easing.bezier(0.22, 1, 0.36, 1);
|
|
633
643
|
var POP_EASING = Easing.bezier(0.34, 1.56, 0.64, 1);
|
|
644
|
+
function useLiveRepPop(active, liveRepIndex, liveVelocity, isNewPeak) {
|
|
645
|
+
const prefersReducedMotion = usePrefersReducedMotion();
|
|
646
|
+
const [liveScale] = useState(() => new Animated.Value(1));
|
|
647
|
+
useEffect(() => {
|
|
648
|
+
if (!active || liveRepIndex == null || liveVelocity == null) return;
|
|
649
|
+
if (prefersReducedMotion) {
|
|
650
|
+
liveScale.setValue(1);
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
if (isNewPeak) {
|
|
654
|
+
liveScale.setValue(1);
|
|
655
|
+
Animated.sequence([
|
|
656
|
+
Animated.timing(liveScale, {
|
|
657
|
+
toValue: 1.25,
|
|
658
|
+
duration: 150,
|
|
659
|
+
easing: Easing.out(Easing.quad),
|
|
660
|
+
useNativeDriver: true
|
|
661
|
+
}),
|
|
662
|
+
Animated.spring(liveScale, {
|
|
663
|
+
toValue: 1,
|
|
664
|
+
friction: 3,
|
|
665
|
+
tension: 140,
|
|
666
|
+
useNativeDriver: true
|
|
667
|
+
})
|
|
668
|
+
]).start();
|
|
669
|
+
} else {
|
|
670
|
+
liveScale.setValue(0.8);
|
|
671
|
+
Animated.timing(liveScale, {
|
|
672
|
+
toValue: 1,
|
|
673
|
+
duration: 300,
|
|
674
|
+
easing: POP_EASING,
|
|
675
|
+
useNativeDriver: true
|
|
676
|
+
}).start();
|
|
677
|
+
}
|
|
678
|
+
}, [active, liveRepIndex, liveVelocity, isNewPeak, prefersReducedMotion, liveScale]);
|
|
679
|
+
return liveScale;
|
|
680
|
+
}
|
|
681
|
+
function HeroVelocityChart({
|
|
682
|
+
doneVelocities,
|
|
683
|
+
barColorFor,
|
|
684
|
+
scaleDenom,
|
|
685
|
+
referenceVelocity,
|
|
686
|
+
liveRepIndex,
|
|
687
|
+
isNewPeak,
|
|
688
|
+
targetReps,
|
|
689
|
+
height,
|
|
690
|
+
className,
|
|
691
|
+
viewProps
|
|
692
|
+
}) {
|
|
693
|
+
const liveVelocity = liveRepIndex != null ? doneVelocities[liveRepIndex] : void 0;
|
|
694
|
+
const liveScale = useLiveRepPop(true, liveRepIndex, liveVelocity, isNewPeak);
|
|
695
|
+
const plotHeight = Math.max(0, height - HERO_LABEL_HEADROOM);
|
|
696
|
+
const barHeight = (velocity) => scaleDenom > 0 ? Math.max(HERO_MIN_BAR_HEIGHT, Math.min(1, velocity / scaleDenom) * plotHeight) : HERO_MIN_BAR_HEIGHT;
|
|
697
|
+
const pendingCount = Math.max(0, (targetReps ?? 0) - doneVelocities.length);
|
|
698
|
+
const referencePx = referenceVelocity > 0 && scaleDenom > 0 ? Math.min(plotHeight, referenceVelocity / scaleDenom * plotHeight) : 0;
|
|
699
|
+
const repCount = doneVelocities.length;
|
|
700
|
+
const total = Math.max(repCount, targetReps ?? repCount);
|
|
701
|
+
const label = referenceVelocity > 0 ? `Velocity chart, ${repCount} of ${total} reps, best ${formatVelocity(referenceVelocity)} meters per second` : `Velocity chart, ${repCount} of ${total} reps`;
|
|
702
|
+
const { style: externalStyle, ...restProps } = viewProps;
|
|
703
|
+
return /* @__PURE__ */ jsx(
|
|
704
|
+
View,
|
|
705
|
+
{
|
|
706
|
+
className,
|
|
707
|
+
style: [{ height }, externalStyle],
|
|
708
|
+
accessibilityRole: "image",
|
|
709
|
+
accessibilityLabel: label,
|
|
710
|
+
testID: "velocity-strip-hero",
|
|
711
|
+
...restProps,
|
|
712
|
+
children: /* @__PURE__ */ jsxs(
|
|
713
|
+
View,
|
|
714
|
+
{
|
|
715
|
+
style: {
|
|
716
|
+
flex: 1,
|
|
717
|
+
flexDirection: "row",
|
|
718
|
+
alignItems: "flex-end",
|
|
719
|
+
gap: HERO_BAR_GAP,
|
|
720
|
+
position: "relative",
|
|
721
|
+
borderBottomWidth: 2,
|
|
722
|
+
borderBottomColor: HERO_PENDING_COLOR
|
|
723
|
+
},
|
|
724
|
+
children: [
|
|
725
|
+
referencePx > 0 && /* @__PURE__ */ jsx(
|
|
726
|
+
View,
|
|
727
|
+
{
|
|
728
|
+
accessibilityElementsHidden: true,
|
|
729
|
+
style: {
|
|
730
|
+
position: "absolute",
|
|
731
|
+
left: 0,
|
|
732
|
+
right: 0,
|
|
733
|
+
bottom: referencePx,
|
|
734
|
+
borderTopWidth: 1,
|
|
735
|
+
borderStyle: "dashed",
|
|
736
|
+
borderColor: HERO_REFERENCE_COLOR,
|
|
737
|
+
pointerEvents: "none"
|
|
738
|
+
},
|
|
739
|
+
testID: "velocity-hero-reference"
|
|
740
|
+
}
|
|
741
|
+
),
|
|
742
|
+
doneVelocities.map((velocity, i) => {
|
|
743
|
+
const isLive = liveRepIndex === i;
|
|
744
|
+
return /* @__PURE__ */ jsxs(
|
|
745
|
+
View,
|
|
746
|
+
{
|
|
747
|
+
accessibilityElementsHidden: true,
|
|
748
|
+
style: {
|
|
749
|
+
flex: 1,
|
|
750
|
+
maxWidth: HERO_BAR_MAX_WIDTH,
|
|
751
|
+
height: "100%",
|
|
752
|
+
alignItems: "center",
|
|
753
|
+
justifyContent: "flex-end"
|
|
754
|
+
},
|
|
755
|
+
children: [
|
|
756
|
+
/* @__PURE__ */ jsx(
|
|
757
|
+
Text,
|
|
758
|
+
{
|
|
759
|
+
className: "text-text-primary",
|
|
760
|
+
style: { fontSize: 12, fontWeight: "800", marginBottom: 4 },
|
|
761
|
+
testID: `velocity-label-${i}`,
|
|
762
|
+
children: formatVelocity(velocity)
|
|
763
|
+
}
|
|
764
|
+
),
|
|
765
|
+
/* @__PURE__ */ jsx(
|
|
766
|
+
Animated.View,
|
|
767
|
+
{
|
|
768
|
+
style: [
|
|
769
|
+
{
|
|
770
|
+
width: "100%",
|
|
771
|
+
height: barHeight(velocity),
|
|
772
|
+
borderTopLeftRadius: HERO_BAR_RADIUS,
|
|
773
|
+
borderTopRightRadius: HERO_BAR_RADIUS,
|
|
774
|
+
backgroundColor: barColorFor(velocity)
|
|
775
|
+
},
|
|
776
|
+
isLive ? { transform: [{ scale: liveScale }] } : null
|
|
777
|
+
],
|
|
778
|
+
testID: `velocity-bar-${i}`
|
|
779
|
+
}
|
|
780
|
+
)
|
|
781
|
+
]
|
|
782
|
+
},
|
|
783
|
+
i
|
|
784
|
+
);
|
|
785
|
+
}),
|
|
786
|
+
Array.from({ length: pendingCount }, (_, i) => /* @__PURE__ */ jsx(
|
|
787
|
+
View,
|
|
788
|
+
{
|
|
789
|
+
accessibilityElementsHidden: true,
|
|
790
|
+
style: {
|
|
791
|
+
flex: 1,
|
|
792
|
+
maxWidth: HERO_BAR_MAX_WIDTH,
|
|
793
|
+
height: "100%",
|
|
794
|
+
alignItems: "center",
|
|
795
|
+
justifyContent: "flex-end"
|
|
796
|
+
},
|
|
797
|
+
children: /* @__PURE__ */ jsx(
|
|
798
|
+
View,
|
|
799
|
+
{
|
|
800
|
+
style: {
|
|
801
|
+
width: "100%",
|
|
802
|
+
height: HERO_MIN_BAR_HEIGHT * 3,
|
|
803
|
+
borderWidth: 1,
|
|
804
|
+
borderStyle: "dashed",
|
|
805
|
+
borderColor: HERO_PENDING_COLOR,
|
|
806
|
+
borderTopLeftRadius: HERO_BAR_RADIUS,
|
|
807
|
+
borderTopRightRadius: HERO_BAR_RADIUS
|
|
808
|
+
},
|
|
809
|
+
testID: "velocity-slot-todo"
|
|
810
|
+
}
|
|
811
|
+
)
|
|
812
|
+
},
|
|
813
|
+
`pending-${i}`
|
|
814
|
+
))
|
|
815
|
+
]
|
|
816
|
+
}
|
|
817
|
+
)
|
|
818
|
+
}
|
|
819
|
+
);
|
|
820
|
+
}
|
|
634
821
|
function VelocityStrip({
|
|
635
822
|
velocities,
|
|
636
823
|
set,
|
|
@@ -641,6 +828,7 @@ function VelocityStrip({
|
|
|
641
828
|
onRepPress,
|
|
642
829
|
variant = "expanded",
|
|
643
830
|
height = EXPANDED_HEIGHT,
|
|
831
|
+
targetReps,
|
|
644
832
|
scale = "peak",
|
|
645
833
|
showNumbers = true,
|
|
646
834
|
showInfo = true,
|
|
@@ -666,45 +854,17 @@ function VelocityStrip({
|
|
|
666
854
|
return SET_STRIP_VARIABLE_COLOR;
|
|
667
855
|
};
|
|
668
856
|
const meanZone = hasZones ? classifyBand(meanVelocity, zones)?.label ?? "" : getVelocityZoneName(meanVelocity);
|
|
669
|
-
const prefersReducedMotion = usePrefersReducedMotion();
|
|
670
857
|
const [heightAnim] = useState(() => new Animated.Value(expanded ? height : 3));
|
|
671
858
|
const [labelOpacity] = useState(() => new Animated.Value(expanded ? 1 : 0));
|
|
672
859
|
const [infoOpacity] = useState(() => new Animated.Value(expanded ? 1 : 0));
|
|
673
|
-
const [liveScale] = useState(() => new Animated.Value(1));
|
|
674
860
|
const liveVelocity = liveRepIndex != null ? doneVelocities[liveRepIndex] : void 0;
|
|
675
861
|
const isNewPeak = liveVelocity != null && maxVelocity > 0 && liveVelocity === maxVelocity;
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
if (isNewPeak) {
|
|
683
|
-
liveScale.setValue(1);
|
|
684
|
-
Animated.sequence([
|
|
685
|
-
Animated.timing(liveScale, {
|
|
686
|
-
toValue: 1.25,
|
|
687
|
-
duration: 150,
|
|
688
|
-
easing: Easing.out(Easing.quad),
|
|
689
|
-
useNativeDriver: true
|
|
690
|
-
}),
|
|
691
|
-
Animated.spring(liveScale, {
|
|
692
|
-
toValue: 1,
|
|
693
|
-
friction: 3,
|
|
694
|
-
tension: 140,
|
|
695
|
-
useNativeDriver: true
|
|
696
|
-
})
|
|
697
|
-
]).start();
|
|
698
|
-
} else {
|
|
699
|
-
liveScale.setValue(0.8);
|
|
700
|
-
Animated.timing(liveScale, {
|
|
701
|
-
toValue: 1,
|
|
702
|
-
duration: 300,
|
|
703
|
-
easing: POP_EASING,
|
|
704
|
-
useNativeDriver: true
|
|
705
|
-
}).start();
|
|
706
|
-
}
|
|
707
|
-
}, [variant, framed, liveRepIndex, liveVelocity, isNewPeak, prefersReducedMotion, liveScale]);
|
|
862
|
+
const liveScale = useLiveRepPop(
|
|
863
|
+
variant === "expanded" && framed,
|
|
864
|
+
liveRepIndex,
|
|
865
|
+
liveVelocity,
|
|
866
|
+
isNewPeak
|
|
867
|
+
);
|
|
708
868
|
useEffect(() => {
|
|
709
869
|
if (variant !== "expanded" || !framed) return;
|
|
710
870
|
Animated.parallel([
|
|
@@ -731,6 +891,24 @@ function VelocityStrip({
|
|
|
731
891
|
if (set == null && velocities == null) return null;
|
|
732
892
|
const repCount = doneVelocities.length;
|
|
733
893
|
const miniLabel = set ? setAccessibilityLabel(set, repCount) : `Velocity strip, ${repCount} reps`;
|
|
894
|
+
if (variant === "hero") {
|
|
895
|
+
const heroHeight = height === EXPANDED_HEIGHT ? HERO_HEIGHT : height;
|
|
896
|
+
return /* @__PURE__ */ jsx(
|
|
897
|
+
HeroVelocityChart,
|
|
898
|
+
{
|
|
899
|
+
doneVelocities,
|
|
900
|
+
barColorFor,
|
|
901
|
+
scaleDenom,
|
|
902
|
+
referenceVelocity: maxVelocity,
|
|
903
|
+
liveRepIndex,
|
|
904
|
+
isNewPeak,
|
|
905
|
+
targetReps,
|
|
906
|
+
height: heroHeight,
|
|
907
|
+
className,
|
|
908
|
+
viewProps: props
|
|
909
|
+
}
|
|
910
|
+
);
|
|
911
|
+
}
|
|
734
912
|
if (variant === "mini") {
|
|
735
913
|
const { style: externalStyle, ...restProps } = props;
|
|
736
914
|
return /* @__PURE__ */ jsx(
|
|
@@ -2991,9 +3169,270 @@ function useTimer(options = {}) {
|
|
|
2991
3169
|
const done = durationMs != null && elapsedMs >= durationMs;
|
|
2992
3170
|
return { elapsedMs, remainingMs, progress, label, done };
|
|
2993
3171
|
}
|
|
3172
|
+
var colorVarMap = {
|
|
3173
|
+
primary: "var(--color-brand-primary)",
|
|
3174
|
+
secondary: "var(--color-brand-secondary)",
|
|
3175
|
+
success: "var(--color-status-success)",
|
|
3176
|
+
error: "var(--color-status-error)",
|
|
3177
|
+
warning: "var(--color-status-warning)",
|
|
3178
|
+
info: "var(--color-status-info)"
|
|
3179
|
+
};
|
|
3180
|
+
function CircularProgress({
|
|
3181
|
+
value = 0,
|
|
3182
|
+
max = 100,
|
|
3183
|
+
isIndeterminate = false,
|
|
3184
|
+
size = 48,
|
|
3185
|
+
strokeWidth = 4,
|
|
3186
|
+
color = "primary",
|
|
3187
|
+
showValue = false,
|
|
3188
|
+
formatValue: formatValue2 = (v, m) => `${Math.round(v / m * 100)}%`,
|
|
3189
|
+
children,
|
|
3190
|
+
fill = false,
|
|
3191
|
+
className,
|
|
3192
|
+
...props
|
|
3193
|
+
}) {
|
|
3194
|
+
const percentage = Math.min(Math.max(value / max * 100, 0), 100);
|
|
3195
|
+
const radius = (size - strokeWidth) / 2;
|
|
3196
|
+
const circumference = 2 * Math.PI * radius;
|
|
3197
|
+
const strokeDashoffset = circumference - percentage / 100 * circumference;
|
|
3198
|
+
const center = size / 2;
|
|
3199
|
+
const boxStyle = fill ? { width: "100%", aspectRatio: 1 } : { width: size, height: size };
|
|
3200
|
+
return /* @__PURE__ */ jsxs(
|
|
3201
|
+
View,
|
|
3202
|
+
{
|
|
3203
|
+
className: cn("items-center justify-center", className),
|
|
3204
|
+
style: boxStyle,
|
|
3205
|
+
accessibilityRole: "progressbar",
|
|
3206
|
+
accessibilityValue: {
|
|
3207
|
+
min: 0,
|
|
3208
|
+
max,
|
|
3209
|
+
now: isIndeterminate ? void 0 : value
|
|
3210
|
+
},
|
|
3211
|
+
...props,
|
|
3212
|
+
children: [
|
|
3213
|
+
/* @__PURE__ */ jsx(View, { className: cn(isIndeterminate && "animate-spin"), style: boxStyle, children: /* @__PURE__ */ jsxs(
|
|
3214
|
+
"svg",
|
|
3215
|
+
{
|
|
3216
|
+
width: "100%",
|
|
3217
|
+
height: "100%",
|
|
3218
|
+
viewBox: `0 0 ${size} ${size}`,
|
|
3219
|
+
style: { position: "absolute" },
|
|
3220
|
+
children: [
|
|
3221
|
+
/* @__PURE__ */ jsx(
|
|
3222
|
+
"circle",
|
|
3223
|
+
{
|
|
3224
|
+
cx: center,
|
|
3225
|
+
cy: center,
|
|
3226
|
+
r: radius,
|
|
3227
|
+
fill: "none",
|
|
3228
|
+
stroke: "var(--color-border-default)",
|
|
3229
|
+
strokeWidth
|
|
3230
|
+
}
|
|
3231
|
+
),
|
|
3232
|
+
/* @__PURE__ */ jsx(
|
|
3233
|
+
"circle",
|
|
3234
|
+
{
|
|
3235
|
+
cx: center,
|
|
3236
|
+
cy: center,
|
|
3237
|
+
r: radius,
|
|
3238
|
+
fill: "none",
|
|
3239
|
+
stroke: colorVarMap[color],
|
|
3240
|
+
strokeWidth,
|
|
3241
|
+
strokeLinecap: "round",
|
|
3242
|
+
strokeDasharray: circumference,
|
|
3243
|
+
strokeDashoffset: isIndeterminate ? circumference * 0.75 : strokeDashoffset,
|
|
3244
|
+
transform: `rotate(-90 ${center} ${center})`,
|
|
3245
|
+
style: { transition: "stroke-dashoffset 300ms cubic-bezier(0.22, 1, 0.36, 1)" }
|
|
3246
|
+
}
|
|
3247
|
+
)
|
|
3248
|
+
]
|
|
3249
|
+
}
|
|
3250
|
+
) }),
|
|
3251
|
+
children != null ? /* @__PURE__ */ jsx(
|
|
3252
|
+
View,
|
|
3253
|
+
{
|
|
3254
|
+
style: {
|
|
3255
|
+
position: "absolute",
|
|
3256
|
+
alignItems: "center",
|
|
3257
|
+
justifyContent: "center",
|
|
3258
|
+
pointerEvents: "none"
|
|
3259
|
+
},
|
|
3260
|
+
children
|
|
3261
|
+
}
|
|
3262
|
+
) : showValue && !isIndeterminate && /* @__PURE__ */ jsx(
|
|
3263
|
+
Text,
|
|
3264
|
+
{
|
|
3265
|
+
className: "text-xs font-semibold text-text-primary absolute",
|
|
3266
|
+
style: { fontSize: Math.max(size * 0.22, 10) },
|
|
3267
|
+
children: formatValue2(value, max)
|
|
3268
|
+
}
|
|
3269
|
+
)
|
|
3270
|
+
]
|
|
3271
|
+
}
|
|
3272
|
+
);
|
|
3273
|
+
}
|
|
3274
|
+
|
|
3275
|
+
// src/components/custom/CircularTimer/CircularTimer.tsx
|
|
3276
|
+
function CircularTimer({
|
|
3277
|
+
durationMs,
|
|
3278
|
+
elapsedMs,
|
|
3279
|
+
mode = "down",
|
|
3280
|
+
size = 160,
|
|
3281
|
+
strokeWidth = 8,
|
|
3282
|
+
fill = false,
|
|
3283
|
+
color = "primary",
|
|
3284
|
+
doneLabel,
|
|
3285
|
+
doneColor = "success",
|
|
3286
|
+
controls,
|
|
3287
|
+
accessibilityLabel: accessibilityLabel2,
|
|
3288
|
+
testID
|
|
3289
|
+
}) {
|
|
3290
|
+
const { remainingMs, progress, label, done } = useTimer({ mode, durationMs, elapsedMs });
|
|
3291
|
+
const isDone = done && mode === "down";
|
|
3292
|
+
const showDone = isDone && doneLabel != null;
|
|
3293
|
+
const remainingFrac = durationMs > 0 ? Math.max(0, Math.min(1, remainingMs / durationMs)) : 0;
|
|
3294
|
+
const arc = mode === "down" ? showDone ? 1 : remainingFrac : progress;
|
|
3295
|
+
const ringColor = showDone ? doneColor : color;
|
|
3296
|
+
const remainingSec = Math.ceil(Math.max(0, remainingMs) / 1e3);
|
|
3297
|
+
const a11y = accessibilityLabel2 ?? (isDone ? "Timer complete" : `${remainingSec} seconds remaining`);
|
|
3298
|
+
return /* @__PURE__ */ jsxs(
|
|
3299
|
+
View,
|
|
3300
|
+
{
|
|
3301
|
+
style: { alignItems: "center", gap: 16 },
|
|
3302
|
+
accessibilityRole: "timer",
|
|
3303
|
+
accessibilityLabel: a11y,
|
|
3304
|
+
testID: testID ?? "circular-timer",
|
|
3305
|
+
children: [
|
|
3306
|
+
/* @__PURE__ */ jsx(
|
|
3307
|
+
CircularProgress,
|
|
3308
|
+
{
|
|
3309
|
+
value: arc,
|
|
3310
|
+
max: 1,
|
|
3311
|
+
size,
|
|
3312
|
+
strokeWidth,
|
|
3313
|
+
fill,
|
|
3314
|
+
color: ringColor,
|
|
3315
|
+
accessibilityLabel: "Timer ring",
|
|
3316
|
+
testID: "circular-timer-ring",
|
|
3317
|
+
children: /* @__PURE__ */ jsx(
|
|
3318
|
+
Text,
|
|
3319
|
+
{
|
|
3320
|
+
className: showDone ? void 0 : "text-text-primary",
|
|
3321
|
+
style: {
|
|
3322
|
+
fontSize: Math.round(size * (showDone ? 0.28 : 0.24)),
|
|
3323
|
+
fontFamily: '"Space Grotesk", sans-serif',
|
|
3324
|
+
fontWeight: "800",
|
|
3325
|
+
fontVariant: ["tabular-nums"],
|
|
3326
|
+
letterSpacing: showDone ? 1 : -1,
|
|
3327
|
+
...showDone ? { color: colorVarMap[doneColor] } : null
|
|
3328
|
+
},
|
|
3329
|
+
testID: "circular-timer-label",
|
|
3330
|
+
children: showDone ? doneLabel : label
|
|
3331
|
+
}
|
|
3332
|
+
)
|
|
3333
|
+
}
|
|
3334
|
+
),
|
|
3335
|
+
controls
|
|
3336
|
+
]
|
|
3337
|
+
}
|
|
3338
|
+
);
|
|
3339
|
+
}
|
|
2994
3340
|
|
|
2995
3341
|
// src/components/custom/Workout/RestTimer.tsx
|
|
2996
3342
|
var BRAND_PRIMARY3 = getSemanticColors("dark")["brand-primary"];
|
|
3343
|
+
var RING_DEFAULT_SIZE = 180;
|
|
3344
|
+
function RestActions({ onAddTime, onSkip }) {
|
|
3345
|
+
return /* @__PURE__ */ jsxs(View, { style: { flexDirection: "row", gap: 8 }, children: [
|
|
3346
|
+
/* @__PURE__ */ jsx(
|
|
3347
|
+
Pressable,
|
|
3348
|
+
{
|
|
3349
|
+
onPress: onAddTime,
|
|
3350
|
+
style: {
|
|
3351
|
+
backgroundColor: "rgba(255,255,255,0.06)",
|
|
3352
|
+
paddingVertical: 8,
|
|
3353
|
+
paddingHorizontal: 20,
|
|
3354
|
+
borderRadius: 8
|
|
3355
|
+
},
|
|
3356
|
+
accessibilityRole: "button",
|
|
3357
|
+
accessibilityLabel: "Add 30 seconds",
|
|
3358
|
+
testID: "rest-timer-add-time",
|
|
3359
|
+
children: /* @__PURE__ */ jsx(
|
|
3360
|
+
Text,
|
|
3361
|
+
{
|
|
3362
|
+
className: "text-text-secondary",
|
|
3363
|
+
style: { fontSize: 11, fontFamily: "Inter, sans-serif", fontWeight: "600" },
|
|
3364
|
+
children: "+30s"
|
|
3365
|
+
}
|
|
3366
|
+
)
|
|
3367
|
+
}
|
|
3368
|
+
),
|
|
3369
|
+
/* @__PURE__ */ jsx(
|
|
3370
|
+
Pressable,
|
|
3371
|
+
{
|
|
3372
|
+
onPress: onSkip,
|
|
3373
|
+
style: {
|
|
3374
|
+
backgroundColor: "rgba(255,121,0,0.12)",
|
|
3375
|
+
paddingVertical: 8,
|
|
3376
|
+
paddingHorizontal: 20,
|
|
3377
|
+
borderRadius: 8
|
|
3378
|
+
},
|
|
3379
|
+
accessibilityRole: "button",
|
|
3380
|
+
accessibilityLabel: "Skip rest",
|
|
3381
|
+
testID: "rest-timer-skip",
|
|
3382
|
+
children: /* @__PURE__ */ jsx(
|
|
3383
|
+
Text,
|
|
3384
|
+
{
|
|
3385
|
+
style: {
|
|
3386
|
+
fontSize: 11,
|
|
3387
|
+
fontFamily: "Inter, sans-serif",
|
|
3388
|
+
fontWeight: "600",
|
|
3389
|
+
color: BRAND_PRIMARY3
|
|
3390
|
+
},
|
|
3391
|
+
children: "Skip"
|
|
3392
|
+
}
|
|
3393
|
+
)
|
|
3394
|
+
}
|
|
3395
|
+
)
|
|
3396
|
+
] });
|
|
3397
|
+
}
|
|
3398
|
+
function RestTimerRing({
|
|
3399
|
+
totalSeconds,
|
|
3400
|
+
elapsedMs,
|
|
3401
|
+
remainingSec,
|
|
3402
|
+
done,
|
|
3403
|
+
size,
|
|
3404
|
+
nextSetInfo,
|
|
3405
|
+
displayOnly,
|
|
3406
|
+
onSkip,
|
|
3407
|
+
onAddTime
|
|
3408
|
+
}) {
|
|
3409
|
+
const a11yLabel = done ? "Rest complete, next set ready" : `Rest timer, ${Math.max(0, remainingSec)} seconds remaining`;
|
|
3410
|
+
return /* @__PURE__ */ jsxs(View, { style: { alignItems: "center", gap: 16 }, testID: "rest-timer", children: [
|
|
3411
|
+
/* @__PURE__ */ jsx(
|
|
3412
|
+
CircularTimer,
|
|
3413
|
+
{
|
|
3414
|
+
durationMs: totalSeconds * 1e3,
|
|
3415
|
+
elapsedMs,
|
|
3416
|
+
mode: "down",
|
|
3417
|
+
size,
|
|
3418
|
+
doneLabel: "GO",
|
|
3419
|
+
doneColor: "success",
|
|
3420
|
+
accessibilityLabel: a11yLabel,
|
|
3421
|
+
controls: !displayOnly ? /* @__PURE__ */ jsx(RestActions, { onAddTime, onSkip }) : void 0,
|
|
3422
|
+
testID: "rest-timer-ring"
|
|
3423
|
+
}
|
|
3424
|
+
),
|
|
3425
|
+
nextSetInfo != null && /* @__PURE__ */ jsx(
|
|
3426
|
+
Text,
|
|
3427
|
+
{
|
|
3428
|
+
className: "text-text-tertiary",
|
|
3429
|
+
style: { fontSize: 13, fontFamily: "Inter, sans-serif" },
|
|
3430
|
+
testID: "rest-timer-next-set",
|
|
3431
|
+
children: nextSetInfo
|
|
3432
|
+
}
|
|
3433
|
+
)
|
|
3434
|
+
] });
|
|
3435
|
+
}
|
|
2997
3436
|
function RestTimer({
|
|
2998
3437
|
totalSeconds,
|
|
2999
3438
|
elapsedMs,
|
|
@@ -3001,12 +3440,15 @@ function RestTimer({
|
|
|
3001
3440
|
onAddTime,
|
|
3002
3441
|
nextSetInfo,
|
|
3003
3442
|
visible,
|
|
3004
|
-
displayOnly = false
|
|
3443
|
+
displayOnly = false,
|
|
3444
|
+
variant = "bar",
|
|
3445
|
+
size = RING_DEFAULT_SIZE
|
|
3005
3446
|
}) {
|
|
3006
3447
|
const {
|
|
3007
3448
|
remainingMs,
|
|
3008
3449
|
progress,
|
|
3009
|
-
label: timeDisplay
|
|
3450
|
+
label: timeDisplay,
|
|
3451
|
+
done
|
|
3010
3452
|
} = useTimer({
|
|
3011
3453
|
mode: "down",
|
|
3012
3454
|
durationMs: totalSeconds * 1e3,
|
|
@@ -3015,6 +3457,22 @@ function RestTimer({
|
|
|
3015
3457
|
const remainingSec = Math.ceil(remainingMs / 1e3);
|
|
3016
3458
|
const progressPct = progress * 100;
|
|
3017
3459
|
if (!visible) return null;
|
|
3460
|
+
if (variant === "ring") {
|
|
3461
|
+
return /* @__PURE__ */ jsx(
|
|
3462
|
+
RestTimerRing,
|
|
3463
|
+
{
|
|
3464
|
+
totalSeconds,
|
|
3465
|
+
elapsedMs,
|
|
3466
|
+
remainingSec,
|
|
3467
|
+
done,
|
|
3468
|
+
size,
|
|
3469
|
+
nextSetInfo,
|
|
3470
|
+
displayOnly,
|
|
3471
|
+
onSkip,
|
|
3472
|
+
onAddTime
|
|
3473
|
+
}
|
|
3474
|
+
);
|
|
3475
|
+
}
|
|
3018
3476
|
return /* @__PURE__ */ jsxs(
|
|
3019
3477
|
View,
|
|
3020
3478
|
{
|
|
@@ -3112,62 +3570,7 @@ function RestTimer({
|
|
|
3112
3570
|
)
|
|
3113
3571
|
}
|
|
3114
3572
|
),
|
|
3115
|
-
!displayOnly && /* @__PURE__ */
|
|
3116
|
-
/* @__PURE__ */ jsx(
|
|
3117
|
-
Pressable,
|
|
3118
|
-
{
|
|
3119
|
-
onPress: onAddTime,
|
|
3120
|
-
style: {
|
|
3121
|
-
backgroundColor: "rgba(255,255,255,0.06)",
|
|
3122
|
-
paddingVertical: 8,
|
|
3123
|
-
paddingHorizontal: 20,
|
|
3124
|
-
borderRadius: 8
|
|
3125
|
-
},
|
|
3126
|
-
accessibilityRole: "button",
|
|
3127
|
-
accessibilityLabel: "Add 30 seconds",
|
|
3128
|
-
testID: "rest-timer-add-time",
|
|
3129
|
-
children: /* @__PURE__ */ jsx(
|
|
3130
|
-
Text,
|
|
3131
|
-
{
|
|
3132
|
-
className: "text-text-secondary",
|
|
3133
|
-
style: {
|
|
3134
|
-
fontSize: 11,
|
|
3135
|
-
fontFamily: "Inter, sans-serif",
|
|
3136
|
-
fontWeight: "600"
|
|
3137
|
-
},
|
|
3138
|
-
children: "+30s"
|
|
3139
|
-
}
|
|
3140
|
-
)
|
|
3141
|
-
}
|
|
3142
|
-
),
|
|
3143
|
-
/* @__PURE__ */ jsx(
|
|
3144
|
-
Pressable,
|
|
3145
|
-
{
|
|
3146
|
-
onPress: onSkip,
|
|
3147
|
-
style: {
|
|
3148
|
-
backgroundColor: "rgba(255,121,0,0.12)",
|
|
3149
|
-
paddingVertical: 8,
|
|
3150
|
-
paddingHorizontal: 20,
|
|
3151
|
-
borderRadius: 8
|
|
3152
|
-
},
|
|
3153
|
-
accessibilityRole: "button",
|
|
3154
|
-
accessibilityLabel: "Skip rest",
|
|
3155
|
-
testID: "rest-timer-skip",
|
|
3156
|
-
children: /* @__PURE__ */ jsx(
|
|
3157
|
-
Text,
|
|
3158
|
-
{
|
|
3159
|
-
style: {
|
|
3160
|
-
fontSize: 11,
|
|
3161
|
-
fontFamily: "Inter, sans-serif",
|
|
3162
|
-
fontWeight: "600",
|
|
3163
|
-
color: BRAND_PRIMARY3
|
|
3164
|
-
},
|
|
3165
|
-
children: "Skip"
|
|
3166
|
-
}
|
|
3167
|
-
)
|
|
3168
|
-
}
|
|
3169
|
-
)
|
|
3170
|
-
] })
|
|
3573
|
+
!displayOnly && /* @__PURE__ */ jsx(RestActions, { onAddTime, onSkip })
|
|
3171
3574
|
]
|
|
3172
3575
|
}
|
|
3173
3576
|
);
|