@titan-design/react-ui 0.6.0 → 0.7.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/bodymap.js +12 -7
- package/dist/bodymap.js.map +1 -1
- package/dist/bodymap.mjs +12 -7
- package/dist/bodymap.mjs.map +1 -1
- package/dist/index.d.mts +855 -124
- package/dist/index.d.ts +855 -124
- package/dist/index.js +2276 -801
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2230 -802
- package/dist/index.mjs.map +1 -1
- package/dist/{pages-MO0JCySL.d.mts → pages-8u_4WbL-.d.mts} +280 -57
- package/dist/{pages-D7Jlssvp.d.ts → pages-DaWiBOGa.d.ts} +280 -57
- package/dist/pages.d.mts +1 -1
- package/dist/pages.d.ts +1 -1
- package/dist/pages.js +1255 -798
- package/dist/pages.js.map +1 -1
- package/dist/pages.mjs +1255 -798
- package/dist/pages.mjs.map +1 -1
- package/dist/theme/index.d.mts +16 -82
- package/dist/theme/index.d.ts +16 -82
- package/dist/theme/index.js +13 -115
- package/dist/theme/index.js.map +1 -1
- package/dist/theme/index.mjs +13 -115
- package/dist/theme/index.mjs.map +1 -1
- package/dist/theme/tokens-css.js +14 -6
- package/dist/theme/tokens-css.js.map +1 -1
- package/dist/theme/tokens-css.mjs +14 -6
- package/dist/theme/tokens-css.mjs.map +1 -1
- package/package.json +1 -1
- package/src/arch/Architecture.stories.tsx +870 -0
- package/src/arch/annotations.json +48 -0
- package/src/arch/arch-graph.json +4301 -0
- package/src/components/custom/DateTime/DateTime.stories.tsx +1 -1
- package/src/components/custom/EmptyState/EmptyState.stories.tsx +1 -1
- package/src/components/custom/Gauge/Gauge.stories.tsx +1 -1
- package/src/components/custom/Metric/Metric.stories.tsx +1 -1
- package/src/components/custom/Scatter/Scatter.stories.tsx +1 -1
- package/src/components/custom/Sidebar/Sidebar.stories.tsx +1 -1
- package/src/components/custom/Table/Table.stories.tsx +1 -1
- package/src/components/custom/TimerReadout/TimerReadout.stories.tsx +62 -0
- package/src/components/custom/TimerReadout/TimerReadout.test.tsx +72 -0
- package/src/components/custom/TimerReadout/TimerReadout.tsx +75 -0
- package/src/components/custom/TimerReadout/index.ts +1 -0
- package/src/components/custom/Treemap/Treemap.stories.tsx +1 -1
- package/src/components/custom/Typography/Typography.stories.tsx +1 -1
- package/src/components/custom/Workout/ActiveWorkoutPage.stories.tsx +21 -13
- package/src/components/custom/Workout/ActiveWorkoutPage.test.tsx +16 -13
- package/src/components/custom/Workout/ActiveWorkoutPage.tsx +9 -15
- package/src/components/custom/Workout/BaseBadge.stories.tsx +1 -1
- package/src/components/custom/Workout/BodyMap.stories.tsx +1 -1
- package/src/components/custom/Workout/BodyMapDetailPanel.stories.tsx +1 -1
- package/src/components/custom/Workout/CapacityBandChart.stories.tsx +1 -1
- package/src/components/custom/Workout/DeviationBar.stories.tsx +1 -1
- package/src/components/custom/Workout/ExerciseCard.stories.tsx +51 -66
- package/src/components/custom/Workout/ExerciseCard.test.tsx +146 -143
- package/src/components/custom/Workout/ExerciseCard.tsx +176 -198
- package/src/components/custom/Workout/ExerciseCardHeading.stories.tsx +99 -0
- package/src/components/custom/Workout/ExerciseCardHeading.test.tsx +72 -0
- package/src/components/custom/Workout/ExerciseCardHeading.tsx +43 -0
- package/src/components/custom/Workout/ExerciseCardUnified.stories.tsx +151 -0
- package/src/components/custom/Workout/ExerciseDetailPage.stories.tsx +5 -3
- package/src/components/custom/Workout/ExerciseDetailPage.test.tsx +4 -4
- package/src/components/custom/Workout/ExerciseDetailPage.tsx +4 -5
- package/src/components/custom/Workout/ExerciseHeading.stories.tsx +81 -0
- package/src/components/custom/Workout/ExerciseHeading.test.tsx +77 -0
- package/src/components/custom/Workout/ExerciseHeading.tsx +85 -0
- package/src/components/custom/Workout/ExerciseIndicator.stories.tsx +110 -0
- package/src/components/custom/Workout/ExerciseIndicator.test.tsx +93 -0
- package/src/components/custom/Workout/ExerciseIndicator.tsx +120 -0
- package/src/components/custom/Workout/FatigueMeter.stories.tsx +51 -0
- package/src/components/custom/Workout/FatigueMeter.test.tsx +73 -0
- package/src/components/custom/Workout/FatigueMeter.tsx +82 -0
- package/src/components/custom/Workout/InputBar.stories.tsx +1 -1
- package/src/components/custom/Workout/IntensityBar.stories.tsx +1 -1
- package/src/components/custom/Workout/LiveAuraFrame.stories.tsx +153 -0
- package/src/components/custom/Workout/LiveAuraFrame.test.tsx +74 -0
- package/src/components/custom/Workout/LiveAuraFrame.tsx +105 -0
- package/src/components/custom/Workout/MesoCard.stories.tsx +1 -1
- package/src/components/custom/Workout/MesoProgressBar.stories.tsx +1 -1
- package/src/components/custom/Workout/MesoStatusCard.stories.tsx +1 -1
- package/src/components/custom/Workout/MetricTiles.stories.tsx +60 -0
- package/src/components/custom/Workout/MetricTiles.test.tsx +58 -0
- package/src/components/custom/Workout/MetricTiles.tsx +48 -0
- package/src/components/custom/Workout/MuscleGroupChip.stories.tsx +1 -1
- package/src/components/custom/Workout/PlaceholderStrip.stories.tsx +1 -1
- package/src/components/custom/Workout/PrBadge.stories.tsx +1 -1
- package/src/components/custom/Workout/PrHistoryModal.stories.tsx +1 -1
- package/src/components/custom/Workout/ProgramPlanningPage.stories.tsx +4 -16
- package/src/components/custom/Workout/ProgramPlanningPage.tsx +8 -6
- package/src/components/custom/Workout/README.md +146 -4
- package/src/components/custom/Workout/ReadinessCheck.stories.tsx +1 -1
- package/src/components/custom/Workout/RestTimer.stories.tsx +1 -1
- package/src/components/custom/Workout/RestTimer.tsx +15 -10
- package/src/components/custom/Workout/RowInventory.stories.tsx +405 -0
- package/src/components/custom/Workout/S3FullRail.stories.tsx +34 -0
- package/src/components/custom/Workout/S3SessionPace.stories.tsx +95 -0
- package/src/components/custom/Workout/S3SessionRailHeading.stories.tsx +61 -0
- package/src/components/custom/Workout/S3SetTypes.stories.tsx +176 -0
- package/src/components/custom/Workout/S3WorkoutExpansion.stories.tsx +240 -0
- package/src/components/custom/Workout/ScheduleTiles.stories.tsx +40 -0
- package/src/components/custom/Workout/ScheduleTiles.test.tsx +34 -0
- package/src/components/custom/Workout/ScheduleTiles.tsx +70 -0
- package/src/components/custom/Workout/SegmentedBar.stories.tsx +99 -0
- package/src/components/custom/Workout/SegmentedBar.test.tsx +77 -0
- package/src/components/custom/Workout/SegmentedBar.tsx +172 -0
- package/src/components/custom/Workout/SegmentedProgressBar.stories.tsx +50 -0
- package/src/components/custom/Workout/SegmentedProgressBar.test.tsx +80 -0
- package/src/components/custom/Workout/SegmentedProgressBar.tsx +67 -0
- package/src/components/custom/Workout/SessionHeader.stories.tsx +121 -0
- package/src/components/custom/Workout/SessionHeader.test.tsx +122 -0
- package/src/components/custom/Workout/SessionHeader.tsx +152 -0
- package/src/components/custom/Workout/SessionRail.stories.tsx +153 -0
- package/src/components/custom/Workout/SessionRail.test.tsx +86 -0
- package/src/components/custom/Workout/SessionRail.tsx +126 -0
- package/src/components/custom/Workout/SetBar.stories.tsx +124 -0
- package/src/components/custom/Workout/SetBar.test.tsx +123 -0
- package/src/components/custom/Workout/SetBar.tsx +171 -0
- package/src/components/custom/Workout/SetRow.stories.tsx +50 -100
- package/src/components/custom/Workout/SetRow.test.tsx +94 -166
- package/src/components/custom/Workout/SetRow.tsx +169 -232
- package/src/components/custom/Workout/SetStrip.stories.tsx +154 -0
- package/src/components/custom/Workout/SetStrip.test.tsx +109 -0
- package/src/components/custom/Workout/SetStrip.tsx +71 -0
- package/src/components/custom/Workout/SetTableHeader.stories.tsx +66 -0
- package/src/components/custom/Workout/SetTableHeader.test.tsx +47 -0
- package/src/components/custom/Workout/SetTableHeader.tsx +88 -0
- package/src/components/custom/Workout/SetsRepsLoad.stories.tsx +47 -0
- package/src/components/custom/Workout/SetsRepsLoad.test.tsx +44 -0
- package/src/components/custom/Workout/SetsRepsLoad.tsx +52 -0
- package/src/components/custom/Workout/Sparkline.stories.tsx +1 -1
- package/src/components/custom/Workout/StatusDot.stories.tsx +1 -1
- package/src/components/custom/Workout/StatusPill.stories.tsx +59 -0
- package/src/components/custom/Workout/StatusPill.test.tsx +63 -0
- package/src/components/custom/Workout/StatusPill.tsx +88 -0
- package/src/components/custom/Workout/StrengthTrendChart.stories.tsx +1 -1
- package/src/components/custom/Workout/SupersetWrapper.stories.tsx +1 -1
- package/src/components/custom/Workout/TempoBar.stories.tsx +1 -1
- package/src/components/custom/Workout/TempoDisplay.stories.tsx +12 -1
- package/src/components/custom/Workout/TempoDisplay.tsx +23 -33
- package/src/components/custom/Workout/TrainingStatusPage.stories.tsx +1 -1
- package/src/components/custom/Workout/VelocityStrip.modalities.stories.tsx +254 -0
- package/src/components/custom/Workout/VelocityStrip.responsive.stories.tsx +145 -0
- package/src/components/custom/Workout/VelocityStrip.stories.tsx +129 -66
- package/src/components/custom/Workout/VelocityStrip.test.tsx +376 -141
- package/src/components/custom/Workout/VelocityStrip.tsx +508 -120
- package/src/components/custom/Workout/VolumeLandmarkBar.stories.tsx +73 -0
- package/src/components/custom/Workout/VolumeLandmarkBar.test.tsx +119 -0
- package/src/components/custom/Workout/VolumeLandmarkBar.tsx +154 -0
- package/src/components/custom/Workout/WeekRow.stories.tsx +1 -1
- package/src/components/custom/Workout/WeightBadge.stories.tsx +1 -1
- package/src/components/custom/Workout/WorkoutCard.stories.tsx +2 -6
- package/src/components/custom/Workout/WorkoutCard.test.tsx +13 -46
- package/src/components/custom/Workout/WorkoutPill.stories.tsx +1 -1
- package/src/components/custom/Workout/ZoneTrack.stories.tsx +140 -0
- package/src/components/custom/Workout/ZoneTrack.test.tsx +200 -0
- package/src/components/custom/Workout/ZoneTrack.tsx +315 -0
- package/src/components/custom/Workout/index.ts +55 -2
- package/src/components/custom/Workout/metricText.stories.tsx +69 -0
- package/src/components/custom/Workout/metricText.tsx +34 -0
- package/src/components/custom/Workout/paceTone.test.ts +29 -0
- package/src/components/custom/Workout/paceTone.ts +27 -0
- package/src/components/custom/Workout/setHeadingKit.tsx +177 -0
- package/src/components/custom/index.ts +1 -0
- package/src/components/custom/stepper/Stepper.stories.tsx +1 -1
- package/src/components/icons/icons.stories.tsx +67 -2
- package/src/components/icons/icons.test.tsx +32 -2
- package/src/components/icons/icons.tsx +108 -0
- package/src/components/shell/BrandLockup.stories.tsx +3 -3
- package/src/components/shell/DashboardShell.stories.tsx +65 -0
- package/src/components/shell/DashboardShell.test.tsx +42 -0
- package/src/components/shell/DashboardShell.tsx +74 -0
- package/src/components/shell/DeviceIndicator.stories.tsx +2 -2
- package/src/components/shell/DeviceMenu.stories.tsx +5 -5
- package/src/components/shell/DeviceRow.stories.tsx +3 -3
- package/src/components/shell/NavItem.stories.tsx +52 -0
- package/src/components/shell/NavItem.test.tsx +35 -0
- package/src/components/shell/NavItem.tsx +75 -0
- package/src/components/shell/README.md +30 -0
- package/src/components/shell/SessionRailSpecimen.stories.tsx +753 -0
- package/src/components/shell/SessionStatePill.stories.tsx +3 -3
- package/src/components/shell/SessionStatePill.tsx +1 -1
- package/src/components/shell/SideNav.stories.tsx +81 -0
- package/src/components/shell/SideNav.test.tsx +51 -0
- package/src/components/shell/SideNav.tsx +74 -0
- package/src/components/shell/TopBar.stories.tsx +5 -5
- package/src/components/shell/index.ts +5 -0
- package/src/components/ui/alert/Alert.stories.tsx +1 -1
- package/src/components/ui/autocomplete/Autocomplete.stories.tsx +1 -1
- package/src/components/ui/avatar/Avatar.stories.tsx +1 -1
- package/src/components/ui/badge/Badge.stories.tsx +1 -1
- package/src/components/ui/breadcrumbs/Breadcrumbs.stories.tsx +1 -1
- package/src/components/ui/button/Button.stories.tsx +1 -1
- package/src/components/ui/card/Card.stories.tsx +1 -1
- package/src/components/ui/checkbox/Checkbox.stories.tsx +1 -1
- package/src/components/ui/chip/Chip.stories.tsx +1 -1
- package/src/components/ui/collapse/Collapse.stories.tsx +1 -1
- package/src/components/ui/data-row/DataRow.stories.tsx +1 -1
- package/src/components/ui/divider/Divider.stories.tsx +1 -1
- package/src/components/ui/drawer/Drawer.stories.tsx +1 -1
- package/src/components/ui/form-field/FormField.stories.tsx +1 -1
- package/src/components/ui/help-tip/HelpTip.stories.tsx +1 -1
- package/src/components/ui/icon-box/IconBox.stories.tsx +1 -1
- package/src/components/ui/index.ts +1 -0
- package/src/components/ui/indicator/Indicator.stories.tsx +2 -2
- package/src/components/ui/indicator/Indicator.test.tsx +1 -0
- package/src/components/ui/indicator/Indicator.tsx +6 -1
- package/src/components/ui/input/Input.stories.tsx +1 -1
- package/src/components/ui/link/Link.stories.tsx +1 -1
- package/src/components/ui/list-item/ListItem.stories.tsx +1 -1
- package/src/components/ui/menu/Menu.stories.tsx +1 -1
- package/src/components/ui/modal/Modal.stories.tsx +1 -1
- package/src/components/ui/pill/Pill.stories.tsx +1 -1
- package/src/components/ui/popover/Popover.stories.tsx +1 -1
- package/src/components/ui/progress/Progress.stories.tsx +1 -1
- package/src/components/ui/radio/Radio.stories.tsx +1 -1
- package/src/components/ui/section/Section.stories.tsx +1 -1
- package/src/components/ui/select/Select.stories.tsx +1 -1
- package/src/components/ui/skeleton/Skeleton.stories.tsx +1 -1
- package/src/components/ui/spinner/Spinner.stories.tsx +1 -1
- package/src/components/ui/stack/Stack.stories.tsx +1 -1
- package/src/components/ui/surface/Surface.stories.tsx +1 -1
- package/src/components/ui/switch/Switch.stories.tsx +1 -1
- package/src/components/ui/tabs/Tabs.stories.tsx +1 -1
- package/src/components/ui/tile/Tile.stories.tsx +79 -0
- package/src/components/ui/tile/Tile.test.tsx +48 -0
- package/src/components/ui/tile/Tile.tsx +64 -0
- package/src/components/ui/tile/index.ts +2 -0
- package/src/components/ui/toast/Toast.stories.tsx +1 -1
- package/src/components/ui/toolbar-button/ToolbarButton.stories.tsx +1 -1
- package/src/components/ui/tooltip/Tooltip.stories.tsx +1 -1
- package/src/examples/react-hook-form/ReactHookForm.stories.tsx +1 -1
- package/src/hooks/index.ts +2 -0
- package/src/hooks/useTimer.test.ts +150 -0
- package/src/hooks/useTimer.ts +77 -0
- package/src/index.ts +3 -0
- package/src/pages.ts +1 -1
- package/src/stories/PresetShowcase.stories.tsx +1 -1
- package/src/stories/ThemePresets.stories.tsx +1 -1
- package/src/theme/ColorSystem.stories.tsx +1 -1
- package/src/theme/Colors.stories.tsx +5 -8
- package/src/theme/config.ts +4 -0
- package/src/theme/elevation.stories.tsx +1 -1
- package/src/theme/global.css +5 -1
- package/src/theme/shadows.stories.tsx +1 -1
- package/src/theme/tokens/primitives.ts +11 -107
- package/src/theme/tokens/semantic.ts +8 -0
- package/src/theme/workout-tokens.ts +11 -7
- package/tailwind.config.js +5 -0
package/dist/pages.mjs
CHANGED
|
@@ -8,21 +8,18 @@ import { twMerge } from 'tailwind-merge';
|
|
|
8
8
|
|
|
9
9
|
// src/theme/tokens/primitives.ts
|
|
10
10
|
var primitiveColors = {
|
|
11
|
-
// Blue scale
|
|
11
|
+
// Blue scale
|
|
12
12
|
blue: {
|
|
13
13
|
400: "#60A5FA",
|
|
14
14
|
600: "#5048E5",
|
|
15
|
-
// Primary main
|
|
16
15
|
700: "#3832A0"},
|
|
17
|
-
// Red scale
|
|
16
|
+
// Red scale
|
|
18
17
|
red: {
|
|
19
|
-
// Error light
|
|
20
18
|
500: "#EF4444",
|
|
21
19
|
600: "#D14343"},
|
|
22
|
-
// Vivid red scale
|
|
20
|
+
// Vivid red scale
|
|
23
21
|
redVivid: {
|
|
24
22
|
400: "#FF6070",
|
|
25
|
-
// Error vivid light
|
|
26
23
|
500: "#FF4757",
|
|
27
24
|
700: "#C42539"},
|
|
28
25
|
// Neutral/Gray scale
|
|
@@ -65,11 +62,16 @@ var primitiveRamps = {
|
|
|
65
62
|
50: "#FFF7DD",
|
|
66
63
|
200: "#FFD352",
|
|
67
64
|
300: "#F9B415",
|
|
65
|
+
// pin
|
|
66
|
+
400: "#E08C00",
|
|
68
67
|
500: "#C27400"},
|
|
69
68
|
green: {
|
|
70
69
|
50: "#E3FFEE",
|
|
71
70
|
200: "#58F69E",
|
|
72
71
|
300: "#2ED573",
|
|
72
|
+
// pin
|
|
73
|
+
400: "#21C05D",
|
|
74
|
+
500: "#22A444",
|
|
73
75
|
600: "#298732"},
|
|
74
76
|
cyan: {
|
|
75
77
|
// pin
|
|
@@ -77,7 +79,8 @@ var primitiveRamps = {
|
|
|
77
79
|
500: "#2697B7",
|
|
78
80
|
600: "#307B9B",
|
|
79
81
|
700: "#2A617F",
|
|
80
|
-
800: "#22465F"
|
|
82
|
+
800: "#22465F",
|
|
83
|
+
900: "#0B3149"},
|
|
81
84
|
blue: {
|
|
82
85
|
50: "#EFF8FF",
|
|
83
86
|
300: "#78C2FF",
|
|
@@ -132,15 +135,22 @@ var WORKOUT_TOKENS = {
|
|
|
132
135
|
// Canonical 4-band performance scale — the single source for BOTH the
|
|
133
136
|
// VelocityStrip zone bars and the SetRow RPE color (TD-03.43). The direction
|
|
134
137
|
// is intentionally inverted between the two consumers: for velocity, green =
|
|
135
|
-
// fastest/best; for RPE, green = easiest. This is the [0,
|
|
136
|
-
// the canonical `sequentialEffort` primitive
|
|
137
|
-
//
|
|
138
|
+
// fastest/best; for RPE, green = easiest. This is the [0,2,3,4] subsample of
|
|
139
|
+
// the canonical `sequentialEffort` primitive: an EVEN hue walk across the ramp
|
|
140
|
+
// (green → gold → orange → red). The earlier [0,1,2,4] spent two of its four
|
|
141
|
+
// bands on adjacent yellows (amber-200 + amber-300) and skipped orange, reading
|
|
142
|
+
// yellow-heavy with an abrupt jump to red; sampling amber-300 + orange-400
|
|
143
|
+
// instead gives four cleanly separable bands that hold at 3px strip scale.
|
|
144
|
+
// Consumed inline (RN).
|
|
138
145
|
scale: {
|
|
139
146
|
green: sequentialEffort[0],
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
// gold
|
|
147
|
+
// green-300
|
|
148
|
+
yellow: sequentialEffort[2],
|
|
149
|
+
// amber-300 (gold)
|
|
150
|
+
orange: sequentialEffort[3],
|
|
151
|
+
// orange-400 (true orange)
|
|
143
152
|
red: sequentialEffort[4]
|
|
153
|
+
// red-600
|
|
144
154
|
},
|
|
145
155
|
// Surface and border colors were removed here: those flip with the theme, so
|
|
146
156
|
// components use the CSS custom properties (var(--color-surface-*/border-*))
|
|
@@ -159,12 +169,6 @@ var WORKOUT_TOKENS = {
|
|
|
159
169
|
function roundRpe(rpe) {
|
|
160
170
|
return Math.round(rpe * 2) / 2;
|
|
161
171
|
}
|
|
162
|
-
function rpeColor(rpe) {
|
|
163
|
-
if (rpe >= 9.5) return WORKOUT_TOKENS.scale.red;
|
|
164
|
-
if (rpe >= 8.5) return WORKOUT_TOKENS.scale.orange;
|
|
165
|
-
if (rpe >= 7) return WORKOUT_TOKENS.scale.yellow;
|
|
166
|
-
return WORKOUT_TOKENS.scale.green;
|
|
167
|
-
}
|
|
168
172
|
function roundWeight(weight) {
|
|
169
173
|
return Math.round(weight);
|
|
170
174
|
}
|
|
@@ -192,14 +196,258 @@ function wrapJsx(fn) {
|
|
|
192
196
|
var jsx = wrapJsx(jsx$1);
|
|
193
197
|
var jsxs = wrapJsx(jsxs$1);
|
|
194
198
|
|
|
199
|
+
// src/components/custom/Workout/SegmentedBar.tsx
|
|
200
|
+
var PULSE_HALF_MS = 950;
|
|
201
|
+
var PULSE_MIN_OPACITY = 0.45;
|
|
202
|
+
var SEGMENTED_BAR_GAP = 5;
|
|
203
|
+
function usePulse(active) {
|
|
204
|
+
const [value] = useState(() => new Animated.Value(0));
|
|
205
|
+
useEffect(() => {
|
|
206
|
+
if (!active) return;
|
|
207
|
+
const loop = Animated.loop(
|
|
208
|
+
Animated.sequence([
|
|
209
|
+
Animated.timing(value, {
|
|
210
|
+
toValue: 1,
|
|
211
|
+
duration: PULSE_HALF_MS,
|
|
212
|
+
easing: Easing.inOut(Easing.ease),
|
|
213
|
+
useNativeDriver: false
|
|
214
|
+
}),
|
|
215
|
+
Animated.timing(value, {
|
|
216
|
+
toValue: 0,
|
|
217
|
+
duration: PULSE_HALF_MS,
|
|
218
|
+
easing: Easing.inOut(Easing.ease),
|
|
219
|
+
useNativeDriver: false
|
|
220
|
+
})
|
|
221
|
+
])
|
|
222
|
+
);
|
|
223
|
+
loop.start();
|
|
224
|
+
return () => loop.stop();
|
|
225
|
+
}, [active, value]);
|
|
226
|
+
return value;
|
|
227
|
+
}
|
|
228
|
+
function SegmentedBar({
|
|
229
|
+
segments,
|
|
230
|
+
height = 8,
|
|
231
|
+
gap = SEGMENTED_BAR_GAP,
|
|
232
|
+
radius = 2,
|
|
233
|
+
marker = null,
|
|
234
|
+
segmentTestID,
|
|
235
|
+
style,
|
|
236
|
+
...props
|
|
237
|
+
}) {
|
|
238
|
+
const pulse = usePulse(segments.some((s) => s.pulse));
|
|
239
|
+
return /* @__PURE__ */ jsxs(View, { style: [{ flexDirection: "row", height, gap, position: "relative" }, style], ...props, children: [
|
|
240
|
+
segments.map((seg, i) => {
|
|
241
|
+
const fillStyle = {
|
|
242
|
+
width: `${(seg.fill ?? 1) * 100}%`,
|
|
243
|
+
height: "100%",
|
|
244
|
+
backgroundColor: seg.color,
|
|
245
|
+
opacity: seg.opacity
|
|
246
|
+
};
|
|
247
|
+
const testID = segmentTestID?.(seg, i) ?? "segmented-bar-segment";
|
|
248
|
+
return /* @__PURE__ */ jsx(
|
|
249
|
+
View,
|
|
250
|
+
{
|
|
251
|
+
style: {
|
|
252
|
+
flex: seg.weight ?? 1,
|
|
253
|
+
minWidth: 0,
|
|
254
|
+
height: "100%",
|
|
255
|
+
marginLeft: seg.leadingGap,
|
|
256
|
+
borderRadius: radius,
|
|
257
|
+
overflow: "hidden"
|
|
258
|
+
},
|
|
259
|
+
children: seg.pulse ? /* @__PURE__ */ jsx(
|
|
260
|
+
Animated.View,
|
|
261
|
+
{
|
|
262
|
+
style: {
|
|
263
|
+
...fillStyle,
|
|
264
|
+
...seg.pulseColor ? {
|
|
265
|
+
backgroundColor: pulse.interpolate({
|
|
266
|
+
inputRange: [0, 1],
|
|
267
|
+
outputRange: seg.pulseColor
|
|
268
|
+
})
|
|
269
|
+
} : {
|
|
270
|
+
opacity: pulse.interpolate({
|
|
271
|
+
inputRange: [0, 1],
|
|
272
|
+
outputRange: [PULSE_MIN_OPACITY, 1]
|
|
273
|
+
})
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
accessibilityElementsHidden: true,
|
|
277
|
+
testID
|
|
278
|
+
}
|
|
279
|
+
) : /* @__PURE__ */ jsx(View, { style: fillStyle, accessibilityElementsHidden: true, testID })
|
|
280
|
+
},
|
|
281
|
+
i
|
|
282
|
+
);
|
|
283
|
+
}),
|
|
284
|
+
marker ? /* @__PURE__ */ jsx(
|
|
285
|
+
View,
|
|
286
|
+
{
|
|
287
|
+
style: {
|
|
288
|
+
position: "absolute",
|
|
289
|
+
left: `${marker.position * 100}%`,
|
|
290
|
+
top: 0,
|
|
291
|
+
bottom: 0,
|
|
292
|
+
width: 2,
|
|
293
|
+
backgroundColor: marker.color
|
|
294
|
+
},
|
|
295
|
+
accessibilityElementsHidden: true,
|
|
296
|
+
testID: "segmented-bar-marker"
|
|
297
|
+
}
|
|
298
|
+
) : null
|
|
299
|
+
] });
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// src/components/custom/Workout/SetBar.tsx
|
|
303
|
+
var SET_STRIP_ZONES = {
|
|
304
|
+
slow: primitiveRamps.red[600],
|
|
305
|
+
moderate: primitiveRamps.orange[400],
|
|
306
|
+
fast: primitiveRamps.amber[300],
|
|
307
|
+
fastest: primitiveRamps.green[300]
|
|
308
|
+
};
|
|
309
|
+
var TODO_COLOR = primitiveColors.charcoal[300];
|
|
310
|
+
var SET_STRIP_VARIABLE_COLOR = primitiveRamps.cyan[900];
|
|
311
|
+
var NOTCH_GAP = 2;
|
|
312
|
+
var CLUSTER_GAP = 3;
|
|
313
|
+
var MYO_UPCOMING_CLUSTERS = 3;
|
|
314
|
+
var MYO_UPCOMING_CLUSTER_LEN = 5;
|
|
315
|
+
var MYO_UPCOMING_FADE = 0.6;
|
|
316
|
+
var PULSE_RANGE = {
|
|
317
|
+
[SET_STRIP_ZONES.slow]: [primitiveRamps.red[500], primitiveRamps.red[700]],
|
|
318
|
+
[SET_STRIP_ZONES.moderate]: [primitiveRamps.orange[300], primitiveRamps.orange[500]],
|
|
319
|
+
[SET_STRIP_ZONES.fast]: [primitiveRamps.amber[200], primitiveRamps.amber[400]],
|
|
320
|
+
[SET_STRIP_ZONES.fastest]: [primitiveRamps.green[200], primitiveRamps.green[400]]
|
|
321
|
+
};
|
|
322
|
+
function velocityZoneColor(v) {
|
|
323
|
+
if (v < 0.5) return SET_STRIP_ZONES.slow;
|
|
324
|
+
if (v < 0.75) return SET_STRIP_ZONES.moderate;
|
|
325
|
+
if (v < 1) return SET_STRIP_ZONES.fast;
|
|
326
|
+
return SET_STRIP_ZONES.fastest;
|
|
327
|
+
}
|
|
328
|
+
function chunkedSegments(chunks, gap) {
|
|
329
|
+
return chunks.flatMap(
|
|
330
|
+
(vels, ci) => vels.map((v, ri) => ({
|
|
331
|
+
color: velocityZoneColor(v),
|
|
332
|
+
...ci > 0 && ri === 0 ? { leadingGap: gap } : {}
|
|
333
|
+
}))
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
function rangeSegments(floor, max, doneVels) {
|
|
337
|
+
return Array.from({ length: max }, (_, i) => {
|
|
338
|
+
if (i < doneVels.length) return { color: velocityZoneColor(doneVels[i]) };
|
|
339
|
+
return { color: i < floor ? TODO_COLOR : SET_STRIP_VARIABLE_COLOR };
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
function myoUpcomingSegments(activationLen, clusterCount) {
|
|
343
|
+
const activation = Array.from({ length: activationLen }, () => ({ color: TODO_COLOR }));
|
|
344
|
+
const trail = Array.from(
|
|
345
|
+
{ length: clusterCount },
|
|
346
|
+
(_, ci) => Array.from({ length: MYO_UPCOMING_CLUSTER_LEN }, (_2, ri) => ({
|
|
347
|
+
color: SET_STRIP_VARIABLE_COLOR,
|
|
348
|
+
opacity: MYO_UPCOMING_FADE ** ci,
|
|
349
|
+
...ri === 0 ? { leadingGap: CLUSTER_GAP } : {}
|
|
350
|
+
}))
|
|
351
|
+
).flat();
|
|
352
|
+
return [...activation, ...trail];
|
|
353
|
+
}
|
|
354
|
+
function setSegments(set) {
|
|
355
|
+
if (set.status === "todo") {
|
|
356
|
+
return [{ color: TODO_COLOR }];
|
|
357
|
+
}
|
|
358
|
+
if (set.status === "done") {
|
|
359
|
+
return set.velocities.map((v) => ({ color: velocityZoneColor(v) }));
|
|
360
|
+
}
|
|
361
|
+
if (set.status === "range") {
|
|
362
|
+
return rangeSegments(set.floor, set.max, set.doneVels);
|
|
363
|
+
}
|
|
364
|
+
if (set.status === "drop") {
|
|
365
|
+
return chunkedSegments(set.subloads, NOTCH_GAP);
|
|
366
|
+
}
|
|
367
|
+
if (set.status === "myo") {
|
|
368
|
+
return chunkedSegments([set.activation, ...set.clusters], CLUSTER_GAP);
|
|
369
|
+
}
|
|
370
|
+
if (set.status === "myo-upcoming") {
|
|
371
|
+
return myoUpcomingSegments(set.activationLen, set.clusterCount ?? MYO_UPCOMING_CLUSTERS);
|
|
372
|
+
}
|
|
373
|
+
const performed = set.velocities.map((v) => {
|
|
374
|
+
const color = velocityZoneColor(v);
|
|
375
|
+
return { color, pulse: true, pulseColor: PULSE_RANGE[color] };
|
|
376
|
+
});
|
|
377
|
+
const remaining = Math.max(0, set.planned - set.velocities.length);
|
|
378
|
+
return [...performed, ...Array.from({ length: remaining }, () => ({ color: TODO_COLOR }))];
|
|
379
|
+
}
|
|
380
|
+
function setBarSegmentTestID(seg) {
|
|
381
|
+
if (seg.pulse) return "set-strip-pulse";
|
|
382
|
+
if (seg.color === SET_STRIP_VARIABLE_COLOR) return "set-strip-variable";
|
|
383
|
+
return seg.color === TODO_COLOR ? "set-strip-empty" : "set-strip-fill";
|
|
384
|
+
}
|
|
385
|
+
function SetBar({ set, height = 8 }) {
|
|
386
|
+
return /* @__PURE__ */ jsx(
|
|
387
|
+
SegmentedBar,
|
|
388
|
+
{
|
|
389
|
+
segments: setSegments(set),
|
|
390
|
+
height,
|
|
391
|
+
gap: 0,
|
|
392
|
+
radius: 0,
|
|
393
|
+
segmentTestID: setBarSegmentTestID,
|
|
394
|
+
style: { flex: 1, minWidth: 0, borderRadius: 2, overflow: "hidden" },
|
|
395
|
+
testID: "set-strip-set"
|
|
396
|
+
}
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// src/components/custom/Workout/SetStrip.tsx
|
|
401
|
+
var SET_STRIP_GAP = 5;
|
|
402
|
+
function setCategory(set) {
|
|
403
|
+
switch (set.status) {
|
|
404
|
+
case "done":
|
|
405
|
+
case "drop":
|
|
406
|
+
case "myo":
|
|
407
|
+
return "done";
|
|
408
|
+
case "active":
|
|
409
|
+
return "active";
|
|
410
|
+
case "range":
|
|
411
|
+
return set.doneVels.length > 0 ? "active" : "upcoming";
|
|
412
|
+
case "todo":
|
|
413
|
+
case "myo-upcoming":
|
|
414
|
+
return "upcoming";
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
function describeSets(sets) {
|
|
418
|
+
const done = sets.filter((s) => setCategory(s) === "done").length;
|
|
419
|
+
const active = sets.filter((s) => setCategory(s) === "active").length;
|
|
420
|
+
const upcoming = sets.filter((s) => setCategory(s) === "upcoming").length;
|
|
421
|
+
return `Set progress: ${done} done, ${active} in progress, ${upcoming} upcoming`;
|
|
422
|
+
}
|
|
423
|
+
function SetStrip({ sets, height = 8, className, ...props }) {
|
|
424
|
+
return /* @__PURE__ */ jsx(
|
|
425
|
+
View,
|
|
426
|
+
{
|
|
427
|
+
className,
|
|
428
|
+
style: { flexDirection: "row", width: "100%", height, gap: SET_STRIP_GAP },
|
|
429
|
+
accessibilityRole: "image",
|
|
430
|
+
accessibilityLabel: describeSets(sets),
|
|
431
|
+
testID: "set-strip",
|
|
432
|
+
...props,
|
|
433
|
+
children: sets.map((set, i) => /* @__PURE__ */ jsx(SetBar, { set, height }, i))
|
|
434
|
+
}
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
|
|
195
438
|
// src/components/custom/Workout/VelocityStrip.tsx
|
|
196
439
|
var VEL_COLORS = WORKOUT_TOKENS.scale;
|
|
197
|
-
var
|
|
198
|
-
speed:
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
440
|
+
var bandIdToEffortColor = {
|
|
441
|
+
speed: sequentialEffort[0],
|
|
442
|
+
// green-300
|
|
443
|
+
power: sequentialEffort[2],
|
|
444
|
+
// amber-300 (gold)
|
|
445
|
+
strengthSpeed: sequentialEffort[3],
|
|
446
|
+
// orange-400
|
|
447
|
+
maximalStrength: sequentialEffort[4],
|
|
448
|
+
// red-600
|
|
449
|
+
grinding: sequentialEffort[5]
|
|
450
|
+
// red-700 — the distinct 5th band
|
|
203
451
|
};
|
|
204
452
|
function getVelocityZoneColor(velocity) {
|
|
205
453
|
if (velocity >= 1) return "vel-green";
|
|
@@ -238,14 +486,132 @@ function classifyBand(velocity, bands) {
|
|
|
238
486
|
return bands[bands.length - 1];
|
|
239
487
|
}
|
|
240
488
|
function bandColor(band) {
|
|
241
|
-
|
|
242
|
-
return token ? VEL_COLORS[token] : VEL_COLORS.green;
|
|
489
|
+
return bandIdToEffortColor[band.id] ?? VEL_COLORS.green;
|
|
243
490
|
}
|
|
244
491
|
function getLossStyle(loss) {
|
|
245
492
|
if (loss > 25) return { color: VEL_COLORS.red };
|
|
246
493
|
if (loss > 20) return { color: VEL_COLORS.orange };
|
|
247
494
|
return null;
|
|
248
495
|
}
|
|
496
|
+
var REP_GAP = 2;
|
|
497
|
+
var WIDE_GAP = 8;
|
|
498
|
+
var TODO_COLOR2 = primitiveColors.charcoal[300];
|
|
499
|
+
var CONTINUE_OUTLINE = primitiveRamps.cyan[800];
|
|
500
|
+
function deriveDoneVelocities(set) {
|
|
501
|
+
switch (set.type) {
|
|
502
|
+
case "drop":
|
|
503
|
+
return set.subloads.flat();
|
|
504
|
+
case "myo":
|
|
505
|
+
return [...set.activation, ...set.clusters.flat()];
|
|
506
|
+
default:
|
|
507
|
+
return set.velocities;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
function chunkedRepSlots(chunks) {
|
|
511
|
+
const slots = [];
|
|
512
|
+
chunks.forEach((chunk) => {
|
|
513
|
+
chunk.forEach((velocity, ri) => {
|
|
514
|
+
const first = slots.length === 0;
|
|
515
|
+
slots.push({ kind: "rep", velocity, leadingGap: first ? 0 : ri === 0 ? WIDE_GAP : REP_GAP });
|
|
516
|
+
});
|
|
517
|
+
});
|
|
518
|
+
return slots;
|
|
519
|
+
}
|
|
520
|
+
function buildSlots(set) {
|
|
521
|
+
switch (set.type) {
|
|
522
|
+
case "straight": {
|
|
523
|
+
const total = Math.max(set.velocities.length, set.planned ?? set.velocities.length);
|
|
524
|
+
return Array.from({ length: total }, (_, i) => {
|
|
525
|
+
const done = i < set.velocities.length;
|
|
526
|
+
return {
|
|
527
|
+
kind: done ? "rep" : "todo",
|
|
528
|
+
velocity: done ? set.velocities[i] : void 0,
|
|
529
|
+
leadingGap: i === 0 ? 0 : REP_GAP
|
|
530
|
+
};
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
case "range": {
|
|
534
|
+
const total = Math.max(set.max, set.velocities.length);
|
|
535
|
+
return Array.from({ length: total }, (_, i) => {
|
|
536
|
+
const kind = i < set.velocities.length ? "rep" : i < set.floor ? "todo" : "variable";
|
|
537
|
+
return {
|
|
538
|
+
kind,
|
|
539
|
+
velocity: kind === "rep" ? set.velocities[i] : void 0,
|
|
540
|
+
leadingGap: i === 0 ? 0 : REP_GAP
|
|
541
|
+
};
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
case "amrap": {
|
|
545
|
+
const reps = set.velocities.map((velocity, i) => ({
|
|
546
|
+
kind: "rep",
|
|
547
|
+
velocity,
|
|
548
|
+
leadingGap: i === 0 ? 0 : REP_GAP
|
|
549
|
+
}));
|
|
550
|
+
reps.push({ kind: "continue", leadingGap: reps.length === 0 ? 0 : REP_GAP });
|
|
551
|
+
return reps;
|
|
552
|
+
}
|
|
553
|
+
case "drop":
|
|
554
|
+
return chunkedRepSlots(set.subloads);
|
|
555
|
+
case "myo": {
|
|
556
|
+
const slots = chunkedRepSlots([set.activation, ...set.clusters]);
|
|
557
|
+
if (set.open) slots.push({ kind: "continue", leadingGap: slots.length === 0 ? 0 : REP_GAP });
|
|
558
|
+
return slots;
|
|
559
|
+
}
|
|
560
|
+
case "cluster": {
|
|
561
|
+
const total = Math.max(set.velocities.length, set.planned ?? set.velocities.length);
|
|
562
|
+
return Array.from({ length: total }, (_, i) => {
|
|
563
|
+
const done = i < set.velocities.length;
|
|
564
|
+
const boundary = i > 0 && i % set.groupSize === 0;
|
|
565
|
+
return {
|
|
566
|
+
kind: done ? "rep" : "todo",
|
|
567
|
+
velocity: done ? set.velocities[i] : void 0,
|
|
568
|
+
leadingGap: i === 0 ? 0 : boundary ? WIDE_GAP : REP_GAP
|
|
569
|
+
};
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
function setAccessibilityLabel(set, repCount) {
|
|
575
|
+
switch (set.type) {
|
|
576
|
+
case "straight":
|
|
577
|
+
return `Velocity strip, straight set, ${repCount} reps`;
|
|
578
|
+
case "range":
|
|
579
|
+
return `Velocity strip, rep-range set, floor ${set.floor} to ${set.max}, ${repCount} reps done`;
|
|
580
|
+
case "amrap":
|
|
581
|
+
return `Velocity strip, AMRAP set, ${repCount} reps and counting`;
|
|
582
|
+
case "drop":
|
|
583
|
+
return `Velocity strip, drop set, ${set.subloads.length} loads, ${repCount} reps`;
|
|
584
|
+
case "myo":
|
|
585
|
+
return `Velocity strip, myo-reps set, ${set.clusters.length} clusters${set.open ? ", open" : ""}, ${repCount} reps`;
|
|
586
|
+
case "cluster":
|
|
587
|
+
return `Velocity strip, cluster set, groups of ${set.groupSize}, ${repCount} reps`;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
function slotAccessibilityLabel(slot, index) {
|
|
591
|
+
switch (slot.kind) {
|
|
592
|
+
case "rep":
|
|
593
|
+
return `Rep ${index + 1}: ${formatVelocity(slot.velocity ?? 0)} meters per second`;
|
|
594
|
+
case "todo":
|
|
595
|
+
return `Rep ${index + 1}: planned`;
|
|
596
|
+
case "variable":
|
|
597
|
+
return `Rep ${index + 1}: variable`;
|
|
598
|
+
case "continue":
|
|
599
|
+
return "Keep going";
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
var EXPANDED_TODO_STUB_PCT = 16;
|
|
603
|
+
var EXPANDED_ENCODED_PCT = 45;
|
|
604
|
+
var EXPANDED_HEIGHT = 60;
|
|
605
|
+
var FIXED_MAX_VELOCITY = 1.15;
|
|
606
|
+
var BARE_STUB_HEIGHT = 3;
|
|
607
|
+
function scaleDenominator(scale, maxVelocity) {
|
|
608
|
+
return scale === "fixed" ? FIXED_MAX_VELOCITY : maxVelocity * 1.15;
|
|
609
|
+
}
|
|
610
|
+
function bareSlotHeight(slot, height, denom) {
|
|
611
|
+
if (slot.kind !== "rep" || denom <= 0) return BARE_STUB_HEIGHT;
|
|
612
|
+
const ratio = Math.min(1, (slot.velocity ?? 0) / denom);
|
|
613
|
+
return Math.max(BARE_STUB_HEIGHT, ratio * height);
|
|
614
|
+
}
|
|
249
615
|
function getReducedMotionPreference() {
|
|
250
616
|
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
|
|
251
617
|
return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
@@ -267,31 +633,48 @@ var ANIMATION_EASING = Easing.bezier(0.22, 1, 0.36, 1);
|
|
|
267
633
|
var POP_EASING = Easing.bezier(0.34, 1.56, 0.64, 1);
|
|
268
634
|
function VelocityStrip({
|
|
269
635
|
velocities,
|
|
636
|
+
set,
|
|
270
637
|
zones,
|
|
271
638
|
liveRepIndex,
|
|
272
|
-
expanded =
|
|
639
|
+
expanded = true,
|
|
273
640
|
onToggle,
|
|
274
641
|
onRepPress,
|
|
275
|
-
variant = "
|
|
642
|
+
variant = "expanded",
|
|
643
|
+
height = EXPANDED_HEIGHT,
|
|
644
|
+
scale = "peak",
|
|
645
|
+
showNumbers = true,
|
|
276
646
|
showInfo = true,
|
|
277
647
|
className,
|
|
278
648
|
...props
|
|
279
649
|
}) {
|
|
280
|
-
const
|
|
281
|
-
const
|
|
282
|
-
|
|
650
|
+
const doneVelocities = set ? deriveDoneVelocities(set) : velocities ?? [];
|
|
651
|
+
const slots = set ? buildSlots(set) : doneVelocities.map((v, i) => ({
|
|
652
|
+
kind: "rep",
|
|
653
|
+
velocity: v,
|
|
654
|
+
leadingGap: i === 0 ? 0 : REP_GAP
|
|
655
|
+
}));
|
|
656
|
+
const maxVelocity = Math.max(...doneVelocities, 0);
|
|
657
|
+
const meanVelocity = calculateMeanVelocity(doneVelocities);
|
|
658
|
+
const loss = calculateVelocityLoss(doneVelocities);
|
|
659
|
+
const framed = showNumbers || showInfo;
|
|
660
|
+
const scaleDenom = scaleDenominator(scale, maxVelocity);
|
|
283
661
|
const hasZones = zones != null && zones.length > 0;
|
|
284
662
|
const barColorFor = (v) => hasZones ? bandColor(classifyBand(v, zones)) : zoneHexMap[getVelocityZoneColor(v)];
|
|
663
|
+
const slotColor = (slot) => {
|
|
664
|
+
if (slot.kind === "rep") return barColorFor(slot.velocity ?? 0);
|
|
665
|
+
if (slot.kind === "todo") return TODO_COLOR2;
|
|
666
|
+
return SET_STRIP_VARIABLE_COLOR;
|
|
667
|
+
};
|
|
285
668
|
const meanZone = hasZones ? classifyBand(meanVelocity, zones)?.label ?? "" : getVelocityZoneName(meanVelocity);
|
|
286
669
|
const prefersReducedMotion = usePrefersReducedMotion();
|
|
287
|
-
const [heightAnim] = useState(() => new Animated.Value(expanded ?
|
|
670
|
+
const [heightAnim] = useState(() => new Animated.Value(expanded ? height : 3));
|
|
288
671
|
const [labelOpacity] = useState(() => new Animated.Value(expanded ? 1 : 0));
|
|
289
672
|
const [infoOpacity] = useState(() => new Animated.Value(expanded ? 1 : 0));
|
|
290
673
|
const [liveScale] = useState(() => new Animated.Value(1));
|
|
291
|
-
const liveVelocity = liveRepIndex != null ?
|
|
674
|
+
const liveVelocity = liveRepIndex != null ? doneVelocities[liveRepIndex] : void 0;
|
|
292
675
|
const isNewPeak = liveVelocity != null && maxVelocity > 0 && liveVelocity === maxVelocity;
|
|
293
676
|
useEffect(() => {
|
|
294
|
-
if (variant
|
|
677
|
+
if (variant !== "expanded" || !framed || liveRepIndex == null || liveVelocity == null) return;
|
|
295
678
|
if (prefersReducedMotion) {
|
|
296
679
|
liveScale.setValue(1);
|
|
297
680
|
return;
|
|
@@ -321,12 +704,12 @@ function VelocityStrip({
|
|
|
321
704
|
useNativeDriver: true
|
|
322
705
|
}).start();
|
|
323
706
|
}
|
|
324
|
-
}, [variant, liveRepIndex, liveVelocity, isNewPeak, prefersReducedMotion, liveScale]);
|
|
707
|
+
}, [variant, framed, liveRepIndex, liveVelocity, isNewPeak, prefersReducedMotion, liveScale]);
|
|
325
708
|
useEffect(() => {
|
|
326
|
-
if (variant
|
|
709
|
+
if (variant !== "expanded" || !framed) return;
|
|
327
710
|
Animated.parallel([
|
|
328
711
|
Animated.timing(heightAnim, {
|
|
329
|
-
toValue: expanded ?
|
|
712
|
+
toValue: expanded ? height : 3,
|
|
330
713
|
duration: ANIMATION_DURATION,
|
|
331
714
|
easing: ANIMATION_EASING,
|
|
332
715
|
useNativeDriver: false
|
|
@@ -344,39 +727,118 @@ function VelocityStrip({
|
|
|
344
727
|
useNativeDriver: false
|
|
345
728
|
})
|
|
346
729
|
]).start();
|
|
347
|
-
}, [expanded, variant, heightAnim, labelOpacity, infoOpacity]);
|
|
730
|
+
}, [expanded, variant, framed, height, heightAnim, labelOpacity, infoOpacity]);
|
|
731
|
+
if (set == null && velocities == null) return null;
|
|
732
|
+
const repCount = doneVelocities.length;
|
|
733
|
+
const miniLabel = set ? setAccessibilityLabel(set, repCount) : `Velocity strip, ${repCount} reps`;
|
|
348
734
|
if (variant === "mini") {
|
|
349
735
|
const { style: externalStyle, ...restProps } = props;
|
|
350
736
|
return /* @__PURE__ */ jsx(
|
|
351
737
|
View,
|
|
352
738
|
{
|
|
353
739
|
className,
|
|
354
|
-
style: [
|
|
740
|
+
style: [
|
|
741
|
+
{ flexDirection: "row", height: 3, gap: REP_GAP, borderRadius: 2, overflow: "hidden" },
|
|
742
|
+
externalStyle
|
|
743
|
+
],
|
|
355
744
|
accessibilityRole: "image",
|
|
356
|
-
accessibilityLabel:
|
|
745
|
+
accessibilityLabel: miniLabel,
|
|
357
746
|
testID: "velocity-strip-mini",
|
|
358
747
|
...restProps,
|
|
359
|
-
children:
|
|
748
|
+
children: slots.map((slot, i) => /* @__PURE__ */ jsx(
|
|
360
749
|
View,
|
|
361
750
|
{
|
|
362
751
|
style: {
|
|
363
752
|
flex: 1,
|
|
364
|
-
backgroundColor:
|
|
753
|
+
backgroundColor: slotColor(slot),
|
|
365
754
|
borderRadius: 1,
|
|
366
755
|
minWidth: 4,
|
|
367
|
-
height: "100%"
|
|
756
|
+
height: "100%",
|
|
757
|
+
// The container's uniform 2px gap covers rep spacing; a wide slot adds
|
|
758
|
+
// only the EXTRA (WIDE_GAP − REP_GAP) so the notch totals WIDE_GAP.
|
|
759
|
+
marginLeft: Math.max(0, slot.leadingGap - REP_GAP),
|
|
760
|
+
...slot.kind === "continue" ? { borderWidth: 1, borderColor: CONTINUE_OUTLINE } : {}
|
|
761
|
+
},
|
|
762
|
+
accessibilityElementsHidden: true,
|
|
763
|
+
testID: slot.kind === "rep" ? `velocity-bar-${i}` : `velocity-slot-${slot.kind}`
|
|
764
|
+
},
|
|
765
|
+
i
|
|
766
|
+
))
|
|
767
|
+
}
|
|
768
|
+
);
|
|
769
|
+
}
|
|
770
|
+
if (!framed) {
|
|
771
|
+
const { style: externalStyle, ...restProps } = props;
|
|
772
|
+
return /* @__PURE__ */ jsx(
|
|
773
|
+
View,
|
|
774
|
+
{
|
|
775
|
+
className,
|
|
776
|
+
style: [
|
|
777
|
+
{ flexDirection: "row", height, gap: REP_GAP, alignItems: "flex-end" },
|
|
778
|
+
externalStyle
|
|
779
|
+
],
|
|
780
|
+
accessibilityRole: "image",
|
|
781
|
+
accessibilityLabel: miniLabel,
|
|
782
|
+
testID: "velocity-strip-compact",
|
|
783
|
+
...restProps,
|
|
784
|
+
children: slots.map((slot, i) => /* @__PURE__ */ jsx(
|
|
785
|
+
View,
|
|
786
|
+
{
|
|
787
|
+
style: {
|
|
788
|
+
flex: 1,
|
|
789
|
+
minWidth: 4,
|
|
790
|
+
height: bareSlotHeight(slot, height, scaleDenom),
|
|
791
|
+
// Top-rounded to match the framed chart's bars (the "rounded tops from
|
|
792
|
+
// the numbers one"); square bottoms since bars sit on the baseline.
|
|
793
|
+
borderTopLeftRadius: 2,
|
|
794
|
+
borderTopRightRadius: 2,
|
|
795
|
+
backgroundColor: slotColor(slot),
|
|
796
|
+
marginLeft: Math.max(0, slot.leadingGap - REP_GAP),
|
|
797
|
+
...slot.kind === "continue" ? { borderWidth: 1, borderColor: CONTINUE_OUTLINE } : {}
|
|
368
798
|
},
|
|
369
799
|
accessibilityElementsHidden: true,
|
|
370
|
-
testID: `velocity-bar-${i}`
|
|
800
|
+
testID: slot.kind === "rep" ? `velocity-bar-${i}` : `velocity-slot-${slot.kind}`
|
|
371
801
|
},
|
|
372
802
|
i
|
|
373
803
|
))
|
|
374
804
|
}
|
|
375
805
|
);
|
|
376
806
|
}
|
|
377
|
-
const stripLabel = `Velocity chart for set, ${
|
|
807
|
+
const stripLabel = set ? `${setAccessibilityLabel(set, repCount)}, tap to ${expanded ? "collapse" : "expand"}` : `Velocity chart for set, ${repCount} reps, tap to ${expanded ? "collapse" : "expand"}`;
|
|
378
808
|
const hasInteractiveContainer = onToggle != null;
|
|
379
809
|
const hasInteractiveReps = onRepPress != null && expanded;
|
|
810
|
+
const setBars = set != null && slots.map((slot, i) => {
|
|
811
|
+
const isStraightRep = set.type === "straight" && slot.kind === "rep";
|
|
812
|
+
const heightPct = isStraightRep ? scaleDenom > 0 ? Math.round((slot.velocity ?? 0) / scaleDenom * 100) : 0 : set.type === "straight" ? EXPANDED_TODO_STUB_PCT : EXPANDED_ENCODED_PCT;
|
|
813
|
+
return /* @__PURE__ */ jsx(
|
|
814
|
+
View,
|
|
815
|
+
{
|
|
816
|
+
style: {
|
|
817
|
+
flex: 1,
|
|
818
|
+
height: "100%",
|
|
819
|
+
justifyContent: "flex-end",
|
|
820
|
+
marginLeft: slot.leadingGap
|
|
821
|
+
},
|
|
822
|
+
children: /* @__PURE__ */ jsx(
|
|
823
|
+
View,
|
|
824
|
+
{
|
|
825
|
+
style: {
|
|
826
|
+
height: `${heightPct}%`,
|
|
827
|
+
minHeight: 2,
|
|
828
|
+
borderTopLeftRadius: 2,
|
|
829
|
+
borderTopRightRadius: 2,
|
|
830
|
+
backgroundColor: slotColor(slot),
|
|
831
|
+
...slot.kind === "continue" ? { borderWidth: 1, borderColor: CONTINUE_OUTLINE } : {}
|
|
832
|
+
},
|
|
833
|
+
testID: slot.kind === "rep" ? `velocity-bar-${i}` : `velocity-slot-${slot.kind}`,
|
|
834
|
+
accessibilityRole: "image",
|
|
835
|
+
accessibilityLabel: slotAccessibilityLabel(slot, i)
|
|
836
|
+
}
|
|
837
|
+
)
|
|
838
|
+
},
|
|
839
|
+
i
|
|
840
|
+
);
|
|
841
|
+
});
|
|
380
842
|
const stripContent = /* @__PURE__ */ jsxs(
|
|
381
843
|
Animated.View,
|
|
382
844
|
{
|
|
@@ -389,7 +851,9 @@ function VelocityStrip({
|
|
|
389
851
|
borderRadius: expanded ? 6 : 2,
|
|
390
852
|
paddingTop: expanded ? 16 : 0,
|
|
391
853
|
paddingBottom: expanded ? 24 : 0,
|
|
392
|
-
|
|
854
|
+
// No horizontal inset — the framed chart's bars span full width, matching
|
|
855
|
+
// the bare spotlight and mini. Numbers/info sit in the vertical padding.
|
|
856
|
+
paddingHorizontal: 0,
|
|
393
857
|
paddingVertical: expanded ? void 0 : 8,
|
|
394
858
|
overflow: expanded ? "visible" : "hidden"
|
|
395
859
|
}
|
|
@@ -399,46 +863,65 @@ function VelocityStrip({
|
|
|
399
863
|
testID: "velocity-strip",
|
|
400
864
|
...props,
|
|
401
865
|
children: [
|
|
402
|
-
/* @__PURE__ */ jsx(
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
)
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
866
|
+
/* @__PURE__ */ jsx(View, { style: { flexDirection: "row", flex: 1, gap: set ? 0 : 2, alignItems: "flex-end" }, children: set ? setBars : doneVelocities.map((v, i) => {
|
|
867
|
+
const barBackground = barColorFor(v);
|
|
868
|
+
const barHeightPct = scaleDenom > 0 ? Math.round(v / scaleDenom * 100) : 0;
|
|
869
|
+
const isLive = liveRepIndex === i;
|
|
870
|
+
const liveLabelSuffix = isLive ? isNewPeak ? ", latest rep, new set peak" : ", latest rep" : "";
|
|
871
|
+
const barStyle = expanded ? {
|
|
872
|
+
height: `${barHeightPct}%`,
|
|
873
|
+
minHeight: 2,
|
|
874
|
+
borderTopLeftRadius: 2,
|
|
875
|
+
borderTopRightRadius: 2,
|
|
876
|
+
backgroundColor: barBackground
|
|
877
|
+
} : {
|
|
878
|
+
minHeight: "100%",
|
|
879
|
+
borderTopLeftRadius: 2,
|
|
880
|
+
borderTopRightRadius: 2,
|
|
881
|
+
borderBottomLeftRadius: 0,
|
|
882
|
+
borderBottomRightRadius: 0,
|
|
883
|
+
backgroundColor: barBackground
|
|
884
|
+
};
|
|
885
|
+
const barInner = isLive ? /* @__PURE__ */ jsx(
|
|
886
|
+
Animated.View,
|
|
887
|
+
{
|
|
888
|
+
style: [barStyle, { transform: [{ scale: liveScale }] }],
|
|
889
|
+
testID: `velocity-bar-${i}`,
|
|
890
|
+
accessibilityRole: "image",
|
|
891
|
+
accessibilityLabel: `Rep ${i + 1}: ${formatVelocity(v)} meters per second${liveLabelSuffix}`
|
|
892
|
+
}
|
|
893
|
+
) : /* @__PURE__ */ jsx(
|
|
894
|
+
View,
|
|
895
|
+
{
|
|
896
|
+
style: barStyle,
|
|
897
|
+
testID: `velocity-bar-${i}`,
|
|
898
|
+
accessibilityRole: "image",
|
|
899
|
+
accessibilityLabel: `Rep ${i + 1}: ${formatVelocity(v)} meters per second`
|
|
900
|
+
}
|
|
901
|
+
);
|
|
902
|
+
const bar = /* @__PURE__ */ jsxs(
|
|
903
|
+
View,
|
|
904
|
+
{
|
|
905
|
+
style: {
|
|
906
|
+
flex: 1,
|
|
907
|
+
height: "100%",
|
|
908
|
+
justifyContent: "flex-end",
|
|
909
|
+
position: "relative"
|
|
910
|
+
},
|
|
911
|
+
children: [
|
|
912
|
+
expanded && showNumbers && /* @__PURE__ */ jsx(
|
|
913
|
+
Animated.View,
|
|
914
|
+
{
|
|
915
|
+
style: {
|
|
916
|
+
opacity: labelOpacity,
|
|
917
|
+
alignItems: "center",
|
|
918
|
+
position: "absolute",
|
|
919
|
+
top: -13,
|
|
920
|
+
left: 0,
|
|
921
|
+
right: 0
|
|
922
|
+
},
|
|
923
|
+
accessibilityElementsHidden: true,
|
|
924
|
+
children: /* @__PURE__ */ jsx(
|
|
442
925
|
Text,
|
|
443
926
|
{
|
|
444
927
|
className: "text-text-secondary",
|
|
@@ -446,30 +929,30 @@ function VelocityStrip({
|
|
|
446
929
|
testID: `velocity-label-${i}`,
|
|
447
930
|
children: formatVelocity(v)
|
|
448
931
|
}
|
|
449
|
-
)
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
})
|
|
932
|
+
)
|
|
933
|
+
}
|
|
934
|
+
),
|
|
935
|
+
barInner
|
|
936
|
+
]
|
|
937
|
+
},
|
|
938
|
+
i
|
|
939
|
+
);
|
|
940
|
+
if (onRepPress && expanded) {
|
|
941
|
+
return /* @__PURE__ */ jsx(
|
|
942
|
+
Pressable,
|
|
943
|
+
{
|
|
944
|
+
style: { flex: 1, height: "100%" },
|
|
945
|
+
onPress: () => onRepPress(i, v),
|
|
946
|
+
accessibilityRole: "button",
|
|
947
|
+
accessibilityLabel: `Rep ${i + 1}: ${formatVelocity(v)} meters per second, tap for details`,
|
|
948
|
+
testID: `velocity-bar-pressable-${i}`,
|
|
949
|
+
children: bar
|
|
950
|
+
},
|
|
951
|
+
i
|
|
952
|
+
);
|
|
471
953
|
}
|
|
472
|
-
|
|
954
|
+
return bar;
|
|
955
|
+
}) }),
|
|
473
956
|
expanded && showInfo && /* @__PURE__ */ jsxs(
|
|
474
957
|
Animated.View,
|
|
475
958
|
{
|
|
@@ -645,17 +1128,49 @@ function SvgIcon({
|
|
|
645
1128
|
}
|
|
646
1129
|
|
|
647
1130
|
// src/components/icons/icons.tsx
|
|
648
|
-
function
|
|
1131
|
+
function StarIcon(props) {
|
|
1132
|
+
return /* @__PURE__ */ jsx(SvgIcon, { ...props, children: /* @__PURE__ */ jsx("path", { d: "M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.123 2.123 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.123 2.123 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.122 2.122 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.122 2.122 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.122 2.122 0 0 0 1.597-1.16z" }) });
|
|
1133
|
+
}
|
|
1134
|
+
function ScaleIcon(props) {
|
|
649
1135
|
return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
|
|
650
|
-
/* @__PURE__ */ jsx("path", { d: "
|
|
651
|
-
/* @__PURE__ */ jsx("path", { d: "
|
|
652
|
-
/* @__PURE__ */ jsx("path", { d: "
|
|
653
|
-
/* @__PURE__ */ jsx("path", { d: "
|
|
654
|
-
/* @__PURE__ */ jsx("path", { d: "
|
|
1136
|
+
/* @__PURE__ */ jsx("path", { d: "m16 16 3-8 3 8c-.87.65-1.92 1-3 1s-2.13-.35-3-1Z" }),
|
|
1137
|
+
/* @__PURE__ */ jsx("path", { d: "m2 16 3-8 3 8c-.87.65-1.92 1-3 1s-2.13-.35-3-1Z" }),
|
|
1138
|
+
/* @__PURE__ */ jsx("path", { d: "M7 21h10" }),
|
|
1139
|
+
/* @__PURE__ */ jsx("path", { d: "M12 3v18" }),
|
|
1140
|
+
/* @__PURE__ */ jsx("path", { d: "M3 7h2c2 0 5-1 7-2 2 1 5 2 7 2h2" })
|
|
655
1141
|
] });
|
|
656
1142
|
}
|
|
657
|
-
function
|
|
658
|
-
return /* @__PURE__ */
|
|
1143
|
+
function AlertTriangleIcon(props) {
|
|
1144
|
+
return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
|
|
1145
|
+
/* @__PURE__ */ jsx("path", { d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z" }),
|
|
1146
|
+
/* @__PURE__ */ jsx("path", { d: "M12 9v4" }),
|
|
1147
|
+
/* @__PURE__ */ jsx("path", { d: "M12 17h.01" })
|
|
1148
|
+
] });
|
|
1149
|
+
}
|
|
1150
|
+
function TrendingDownIcon(props) {
|
|
1151
|
+
return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
|
|
1152
|
+
/* @__PURE__ */ jsx("path", { d: "M16 17h6v-6" }),
|
|
1153
|
+
/* @__PURE__ */ jsx("path", { d: "m22 17-8.5-8.5-5 5L2 7" })
|
|
1154
|
+
] });
|
|
1155
|
+
}
|
|
1156
|
+
function CircleSlashIcon(props) {
|
|
1157
|
+
return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
|
|
1158
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
|
|
1159
|
+
/* @__PURE__ */ jsx("path", { d: "m9 15 6-6" })
|
|
1160
|
+
] });
|
|
1161
|
+
}
|
|
1162
|
+
function AwardIcon(props) {
|
|
1163
|
+
return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
|
|
1164
|
+
/* @__PURE__ */ jsx("path", { d: "m15.477 12.89 1.515 8.526a.5.5 0 0 1-.81.47l-3.58-2.687a1 1 0 0 0-1.197 0l-3.586 2.686a.5.5 0 0 1-.81-.469l1.514-8.526" }),
|
|
1165
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "8", r: "6" })
|
|
1166
|
+
] });
|
|
1167
|
+
}
|
|
1168
|
+
function InfoIcon(props) {
|
|
1169
|
+
return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
|
|
1170
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
|
|
1171
|
+
/* @__PURE__ */ jsx("path", { d: "M12 16v-4" }),
|
|
1172
|
+
/* @__PURE__ */ jsx("path", { d: "M12 8h.01" })
|
|
1173
|
+
] });
|
|
659
1174
|
}
|
|
660
1175
|
var baseBadgeSizeConfig = {
|
|
661
1176
|
sm: { fontSize: 9, paddingH: 6, paddingV: 2, iconSize: 10 },
|
|
@@ -758,6 +1273,9 @@ var semanticColorsLight = {
|
|
|
758
1273
|
"status-success-light": primitiveRamps.green[200],
|
|
759
1274
|
"status-success-dark": primitiveRamps.green[600],
|
|
760
1275
|
"status-success-subtle": primitiveRamps.green[50],
|
|
1276
|
+
// Live-session accent — its OWN role, decoupled from success so the two can diverge
|
|
1277
|
+
"status-live": primitiveRamps.green[300],
|
|
1278
|
+
"status-live-muted": primitiveRamps.green[500],
|
|
761
1279
|
"status-error": primitiveRamps.red[600],
|
|
762
1280
|
"status-error-light": primitiveRamps.red[500],
|
|
763
1281
|
"status-error-dark": primitiveRamps.red[700],
|
|
@@ -891,6 +1409,9 @@ var semanticColorsDark = {
|
|
|
891
1409
|
"status-success-light": primitiveRamps.green[200],
|
|
892
1410
|
"status-success-dark": primitiveRamps.green[600],
|
|
893
1411
|
"status-success-subtle": "rgba(46, 213, 115, 0.12)",
|
|
1412
|
+
// Live-session accent — its OWN role, decoupled from success so the two can diverge
|
|
1413
|
+
"status-live": primitiveRamps.green[300],
|
|
1414
|
+
"status-live-muted": primitiveRamps.green[500],
|
|
894
1415
|
"status-error": primitiveRamps.red[600],
|
|
895
1416
|
"status-error-light": primitiveRamps.red[500],
|
|
896
1417
|
"status-error-dark": primitiveRamps.red[700],
|
|
@@ -995,106 +1516,20 @@ function getSemanticColors(mode) {
|
|
|
995
1516
|
return mode === "dark" ? semanticColorsDark : semanticColorsLight;
|
|
996
1517
|
}
|
|
997
1518
|
|
|
998
|
-
// src/components/custom/Workout/
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
const textColor = isPr ? getSemanticColors("dark")["brand-primary"] : "#9CA3AF";
|
|
1013
|
-
const iconColor = textColor;
|
|
1014
|
-
const repMaxClause = reps != null ? `${reps} rep max` : "Estimated one rep max";
|
|
1015
|
-
const deltaLabel = delta != null ? `, ${delta >= 0 ? "+" : ""}${delta}% change` : "";
|
|
1016
|
-
const fullLabel = `${repMaxClause}: ${value} ${unit}${deltaLabel}`;
|
|
1017
|
-
return /* @__PURE__ */ jsxs(
|
|
1018
|
-
BaseBadge,
|
|
1019
|
-
{
|
|
1020
|
-
variant: isPr ? "pr" : "plain",
|
|
1021
|
-
size,
|
|
1022
|
-
onPress,
|
|
1023
|
-
className,
|
|
1024
|
-
icon: showIcon ? /* @__PURE__ */ jsx(DumbbellIcon, { size: config.iconSize, color: iconColor, strokeWidth: 2 }) : void 0,
|
|
1025
|
-
accessibilityLabel: fullLabel,
|
|
1026
|
-
testID: "weight-badge",
|
|
1027
|
-
...props,
|
|
1028
|
-
children: [
|
|
1029
|
-
/* @__PURE__ */ jsxs(
|
|
1030
|
-
Text,
|
|
1031
|
-
{
|
|
1032
|
-
style: {
|
|
1033
|
-
fontFamily: '"Space Grotesk", sans-serif',
|
|
1034
|
-
fontWeight: "600",
|
|
1035
|
-
fontSize: config.fontSize,
|
|
1036
|
-
color: textColor
|
|
1037
|
-
},
|
|
1038
|
-
children: [
|
|
1039
|
-
isPr ? "\u2733 " : "",
|
|
1040
|
-
value,
|
|
1041
|
-
" ",
|
|
1042
|
-
unit
|
|
1043
|
-
]
|
|
1044
|
-
}
|
|
1045
|
-
),
|
|
1046
|
-
reps != null && /* @__PURE__ */ jsxs(
|
|
1047
|
-
Text,
|
|
1048
|
-
{
|
|
1049
|
-
style: {
|
|
1050
|
-
fontFamily: '"Space Grotesk", sans-serif',
|
|
1051
|
-
fontWeight: "600",
|
|
1052
|
-
fontSize: config.fontSize,
|
|
1053
|
-
marginLeft: 2,
|
|
1054
|
-
color: textColor,
|
|
1055
|
-
opacity: 0.7
|
|
1056
|
-
},
|
|
1057
|
-
testID: "weight-badge-repmax",
|
|
1058
|
-
children: [
|
|
1059
|
-
reps,
|
|
1060
|
-
"RM"
|
|
1061
|
-
]
|
|
1062
|
-
}
|
|
1063
|
-
),
|
|
1064
|
-
delta != null && /* @__PURE__ */ jsxs(
|
|
1065
|
-
Text,
|
|
1066
|
-
{
|
|
1067
|
-
style: {
|
|
1068
|
-
fontFamily: '"Space Grotesk", sans-serif',
|
|
1069
|
-
fontSize: config.fontSize,
|
|
1070
|
-
marginLeft: 4,
|
|
1071
|
-
color: delta >= 0 ? getSemanticColors("dark")["result-improve"] : getSemanticColors("dark")["result-degrade"]
|
|
1072
|
-
},
|
|
1073
|
-
testID: "weight-badge-delta",
|
|
1074
|
-
children: [
|
|
1075
|
-
delta >= 0 ? "+" : "",
|
|
1076
|
-
delta,
|
|
1077
|
-
"%"
|
|
1078
|
-
]
|
|
1079
|
-
}
|
|
1080
|
-
)
|
|
1081
|
-
]
|
|
1082
|
-
}
|
|
1083
|
-
);
|
|
1084
|
-
}
|
|
1085
|
-
var BRAND_PRIMARY = getSemanticColors("dark")["brand-primary"];
|
|
1086
|
-
var typeLabels = {
|
|
1087
|
-
e1rm: "PR e1RM",
|
|
1088
|
-
weight: "PR Weight",
|
|
1089
|
-
reps: "PR Reps",
|
|
1090
|
-
volume: "PR Volume",
|
|
1091
|
-
velocity: "PR Velocity"
|
|
1092
|
-
};
|
|
1093
|
-
function PrBadge({
|
|
1094
|
-
type = "e1rm",
|
|
1095
|
-
label: labelProp,
|
|
1096
|
-
compact = false,
|
|
1097
|
-
animate = true,
|
|
1519
|
+
// src/components/custom/Workout/PrBadge.tsx
|
|
1520
|
+
var BRAND_PRIMARY = getSemanticColors("dark")["brand-primary"];
|
|
1521
|
+
var typeLabels = {
|
|
1522
|
+
e1rm: "PR e1RM",
|
|
1523
|
+
weight: "PR Weight",
|
|
1524
|
+
reps: "PR Reps",
|
|
1525
|
+
volume: "PR Volume",
|
|
1526
|
+
velocity: "PR Velocity"
|
|
1527
|
+
};
|
|
1528
|
+
function PrBadge({
|
|
1529
|
+
type = "e1rm",
|
|
1530
|
+
label: labelProp,
|
|
1531
|
+
compact = false,
|
|
1532
|
+
animate = true,
|
|
1098
1533
|
className,
|
|
1099
1534
|
...props
|
|
1100
1535
|
}) {
|
|
@@ -1165,6 +1600,204 @@ function PrBadge({
|
|
|
1165
1600
|
}
|
|
1166
1601
|
return badge;
|
|
1167
1602
|
}
|
|
1603
|
+
var DARK = getSemanticColors("dark");
|
|
1604
|
+
var TEXT_ACTIVE = DARK["text-primary"];
|
|
1605
|
+
var TEXT_MUTED = DARK["text-secondary"];
|
|
1606
|
+
var COL = { set: 36, reps: 44, load: 56, rpe: 36 };
|
|
1607
|
+
var cellText = { fontFamily: "Inter, sans-serif", fontSize: 13, fontWeight: "600" };
|
|
1608
|
+
var typeBadge = {
|
|
1609
|
+
fontSize: 10,
|
|
1610
|
+
fontWeight: "700",
|
|
1611
|
+
fontFamily: "Inter, sans-serif",
|
|
1612
|
+
color: WORKOUT_TOKENS.scale.orange,
|
|
1613
|
+
backgroundColor: "rgba(255,165,2,0.12)",
|
|
1614
|
+
paddingVertical: 1,
|
|
1615
|
+
paddingHorizontal: 5,
|
|
1616
|
+
borderRadius: 3,
|
|
1617
|
+
overflow: "hidden"
|
|
1618
|
+
};
|
|
1619
|
+
function displayReps(set) {
|
|
1620
|
+
return set.state === "done" ? set.reps : set.target.reps;
|
|
1621
|
+
}
|
|
1622
|
+
function displayWeight(set) {
|
|
1623
|
+
return set.state === "done" ? set.weight : set.target.weight;
|
|
1624
|
+
}
|
|
1625
|
+
function plannedReps(set) {
|
|
1626
|
+
if (set.state === "done") return set.velocities.length;
|
|
1627
|
+
if (set.state === "live") return set.target.reps;
|
|
1628
|
+
return set.planned ?? set.target.reps;
|
|
1629
|
+
}
|
|
1630
|
+
function accessibilityLabel(set) {
|
|
1631
|
+
const base = `Set ${set.setNumber}: ${displayReps(set)} reps at ${roundWeight(displayWeight(set))} ${set.unit}`;
|
|
1632
|
+
if (set.state === "live") return `${base}, in progress`;
|
|
1633
|
+
if (set.state === "todo") return `${base}, upcoming`;
|
|
1634
|
+
return base;
|
|
1635
|
+
}
|
|
1636
|
+
function Cell({
|
|
1637
|
+
width,
|
|
1638
|
+
children,
|
|
1639
|
+
testID
|
|
1640
|
+
}) {
|
|
1641
|
+
return /* @__PURE__ */ jsx(View, { style: { width, alignItems: "center", justifyContent: "center" }, testID, children });
|
|
1642
|
+
}
|
|
1643
|
+
function RowStrip({ set }) {
|
|
1644
|
+
const zones = set.velocityZones;
|
|
1645
|
+
if (set.state === "live") {
|
|
1646
|
+
return /* @__PURE__ */ jsx(
|
|
1647
|
+
VelocityStrip,
|
|
1648
|
+
{
|
|
1649
|
+
variant: "expanded",
|
|
1650
|
+
showNumbers: false,
|
|
1651
|
+
showInfo: false,
|
|
1652
|
+
height: 24,
|
|
1653
|
+
scale: "fixed",
|
|
1654
|
+
set: { type: "straight", velocities: set.velocities, planned: set.target.reps },
|
|
1655
|
+
zones
|
|
1656
|
+
}
|
|
1657
|
+
);
|
|
1658
|
+
}
|
|
1659
|
+
const velocities = set.state === "done" ? set.velocities : [];
|
|
1660
|
+
return /* @__PURE__ */ jsx(
|
|
1661
|
+
VelocityStrip,
|
|
1662
|
+
{
|
|
1663
|
+
variant: "mini",
|
|
1664
|
+
set: { type: "straight", velocities, planned: plannedReps(set) },
|
|
1665
|
+
zones
|
|
1666
|
+
}
|
|
1667
|
+
);
|
|
1668
|
+
}
|
|
1669
|
+
function SetRow(set) {
|
|
1670
|
+
const live = set.state === "live";
|
|
1671
|
+
const valueColor = live ? TEXT_ACTIVE : TEXT_MUTED;
|
|
1672
|
+
const rpe = set.state === "todo" ? null : set.rpe;
|
|
1673
|
+
return /* @__PURE__ */ jsxs(
|
|
1674
|
+
View,
|
|
1675
|
+
{
|
|
1676
|
+
style: { paddingVertical: 6, paddingHorizontal: 8 },
|
|
1677
|
+
accessibilityLabel: accessibilityLabel(set),
|
|
1678
|
+
testID: "set-row",
|
|
1679
|
+
children: [
|
|
1680
|
+
/* @__PURE__ */ jsxs(View, { className: "flex-row items-center", style: { justifyContent: "space-between" }, children: [
|
|
1681
|
+
/* @__PURE__ */ jsx(Cell, { width: COL.set, testID: "set-row-set-number", children: set.setType ? /* @__PURE__ */ jsx(Text, { style: typeBadge, testID: "set-row-type-badge", children: set.setType }) : /* @__PURE__ */ jsx(Text, { style: { ...cellText, color: valueColor, fontWeight: live ? "700" : "600" }, children: set.setNumber }) }),
|
|
1682
|
+
/* @__PURE__ */ jsx(Cell, { width: COL.reps, testID: "set-row-reps", children: /* @__PURE__ */ jsx(Text, { style: { ...cellText, color: valueColor }, children: displayReps(set) }) }),
|
|
1683
|
+
/* @__PURE__ */ jsx(Cell, { width: COL.load, testID: "set-row-weight", children: /* @__PURE__ */ jsx(Text, { style: { ...cellText, color: valueColor }, children: roundWeight(displayWeight(set)) }) }),
|
|
1684
|
+
/* @__PURE__ */ jsx(Cell, { width: COL.rpe, testID: "set-row-rpe", children: /* @__PURE__ */ jsx(Text, { style: { ...cellText, color: TEXT_MUTED }, children: rpe != null ? roundRpe(rpe) : "\u2014" }) })
|
|
1685
|
+
] }),
|
|
1686
|
+
/* @__PURE__ */ jsx(View, { style: { marginTop: live ? 6 : 4 }, testID: "set-row-strip", children: /* @__PURE__ */ jsx(RowStrip, { set }) })
|
|
1687
|
+
]
|
|
1688
|
+
}
|
|
1689
|
+
);
|
|
1690
|
+
}
|
|
1691
|
+
var PREV_COLUMN = { label: "PREV", width: void 0 };
|
|
1692
|
+
function buildColumns(unit, showPrevious) {
|
|
1693
|
+
return [
|
|
1694
|
+
{ label: "SET", width: 36 },
|
|
1695
|
+
...showPrevious ? [PREV_COLUMN] : [],
|
|
1696
|
+
{ label: "REPS", width: 44 },
|
|
1697
|
+
{ label: unit.toUpperCase(), width: 56 },
|
|
1698
|
+
{ label: "RPE", width: 36 }
|
|
1699
|
+
];
|
|
1700
|
+
}
|
|
1701
|
+
function SetTableHeader({
|
|
1702
|
+
unit = "lbs",
|
|
1703
|
+
showPrevious = true,
|
|
1704
|
+
testID = "table-header"
|
|
1705
|
+
}) {
|
|
1706
|
+
const columns = buildColumns(unit, showPrevious);
|
|
1707
|
+
return /* @__PURE__ */ jsx(
|
|
1708
|
+
View,
|
|
1709
|
+
{
|
|
1710
|
+
className: "flex-row",
|
|
1711
|
+
style: {
|
|
1712
|
+
paddingVertical: 8,
|
|
1713
|
+
paddingHorizontal: 8,
|
|
1714
|
+
paddingBottom: 4,
|
|
1715
|
+
...showPrevious ? {} : { justifyContent: "space-between" }
|
|
1716
|
+
},
|
|
1717
|
+
testID,
|
|
1718
|
+
children: columns.map(({ label, width }) => {
|
|
1719
|
+
const isFlex = width === void 0;
|
|
1720
|
+
return /* @__PURE__ */ jsx(
|
|
1721
|
+
View,
|
|
1722
|
+
{
|
|
1723
|
+
style: {
|
|
1724
|
+
...isFlex ? { minWidth: 44 } : { width, flexShrink: 1 },
|
|
1725
|
+
alignItems: "center",
|
|
1726
|
+
justifyContent: "center",
|
|
1727
|
+
gap: 8
|
|
1728
|
+
},
|
|
1729
|
+
...isFlex ? { className: "flex-1" } : {},
|
|
1730
|
+
children: /* @__PURE__ */ jsx(
|
|
1731
|
+
Text,
|
|
1732
|
+
{
|
|
1733
|
+
className: "text-text-tertiary",
|
|
1734
|
+
style: {
|
|
1735
|
+
fontSize: 10,
|
|
1736
|
+
fontWeight: "600",
|
|
1737
|
+
fontFamily: "Inter, sans-serif",
|
|
1738
|
+
textTransform: "uppercase",
|
|
1739
|
+
letterSpacing: 0.8
|
|
1740
|
+
},
|
|
1741
|
+
children: label
|
|
1742
|
+
}
|
|
1743
|
+
)
|
|
1744
|
+
},
|
|
1745
|
+
label
|
|
1746
|
+
);
|
|
1747
|
+
})
|
|
1748
|
+
}
|
|
1749
|
+
);
|
|
1750
|
+
}
|
|
1751
|
+
var METRIC_FONT = "Inter, sans-serif";
|
|
1752
|
+
function MetricCell({ children, color, fontSize = 11 }) {
|
|
1753
|
+
return /* @__PURE__ */ jsx(
|
|
1754
|
+
Text,
|
|
1755
|
+
{
|
|
1756
|
+
style: {
|
|
1757
|
+
fontFamily: METRIC_FONT,
|
|
1758
|
+
fontSize,
|
|
1759
|
+
color,
|
|
1760
|
+
fontWeight: "600",
|
|
1761
|
+
letterSpacing: 1
|
|
1762
|
+
},
|
|
1763
|
+
children
|
|
1764
|
+
}
|
|
1765
|
+
);
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1768
|
+
// src/components/custom/Workout/SetsRepsLoad.tsx
|
|
1769
|
+
var t = getSemanticColors("dark");
|
|
1770
|
+
var TIMES = "\xD7";
|
|
1771
|
+
var AT = "@";
|
|
1772
|
+
function SetsRepsLoad({
|
|
1773
|
+
sets,
|
|
1774
|
+
reps,
|
|
1775
|
+
load,
|
|
1776
|
+
unit = "lb",
|
|
1777
|
+
className,
|
|
1778
|
+
...props
|
|
1779
|
+
}) {
|
|
1780
|
+
const value = t["text-primary"];
|
|
1781
|
+
const sep = t["text-tertiary"];
|
|
1782
|
+
return /* @__PURE__ */ jsxs(
|
|
1783
|
+
View,
|
|
1784
|
+
{
|
|
1785
|
+
className,
|
|
1786
|
+
style: { flexDirection: "row", alignItems: "baseline" },
|
|
1787
|
+
accessibilityLabel: `${sets} sets of ${reps} reps at ${load} ${unit}`,
|
|
1788
|
+
testID: "sets-reps-load",
|
|
1789
|
+
...props,
|
|
1790
|
+
children: [
|
|
1791
|
+
/* @__PURE__ */ jsx(MetricCell, { color: value, children: sets }),
|
|
1792
|
+
/* @__PURE__ */ jsx(MetricCell, { color: sep, children: ` ${TIMES} ` }),
|
|
1793
|
+
/* @__PURE__ */ jsx(MetricCell, { color: value, children: reps }),
|
|
1794
|
+
/* @__PURE__ */ jsx(MetricCell, { color: sep, children: ` ${AT} ` }),
|
|
1795
|
+
/* @__PURE__ */ jsx(MetricCell, { color: value, children: load }),
|
|
1796
|
+
/* @__PURE__ */ jsx(MetricCell, { color: sep, children: ` ${unit}` })
|
|
1797
|
+
]
|
|
1798
|
+
}
|
|
1799
|
+
);
|
|
1800
|
+
}
|
|
1168
1801
|
function hexToRgb(hex) {
|
|
1169
1802
|
const cleaned = hex.replace("#", "");
|
|
1170
1803
|
let r, g, b;
|
|
@@ -1474,7 +2107,7 @@ function alpha(color, opacity) {
|
|
|
1474
2107
|
}
|
|
1475
2108
|
return color;
|
|
1476
2109
|
}
|
|
1477
|
-
var
|
|
2110
|
+
var t2 = getSemanticColors("dark");
|
|
1478
2111
|
var TEMPO_PACING = {
|
|
1479
2112
|
behindThresholdPct: 0.15,
|
|
1480
2113
|
minPhaseDurationMs: 500
|
|
@@ -1490,20 +2123,20 @@ function getTempoFillPct(elapsedMs, targetMs) {
|
|
|
1490
2123
|
return Math.min(100, elapsedMs / targetMs * 100);
|
|
1491
2124
|
}
|
|
1492
2125
|
({
|
|
1493
|
-
concentric: { color:
|
|
1494
|
-
hold: { color:
|
|
1495
|
-
eccentric: { color:
|
|
2126
|
+
concentric: { color: t2["status-success"]},
|
|
2127
|
+
hold: { color: t2["brand-primary"]},
|
|
2128
|
+
eccentric: { color: t2["status-warning"]}
|
|
1496
2129
|
});
|
|
1497
2130
|
|
|
1498
2131
|
// src/components/custom/Workout/TempoDisplay.tsx
|
|
1499
|
-
var
|
|
2132
|
+
var t3 = getSemanticColors("dark");
|
|
1500
2133
|
var INTER = "Inter, sans-serif";
|
|
1501
2134
|
var TEXT_TERTIARY = "#6B7280";
|
|
1502
|
-
var STATUS_ERROR =
|
|
2135
|
+
var STATUS_ERROR = t3["status-error"];
|
|
1503
2136
|
var phaseColors = {
|
|
1504
|
-
eccentric:
|
|
2137
|
+
eccentric: t3["status-warning"],
|
|
1505
2138
|
pauseBottom: TEXT_TERTIARY,
|
|
1506
|
-
concentric:
|
|
2139
|
+
concentric: t3["status-success"],
|
|
1507
2140
|
pauseTop: TEXT_TERTIARY,
|
|
1508
2141
|
dash: TEXT_TERTIARY
|
|
1509
2142
|
};
|
|
@@ -1512,34 +2145,10 @@ function TempoValue({
|
|
|
1512
2145
|
color,
|
|
1513
2146
|
fontSize
|
|
1514
2147
|
}) {
|
|
1515
|
-
return /* @__PURE__ */ jsx(
|
|
1516
|
-
Text,
|
|
1517
|
-
{
|
|
1518
|
-
style: {
|
|
1519
|
-
fontFamily: INTER,
|
|
1520
|
-
fontSize,
|
|
1521
|
-
color,
|
|
1522
|
-
fontWeight: "600",
|
|
1523
|
-
letterSpacing: 1
|
|
1524
|
-
},
|
|
1525
|
-
children: value
|
|
1526
|
-
}
|
|
1527
|
-
);
|
|
2148
|
+
return /* @__PURE__ */ jsx(MetricCell, { color, fontSize, children: value });
|
|
1528
2149
|
}
|
|
1529
2150
|
function TempoSeparator({ color, fontSize }) {
|
|
1530
|
-
return /* @__PURE__ */ jsx(
|
|
1531
|
-
Text,
|
|
1532
|
-
{
|
|
1533
|
-
style: {
|
|
1534
|
-
fontFamily: INTER,
|
|
1535
|
-
fontSize,
|
|
1536
|
-
color,
|
|
1537
|
-
fontWeight: "600",
|
|
1538
|
-
letterSpacing: 1
|
|
1539
|
-
},
|
|
1540
|
-
children: "-"
|
|
1541
|
-
}
|
|
1542
|
-
);
|
|
2151
|
+
return /* @__PURE__ */ jsx(MetricCell, { color, fontSize, children: "-" });
|
|
1543
2152
|
}
|
|
1544
2153
|
var LIVE_PHASES = [
|
|
1545
2154
|
{ key: "eccentric", color: phaseColors.eccentric },
|
|
@@ -1601,6 +2210,7 @@ function TempoDisplay({
|
|
|
1601
2210
|
tempo,
|
|
1602
2211
|
size = "md",
|
|
1603
2212
|
colored = true,
|
|
2213
|
+
showLabel = true,
|
|
1604
2214
|
showInfo = true,
|
|
1605
2215
|
live,
|
|
1606
2216
|
onPress,
|
|
@@ -1634,7 +2244,7 @@ function TempoDisplay({
|
|
|
1634
2244
|
},
|
|
1635
2245
|
...props,
|
|
1636
2246
|
children: [
|
|
1637
|
-
/* @__PURE__ */ jsx(
|
|
2247
|
+
showLabel && /* @__PURE__ */ jsx(
|
|
1638
2248
|
Text,
|
|
1639
2249
|
{
|
|
1640
2250
|
style: {
|
|
@@ -1713,300 +2323,176 @@ function TempoDisplay({
|
|
|
1713
2323
|
children: [
|
|
1714
2324
|
/* @__PURE__ */ jsxs(
|
|
1715
2325
|
View,
|
|
1716
|
-
{
|
|
1717
|
-
style: {
|
|
1718
|
-
backgroundColor: "#191919",
|
|
1719
|
-
borderRadius: 6,
|
|
1720
|
-
paddingVertical: 8,
|
|
1721
|
-
paddingHorizontal: 12,
|
|
1722
|
-
borderWidth: 1,
|
|
1723
|
-
borderColor: "#2C2C2C"
|
|
1724
|
-
},
|
|
1725
|
-
children: [
|
|
1726
|
-
/* @__PURE__ */ jsxs(Text, { style: { fontSize: 10, lineHeight: 16, color: "#9CA3AF", fontFamily: INTER }, children: [
|
|
1727
|
-
"Eccentric: ",
|
|
1728
|
-
eccentric,
|
|
1729
|
-
"s"
|
|
1730
|
-
] }),
|
|
1731
|
-
/* @__PURE__ */ jsxs(Text, { style: { fontSize: 10, lineHeight: 16, color: "#9CA3AF", fontFamily: INTER }, children: [
|
|
1732
|
-
"Pause (bottom): ",
|
|
1733
|
-
pauseBottom,
|
|
1734
|
-
"s"
|
|
1735
|
-
] }),
|
|
1736
|
-
/* @__PURE__ */ jsxs(Text, { style: { fontSize: 10, lineHeight: 16, color: "#9CA3AF", fontFamily: INTER }, children: [
|
|
1737
|
-
"Concentric: ",
|
|
1738
|
-
concentric,
|
|
1739
|
-
"s"
|
|
1740
|
-
] }),
|
|
1741
|
-
/* @__PURE__ */ jsxs(Text, { style: { fontSize: 10, lineHeight: 16, color: "#9CA3AF", fontFamily: INTER }, children: [
|
|
1742
|
-
"Pause (top): ",
|
|
1743
|
-
pauseTop,
|
|
1744
|
-
"s"
|
|
1745
|
-
] })
|
|
1746
|
-
]
|
|
1747
|
-
}
|
|
1748
|
-
),
|
|
1749
|
-
/* @__PURE__ */ jsx(
|
|
1750
|
-
View,
|
|
1751
|
-
{
|
|
1752
|
-
style: {
|
|
1753
|
-
width: 0,
|
|
1754
|
-
height: 0,
|
|
1755
|
-
borderLeftWidth: 5,
|
|
1756
|
-
borderRightWidth: 5,
|
|
1757
|
-
borderTopWidth: 5,
|
|
1758
|
-
borderLeftColor: "transparent",
|
|
1759
|
-
borderRightColor: "transparent",
|
|
1760
|
-
borderTopColor: "#2C2C2C"
|
|
1761
|
-
}
|
|
1762
|
-
}
|
|
1763
|
-
)
|
|
1764
|
-
]
|
|
1765
|
-
}
|
|
1766
|
-
)
|
|
1767
|
-
]
|
|
1768
|
-
}
|
|
1769
|
-
);
|
|
1770
|
-
}
|
|
1771
|
-
function formatAccessibilityLabel(setNumber, reps, weight, unit) {
|
|
1772
|
-
const repsText = reps != null ? `${reps} reps` : "no reps";
|
|
1773
|
-
const weightText = weight != null ? `at ${roundWeight(weight)} ${unit}` : "";
|
|
1774
|
-
return `Set ${setNumber}: ${repsText}${weightText ? ` ${weightText}` : ""}`;
|
|
1775
|
-
}
|
|
1776
|
-
function SetRow({
|
|
1777
|
-
mode,
|
|
1778
|
-
setNumber,
|
|
1779
|
-
previous,
|
|
1780
|
-
reps,
|
|
1781
|
-
weight,
|
|
1782
|
-
rpe,
|
|
1783
|
-
unit,
|
|
1784
|
-
velocities,
|
|
1785
|
-
velocityZones,
|
|
1786
|
-
velocityLiveRepIndex,
|
|
1787
|
-
velocityExpanded,
|
|
1788
|
-
onVelocityToggle,
|
|
1789
|
-
setType,
|
|
1790
|
-
prBadges,
|
|
1791
|
-
isNextSet,
|
|
1792
|
-
targets
|
|
1793
|
-
}) {
|
|
1794
|
-
const isActive = mode === "active";
|
|
1795
|
-
const isCompleted = mode === "completed";
|
|
1796
|
-
const rowStyle = {
|
|
1797
|
-
flexDirection: "row",
|
|
1798
|
-
alignItems: "center",
|
|
1799
|
-
padding: 7,
|
|
1800
|
-
paddingHorizontal: 8,
|
|
1801
|
-
gap: 8,
|
|
1802
|
-
borderRadius: 8
|
|
1803
|
-
};
|
|
1804
|
-
if (isActive && isNextSet) {
|
|
1805
|
-
rowStyle.backgroundColor = "rgba(255,121,0,0.06)";
|
|
1806
|
-
rowStyle.borderWidth = 1;
|
|
1807
|
-
rowStyle.borderColor = "rgba(255,121,0,0.15)";
|
|
1808
|
-
}
|
|
1809
|
-
if (isCompleted && !isNextSet) {
|
|
1810
|
-
rowStyle.opacity = 0.55;
|
|
1811
|
-
}
|
|
1812
|
-
return /* @__PURE__ */ jsxs(
|
|
1813
|
-
View,
|
|
1814
|
-
{
|
|
1815
|
-
style: rowStyle,
|
|
1816
|
-
accessibilityLabel: formatAccessibilityLabel(
|
|
1817
|
-
setNumber,
|
|
1818
|
-
reps,
|
|
1819
|
-
weight,
|
|
1820
|
-
unit
|
|
1821
|
-
),
|
|
1822
|
-
testID: "set-row",
|
|
1823
|
-
children: [
|
|
1824
|
-
/* @__PURE__ */ jsx(
|
|
1825
|
-
View,
|
|
1826
|
-
{
|
|
1827
|
-
style: { width: 36, alignItems: "center", justifyContent: "center" },
|
|
1828
|
-
testID: "set-row-set-number",
|
|
1829
|
-
children: setType ? /* @__PURE__ */ jsx(
|
|
1830
|
-
Text,
|
|
1831
|
-
{
|
|
1832
|
-
style: {
|
|
1833
|
-
fontSize: 10,
|
|
1834
|
-
fontWeight: "700",
|
|
1835
|
-
fontFamily: "Inter, sans-serif",
|
|
1836
|
-
color: WORKOUT_TOKENS.scale.orange,
|
|
1837
|
-
backgroundColor: "rgba(255,165,2,0.12)",
|
|
1838
|
-
paddingVertical: 1,
|
|
1839
|
-
paddingHorizontal: 5,
|
|
1840
|
-
borderRadius: 3,
|
|
1841
|
-
overflow: "hidden"
|
|
1842
|
-
},
|
|
1843
|
-
testID: "set-row-type-badge",
|
|
1844
|
-
children: setType
|
|
1845
|
-
}
|
|
1846
|
-
) : /* @__PURE__ */ jsx(
|
|
1847
|
-
Text,
|
|
1848
|
-
{
|
|
1849
|
-
className: "text-text-secondary",
|
|
1850
|
-
style: {
|
|
1851
|
-
fontSize: 13,
|
|
1852
|
-
fontWeight: "600",
|
|
1853
|
-
fontFamily: "Inter, sans-serif"
|
|
1854
|
-
},
|
|
1855
|
-
children: setNumber
|
|
1856
|
-
}
|
|
1857
|
-
)
|
|
1858
|
-
}
|
|
1859
|
-
),
|
|
1860
|
-
/* @__PURE__ */ jsx(
|
|
1861
|
-
View,
|
|
1862
|
-
{
|
|
1863
|
-
className: "flex-1",
|
|
1864
|
-
testID: "set-row-previous",
|
|
1865
|
-
children: /* @__PURE__ */ jsx(
|
|
1866
|
-
Text,
|
|
1867
|
-
{
|
|
1868
|
-
className: "text-text-secondary",
|
|
1869
|
-
style: {
|
|
1870
|
-
fontSize: 12,
|
|
1871
|
-
fontFamily: "Inter, sans-serif"
|
|
1872
|
-
},
|
|
1873
|
-
children: previous ? `${previous.reps} x ${roundWeight(previous.weight)}` : "\u2014"
|
|
1874
|
-
}
|
|
1875
|
-
)
|
|
1876
|
-
}
|
|
1877
|
-
),
|
|
1878
|
-
/* @__PURE__ */ jsx(
|
|
1879
|
-
View,
|
|
1880
|
-
{
|
|
1881
|
-
style: { width: 44, alignItems: "center", justifyContent: "center" },
|
|
1882
|
-
testID: "set-row-reps",
|
|
1883
|
-
children: isActive && reps == null && targets ? /* @__PURE__ */ jsx(
|
|
1884
|
-
Text,
|
|
1885
|
-
{
|
|
1886
|
-
className: "text-text-tertiary",
|
|
1887
|
-
style: {
|
|
1888
|
-
fontSize: 13,
|
|
1889
|
-
fontStyle: "italic",
|
|
1890
|
-
fontFamily: "Inter, sans-serif"
|
|
1891
|
-
},
|
|
1892
|
-
testID: "set-row-target-reps",
|
|
1893
|
-
children: targets.reps
|
|
1894
|
-
}
|
|
1895
|
-
) : /* @__PURE__ */ jsx(
|
|
1896
|
-
Text,
|
|
1897
|
-
{
|
|
1898
|
-
className: reps != null ? "text-text-primary" : "text-text-tertiary",
|
|
1899
|
-
style: {
|
|
1900
|
-
fontSize: 14,
|
|
1901
|
-
fontWeight: "600",
|
|
1902
|
-
fontFamily: "Inter, sans-serif"
|
|
1903
|
-
},
|
|
1904
|
-
children: reps != null ? reps : "\u2014"
|
|
1905
|
-
}
|
|
1906
|
-
)
|
|
1907
|
-
}
|
|
1908
|
-
),
|
|
1909
|
-
/* @__PURE__ */ jsx(
|
|
1910
|
-
View,
|
|
1911
|
-
{
|
|
1912
|
-
style: { width: 56, alignItems: "center", justifyContent: "center" },
|
|
1913
|
-
testID: "set-row-weight",
|
|
1914
|
-
children: isActive && weight == null && targets ? /* @__PURE__ */ jsx(
|
|
1915
|
-
Text,
|
|
1916
|
-
{
|
|
1917
|
-
className: "text-text-tertiary",
|
|
1918
|
-
style: {
|
|
1919
|
-
fontSize: 13,
|
|
1920
|
-
fontStyle: "italic",
|
|
1921
|
-
fontFamily: "Inter, sans-serif"
|
|
1922
|
-
},
|
|
1923
|
-
testID: "set-row-target-weight",
|
|
1924
|
-
children: roundWeight(targets.weight)
|
|
1925
|
-
}
|
|
1926
|
-
) : /* @__PURE__ */ jsx(
|
|
1927
|
-
Text,
|
|
1928
|
-
{
|
|
1929
|
-
className: weight != null ? "text-text-primary" : "text-text-tertiary",
|
|
1930
|
-
style: {
|
|
1931
|
-
fontSize: 14,
|
|
1932
|
-
fontWeight: "600",
|
|
1933
|
-
fontFamily: "Inter, sans-serif"
|
|
1934
|
-
},
|
|
1935
|
-
children: weight != null ? roundWeight(weight) : "\u2014"
|
|
1936
|
-
}
|
|
1937
|
-
)
|
|
1938
|
-
}
|
|
1939
|
-
),
|
|
1940
|
-
/* @__PURE__ */ jsx(
|
|
1941
|
-
View,
|
|
1942
|
-
{
|
|
1943
|
-
style: { width: 36, alignItems: "center", justifyContent: "center" },
|
|
1944
|
-
testID: "set-row-rpe",
|
|
1945
|
-
children: /* @__PURE__ */ jsx(
|
|
1946
|
-
Text,
|
|
1947
|
-
{
|
|
1948
|
-
className: rpe != null ? void 0 : "text-text-tertiary",
|
|
1949
|
-
style: {
|
|
1950
|
-
fontSize: 13,
|
|
1951
|
-
fontWeight: "600",
|
|
1952
|
-
fontFamily: "Inter, sans-serif",
|
|
1953
|
-
// rpeColor returns a concrete hex (native-safe); only the tertiary
|
|
1954
|
-
// fallback needs the className token.
|
|
1955
|
-
color: rpe != null ? rpeColor(rpe) : void 0
|
|
1956
|
-
},
|
|
1957
|
-
children: rpe != null ? roundRpe(rpe) : "\u2014"
|
|
1958
|
-
}
|
|
1959
|
-
)
|
|
1960
|
-
}
|
|
1961
|
-
),
|
|
1962
|
-
prBadges && prBadges.length > 0 && /* @__PURE__ */ jsx(
|
|
1963
|
-
View,
|
|
1964
|
-
{
|
|
1965
|
-
className: "flex-row items-center",
|
|
1966
|
-
style: { gap: 4 },
|
|
1967
|
-
testID: "set-row-pr-badges",
|
|
1968
|
-
children: prBadges.map((badge, i) => /* @__PURE__ */ jsx(
|
|
1969
|
-
PrBadge,
|
|
1970
|
-
{
|
|
1971
|
-
type: badge.type,
|
|
1972
|
-
label: badge.label,
|
|
1973
|
-
animate: false,
|
|
1974
|
-
compact: true
|
|
1975
|
-
},
|
|
1976
|
-
i
|
|
1977
|
-
))
|
|
1978
|
-
}
|
|
1979
|
-
),
|
|
1980
|
-
velocities && velocities.length > 0 && /* @__PURE__ */ jsx(
|
|
1981
|
-
View,
|
|
1982
|
-
{
|
|
1983
|
-
style: { position: "absolute", bottom: 0, left: 8, right: 8 },
|
|
1984
|
-
testID: "set-row-velocity-strip",
|
|
1985
|
-
children: /* @__PURE__ */ jsx(
|
|
1986
|
-
VelocityStrip,
|
|
1987
|
-
{
|
|
1988
|
-
velocities,
|
|
1989
|
-
zones: velocityZones,
|
|
1990
|
-
liveRepIndex: velocityLiveRepIndex,
|
|
1991
|
-
expanded: velocityExpanded,
|
|
1992
|
-
onToggle: onVelocityToggle,
|
|
1993
|
-
variant: onVelocityToggle ? "full" : "mini"
|
|
1994
|
-
}
|
|
1995
|
-
)
|
|
2326
|
+
{
|
|
2327
|
+
style: {
|
|
2328
|
+
backgroundColor: "#191919",
|
|
2329
|
+
borderRadius: 6,
|
|
2330
|
+
paddingVertical: 8,
|
|
2331
|
+
paddingHorizontal: 12,
|
|
2332
|
+
borderWidth: 1,
|
|
2333
|
+
borderColor: "#2C2C2C"
|
|
2334
|
+
},
|
|
2335
|
+
children: [
|
|
2336
|
+
/* @__PURE__ */ jsxs(Text, { style: { fontSize: 10, lineHeight: 16, color: "#9CA3AF", fontFamily: INTER }, children: [
|
|
2337
|
+
"Eccentric: ",
|
|
2338
|
+
eccentric,
|
|
2339
|
+
"s"
|
|
2340
|
+
] }),
|
|
2341
|
+
/* @__PURE__ */ jsxs(Text, { style: { fontSize: 10, lineHeight: 16, color: "#9CA3AF", fontFamily: INTER }, children: [
|
|
2342
|
+
"Pause (bottom): ",
|
|
2343
|
+
pauseBottom,
|
|
2344
|
+
"s"
|
|
2345
|
+
] }),
|
|
2346
|
+
/* @__PURE__ */ jsxs(Text, { style: { fontSize: 10, lineHeight: 16, color: "#9CA3AF", fontFamily: INTER }, children: [
|
|
2347
|
+
"Concentric: ",
|
|
2348
|
+
concentric,
|
|
2349
|
+
"s"
|
|
2350
|
+
] }),
|
|
2351
|
+
/* @__PURE__ */ jsxs(Text, { style: { fontSize: 10, lineHeight: 16, color: "#9CA3AF", fontFamily: INTER }, children: [
|
|
2352
|
+
"Pause (top): ",
|
|
2353
|
+
pauseTop,
|
|
2354
|
+
"s"
|
|
2355
|
+
] })
|
|
2356
|
+
]
|
|
2357
|
+
}
|
|
2358
|
+
),
|
|
2359
|
+
/* @__PURE__ */ jsx(
|
|
2360
|
+
View,
|
|
2361
|
+
{
|
|
2362
|
+
style: {
|
|
2363
|
+
width: 0,
|
|
2364
|
+
height: 0,
|
|
2365
|
+
borderLeftWidth: 5,
|
|
2366
|
+
borderRightWidth: 5,
|
|
2367
|
+
borderTopWidth: 5,
|
|
2368
|
+
borderLeftColor: "transparent",
|
|
2369
|
+
borderRightColor: "transparent",
|
|
2370
|
+
borderTopColor: "#2C2C2C"
|
|
2371
|
+
}
|
|
2372
|
+
}
|
|
2373
|
+
)
|
|
2374
|
+
]
|
|
1996
2375
|
}
|
|
1997
2376
|
)
|
|
1998
2377
|
]
|
|
1999
2378
|
}
|
|
2000
2379
|
);
|
|
2001
2380
|
}
|
|
2002
|
-
|
|
2003
|
-
|
|
2381
|
+
var t4 = getSemanticColors("dark");
|
|
2382
|
+
var INDICATOR_CONFIG = {
|
|
2383
|
+
imbalance: { Icon: ScaleIcon, color: t4["status-error"], label: "Left/right imbalance" },
|
|
2384
|
+
overshoot: { Icon: AlertTriangleIcon, color: t4["status-error"], label: "Load overshoot" },
|
|
2385
|
+
"velocity-loss": { Icon: TrendingDownIcon, color: t4["status-warning"], label: "Velocity loss" },
|
|
2386
|
+
"missed-reps": { Icon: CircleSlashIcon, color: t4["status-warning"], label: "Missed reps" },
|
|
2387
|
+
pr: { Icon: AwardIcon, color: t4["status-success"], label: "Personal record" },
|
|
2388
|
+
info: { Icon: InfoIcon, color: t4["status-info"], label: "More info" }
|
|
2389
|
+
};
|
|
2390
|
+
function ExerciseIndicator({ kind, onPress, className, ...props }) {
|
|
2391
|
+
const { Icon, color, label } = INDICATOR_CONFIG[kind];
|
|
2392
|
+
const chip = /* @__PURE__ */ jsx(
|
|
2393
|
+
View,
|
|
2394
|
+
{
|
|
2395
|
+
className,
|
|
2396
|
+
style: {
|
|
2397
|
+
width: 16,
|
|
2398
|
+
height: 16,
|
|
2399
|
+
borderRadius: 8,
|
|
2400
|
+
borderWidth: 1,
|
|
2401
|
+
borderColor: color,
|
|
2402
|
+
alignItems: "center",
|
|
2403
|
+
justifyContent: "center"
|
|
2404
|
+
},
|
|
2405
|
+
accessibilityRole: "image",
|
|
2406
|
+
accessibilityLabel: label,
|
|
2407
|
+
testID: "exercise-indicator",
|
|
2408
|
+
...props,
|
|
2409
|
+
children: /* @__PURE__ */ jsx(Icon, { size: 11, color })
|
|
2410
|
+
}
|
|
2411
|
+
);
|
|
2412
|
+
if (onPress) {
|
|
2413
|
+
return /* @__PURE__ */ jsx(Pressable, { onPress, accessibilityRole: "button", accessibilityLabel: label, children: chip });
|
|
2414
|
+
}
|
|
2415
|
+
return chip;
|
|
2004
2416
|
}
|
|
2005
2417
|
|
|
2006
|
-
// src/components/custom/Workout/
|
|
2007
|
-
function
|
|
2008
|
-
return
|
|
2418
|
+
// src/components/custom/Workout/ExerciseHeading.tsx
|
|
2419
|
+
function headingLabel(name, sets, reps, load, unit) {
|
|
2420
|
+
return `${name}, ${sets}\xD7${reps} @ ${roundWeight(load)} ${unit}`;
|
|
2421
|
+
}
|
|
2422
|
+
function ExerciseHeading({
|
|
2423
|
+
name,
|
|
2424
|
+
sets,
|
|
2425
|
+
reps,
|
|
2426
|
+
load,
|
|
2427
|
+
unit = "lbs",
|
|
2428
|
+
tempo,
|
|
2429
|
+
indicator,
|
|
2430
|
+
dimmed,
|
|
2431
|
+
onPress
|
|
2432
|
+
}) {
|
|
2433
|
+
return /* @__PURE__ */ jsxs(View, { style: { opacity: dimmed ? 0.55 : 1 }, testID: "exercise-heading", children: [
|
|
2434
|
+
/* @__PURE__ */ jsxs(
|
|
2435
|
+
Pressable,
|
|
2436
|
+
{
|
|
2437
|
+
onPress,
|
|
2438
|
+
accessibilityRole: "button",
|
|
2439
|
+
accessibilityLabel: headingLabel(name, sets, reps, load, unit),
|
|
2440
|
+
className: "flex-row items-center",
|
|
2441
|
+
style: { gap: 8 },
|
|
2442
|
+
testID: "exercise-card-header",
|
|
2443
|
+
children: [
|
|
2444
|
+
/* @__PURE__ */ jsx(
|
|
2445
|
+
Text,
|
|
2446
|
+
{
|
|
2447
|
+
className: "text-text-primary",
|
|
2448
|
+
style: { fontSize: 14, fontFamily: '"Space Grotesk", sans-serif', fontWeight: "700" },
|
|
2449
|
+
testID: "exercise-card-name",
|
|
2450
|
+
children: name
|
|
2451
|
+
}
|
|
2452
|
+
),
|
|
2453
|
+
/* @__PURE__ */ jsx(View, { className: "flex-1" }),
|
|
2454
|
+
indicator && /* @__PURE__ */ jsx(ExerciseIndicator, { kind: indicator })
|
|
2455
|
+
]
|
|
2456
|
+
}
|
|
2457
|
+
),
|
|
2458
|
+
/* @__PURE__ */ jsxs(
|
|
2459
|
+
View,
|
|
2460
|
+
{
|
|
2461
|
+
className: "flex-row items-center",
|
|
2462
|
+
style: { gap: 8, marginTop: 1 },
|
|
2463
|
+
testID: "exercise-card-summary",
|
|
2464
|
+
children: [
|
|
2465
|
+
/* @__PURE__ */ jsx(SetsRepsLoad, { sets, reps, load, unit }),
|
|
2466
|
+
/* @__PURE__ */ jsx(View, { className: "flex-1" }),
|
|
2467
|
+
tempo && /* @__PURE__ */ jsx(TempoDisplay, { tempo, size: "sm", showLabel: false, showInfo: false })
|
|
2468
|
+
]
|
|
2469
|
+
}
|
|
2470
|
+
)
|
|
2471
|
+
] });
|
|
2472
|
+
}
|
|
2473
|
+
|
|
2474
|
+
// src/components/custom/Workout/ExerciseCardHeading.tsx
|
|
2475
|
+
function ExerciseCardHeading({
|
|
2476
|
+
setStates,
|
|
2477
|
+
stripHeight = 8,
|
|
2478
|
+
dimmed,
|
|
2479
|
+
testID = "exercise-card",
|
|
2480
|
+
...heading
|
|
2481
|
+
}) {
|
|
2482
|
+
return /* @__PURE__ */ jsxs(
|
|
2483
|
+
View,
|
|
2484
|
+
{
|
|
2485
|
+
style: { paddingVertical: 9, paddingHorizontal: 12, opacity: dimmed ? 0.55 : 1 },
|
|
2486
|
+
testID,
|
|
2487
|
+
children: [
|
|
2488
|
+
/* @__PURE__ */ jsx(ExerciseHeading, { ...heading }),
|
|
2489
|
+
setStates.length > 0 && /* @__PURE__ */ jsx(View, { style: { marginTop: 7 }, testID: "exercise-card-strip", children: /* @__PURE__ */ jsx(SetStrip, { sets: setStates, height: stripHeight }) })
|
|
2490
|
+
]
|
|
2491
|
+
}
|
|
2492
|
+
);
|
|
2009
2493
|
}
|
|
2494
|
+
|
|
2495
|
+
// src/components/custom/Workout/ExerciseCard.tsx
|
|
2010
2496
|
function getSupersetBorderRadius(position) {
|
|
2011
2497
|
switch (position) {
|
|
2012
2498
|
case "first":
|
|
@@ -2049,7 +2535,7 @@ function formatSummary(summary) {
|
|
|
2049
2535
|
if (!summary) return "";
|
|
2050
2536
|
return `${summary.sets}\xD7${summary.reps} @ ${roundWeight(summary.weight)} ${summary.unit}`;
|
|
2051
2537
|
}
|
|
2052
|
-
function
|
|
2538
|
+
function formatAccessibilityLabel(name, summary, prescription) {
|
|
2053
2539
|
if (summary) return `${name}, ${formatSummary(summary)}`;
|
|
2054
2540
|
if (prescription) return `${name}, ${prescription}`;
|
|
2055
2541
|
return name;
|
|
@@ -2058,7 +2544,6 @@ function CollapsedCard({
|
|
|
2058
2544
|
name,
|
|
2059
2545
|
onToggle,
|
|
2060
2546
|
summary,
|
|
2061
|
-
e1rm,
|
|
2062
2547
|
isPR,
|
|
2063
2548
|
setVelocities,
|
|
2064
2549
|
velocityZones,
|
|
@@ -2077,7 +2562,7 @@ function CollapsedCard({
|
|
|
2077
2562
|
onPressIn: () => setPressed(true),
|
|
2078
2563
|
onPressOut: () => setPressed(false),
|
|
2079
2564
|
accessibilityRole: "button",
|
|
2080
|
-
accessibilityLabel:
|
|
2565
|
+
accessibilityLabel: formatAccessibilityLabel(name, summary, void 0),
|
|
2081
2566
|
testID: "exercise-card",
|
|
2082
2567
|
children: /* @__PURE__ */ jsxs(
|
|
2083
2568
|
View,
|
|
@@ -2091,100 +2576,93 @@ function CollapsedCard({
|
|
|
2091
2576
|
...supersetMargin
|
|
2092
2577
|
},
|
|
2093
2578
|
children: [
|
|
2094
|
-
/* @__PURE__ */ jsxs(
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
{
|
|
2144
|
-
className: "flex-row",
|
|
2145
|
-
style: { marginTop: 6, gap: 4 },
|
|
2146
|
-
testID: "exercise-card-strips",
|
|
2147
|
-
children: [
|
|
2148
|
-
setVelocities?.map((velocities, i) => /* @__PURE__ */ jsx(
|
|
2149
|
-
VelocityStrip,
|
|
2150
|
-
{
|
|
2151
|
-
velocities,
|
|
2152
|
-
zones: velocityZones,
|
|
2153
|
-
variant: "mini",
|
|
2154
|
-
testID: `exercise-card-velocity-strip-${i}`
|
|
2155
|
-
},
|
|
2156
|
-
i
|
|
2157
|
-
)),
|
|
2158
|
-
Array.from({ length: remaining }, (_, i) => /* @__PURE__ */ jsx(
|
|
2159
|
-
PlaceholderStrip,
|
|
2160
|
-
{
|
|
2161
|
-
testID: `exercise-card-placeholder-${i}`
|
|
2162
|
-
},
|
|
2163
|
-
`placeholder-${i}`
|
|
2164
|
-
))
|
|
2165
|
-
]
|
|
2166
|
-
}
|
|
2167
|
-
)
|
|
2579
|
+
/* @__PURE__ */ jsxs(View, { className: "flex-row items-center", testID: "exercise-card-header", children: [
|
|
2580
|
+
/* @__PURE__ */ jsx(
|
|
2581
|
+
Text,
|
|
2582
|
+
{
|
|
2583
|
+
className: "text-text-primary",
|
|
2584
|
+
style: {
|
|
2585
|
+
fontSize: 14,
|
|
2586
|
+
fontFamily: '"Space Grotesk", sans-serif',
|
|
2587
|
+
fontWeight: "700"
|
|
2588
|
+
},
|
|
2589
|
+
testID: "exercise-card-name",
|
|
2590
|
+
children: name
|
|
2591
|
+
}
|
|
2592
|
+
),
|
|
2593
|
+
/* @__PURE__ */ jsx(View, { className: "flex-1" }),
|
|
2594
|
+
summary && /* @__PURE__ */ jsx(
|
|
2595
|
+
Text,
|
|
2596
|
+
{
|
|
2597
|
+
className: "text-text-secondary",
|
|
2598
|
+
style: {
|
|
2599
|
+
fontSize: 12,
|
|
2600
|
+
fontFamily: "Inter, sans-serif",
|
|
2601
|
+
marginRight: 8
|
|
2602
|
+
},
|
|
2603
|
+
testID: "exercise-card-summary",
|
|
2604
|
+
children: formatSummary(summary)
|
|
2605
|
+
}
|
|
2606
|
+
),
|
|
2607
|
+
isPR && /* @__PURE__ */ jsx(View, { style: { marginLeft: 4 }, children: /* @__PURE__ */ jsx(PrBadge, { type: "e1rm", compact: true, animate: false }) })
|
|
2608
|
+
] }),
|
|
2609
|
+
(completedSets > 0 || remaining > 0) && /* @__PURE__ */ jsxs(View, { className: "flex-row", style: { marginTop: 6, gap: 4 }, testID: "exercise-card-strips", children: [
|
|
2610
|
+
setVelocities?.map((velocities, i) => /* @__PURE__ */ jsx(
|
|
2611
|
+
VelocityStrip,
|
|
2612
|
+
{
|
|
2613
|
+
velocities,
|
|
2614
|
+
zones: velocityZones,
|
|
2615
|
+
variant: "mini",
|
|
2616
|
+
testID: `exercise-card-velocity-strip-${i}`
|
|
2617
|
+
},
|
|
2618
|
+
i
|
|
2619
|
+
)),
|
|
2620
|
+
Array.from({ length: remaining }, (_, i) => /* @__PURE__ */ jsx(
|
|
2621
|
+
PlaceholderStrip,
|
|
2622
|
+
{
|
|
2623
|
+
testID: `exercise-card-placeholder-${i}`
|
|
2624
|
+
},
|
|
2625
|
+
`placeholder-${i}`
|
|
2626
|
+
))
|
|
2627
|
+
] })
|
|
2168
2628
|
]
|
|
2169
2629
|
}
|
|
2170
2630
|
)
|
|
2171
2631
|
}
|
|
2172
2632
|
);
|
|
2173
2633
|
}
|
|
2634
|
+
var DARK2 = getSemanticColors("dark");
|
|
2635
|
+
var BODY_DIVIDER = DARK2["border-subtle"];
|
|
2636
|
+
function deriveHeaderSetStates(sets) {
|
|
2637
|
+
return sets.map((set) => {
|
|
2638
|
+
switch (set.state) {
|
|
2639
|
+
case "live":
|
|
2640
|
+
return { status: "active", velocities: set.velocities, planned: set.target.reps };
|
|
2641
|
+
case "done":
|
|
2642
|
+
return { status: "done", velocities: set.velocities };
|
|
2643
|
+
case "todo":
|
|
2644
|
+
return { status: "todo", planned: set.planned ?? set.target.reps };
|
|
2645
|
+
}
|
|
2646
|
+
});
|
|
2647
|
+
}
|
|
2174
2648
|
function ExpandedCard({
|
|
2175
2649
|
name,
|
|
2176
2650
|
onToggle,
|
|
2177
2651
|
summary,
|
|
2178
|
-
e1rm,
|
|
2179
2652
|
isPR,
|
|
2180
2653
|
sets,
|
|
2181
2654
|
tempo,
|
|
2655
|
+
indicator,
|
|
2656
|
+
setStates,
|
|
2657
|
+
stripHeight = 8,
|
|
2658
|
+
velocityZones,
|
|
2182
2659
|
supersetPosition
|
|
2183
2660
|
}) {
|
|
2184
2661
|
const borderRadius = getSupersetBorderRadius(supersetPosition);
|
|
2185
2662
|
const supersetMargin = getSupersetMargin(supersetPosition);
|
|
2186
2663
|
const unit = summary?.unit ?? sets?.[0]?.unit ?? "lbs";
|
|
2187
|
-
const
|
|
2664
|
+
const headerStates = setStates ?? (sets ? deriveHeaderSetStates(sets) : []);
|
|
2665
|
+
const headerIndicator = indicator ?? (isPR ? "pr" : void 0);
|
|
2188
2666
|
return /* @__PURE__ */ jsxs(
|
|
2189
2667
|
View,
|
|
2190
2668
|
{
|
|
@@ -2197,114 +2675,32 @@ function ExpandedCard({
|
|
|
2197
2675
|
testID: "exercise-card",
|
|
2198
2676
|
children: [
|
|
2199
2677
|
/* @__PURE__ */ jsx(
|
|
2200
|
-
|
|
2678
|
+
ExerciseCardHeading,
|
|
2201
2679
|
{
|
|
2680
|
+
name,
|
|
2681
|
+
sets: summary?.sets ?? sets?.length ?? 0,
|
|
2682
|
+
reps: summary?.reps ?? 0,
|
|
2683
|
+
load: summary?.weight ?? 0,
|
|
2684
|
+
unit,
|
|
2685
|
+
tempo,
|
|
2686
|
+
indicator: headerIndicator,
|
|
2687
|
+
setStates: headerStates,
|
|
2688
|
+
stripHeight,
|
|
2202
2689
|
onPress: onToggle,
|
|
2203
|
-
|
|
2204
|
-
accessibilityLabel: formatAccessibilityLabel2(name, summary, void 0),
|
|
2205
|
-
"aria-expanded": true,
|
|
2206
|
-
testID: "exercise-card-header",
|
|
2207
|
-
children: /* @__PURE__ */ jsxs(
|
|
2208
|
-
View,
|
|
2209
|
-
{
|
|
2210
|
-
className: "flex-row items-center",
|
|
2211
|
-
style: {
|
|
2212
|
-
padding: 12,
|
|
2213
|
-
paddingHorizontal: 14,
|
|
2214
|
-
paddingBottom: 10,
|
|
2215
|
-
borderBottomWidth: 1,
|
|
2216
|
-
borderBottomColor: resolveColor("border-default")
|
|
2217
|
-
},
|
|
2218
|
-
children: [
|
|
2219
|
-
/* @__PURE__ */ jsx(
|
|
2220
|
-
Text,
|
|
2221
|
-
{
|
|
2222
|
-
className: "text-text-primary",
|
|
2223
|
-
style: {
|
|
2224
|
-
fontSize: 14,
|
|
2225
|
-
fontFamily: '"Space Grotesk", sans-serif',
|
|
2226
|
-
fontWeight: "700"
|
|
2227
|
-
},
|
|
2228
|
-
testID: "exercise-card-name",
|
|
2229
|
-
children: name
|
|
2230
|
-
}
|
|
2231
|
-
),
|
|
2232
|
-
/* @__PURE__ */ jsx(View, { className: "flex-1" }),
|
|
2233
|
-
summary && /* @__PURE__ */ jsx(
|
|
2234
|
-
Text,
|
|
2235
|
-
{
|
|
2236
|
-
className: "text-text-secondary",
|
|
2237
|
-
style: {
|
|
2238
|
-
fontSize: 12,
|
|
2239
|
-
fontFamily: "Inter, sans-serif",
|
|
2240
|
-
marginRight: 8
|
|
2241
|
-
},
|
|
2242
|
-
testID: "exercise-card-summary",
|
|
2243
|
-
children: formatSummary(summary)
|
|
2244
|
-
}
|
|
2245
|
-
),
|
|
2246
|
-
e1rm && /* @__PURE__ */ jsx(
|
|
2247
|
-
WeightBadge,
|
|
2248
|
-
{
|
|
2249
|
-
value: roundWeight(e1rm.value),
|
|
2250
|
-
unit: e1rm.unit,
|
|
2251
|
-
size: "sm",
|
|
2252
|
-
showIcon: false,
|
|
2253
|
-
testID: "exercise-card-e1rm"
|
|
2254
|
-
}
|
|
2255
|
-
),
|
|
2256
|
-
isPR && /* @__PURE__ */ jsx(View, { style: { marginLeft: 4 }, children: /* @__PURE__ */ jsx(PrBadge, { type: "e1rm", compact: true, animate: false }) })
|
|
2257
|
-
]
|
|
2258
|
-
}
|
|
2259
|
-
)
|
|
2690
|
+
testID: "exercise-card-heading"
|
|
2260
2691
|
}
|
|
2261
2692
|
),
|
|
2262
|
-
|
|
2263
|
-
/* @__PURE__ */ jsx(
|
|
2693
|
+
/* @__PURE__ */ jsxs(
|
|
2264
2694
|
View,
|
|
2265
2695
|
{
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
testID: "exercise-card-column-headers",
|
|
2273
|
-
children: columnHeaders.map((header, i) => {
|
|
2274
|
-
const widths = [36, void 0, 44, 56, 36];
|
|
2275
|
-
const width = widths[i];
|
|
2276
|
-
const isFlex = width === void 0;
|
|
2277
|
-
return /* @__PURE__ */ jsx(
|
|
2278
|
-
View,
|
|
2279
|
-
{
|
|
2280
|
-
style: {
|
|
2281
|
-
...isFlex ? {} : { width },
|
|
2282
|
-
alignItems: "center",
|
|
2283
|
-
justifyContent: "center",
|
|
2284
|
-
gap: 8
|
|
2285
|
-
},
|
|
2286
|
-
...isFlex ? { className: "flex-1" } : {},
|
|
2287
|
-
children: /* @__PURE__ */ jsx(
|
|
2288
|
-
Text,
|
|
2289
|
-
{
|
|
2290
|
-
className: "text-text-tertiary",
|
|
2291
|
-
style: {
|
|
2292
|
-
fontSize: 10,
|
|
2293
|
-
fontWeight: "600",
|
|
2294
|
-
fontFamily: "Inter, sans-serif",
|
|
2295
|
-
textTransform: "uppercase",
|
|
2296
|
-
letterSpacing: 0.8
|
|
2297
|
-
},
|
|
2298
|
-
children: header
|
|
2299
|
-
}
|
|
2300
|
-
)
|
|
2301
|
-
},
|
|
2302
|
-
header
|
|
2303
|
-
);
|
|
2304
|
-
})
|
|
2696
|
+
style: { borderTopWidth: 1, borderTopColor: BODY_DIVIDER, paddingBottom: 6 },
|
|
2697
|
+
testID: "exercise-card-body",
|
|
2698
|
+
children: [
|
|
2699
|
+
/* @__PURE__ */ jsx(SetTableHeader, { unit, showPrevious: false, testID: "exercise-card-column-headers" }),
|
|
2700
|
+
sets && /* @__PURE__ */ jsx(View, { testID: "exercise-card-sets", children: sets.map((set, i) => /* @__PURE__ */ jsx(SetRow, { ...set, velocityZones: set.velocityZones ?? velocityZones }, i)) })
|
|
2701
|
+
]
|
|
2305
2702
|
}
|
|
2306
|
-
)
|
|
2307
|
-
sets && /* @__PURE__ */ jsx(View, { testID: "exercise-card-sets", children: sets.map((setProps, i) => /* @__PURE__ */ jsx(SetRow, { ...setProps }, i)) })
|
|
2703
|
+
)
|
|
2308
2704
|
]
|
|
2309
2705
|
}
|
|
2310
2706
|
);
|
|
@@ -2313,11 +2709,12 @@ function UpcomingCard({
|
|
|
2313
2709
|
name,
|
|
2314
2710
|
prescription,
|
|
2315
2711
|
previousBest,
|
|
2316
|
-
supersetPosition
|
|
2712
|
+
supersetPosition,
|
|
2713
|
+
onToggle
|
|
2317
2714
|
}) {
|
|
2318
2715
|
const borderRadius = getSupersetBorderRadius(supersetPosition);
|
|
2319
2716
|
const supersetMargin = getSupersetMargin(supersetPosition);
|
|
2320
|
-
return /* @__PURE__ */ jsx(
|
|
2717
|
+
return /* @__PURE__ */ jsx(Pressable, { onPress: onToggle, accessibilityRole: "button", children: /* @__PURE__ */ jsx(
|
|
2321
2718
|
View,
|
|
2322
2719
|
{
|
|
2323
2720
|
style: {
|
|
@@ -2327,7 +2724,7 @@ function UpcomingCard({
|
|
|
2327
2724
|
...borderRadius,
|
|
2328
2725
|
...supersetMargin
|
|
2329
2726
|
},
|
|
2330
|
-
accessibilityLabel:
|
|
2727
|
+
accessibilityLabel: formatAccessibilityLabel(name, void 0, prescription),
|
|
2331
2728
|
testID: "exercise-card",
|
|
2332
2729
|
children: /* @__PURE__ */ jsxs(View, { className: "flex-row items-center", children: [
|
|
2333
2730
|
/* @__PURE__ */ jsx(
|
|
@@ -2335,6 +2732,7 @@ function UpcomingCard({
|
|
|
2335
2732
|
{
|
|
2336
2733
|
className: "text-text-primary",
|
|
2337
2734
|
style: {
|
|
2735
|
+
flexShrink: 0,
|
|
2338
2736
|
fontSize: 14,
|
|
2339
2737
|
fontFamily: '"Space Grotesk", sans-serif',
|
|
2340
2738
|
fontWeight: "700"
|
|
@@ -2347,7 +2745,9 @@ function UpcomingCard({
|
|
|
2347
2745
|
Text,
|
|
2348
2746
|
{
|
|
2349
2747
|
className: "text-text-secondary",
|
|
2748
|
+
numberOfLines: 1,
|
|
2350
2749
|
style: {
|
|
2750
|
+
flexShrink: 1,
|
|
2351
2751
|
fontSize: 12,
|
|
2352
2752
|
fontFamily: "Inter, sans-serif",
|
|
2353
2753
|
marginLeft: 8
|
|
@@ -2356,12 +2756,14 @@ function UpcomingCard({
|
|
|
2356
2756
|
children: prescription
|
|
2357
2757
|
}
|
|
2358
2758
|
),
|
|
2359
|
-
/* @__PURE__ */ jsx(View, { className: "flex-1" }),
|
|
2759
|
+
/* @__PURE__ */ jsx(View, { className: "flex-1", style: { minWidth: 8 } }),
|
|
2360
2760
|
previousBest && /* @__PURE__ */ jsx(
|
|
2361
2761
|
Text,
|
|
2362
2762
|
{
|
|
2363
2763
|
className: "text-text-tertiary",
|
|
2764
|
+
numberOfLines: 1,
|
|
2364
2765
|
style: {
|
|
2766
|
+
flexShrink: 1,
|
|
2365
2767
|
fontSize: 11,
|
|
2366
2768
|
fontFamily: "Inter, sans-serif"
|
|
2367
2769
|
},
|
|
@@ -2371,18 +2773,31 @@ function UpcomingCard({
|
|
|
2371
2773
|
)
|
|
2372
2774
|
] })
|
|
2373
2775
|
}
|
|
2374
|
-
);
|
|
2776
|
+
) });
|
|
2375
2777
|
}
|
|
2376
|
-
function ExerciseCard(
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2778
|
+
function ExerciseCard({
|
|
2779
|
+
upcoming,
|
|
2780
|
+
expanded,
|
|
2781
|
+
defaultExpanded,
|
|
2782
|
+
onExpandedChange,
|
|
2783
|
+
...rest
|
|
2784
|
+
}) {
|
|
2785
|
+
const [internalExpanded, setInternalExpanded] = useState(defaultExpanded ?? false);
|
|
2786
|
+
const isControlled = expanded !== void 0;
|
|
2787
|
+
const isExpanded = isControlled ? expanded : internalExpanded;
|
|
2788
|
+
const onToggle = () => {
|
|
2789
|
+
const next = !isExpanded;
|
|
2790
|
+
onExpandedChange?.(next);
|
|
2791
|
+
if (!isControlled) setInternalExpanded(next);
|
|
2792
|
+
};
|
|
2793
|
+
if (upcoming) return /* @__PURE__ */ jsx(UpcomingCard, { ...rest, onToggle });
|
|
2794
|
+
return isExpanded ? /* @__PURE__ */ jsx(ExpandedCard, { ...rest, onToggle }) : /* @__PURE__ */ jsx(CollapsedCard, { ...rest, onToggle });
|
|
2385
2795
|
}
|
|
2796
|
+
function resolveColor(token, mode = "dark") {
|
|
2797
|
+
return Platform.OS === "web" ? `var(--color-${token})` : getSemanticColors(mode)[token];
|
|
2798
|
+
}
|
|
2799
|
+
|
|
2800
|
+
// src/components/custom/Workout/InputBar.tsx
|
|
2386
2801
|
var BRAND_PRIMARY2 = getSemanticColors("dark")["brand-primary"];
|
|
2387
2802
|
var INPUT_CLASSNAME = "bg-surface-raised border-border-strong text-text-primary";
|
|
2388
2803
|
var inputStyle = {
|
|
@@ -2539,6 +2954,45 @@ function InputBar({
|
|
|
2539
2954
|
}
|
|
2540
2955
|
);
|
|
2541
2956
|
}
|
|
2957
|
+
function formatDuration(ms) {
|
|
2958
|
+
const totalSeconds = Math.max(0, Math.ceil(ms / 1e3));
|
|
2959
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
2960
|
+
const seconds = totalSeconds % 60;
|
|
2961
|
+
return `${minutes}:${String(seconds).padStart(2, "0")}`;
|
|
2962
|
+
}
|
|
2963
|
+
function useTimer(options = {}) {
|
|
2964
|
+
const {
|
|
2965
|
+
mode = "up",
|
|
2966
|
+
durationMs,
|
|
2967
|
+
elapsedMs: controlledElapsedMs,
|
|
2968
|
+
running = false,
|
|
2969
|
+
autoTick = false,
|
|
2970
|
+
tickMs = 1e3
|
|
2971
|
+
} = options;
|
|
2972
|
+
const isControlled = controlledElapsedMs != null;
|
|
2973
|
+
const [internalElapsedMs, setInternalElapsedMs] = useState(0);
|
|
2974
|
+
useEffect(() => {
|
|
2975
|
+
if (isControlled || !autoTick || !running) return;
|
|
2976
|
+
const id = setInterval(() => {
|
|
2977
|
+
setInternalElapsedMs((prev) => prev + tickMs);
|
|
2978
|
+
}, tickMs);
|
|
2979
|
+
return () => clearInterval(id);
|
|
2980
|
+
}, [isControlled, autoTick, running, tickMs]);
|
|
2981
|
+
const elapsedMs = isControlled ? controlledElapsedMs : internalElapsedMs;
|
|
2982
|
+
const hasDuration = durationMs != null && durationMs > 0;
|
|
2983
|
+
const remainingMs = durationMs != null ? Math.max(0, durationMs - elapsedMs) : 0;
|
|
2984
|
+
let progress;
|
|
2985
|
+
if (hasDuration) {
|
|
2986
|
+
progress = Math.min(1, Math.max(0, elapsedMs / durationMs));
|
|
2987
|
+
} else {
|
|
2988
|
+
progress = mode === "down" ? 1 : 0;
|
|
2989
|
+
}
|
|
2990
|
+
const label = formatDuration(mode === "down" ? remainingMs : elapsedMs);
|
|
2991
|
+
const done = durationMs != null && elapsedMs >= durationMs;
|
|
2992
|
+
return { elapsedMs, remainingMs, progress, label, done };
|
|
2993
|
+
}
|
|
2994
|
+
|
|
2995
|
+
// src/components/custom/Workout/RestTimer.tsx
|
|
2542
2996
|
var BRAND_PRIMARY3 = getSemanticColors("dark")["brand-primary"];
|
|
2543
2997
|
function RestTimer({
|
|
2544
2998
|
totalSeconds,
|
|
@@ -2549,14 +3003,18 @@ function RestTimer({
|
|
|
2549
3003
|
visible,
|
|
2550
3004
|
displayOnly = false
|
|
2551
3005
|
}) {
|
|
2552
|
-
|
|
2553
|
-
|
|
3006
|
+
const {
|
|
3007
|
+
remainingMs,
|
|
3008
|
+
progress,
|
|
3009
|
+
label: timeDisplay
|
|
3010
|
+
} = useTimer({
|
|
3011
|
+
mode: "down",
|
|
3012
|
+
durationMs: totalSeconds * 1e3,
|
|
3013
|
+
elapsedMs
|
|
3014
|
+
});
|
|
2554
3015
|
const remainingSec = Math.ceil(remainingMs / 1e3);
|
|
2555
|
-
const
|
|
2556
|
-
|
|
2557
|
-
const timeDisplay = `${minutes}:${String(seconds).padStart(2, "0")}`;
|
|
2558
|
-
const totalMs = totalSeconds * 1e3;
|
|
2559
|
-
const progressPct = totalMs > 0 ? Math.min(100, elapsedMs / totalMs * 100) : 100;
|
|
3016
|
+
const progressPct = progress * 100;
|
|
3017
|
+
if (!visible) return null;
|
|
2560
3018
|
return /* @__PURE__ */ jsxs(
|
|
2561
3019
|
View,
|
|
2562
3020
|
{
|
|
@@ -2795,23 +3253,22 @@ function deriveWorkoutProgress(exercises) {
|
|
|
2795
3253
|
function findActiveExercise(exercises) {
|
|
2796
3254
|
return exercises.find((exercise) => exercise.status === "active") ?? null;
|
|
2797
3255
|
}
|
|
2798
|
-
function
|
|
2799
|
-
|
|
2800
|
-
return focused ? "expanded" : "collapsed";
|
|
3256
|
+
function isUpcomingExercise(status) {
|
|
3257
|
+
return status === "upcoming";
|
|
2801
3258
|
}
|
|
2802
3259
|
function toActiveCardProps(exercise, focused, onToggle, supersetPosition = null, supersetColor) {
|
|
2803
|
-
const
|
|
3260
|
+
const upcoming = isUpcomingExercise(exercise.status);
|
|
2804
3261
|
return {
|
|
2805
3262
|
name: exercise.name,
|
|
2806
|
-
|
|
2807
|
-
|
|
3263
|
+
upcoming,
|
|
3264
|
+
expanded: focused,
|
|
3265
|
+
onExpandedChange: onToggle,
|
|
2808
3266
|
summary: exercise.summary,
|
|
2809
|
-
e1rm: exercise.e1rm,
|
|
2810
3267
|
isPR: exercise.isPR,
|
|
2811
3268
|
tempo: exercise.tempo,
|
|
2812
3269
|
setVelocities: exercise.setVelocities,
|
|
2813
3270
|
totalPlannedSets: exercise.totalPlannedSets,
|
|
2814
|
-
sets:
|
|
3271
|
+
sets: focused ? exercise.sets : void 0,
|
|
2815
3272
|
prescription: exercise.prescription,
|
|
2816
3273
|
previousBest: exercise.previousBest,
|
|
2817
3274
|
supersetPosition,
|
|
@@ -3500,11 +3957,11 @@ function CardContent({ children, className }) {
|
|
|
3500
3957
|
function CardFooter({ children, className }) {
|
|
3501
3958
|
return /* @__PURE__ */ jsx(View, { className: cn("px-6 py-4 flex-row items-center gap-2", className), children });
|
|
3502
3959
|
}
|
|
3503
|
-
var
|
|
3960
|
+
var t5 = getSemanticColors("dark");
|
|
3504
3961
|
var solidVariantColors = {
|
|
3505
|
-
success:
|
|
3506
|
-
warning:
|
|
3507
|
-
error:
|
|
3962
|
+
success: t5["status-success"],
|
|
3963
|
+
warning: t5["status-warning"],
|
|
3964
|
+
error: t5["status-error"],
|
|
3508
3965
|
neutral: "#6B7280"
|
|
3509
3966
|
};
|
|
3510
3967
|
var ringVariantStyles = {
|
|
@@ -3544,8 +4001,8 @@ var iconColors = {
|
|
|
3544
4001
|
warning: "#6B4000",
|
|
3545
4002
|
error: "#5C1A1A",
|
|
3546
4003
|
neutral: "#D1D5DB",
|
|
3547
|
-
"on-track":
|
|
3548
|
-
deviation:
|
|
4004
|
+
"on-track": t5["status-success"],
|
|
4005
|
+
deviation: t5["status-warning"],
|
|
3549
4006
|
future: "#6B7280"
|
|
3550
4007
|
};
|
|
3551
4008
|
var sizeDimensions = {
|
|
@@ -3631,13 +4088,13 @@ var MESO_ACCENT_GRADIENT_LIGHT = primitiveRamps.orange[300];
|
|
|
3631
4088
|
var WORKOUT_PILL_DELOAD = primitiveRamps.magenta[600];
|
|
3632
4089
|
|
|
3633
4090
|
// src/components/custom/Workout/MesoStatusCard.tsx
|
|
3634
|
-
var
|
|
3635
|
-
var BRAND_PRIMARY5 =
|
|
4091
|
+
var t6 = getSemanticColors("dark");
|
|
4092
|
+
var BRAND_PRIMARY5 = t6["brand-primary"];
|
|
3636
4093
|
var BRAND_PRIMARY_DARK = MESO_ACCENT_GRADIENT_DARK;
|
|
3637
4094
|
var BRAND_PRIMARY_LIGHT = MESO_ACCENT_GRADIENT_LIGHT;
|
|
3638
|
-
var SUCCESS =
|
|
3639
|
-
var WARNING =
|
|
3640
|
-
var ERROR =
|
|
4095
|
+
var SUCCESS = t6["status-success"];
|
|
4096
|
+
var WARNING = t6["status-warning"];
|
|
4097
|
+
var ERROR = t6["status-error"];
|
|
3641
4098
|
var ACCENT_STOPS = [BRAND_PRIMARY_DARK, BRAND_PRIMARY5, BRAND_PRIMARY_LIGHT];
|
|
3642
4099
|
var CARD_GRADIENT = `linear-gradient(135deg, ${resolveColor("surface-elevated")} 0%, ${resolveColor("surface-raised")} 100%)`;
|
|
3643
4100
|
var GAUGE_GRADIENT = "linear-gradient(90deg, rgba(46,213,115,0.25) 0%, rgba(249,180,21,0.25) 50%, rgba(209,67,67,0.25) 100%)";
|
|
@@ -3709,7 +4166,7 @@ function StatusPill({ badge }) {
|
|
|
3709
4166
|
}
|
|
3710
4167
|
);
|
|
3711
4168
|
}
|
|
3712
|
-
function
|
|
4169
|
+
function MetricCell2({ metric }) {
|
|
3713
4170
|
return /* @__PURE__ */ jsxs(View, { style: { width: "48%" }, testID: "meso-status-card-metric", children: [
|
|
3714
4171
|
/* @__PURE__ */ jsx(
|
|
3715
4172
|
Text,
|
|
@@ -3927,7 +4384,7 @@ function MesoStatusCard({
|
|
|
3927
4384
|
className: "flex-row flex-wrap",
|
|
3928
4385
|
style: { gap: 8 },
|
|
3929
4386
|
testID: "meso-status-card-metrics",
|
|
3930
|
-
children: metrics.map((metric, index) => /* @__PURE__ */ jsx(
|
|
4387
|
+
children: metrics.map((metric, index) => /* @__PURE__ */ jsx(MetricCell2, { metric }, `${metric.label}-${index}`))
|
|
3931
4388
|
}
|
|
3932
4389
|
),
|
|
3933
4390
|
gauges.length > 0 && /* @__PURE__ */ jsx(View, { style: { gap: 12 }, testID: "meso-status-card-gauges", children: gauges.map((gauge, index) => /* @__PURE__ */ jsx(Gauge, { gauge }, `${gauge.label}-${index}`)) }),
|
|
@@ -4008,8 +4465,8 @@ var ERROR_PILL_BORDER = "rgba(209,67,67,0.20)";
|
|
|
4008
4465
|
var PLOT_LEFT = 26;
|
|
4009
4466
|
var TOOLTIP_WIDTH = 124;
|
|
4010
4467
|
function timeOf(dateStr) {
|
|
4011
|
-
const
|
|
4012
|
-
return Number.isNaN(
|
|
4468
|
+
const t12 = Date.parse(dateStr);
|
|
4469
|
+
return Number.isNaN(t12) ? 0 : t12;
|
|
4013
4470
|
}
|
|
4014
4471
|
function formatValue(value) {
|
|
4015
4472
|
return `${Math.round(value)}`;
|
|
@@ -4498,10 +4955,10 @@ function StrengthTrendChart({
|
|
|
4498
4955
|
}
|
|
4499
4956
|
);
|
|
4500
4957
|
}
|
|
4501
|
-
var
|
|
4502
|
-
var STATUS_SUCCESS2 =
|
|
4503
|
-
var STATUS_WARNING2 =
|
|
4504
|
-
var STATUS_INFO =
|
|
4958
|
+
var t7 = getSemanticColors("dark");
|
|
4959
|
+
var STATUS_SUCCESS2 = t7["status-success"];
|
|
4960
|
+
var STATUS_WARNING2 = t7["status-warning"];
|
|
4961
|
+
var STATUS_INFO = t7["status-info"];
|
|
4505
4962
|
var DOT_BORDER = "#F3F4F6";
|
|
4506
4963
|
var BAND_FILL = "rgba(46,213,115,0.1)";
|
|
4507
4964
|
var BAND_EDGE = "rgba(46,213,115,0.45)";
|
|
@@ -4544,13 +5001,13 @@ function interpEdge(pixels, x, key) {
|
|
|
4544
5001
|
const a = pixels[i];
|
|
4545
5002
|
const b = pixels[i + 1];
|
|
4546
5003
|
if (x >= a.x && x <= b.x) {
|
|
4547
|
-
const
|
|
4548
|
-
return a[key] +
|
|
5004
|
+
const t12 = b.x === a.x ? 0 : (x - a.x) / (b.x - a.x);
|
|
5005
|
+
return a[key] + t12 * (b[key] - a[key]);
|
|
4549
5006
|
}
|
|
4550
5007
|
}
|
|
4551
5008
|
return pixels[pixels.length - 1][key];
|
|
4552
5009
|
}
|
|
4553
|
-
function
|
|
5010
|
+
function buildColumns2(pixels, step) {
|
|
4554
5011
|
if (pixels.length < 2) return [];
|
|
4555
5012
|
const start = pixels[0].x;
|
|
4556
5013
|
const end = pixels[pixels.length - 1].x;
|
|
@@ -4662,7 +5119,7 @@ function CapacityBandChart({
|
|
|
4662
5119
|
}
|
|
4663
5120
|
const { toX, toY } = scale;
|
|
4664
5121
|
const bandPixels = toPixels(band, toX, toY);
|
|
4665
|
-
const columns =
|
|
5122
|
+
const columns = buildColumns2(bandPixels, COLUMN_STEP);
|
|
4666
5123
|
const topEdge = buildEdges(bandPixels, "yHigh");
|
|
4667
5124
|
const bottomEdge = buildEdges(bandPixels, "yLow");
|
|
4668
5125
|
const lastBandPoint = band[band.length - 1];
|
|
@@ -4737,8 +5194,8 @@ function CapacityBandChart({
|
|
|
4737
5194
|
testID: "capacity-band-chart-reveal",
|
|
4738
5195
|
children: /* @__PURE__ */ jsxs(View, { style: { width, height, position: "relative" }, children: [
|
|
4739
5196
|
projection && /* @__PURE__ */ jsxs(View, { testID: "capacity-band-chart-projection", children: [
|
|
4740
|
-
renderColumns(
|
|
4741
|
-
renderColumns(
|
|
5197
|
+
renderColumns(buildColumns2(trainingPixels, COLUMN_STEP), PROJECTION_FILL, "capacity-band-chart-projection-fill"),
|
|
5198
|
+
renderColumns(buildColumns2(restPixels, COLUMN_STEP), PROJECTION_FILL, "capacity-band-chart-projection-fill"),
|
|
4742
5199
|
renderEdges(buildEdges(trainingPixels, "yHigh"), STATUS_SUCCESS2, true, "capacity-band-chart-projection-training"),
|
|
4743
5200
|
renderEdges(buildEdges(trainingPixels, "yLow"), STATUS_SUCCESS2, true, "capacity-band-chart-projection-training"),
|
|
4744
5201
|
renderEdges(buildEdges(restPixels, "yHigh"), STATUS_INFO, true, "capacity-band-chart-projection-rest"),
|
|
@@ -4899,10 +5356,9 @@ function deriveExerciseStats(entries) {
|
|
|
4899
5356
|
function toExerciseCardProps(entry, expanded, onToggle) {
|
|
4900
5357
|
return {
|
|
4901
5358
|
name: entry.title,
|
|
4902
|
-
|
|
4903
|
-
onToggle,
|
|
5359
|
+
expanded,
|
|
5360
|
+
onExpandedChange: onToggle,
|
|
4904
5361
|
summary: entry.summary,
|
|
4905
|
-
e1rm: entry.e1rm,
|
|
4906
5362
|
isPR: entry.isPR,
|
|
4907
5363
|
tempo: entry.tempo,
|
|
4908
5364
|
sets: expanded ? entry.sets : void 0
|
|
@@ -5446,6 +5902,7 @@ var colorStyles = {
|
|
|
5446
5902
|
default: "bg-text-tertiary",
|
|
5447
5903
|
primary: "bg-brand-primary",
|
|
5448
5904
|
success: "bg-status-success",
|
|
5905
|
+
live: "bg-status-live",
|
|
5449
5906
|
error: "bg-status-error",
|
|
5450
5907
|
warning: "bg-status-warning",
|
|
5451
5908
|
info: "bg-status-info",
|
|
@@ -5502,7 +5959,7 @@ function Indicator({
|
|
|
5502
5959
|
colorClass,
|
|
5503
5960
|
pulseMode === "opacity" && "animate-pulse",
|
|
5504
5961
|
ring && "border-2 border-background-base",
|
|
5505
|
-
glow && !customColor && color === "success" && "shadow-[0_0_6px_rgba(46,213,115,0.6)]",
|
|
5962
|
+
glow && !customColor && (color === "success" || color === "live") && "shadow-[0_0_6px_rgba(46,213,115,0.6)]",
|
|
5506
5963
|
glow && !customColor && color === "error" && "shadow-[0_0_6px_rgba(239,68,68,0.6)]",
|
|
5507
5964
|
glow && !customColor && color === "warning" && "shadow-[0_0_6px_rgba(245,158,11,0.6)]",
|
|
5508
5965
|
glow && !customColor && color === "primary" && "shadow-[0_0_6px_rgba(255,121,0,0.6)]",
|
|
@@ -5584,7 +6041,7 @@ function Badge({
|
|
|
5584
6041
|
}
|
|
5585
6042
|
);
|
|
5586
6043
|
}
|
|
5587
|
-
var
|
|
6044
|
+
var t8 = getSemanticColors("dark");
|
|
5588
6045
|
var statusBgColors = {
|
|
5589
6046
|
completed: "rgba(46,213,115,0.15)",
|
|
5590
6047
|
current: "rgba(255,121,0,0.15)",
|
|
@@ -5602,14 +6059,14 @@ var statusBorderStyles = {
|
|
|
5602
6059
|
deload: { borderWidth: 1, borderColor: "rgba(186,41,150,0.3)" }
|
|
5603
6060
|
};
|
|
5604
6061
|
var textColors = {
|
|
5605
|
-
completed:
|
|
5606
|
-
current:
|
|
5607
|
-
next:
|
|
6062
|
+
completed: t8["status-success"],
|
|
6063
|
+
current: t8["brand-primary"],
|
|
6064
|
+
next: t8["brand-primary"],
|
|
5608
6065
|
upcoming: "#6B7280",
|
|
5609
6066
|
missed: "rgba(209,67,67,0.7)",
|
|
5610
6067
|
deload: WORKOUT_PILL_DELOAD
|
|
5611
6068
|
};
|
|
5612
|
-
function
|
|
6069
|
+
function usePulse2(enabled) {
|
|
5613
6070
|
const [opacity] = useState(() => new Animated.Value(1));
|
|
5614
6071
|
useEffect(() => {
|
|
5615
6072
|
if (!enabled) {
|
|
@@ -5647,7 +6104,7 @@ function WorkoutPill({
|
|
|
5647
6104
|
...props
|
|
5648
6105
|
}) {
|
|
5649
6106
|
const shouldPulse = pulse ?? status === "current";
|
|
5650
|
-
const pulseOpacity =
|
|
6107
|
+
const pulseOpacity = usePulse2(shouldPulse);
|
|
5651
6108
|
const isCompleted = status === "completed";
|
|
5652
6109
|
const isMissed = status === "missed";
|
|
5653
6110
|
const pill = /* @__PURE__ */ jsx(
|
|
@@ -5737,19 +6194,19 @@ function WorkoutPill({
|
|
|
5737
6194
|
}
|
|
5738
6195
|
return pill;
|
|
5739
6196
|
}
|
|
5740
|
-
var
|
|
6197
|
+
var t9 = getSemanticColors("dark");
|
|
5741
6198
|
var TRACK_BG = "#333333";
|
|
5742
6199
|
var LABEL_COLOR = "#6B7280";
|
|
5743
6200
|
var TARGET_LINE_COLOR = "rgba(33, 150, 243, 0.5)";
|
|
5744
6201
|
var AT_TARGET_GLOW = "0 0 5px 1px rgba(33, 150, 243, 0.35), 0 0 10px 3px rgba(33, 150, 243, 0.15)";
|
|
5745
6202
|
var ZONE = {
|
|
5746
|
-
building:
|
|
6203
|
+
building: t9["status-success"],
|
|
5747
6204
|
// below target ramp-up
|
|
5748
|
-
approaching:
|
|
6205
|
+
approaching: t9["status-warning"],
|
|
5749
6206
|
// nearing target
|
|
5750
|
-
target:
|
|
6207
|
+
target: t9["brand-primary"],
|
|
5751
6208
|
// exactly at target
|
|
5752
|
-
over1:
|
|
6209
|
+
over1: t9["status-error"],
|
|
5753
6210
|
// mild over-target
|
|
5754
6211
|
over2: WORKOUT_TOKENS.intensity.over2,
|
|
5755
6212
|
// moderate over-target
|
|
@@ -6172,13 +6629,13 @@ function MesoCard({
|
|
|
6172
6629
|
}
|
|
6173
6630
|
) });
|
|
6174
6631
|
}
|
|
6175
|
-
var
|
|
6632
|
+
var t10 = getSemanticColors("dark");
|
|
6176
6633
|
var dotColorMap = {
|
|
6177
6634
|
untrained: "#2C2C2C",
|
|
6178
|
-
behind:
|
|
6179
|
-
ontrack:
|
|
6180
|
-
target:
|
|
6181
|
-
over:
|
|
6635
|
+
behind: t10["brand-secondary"],
|
|
6636
|
+
ontrack: t10["status-success"],
|
|
6637
|
+
target: t10["brand-primary"],
|
|
6638
|
+
over: t10["status-error"]
|
|
6182
6639
|
};
|
|
6183
6640
|
function MuscleGroupChip({
|
|
6184
6641
|
name,
|
|
@@ -6252,10 +6709,10 @@ function MuscleGroupChip({
|
|
|
6252
6709
|
}
|
|
6253
6710
|
|
|
6254
6711
|
// src/components/custom/Workout/WorkoutCard.tsx
|
|
6255
|
-
var
|
|
6712
|
+
var t11 = getSemanticColors("dark");
|
|
6256
6713
|
var COLORS = {
|
|
6257
|
-
statusSuccess:
|
|
6258
|
-
brandPrimary:
|
|
6714
|
+
statusSuccess: t11["status-success"],
|
|
6715
|
+
brandPrimary: t11["brand-primary"],
|
|
6259
6716
|
borderDefault: "#1F1F1F",
|
|
6260
6717
|
brandPrimarySubtle: "rgba(255,121,0,0.06)"
|
|
6261
6718
|
};
|
|
@@ -6539,8 +6996,8 @@ function WeekLevel({ week, onSelectWorkout }) {
|
|
|
6539
6996
|
function WorkoutLevel({ workout, expandedExercise, onToggleExercise }) {
|
|
6540
6997
|
const exercises = workout.exercises.map((exercise, index) => ({
|
|
6541
6998
|
...exercise,
|
|
6542
|
-
|
|
6543
|
-
|
|
6999
|
+
expanded: index === expandedExercise,
|
|
7000
|
+
onExpandedChange: () => onToggleExercise(index)
|
|
6544
7001
|
}));
|
|
6545
7002
|
return /* @__PURE__ */ jsx(View, { testID: "program-planning-page-workout-level", children: /* @__PURE__ */ jsx(
|
|
6546
7003
|
WorkoutCard,
|
|
@@ -6669,6 +7126,6 @@ function ProgramPlanningPage({
|
|
|
6669
7126
|
);
|
|
6670
7127
|
}
|
|
6671
7128
|
|
|
6672
|
-
export { ActiveWorkoutPage, EXERCISE_DETAIL_TABS, ExerciseDetailPage, ProgramPlanningPage, buildBreadcrumbs, countCompletedSets, deriveExerciseStats, deriveNavLevel, deriveWorkoutProgress, findActiveExercise, findMeso, findWeek, findWorkout, groupExercises,
|
|
7129
|
+
export { ActiveWorkoutPage, EXERCISE_DETAIL_TABS, ExerciseDetailPage, ProgramPlanningPage, buildBreadcrumbs, countCompletedSets, deriveExerciseStats, deriveNavLevel, deriveWorkoutProgress, findActiveExercise, findMeso, findWeek, findWorkout, groupExercises, isUpcomingExercise, summarizeVbt, toActiveCardProps, toExerciseCardProps, toProgressBarMesos };
|
|
6673
7130
|
//# sourceMappingURL=pages.mjs.map
|
|
6674
7131
|
//# sourceMappingURL=pages.mjs.map
|