@titan-design/react-ui 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/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 +956 -128
- package/dist/index.d.ts +956 -128
- package/dist/index.js +2873 -981
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2826 -982
- package/dist/index.mjs.map +1 -1
- package/dist/{pages-MO0JCySL.d.mts → pages-DCFBqp79.d.mts} +289 -57
- package/dist/{pages-D7Jlssvp.d.ts → pages-_fI3-x7Z.d.ts} +289 -57
- package/dist/pages.d.mts +1 -1
- package/dist/pages.d.ts +1 -1
- package/dist/pages.js +1659 -799
- package/dist/pages.js.map +1 -1
- package/dist/pages.mjs +1660 -800
- 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/CircularTimer/CircularTimer.stories.tsx +90 -0
- package/src/components/custom/CircularTimer/CircularTimer.test.tsx +62 -0
- package/src/components/custom/CircularTimer/CircularTimer.tsx +112 -0
- package/src/components/custom/CircularTimer/index.ts +1 -0
- package/src/components/custom/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 +85 -0
- package/src/components/custom/Workout/FatigueMeter.tsx +86 -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 +193 -4
- package/src/components/custom/Workout/ReadinessCheck.stories.tsx +1 -1
- package/src/components/custom/Workout/RestTimer.stories.tsx +133 -3
- package/src/components/custom/Workout/RestTimer.test.tsx +73 -0
- package/src/components/custom/Workout/RestTimer.tsx +155 -59
- 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 +70 -1
- package/src/components/custom/Workout/TempoBar.test.tsx +119 -4
- package/src/components/custom/Workout/TempoBar.tsx +107 -22
- 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 +262 -56
- package/src/components/custom/Workout/VelocityStrip.test.tsx +440 -141
- package/src/components/custom/Workout/VelocityStrip.tsx +776 -141
- 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 +139 -0
- package/src/components/custom/Workout/ZoneTrack.test.tsx +234 -0
- package/src/components/custom/Workout/ZoneTrack.tsx +373 -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 +2 -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 +86 -3
- package/src/components/ui/alert/Alert.test.tsx +52 -0
- package/src/components/ui/alert/Alert.tsx +54 -20
- 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/progress/Progress.tsx +47 -22
- 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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useState, useMemo, useEffect, Fragment as Fragment$1, useSyncExternalStore, useCallback } from 'react';
|
|
2
|
-
import { Easing, Platform, View, Text,
|
|
2
|
+
import { Easing, Platform, View, Text, TextInput, Pressable, Animated, ScrollView } from 'react-native';
|
|
3
3
|
import { Fragment, jsx as jsx$1, jsxs as jsxs$1 } from 'react/jsx-runtime';
|
|
4
4
|
import { clsx } from 'clsx';
|
|
5
5
|
import { twMerge } from 'tailwind-merge';
|
|
@@ -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
|
|
@@ -36,6 +33,8 @@ var primitiveColors = {
|
|
|
36
33
|
900: "#111827"},
|
|
37
34
|
// Charcoal scale (dark backgrounds)
|
|
38
35
|
charcoal: {
|
|
36
|
+
0: "#6E6E6E",
|
|
37
|
+
100: "#4C4C4C",
|
|
39
38
|
200: "#3C3C3C",
|
|
40
39
|
300: "#2C2C2C",
|
|
41
40
|
400: "#1F1F1F",
|
|
@@ -65,11 +64,16 @@ var primitiveRamps = {
|
|
|
65
64
|
50: "#FFF7DD",
|
|
66
65
|
200: "#FFD352",
|
|
67
66
|
300: "#F9B415",
|
|
67
|
+
// pin
|
|
68
|
+
400: "#E08C00",
|
|
68
69
|
500: "#C27400"},
|
|
69
70
|
green: {
|
|
70
71
|
50: "#E3FFEE",
|
|
71
72
|
200: "#58F69E",
|
|
72
73
|
300: "#2ED573",
|
|
74
|
+
// pin
|
|
75
|
+
400: "#21C05D",
|
|
76
|
+
500: "#22A444",
|
|
73
77
|
600: "#298732"},
|
|
74
78
|
cyan: {
|
|
75
79
|
// pin
|
|
@@ -77,7 +81,8 @@ var primitiveRamps = {
|
|
|
77
81
|
500: "#2697B7",
|
|
78
82
|
600: "#307B9B",
|
|
79
83
|
700: "#2A617F",
|
|
80
|
-
800: "#22465F"
|
|
84
|
+
800: "#22465F",
|
|
85
|
+
900: "#0B3149"},
|
|
81
86
|
blue: {
|
|
82
87
|
50: "#EFF8FF",
|
|
83
88
|
300: "#78C2FF",
|
|
@@ -132,15 +137,22 @@ var WORKOUT_TOKENS = {
|
|
|
132
137
|
// Canonical 4-band performance scale — the single source for BOTH the
|
|
133
138
|
// VelocityStrip zone bars and the SetRow RPE color (TD-03.43). The direction
|
|
134
139
|
// 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
|
-
//
|
|
140
|
+
// fastest/best; for RPE, green = easiest. This is the [0,2,3,4] subsample of
|
|
141
|
+
// the canonical `sequentialEffort` primitive: an EVEN hue walk across the ramp
|
|
142
|
+
// (green → gold → orange → red). The earlier [0,1,2,4] spent two of its four
|
|
143
|
+
// bands on adjacent yellows (amber-200 + amber-300) and skipped orange, reading
|
|
144
|
+
// yellow-heavy with an abrupt jump to red; sampling amber-300 + orange-400
|
|
145
|
+
// instead gives four cleanly separable bands that hold at 3px strip scale.
|
|
146
|
+
// Consumed inline (RN).
|
|
138
147
|
scale: {
|
|
139
148
|
green: sequentialEffort[0],
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
// gold
|
|
149
|
+
// green-300
|
|
150
|
+
yellow: sequentialEffort[2],
|
|
151
|
+
// amber-300 (gold)
|
|
152
|
+
orange: sequentialEffort[3],
|
|
153
|
+
// orange-400 (true orange)
|
|
143
154
|
red: sequentialEffort[4]
|
|
155
|
+
// red-600
|
|
144
156
|
},
|
|
145
157
|
// Surface and border colors were removed here: those flip with the theme, so
|
|
146
158
|
// components use the CSS custom properties (var(--color-surface-*/border-*))
|
|
@@ -159,12 +171,6 @@ var WORKOUT_TOKENS = {
|
|
|
159
171
|
function roundRpe(rpe) {
|
|
160
172
|
return Math.round(rpe * 2) / 2;
|
|
161
173
|
}
|
|
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
174
|
function roundWeight(weight) {
|
|
169
175
|
return Math.round(weight);
|
|
170
176
|
}
|
|
@@ -192,14 +198,258 @@ function wrapJsx(fn) {
|
|
|
192
198
|
var jsx = wrapJsx(jsx$1);
|
|
193
199
|
var jsxs = wrapJsx(jsxs$1);
|
|
194
200
|
|
|
201
|
+
// src/components/custom/Workout/SegmentedBar.tsx
|
|
202
|
+
var PULSE_HALF_MS = 950;
|
|
203
|
+
var PULSE_MIN_OPACITY = 0.45;
|
|
204
|
+
var SEGMENTED_BAR_GAP = 5;
|
|
205
|
+
function usePulse(active) {
|
|
206
|
+
const [value] = useState(() => new Animated.Value(0));
|
|
207
|
+
useEffect(() => {
|
|
208
|
+
if (!active) return;
|
|
209
|
+
const loop = Animated.loop(
|
|
210
|
+
Animated.sequence([
|
|
211
|
+
Animated.timing(value, {
|
|
212
|
+
toValue: 1,
|
|
213
|
+
duration: PULSE_HALF_MS,
|
|
214
|
+
easing: Easing.inOut(Easing.ease),
|
|
215
|
+
useNativeDriver: false
|
|
216
|
+
}),
|
|
217
|
+
Animated.timing(value, {
|
|
218
|
+
toValue: 0,
|
|
219
|
+
duration: PULSE_HALF_MS,
|
|
220
|
+
easing: Easing.inOut(Easing.ease),
|
|
221
|
+
useNativeDriver: false
|
|
222
|
+
})
|
|
223
|
+
])
|
|
224
|
+
);
|
|
225
|
+
loop.start();
|
|
226
|
+
return () => loop.stop();
|
|
227
|
+
}, [active, value]);
|
|
228
|
+
return value;
|
|
229
|
+
}
|
|
230
|
+
function SegmentedBar({
|
|
231
|
+
segments,
|
|
232
|
+
height = 8,
|
|
233
|
+
gap = SEGMENTED_BAR_GAP,
|
|
234
|
+
radius = 2,
|
|
235
|
+
marker = null,
|
|
236
|
+
segmentTestID,
|
|
237
|
+
style,
|
|
238
|
+
...props
|
|
239
|
+
}) {
|
|
240
|
+
const pulse = usePulse(segments.some((s) => s.pulse));
|
|
241
|
+
return /* @__PURE__ */ jsxs(View, { style: [{ flexDirection: "row", height, gap, position: "relative" }, style], ...props, children: [
|
|
242
|
+
segments.map((seg, i) => {
|
|
243
|
+
const fillStyle = {
|
|
244
|
+
width: `${(seg.fill ?? 1) * 100}%`,
|
|
245
|
+
height: "100%",
|
|
246
|
+
backgroundColor: seg.color,
|
|
247
|
+
opacity: seg.opacity
|
|
248
|
+
};
|
|
249
|
+
const testID = segmentTestID?.(seg, i) ?? "segmented-bar-segment";
|
|
250
|
+
return /* @__PURE__ */ jsx(
|
|
251
|
+
View,
|
|
252
|
+
{
|
|
253
|
+
style: {
|
|
254
|
+
flex: seg.weight ?? 1,
|
|
255
|
+
minWidth: 0,
|
|
256
|
+
height: "100%",
|
|
257
|
+
marginLeft: seg.leadingGap,
|
|
258
|
+
borderRadius: radius,
|
|
259
|
+
overflow: "hidden"
|
|
260
|
+
},
|
|
261
|
+
children: seg.pulse ? /* @__PURE__ */ jsx(
|
|
262
|
+
Animated.View,
|
|
263
|
+
{
|
|
264
|
+
style: {
|
|
265
|
+
...fillStyle,
|
|
266
|
+
...seg.pulseColor ? {
|
|
267
|
+
backgroundColor: pulse.interpolate({
|
|
268
|
+
inputRange: [0, 1],
|
|
269
|
+
outputRange: seg.pulseColor
|
|
270
|
+
})
|
|
271
|
+
} : {
|
|
272
|
+
opacity: pulse.interpolate({
|
|
273
|
+
inputRange: [0, 1],
|
|
274
|
+
outputRange: [PULSE_MIN_OPACITY, 1]
|
|
275
|
+
})
|
|
276
|
+
}
|
|
277
|
+
},
|
|
278
|
+
accessibilityElementsHidden: true,
|
|
279
|
+
testID
|
|
280
|
+
}
|
|
281
|
+
) : /* @__PURE__ */ jsx(View, { style: fillStyle, accessibilityElementsHidden: true, testID })
|
|
282
|
+
},
|
|
283
|
+
i
|
|
284
|
+
);
|
|
285
|
+
}),
|
|
286
|
+
marker ? /* @__PURE__ */ jsx(
|
|
287
|
+
View,
|
|
288
|
+
{
|
|
289
|
+
style: {
|
|
290
|
+
position: "absolute",
|
|
291
|
+
left: `${marker.position * 100}%`,
|
|
292
|
+
top: 0,
|
|
293
|
+
bottom: 0,
|
|
294
|
+
width: 2,
|
|
295
|
+
backgroundColor: marker.color
|
|
296
|
+
},
|
|
297
|
+
accessibilityElementsHidden: true,
|
|
298
|
+
testID: "segmented-bar-marker"
|
|
299
|
+
}
|
|
300
|
+
) : null
|
|
301
|
+
] });
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// src/components/custom/Workout/SetBar.tsx
|
|
305
|
+
var SET_STRIP_ZONES = {
|
|
306
|
+
slow: primitiveRamps.red[600],
|
|
307
|
+
moderate: primitiveRamps.orange[400],
|
|
308
|
+
fast: primitiveRamps.amber[300],
|
|
309
|
+
fastest: primitiveRamps.green[300]
|
|
310
|
+
};
|
|
311
|
+
var TODO_COLOR = primitiveColors.charcoal[300];
|
|
312
|
+
var SET_STRIP_VARIABLE_COLOR = primitiveRamps.cyan[900];
|
|
313
|
+
var NOTCH_GAP = 2;
|
|
314
|
+
var CLUSTER_GAP = 3;
|
|
315
|
+
var MYO_UPCOMING_CLUSTERS = 3;
|
|
316
|
+
var MYO_UPCOMING_CLUSTER_LEN = 5;
|
|
317
|
+
var MYO_UPCOMING_FADE = 0.6;
|
|
318
|
+
var PULSE_RANGE = {
|
|
319
|
+
[SET_STRIP_ZONES.slow]: [primitiveRamps.red[500], primitiveRamps.red[700]],
|
|
320
|
+
[SET_STRIP_ZONES.moderate]: [primitiveRamps.orange[300], primitiveRamps.orange[500]],
|
|
321
|
+
[SET_STRIP_ZONES.fast]: [primitiveRamps.amber[200], primitiveRamps.amber[400]],
|
|
322
|
+
[SET_STRIP_ZONES.fastest]: [primitiveRamps.green[200], primitiveRamps.green[400]]
|
|
323
|
+
};
|
|
324
|
+
function velocityZoneColor(v) {
|
|
325
|
+
if (v < 0.5) return SET_STRIP_ZONES.slow;
|
|
326
|
+
if (v < 0.75) return SET_STRIP_ZONES.moderate;
|
|
327
|
+
if (v < 1) return SET_STRIP_ZONES.fast;
|
|
328
|
+
return SET_STRIP_ZONES.fastest;
|
|
329
|
+
}
|
|
330
|
+
function chunkedSegments(chunks, gap) {
|
|
331
|
+
return chunks.flatMap(
|
|
332
|
+
(vels, ci) => vels.map((v, ri) => ({
|
|
333
|
+
color: velocityZoneColor(v),
|
|
334
|
+
...ci > 0 && ri === 0 ? { leadingGap: gap } : {}
|
|
335
|
+
}))
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
function rangeSegments(floor, max, doneVels) {
|
|
339
|
+
return Array.from({ length: max }, (_, i) => {
|
|
340
|
+
if (i < doneVels.length) return { color: velocityZoneColor(doneVels[i]) };
|
|
341
|
+
return { color: i < floor ? TODO_COLOR : SET_STRIP_VARIABLE_COLOR };
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
function myoUpcomingSegments(activationLen, clusterCount) {
|
|
345
|
+
const activation = Array.from({ length: activationLen }, () => ({ color: TODO_COLOR }));
|
|
346
|
+
const trail = Array.from(
|
|
347
|
+
{ length: clusterCount },
|
|
348
|
+
(_, ci) => Array.from({ length: MYO_UPCOMING_CLUSTER_LEN }, (_2, ri) => ({
|
|
349
|
+
color: SET_STRIP_VARIABLE_COLOR,
|
|
350
|
+
opacity: MYO_UPCOMING_FADE ** ci,
|
|
351
|
+
...ri === 0 ? { leadingGap: CLUSTER_GAP } : {}
|
|
352
|
+
}))
|
|
353
|
+
).flat();
|
|
354
|
+
return [...activation, ...trail];
|
|
355
|
+
}
|
|
356
|
+
function setSegments(set) {
|
|
357
|
+
if (set.status === "todo") {
|
|
358
|
+
return [{ color: TODO_COLOR }];
|
|
359
|
+
}
|
|
360
|
+
if (set.status === "done") {
|
|
361
|
+
return set.velocities.map((v) => ({ color: velocityZoneColor(v) }));
|
|
362
|
+
}
|
|
363
|
+
if (set.status === "range") {
|
|
364
|
+
return rangeSegments(set.floor, set.max, set.doneVels);
|
|
365
|
+
}
|
|
366
|
+
if (set.status === "drop") {
|
|
367
|
+
return chunkedSegments(set.subloads, NOTCH_GAP);
|
|
368
|
+
}
|
|
369
|
+
if (set.status === "myo") {
|
|
370
|
+
return chunkedSegments([set.activation, ...set.clusters], CLUSTER_GAP);
|
|
371
|
+
}
|
|
372
|
+
if (set.status === "myo-upcoming") {
|
|
373
|
+
return myoUpcomingSegments(set.activationLen, set.clusterCount ?? MYO_UPCOMING_CLUSTERS);
|
|
374
|
+
}
|
|
375
|
+
const performed = set.velocities.map((v) => {
|
|
376
|
+
const color = velocityZoneColor(v);
|
|
377
|
+
return { color, pulse: true, pulseColor: PULSE_RANGE[color] };
|
|
378
|
+
});
|
|
379
|
+
const remaining = Math.max(0, set.planned - set.velocities.length);
|
|
380
|
+
return [...performed, ...Array.from({ length: remaining }, () => ({ color: TODO_COLOR }))];
|
|
381
|
+
}
|
|
382
|
+
function setBarSegmentTestID(seg) {
|
|
383
|
+
if (seg.pulse) return "set-strip-pulse";
|
|
384
|
+
if (seg.color === SET_STRIP_VARIABLE_COLOR) return "set-strip-variable";
|
|
385
|
+
return seg.color === TODO_COLOR ? "set-strip-empty" : "set-strip-fill";
|
|
386
|
+
}
|
|
387
|
+
function SetBar({ set, height = 8 }) {
|
|
388
|
+
return /* @__PURE__ */ jsx(
|
|
389
|
+
SegmentedBar,
|
|
390
|
+
{
|
|
391
|
+
segments: setSegments(set),
|
|
392
|
+
height,
|
|
393
|
+
gap: 0,
|
|
394
|
+
radius: 0,
|
|
395
|
+
segmentTestID: setBarSegmentTestID,
|
|
396
|
+
style: { flex: 1, minWidth: 0, borderRadius: 2, overflow: "hidden" },
|
|
397
|
+
testID: "set-strip-set"
|
|
398
|
+
}
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// src/components/custom/Workout/SetStrip.tsx
|
|
403
|
+
var SET_STRIP_GAP = 5;
|
|
404
|
+
function setCategory(set) {
|
|
405
|
+
switch (set.status) {
|
|
406
|
+
case "done":
|
|
407
|
+
case "drop":
|
|
408
|
+
case "myo":
|
|
409
|
+
return "done";
|
|
410
|
+
case "active":
|
|
411
|
+
return "active";
|
|
412
|
+
case "range":
|
|
413
|
+
return set.doneVels.length > 0 ? "active" : "upcoming";
|
|
414
|
+
case "todo":
|
|
415
|
+
case "myo-upcoming":
|
|
416
|
+
return "upcoming";
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
function describeSets(sets) {
|
|
420
|
+
const done = sets.filter((s) => setCategory(s) === "done").length;
|
|
421
|
+
const active = sets.filter((s) => setCategory(s) === "active").length;
|
|
422
|
+
const upcoming = sets.filter((s) => setCategory(s) === "upcoming").length;
|
|
423
|
+
return `Set progress: ${done} done, ${active} in progress, ${upcoming} upcoming`;
|
|
424
|
+
}
|
|
425
|
+
function SetStrip({ sets, height = 8, className, ...props }) {
|
|
426
|
+
return /* @__PURE__ */ jsx(
|
|
427
|
+
View,
|
|
428
|
+
{
|
|
429
|
+
className,
|
|
430
|
+
style: { flexDirection: "row", width: "100%", height, gap: SET_STRIP_GAP },
|
|
431
|
+
accessibilityRole: "image",
|
|
432
|
+
accessibilityLabel: describeSets(sets),
|
|
433
|
+
testID: "set-strip",
|
|
434
|
+
...props,
|
|
435
|
+
children: sets.map((set, i) => /* @__PURE__ */ jsx(SetBar, { set, height }, i))
|
|
436
|
+
}
|
|
437
|
+
);
|
|
438
|
+
}
|
|
439
|
+
|
|
195
440
|
// src/components/custom/Workout/VelocityStrip.tsx
|
|
196
441
|
var VEL_COLORS = WORKOUT_TOKENS.scale;
|
|
197
|
-
var
|
|
198
|
-
speed:
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
442
|
+
var bandIdToEffortColor = {
|
|
443
|
+
speed: sequentialEffort[0],
|
|
444
|
+
// green-300
|
|
445
|
+
power: sequentialEffort[2],
|
|
446
|
+
// amber-300 (gold)
|
|
447
|
+
strengthSpeed: sequentialEffort[3],
|
|
448
|
+
// orange-400
|
|
449
|
+
maximalStrength: sequentialEffort[4],
|
|
450
|
+
// red-600
|
|
451
|
+
grinding: sequentialEffort[5]
|
|
452
|
+
// red-700 — the distinct 5th band
|
|
203
453
|
};
|
|
204
454
|
function getVelocityZoneColor(velocity) {
|
|
205
455
|
if (velocity >= 1) return "vel-green";
|
|
@@ -238,14 +488,140 @@ function classifyBand(velocity, bands) {
|
|
|
238
488
|
return bands[bands.length - 1];
|
|
239
489
|
}
|
|
240
490
|
function bandColor(band) {
|
|
241
|
-
|
|
242
|
-
return token ? VEL_COLORS[token] : VEL_COLORS.green;
|
|
491
|
+
return bandIdToEffortColor[band.id] ?? VEL_COLORS.green;
|
|
243
492
|
}
|
|
244
493
|
function getLossStyle(loss) {
|
|
245
494
|
if (loss > 25) return { color: VEL_COLORS.red };
|
|
246
495
|
if (loss > 20) return { color: VEL_COLORS.orange };
|
|
247
496
|
return null;
|
|
248
497
|
}
|
|
498
|
+
var REP_GAP = 2;
|
|
499
|
+
var WIDE_GAP = 8;
|
|
500
|
+
var TODO_COLOR2 = primitiveColors.charcoal[300];
|
|
501
|
+
var CONTINUE_OUTLINE = primitiveRamps.cyan[800];
|
|
502
|
+
function deriveDoneVelocities(set) {
|
|
503
|
+
switch (set.type) {
|
|
504
|
+
case "drop":
|
|
505
|
+
return set.subloads.flat();
|
|
506
|
+
case "myo":
|
|
507
|
+
return [...set.activation, ...set.clusters.flat()];
|
|
508
|
+
default:
|
|
509
|
+
return set.velocities;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
function chunkedRepSlots(chunks) {
|
|
513
|
+
const slots = [];
|
|
514
|
+
chunks.forEach((chunk) => {
|
|
515
|
+
chunk.forEach((velocity, ri) => {
|
|
516
|
+
const first = slots.length === 0;
|
|
517
|
+
slots.push({ kind: "rep", velocity, leadingGap: first ? 0 : ri === 0 ? WIDE_GAP : REP_GAP });
|
|
518
|
+
});
|
|
519
|
+
});
|
|
520
|
+
return slots;
|
|
521
|
+
}
|
|
522
|
+
function buildSlots(set) {
|
|
523
|
+
switch (set.type) {
|
|
524
|
+
case "straight": {
|
|
525
|
+
const total = Math.max(set.velocities.length, set.planned ?? set.velocities.length);
|
|
526
|
+
return Array.from({ length: total }, (_, i) => {
|
|
527
|
+
const done = i < set.velocities.length;
|
|
528
|
+
return {
|
|
529
|
+
kind: done ? "rep" : "todo",
|
|
530
|
+
velocity: done ? set.velocities[i] : void 0,
|
|
531
|
+
leadingGap: i === 0 ? 0 : REP_GAP
|
|
532
|
+
};
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
case "range": {
|
|
536
|
+
const total = Math.max(set.max, set.velocities.length);
|
|
537
|
+
return Array.from({ length: total }, (_, i) => {
|
|
538
|
+
const kind = i < set.velocities.length ? "rep" : i < set.floor ? "todo" : "variable";
|
|
539
|
+
return {
|
|
540
|
+
kind,
|
|
541
|
+
velocity: kind === "rep" ? set.velocities[i] : void 0,
|
|
542
|
+
leadingGap: i === 0 ? 0 : REP_GAP
|
|
543
|
+
};
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
case "amrap": {
|
|
547
|
+
const reps = set.velocities.map((velocity, i) => ({
|
|
548
|
+
kind: "rep",
|
|
549
|
+
velocity,
|
|
550
|
+
leadingGap: i === 0 ? 0 : REP_GAP
|
|
551
|
+
}));
|
|
552
|
+
reps.push({ kind: "continue", leadingGap: reps.length === 0 ? 0 : REP_GAP });
|
|
553
|
+
return reps;
|
|
554
|
+
}
|
|
555
|
+
case "drop":
|
|
556
|
+
return chunkedRepSlots(set.subloads);
|
|
557
|
+
case "myo": {
|
|
558
|
+
const slots = chunkedRepSlots([set.activation, ...set.clusters]);
|
|
559
|
+
if (set.open) slots.push({ kind: "continue", leadingGap: slots.length === 0 ? 0 : REP_GAP });
|
|
560
|
+
return slots;
|
|
561
|
+
}
|
|
562
|
+
case "cluster": {
|
|
563
|
+
const total = Math.max(set.velocities.length, set.planned ?? set.velocities.length);
|
|
564
|
+
return Array.from({ length: total }, (_, i) => {
|
|
565
|
+
const done = i < set.velocities.length;
|
|
566
|
+
const boundary = i > 0 && i % set.groupSize === 0;
|
|
567
|
+
return {
|
|
568
|
+
kind: done ? "rep" : "todo",
|
|
569
|
+
velocity: done ? set.velocities[i] : void 0,
|
|
570
|
+
leadingGap: i === 0 ? 0 : boundary ? WIDE_GAP : REP_GAP
|
|
571
|
+
};
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
function setAccessibilityLabel(set, repCount) {
|
|
577
|
+
switch (set.type) {
|
|
578
|
+
case "straight":
|
|
579
|
+
return `Velocity strip, straight set, ${repCount} reps`;
|
|
580
|
+
case "range":
|
|
581
|
+
return `Velocity strip, rep-range set, floor ${set.floor} to ${set.max}, ${repCount} reps done`;
|
|
582
|
+
case "amrap":
|
|
583
|
+
return `Velocity strip, AMRAP set, ${repCount} reps and counting`;
|
|
584
|
+
case "drop":
|
|
585
|
+
return `Velocity strip, drop set, ${set.subloads.length} loads, ${repCount} reps`;
|
|
586
|
+
case "myo":
|
|
587
|
+
return `Velocity strip, myo-reps set, ${set.clusters.length} clusters${set.open ? ", open" : ""}, ${repCount} reps`;
|
|
588
|
+
case "cluster":
|
|
589
|
+
return `Velocity strip, cluster set, groups of ${set.groupSize}, ${repCount} reps`;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
function slotAccessibilityLabel(slot, index) {
|
|
593
|
+
switch (slot.kind) {
|
|
594
|
+
case "rep":
|
|
595
|
+
return `Rep ${index + 1}: ${formatVelocity(slot.velocity ?? 0)} meters per second`;
|
|
596
|
+
case "todo":
|
|
597
|
+
return `Rep ${index + 1}: planned`;
|
|
598
|
+
case "variable":
|
|
599
|
+
return `Rep ${index + 1}: variable`;
|
|
600
|
+
case "continue":
|
|
601
|
+
return "Keep going";
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
var EXPANDED_TODO_STUB_PCT = 16;
|
|
605
|
+
var EXPANDED_ENCODED_PCT = 45;
|
|
606
|
+
var EXPANDED_HEIGHT = 60;
|
|
607
|
+
var FIXED_MAX_VELOCITY = 1.15;
|
|
608
|
+
var BARE_STUB_HEIGHT = 3;
|
|
609
|
+
var HERO_HEIGHT = 220;
|
|
610
|
+
var HERO_LABEL_HEADROOM = 20;
|
|
611
|
+
var HERO_BAR_GAP = 8;
|
|
612
|
+
var HERO_BAR_MAX_WIDTH = 52;
|
|
613
|
+
var HERO_BAR_RADIUS = 5;
|
|
614
|
+
var HERO_MIN_BAR_HEIGHT = 4;
|
|
615
|
+
var HERO_PENDING_COLOR = primitiveColors.charcoal[100];
|
|
616
|
+
var HERO_REFERENCE_COLOR = primitiveColors.charcoal[0];
|
|
617
|
+
function scaleDenominator(scale, maxVelocity) {
|
|
618
|
+
return scale === "fixed" ? FIXED_MAX_VELOCITY : maxVelocity * 1.15;
|
|
619
|
+
}
|
|
620
|
+
function bareSlotHeight(slot, height, denom) {
|
|
621
|
+
if (slot.kind !== "rep" || denom <= 0) return BARE_STUB_HEIGHT;
|
|
622
|
+
const ratio = Math.min(1, (slot.velocity ?? 0) / denom);
|
|
623
|
+
return Math.max(BARE_STUB_HEIGHT, ratio * height);
|
|
624
|
+
}
|
|
249
625
|
function getReducedMotionPreference() {
|
|
250
626
|
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
|
|
251
627
|
return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
@@ -265,33 +641,11 @@ function usePrefersReducedMotion() {
|
|
|
265
641
|
var ANIMATION_DURATION = 400;
|
|
266
642
|
var ANIMATION_EASING = Easing.bezier(0.22, 1, 0.36, 1);
|
|
267
643
|
var POP_EASING = Easing.bezier(0.34, 1.56, 0.64, 1);
|
|
268
|
-
function
|
|
269
|
-
velocities,
|
|
270
|
-
zones,
|
|
271
|
-
liveRepIndex,
|
|
272
|
-
expanded = false,
|
|
273
|
-
onToggle,
|
|
274
|
-
onRepPress,
|
|
275
|
-
variant = "full",
|
|
276
|
-
showInfo = true,
|
|
277
|
-
className,
|
|
278
|
-
...props
|
|
279
|
-
}) {
|
|
280
|
-
const maxVelocity = Math.max(...velocities, 0);
|
|
281
|
-
const meanVelocity = calculateMeanVelocity(velocities);
|
|
282
|
-
const loss = calculateVelocityLoss(velocities);
|
|
283
|
-
const hasZones = zones != null && zones.length > 0;
|
|
284
|
-
const barColorFor = (v) => hasZones ? bandColor(classifyBand(v, zones)) : zoneHexMap[getVelocityZoneColor(v)];
|
|
285
|
-
const meanZone = hasZones ? classifyBand(meanVelocity, zones)?.label ?? "" : getVelocityZoneName(meanVelocity);
|
|
644
|
+
function useLiveRepPop(active, liveRepIndex, liveVelocity, isNewPeak) {
|
|
286
645
|
const prefersReducedMotion = usePrefersReducedMotion();
|
|
287
|
-
const [heightAnim] = useState(() => new Animated.Value(expanded ? 60 : 3));
|
|
288
|
-
const [labelOpacity] = useState(() => new Animated.Value(expanded ? 1 : 0));
|
|
289
|
-
const [infoOpacity] = useState(() => new Animated.Value(expanded ? 1 : 0));
|
|
290
646
|
const [liveScale] = useState(() => new Animated.Value(1));
|
|
291
|
-
const liveVelocity = liveRepIndex != null ? velocities[liveRepIndex] : void 0;
|
|
292
|
-
const isNewPeak = liveVelocity != null && maxVelocity > 0 && liveVelocity === maxVelocity;
|
|
293
647
|
useEffect(() => {
|
|
294
|
-
if (
|
|
648
|
+
if (!active || liveRepIndex == null || liveVelocity == null) return;
|
|
295
649
|
if (prefersReducedMotion) {
|
|
296
650
|
liveScale.setValue(1);
|
|
297
651
|
return;
|
|
@@ -321,12 +675,201 @@ function VelocityStrip({
|
|
|
321
675
|
useNativeDriver: true
|
|
322
676
|
}).start();
|
|
323
677
|
}
|
|
324
|
-
}, [
|
|
678
|
+
}, [active, liveRepIndex, liveVelocity, isNewPeak, prefersReducedMotion, liveScale]);
|
|
679
|
+
return liveScale;
|
|
680
|
+
}
|
|
681
|
+
function HeroVelocityChart({
|
|
682
|
+
doneVelocities,
|
|
683
|
+
barColorFor,
|
|
684
|
+
scaleDenom,
|
|
685
|
+
referenceVelocity,
|
|
686
|
+
liveRepIndex,
|
|
687
|
+
isNewPeak,
|
|
688
|
+
targetReps,
|
|
689
|
+
height,
|
|
690
|
+
className,
|
|
691
|
+
viewProps
|
|
692
|
+
}) {
|
|
693
|
+
const liveVelocity = liveRepIndex != null ? doneVelocities[liveRepIndex] : void 0;
|
|
694
|
+
const liveScale = useLiveRepPop(true, liveRepIndex, liveVelocity, isNewPeak);
|
|
695
|
+
const plotHeight = Math.max(0, height - HERO_LABEL_HEADROOM);
|
|
696
|
+
const barHeight = (velocity) => scaleDenom > 0 ? Math.max(HERO_MIN_BAR_HEIGHT, Math.min(1, velocity / scaleDenom) * plotHeight) : HERO_MIN_BAR_HEIGHT;
|
|
697
|
+
const pendingCount = Math.max(0, (targetReps ?? 0) - doneVelocities.length);
|
|
698
|
+
const referencePx = referenceVelocity > 0 && scaleDenom > 0 ? Math.min(plotHeight, referenceVelocity / scaleDenom * plotHeight) : 0;
|
|
699
|
+
const repCount = doneVelocities.length;
|
|
700
|
+
const total = Math.max(repCount, targetReps ?? repCount);
|
|
701
|
+
const label = referenceVelocity > 0 ? `Velocity chart, ${repCount} of ${total} reps, best ${formatVelocity(referenceVelocity)} meters per second` : `Velocity chart, ${repCount} of ${total} reps`;
|
|
702
|
+
const { style: externalStyle, ...restProps } = viewProps;
|
|
703
|
+
return /* @__PURE__ */ jsx(
|
|
704
|
+
View,
|
|
705
|
+
{
|
|
706
|
+
className,
|
|
707
|
+
style: [{ height }, externalStyle],
|
|
708
|
+
accessibilityRole: "image",
|
|
709
|
+
accessibilityLabel: label,
|
|
710
|
+
testID: "velocity-strip-hero",
|
|
711
|
+
...restProps,
|
|
712
|
+
children: /* @__PURE__ */ jsxs(
|
|
713
|
+
View,
|
|
714
|
+
{
|
|
715
|
+
style: {
|
|
716
|
+
flex: 1,
|
|
717
|
+
flexDirection: "row",
|
|
718
|
+
alignItems: "flex-end",
|
|
719
|
+
gap: HERO_BAR_GAP,
|
|
720
|
+
position: "relative",
|
|
721
|
+
borderBottomWidth: 2,
|
|
722
|
+
borderBottomColor: HERO_PENDING_COLOR
|
|
723
|
+
},
|
|
724
|
+
children: [
|
|
725
|
+
referencePx > 0 && /* @__PURE__ */ jsx(
|
|
726
|
+
View,
|
|
727
|
+
{
|
|
728
|
+
accessibilityElementsHidden: true,
|
|
729
|
+
style: {
|
|
730
|
+
position: "absolute",
|
|
731
|
+
left: 0,
|
|
732
|
+
right: 0,
|
|
733
|
+
bottom: referencePx,
|
|
734
|
+
borderTopWidth: 1,
|
|
735
|
+
borderStyle: "dashed",
|
|
736
|
+
borderColor: HERO_REFERENCE_COLOR,
|
|
737
|
+
pointerEvents: "none"
|
|
738
|
+
},
|
|
739
|
+
testID: "velocity-hero-reference"
|
|
740
|
+
}
|
|
741
|
+
),
|
|
742
|
+
doneVelocities.map((velocity, i) => {
|
|
743
|
+
const isLive = liveRepIndex === i;
|
|
744
|
+
return /* @__PURE__ */ jsxs(
|
|
745
|
+
View,
|
|
746
|
+
{
|
|
747
|
+
accessibilityElementsHidden: true,
|
|
748
|
+
style: {
|
|
749
|
+
flex: 1,
|
|
750
|
+
maxWidth: HERO_BAR_MAX_WIDTH,
|
|
751
|
+
height: "100%",
|
|
752
|
+
alignItems: "center",
|
|
753
|
+
justifyContent: "flex-end"
|
|
754
|
+
},
|
|
755
|
+
children: [
|
|
756
|
+
/* @__PURE__ */ jsx(
|
|
757
|
+
Text,
|
|
758
|
+
{
|
|
759
|
+
className: "text-text-primary",
|
|
760
|
+
style: { fontSize: 12, fontWeight: "800", marginBottom: 4 },
|
|
761
|
+
testID: `velocity-label-${i}`,
|
|
762
|
+
children: formatVelocity(velocity)
|
|
763
|
+
}
|
|
764
|
+
),
|
|
765
|
+
/* @__PURE__ */ jsx(
|
|
766
|
+
Animated.View,
|
|
767
|
+
{
|
|
768
|
+
style: [
|
|
769
|
+
{
|
|
770
|
+
width: "100%",
|
|
771
|
+
height: barHeight(velocity),
|
|
772
|
+
borderTopLeftRadius: HERO_BAR_RADIUS,
|
|
773
|
+
borderTopRightRadius: HERO_BAR_RADIUS,
|
|
774
|
+
backgroundColor: barColorFor(velocity)
|
|
775
|
+
},
|
|
776
|
+
isLive ? { transform: [{ scale: liveScale }] } : null
|
|
777
|
+
],
|
|
778
|
+
testID: `velocity-bar-${i}`
|
|
779
|
+
}
|
|
780
|
+
)
|
|
781
|
+
]
|
|
782
|
+
},
|
|
783
|
+
i
|
|
784
|
+
);
|
|
785
|
+
}),
|
|
786
|
+
Array.from({ length: pendingCount }, (_, i) => /* @__PURE__ */ jsx(
|
|
787
|
+
View,
|
|
788
|
+
{
|
|
789
|
+
accessibilityElementsHidden: true,
|
|
790
|
+
style: {
|
|
791
|
+
flex: 1,
|
|
792
|
+
maxWidth: HERO_BAR_MAX_WIDTH,
|
|
793
|
+
height: "100%",
|
|
794
|
+
alignItems: "center",
|
|
795
|
+
justifyContent: "flex-end"
|
|
796
|
+
},
|
|
797
|
+
children: /* @__PURE__ */ jsx(
|
|
798
|
+
View,
|
|
799
|
+
{
|
|
800
|
+
style: {
|
|
801
|
+
width: "100%",
|
|
802
|
+
height: HERO_MIN_BAR_HEIGHT * 3,
|
|
803
|
+
borderWidth: 1,
|
|
804
|
+
borderStyle: "dashed",
|
|
805
|
+
borderColor: HERO_PENDING_COLOR,
|
|
806
|
+
borderTopLeftRadius: HERO_BAR_RADIUS,
|
|
807
|
+
borderTopRightRadius: HERO_BAR_RADIUS
|
|
808
|
+
},
|
|
809
|
+
testID: "velocity-slot-todo"
|
|
810
|
+
}
|
|
811
|
+
)
|
|
812
|
+
},
|
|
813
|
+
`pending-${i}`
|
|
814
|
+
))
|
|
815
|
+
]
|
|
816
|
+
}
|
|
817
|
+
)
|
|
818
|
+
}
|
|
819
|
+
);
|
|
820
|
+
}
|
|
821
|
+
function VelocityStrip({
|
|
822
|
+
velocities,
|
|
823
|
+
set,
|
|
824
|
+
zones,
|
|
825
|
+
liveRepIndex,
|
|
826
|
+
expanded = true,
|
|
827
|
+
onToggle,
|
|
828
|
+
onRepPress,
|
|
829
|
+
variant = "expanded",
|
|
830
|
+
height = EXPANDED_HEIGHT,
|
|
831
|
+
targetReps,
|
|
832
|
+
scale = "peak",
|
|
833
|
+
showNumbers = true,
|
|
834
|
+
showInfo = true,
|
|
835
|
+
className,
|
|
836
|
+
...props
|
|
837
|
+
}) {
|
|
838
|
+
const doneVelocities = set ? deriveDoneVelocities(set) : velocities ?? [];
|
|
839
|
+
const slots = set ? buildSlots(set) : doneVelocities.map((v, i) => ({
|
|
840
|
+
kind: "rep",
|
|
841
|
+
velocity: v,
|
|
842
|
+
leadingGap: i === 0 ? 0 : REP_GAP
|
|
843
|
+
}));
|
|
844
|
+
const maxVelocity = Math.max(...doneVelocities, 0);
|
|
845
|
+
const meanVelocity = calculateMeanVelocity(doneVelocities);
|
|
846
|
+
const loss = calculateVelocityLoss(doneVelocities);
|
|
847
|
+
const framed = showNumbers || showInfo;
|
|
848
|
+
const scaleDenom = scaleDenominator(scale, maxVelocity);
|
|
849
|
+
const hasZones = zones != null && zones.length > 0;
|
|
850
|
+
const barColorFor = (v) => hasZones ? bandColor(classifyBand(v, zones)) : zoneHexMap[getVelocityZoneColor(v)];
|
|
851
|
+
const slotColor = (slot) => {
|
|
852
|
+
if (slot.kind === "rep") return barColorFor(slot.velocity ?? 0);
|
|
853
|
+
if (slot.kind === "todo") return TODO_COLOR2;
|
|
854
|
+
return SET_STRIP_VARIABLE_COLOR;
|
|
855
|
+
};
|
|
856
|
+
const meanZone = hasZones ? classifyBand(meanVelocity, zones)?.label ?? "" : getVelocityZoneName(meanVelocity);
|
|
857
|
+
const [heightAnim] = useState(() => new Animated.Value(expanded ? height : 3));
|
|
858
|
+
const [labelOpacity] = useState(() => new Animated.Value(expanded ? 1 : 0));
|
|
859
|
+
const [infoOpacity] = useState(() => new Animated.Value(expanded ? 1 : 0));
|
|
860
|
+
const liveVelocity = liveRepIndex != null ? doneVelocities[liveRepIndex] : void 0;
|
|
861
|
+
const isNewPeak = liveVelocity != null && maxVelocity > 0 && liveVelocity === maxVelocity;
|
|
862
|
+
const liveScale = useLiveRepPop(
|
|
863
|
+
variant === "expanded" && framed,
|
|
864
|
+
liveRepIndex,
|
|
865
|
+
liveVelocity,
|
|
866
|
+
isNewPeak
|
|
867
|
+
);
|
|
325
868
|
useEffect(() => {
|
|
326
|
-
if (variant
|
|
869
|
+
if (variant !== "expanded" || !framed) return;
|
|
327
870
|
Animated.parallel([
|
|
328
871
|
Animated.timing(heightAnim, {
|
|
329
|
-
toValue: expanded ?
|
|
872
|
+
toValue: expanded ? height : 3,
|
|
330
873
|
duration: ANIMATION_DURATION,
|
|
331
874
|
easing: ANIMATION_EASING,
|
|
332
875
|
useNativeDriver: false
|
|
@@ -344,39 +887,136 @@ function VelocityStrip({
|
|
|
344
887
|
useNativeDriver: false
|
|
345
888
|
})
|
|
346
889
|
]).start();
|
|
347
|
-
}, [expanded, variant, heightAnim, labelOpacity, infoOpacity]);
|
|
890
|
+
}, [expanded, variant, framed, height, heightAnim, labelOpacity, infoOpacity]);
|
|
891
|
+
if (set == null && velocities == null) return null;
|
|
892
|
+
const repCount = doneVelocities.length;
|
|
893
|
+
const miniLabel = set ? setAccessibilityLabel(set, repCount) : `Velocity strip, ${repCount} reps`;
|
|
894
|
+
if (variant === "hero") {
|
|
895
|
+
const heroHeight = height === EXPANDED_HEIGHT ? HERO_HEIGHT : height;
|
|
896
|
+
return /* @__PURE__ */ jsx(
|
|
897
|
+
HeroVelocityChart,
|
|
898
|
+
{
|
|
899
|
+
doneVelocities,
|
|
900
|
+
barColorFor,
|
|
901
|
+
scaleDenom,
|
|
902
|
+
referenceVelocity: maxVelocity,
|
|
903
|
+
liveRepIndex,
|
|
904
|
+
isNewPeak,
|
|
905
|
+
targetReps,
|
|
906
|
+
height: heroHeight,
|
|
907
|
+
className,
|
|
908
|
+
viewProps: props
|
|
909
|
+
}
|
|
910
|
+
);
|
|
911
|
+
}
|
|
348
912
|
if (variant === "mini") {
|
|
349
913
|
const { style: externalStyle, ...restProps } = props;
|
|
350
914
|
return /* @__PURE__ */ jsx(
|
|
351
915
|
View,
|
|
352
916
|
{
|
|
353
917
|
className,
|
|
354
|
-
style: [
|
|
918
|
+
style: [
|
|
919
|
+
{ flexDirection: "row", height: 3, gap: REP_GAP, borderRadius: 2, overflow: "hidden" },
|
|
920
|
+
externalStyle
|
|
921
|
+
],
|
|
355
922
|
accessibilityRole: "image",
|
|
356
|
-
accessibilityLabel:
|
|
923
|
+
accessibilityLabel: miniLabel,
|
|
357
924
|
testID: "velocity-strip-mini",
|
|
358
925
|
...restProps,
|
|
359
|
-
children:
|
|
926
|
+
children: slots.map((slot, i) => /* @__PURE__ */ jsx(
|
|
360
927
|
View,
|
|
361
928
|
{
|
|
362
929
|
style: {
|
|
363
930
|
flex: 1,
|
|
364
|
-
backgroundColor:
|
|
931
|
+
backgroundColor: slotColor(slot),
|
|
365
932
|
borderRadius: 1,
|
|
366
933
|
minWidth: 4,
|
|
367
|
-
height: "100%"
|
|
934
|
+
height: "100%",
|
|
935
|
+
// The container's uniform 2px gap covers rep spacing; a wide slot adds
|
|
936
|
+
// only the EXTRA (WIDE_GAP − REP_GAP) so the notch totals WIDE_GAP.
|
|
937
|
+
marginLeft: Math.max(0, slot.leadingGap - REP_GAP),
|
|
938
|
+
...slot.kind === "continue" ? { borderWidth: 1, borderColor: CONTINUE_OUTLINE } : {}
|
|
368
939
|
},
|
|
369
940
|
accessibilityElementsHidden: true,
|
|
370
|
-
testID: `velocity-bar-${i}`
|
|
941
|
+
testID: slot.kind === "rep" ? `velocity-bar-${i}` : `velocity-slot-${slot.kind}`
|
|
371
942
|
},
|
|
372
943
|
i
|
|
373
944
|
))
|
|
374
945
|
}
|
|
375
946
|
);
|
|
376
947
|
}
|
|
377
|
-
|
|
948
|
+
if (!framed) {
|
|
949
|
+
const { style: externalStyle, ...restProps } = props;
|
|
950
|
+
return /* @__PURE__ */ jsx(
|
|
951
|
+
View,
|
|
952
|
+
{
|
|
953
|
+
className,
|
|
954
|
+
style: [
|
|
955
|
+
{ flexDirection: "row", height, gap: REP_GAP, alignItems: "flex-end" },
|
|
956
|
+
externalStyle
|
|
957
|
+
],
|
|
958
|
+
accessibilityRole: "image",
|
|
959
|
+
accessibilityLabel: miniLabel,
|
|
960
|
+
testID: "velocity-strip-compact",
|
|
961
|
+
...restProps,
|
|
962
|
+
children: slots.map((slot, i) => /* @__PURE__ */ jsx(
|
|
963
|
+
View,
|
|
964
|
+
{
|
|
965
|
+
style: {
|
|
966
|
+
flex: 1,
|
|
967
|
+
minWidth: 4,
|
|
968
|
+
height: bareSlotHeight(slot, height, scaleDenom),
|
|
969
|
+
// Top-rounded to match the framed chart's bars (the "rounded tops from
|
|
970
|
+
// the numbers one"); square bottoms since bars sit on the baseline.
|
|
971
|
+
borderTopLeftRadius: 2,
|
|
972
|
+
borderTopRightRadius: 2,
|
|
973
|
+
backgroundColor: slotColor(slot),
|
|
974
|
+
marginLeft: Math.max(0, slot.leadingGap - REP_GAP),
|
|
975
|
+
...slot.kind === "continue" ? { borderWidth: 1, borderColor: CONTINUE_OUTLINE } : {}
|
|
976
|
+
},
|
|
977
|
+
accessibilityElementsHidden: true,
|
|
978
|
+
testID: slot.kind === "rep" ? `velocity-bar-${i}` : `velocity-slot-${slot.kind}`
|
|
979
|
+
},
|
|
980
|
+
i
|
|
981
|
+
))
|
|
982
|
+
}
|
|
983
|
+
);
|
|
984
|
+
}
|
|
985
|
+
const stripLabel = set ? `${setAccessibilityLabel(set, repCount)}, tap to ${expanded ? "collapse" : "expand"}` : `Velocity chart for set, ${repCount} reps, tap to ${expanded ? "collapse" : "expand"}`;
|
|
378
986
|
const hasInteractiveContainer = onToggle != null;
|
|
379
987
|
const hasInteractiveReps = onRepPress != null && expanded;
|
|
988
|
+
const setBars = set != null && slots.map((slot, i) => {
|
|
989
|
+
const isStraightRep = set.type === "straight" && slot.kind === "rep";
|
|
990
|
+
const heightPct = isStraightRep ? scaleDenom > 0 ? Math.round((slot.velocity ?? 0) / scaleDenom * 100) : 0 : set.type === "straight" ? EXPANDED_TODO_STUB_PCT : EXPANDED_ENCODED_PCT;
|
|
991
|
+
return /* @__PURE__ */ jsx(
|
|
992
|
+
View,
|
|
993
|
+
{
|
|
994
|
+
style: {
|
|
995
|
+
flex: 1,
|
|
996
|
+
height: "100%",
|
|
997
|
+
justifyContent: "flex-end",
|
|
998
|
+
marginLeft: slot.leadingGap
|
|
999
|
+
},
|
|
1000
|
+
children: /* @__PURE__ */ jsx(
|
|
1001
|
+
View,
|
|
1002
|
+
{
|
|
1003
|
+
style: {
|
|
1004
|
+
height: `${heightPct}%`,
|
|
1005
|
+
minHeight: 2,
|
|
1006
|
+
borderTopLeftRadius: 2,
|
|
1007
|
+
borderTopRightRadius: 2,
|
|
1008
|
+
backgroundColor: slotColor(slot),
|
|
1009
|
+
...slot.kind === "continue" ? { borderWidth: 1, borderColor: CONTINUE_OUTLINE } : {}
|
|
1010
|
+
},
|
|
1011
|
+
testID: slot.kind === "rep" ? `velocity-bar-${i}` : `velocity-slot-${slot.kind}`,
|
|
1012
|
+
accessibilityRole: "image",
|
|
1013
|
+
accessibilityLabel: slotAccessibilityLabel(slot, i)
|
|
1014
|
+
}
|
|
1015
|
+
)
|
|
1016
|
+
},
|
|
1017
|
+
i
|
|
1018
|
+
);
|
|
1019
|
+
});
|
|
380
1020
|
const stripContent = /* @__PURE__ */ jsxs(
|
|
381
1021
|
Animated.View,
|
|
382
1022
|
{
|
|
@@ -389,7 +1029,9 @@ function VelocityStrip({
|
|
|
389
1029
|
borderRadius: expanded ? 6 : 2,
|
|
390
1030
|
paddingTop: expanded ? 16 : 0,
|
|
391
1031
|
paddingBottom: expanded ? 24 : 0,
|
|
392
|
-
|
|
1032
|
+
// No horizontal inset — the framed chart's bars span full width, matching
|
|
1033
|
+
// the bare spotlight and mini. Numbers/info sit in the vertical padding.
|
|
1034
|
+
paddingHorizontal: 0,
|
|
393
1035
|
paddingVertical: expanded ? void 0 : 8,
|
|
394
1036
|
overflow: expanded ? "visible" : "hidden"
|
|
395
1037
|
}
|
|
@@ -399,46 +1041,65 @@ function VelocityStrip({
|
|
|
399
1041
|
testID: "velocity-strip",
|
|
400
1042
|
...props,
|
|
401
1043
|
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
|
-
|
|
1044
|
+
/* @__PURE__ */ jsx(View, { style: { flexDirection: "row", flex: 1, gap: set ? 0 : 2, alignItems: "flex-end" }, children: set ? setBars : doneVelocities.map((v, i) => {
|
|
1045
|
+
const barBackground = barColorFor(v);
|
|
1046
|
+
const barHeightPct = scaleDenom > 0 ? Math.round(v / scaleDenom * 100) : 0;
|
|
1047
|
+
const isLive = liveRepIndex === i;
|
|
1048
|
+
const liveLabelSuffix = isLive ? isNewPeak ? ", latest rep, new set peak" : ", latest rep" : "";
|
|
1049
|
+
const barStyle = expanded ? {
|
|
1050
|
+
height: `${barHeightPct}%`,
|
|
1051
|
+
minHeight: 2,
|
|
1052
|
+
borderTopLeftRadius: 2,
|
|
1053
|
+
borderTopRightRadius: 2,
|
|
1054
|
+
backgroundColor: barBackground
|
|
1055
|
+
} : {
|
|
1056
|
+
minHeight: "100%",
|
|
1057
|
+
borderTopLeftRadius: 2,
|
|
1058
|
+
borderTopRightRadius: 2,
|
|
1059
|
+
borderBottomLeftRadius: 0,
|
|
1060
|
+
borderBottomRightRadius: 0,
|
|
1061
|
+
backgroundColor: barBackground
|
|
1062
|
+
};
|
|
1063
|
+
const barInner = isLive ? /* @__PURE__ */ jsx(
|
|
1064
|
+
Animated.View,
|
|
1065
|
+
{
|
|
1066
|
+
style: [barStyle, { transform: [{ scale: liveScale }] }],
|
|
1067
|
+
testID: `velocity-bar-${i}`,
|
|
1068
|
+
accessibilityRole: "image",
|
|
1069
|
+
accessibilityLabel: `Rep ${i + 1}: ${formatVelocity(v)} meters per second${liveLabelSuffix}`
|
|
1070
|
+
}
|
|
1071
|
+
) : /* @__PURE__ */ jsx(
|
|
1072
|
+
View,
|
|
1073
|
+
{
|
|
1074
|
+
style: barStyle,
|
|
1075
|
+
testID: `velocity-bar-${i}`,
|
|
1076
|
+
accessibilityRole: "image",
|
|
1077
|
+
accessibilityLabel: `Rep ${i + 1}: ${formatVelocity(v)} meters per second`
|
|
1078
|
+
}
|
|
1079
|
+
);
|
|
1080
|
+
const bar = /* @__PURE__ */ jsxs(
|
|
1081
|
+
View,
|
|
1082
|
+
{
|
|
1083
|
+
style: {
|
|
1084
|
+
flex: 1,
|
|
1085
|
+
height: "100%",
|
|
1086
|
+
justifyContent: "flex-end",
|
|
1087
|
+
position: "relative"
|
|
1088
|
+
},
|
|
1089
|
+
children: [
|
|
1090
|
+
expanded && showNumbers && /* @__PURE__ */ jsx(
|
|
1091
|
+
Animated.View,
|
|
1092
|
+
{
|
|
1093
|
+
style: {
|
|
1094
|
+
opacity: labelOpacity,
|
|
1095
|
+
alignItems: "center",
|
|
1096
|
+
position: "absolute",
|
|
1097
|
+
top: -13,
|
|
1098
|
+
left: 0,
|
|
1099
|
+
right: 0
|
|
1100
|
+
},
|
|
1101
|
+
accessibilityElementsHidden: true,
|
|
1102
|
+
children: /* @__PURE__ */ jsx(
|
|
442
1103
|
Text,
|
|
443
1104
|
{
|
|
444
1105
|
className: "text-text-secondary",
|
|
@@ -446,30 +1107,30 @@ function VelocityStrip({
|
|
|
446
1107
|
testID: `velocity-label-${i}`,
|
|
447
1108
|
children: formatVelocity(v)
|
|
448
1109
|
}
|
|
449
|
-
)
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
})
|
|
1110
|
+
)
|
|
1111
|
+
}
|
|
1112
|
+
),
|
|
1113
|
+
barInner
|
|
1114
|
+
]
|
|
1115
|
+
},
|
|
1116
|
+
i
|
|
1117
|
+
);
|
|
1118
|
+
if (onRepPress && expanded) {
|
|
1119
|
+
return /* @__PURE__ */ jsx(
|
|
1120
|
+
Pressable,
|
|
1121
|
+
{
|
|
1122
|
+
style: { flex: 1, height: "100%" },
|
|
1123
|
+
onPress: () => onRepPress(i, v),
|
|
1124
|
+
accessibilityRole: "button",
|
|
1125
|
+
accessibilityLabel: `Rep ${i + 1}: ${formatVelocity(v)} meters per second, tap for details`,
|
|
1126
|
+
testID: `velocity-bar-pressable-${i}`,
|
|
1127
|
+
children: bar
|
|
1128
|
+
},
|
|
1129
|
+
i
|
|
1130
|
+
);
|
|
471
1131
|
}
|
|
472
|
-
|
|
1132
|
+
return bar;
|
|
1133
|
+
}) }),
|
|
473
1134
|
expanded && showInfo && /* @__PURE__ */ jsxs(
|
|
474
1135
|
Animated.View,
|
|
475
1136
|
{
|
|
@@ -645,17 +1306,49 @@ function SvgIcon({
|
|
|
645
1306
|
}
|
|
646
1307
|
|
|
647
1308
|
// src/components/icons/icons.tsx
|
|
648
|
-
function
|
|
1309
|
+
function StarIcon(props) {
|
|
1310
|
+
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" }) });
|
|
1311
|
+
}
|
|
1312
|
+
function ScaleIcon(props) {
|
|
649
1313
|
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: "
|
|
1314
|
+
/* @__PURE__ */ jsx("path", { d: "m16 16 3-8 3 8c-.87.65-1.92 1-3 1s-2.13-.35-3-1Z" }),
|
|
1315
|
+
/* @__PURE__ */ jsx("path", { d: "m2 16 3-8 3 8c-.87.65-1.92 1-3 1s-2.13-.35-3-1Z" }),
|
|
1316
|
+
/* @__PURE__ */ jsx("path", { d: "M7 21h10" }),
|
|
1317
|
+
/* @__PURE__ */ jsx("path", { d: "M12 3v18" }),
|
|
1318
|
+
/* @__PURE__ */ jsx("path", { d: "M3 7h2c2 0 5-1 7-2 2 1 5 2 7 2h2" })
|
|
655
1319
|
] });
|
|
656
1320
|
}
|
|
657
|
-
function
|
|
658
|
-
return /* @__PURE__ */
|
|
1321
|
+
function AlertTriangleIcon(props) {
|
|
1322
|
+
return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
|
|
1323
|
+
/* @__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" }),
|
|
1324
|
+
/* @__PURE__ */ jsx("path", { d: "M12 9v4" }),
|
|
1325
|
+
/* @__PURE__ */ jsx("path", { d: "M12 17h.01" })
|
|
1326
|
+
] });
|
|
1327
|
+
}
|
|
1328
|
+
function TrendingDownIcon(props) {
|
|
1329
|
+
return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
|
|
1330
|
+
/* @__PURE__ */ jsx("path", { d: "M16 17h6v-6" }),
|
|
1331
|
+
/* @__PURE__ */ jsx("path", { d: "m22 17-8.5-8.5-5 5L2 7" })
|
|
1332
|
+
] });
|
|
1333
|
+
}
|
|
1334
|
+
function CircleSlashIcon(props) {
|
|
1335
|
+
return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
|
|
1336
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
|
|
1337
|
+
/* @__PURE__ */ jsx("path", { d: "m9 15 6-6" })
|
|
1338
|
+
] });
|
|
1339
|
+
}
|
|
1340
|
+
function AwardIcon(props) {
|
|
1341
|
+
return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
|
|
1342
|
+
/* @__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" }),
|
|
1343
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "8", r: "6" })
|
|
1344
|
+
] });
|
|
1345
|
+
}
|
|
1346
|
+
function InfoIcon(props) {
|
|
1347
|
+
return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
|
|
1348
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
|
|
1349
|
+
/* @__PURE__ */ jsx("path", { d: "M12 16v-4" }),
|
|
1350
|
+
/* @__PURE__ */ jsx("path", { d: "M12 8h.01" })
|
|
1351
|
+
] });
|
|
659
1352
|
}
|
|
660
1353
|
var baseBadgeSizeConfig = {
|
|
661
1354
|
sm: { fontSize: 9, paddingH: 6, paddingV: 2, iconSize: 10 },
|
|
@@ -758,6 +1451,9 @@ var semanticColorsLight = {
|
|
|
758
1451
|
"status-success-light": primitiveRamps.green[200],
|
|
759
1452
|
"status-success-dark": primitiveRamps.green[600],
|
|
760
1453
|
"status-success-subtle": primitiveRamps.green[50],
|
|
1454
|
+
// Live-session accent — its OWN role, decoupled from success so the two can diverge
|
|
1455
|
+
"status-live": primitiveRamps.green[300],
|
|
1456
|
+
"status-live-muted": primitiveRamps.green[500],
|
|
761
1457
|
"status-error": primitiveRamps.red[600],
|
|
762
1458
|
"status-error-light": primitiveRamps.red[500],
|
|
763
1459
|
"status-error-dark": primitiveRamps.red[700],
|
|
@@ -891,6 +1587,9 @@ var semanticColorsDark = {
|
|
|
891
1587
|
"status-success-light": primitiveRamps.green[200],
|
|
892
1588
|
"status-success-dark": primitiveRamps.green[600],
|
|
893
1589
|
"status-success-subtle": "rgba(46, 213, 115, 0.12)",
|
|
1590
|
+
// Live-session accent — its OWN role, decoupled from success so the two can diverge
|
|
1591
|
+
"status-live": primitiveRamps.green[300],
|
|
1592
|
+
"status-live-muted": primitiveRamps.green[500],
|
|
894
1593
|
"status-error": primitiveRamps.red[600],
|
|
895
1594
|
"status-error-light": primitiveRamps.red[500],
|
|
896
1595
|
"status-error-dark": primitiveRamps.red[700],
|
|
@@ -995,93 +1694,7 @@ function getSemanticColors(mode) {
|
|
|
995
1694
|
return mode === "dark" ? semanticColorsDark : semanticColorsLight;
|
|
996
1695
|
}
|
|
997
1696
|
|
|
998
|
-
// src/components/custom/Workout/
|
|
999
|
-
function WeightBadge({
|
|
1000
|
-
value,
|
|
1001
|
-
unit = "lbs",
|
|
1002
|
-
reps,
|
|
1003
|
-
delta,
|
|
1004
|
-
isPr,
|
|
1005
|
-
showIcon = true,
|
|
1006
|
-
size = "md",
|
|
1007
|
-
onPress,
|
|
1008
|
-
className,
|
|
1009
|
-
...props
|
|
1010
|
-
}) {
|
|
1011
|
-
const config = baseBadgeSizeConfig[size];
|
|
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
|
-
}
|
|
1697
|
+
// src/components/custom/Workout/PrBadge.tsx
|
|
1085
1698
|
var BRAND_PRIMARY = getSemanticColors("dark")["brand-primary"];
|
|
1086
1699
|
var typeLabels = {
|
|
1087
1700
|
e1rm: "PR e1RM",
|
|
@@ -1165,6 +1778,204 @@ function PrBadge({
|
|
|
1165
1778
|
}
|
|
1166
1779
|
return badge;
|
|
1167
1780
|
}
|
|
1781
|
+
var DARK = getSemanticColors("dark");
|
|
1782
|
+
var TEXT_ACTIVE = DARK["text-primary"];
|
|
1783
|
+
var TEXT_MUTED = DARK["text-secondary"];
|
|
1784
|
+
var COL = { set: 36, reps: 44, load: 56, rpe: 36 };
|
|
1785
|
+
var cellText = { fontFamily: "Inter, sans-serif", fontSize: 13, fontWeight: "600" };
|
|
1786
|
+
var typeBadge = {
|
|
1787
|
+
fontSize: 10,
|
|
1788
|
+
fontWeight: "700",
|
|
1789
|
+
fontFamily: "Inter, sans-serif",
|
|
1790
|
+
color: WORKOUT_TOKENS.scale.orange,
|
|
1791
|
+
backgroundColor: "rgba(255,165,2,0.12)",
|
|
1792
|
+
paddingVertical: 1,
|
|
1793
|
+
paddingHorizontal: 5,
|
|
1794
|
+
borderRadius: 3,
|
|
1795
|
+
overflow: "hidden"
|
|
1796
|
+
};
|
|
1797
|
+
function displayReps(set) {
|
|
1798
|
+
return set.state === "done" ? set.reps : set.target.reps;
|
|
1799
|
+
}
|
|
1800
|
+
function displayWeight(set) {
|
|
1801
|
+
return set.state === "done" ? set.weight : set.target.weight;
|
|
1802
|
+
}
|
|
1803
|
+
function plannedReps(set) {
|
|
1804
|
+
if (set.state === "done") return set.velocities.length;
|
|
1805
|
+
if (set.state === "live") return set.target.reps;
|
|
1806
|
+
return set.planned ?? set.target.reps;
|
|
1807
|
+
}
|
|
1808
|
+
function accessibilityLabel(set) {
|
|
1809
|
+
const base = `Set ${set.setNumber}: ${displayReps(set)} reps at ${roundWeight(displayWeight(set))} ${set.unit}`;
|
|
1810
|
+
if (set.state === "live") return `${base}, in progress`;
|
|
1811
|
+
if (set.state === "todo") return `${base}, upcoming`;
|
|
1812
|
+
return base;
|
|
1813
|
+
}
|
|
1814
|
+
function Cell({
|
|
1815
|
+
width,
|
|
1816
|
+
children,
|
|
1817
|
+
testID
|
|
1818
|
+
}) {
|
|
1819
|
+
return /* @__PURE__ */ jsx(View, { style: { width, alignItems: "center", justifyContent: "center" }, testID, children });
|
|
1820
|
+
}
|
|
1821
|
+
function RowStrip({ set }) {
|
|
1822
|
+
const zones = set.velocityZones;
|
|
1823
|
+
if (set.state === "live") {
|
|
1824
|
+
return /* @__PURE__ */ jsx(
|
|
1825
|
+
VelocityStrip,
|
|
1826
|
+
{
|
|
1827
|
+
variant: "expanded",
|
|
1828
|
+
showNumbers: false,
|
|
1829
|
+
showInfo: false,
|
|
1830
|
+
height: 24,
|
|
1831
|
+
scale: "fixed",
|
|
1832
|
+
set: { type: "straight", velocities: set.velocities, planned: set.target.reps },
|
|
1833
|
+
zones
|
|
1834
|
+
}
|
|
1835
|
+
);
|
|
1836
|
+
}
|
|
1837
|
+
const velocities = set.state === "done" ? set.velocities : [];
|
|
1838
|
+
return /* @__PURE__ */ jsx(
|
|
1839
|
+
VelocityStrip,
|
|
1840
|
+
{
|
|
1841
|
+
variant: "mini",
|
|
1842
|
+
set: { type: "straight", velocities, planned: plannedReps(set) },
|
|
1843
|
+
zones
|
|
1844
|
+
}
|
|
1845
|
+
);
|
|
1846
|
+
}
|
|
1847
|
+
function SetRow(set) {
|
|
1848
|
+
const live = set.state === "live";
|
|
1849
|
+
const valueColor = live ? TEXT_ACTIVE : TEXT_MUTED;
|
|
1850
|
+
const rpe = set.state === "todo" ? null : set.rpe;
|
|
1851
|
+
return /* @__PURE__ */ jsxs(
|
|
1852
|
+
View,
|
|
1853
|
+
{
|
|
1854
|
+
style: { paddingVertical: 6, paddingHorizontal: 8 },
|
|
1855
|
+
accessibilityLabel: accessibilityLabel(set),
|
|
1856
|
+
testID: "set-row",
|
|
1857
|
+
children: [
|
|
1858
|
+
/* @__PURE__ */ jsxs(View, { className: "flex-row items-center", style: { justifyContent: "space-between" }, children: [
|
|
1859
|
+
/* @__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 }) }),
|
|
1860
|
+
/* @__PURE__ */ jsx(Cell, { width: COL.reps, testID: "set-row-reps", children: /* @__PURE__ */ jsx(Text, { style: { ...cellText, color: valueColor }, children: displayReps(set) }) }),
|
|
1861
|
+
/* @__PURE__ */ jsx(Cell, { width: COL.load, testID: "set-row-weight", children: /* @__PURE__ */ jsx(Text, { style: { ...cellText, color: valueColor }, children: roundWeight(displayWeight(set)) }) }),
|
|
1862
|
+
/* @__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" }) })
|
|
1863
|
+
] }),
|
|
1864
|
+
/* @__PURE__ */ jsx(View, { style: { marginTop: live ? 6 : 4 }, testID: "set-row-strip", children: /* @__PURE__ */ jsx(RowStrip, { set }) })
|
|
1865
|
+
]
|
|
1866
|
+
}
|
|
1867
|
+
);
|
|
1868
|
+
}
|
|
1869
|
+
var PREV_COLUMN = { label: "PREV", width: void 0 };
|
|
1870
|
+
function buildColumns(unit, showPrevious) {
|
|
1871
|
+
return [
|
|
1872
|
+
{ label: "SET", width: 36 },
|
|
1873
|
+
...showPrevious ? [PREV_COLUMN] : [],
|
|
1874
|
+
{ label: "REPS", width: 44 },
|
|
1875
|
+
{ label: unit.toUpperCase(), width: 56 },
|
|
1876
|
+
{ label: "RPE", width: 36 }
|
|
1877
|
+
];
|
|
1878
|
+
}
|
|
1879
|
+
function SetTableHeader({
|
|
1880
|
+
unit = "lbs",
|
|
1881
|
+
showPrevious = true,
|
|
1882
|
+
testID = "table-header"
|
|
1883
|
+
}) {
|
|
1884
|
+
const columns = buildColumns(unit, showPrevious);
|
|
1885
|
+
return /* @__PURE__ */ jsx(
|
|
1886
|
+
View,
|
|
1887
|
+
{
|
|
1888
|
+
className: "flex-row",
|
|
1889
|
+
style: {
|
|
1890
|
+
paddingVertical: 8,
|
|
1891
|
+
paddingHorizontal: 8,
|
|
1892
|
+
paddingBottom: 4,
|
|
1893
|
+
...showPrevious ? {} : { justifyContent: "space-between" }
|
|
1894
|
+
},
|
|
1895
|
+
testID,
|
|
1896
|
+
children: columns.map(({ label, width }) => {
|
|
1897
|
+
const isFlex = width === void 0;
|
|
1898
|
+
return /* @__PURE__ */ jsx(
|
|
1899
|
+
View,
|
|
1900
|
+
{
|
|
1901
|
+
style: {
|
|
1902
|
+
...isFlex ? { minWidth: 44 } : { width, flexShrink: 1 },
|
|
1903
|
+
alignItems: "center",
|
|
1904
|
+
justifyContent: "center",
|
|
1905
|
+
gap: 8
|
|
1906
|
+
},
|
|
1907
|
+
...isFlex ? { className: "flex-1" } : {},
|
|
1908
|
+
children: /* @__PURE__ */ jsx(
|
|
1909
|
+
Text,
|
|
1910
|
+
{
|
|
1911
|
+
className: "text-text-tertiary",
|
|
1912
|
+
style: {
|
|
1913
|
+
fontSize: 10,
|
|
1914
|
+
fontWeight: "600",
|
|
1915
|
+
fontFamily: "Inter, sans-serif",
|
|
1916
|
+
textTransform: "uppercase",
|
|
1917
|
+
letterSpacing: 0.8
|
|
1918
|
+
},
|
|
1919
|
+
children: label
|
|
1920
|
+
}
|
|
1921
|
+
)
|
|
1922
|
+
},
|
|
1923
|
+
label
|
|
1924
|
+
);
|
|
1925
|
+
})
|
|
1926
|
+
}
|
|
1927
|
+
);
|
|
1928
|
+
}
|
|
1929
|
+
var METRIC_FONT = "Inter, sans-serif";
|
|
1930
|
+
function MetricCell({ children, color, fontSize = 11 }) {
|
|
1931
|
+
return /* @__PURE__ */ jsx(
|
|
1932
|
+
Text,
|
|
1933
|
+
{
|
|
1934
|
+
style: {
|
|
1935
|
+
fontFamily: METRIC_FONT,
|
|
1936
|
+
fontSize,
|
|
1937
|
+
color,
|
|
1938
|
+
fontWeight: "600",
|
|
1939
|
+
letterSpacing: 1
|
|
1940
|
+
},
|
|
1941
|
+
children
|
|
1942
|
+
}
|
|
1943
|
+
);
|
|
1944
|
+
}
|
|
1945
|
+
|
|
1946
|
+
// src/components/custom/Workout/SetsRepsLoad.tsx
|
|
1947
|
+
var t = getSemanticColors("dark");
|
|
1948
|
+
var TIMES = "\xD7";
|
|
1949
|
+
var AT = "@";
|
|
1950
|
+
function SetsRepsLoad({
|
|
1951
|
+
sets,
|
|
1952
|
+
reps,
|
|
1953
|
+
load,
|
|
1954
|
+
unit = "lb",
|
|
1955
|
+
className,
|
|
1956
|
+
...props
|
|
1957
|
+
}) {
|
|
1958
|
+
const value = t["text-primary"];
|
|
1959
|
+
const sep = t["text-tertiary"];
|
|
1960
|
+
return /* @__PURE__ */ jsxs(
|
|
1961
|
+
View,
|
|
1962
|
+
{
|
|
1963
|
+
className,
|
|
1964
|
+
style: { flexDirection: "row", alignItems: "baseline" },
|
|
1965
|
+
accessibilityLabel: `${sets} sets of ${reps} reps at ${load} ${unit}`,
|
|
1966
|
+
testID: "sets-reps-load",
|
|
1967
|
+
...props,
|
|
1968
|
+
children: [
|
|
1969
|
+
/* @__PURE__ */ jsx(MetricCell, { color: value, children: sets }),
|
|
1970
|
+
/* @__PURE__ */ jsx(MetricCell, { color: sep, children: ` ${TIMES} ` }),
|
|
1971
|
+
/* @__PURE__ */ jsx(MetricCell, { color: value, children: reps }),
|
|
1972
|
+
/* @__PURE__ */ jsx(MetricCell, { color: sep, children: ` ${AT} ` }),
|
|
1973
|
+
/* @__PURE__ */ jsx(MetricCell, { color: value, children: load }),
|
|
1974
|
+
/* @__PURE__ */ jsx(MetricCell, { color: sep, children: ` ${unit}` })
|
|
1975
|
+
]
|
|
1976
|
+
}
|
|
1977
|
+
);
|
|
1978
|
+
}
|
|
1168
1979
|
function hexToRgb(hex) {
|
|
1169
1980
|
const cleaned = hex.replace("#", "");
|
|
1170
1981
|
let r, g, b;
|
|
@@ -1474,7 +2285,7 @@ function alpha(color, opacity) {
|
|
|
1474
2285
|
}
|
|
1475
2286
|
return color;
|
|
1476
2287
|
}
|
|
1477
|
-
var
|
|
2288
|
+
var t2 = getSemanticColors("dark");
|
|
1478
2289
|
var TEMPO_PACING = {
|
|
1479
2290
|
behindThresholdPct: 0.15,
|
|
1480
2291
|
minPhaseDurationMs: 500
|
|
@@ -1490,20 +2301,20 @@ function getTempoFillPct(elapsedMs, targetMs) {
|
|
|
1490
2301
|
return Math.min(100, elapsedMs / targetMs * 100);
|
|
1491
2302
|
}
|
|
1492
2303
|
({
|
|
1493
|
-
concentric: { color:
|
|
1494
|
-
hold: { color:
|
|
1495
|
-
eccentric: { color:
|
|
2304
|
+
concentric: { color: t2["status-success"]},
|
|
2305
|
+
hold: { color: t2["brand-primary"]},
|
|
2306
|
+
eccentric: { color: t2["status-warning"]}
|
|
1496
2307
|
});
|
|
1497
2308
|
|
|
1498
2309
|
// src/components/custom/Workout/TempoDisplay.tsx
|
|
1499
|
-
var
|
|
2310
|
+
var t3 = getSemanticColors("dark");
|
|
1500
2311
|
var INTER = "Inter, sans-serif";
|
|
1501
2312
|
var TEXT_TERTIARY = "#6B7280";
|
|
1502
|
-
var STATUS_ERROR =
|
|
2313
|
+
var STATUS_ERROR = t3["status-error"];
|
|
1503
2314
|
var phaseColors = {
|
|
1504
|
-
eccentric:
|
|
2315
|
+
eccentric: t3["status-warning"],
|
|
1505
2316
|
pauseBottom: TEXT_TERTIARY,
|
|
1506
|
-
concentric:
|
|
2317
|
+
concentric: t3["status-success"],
|
|
1507
2318
|
pauseTop: TEXT_TERTIARY,
|
|
1508
2319
|
dash: TEXT_TERTIARY
|
|
1509
2320
|
};
|
|
@@ -1512,34 +2323,10 @@ function TempoValue({
|
|
|
1512
2323
|
color,
|
|
1513
2324
|
fontSize
|
|
1514
2325
|
}) {
|
|
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
|
-
);
|
|
2326
|
+
return /* @__PURE__ */ jsx(MetricCell, { color, fontSize, children: value });
|
|
1528
2327
|
}
|
|
1529
2328
|
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
|
-
);
|
|
2329
|
+
return /* @__PURE__ */ jsx(MetricCell, { color, fontSize, children: "-" });
|
|
1543
2330
|
}
|
|
1544
2331
|
var LIVE_PHASES = [
|
|
1545
2332
|
{ key: "eccentric", color: phaseColors.eccentric },
|
|
@@ -1601,6 +2388,7 @@ function TempoDisplay({
|
|
|
1601
2388
|
tempo,
|
|
1602
2389
|
size = "md",
|
|
1603
2390
|
colored = true,
|
|
2391
|
+
showLabel = true,
|
|
1604
2392
|
showInfo = true,
|
|
1605
2393
|
live,
|
|
1606
2394
|
onPress,
|
|
@@ -1634,7 +2422,7 @@ function TempoDisplay({
|
|
|
1634
2422
|
},
|
|
1635
2423
|
...props,
|
|
1636
2424
|
children: [
|
|
1637
|
-
/* @__PURE__ */ jsx(
|
|
2425
|
+
showLabel && /* @__PURE__ */ jsx(
|
|
1638
2426
|
Text,
|
|
1639
2427
|
{
|
|
1640
2428
|
style: {
|
|
@@ -1768,245 +2556,121 @@ function TempoDisplay({
|
|
|
1768
2556
|
}
|
|
1769
2557
|
);
|
|
1770
2558
|
}
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
2559
|
+
var t4 = getSemanticColors("dark");
|
|
2560
|
+
var INDICATOR_CONFIG = {
|
|
2561
|
+
imbalance: { Icon: ScaleIcon, color: t4["status-error"], label: "Left/right imbalance" },
|
|
2562
|
+
overshoot: { Icon: AlertTriangleIcon, color: t4["status-error"], label: "Load overshoot" },
|
|
2563
|
+
"velocity-loss": { Icon: TrendingDownIcon, color: t4["status-warning"], label: "Velocity loss" },
|
|
2564
|
+
"missed-reps": { Icon: CircleSlashIcon, color: t4["status-warning"], label: "Missed reps" },
|
|
2565
|
+
pr: { Icon: AwardIcon, color: t4["status-success"], label: "Personal record" },
|
|
2566
|
+
info: { Icon: InfoIcon, color: t4["status-info"], label: "More info" }
|
|
2567
|
+
};
|
|
2568
|
+
function ExerciseIndicator({ kind, onPress, className, ...props }) {
|
|
2569
|
+
const { Icon, color, label } = INDICATOR_CONFIG[kind];
|
|
2570
|
+
const chip = /* @__PURE__ */ jsx(
|
|
2571
|
+
View,
|
|
2572
|
+
{
|
|
2573
|
+
className,
|
|
2574
|
+
style: {
|
|
2575
|
+
width: 16,
|
|
2576
|
+
height: 16,
|
|
2577
|
+
borderRadius: 8,
|
|
2578
|
+
borderWidth: 1,
|
|
2579
|
+
borderColor: color,
|
|
2580
|
+
alignItems: "center",
|
|
2581
|
+
justifyContent: "center"
|
|
2582
|
+
},
|
|
2583
|
+
accessibilityRole: "image",
|
|
2584
|
+
accessibilityLabel: label,
|
|
2585
|
+
testID: "exercise-indicator",
|
|
2586
|
+
...props,
|
|
2587
|
+
children: /* @__PURE__ */ jsx(Icon, { size: 11, color })
|
|
2588
|
+
}
|
|
2589
|
+
);
|
|
2590
|
+
if (onPress) {
|
|
2591
|
+
return /* @__PURE__ */ jsx(Pressable, { onPress, accessibilityRole: "button", accessibilityLabel: label, children: chip });
|
|
2592
|
+
}
|
|
2593
|
+
return chip;
|
|
1775
2594
|
}
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
2595
|
+
|
|
2596
|
+
// src/components/custom/Workout/ExerciseHeading.tsx
|
|
2597
|
+
function headingLabel(name, sets, reps, load, unit) {
|
|
2598
|
+
return `${name}, ${sets}\xD7${reps} @ ${roundWeight(load)} ${unit}`;
|
|
2599
|
+
}
|
|
2600
|
+
function ExerciseHeading({
|
|
2601
|
+
name,
|
|
2602
|
+
sets,
|
|
1780
2603
|
reps,
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
2604
|
+
load,
|
|
2605
|
+
unit = "lbs",
|
|
2606
|
+
tempo,
|
|
2607
|
+
indicator,
|
|
2608
|
+
dimmed,
|
|
2609
|
+
onPress
|
|
2610
|
+
}) {
|
|
2611
|
+
return /* @__PURE__ */ jsxs(View, { style: { opacity: dimmed ? 0.55 : 1 }, testID: "exercise-heading", children: [
|
|
2612
|
+
/* @__PURE__ */ jsxs(
|
|
2613
|
+
Pressable,
|
|
2614
|
+
{
|
|
2615
|
+
onPress,
|
|
2616
|
+
accessibilityRole: "button",
|
|
2617
|
+
accessibilityLabel: headingLabel(name, sets, reps, load, unit),
|
|
2618
|
+
className: "flex-row items-center",
|
|
2619
|
+
style: { gap: 8 },
|
|
2620
|
+
testID: "exercise-card-header",
|
|
2621
|
+
children: [
|
|
2622
|
+
/* @__PURE__ */ jsx(
|
|
2623
|
+
Text,
|
|
2624
|
+
{
|
|
2625
|
+
className: "text-text-primary",
|
|
2626
|
+
style: { fontSize: 14, fontFamily: '"Space Grotesk", sans-serif', fontWeight: "700" },
|
|
2627
|
+
testID: "exercise-card-name",
|
|
2628
|
+
children: name
|
|
2629
|
+
}
|
|
2630
|
+
),
|
|
2631
|
+
/* @__PURE__ */ jsx(View, { className: "flex-1" }),
|
|
2632
|
+
indicator && /* @__PURE__ */ jsx(ExerciseIndicator, { kind: indicator })
|
|
2633
|
+
]
|
|
2634
|
+
}
|
|
2635
|
+
),
|
|
2636
|
+
/* @__PURE__ */ jsxs(
|
|
2637
|
+
View,
|
|
2638
|
+
{
|
|
2639
|
+
className: "flex-row items-center",
|
|
2640
|
+
style: { gap: 8, marginTop: 1 },
|
|
2641
|
+
testID: "exercise-card-summary",
|
|
2642
|
+
children: [
|
|
2643
|
+
/* @__PURE__ */ jsx(SetsRepsLoad, { sets, reps, load, unit }),
|
|
2644
|
+
/* @__PURE__ */ jsx(View, { className: "flex-1" }),
|
|
2645
|
+
tempo && /* @__PURE__ */ jsx(TempoDisplay, { tempo, size: "sm", showLabel: false, showInfo: false })
|
|
2646
|
+
]
|
|
2647
|
+
}
|
|
2648
|
+
)
|
|
2649
|
+
] });
|
|
2650
|
+
}
|
|
2651
|
+
|
|
2652
|
+
// src/components/custom/Workout/ExerciseCardHeading.tsx
|
|
2653
|
+
function ExerciseCardHeading({
|
|
2654
|
+
setStates,
|
|
2655
|
+
stripHeight = 8,
|
|
2656
|
+
dimmed,
|
|
2657
|
+
testID = "exercise-card",
|
|
2658
|
+
...heading
|
|
1793
2659
|
}) {
|
|
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
2660
|
return /* @__PURE__ */ jsxs(
|
|
1813
2661
|
View,
|
|
1814
2662
|
{
|
|
1815
|
-
style:
|
|
1816
|
-
|
|
1817
|
-
setNumber,
|
|
1818
|
-
reps,
|
|
1819
|
-
weight,
|
|
1820
|
-
unit
|
|
1821
|
-
),
|
|
1822
|
-
testID: "set-row",
|
|
2663
|
+
style: { paddingVertical: 9, paddingHorizontal: 12, opacity: dimmed ? 0.55 : 1 },
|
|
2664
|
+
testID,
|
|
1823
2665
|
children: [
|
|
1824
|
-
/* @__PURE__ */ jsx(
|
|
1825
|
-
|
|
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
|
-
)
|
|
1996
|
-
}
|
|
1997
|
-
)
|
|
2666
|
+
/* @__PURE__ */ jsx(ExerciseHeading, { ...heading }),
|
|
2667
|
+
setStates.length > 0 && /* @__PURE__ */ jsx(View, { style: { marginTop: 7 }, testID: "exercise-card-strip", children: /* @__PURE__ */ jsx(SetStrip, { sets: setStates, height: stripHeight }) })
|
|
1998
2668
|
]
|
|
1999
2669
|
}
|
|
2000
2670
|
);
|
|
2001
2671
|
}
|
|
2002
|
-
function resolveColor(token, mode = "dark") {
|
|
2003
|
-
return Platform.OS === "web" ? `var(--color-${token})` : getSemanticColors(mode)[token];
|
|
2004
|
-
}
|
|
2005
2672
|
|
|
2006
2673
|
// src/components/custom/Workout/ExerciseCard.tsx
|
|
2007
|
-
function buildColumnHeaders(unit) {
|
|
2008
|
-
return ["SET", "PREV", "REPS", unit.toUpperCase(), "RPE"];
|
|
2009
|
-
}
|
|
2010
2674
|
function getSupersetBorderRadius(position) {
|
|
2011
2675
|
switch (position) {
|
|
2012
2676
|
case "first":
|
|
@@ -2049,7 +2713,7 @@ function formatSummary(summary) {
|
|
|
2049
2713
|
if (!summary) return "";
|
|
2050
2714
|
return `${summary.sets}\xD7${summary.reps} @ ${roundWeight(summary.weight)} ${summary.unit}`;
|
|
2051
2715
|
}
|
|
2052
|
-
function
|
|
2716
|
+
function formatAccessibilityLabel(name, summary, prescription) {
|
|
2053
2717
|
if (summary) return `${name}, ${formatSummary(summary)}`;
|
|
2054
2718
|
if (prescription) return `${name}, ${prescription}`;
|
|
2055
2719
|
return name;
|
|
@@ -2058,7 +2722,6 @@ function CollapsedCard({
|
|
|
2058
2722
|
name,
|
|
2059
2723
|
onToggle,
|
|
2060
2724
|
summary,
|
|
2061
|
-
e1rm,
|
|
2062
2725
|
isPR,
|
|
2063
2726
|
setVelocities,
|
|
2064
2727
|
velocityZones,
|
|
@@ -2077,7 +2740,7 @@ function CollapsedCard({
|
|
|
2077
2740
|
onPressIn: () => setPressed(true),
|
|
2078
2741
|
onPressOut: () => setPressed(false),
|
|
2079
2742
|
accessibilityRole: "button",
|
|
2080
|
-
accessibilityLabel:
|
|
2743
|
+
accessibilityLabel: formatAccessibilityLabel(name, summary, void 0),
|
|
2081
2744
|
testID: "exercise-card",
|
|
2082
2745
|
children: /* @__PURE__ */ jsxs(
|
|
2083
2746
|
View,
|
|
@@ -2091,100 +2754,93 @@ function CollapsedCard({
|
|
|
2091
2754
|
...supersetMargin
|
|
2092
2755
|
},
|
|
2093
2756
|
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
|
-
)
|
|
2757
|
+
/* @__PURE__ */ jsxs(View, { className: "flex-row items-center", testID: "exercise-card-header", children: [
|
|
2758
|
+
/* @__PURE__ */ jsx(
|
|
2759
|
+
Text,
|
|
2760
|
+
{
|
|
2761
|
+
className: "text-text-primary",
|
|
2762
|
+
style: {
|
|
2763
|
+
fontSize: 14,
|
|
2764
|
+
fontFamily: '"Space Grotesk", sans-serif',
|
|
2765
|
+
fontWeight: "700"
|
|
2766
|
+
},
|
|
2767
|
+
testID: "exercise-card-name",
|
|
2768
|
+
children: name
|
|
2769
|
+
}
|
|
2770
|
+
),
|
|
2771
|
+
/* @__PURE__ */ jsx(View, { className: "flex-1" }),
|
|
2772
|
+
summary && /* @__PURE__ */ jsx(
|
|
2773
|
+
Text,
|
|
2774
|
+
{
|
|
2775
|
+
className: "text-text-secondary",
|
|
2776
|
+
style: {
|
|
2777
|
+
fontSize: 12,
|
|
2778
|
+
fontFamily: "Inter, sans-serif",
|
|
2779
|
+
marginRight: 8
|
|
2780
|
+
},
|
|
2781
|
+
testID: "exercise-card-summary",
|
|
2782
|
+
children: formatSummary(summary)
|
|
2783
|
+
}
|
|
2784
|
+
),
|
|
2785
|
+
isPR && /* @__PURE__ */ jsx(View, { style: { marginLeft: 4 }, children: /* @__PURE__ */ jsx(PrBadge, { type: "e1rm", compact: true, animate: false }) })
|
|
2786
|
+
] }),
|
|
2787
|
+
(completedSets > 0 || remaining > 0) && /* @__PURE__ */ jsxs(View, { className: "flex-row", style: { marginTop: 6, gap: 4 }, testID: "exercise-card-strips", children: [
|
|
2788
|
+
setVelocities?.map((velocities, i) => /* @__PURE__ */ jsx(
|
|
2789
|
+
VelocityStrip,
|
|
2790
|
+
{
|
|
2791
|
+
velocities,
|
|
2792
|
+
zones: velocityZones,
|
|
2793
|
+
variant: "mini",
|
|
2794
|
+
testID: `exercise-card-velocity-strip-${i}`
|
|
2795
|
+
},
|
|
2796
|
+
i
|
|
2797
|
+
)),
|
|
2798
|
+
Array.from({ length: remaining }, (_, i) => /* @__PURE__ */ jsx(
|
|
2799
|
+
PlaceholderStrip,
|
|
2800
|
+
{
|
|
2801
|
+
testID: `exercise-card-placeholder-${i}`
|
|
2802
|
+
},
|
|
2803
|
+
`placeholder-${i}`
|
|
2804
|
+
))
|
|
2805
|
+
] })
|
|
2168
2806
|
]
|
|
2169
2807
|
}
|
|
2170
2808
|
)
|
|
2171
2809
|
}
|
|
2172
2810
|
);
|
|
2173
2811
|
}
|
|
2812
|
+
var DARK2 = getSemanticColors("dark");
|
|
2813
|
+
var BODY_DIVIDER = DARK2["border-subtle"];
|
|
2814
|
+
function deriveHeaderSetStates(sets) {
|
|
2815
|
+
return sets.map((set) => {
|
|
2816
|
+
switch (set.state) {
|
|
2817
|
+
case "live":
|
|
2818
|
+
return { status: "active", velocities: set.velocities, planned: set.target.reps };
|
|
2819
|
+
case "done":
|
|
2820
|
+
return { status: "done", velocities: set.velocities };
|
|
2821
|
+
case "todo":
|
|
2822
|
+
return { status: "todo", planned: set.planned ?? set.target.reps };
|
|
2823
|
+
}
|
|
2824
|
+
});
|
|
2825
|
+
}
|
|
2174
2826
|
function ExpandedCard({
|
|
2175
2827
|
name,
|
|
2176
2828
|
onToggle,
|
|
2177
2829
|
summary,
|
|
2178
|
-
e1rm,
|
|
2179
2830
|
isPR,
|
|
2180
2831
|
sets,
|
|
2181
2832
|
tempo,
|
|
2833
|
+
indicator,
|
|
2834
|
+
setStates,
|
|
2835
|
+
stripHeight = 8,
|
|
2836
|
+
velocityZones,
|
|
2182
2837
|
supersetPosition
|
|
2183
2838
|
}) {
|
|
2184
2839
|
const borderRadius = getSupersetBorderRadius(supersetPosition);
|
|
2185
2840
|
const supersetMargin = getSupersetMargin(supersetPosition);
|
|
2186
2841
|
const unit = summary?.unit ?? sets?.[0]?.unit ?? "lbs";
|
|
2187
|
-
const
|
|
2842
|
+
const headerStates = setStates ?? (sets ? deriveHeaderSetStates(sets) : []);
|
|
2843
|
+
const headerIndicator = indicator ?? (isPR ? "pr" : void 0);
|
|
2188
2844
|
return /* @__PURE__ */ jsxs(
|
|
2189
2845
|
View,
|
|
2190
2846
|
{
|
|
@@ -2197,114 +2853,32 @@ function ExpandedCard({
|
|
|
2197
2853
|
testID: "exercise-card",
|
|
2198
2854
|
children: [
|
|
2199
2855
|
/* @__PURE__ */ jsx(
|
|
2200
|
-
|
|
2856
|
+
ExerciseCardHeading,
|
|
2201
2857
|
{
|
|
2858
|
+
name,
|
|
2859
|
+
sets: summary?.sets ?? sets?.length ?? 0,
|
|
2860
|
+
reps: summary?.reps ?? 0,
|
|
2861
|
+
load: summary?.weight ?? 0,
|
|
2862
|
+
unit,
|
|
2863
|
+
tempo,
|
|
2864
|
+
indicator: headerIndicator,
|
|
2865
|
+
setStates: headerStates,
|
|
2866
|
+
stripHeight,
|
|
2202
2867
|
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
|
-
)
|
|
2868
|
+
testID: "exercise-card-heading"
|
|
2260
2869
|
}
|
|
2261
2870
|
),
|
|
2262
|
-
|
|
2263
|
-
/* @__PURE__ */ jsx(
|
|
2871
|
+
/* @__PURE__ */ jsxs(
|
|
2264
2872
|
View,
|
|
2265
2873
|
{
|
|
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
|
-
})
|
|
2874
|
+
style: { borderTopWidth: 1, borderTopColor: BODY_DIVIDER, paddingBottom: 6 },
|
|
2875
|
+
testID: "exercise-card-body",
|
|
2876
|
+
children: [
|
|
2877
|
+
/* @__PURE__ */ jsx(SetTableHeader, { unit, showPrevious: false, testID: "exercise-card-column-headers" }),
|
|
2878
|
+
sets && /* @__PURE__ */ jsx(View, { testID: "exercise-card-sets", children: sets.map((set, i) => /* @__PURE__ */ jsx(SetRow, { ...set, velocityZones: set.velocityZones ?? velocityZones }, i)) })
|
|
2879
|
+
]
|
|
2305
2880
|
}
|
|
2306
|
-
)
|
|
2307
|
-
sets && /* @__PURE__ */ jsx(View, { testID: "exercise-card-sets", children: sets.map((setProps, i) => /* @__PURE__ */ jsx(SetRow, { ...setProps }, i)) })
|
|
2881
|
+
)
|
|
2308
2882
|
]
|
|
2309
2883
|
}
|
|
2310
2884
|
);
|
|
@@ -2313,11 +2887,12 @@ function UpcomingCard({
|
|
|
2313
2887
|
name,
|
|
2314
2888
|
prescription,
|
|
2315
2889
|
previousBest,
|
|
2316
|
-
supersetPosition
|
|
2890
|
+
supersetPosition,
|
|
2891
|
+
onToggle
|
|
2317
2892
|
}) {
|
|
2318
2893
|
const borderRadius = getSupersetBorderRadius(supersetPosition);
|
|
2319
2894
|
const supersetMargin = getSupersetMargin(supersetPosition);
|
|
2320
|
-
return /* @__PURE__ */ jsx(
|
|
2895
|
+
return /* @__PURE__ */ jsx(Pressable, { onPress: onToggle, accessibilityRole: "button", children: /* @__PURE__ */ jsx(
|
|
2321
2896
|
View,
|
|
2322
2897
|
{
|
|
2323
2898
|
style: {
|
|
@@ -2327,7 +2902,7 @@ function UpcomingCard({
|
|
|
2327
2902
|
...borderRadius,
|
|
2328
2903
|
...supersetMargin
|
|
2329
2904
|
},
|
|
2330
|
-
accessibilityLabel:
|
|
2905
|
+
accessibilityLabel: formatAccessibilityLabel(name, void 0, prescription),
|
|
2331
2906
|
testID: "exercise-card",
|
|
2332
2907
|
children: /* @__PURE__ */ jsxs(View, { className: "flex-row items-center", children: [
|
|
2333
2908
|
/* @__PURE__ */ jsx(
|
|
@@ -2335,6 +2910,7 @@ function UpcomingCard({
|
|
|
2335
2910
|
{
|
|
2336
2911
|
className: "text-text-primary",
|
|
2337
2912
|
style: {
|
|
2913
|
+
flexShrink: 0,
|
|
2338
2914
|
fontSize: 14,
|
|
2339
2915
|
fontFamily: '"Space Grotesk", sans-serif',
|
|
2340
2916
|
fontWeight: "700"
|
|
@@ -2347,7 +2923,9 @@ function UpcomingCard({
|
|
|
2347
2923
|
Text,
|
|
2348
2924
|
{
|
|
2349
2925
|
className: "text-text-secondary",
|
|
2926
|
+
numberOfLines: 1,
|
|
2350
2927
|
style: {
|
|
2928
|
+
flexShrink: 1,
|
|
2351
2929
|
fontSize: 12,
|
|
2352
2930
|
fontFamily: "Inter, sans-serif",
|
|
2353
2931
|
marginLeft: 8
|
|
@@ -2356,12 +2934,14 @@ function UpcomingCard({
|
|
|
2356
2934
|
children: prescription
|
|
2357
2935
|
}
|
|
2358
2936
|
),
|
|
2359
|
-
/* @__PURE__ */ jsx(View, { className: "flex-1" }),
|
|
2937
|
+
/* @__PURE__ */ jsx(View, { className: "flex-1", style: { minWidth: 8 } }),
|
|
2360
2938
|
previousBest && /* @__PURE__ */ jsx(
|
|
2361
2939
|
Text,
|
|
2362
2940
|
{
|
|
2363
2941
|
className: "text-text-tertiary",
|
|
2942
|
+
numberOfLines: 1,
|
|
2364
2943
|
style: {
|
|
2944
|
+
flexShrink: 1,
|
|
2365
2945
|
fontSize: 11,
|
|
2366
2946
|
fontFamily: "Inter, sans-serif"
|
|
2367
2947
|
},
|
|
@@ -2371,18 +2951,31 @@ function UpcomingCard({
|
|
|
2371
2951
|
)
|
|
2372
2952
|
] })
|
|
2373
2953
|
}
|
|
2374
|
-
);
|
|
2954
|
+
) });
|
|
2375
2955
|
}
|
|
2376
|
-
function ExerciseCard(
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2956
|
+
function ExerciseCard({
|
|
2957
|
+
upcoming,
|
|
2958
|
+
expanded,
|
|
2959
|
+
defaultExpanded,
|
|
2960
|
+
onExpandedChange,
|
|
2961
|
+
...rest
|
|
2962
|
+
}) {
|
|
2963
|
+
const [internalExpanded, setInternalExpanded] = useState(defaultExpanded ?? false);
|
|
2964
|
+
const isControlled = expanded !== void 0;
|
|
2965
|
+
const isExpanded = isControlled ? expanded : internalExpanded;
|
|
2966
|
+
const onToggle = () => {
|
|
2967
|
+
const next = !isExpanded;
|
|
2968
|
+
onExpandedChange?.(next);
|
|
2969
|
+
if (!isControlled) setInternalExpanded(next);
|
|
2970
|
+
};
|
|
2971
|
+
if (upcoming) return /* @__PURE__ */ jsx(UpcomingCard, { ...rest, onToggle });
|
|
2972
|
+
return isExpanded ? /* @__PURE__ */ jsx(ExpandedCard, { ...rest, onToggle }) : /* @__PURE__ */ jsx(CollapsedCard, { ...rest, onToggle });
|
|
2973
|
+
}
|
|
2974
|
+
function resolveColor(token, mode = "dark") {
|
|
2975
|
+
return Platform.OS === "web" ? `var(--color-${token})` : getSemanticColors(mode)[token];
|
|
2385
2976
|
}
|
|
2977
|
+
|
|
2978
|
+
// src/components/custom/Workout/InputBar.tsx
|
|
2386
2979
|
var BRAND_PRIMARY2 = getSemanticColors("dark")["brand-primary"];
|
|
2387
2980
|
var INPUT_CLASSNAME = "bg-surface-raised border-border-strong text-text-primary";
|
|
2388
2981
|
var inputStyle = {
|
|
@@ -2539,7 +3132,307 @@ function InputBar({
|
|
|
2539
3132
|
}
|
|
2540
3133
|
);
|
|
2541
3134
|
}
|
|
3135
|
+
function formatDuration(ms) {
|
|
3136
|
+
const totalSeconds = Math.max(0, Math.ceil(ms / 1e3));
|
|
3137
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
3138
|
+
const seconds = totalSeconds % 60;
|
|
3139
|
+
return `${minutes}:${String(seconds).padStart(2, "0")}`;
|
|
3140
|
+
}
|
|
3141
|
+
function useTimer(options = {}) {
|
|
3142
|
+
const {
|
|
3143
|
+
mode = "up",
|
|
3144
|
+
durationMs,
|
|
3145
|
+
elapsedMs: controlledElapsedMs,
|
|
3146
|
+
running = false,
|
|
3147
|
+
autoTick = false,
|
|
3148
|
+
tickMs = 1e3
|
|
3149
|
+
} = options;
|
|
3150
|
+
const isControlled = controlledElapsedMs != null;
|
|
3151
|
+
const [internalElapsedMs, setInternalElapsedMs] = useState(0);
|
|
3152
|
+
useEffect(() => {
|
|
3153
|
+
if (isControlled || !autoTick || !running) return;
|
|
3154
|
+
const id = setInterval(() => {
|
|
3155
|
+
setInternalElapsedMs((prev) => prev + tickMs);
|
|
3156
|
+
}, tickMs);
|
|
3157
|
+
return () => clearInterval(id);
|
|
3158
|
+
}, [isControlled, autoTick, running, tickMs]);
|
|
3159
|
+
const elapsedMs = isControlled ? controlledElapsedMs : internalElapsedMs;
|
|
3160
|
+
const hasDuration = durationMs != null && durationMs > 0;
|
|
3161
|
+
const remainingMs = durationMs != null ? Math.max(0, durationMs - elapsedMs) : 0;
|
|
3162
|
+
let progress;
|
|
3163
|
+
if (hasDuration) {
|
|
3164
|
+
progress = Math.min(1, Math.max(0, elapsedMs / durationMs));
|
|
3165
|
+
} else {
|
|
3166
|
+
progress = mode === "down" ? 1 : 0;
|
|
3167
|
+
}
|
|
3168
|
+
const label = formatDuration(mode === "down" ? remainingMs : elapsedMs);
|
|
3169
|
+
const done = durationMs != null && elapsedMs >= durationMs;
|
|
3170
|
+
return { elapsedMs, remainingMs, progress, label, done };
|
|
3171
|
+
}
|
|
3172
|
+
var colorVarMap = {
|
|
3173
|
+
primary: "var(--color-brand-primary)",
|
|
3174
|
+
secondary: "var(--color-brand-secondary)",
|
|
3175
|
+
success: "var(--color-status-success)",
|
|
3176
|
+
error: "var(--color-status-error)",
|
|
3177
|
+
warning: "var(--color-status-warning)",
|
|
3178
|
+
info: "var(--color-status-info)"
|
|
3179
|
+
};
|
|
3180
|
+
function CircularProgress({
|
|
3181
|
+
value = 0,
|
|
3182
|
+
max = 100,
|
|
3183
|
+
isIndeterminate = false,
|
|
3184
|
+
size = 48,
|
|
3185
|
+
strokeWidth = 4,
|
|
3186
|
+
color = "primary",
|
|
3187
|
+
showValue = false,
|
|
3188
|
+
formatValue: formatValue2 = (v, m) => `${Math.round(v / m * 100)}%`,
|
|
3189
|
+
children,
|
|
3190
|
+
fill = false,
|
|
3191
|
+
className,
|
|
3192
|
+
...props
|
|
3193
|
+
}) {
|
|
3194
|
+
const percentage = Math.min(Math.max(value / max * 100, 0), 100);
|
|
3195
|
+
const radius = (size - strokeWidth) / 2;
|
|
3196
|
+
const circumference = 2 * Math.PI * radius;
|
|
3197
|
+
const strokeDashoffset = circumference - percentage / 100 * circumference;
|
|
3198
|
+
const center = size / 2;
|
|
3199
|
+
const boxStyle = fill ? { width: "100%", aspectRatio: 1 } : { width: size, height: size };
|
|
3200
|
+
return /* @__PURE__ */ jsxs(
|
|
3201
|
+
View,
|
|
3202
|
+
{
|
|
3203
|
+
className: cn("items-center justify-center", className),
|
|
3204
|
+
style: boxStyle,
|
|
3205
|
+
accessibilityRole: "progressbar",
|
|
3206
|
+
accessibilityValue: {
|
|
3207
|
+
min: 0,
|
|
3208
|
+
max,
|
|
3209
|
+
now: isIndeterminate ? void 0 : value
|
|
3210
|
+
},
|
|
3211
|
+
...props,
|
|
3212
|
+
children: [
|
|
3213
|
+
/* @__PURE__ */ jsx(View, { className: cn(isIndeterminate && "animate-spin"), style: boxStyle, children: /* @__PURE__ */ jsxs(
|
|
3214
|
+
"svg",
|
|
3215
|
+
{
|
|
3216
|
+
width: "100%",
|
|
3217
|
+
height: "100%",
|
|
3218
|
+
viewBox: `0 0 ${size} ${size}`,
|
|
3219
|
+
style: { position: "absolute" },
|
|
3220
|
+
children: [
|
|
3221
|
+
/* @__PURE__ */ jsx(
|
|
3222
|
+
"circle",
|
|
3223
|
+
{
|
|
3224
|
+
cx: center,
|
|
3225
|
+
cy: center,
|
|
3226
|
+
r: radius,
|
|
3227
|
+
fill: "none",
|
|
3228
|
+
stroke: "var(--color-border-default)",
|
|
3229
|
+
strokeWidth
|
|
3230
|
+
}
|
|
3231
|
+
),
|
|
3232
|
+
/* @__PURE__ */ jsx(
|
|
3233
|
+
"circle",
|
|
3234
|
+
{
|
|
3235
|
+
cx: center,
|
|
3236
|
+
cy: center,
|
|
3237
|
+
r: radius,
|
|
3238
|
+
fill: "none",
|
|
3239
|
+
stroke: colorVarMap[color],
|
|
3240
|
+
strokeWidth,
|
|
3241
|
+
strokeLinecap: "round",
|
|
3242
|
+
strokeDasharray: circumference,
|
|
3243
|
+
strokeDashoffset: isIndeterminate ? circumference * 0.75 : strokeDashoffset,
|
|
3244
|
+
transform: `rotate(-90 ${center} ${center})`,
|
|
3245
|
+
style: { transition: "stroke-dashoffset 300ms cubic-bezier(0.22, 1, 0.36, 1)" }
|
|
3246
|
+
}
|
|
3247
|
+
)
|
|
3248
|
+
]
|
|
3249
|
+
}
|
|
3250
|
+
) }),
|
|
3251
|
+
children != null ? /* @__PURE__ */ jsx(
|
|
3252
|
+
View,
|
|
3253
|
+
{
|
|
3254
|
+
style: {
|
|
3255
|
+
position: "absolute",
|
|
3256
|
+
alignItems: "center",
|
|
3257
|
+
justifyContent: "center",
|
|
3258
|
+
pointerEvents: "none"
|
|
3259
|
+
},
|
|
3260
|
+
children
|
|
3261
|
+
}
|
|
3262
|
+
) : showValue && !isIndeterminate && /* @__PURE__ */ jsx(
|
|
3263
|
+
Text,
|
|
3264
|
+
{
|
|
3265
|
+
className: "text-xs font-semibold text-text-primary absolute",
|
|
3266
|
+
style: { fontSize: Math.max(size * 0.22, 10) },
|
|
3267
|
+
children: formatValue2(value, max)
|
|
3268
|
+
}
|
|
3269
|
+
)
|
|
3270
|
+
]
|
|
3271
|
+
}
|
|
3272
|
+
);
|
|
3273
|
+
}
|
|
3274
|
+
|
|
3275
|
+
// src/components/custom/CircularTimer/CircularTimer.tsx
|
|
3276
|
+
function CircularTimer({
|
|
3277
|
+
durationMs,
|
|
3278
|
+
elapsedMs,
|
|
3279
|
+
mode = "down",
|
|
3280
|
+
size = 160,
|
|
3281
|
+
strokeWidth = 8,
|
|
3282
|
+
fill = false,
|
|
3283
|
+
color = "primary",
|
|
3284
|
+
doneLabel,
|
|
3285
|
+
doneColor = "success",
|
|
3286
|
+
controls,
|
|
3287
|
+
accessibilityLabel: accessibilityLabel2,
|
|
3288
|
+
testID
|
|
3289
|
+
}) {
|
|
3290
|
+
const { remainingMs, progress, label, done } = useTimer({ mode, durationMs, elapsedMs });
|
|
3291
|
+
const isDone = done && mode === "down";
|
|
3292
|
+
const showDone = isDone && doneLabel != null;
|
|
3293
|
+
const remainingFrac = durationMs > 0 ? Math.max(0, Math.min(1, remainingMs / durationMs)) : 0;
|
|
3294
|
+
const arc = mode === "down" ? showDone ? 1 : remainingFrac : progress;
|
|
3295
|
+
const ringColor = showDone ? doneColor : color;
|
|
3296
|
+
const remainingSec = Math.ceil(Math.max(0, remainingMs) / 1e3);
|
|
3297
|
+
const a11y = accessibilityLabel2 ?? (isDone ? "Timer complete" : `${remainingSec} seconds remaining`);
|
|
3298
|
+
return /* @__PURE__ */ jsxs(
|
|
3299
|
+
View,
|
|
3300
|
+
{
|
|
3301
|
+
style: { alignItems: "center", gap: 16 },
|
|
3302
|
+
accessibilityRole: "timer",
|
|
3303
|
+
accessibilityLabel: a11y,
|
|
3304
|
+
testID: testID ?? "circular-timer",
|
|
3305
|
+
children: [
|
|
3306
|
+
/* @__PURE__ */ jsx(
|
|
3307
|
+
CircularProgress,
|
|
3308
|
+
{
|
|
3309
|
+
value: arc,
|
|
3310
|
+
max: 1,
|
|
3311
|
+
size,
|
|
3312
|
+
strokeWidth,
|
|
3313
|
+
fill,
|
|
3314
|
+
color: ringColor,
|
|
3315
|
+
accessibilityLabel: "Timer ring",
|
|
3316
|
+
testID: "circular-timer-ring",
|
|
3317
|
+
children: /* @__PURE__ */ jsx(
|
|
3318
|
+
Text,
|
|
3319
|
+
{
|
|
3320
|
+
className: showDone ? void 0 : "text-text-primary",
|
|
3321
|
+
style: {
|
|
3322
|
+
fontSize: Math.round(size * (showDone ? 0.28 : 0.24)),
|
|
3323
|
+
fontFamily: '"Space Grotesk", sans-serif',
|
|
3324
|
+
fontWeight: "800",
|
|
3325
|
+
fontVariant: ["tabular-nums"],
|
|
3326
|
+
letterSpacing: showDone ? 1 : -1,
|
|
3327
|
+
...showDone ? { color: colorVarMap[doneColor] } : null
|
|
3328
|
+
},
|
|
3329
|
+
testID: "circular-timer-label",
|
|
3330
|
+
children: showDone ? doneLabel : label
|
|
3331
|
+
}
|
|
3332
|
+
)
|
|
3333
|
+
}
|
|
3334
|
+
),
|
|
3335
|
+
controls
|
|
3336
|
+
]
|
|
3337
|
+
}
|
|
3338
|
+
);
|
|
3339
|
+
}
|
|
3340
|
+
|
|
3341
|
+
// src/components/custom/Workout/RestTimer.tsx
|
|
2542
3342
|
var BRAND_PRIMARY3 = getSemanticColors("dark")["brand-primary"];
|
|
3343
|
+
var RING_DEFAULT_SIZE = 180;
|
|
3344
|
+
function RestActions({ onAddTime, onSkip }) {
|
|
3345
|
+
return /* @__PURE__ */ jsxs(View, { style: { flexDirection: "row", gap: 8 }, children: [
|
|
3346
|
+
/* @__PURE__ */ jsx(
|
|
3347
|
+
Pressable,
|
|
3348
|
+
{
|
|
3349
|
+
onPress: onAddTime,
|
|
3350
|
+
style: {
|
|
3351
|
+
backgroundColor: "rgba(255,255,255,0.06)",
|
|
3352
|
+
paddingVertical: 8,
|
|
3353
|
+
paddingHorizontal: 20,
|
|
3354
|
+
borderRadius: 8
|
|
3355
|
+
},
|
|
3356
|
+
accessibilityRole: "button",
|
|
3357
|
+
accessibilityLabel: "Add 30 seconds",
|
|
3358
|
+
testID: "rest-timer-add-time",
|
|
3359
|
+
children: /* @__PURE__ */ jsx(
|
|
3360
|
+
Text,
|
|
3361
|
+
{
|
|
3362
|
+
className: "text-text-secondary",
|
|
3363
|
+
style: { fontSize: 11, fontFamily: "Inter, sans-serif", fontWeight: "600" },
|
|
3364
|
+
children: "+30s"
|
|
3365
|
+
}
|
|
3366
|
+
)
|
|
3367
|
+
}
|
|
3368
|
+
),
|
|
3369
|
+
/* @__PURE__ */ jsx(
|
|
3370
|
+
Pressable,
|
|
3371
|
+
{
|
|
3372
|
+
onPress: onSkip,
|
|
3373
|
+
style: {
|
|
3374
|
+
backgroundColor: "rgba(255,121,0,0.12)",
|
|
3375
|
+
paddingVertical: 8,
|
|
3376
|
+
paddingHorizontal: 20,
|
|
3377
|
+
borderRadius: 8
|
|
3378
|
+
},
|
|
3379
|
+
accessibilityRole: "button",
|
|
3380
|
+
accessibilityLabel: "Skip rest",
|
|
3381
|
+
testID: "rest-timer-skip",
|
|
3382
|
+
children: /* @__PURE__ */ jsx(
|
|
3383
|
+
Text,
|
|
3384
|
+
{
|
|
3385
|
+
style: {
|
|
3386
|
+
fontSize: 11,
|
|
3387
|
+
fontFamily: "Inter, sans-serif",
|
|
3388
|
+
fontWeight: "600",
|
|
3389
|
+
color: BRAND_PRIMARY3
|
|
3390
|
+
},
|
|
3391
|
+
children: "Skip"
|
|
3392
|
+
}
|
|
3393
|
+
)
|
|
3394
|
+
}
|
|
3395
|
+
)
|
|
3396
|
+
] });
|
|
3397
|
+
}
|
|
3398
|
+
function RestTimerRing({
|
|
3399
|
+
totalSeconds,
|
|
3400
|
+
elapsedMs,
|
|
3401
|
+
remainingSec,
|
|
3402
|
+
done,
|
|
3403
|
+
size,
|
|
3404
|
+
nextSetInfo,
|
|
3405
|
+
displayOnly,
|
|
3406
|
+
onSkip,
|
|
3407
|
+
onAddTime
|
|
3408
|
+
}) {
|
|
3409
|
+
const a11yLabel = done ? "Rest complete, next set ready" : `Rest timer, ${Math.max(0, remainingSec)} seconds remaining`;
|
|
3410
|
+
return /* @__PURE__ */ jsxs(View, { style: { alignItems: "center", gap: 16 }, testID: "rest-timer", children: [
|
|
3411
|
+
/* @__PURE__ */ jsx(
|
|
3412
|
+
CircularTimer,
|
|
3413
|
+
{
|
|
3414
|
+
durationMs: totalSeconds * 1e3,
|
|
3415
|
+
elapsedMs,
|
|
3416
|
+
mode: "down",
|
|
3417
|
+
size,
|
|
3418
|
+
doneLabel: "GO",
|
|
3419
|
+
doneColor: "success",
|
|
3420
|
+
accessibilityLabel: a11yLabel,
|
|
3421
|
+
controls: !displayOnly ? /* @__PURE__ */ jsx(RestActions, { onAddTime, onSkip }) : void 0,
|
|
3422
|
+
testID: "rest-timer-ring"
|
|
3423
|
+
}
|
|
3424
|
+
),
|
|
3425
|
+
nextSetInfo != null && /* @__PURE__ */ jsx(
|
|
3426
|
+
Text,
|
|
3427
|
+
{
|
|
3428
|
+
className: "text-text-tertiary",
|
|
3429
|
+
style: { fontSize: 13, fontFamily: "Inter, sans-serif" },
|
|
3430
|
+
testID: "rest-timer-next-set",
|
|
3431
|
+
children: nextSetInfo
|
|
3432
|
+
}
|
|
3433
|
+
)
|
|
3434
|
+
] });
|
|
3435
|
+
}
|
|
2543
3436
|
function RestTimer({
|
|
2544
3437
|
totalSeconds,
|
|
2545
3438
|
elapsedMs,
|
|
@@ -2547,16 +3440,39 @@ function RestTimer({
|
|
|
2547
3440
|
onAddTime,
|
|
2548
3441
|
nextSetInfo,
|
|
2549
3442
|
visible,
|
|
2550
|
-
displayOnly = false
|
|
3443
|
+
displayOnly = false,
|
|
3444
|
+
variant = "bar",
|
|
3445
|
+
size = RING_DEFAULT_SIZE
|
|
2551
3446
|
}) {
|
|
2552
|
-
|
|
2553
|
-
|
|
3447
|
+
const {
|
|
3448
|
+
remainingMs,
|
|
3449
|
+
progress,
|
|
3450
|
+
label: timeDisplay,
|
|
3451
|
+
done
|
|
3452
|
+
} = useTimer({
|
|
3453
|
+
mode: "down",
|
|
3454
|
+
durationMs: totalSeconds * 1e3,
|
|
3455
|
+
elapsedMs
|
|
3456
|
+
});
|
|
2554
3457
|
const remainingSec = Math.ceil(remainingMs / 1e3);
|
|
2555
|
-
const
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
3458
|
+
const progressPct = progress * 100;
|
|
3459
|
+
if (!visible) return null;
|
|
3460
|
+
if (variant === "ring") {
|
|
3461
|
+
return /* @__PURE__ */ jsx(
|
|
3462
|
+
RestTimerRing,
|
|
3463
|
+
{
|
|
3464
|
+
totalSeconds,
|
|
3465
|
+
elapsedMs,
|
|
3466
|
+
remainingSec,
|
|
3467
|
+
done,
|
|
3468
|
+
size,
|
|
3469
|
+
nextSetInfo,
|
|
3470
|
+
displayOnly,
|
|
3471
|
+
onSkip,
|
|
3472
|
+
onAddTime
|
|
3473
|
+
}
|
|
3474
|
+
);
|
|
3475
|
+
}
|
|
2560
3476
|
return /* @__PURE__ */ jsxs(
|
|
2561
3477
|
View,
|
|
2562
3478
|
{
|
|
@@ -2654,62 +3570,7 @@ function RestTimer({
|
|
|
2654
3570
|
)
|
|
2655
3571
|
}
|
|
2656
3572
|
),
|
|
2657
|
-
!displayOnly && /* @__PURE__ */
|
|
2658
|
-
/* @__PURE__ */ jsx(
|
|
2659
|
-
Pressable,
|
|
2660
|
-
{
|
|
2661
|
-
onPress: onAddTime,
|
|
2662
|
-
style: {
|
|
2663
|
-
backgroundColor: "rgba(255,255,255,0.06)",
|
|
2664
|
-
paddingVertical: 8,
|
|
2665
|
-
paddingHorizontal: 20,
|
|
2666
|
-
borderRadius: 8
|
|
2667
|
-
},
|
|
2668
|
-
accessibilityRole: "button",
|
|
2669
|
-
accessibilityLabel: "Add 30 seconds",
|
|
2670
|
-
testID: "rest-timer-add-time",
|
|
2671
|
-
children: /* @__PURE__ */ jsx(
|
|
2672
|
-
Text,
|
|
2673
|
-
{
|
|
2674
|
-
className: "text-text-secondary",
|
|
2675
|
-
style: {
|
|
2676
|
-
fontSize: 11,
|
|
2677
|
-
fontFamily: "Inter, sans-serif",
|
|
2678
|
-
fontWeight: "600"
|
|
2679
|
-
},
|
|
2680
|
-
children: "+30s"
|
|
2681
|
-
}
|
|
2682
|
-
)
|
|
2683
|
-
}
|
|
2684
|
-
),
|
|
2685
|
-
/* @__PURE__ */ jsx(
|
|
2686
|
-
Pressable,
|
|
2687
|
-
{
|
|
2688
|
-
onPress: onSkip,
|
|
2689
|
-
style: {
|
|
2690
|
-
backgroundColor: "rgba(255,121,0,0.12)",
|
|
2691
|
-
paddingVertical: 8,
|
|
2692
|
-
paddingHorizontal: 20,
|
|
2693
|
-
borderRadius: 8
|
|
2694
|
-
},
|
|
2695
|
-
accessibilityRole: "button",
|
|
2696
|
-
accessibilityLabel: "Skip rest",
|
|
2697
|
-
testID: "rest-timer-skip",
|
|
2698
|
-
children: /* @__PURE__ */ jsx(
|
|
2699
|
-
Text,
|
|
2700
|
-
{
|
|
2701
|
-
style: {
|
|
2702
|
-
fontSize: 11,
|
|
2703
|
-
fontFamily: "Inter, sans-serif",
|
|
2704
|
-
fontWeight: "600",
|
|
2705
|
-
color: BRAND_PRIMARY3
|
|
2706
|
-
},
|
|
2707
|
-
children: "Skip"
|
|
2708
|
-
}
|
|
2709
|
-
)
|
|
2710
|
-
}
|
|
2711
|
-
)
|
|
2712
|
-
] })
|
|
3573
|
+
!displayOnly && /* @__PURE__ */ jsx(RestActions, { onAddTime, onSkip })
|
|
2713
3574
|
]
|
|
2714
3575
|
}
|
|
2715
3576
|
);
|
|
@@ -2795,23 +3656,22 @@ function deriveWorkoutProgress(exercises) {
|
|
|
2795
3656
|
function findActiveExercise(exercises) {
|
|
2796
3657
|
return exercises.find((exercise) => exercise.status === "active") ?? null;
|
|
2797
3658
|
}
|
|
2798
|
-
function
|
|
2799
|
-
|
|
2800
|
-
return focused ? "expanded" : "collapsed";
|
|
3659
|
+
function isUpcomingExercise(status) {
|
|
3660
|
+
return status === "upcoming";
|
|
2801
3661
|
}
|
|
2802
3662
|
function toActiveCardProps(exercise, focused, onToggle, supersetPosition = null, supersetColor) {
|
|
2803
|
-
const
|
|
3663
|
+
const upcoming = isUpcomingExercise(exercise.status);
|
|
2804
3664
|
return {
|
|
2805
3665
|
name: exercise.name,
|
|
2806
|
-
|
|
2807
|
-
|
|
3666
|
+
upcoming,
|
|
3667
|
+
expanded: focused,
|
|
3668
|
+
onExpandedChange: onToggle,
|
|
2808
3669
|
summary: exercise.summary,
|
|
2809
|
-
e1rm: exercise.e1rm,
|
|
2810
3670
|
isPR: exercise.isPR,
|
|
2811
3671
|
tempo: exercise.tempo,
|
|
2812
3672
|
setVelocities: exercise.setVelocities,
|
|
2813
3673
|
totalPlannedSets: exercise.totalPlannedSets,
|
|
2814
|
-
sets:
|
|
3674
|
+
sets: focused ? exercise.sets : void 0,
|
|
2815
3675
|
prescription: exercise.prescription,
|
|
2816
3676
|
previousBest: exercise.previousBest,
|
|
2817
3677
|
supersetPosition,
|
|
@@ -3500,11 +4360,11 @@ function CardContent({ children, className }) {
|
|
|
3500
4360
|
function CardFooter({ children, className }) {
|
|
3501
4361
|
return /* @__PURE__ */ jsx(View, { className: cn("px-6 py-4 flex-row items-center gap-2", className), children });
|
|
3502
4362
|
}
|
|
3503
|
-
var
|
|
4363
|
+
var t5 = getSemanticColors("dark");
|
|
3504
4364
|
var solidVariantColors = {
|
|
3505
|
-
success:
|
|
3506
|
-
warning:
|
|
3507
|
-
error:
|
|
4365
|
+
success: t5["status-success"],
|
|
4366
|
+
warning: t5["status-warning"],
|
|
4367
|
+
error: t5["status-error"],
|
|
3508
4368
|
neutral: "#6B7280"
|
|
3509
4369
|
};
|
|
3510
4370
|
var ringVariantStyles = {
|
|
@@ -3544,8 +4404,8 @@ var iconColors = {
|
|
|
3544
4404
|
warning: "#6B4000",
|
|
3545
4405
|
error: "#5C1A1A",
|
|
3546
4406
|
neutral: "#D1D5DB",
|
|
3547
|
-
"on-track":
|
|
3548
|
-
deviation:
|
|
4407
|
+
"on-track": t5["status-success"],
|
|
4408
|
+
deviation: t5["status-warning"],
|
|
3549
4409
|
future: "#6B7280"
|
|
3550
4410
|
};
|
|
3551
4411
|
var sizeDimensions = {
|
|
@@ -3631,13 +4491,13 @@ var MESO_ACCENT_GRADIENT_LIGHT = primitiveRamps.orange[300];
|
|
|
3631
4491
|
var WORKOUT_PILL_DELOAD = primitiveRamps.magenta[600];
|
|
3632
4492
|
|
|
3633
4493
|
// src/components/custom/Workout/MesoStatusCard.tsx
|
|
3634
|
-
var
|
|
3635
|
-
var BRAND_PRIMARY5 =
|
|
4494
|
+
var t6 = getSemanticColors("dark");
|
|
4495
|
+
var BRAND_PRIMARY5 = t6["brand-primary"];
|
|
3636
4496
|
var BRAND_PRIMARY_DARK = MESO_ACCENT_GRADIENT_DARK;
|
|
3637
4497
|
var BRAND_PRIMARY_LIGHT = MESO_ACCENT_GRADIENT_LIGHT;
|
|
3638
|
-
var SUCCESS =
|
|
3639
|
-
var WARNING =
|
|
3640
|
-
var ERROR =
|
|
4498
|
+
var SUCCESS = t6["status-success"];
|
|
4499
|
+
var WARNING = t6["status-warning"];
|
|
4500
|
+
var ERROR = t6["status-error"];
|
|
3641
4501
|
var ACCENT_STOPS = [BRAND_PRIMARY_DARK, BRAND_PRIMARY5, BRAND_PRIMARY_LIGHT];
|
|
3642
4502
|
var CARD_GRADIENT = `linear-gradient(135deg, ${resolveColor("surface-elevated")} 0%, ${resolveColor("surface-raised")} 100%)`;
|
|
3643
4503
|
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 +4569,7 @@ function StatusPill({ badge }) {
|
|
|
3709
4569
|
}
|
|
3710
4570
|
);
|
|
3711
4571
|
}
|
|
3712
|
-
function
|
|
4572
|
+
function MetricCell2({ metric }) {
|
|
3713
4573
|
return /* @__PURE__ */ jsxs(View, { style: { width: "48%" }, testID: "meso-status-card-metric", children: [
|
|
3714
4574
|
/* @__PURE__ */ jsx(
|
|
3715
4575
|
Text,
|
|
@@ -3927,7 +4787,7 @@ function MesoStatusCard({
|
|
|
3927
4787
|
className: "flex-row flex-wrap",
|
|
3928
4788
|
style: { gap: 8 },
|
|
3929
4789
|
testID: "meso-status-card-metrics",
|
|
3930
|
-
children: metrics.map((metric, index) => /* @__PURE__ */ jsx(
|
|
4790
|
+
children: metrics.map((metric, index) => /* @__PURE__ */ jsx(MetricCell2, { metric }, `${metric.label}-${index}`))
|
|
3931
4791
|
}
|
|
3932
4792
|
),
|
|
3933
4793
|
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 +4868,8 @@ var ERROR_PILL_BORDER = "rgba(209,67,67,0.20)";
|
|
|
4008
4868
|
var PLOT_LEFT = 26;
|
|
4009
4869
|
var TOOLTIP_WIDTH = 124;
|
|
4010
4870
|
function timeOf(dateStr) {
|
|
4011
|
-
const
|
|
4012
|
-
return Number.isNaN(
|
|
4871
|
+
const t12 = Date.parse(dateStr);
|
|
4872
|
+
return Number.isNaN(t12) ? 0 : t12;
|
|
4013
4873
|
}
|
|
4014
4874
|
function formatValue(value) {
|
|
4015
4875
|
return `${Math.round(value)}`;
|
|
@@ -4498,10 +5358,10 @@ function StrengthTrendChart({
|
|
|
4498
5358
|
}
|
|
4499
5359
|
);
|
|
4500
5360
|
}
|
|
4501
|
-
var
|
|
4502
|
-
var STATUS_SUCCESS2 =
|
|
4503
|
-
var STATUS_WARNING2 =
|
|
4504
|
-
var STATUS_INFO =
|
|
5361
|
+
var t7 = getSemanticColors("dark");
|
|
5362
|
+
var STATUS_SUCCESS2 = t7["status-success"];
|
|
5363
|
+
var STATUS_WARNING2 = t7["status-warning"];
|
|
5364
|
+
var STATUS_INFO = t7["status-info"];
|
|
4505
5365
|
var DOT_BORDER = "#F3F4F6";
|
|
4506
5366
|
var BAND_FILL = "rgba(46,213,115,0.1)";
|
|
4507
5367
|
var BAND_EDGE = "rgba(46,213,115,0.45)";
|
|
@@ -4544,13 +5404,13 @@ function interpEdge(pixels, x, key) {
|
|
|
4544
5404
|
const a = pixels[i];
|
|
4545
5405
|
const b = pixels[i + 1];
|
|
4546
5406
|
if (x >= a.x && x <= b.x) {
|
|
4547
|
-
const
|
|
4548
|
-
return a[key] +
|
|
5407
|
+
const t12 = b.x === a.x ? 0 : (x - a.x) / (b.x - a.x);
|
|
5408
|
+
return a[key] + t12 * (b[key] - a[key]);
|
|
4549
5409
|
}
|
|
4550
5410
|
}
|
|
4551
5411
|
return pixels[pixels.length - 1][key];
|
|
4552
5412
|
}
|
|
4553
|
-
function
|
|
5413
|
+
function buildColumns2(pixels, step) {
|
|
4554
5414
|
if (pixels.length < 2) return [];
|
|
4555
5415
|
const start = pixels[0].x;
|
|
4556
5416
|
const end = pixels[pixels.length - 1].x;
|
|
@@ -4662,7 +5522,7 @@ function CapacityBandChart({
|
|
|
4662
5522
|
}
|
|
4663
5523
|
const { toX, toY } = scale;
|
|
4664
5524
|
const bandPixels = toPixels(band, toX, toY);
|
|
4665
|
-
const columns =
|
|
5525
|
+
const columns = buildColumns2(bandPixels, COLUMN_STEP);
|
|
4666
5526
|
const topEdge = buildEdges(bandPixels, "yHigh");
|
|
4667
5527
|
const bottomEdge = buildEdges(bandPixels, "yLow");
|
|
4668
5528
|
const lastBandPoint = band[band.length - 1];
|
|
@@ -4737,8 +5597,8 @@ function CapacityBandChart({
|
|
|
4737
5597
|
testID: "capacity-band-chart-reveal",
|
|
4738
5598
|
children: /* @__PURE__ */ jsxs(View, { style: { width, height, position: "relative" }, children: [
|
|
4739
5599
|
projection && /* @__PURE__ */ jsxs(View, { testID: "capacity-band-chart-projection", children: [
|
|
4740
|
-
renderColumns(
|
|
4741
|
-
renderColumns(
|
|
5600
|
+
renderColumns(buildColumns2(trainingPixels, COLUMN_STEP), PROJECTION_FILL, "capacity-band-chart-projection-fill"),
|
|
5601
|
+
renderColumns(buildColumns2(restPixels, COLUMN_STEP), PROJECTION_FILL, "capacity-band-chart-projection-fill"),
|
|
4742
5602
|
renderEdges(buildEdges(trainingPixels, "yHigh"), STATUS_SUCCESS2, true, "capacity-band-chart-projection-training"),
|
|
4743
5603
|
renderEdges(buildEdges(trainingPixels, "yLow"), STATUS_SUCCESS2, true, "capacity-band-chart-projection-training"),
|
|
4744
5604
|
renderEdges(buildEdges(restPixels, "yHigh"), STATUS_INFO, true, "capacity-band-chart-projection-rest"),
|
|
@@ -4899,10 +5759,9 @@ function deriveExerciseStats(entries) {
|
|
|
4899
5759
|
function toExerciseCardProps(entry, expanded, onToggle) {
|
|
4900
5760
|
return {
|
|
4901
5761
|
name: entry.title,
|
|
4902
|
-
|
|
4903
|
-
onToggle,
|
|
5762
|
+
expanded,
|
|
5763
|
+
onExpandedChange: onToggle,
|
|
4904
5764
|
summary: entry.summary,
|
|
4905
|
-
e1rm: entry.e1rm,
|
|
4906
5765
|
isPR: entry.isPR,
|
|
4907
5766
|
tempo: entry.tempo,
|
|
4908
5767
|
sets: expanded ? entry.sets : void 0
|
|
@@ -5446,6 +6305,7 @@ var colorStyles = {
|
|
|
5446
6305
|
default: "bg-text-tertiary",
|
|
5447
6306
|
primary: "bg-brand-primary",
|
|
5448
6307
|
success: "bg-status-success",
|
|
6308
|
+
live: "bg-status-live",
|
|
5449
6309
|
error: "bg-status-error",
|
|
5450
6310
|
warning: "bg-status-warning",
|
|
5451
6311
|
info: "bg-status-info",
|
|
@@ -5502,7 +6362,7 @@ function Indicator({
|
|
|
5502
6362
|
colorClass,
|
|
5503
6363
|
pulseMode === "opacity" && "animate-pulse",
|
|
5504
6364
|
ring && "border-2 border-background-base",
|
|
5505
|
-
glow && !customColor && color === "success" && "shadow-[0_0_6px_rgba(46,213,115,0.6)]",
|
|
6365
|
+
glow && !customColor && (color === "success" || color === "live") && "shadow-[0_0_6px_rgba(46,213,115,0.6)]",
|
|
5506
6366
|
glow && !customColor && color === "error" && "shadow-[0_0_6px_rgba(239,68,68,0.6)]",
|
|
5507
6367
|
glow && !customColor && color === "warning" && "shadow-[0_0_6px_rgba(245,158,11,0.6)]",
|
|
5508
6368
|
glow && !customColor && color === "primary" && "shadow-[0_0_6px_rgba(255,121,0,0.6)]",
|
|
@@ -5584,7 +6444,7 @@ function Badge({
|
|
|
5584
6444
|
}
|
|
5585
6445
|
);
|
|
5586
6446
|
}
|
|
5587
|
-
var
|
|
6447
|
+
var t8 = getSemanticColors("dark");
|
|
5588
6448
|
var statusBgColors = {
|
|
5589
6449
|
completed: "rgba(46,213,115,0.15)",
|
|
5590
6450
|
current: "rgba(255,121,0,0.15)",
|
|
@@ -5602,14 +6462,14 @@ var statusBorderStyles = {
|
|
|
5602
6462
|
deload: { borderWidth: 1, borderColor: "rgba(186,41,150,0.3)" }
|
|
5603
6463
|
};
|
|
5604
6464
|
var textColors = {
|
|
5605
|
-
completed:
|
|
5606
|
-
current:
|
|
5607
|
-
next:
|
|
6465
|
+
completed: t8["status-success"],
|
|
6466
|
+
current: t8["brand-primary"],
|
|
6467
|
+
next: t8["brand-primary"],
|
|
5608
6468
|
upcoming: "#6B7280",
|
|
5609
6469
|
missed: "rgba(209,67,67,0.7)",
|
|
5610
6470
|
deload: WORKOUT_PILL_DELOAD
|
|
5611
6471
|
};
|
|
5612
|
-
function
|
|
6472
|
+
function usePulse2(enabled) {
|
|
5613
6473
|
const [opacity] = useState(() => new Animated.Value(1));
|
|
5614
6474
|
useEffect(() => {
|
|
5615
6475
|
if (!enabled) {
|
|
@@ -5647,7 +6507,7 @@ function WorkoutPill({
|
|
|
5647
6507
|
...props
|
|
5648
6508
|
}) {
|
|
5649
6509
|
const shouldPulse = pulse ?? status === "current";
|
|
5650
|
-
const pulseOpacity =
|
|
6510
|
+
const pulseOpacity = usePulse2(shouldPulse);
|
|
5651
6511
|
const isCompleted = status === "completed";
|
|
5652
6512
|
const isMissed = status === "missed";
|
|
5653
6513
|
const pill = /* @__PURE__ */ jsx(
|
|
@@ -5737,19 +6597,19 @@ function WorkoutPill({
|
|
|
5737
6597
|
}
|
|
5738
6598
|
return pill;
|
|
5739
6599
|
}
|
|
5740
|
-
var
|
|
6600
|
+
var t9 = getSemanticColors("dark");
|
|
5741
6601
|
var TRACK_BG = "#333333";
|
|
5742
6602
|
var LABEL_COLOR = "#6B7280";
|
|
5743
6603
|
var TARGET_LINE_COLOR = "rgba(33, 150, 243, 0.5)";
|
|
5744
6604
|
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
6605
|
var ZONE = {
|
|
5746
|
-
building:
|
|
6606
|
+
building: t9["status-success"],
|
|
5747
6607
|
// below target ramp-up
|
|
5748
|
-
approaching:
|
|
6608
|
+
approaching: t9["status-warning"],
|
|
5749
6609
|
// nearing target
|
|
5750
|
-
target:
|
|
6610
|
+
target: t9["brand-primary"],
|
|
5751
6611
|
// exactly at target
|
|
5752
|
-
over1:
|
|
6612
|
+
over1: t9["status-error"],
|
|
5753
6613
|
// mild over-target
|
|
5754
6614
|
over2: WORKOUT_TOKENS.intensity.over2,
|
|
5755
6615
|
// moderate over-target
|
|
@@ -6172,13 +7032,13 @@ function MesoCard({
|
|
|
6172
7032
|
}
|
|
6173
7033
|
) });
|
|
6174
7034
|
}
|
|
6175
|
-
var
|
|
7035
|
+
var t10 = getSemanticColors("dark");
|
|
6176
7036
|
var dotColorMap = {
|
|
6177
7037
|
untrained: "#2C2C2C",
|
|
6178
|
-
behind:
|
|
6179
|
-
ontrack:
|
|
6180
|
-
target:
|
|
6181
|
-
over:
|
|
7038
|
+
behind: t10["brand-secondary"],
|
|
7039
|
+
ontrack: t10["status-success"],
|
|
7040
|
+
target: t10["brand-primary"],
|
|
7041
|
+
over: t10["status-error"]
|
|
6182
7042
|
};
|
|
6183
7043
|
function MuscleGroupChip({
|
|
6184
7044
|
name,
|
|
@@ -6252,10 +7112,10 @@ function MuscleGroupChip({
|
|
|
6252
7112
|
}
|
|
6253
7113
|
|
|
6254
7114
|
// src/components/custom/Workout/WorkoutCard.tsx
|
|
6255
|
-
var
|
|
7115
|
+
var t11 = getSemanticColors("dark");
|
|
6256
7116
|
var COLORS = {
|
|
6257
|
-
statusSuccess:
|
|
6258
|
-
brandPrimary:
|
|
7117
|
+
statusSuccess: t11["status-success"],
|
|
7118
|
+
brandPrimary: t11["brand-primary"],
|
|
6259
7119
|
borderDefault: "#1F1F1F",
|
|
6260
7120
|
brandPrimarySubtle: "rgba(255,121,0,0.06)"
|
|
6261
7121
|
};
|
|
@@ -6539,8 +7399,8 @@ function WeekLevel({ week, onSelectWorkout }) {
|
|
|
6539
7399
|
function WorkoutLevel({ workout, expandedExercise, onToggleExercise }) {
|
|
6540
7400
|
const exercises = workout.exercises.map((exercise, index) => ({
|
|
6541
7401
|
...exercise,
|
|
6542
|
-
|
|
6543
|
-
|
|
7402
|
+
expanded: index === expandedExercise,
|
|
7403
|
+
onExpandedChange: () => onToggleExercise(index)
|
|
6544
7404
|
}));
|
|
6545
7405
|
return /* @__PURE__ */ jsx(View, { testID: "program-planning-page-workout-level", children: /* @__PURE__ */ jsx(
|
|
6546
7406
|
WorkoutCard,
|
|
@@ -6669,6 +7529,6 @@ function ProgramPlanningPage({
|
|
|
6669
7529
|
);
|
|
6670
7530
|
}
|
|
6671
7531
|
|
|
6672
|
-
export { ActiveWorkoutPage, EXERCISE_DETAIL_TABS, ExerciseDetailPage, ProgramPlanningPage, buildBreadcrumbs, countCompletedSets, deriveExerciseStats, deriveNavLevel, deriveWorkoutProgress, findActiveExercise, findMeso, findWeek, findWorkout, groupExercises,
|
|
7532
|
+
export { ActiveWorkoutPage, EXERCISE_DETAIL_TABS, ExerciseDetailPage, ProgramPlanningPage, buildBreadcrumbs, countCompletedSets, deriveExerciseStats, deriveNavLevel, deriveWorkoutProgress, findActiveExercise, findMeso, findWeek, findWorkout, groupExercises, isUpcomingExercise, summarizeVbt, toActiveCardProps, toExerciseCardProps, toProgressBarMesos };
|
|
6673
7533
|
//# sourceMappingURL=pages.mjs.map
|
|
6674
7534
|
//# sourceMappingURL=pages.mjs.map
|