@titan-design/react-ui 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bodymap.js +12 -7
- package/dist/bodymap.js.map +1 -1
- package/dist/bodymap.mjs +12 -7
- package/dist/bodymap.mjs.map +1 -1
- package/dist/index.d.mts +855 -124
- package/dist/index.d.ts +855 -124
- package/dist/index.js +2276 -801
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2230 -802
- package/dist/index.mjs.map +1 -1
- package/dist/{pages-MO0JCySL.d.mts → pages-8u_4WbL-.d.mts} +280 -57
- package/dist/{pages-D7Jlssvp.d.ts → pages-DaWiBOGa.d.ts} +280 -57
- package/dist/pages.d.mts +1 -1
- package/dist/pages.d.ts +1 -1
- package/dist/pages.js +1255 -798
- package/dist/pages.js.map +1 -1
- package/dist/pages.mjs +1255 -798
- package/dist/pages.mjs.map +1 -1
- package/dist/theme/index.d.mts +16 -82
- package/dist/theme/index.d.ts +16 -82
- package/dist/theme/index.js +13 -115
- package/dist/theme/index.js.map +1 -1
- package/dist/theme/index.mjs +13 -115
- package/dist/theme/index.mjs.map +1 -1
- package/dist/theme/tokens-css.js +14 -6
- package/dist/theme/tokens-css.js.map +1 -1
- package/dist/theme/tokens-css.mjs +14 -6
- package/dist/theme/tokens-css.mjs.map +1 -1
- package/package.json +1 -1
- package/src/arch/Architecture.stories.tsx +870 -0
- package/src/arch/annotations.json +48 -0
- package/src/arch/arch-graph.json +4301 -0
- package/src/components/custom/DateTime/DateTime.stories.tsx +1 -1
- package/src/components/custom/EmptyState/EmptyState.stories.tsx +1 -1
- package/src/components/custom/Gauge/Gauge.stories.tsx +1 -1
- package/src/components/custom/Metric/Metric.stories.tsx +1 -1
- package/src/components/custom/Scatter/Scatter.stories.tsx +1 -1
- package/src/components/custom/Sidebar/Sidebar.stories.tsx +1 -1
- package/src/components/custom/Table/Table.stories.tsx +1 -1
- package/src/components/custom/TimerReadout/TimerReadout.stories.tsx +62 -0
- package/src/components/custom/TimerReadout/TimerReadout.test.tsx +72 -0
- package/src/components/custom/TimerReadout/TimerReadout.tsx +75 -0
- package/src/components/custom/TimerReadout/index.ts +1 -0
- package/src/components/custom/Treemap/Treemap.stories.tsx +1 -1
- package/src/components/custom/Typography/Typography.stories.tsx +1 -1
- package/src/components/custom/Workout/ActiveWorkoutPage.stories.tsx +21 -13
- package/src/components/custom/Workout/ActiveWorkoutPage.test.tsx +16 -13
- package/src/components/custom/Workout/ActiveWorkoutPage.tsx +9 -15
- package/src/components/custom/Workout/BaseBadge.stories.tsx +1 -1
- package/src/components/custom/Workout/BodyMap.stories.tsx +1 -1
- package/src/components/custom/Workout/BodyMapDetailPanel.stories.tsx +1 -1
- package/src/components/custom/Workout/CapacityBandChart.stories.tsx +1 -1
- package/src/components/custom/Workout/DeviationBar.stories.tsx +1 -1
- package/src/components/custom/Workout/ExerciseCard.stories.tsx +51 -66
- package/src/components/custom/Workout/ExerciseCard.test.tsx +146 -143
- package/src/components/custom/Workout/ExerciseCard.tsx +176 -198
- package/src/components/custom/Workout/ExerciseCardHeading.stories.tsx +99 -0
- package/src/components/custom/Workout/ExerciseCardHeading.test.tsx +72 -0
- package/src/components/custom/Workout/ExerciseCardHeading.tsx +43 -0
- package/src/components/custom/Workout/ExerciseCardUnified.stories.tsx +151 -0
- package/src/components/custom/Workout/ExerciseDetailPage.stories.tsx +5 -3
- package/src/components/custom/Workout/ExerciseDetailPage.test.tsx +4 -4
- package/src/components/custom/Workout/ExerciseDetailPage.tsx +4 -5
- package/src/components/custom/Workout/ExerciseHeading.stories.tsx +81 -0
- package/src/components/custom/Workout/ExerciseHeading.test.tsx +77 -0
- package/src/components/custom/Workout/ExerciseHeading.tsx +85 -0
- package/src/components/custom/Workout/ExerciseIndicator.stories.tsx +110 -0
- package/src/components/custom/Workout/ExerciseIndicator.test.tsx +93 -0
- package/src/components/custom/Workout/ExerciseIndicator.tsx +120 -0
- package/src/components/custom/Workout/FatigueMeter.stories.tsx +51 -0
- package/src/components/custom/Workout/FatigueMeter.test.tsx +73 -0
- package/src/components/custom/Workout/FatigueMeter.tsx +82 -0
- package/src/components/custom/Workout/InputBar.stories.tsx +1 -1
- package/src/components/custom/Workout/IntensityBar.stories.tsx +1 -1
- package/src/components/custom/Workout/LiveAuraFrame.stories.tsx +153 -0
- package/src/components/custom/Workout/LiveAuraFrame.test.tsx +74 -0
- package/src/components/custom/Workout/LiveAuraFrame.tsx +105 -0
- package/src/components/custom/Workout/MesoCard.stories.tsx +1 -1
- package/src/components/custom/Workout/MesoProgressBar.stories.tsx +1 -1
- package/src/components/custom/Workout/MesoStatusCard.stories.tsx +1 -1
- package/src/components/custom/Workout/MetricTiles.stories.tsx +60 -0
- package/src/components/custom/Workout/MetricTiles.test.tsx +58 -0
- package/src/components/custom/Workout/MetricTiles.tsx +48 -0
- package/src/components/custom/Workout/MuscleGroupChip.stories.tsx +1 -1
- package/src/components/custom/Workout/PlaceholderStrip.stories.tsx +1 -1
- package/src/components/custom/Workout/PrBadge.stories.tsx +1 -1
- package/src/components/custom/Workout/PrHistoryModal.stories.tsx +1 -1
- package/src/components/custom/Workout/ProgramPlanningPage.stories.tsx +4 -16
- package/src/components/custom/Workout/ProgramPlanningPage.tsx +8 -6
- package/src/components/custom/Workout/README.md +146 -4
- package/src/components/custom/Workout/ReadinessCheck.stories.tsx +1 -1
- package/src/components/custom/Workout/RestTimer.stories.tsx +1 -1
- package/src/components/custom/Workout/RestTimer.tsx +15 -10
- package/src/components/custom/Workout/RowInventory.stories.tsx +405 -0
- package/src/components/custom/Workout/S3FullRail.stories.tsx +34 -0
- package/src/components/custom/Workout/S3SessionPace.stories.tsx +95 -0
- package/src/components/custom/Workout/S3SessionRailHeading.stories.tsx +61 -0
- package/src/components/custom/Workout/S3SetTypes.stories.tsx +176 -0
- package/src/components/custom/Workout/S3WorkoutExpansion.stories.tsx +240 -0
- package/src/components/custom/Workout/ScheduleTiles.stories.tsx +40 -0
- package/src/components/custom/Workout/ScheduleTiles.test.tsx +34 -0
- package/src/components/custom/Workout/ScheduleTiles.tsx +70 -0
- package/src/components/custom/Workout/SegmentedBar.stories.tsx +99 -0
- package/src/components/custom/Workout/SegmentedBar.test.tsx +77 -0
- package/src/components/custom/Workout/SegmentedBar.tsx +172 -0
- package/src/components/custom/Workout/SegmentedProgressBar.stories.tsx +50 -0
- package/src/components/custom/Workout/SegmentedProgressBar.test.tsx +80 -0
- package/src/components/custom/Workout/SegmentedProgressBar.tsx +67 -0
- package/src/components/custom/Workout/SessionHeader.stories.tsx +121 -0
- package/src/components/custom/Workout/SessionHeader.test.tsx +122 -0
- package/src/components/custom/Workout/SessionHeader.tsx +152 -0
- package/src/components/custom/Workout/SessionRail.stories.tsx +153 -0
- package/src/components/custom/Workout/SessionRail.test.tsx +86 -0
- package/src/components/custom/Workout/SessionRail.tsx +126 -0
- package/src/components/custom/Workout/SetBar.stories.tsx +124 -0
- package/src/components/custom/Workout/SetBar.test.tsx +123 -0
- package/src/components/custom/Workout/SetBar.tsx +171 -0
- package/src/components/custom/Workout/SetRow.stories.tsx +50 -100
- package/src/components/custom/Workout/SetRow.test.tsx +94 -166
- package/src/components/custom/Workout/SetRow.tsx +169 -232
- package/src/components/custom/Workout/SetStrip.stories.tsx +154 -0
- package/src/components/custom/Workout/SetStrip.test.tsx +109 -0
- package/src/components/custom/Workout/SetStrip.tsx +71 -0
- package/src/components/custom/Workout/SetTableHeader.stories.tsx +66 -0
- package/src/components/custom/Workout/SetTableHeader.test.tsx +47 -0
- package/src/components/custom/Workout/SetTableHeader.tsx +88 -0
- package/src/components/custom/Workout/SetsRepsLoad.stories.tsx +47 -0
- package/src/components/custom/Workout/SetsRepsLoad.test.tsx +44 -0
- package/src/components/custom/Workout/SetsRepsLoad.tsx +52 -0
- package/src/components/custom/Workout/Sparkline.stories.tsx +1 -1
- package/src/components/custom/Workout/StatusDot.stories.tsx +1 -1
- package/src/components/custom/Workout/StatusPill.stories.tsx +59 -0
- package/src/components/custom/Workout/StatusPill.test.tsx +63 -0
- package/src/components/custom/Workout/StatusPill.tsx +88 -0
- package/src/components/custom/Workout/StrengthTrendChart.stories.tsx +1 -1
- package/src/components/custom/Workout/SupersetWrapper.stories.tsx +1 -1
- package/src/components/custom/Workout/TempoBar.stories.tsx +1 -1
- package/src/components/custom/Workout/TempoDisplay.stories.tsx +12 -1
- package/src/components/custom/Workout/TempoDisplay.tsx +23 -33
- package/src/components/custom/Workout/TrainingStatusPage.stories.tsx +1 -1
- package/src/components/custom/Workout/VelocityStrip.modalities.stories.tsx +254 -0
- package/src/components/custom/Workout/VelocityStrip.responsive.stories.tsx +145 -0
- package/src/components/custom/Workout/VelocityStrip.stories.tsx +129 -66
- package/src/components/custom/Workout/VelocityStrip.test.tsx +376 -141
- package/src/components/custom/Workout/VelocityStrip.tsx +508 -120
- package/src/components/custom/Workout/VolumeLandmarkBar.stories.tsx +73 -0
- package/src/components/custom/Workout/VolumeLandmarkBar.test.tsx +119 -0
- package/src/components/custom/Workout/VolumeLandmarkBar.tsx +154 -0
- package/src/components/custom/Workout/WeekRow.stories.tsx +1 -1
- package/src/components/custom/Workout/WeightBadge.stories.tsx +1 -1
- package/src/components/custom/Workout/WorkoutCard.stories.tsx +2 -6
- package/src/components/custom/Workout/WorkoutCard.test.tsx +13 -46
- package/src/components/custom/Workout/WorkoutPill.stories.tsx +1 -1
- package/src/components/custom/Workout/ZoneTrack.stories.tsx +140 -0
- package/src/components/custom/Workout/ZoneTrack.test.tsx +200 -0
- package/src/components/custom/Workout/ZoneTrack.tsx +315 -0
- package/src/components/custom/Workout/index.ts +55 -2
- package/src/components/custom/Workout/metricText.stories.tsx +69 -0
- package/src/components/custom/Workout/metricText.tsx +34 -0
- package/src/components/custom/Workout/paceTone.test.ts +29 -0
- package/src/components/custom/Workout/paceTone.ts +27 -0
- package/src/components/custom/Workout/setHeadingKit.tsx +177 -0
- package/src/components/custom/index.ts +1 -0
- package/src/components/custom/stepper/Stepper.stories.tsx +1 -1
- package/src/components/icons/icons.stories.tsx +67 -2
- package/src/components/icons/icons.test.tsx +32 -2
- package/src/components/icons/icons.tsx +108 -0
- package/src/components/shell/BrandLockup.stories.tsx +3 -3
- package/src/components/shell/DashboardShell.stories.tsx +65 -0
- package/src/components/shell/DashboardShell.test.tsx +42 -0
- package/src/components/shell/DashboardShell.tsx +74 -0
- package/src/components/shell/DeviceIndicator.stories.tsx +2 -2
- package/src/components/shell/DeviceMenu.stories.tsx +5 -5
- package/src/components/shell/DeviceRow.stories.tsx +3 -3
- package/src/components/shell/NavItem.stories.tsx +52 -0
- package/src/components/shell/NavItem.test.tsx +35 -0
- package/src/components/shell/NavItem.tsx +75 -0
- package/src/components/shell/README.md +30 -0
- package/src/components/shell/SessionRailSpecimen.stories.tsx +753 -0
- package/src/components/shell/SessionStatePill.stories.tsx +3 -3
- package/src/components/shell/SessionStatePill.tsx +1 -1
- package/src/components/shell/SideNav.stories.tsx +81 -0
- package/src/components/shell/SideNav.test.tsx +51 -0
- package/src/components/shell/SideNav.tsx +74 -0
- package/src/components/shell/TopBar.stories.tsx +5 -5
- package/src/components/shell/index.ts +5 -0
- package/src/components/ui/alert/Alert.stories.tsx +1 -1
- package/src/components/ui/autocomplete/Autocomplete.stories.tsx +1 -1
- package/src/components/ui/avatar/Avatar.stories.tsx +1 -1
- package/src/components/ui/badge/Badge.stories.tsx +1 -1
- package/src/components/ui/breadcrumbs/Breadcrumbs.stories.tsx +1 -1
- package/src/components/ui/button/Button.stories.tsx +1 -1
- package/src/components/ui/card/Card.stories.tsx +1 -1
- package/src/components/ui/checkbox/Checkbox.stories.tsx +1 -1
- package/src/components/ui/chip/Chip.stories.tsx +1 -1
- package/src/components/ui/collapse/Collapse.stories.tsx +1 -1
- package/src/components/ui/data-row/DataRow.stories.tsx +1 -1
- package/src/components/ui/divider/Divider.stories.tsx +1 -1
- package/src/components/ui/drawer/Drawer.stories.tsx +1 -1
- package/src/components/ui/form-field/FormField.stories.tsx +1 -1
- package/src/components/ui/help-tip/HelpTip.stories.tsx +1 -1
- package/src/components/ui/icon-box/IconBox.stories.tsx +1 -1
- package/src/components/ui/index.ts +1 -0
- package/src/components/ui/indicator/Indicator.stories.tsx +2 -2
- package/src/components/ui/indicator/Indicator.test.tsx +1 -0
- package/src/components/ui/indicator/Indicator.tsx +6 -1
- package/src/components/ui/input/Input.stories.tsx +1 -1
- package/src/components/ui/link/Link.stories.tsx +1 -1
- package/src/components/ui/list-item/ListItem.stories.tsx +1 -1
- package/src/components/ui/menu/Menu.stories.tsx +1 -1
- package/src/components/ui/modal/Modal.stories.tsx +1 -1
- package/src/components/ui/pill/Pill.stories.tsx +1 -1
- package/src/components/ui/popover/Popover.stories.tsx +1 -1
- package/src/components/ui/progress/Progress.stories.tsx +1 -1
- package/src/components/ui/radio/Radio.stories.tsx +1 -1
- package/src/components/ui/section/Section.stories.tsx +1 -1
- package/src/components/ui/select/Select.stories.tsx +1 -1
- package/src/components/ui/skeleton/Skeleton.stories.tsx +1 -1
- package/src/components/ui/spinner/Spinner.stories.tsx +1 -1
- package/src/components/ui/stack/Stack.stories.tsx +1 -1
- package/src/components/ui/surface/Surface.stories.tsx +1 -1
- package/src/components/ui/switch/Switch.stories.tsx +1 -1
- package/src/components/ui/tabs/Tabs.stories.tsx +1 -1
- package/src/components/ui/tile/Tile.stories.tsx +79 -0
- package/src/components/ui/tile/Tile.test.tsx +48 -0
- package/src/components/ui/tile/Tile.tsx +64 -0
- package/src/components/ui/tile/index.ts +2 -0
- package/src/components/ui/toast/Toast.stories.tsx +1 -1
- package/src/components/ui/toolbar-button/ToolbarButton.stories.tsx +1 -1
- package/src/components/ui/tooltip/Tooltip.stories.tsx +1 -1
- package/src/examples/react-hook-form/ReactHookForm.stories.tsx +1 -1
- package/src/hooks/index.ts +2 -0
- package/src/hooks/useTimer.test.ts +150 -0
- package/src/hooks/useTimer.ts +77 -0
- package/src/index.ts +3 -0
- package/src/pages.ts +1 -1
- package/src/stories/PresetShowcase.stories.tsx +1 -1
- package/src/stories/ThemePresets.stories.tsx +1 -1
- package/src/theme/ColorSystem.stories.tsx +1 -1
- package/src/theme/Colors.stories.tsx +5 -8
- package/src/theme/config.ts +4 -0
- package/src/theme/elevation.stories.tsx +1 -1
- package/src/theme/global.css +5 -1
- package/src/theme/shadows.stories.tsx +1 -1
- package/src/theme/tokens/primitives.ts +11 -107
- package/src/theme/tokens/semantic.ts +8 -0
- package/src/theme/workout-tokens.ts +11 -7
- package/tailwind.config.js +5 -0
package/dist/index.js
CHANGED
|
@@ -82,6 +82,72 @@ function DumbbellIcon(props) {
|
|
|
82
82
|
function StarIcon(props) {
|
|
83
83
|
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" }) });
|
|
84
84
|
}
|
|
85
|
+
function ActivityIcon(props) {
|
|
86
|
+
return /* @__PURE__ */ jsx(SvgIcon, { ...props, children: /* @__PURE__ */ jsx("path", { d: "M22 12h-4l-3 9L9 3l-3 9H2" }) });
|
|
87
|
+
}
|
|
88
|
+
function HistoryIcon(props) {
|
|
89
|
+
return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
|
|
90
|
+
/* @__PURE__ */ jsx("path", { d: "M3 12a9 9 0 1 0 3-6.7L3 8" }),
|
|
91
|
+
/* @__PURE__ */ jsx("path", { d: "M3 3v5h5" }),
|
|
92
|
+
/* @__PURE__ */ jsx("path", { d: "M12 7v5l4 2" })
|
|
93
|
+
] });
|
|
94
|
+
}
|
|
95
|
+
function LayersIcon(props) {
|
|
96
|
+
return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
|
|
97
|
+
/* @__PURE__ */ jsx("path", { d: "m12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83Z" }),
|
|
98
|
+
/* @__PURE__ */ jsx("path", { d: "m6.08 9.5-3.49 1.59a1 1 0 0 0 0 1.81l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9a1 1 0 0 0 0-1.83l-3.5-1.59" }),
|
|
99
|
+
/* @__PURE__ */ jsx("path", { d: "m6.08 14.5-3.49 1.59a1 1 0 0 0 0 1.81l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9a1 1 0 0 0 0-1.83l-3.5-1.59" })
|
|
100
|
+
] });
|
|
101
|
+
}
|
|
102
|
+
function PersonStandingIcon(props) {
|
|
103
|
+
return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
|
|
104
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "5", r: "1" }),
|
|
105
|
+
/* @__PURE__ */ jsx("path", { d: "m9 20 3-6 3 6" }),
|
|
106
|
+
/* @__PURE__ */ jsx("path", { d: "m6 8 6 2 6-2" }),
|
|
107
|
+
/* @__PURE__ */ jsx("path", { d: "M12 10v4" })
|
|
108
|
+
] });
|
|
109
|
+
}
|
|
110
|
+
function ScaleIcon(props) {
|
|
111
|
+
return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
|
|
112
|
+
/* @__PURE__ */ jsx("path", { d: "m16 16 3-8 3 8c-.87.65-1.92 1-3 1s-2.13-.35-3-1Z" }),
|
|
113
|
+
/* @__PURE__ */ jsx("path", { d: "m2 16 3-8 3 8c-.87.65-1.92 1-3 1s-2.13-.35-3-1Z" }),
|
|
114
|
+
/* @__PURE__ */ jsx("path", { d: "M7 21h10" }),
|
|
115
|
+
/* @__PURE__ */ jsx("path", { d: "M12 3v18" }),
|
|
116
|
+
/* @__PURE__ */ jsx("path", { d: "M3 7h2c2 0 5-1 7-2 2 1 5 2 7 2h2" })
|
|
117
|
+
] });
|
|
118
|
+
}
|
|
119
|
+
function AlertTriangleIcon(props) {
|
|
120
|
+
return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
|
|
121
|
+
/* @__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" }),
|
|
122
|
+
/* @__PURE__ */ jsx("path", { d: "M12 9v4" }),
|
|
123
|
+
/* @__PURE__ */ jsx("path", { d: "M12 17h.01" })
|
|
124
|
+
] });
|
|
125
|
+
}
|
|
126
|
+
function TrendingDownIcon(props) {
|
|
127
|
+
return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
|
|
128
|
+
/* @__PURE__ */ jsx("path", { d: "M16 17h6v-6" }),
|
|
129
|
+
/* @__PURE__ */ jsx("path", { d: "m22 17-8.5-8.5-5 5L2 7" })
|
|
130
|
+
] });
|
|
131
|
+
}
|
|
132
|
+
function CircleSlashIcon(props) {
|
|
133
|
+
return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
|
|
134
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
|
|
135
|
+
/* @__PURE__ */ jsx("path", { d: "m9 15 6-6" })
|
|
136
|
+
] });
|
|
137
|
+
}
|
|
138
|
+
function AwardIcon(props) {
|
|
139
|
+
return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
|
|
140
|
+
/* @__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" }),
|
|
141
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "8", r: "6" })
|
|
142
|
+
] });
|
|
143
|
+
}
|
|
144
|
+
function InfoIcon(props) {
|
|
145
|
+
return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
|
|
146
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
|
|
147
|
+
/* @__PURE__ */ jsx("path", { d: "M12 16v-4" }),
|
|
148
|
+
/* @__PURE__ */ jsx("path", { d: "M12 8h.01" })
|
|
149
|
+
] });
|
|
150
|
+
}
|
|
85
151
|
function cn(...inputs) {
|
|
86
152
|
return tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
87
153
|
}
|
|
@@ -180,7 +246,7 @@ function AlertDescription({ children, className }) {
|
|
|
180
246
|
|
|
181
247
|
// src/theme/tokens/primitives.ts
|
|
182
248
|
var primitiveColors = {
|
|
183
|
-
// Blue scale
|
|
249
|
+
// Blue scale
|
|
184
250
|
blue: {
|
|
185
251
|
50: "#EFF6FF",
|
|
186
252
|
100: "#DBEAFE",
|
|
@@ -189,124 +255,36 @@ var primitiveColors = {
|
|
|
189
255
|
400: "#60A5FA",
|
|
190
256
|
500: "#3B82F6",
|
|
191
257
|
600: "#5048E5",
|
|
192
|
-
// Primary main
|
|
193
258
|
700: "#3832A0",
|
|
194
|
-
// Primary dark
|
|
195
259
|
800: "#1E40AF",
|
|
196
260
|
900: "#1E3A8A"
|
|
197
261
|
},
|
|
198
|
-
//
|
|
199
|
-
green: {
|
|
200
|
-
50: "#ECFDF5",
|
|
201
|
-
100: "#D1FAE5",
|
|
202
|
-
200: "#A7F3D0",
|
|
203
|
-
300: "#6EE7B7",
|
|
204
|
-
400: "#3FC79A",
|
|
205
|
-
// Secondary light
|
|
206
|
-
500: "#10B981",
|
|
207
|
-
// Secondary main
|
|
208
|
-
600: "#059669",
|
|
209
|
-
700: "#0B815A",
|
|
210
|
-
// Secondary dark
|
|
211
|
-
800: "#065F46",
|
|
212
|
-
900: "#064E3B"
|
|
213
|
-
},
|
|
214
|
-
// Teal scale (success)
|
|
215
|
-
teal: {
|
|
216
|
-
50: "#F0FDFA",
|
|
217
|
-
100: "#CCFBF1",
|
|
218
|
-
200: "#99F6E4",
|
|
219
|
-
300: "#5EEAD4",
|
|
220
|
-
400: "#43C6B7",
|
|
221
|
-
// Success light
|
|
222
|
-
500: "#14B8A6",
|
|
223
|
-
// Success main
|
|
224
|
-
600: "#0D9488",
|
|
225
|
-
700: "#0E8074",
|
|
226
|
-
// Success dark
|
|
227
|
-
800: "#115E59",
|
|
228
|
-
900: "#134E4A"
|
|
229
|
-
},
|
|
230
|
-
// Red scale (error)
|
|
262
|
+
// Red scale
|
|
231
263
|
red: {
|
|
232
264
|
50: "#FEF2F2",
|
|
233
265
|
100: "#FEE2E2",
|
|
234
266
|
200: "#FECACA",
|
|
235
267
|
300: "#FCA5A5",
|
|
236
268
|
400: "#DA6868",
|
|
237
|
-
// Error light
|
|
238
269
|
500: "#EF4444",
|
|
239
270
|
600: "#D14343",
|
|
240
|
-
// Error main
|
|
241
271
|
700: "#922E2E",
|
|
242
|
-
// Error dark
|
|
243
272
|
800: "#991B1B",
|
|
244
273
|
900: "#7F1D1D"
|
|
245
274
|
},
|
|
246
|
-
// Vivid
|
|
247
|
-
greenVivid: {
|
|
248
|
-
50: "#E9FBF0",
|
|
249
|
-
100: "#C8F7DA",
|
|
250
|
-
200: "#9FF0BF",
|
|
251
|
-
300: "#6BE79D",
|
|
252
|
-
400: "#47DE84",
|
|
253
|
-
// Success vivid light
|
|
254
|
-
500: "#2ED573",
|
|
255
|
-
// Success vivid main
|
|
256
|
-
600: "#22B85F",
|
|
257
|
-
700: "#1A9950",
|
|
258
|
-
// Success vivid dark
|
|
259
|
-
800: "#157A40",
|
|
260
|
-
900: "#105C30"
|
|
261
|
-
},
|
|
262
|
-
// Vivid red scale (alert)
|
|
275
|
+
// Vivid red scale
|
|
263
276
|
redVivid: {
|
|
264
277
|
50: "#FFECEE",
|
|
265
278
|
100: "#FFD6DB",
|
|
266
279
|
200: "#FFB3BC",
|
|
267
280
|
300: "#FF8593",
|
|
268
281
|
400: "#FF6070",
|
|
269
|
-
// Error vivid light
|
|
270
282
|
500: "#FF4757",
|
|
271
|
-
// Error vivid main
|
|
272
283
|
600: "#E63548",
|
|
273
284
|
700: "#C42539",
|
|
274
|
-
// Error vivid dark
|
|
275
285
|
800: "#9E1C2C",
|
|
276
286
|
900: "#7A1520"
|
|
277
287
|
},
|
|
278
|
-
// Amber scale (warning)
|
|
279
|
-
amber: {
|
|
280
|
-
50: "#FFFBEB",
|
|
281
|
-
100: "#FEF3C7",
|
|
282
|
-
200: "#FDE68A",
|
|
283
|
-
300: "#FCD34D",
|
|
284
|
-
400: "#FFBF4C",
|
|
285
|
-
// Warning light
|
|
286
|
-
500: "#FFB020",
|
|
287
|
-
// Warning main
|
|
288
|
-
600: "#B27B16",
|
|
289
|
-
// Warning dark
|
|
290
|
-
700: "#B45309",
|
|
291
|
-
800: "#92400E",
|
|
292
|
-
900: "#78350F"
|
|
293
|
-
},
|
|
294
|
-
// Sky/Blue scale (info)
|
|
295
|
-
sky: {
|
|
296
|
-
50: "#F0F9FF",
|
|
297
|
-
100: "#E0F2FE",
|
|
298
|
-
200: "#BAE6FD",
|
|
299
|
-
300: "#7DD3FC",
|
|
300
|
-
400: "#64B6F7",
|
|
301
|
-
// Info light
|
|
302
|
-
500: "#2196F3",
|
|
303
|
-
// Info main
|
|
304
|
-
600: "#0284C7",
|
|
305
|
-
700: "#0B79D0",
|
|
306
|
-
// Info dark
|
|
307
|
-
800: "#075985",
|
|
308
|
-
900: "#0C4A6E"
|
|
309
|
-
},
|
|
310
288
|
// Neutral/Gray scale
|
|
311
289
|
neutral: {
|
|
312
290
|
50: "#FAFAFA",
|
|
@@ -321,18 +299,6 @@ var primitiveColors = {
|
|
|
321
299
|
900: "#111827",
|
|
322
300
|
950: "#030712"
|
|
323
301
|
},
|
|
324
|
-
// Orange scale (accent/brand color)
|
|
325
|
-
accent: {
|
|
326
|
-
100: "#FFA860",
|
|
327
|
-
200: "#FF9630",
|
|
328
|
-
300: "#FF8500",
|
|
329
|
-
400: "#FF7900",
|
|
330
|
-
500: "#F56C00",
|
|
331
|
-
600: "#E06D10",
|
|
332
|
-
700: "#D0620C",
|
|
333
|
-
800: "#B75500",
|
|
334
|
-
900: "#A34900"
|
|
335
|
-
},
|
|
336
302
|
// Charcoal scale (dark backgrounds)
|
|
337
303
|
charcoal: {
|
|
338
304
|
0: "#6E6E6E",
|
|
@@ -347,18 +313,6 @@ var primitiveColors = {
|
|
|
347
313
|
800: "#131313",
|
|
348
314
|
900: "#101010"
|
|
349
315
|
},
|
|
350
|
-
// Steel scale (cool accent)
|
|
351
|
-
steel: {
|
|
352
|
-
100: "#8AA4B8",
|
|
353
|
-
200: "#7894A8",
|
|
354
|
-
300: "#678498",
|
|
355
|
-
400: "#557488",
|
|
356
|
-
500: "#406D87",
|
|
357
|
-
600: "#39617A",
|
|
358
|
-
700: "#32556D",
|
|
359
|
-
800: "#243D53",
|
|
360
|
-
900: "#1D3146"
|
|
361
|
-
},
|
|
362
316
|
// Pure colors
|
|
363
317
|
white: "#FFFFFF",
|
|
364
318
|
black: "#000000",
|
|
@@ -749,6 +703,9 @@ var semanticColorsLight = {
|
|
|
749
703
|
"status-success-light": primitiveRamps.green[200],
|
|
750
704
|
"status-success-dark": primitiveRamps.green[600],
|
|
751
705
|
"status-success-subtle": primitiveRamps.green[50],
|
|
706
|
+
// Live-session accent — its OWN role, decoupled from success so the two can diverge
|
|
707
|
+
"status-live": primitiveRamps.green[300],
|
|
708
|
+
"status-live-muted": primitiveRamps.green[500],
|
|
752
709
|
"status-error": primitiveRamps.red[600],
|
|
753
710
|
"status-error-light": primitiveRamps.red[500],
|
|
754
711
|
"status-error-dark": primitiveRamps.red[700],
|
|
@@ -882,6 +839,9 @@ var semanticColorsDark = {
|
|
|
882
839
|
"status-success-light": primitiveRamps.green[200],
|
|
883
840
|
"status-success-dark": primitiveRamps.green[600],
|
|
884
841
|
"status-success-subtle": "rgba(46, 213, 115, 0.12)",
|
|
842
|
+
// Live-session accent — its OWN role, decoupled from success so the two can diverge
|
|
843
|
+
"status-live": primitiveRamps.green[300],
|
|
844
|
+
"status-live-muted": primitiveRamps.green[500],
|
|
885
845
|
"status-error": primitiveRamps.red[600],
|
|
886
846
|
"status-error-light": primitiveRamps.red[500],
|
|
887
847
|
"status-error-dark": primitiveRamps.red[700],
|
|
@@ -1476,6 +1436,8 @@ var lightThemeCSSVars = {
|
|
|
1476
1436
|
"--color-on-brand-secondary": semanticColorsLight["on-brand-secondary"],
|
|
1477
1437
|
"--color-status-success": semanticColorsLight["status-success"],
|
|
1478
1438
|
"--color-status-success-subtle": semanticColorsLight["status-success-subtle"],
|
|
1439
|
+
"--color-status-live": semanticColorsLight["status-live"],
|
|
1440
|
+
"--color-status-live-muted": semanticColorsLight["status-live-muted"],
|
|
1479
1441
|
"--color-status-error": semanticColorsLight["status-error"],
|
|
1480
1442
|
"--color-status-error-subtle": semanticColorsLight["status-error-subtle"],
|
|
1481
1443
|
"--color-status-warning": semanticColorsLight["status-warning"],
|
|
@@ -1572,6 +1534,8 @@ var darkThemeCSSVars = {
|
|
|
1572
1534
|
"--color-on-brand-secondary": semanticColorsDark["on-brand-secondary"],
|
|
1573
1535
|
"--color-status-success": semanticColorsDark["status-success"],
|
|
1574
1536
|
"--color-status-success-subtle": semanticColorsDark["status-success-subtle"],
|
|
1537
|
+
"--color-status-live": semanticColorsDark["status-live"],
|
|
1538
|
+
"--color-status-live-muted": semanticColorsDark["status-live-muted"],
|
|
1575
1539
|
"--color-status-error": semanticColorsDark["status-error"],
|
|
1576
1540
|
"--color-status-error-subtle": semanticColorsDark["status-error-subtle"],
|
|
1577
1541
|
"--color-status-warning": semanticColorsDark["status-warning"],
|
|
@@ -2789,6 +2753,7 @@ var colorStyles = {
|
|
|
2789
2753
|
default: "bg-text-tertiary",
|
|
2790
2754
|
primary: "bg-brand-primary",
|
|
2791
2755
|
success: "bg-status-success",
|
|
2756
|
+
live: "bg-status-live",
|
|
2792
2757
|
error: "bg-status-error",
|
|
2793
2758
|
warning: "bg-status-warning",
|
|
2794
2759
|
info: "bg-status-info",
|
|
@@ -2845,7 +2810,7 @@ function Indicator({
|
|
|
2845
2810
|
colorClass,
|
|
2846
2811
|
pulseMode === "opacity" && "animate-pulse",
|
|
2847
2812
|
ring && "border-2 border-background-base",
|
|
2848
|
-
glow && !customColor && color === "success" && "shadow-[0_0_6px_rgba(46,213,115,0.6)]",
|
|
2813
|
+
glow && !customColor && (color === "success" || color === "live") && "shadow-[0_0_6px_rgba(46,213,115,0.6)]",
|
|
2849
2814
|
glow && !customColor && color === "error" && "shadow-[0_0_6px_rgba(239,68,68,0.6)]",
|
|
2850
2815
|
glow && !customColor && color === "warning" && "shadow-[0_0_6px_rgba(245,158,11,0.6)]",
|
|
2851
2816
|
glow && !customColor && color === "primary" && "shadow-[0_0_6px_rgba(255,121,0,0.6)]",
|
|
@@ -5269,7 +5234,7 @@ function ToastProvider({
|
|
|
5269
5234
|
return id;
|
|
5270
5235
|
}, [defaultDuration, maxToasts]);
|
|
5271
5236
|
const removeToast = React24.useCallback((id) => {
|
|
5272
|
-
setToasts((prev) => prev.filter((
|
|
5237
|
+
setToasts((prev) => prev.filter((t18) => t18.id !== id));
|
|
5273
5238
|
}, []);
|
|
5274
5239
|
const removeAllToasts = React24.useCallback(() => {
|
|
5275
5240
|
setToasts([]);
|
|
@@ -6138,6 +6103,50 @@ function Pill({
|
|
|
6138
6103
|
}
|
|
6139
6104
|
return /* @__PURE__ */ jsx(reactNative.View, { className: containerClasses, ...props, children: content });
|
|
6140
6105
|
}
|
|
6106
|
+
function Tile({
|
|
6107
|
+
label,
|
|
6108
|
+
value,
|
|
6109
|
+
valueColor,
|
|
6110
|
+
align = "center",
|
|
6111
|
+
className,
|
|
6112
|
+
...props
|
|
6113
|
+
}) {
|
|
6114
|
+
const isCenter = align === "center";
|
|
6115
|
+
return /* @__PURE__ */ jsxs(
|
|
6116
|
+
reactNative.View,
|
|
6117
|
+
{
|
|
6118
|
+
className: cn(
|
|
6119
|
+
"flex-1 rounded-md bg-surface-raised px-1.5 py-2",
|
|
6120
|
+
isCenter ? "items-center" : "items-start",
|
|
6121
|
+
className
|
|
6122
|
+
),
|
|
6123
|
+
...props,
|
|
6124
|
+
children: [
|
|
6125
|
+
/* @__PURE__ */ jsx(
|
|
6126
|
+
reactNative.Text,
|
|
6127
|
+
{
|
|
6128
|
+
className: cn(
|
|
6129
|
+
"text-[10px] font-bold uppercase tracking-wider text-text-tertiary",
|
|
6130
|
+
isCenter ? "text-center" : "text-left"
|
|
6131
|
+
),
|
|
6132
|
+
children: label
|
|
6133
|
+
}
|
|
6134
|
+
),
|
|
6135
|
+
/* @__PURE__ */ jsx(
|
|
6136
|
+
reactNative.Text,
|
|
6137
|
+
{
|
|
6138
|
+
className: cn(
|
|
6139
|
+
"mt-0.5 font-mono text-sm font-bold text-text-primary",
|
|
6140
|
+
isCenter ? "text-center" : "text-left"
|
|
6141
|
+
),
|
|
6142
|
+
style: valueColor ? { color: valueColor } : void 0,
|
|
6143
|
+
children: value
|
|
6144
|
+
}
|
|
6145
|
+
)
|
|
6146
|
+
]
|
|
6147
|
+
}
|
|
6148
|
+
);
|
|
6149
|
+
}
|
|
6141
6150
|
var variantStyles4 = {
|
|
6142
6151
|
h1: "font-heading text-5xl font-bold leading-tight",
|
|
6143
6152
|
h2: "font-heading text-4xl font-bold leading-tight",
|
|
@@ -6997,6 +7006,79 @@ function formatDateTime(value, format = "datetime", isUTC = false, fallback = "-
|
|
|
6997
7006
|
}
|
|
6998
7007
|
return formatDate(value, format, isUTC);
|
|
6999
7008
|
}
|
|
7009
|
+
function formatDuration(ms) {
|
|
7010
|
+
const totalSeconds = Math.max(0, Math.ceil(ms / 1e3));
|
|
7011
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
7012
|
+
const seconds = totalSeconds % 60;
|
|
7013
|
+
return `${minutes}:${String(seconds).padStart(2, "0")}`;
|
|
7014
|
+
}
|
|
7015
|
+
function useTimer(options = {}) {
|
|
7016
|
+
const {
|
|
7017
|
+
mode = "up",
|
|
7018
|
+
durationMs,
|
|
7019
|
+
elapsedMs: controlledElapsedMs,
|
|
7020
|
+
running = false,
|
|
7021
|
+
autoTick = false,
|
|
7022
|
+
tickMs = 1e3
|
|
7023
|
+
} = options;
|
|
7024
|
+
const isControlled = controlledElapsedMs != null;
|
|
7025
|
+
const [internalElapsedMs, setInternalElapsedMs] = React24.useState(0);
|
|
7026
|
+
React24.useEffect(() => {
|
|
7027
|
+
if (isControlled || !autoTick || !running) return;
|
|
7028
|
+
const id = setInterval(() => {
|
|
7029
|
+
setInternalElapsedMs((prev) => prev + tickMs);
|
|
7030
|
+
}, tickMs);
|
|
7031
|
+
return () => clearInterval(id);
|
|
7032
|
+
}, [isControlled, autoTick, running, tickMs]);
|
|
7033
|
+
const elapsedMs = isControlled ? controlledElapsedMs : internalElapsedMs;
|
|
7034
|
+
const hasDuration = durationMs != null && durationMs > 0;
|
|
7035
|
+
const remainingMs = durationMs != null ? Math.max(0, durationMs - elapsedMs) : 0;
|
|
7036
|
+
let progress;
|
|
7037
|
+
if (hasDuration) {
|
|
7038
|
+
progress = Math.min(1, Math.max(0, elapsedMs / durationMs));
|
|
7039
|
+
} else {
|
|
7040
|
+
progress = mode === "down" ? 1 : 0;
|
|
7041
|
+
}
|
|
7042
|
+
const label = formatDuration(mode === "down" ? remainingMs : elapsedMs);
|
|
7043
|
+
const done = durationMs != null && elapsedMs >= durationMs;
|
|
7044
|
+
return { elapsedMs, remainingMs, progress, label, done };
|
|
7045
|
+
}
|
|
7046
|
+
|
|
7047
|
+
// src/components/custom/TimerReadout/TimerReadout.tsx
|
|
7048
|
+
var semantic = getSemanticColors("dark");
|
|
7049
|
+
var LIVE_GREEN = primitiveRamps.green[500];
|
|
7050
|
+
var SECONDARY = semantic["text-secondary"];
|
|
7051
|
+
var TERTIARY = semantic["text-tertiary"];
|
|
7052
|
+
var DEFAULT_GLYPH = "\u23F1";
|
|
7053
|
+
function TimerReadout({
|
|
7054
|
+
mode = "up",
|
|
7055
|
+
elapsedMs,
|
|
7056
|
+
durationMs,
|
|
7057
|
+
running = false,
|
|
7058
|
+
autoTick = false,
|
|
7059
|
+
showTotal = false,
|
|
7060
|
+
glyph = DEFAULT_GLYPH,
|
|
7061
|
+
className,
|
|
7062
|
+
...rest
|
|
7063
|
+
}) {
|
|
7064
|
+
const { label } = useTimer({ mode, durationMs, elapsedMs, running, autoTick });
|
|
7065
|
+
const currentColor = running ? LIVE_GREEN : SECONDARY;
|
|
7066
|
+
const total = showTotal && durationMs != null ? formatDuration(durationMs) : null;
|
|
7067
|
+
return /* @__PURE__ */ jsx(
|
|
7068
|
+
reactNative.View,
|
|
7069
|
+
{
|
|
7070
|
+
accessibilityRole: "timer",
|
|
7071
|
+
accessibilityLabel: total != null ? `${label} of ${total}` : label,
|
|
7072
|
+
className: cn("flex-row items-baseline justify-end", className),
|
|
7073
|
+
...rest,
|
|
7074
|
+
children: /* @__PURE__ */ jsxs(Typography, { variant: "mono", style: { fontSize: 11, textAlign: "right" }, children: [
|
|
7075
|
+
/* @__PURE__ */ jsx(reactNative.Text, { testID: "timer-readout-glyph", style: { color: currentColor, marginRight: 3 }, children: glyph }),
|
|
7076
|
+
/* @__PURE__ */ jsx(reactNative.Text, { testID: "timer-readout-current", style: { color: currentColor }, children: label }),
|
|
7077
|
+
total != null ? /* @__PURE__ */ jsx(reactNative.Text, { testID: "timer-readout-total", style: { color: TERTIARY }, children: ` / ${total}` }) : null
|
|
7078
|
+
] })
|
|
7079
|
+
}
|
|
7080
|
+
);
|
|
7081
|
+
}
|
|
7000
7082
|
var sizeConfig2 = {
|
|
7001
7083
|
sm: { value: "text-lg font-bold", label: "text-xs", unit: "text-xs" },
|
|
7002
7084
|
md: { value: "text-2xl font-bold", label: "text-xs", unit: "text-sm" },
|
|
@@ -7426,15 +7508,22 @@ var WORKOUT_TOKENS = {
|
|
|
7426
7508
|
// Canonical 4-band performance scale — the single source for BOTH the
|
|
7427
7509
|
// VelocityStrip zone bars and the SetRow RPE color (TD-03.43). The direction
|
|
7428
7510
|
// is intentionally inverted between the two consumers: for velocity, green =
|
|
7429
|
-
// fastest/best; for RPE, green = easiest. This is the [0,
|
|
7430
|
-
// the canonical `sequentialEffort` primitive
|
|
7431
|
-
//
|
|
7511
|
+
// fastest/best; for RPE, green = easiest. This is the [0,2,3,4] subsample of
|
|
7512
|
+
// the canonical `sequentialEffort` primitive: an EVEN hue walk across the ramp
|
|
7513
|
+
// (green → gold → orange → red). The earlier [0,1,2,4] spent two of its four
|
|
7514
|
+
// bands on adjacent yellows (amber-200 + amber-300) and skipped orange, reading
|
|
7515
|
+
// yellow-heavy with an abrupt jump to red; sampling amber-300 + orange-400
|
|
7516
|
+
// instead gives four cleanly separable bands that hold at 3px strip scale.
|
|
7517
|
+
// Consumed inline (RN).
|
|
7432
7518
|
scale: {
|
|
7433
7519
|
green: sequentialEffort[0],
|
|
7434
|
-
|
|
7435
|
-
|
|
7436
|
-
// gold
|
|
7520
|
+
// green-300
|
|
7521
|
+
yellow: sequentialEffort[2],
|
|
7522
|
+
// amber-300 (gold)
|
|
7523
|
+
orange: sequentialEffort[3],
|
|
7524
|
+
// orange-400 (true orange)
|
|
7437
7525
|
red: sequentialEffort[4]
|
|
7526
|
+
// red-600
|
|
7438
7527
|
},
|
|
7439
7528
|
// BodyMap volume heatmap — the canonical `divergingScale` (under → optimal →
|
|
7440
7529
|
// over): a true diverging shape with a light green center, cool-blue under-
|
|
@@ -7559,8 +7648,8 @@ function roundTempo(tempo) {
|
|
|
7559
7648
|
return tempo.map((v) => Math.round(v * 10) / 10);
|
|
7560
7649
|
}
|
|
7561
7650
|
function formatSignedPct(ratio) {
|
|
7562
|
-
const
|
|
7563
|
-
return `${
|
|
7651
|
+
const pct2 = Math.round(ratio * 100);
|
|
7652
|
+
return `${pct2 > 0 ? "+" : ""}${pct2}%`;
|
|
7564
7653
|
}
|
|
7565
7654
|
function formatPrescription(p) {
|
|
7566
7655
|
if (p == null) return null;
|
|
@@ -7706,7 +7795,7 @@ function ActiveSegment({
|
|
|
7706
7795
|
const pacing = getTempoPacingState(phaseElapsedMs, targetMs);
|
|
7707
7796
|
const barColor = pacing === "behind" ? t2["status-error"] : config.color;
|
|
7708
7797
|
const fillPct = getTempoFillPct(phaseElapsedMs, targetMs);
|
|
7709
|
-
const label = targetMs ? `${
|
|
7798
|
+
const label = targetMs ? `${formatDuration2(phaseElapsedMs)} / ${formatDuration2(targetMs)}` : formatDuration2(phaseElapsedMs);
|
|
7710
7799
|
return /* @__PURE__ */ jsxs(reactNative.View, { style: { height: "100%", position: "relative" }, testID: `tempo-segment-active-${config.label}`, children: [
|
|
7711
7800
|
/* @__PURE__ */ jsx(
|
|
7712
7801
|
reactNative.View,
|
|
@@ -7756,16 +7845,32 @@ function CompletedSegment({
|
|
|
7756
7845
|
},
|
|
7757
7846
|
testID: `tempo-segment-completed-${config.label}`,
|
|
7758
7847
|
children: /* @__PURE__ */ jsxs(reactNative.Text, { style: { fontSize: 12, fontWeight: "500", color: alpha(config.color, 0.7) }, children: [
|
|
7759
|
-
|
|
7848
|
+
formatDuration2(completedMs),
|
|
7760
7849
|
indicator
|
|
7761
7850
|
] })
|
|
7762
7851
|
}
|
|
7763
7852
|
);
|
|
7764
7853
|
}
|
|
7765
|
-
function
|
|
7854
|
+
function formatDuration2(ms) {
|
|
7766
7855
|
const s = ms / 1e3;
|
|
7767
7856
|
return s < 10 ? s.toFixed(1) : Math.round(s).toString();
|
|
7768
7857
|
}
|
|
7858
|
+
var METRIC_FONT = "Inter, sans-serif";
|
|
7859
|
+
function MetricCell({ children, color, fontSize = 11 }) {
|
|
7860
|
+
return /* @__PURE__ */ jsx(
|
|
7861
|
+
reactNative.Text,
|
|
7862
|
+
{
|
|
7863
|
+
style: {
|
|
7864
|
+
fontFamily: METRIC_FONT,
|
|
7865
|
+
fontSize,
|
|
7866
|
+
color,
|
|
7867
|
+
fontWeight: "600",
|
|
7868
|
+
letterSpacing: 1
|
|
7869
|
+
},
|
|
7870
|
+
children
|
|
7871
|
+
}
|
|
7872
|
+
);
|
|
7873
|
+
}
|
|
7769
7874
|
|
|
7770
7875
|
// src/components/custom/Workout/TempoDisplay.tsx
|
|
7771
7876
|
var t3 = getSemanticColors("dark");
|
|
@@ -7784,34 +7889,10 @@ function TempoValue({
|
|
|
7784
7889
|
color,
|
|
7785
7890
|
fontSize
|
|
7786
7891
|
}) {
|
|
7787
|
-
return /* @__PURE__ */ jsx(
|
|
7788
|
-
reactNative.Text,
|
|
7789
|
-
{
|
|
7790
|
-
style: {
|
|
7791
|
-
fontFamily: INTER,
|
|
7792
|
-
fontSize,
|
|
7793
|
-
color,
|
|
7794
|
-
fontWeight: "600",
|
|
7795
|
-
letterSpacing: 1
|
|
7796
|
-
},
|
|
7797
|
-
children: value
|
|
7798
|
-
}
|
|
7799
|
-
);
|
|
7892
|
+
return /* @__PURE__ */ jsx(MetricCell, { color, fontSize, children: value });
|
|
7800
7893
|
}
|
|
7801
7894
|
function TempoSeparator({ color, fontSize }) {
|
|
7802
|
-
return /* @__PURE__ */ jsx(
|
|
7803
|
-
reactNative.Text,
|
|
7804
|
-
{
|
|
7805
|
-
style: {
|
|
7806
|
-
fontFamily: INTER,
|
|
7807
|
-
fontSize,
|
|
7808
|
-
color,
|
|
7809
|
-
fontWeight: "600",
|
|
7810
|
-
letterSpacing: 1
|
|
7811
|
-
},
|
|
7812
|
-
children: "-"
|
|
7813
|
-
}
|
|
7814
|
-
);
|
|
7895
|
+
return /* @__PURE__ */ jsx(MetricCell, { color, fontSize, children: "-" });
|
|
7815
7896
|
}
|
|
7816
7897
|
var LIVE_PHASES = [
|
|
7817
7898
|
{ key: "eccentric", color: phaseColors.eccentric },
|
|
@@ -7873,6 +7954,7 @@ function TempoDisplay({
|
|
|
7873
7954
|
tempo,
|
|
7874
7955
|
size = "md",
|
|
7875
7956
|
colored = true,
|
|
7957
|
+
showLabel = true,
|
|
7876
7958
|
showInfo = true,
|
|
7877
7959
|
live,
|
|
7878
7960
|
onPress,
|
|
@@ -7906,7 +7988,7 @@ function TempoDisplay({
|
|
|
7906
7988
|
},
|
|
7907
7989
|
...props,
|
|
7908
7990
|
children: [
|
|
7909
|
-
/* @__PURE__ */ jsx(
|
|
7991
|
+
showLabel && /* @__PURE__ */ jsx(
|
|
7910
7992
|
reactNative.Text,
|
|
7911
7993
|
{
|
|
7912
7994
|
style: {
|
|
@@ -8134,12 +8216,12 @@ var ZONE = {
|
|
|
8134
8216
|
// severe over-target
|
|
8135
8217
|
};
|
|
8136
8218
|
var BULGE_SIZE = { 1: 8, 2: 10, 3: 11 };
|
|
8137
|
-
function zoneForPct(
|
|
8138
|
-
if (
|
|
8139
|
-
if (
|
|
8140
|
-
if (
|
|
8141
|
-
if (
|
|
8142
|
-
if (
|
|
8219
|
+
function zoneForPct(pct2) {
|
|
8220
|
+
if (pct2 >= 125) return { color: ZONE.over3, over: 3 };
|
|
8221
|
+
if (pct2 >= 115) return { color: ZONE.over2, over: 2 };
|
|
8222
|
+
if (pct2 > 100) return { color: ZONE.over1, over: 1 };
|
|
8223
|
+
if (pct2 === 100) return { color: ZONE.target, over: 0 };
|
|
8224
|
+
if (pct2 >= 75) return { color: ZONE.approaching, over: 0 };
|
|
8143
8225
|
return { color: ZONE.building, over: 0 };
|
|
8144
8226
|
}
|
|
8145
8227
|
function clampFill(value) {
|
|
@@ -8147,9 +8229,9 @@ function clampFill(value) {
|
|
|
8147
8229
|
if (value > 1) return 1;
|
|
8148
8230
|
return value;
|
|
8149
8231
|
}
|
|
8150
|
-
function isAtTarget(
|
|
8232
|
+
function isAtTarget(pct2, threshold) {
|
|
8151
8233
|
if (threshold == null) return false;
|
|
8152
|
-
return Math.abs(
|
|
8234
|
+
return Math.abs(pct2 - Math.round(threshold * 100)) <= 5;
|
|
8153
8235
|
}
|
|
8154
8236
|
function IntensityBar({
|
|
8155
8237
|
level,
|
|
@@ -8163,9 +8245,9 @@ function IntensityBar({
|
|
|
8163
8245
|
}) {
|
|
8164
8246
|
const isVertical = orientation === "vertical";
|
|
8165
8247
|
const fillLevel = clampFill(level);
|
|
8166
|
-
const
|
|
8167
|
-
const zone = zoneForPct(
|
|
8168
|
-
const atTarget = isAtTarget(
|
|
8248
|
+
const pct2 = Math.round(Math.max(0, level) * 100);
|
|
8249
|
+
const zone = zoneForPct(pct2);
|
|
8250
|
+
const atTarget = isAtTarget(pct2, threshold);
|
|
8169
8251
|
const [fillAnim] = React24.useState(() => new reactNative.Animated.Value(fillLevel));
|
|
8170
8252
|
React24.useEffect(() => {
|
|
8171
8253
|
const animation = reactNative.Animated.timing(fillAnim, {
|
|
@@ -8216,8 +8298,8 @@ function IntensityBar({
|
|
|
8216
8298
|
className,
|
|
8217
8299
|
style: { alignItems: "center" },
|
|
8218
8300
|
accessibilityRole: "progressbar",
|
|
8219
|
-
accessibilityValue: { min: 0, max: 100, now:
|
|
8220
|
-
accessibilityLabel: `Intensity level: ${
|
|
8301
|
+
accessibilityValue: { min: 0, max: 100, now: pct2 },
|
|
8302
|
+
accessibilityLabel: `Intensity level: ${pct2}%`,
|
|
8221
8303
|
testID: "intensity-bar",
|
|
8222
8304
|
...props,
|
|
8223
8305
|
children: [
|
|
@@ -8263,7 +8345,7 @@ function IntensityBar({
|
|
|
8263
8345
|
accessibilityElementsHidden: true,
|
|
8264
8346
|
testID: "intensity-label",
|
|
8265
8347
|
children: [
|
|
8266
|
-
|
|
8348
|
+
pct2,
|
|
8267
8349
|
"%"
|
|
8268
8350
|
]
|
|
8269
8351
|
}
|
|
@@ -8448,13 +8530,257 @@ function WorkoutPill({
|
|
|
8448
8530
|
}
|
|
8449
8531
|
return pill;
|
|
8450
8532
|
}
|
|
8533
|
+
var PULSE_HALF_MS = 950;
|
|
8534
|
+
var PULSE_MIN_OPACITY = 0.45;
|
|
8535
|
+
var SEGMENTED_BAR_GAP = 5;
|
|
8536
|
+
function usePulse2(active) {
|
|
8537
|
+
const [value] = React24.useState(() => new reactNative.Animated.Value(0));
|
|
8538
|
+
React24.useEffect(() => {
|
|
8539
|
+
if (!active) return;
|
|
8540
|
+
const loop = reactNative.Animated.loop(
|
|
8541
|
+
reactNative.Animated.sequence([
|
|
8542
|
+
reactNative.Animated.timing(value, {
|
|
8543
|
+
toValue: 1,
|
|
8544
|
+
duration: PULSE_HALF_MS,
|
|
8545
|
+
easing: reactNative.Easing.inOut(reactNative.Easing.ease),
|
|
8546
|
+
useNativeDriver: false
|
|
8547
|
+
}),
|
|
8548
|
+
reactNative.Animated.timing(value, {
|
|
8549
|
+
toValue: 0,
|
|
8550
|
+
duration: PULSE_HALF_MS,
|
|
8551
|
+
easing: reactNative.Easing.inOut(reactNative.Easing.ease),
|
|
8552
|
+
useNativeDriver: false
|
|
8553
|
+
})
|
|
8554
|
+
])
|
|
8555
|
+
);
|
|
8556
|
+
loop.start();
|
|
8557
|
+
return () => loop.stop();
|
|
8558
|
+
}, [active, value]);
|
|
8559
|
+
return value;
|
|
8560
|
+
}
|
|
8561
|
+
function SegmentedBar({
|
|
8562
|
+
segments,
|
|
8563
|
+
height = 8,
|
|
8564
|
+
gap = SEGMENTED_BAR_GAP,
|
|
8565
|
+
radius = 2,
|
|
8566
|
+
marker = null,
|
|
8567
|
+
segmentTestID,
|
|
8568
|
+
style,
|
|
8569
|
+
...props
|
|
8570
|
+
}) {
|
|
8571
|
+
const pulse = usePulse2(segments.some((s) => s.pulse));
|
|
8572
|
+
return /* @__PURE__ */ jsxs(reactNative.View, { style: [{ flexDirection: "row", height, gap, position: "relative" }, style], ...props, children: [
|
|
8573
|
+
segments.map((seg, i) => {
|
|
8574
|
+
const fillStyle = {
|
|
8575
|
+
width: `${(seg.fill ?? 1) * 100}%`,
|
|
8576
|
+
height: "100%",
|
|
8577
|
+
backgroundColor: seg.color,
|
|
8578
|
+
opacity: seg.opacity
|
|
8579
|
+
};
|
|
8580
|
+
const testID = segmentTestID?.(seg, i) ?? "segmented-bar-segment";
|
|
8581
|
+
return /* @__PURE__ */ jsx(
|
|
8582
|
+
reactNative.View,
|
|
8583
|
+
{
|
|
8584
|
+
style: {
|
|
8585
|
+
flex: seg.weight ?? 1,
|
|
8586
|
+
minWidth: 0,
|
|
8587
|
+
height: "100%",
|
|
8588
|
+
marginLeft: seg.leadingGap,
|
|
8589
|
+
borderRadius: radius,
|
|
8590
|
+
overflow: "hidden"
|
|
8591
|
+
},
|
|
8592
|
+
children: seg.pulse ? /* @__PURE__ */ jsx(
|
|
8593
|
+
reactNative.Animated.View,
|
|
8594
|
+
{
|
|
8595
|
+
style: {
|
|
8596
|
+
...fillStyle,
|
|
8597
|
+
...seg.pulseColor ? {
|
|
8598
|
+
backgroundColor: pulse.interpolate({
|
|
8599
|
+
inputRange: [0, 1],
|
|
8600
|
+
outputRange: seg.pulseColor
|
|
8601
|
+
})
|
|
8602
|
+
} : {
|
|
8603
|
+
opacity: pulse.interpolate({
|
|
8604
|
+
inputRange: [0, 1],
|
|
8605
|
+
outputRange: [PULSE_MIN_OPACITY, 1]
|
|
8606
|
+
})
|
|
8607
|
+
}
|
|
8608
|
+
},
|
|
8609
|
+
accessibilityElementsHidden: true,
|
|
8610
|
+
testID
|
|
8611
|
+
}
|
|
8612
|
+
) : /* @__PURE__ */ jsx(reactNative.View, { style: fillStyle, accessibilityElementsHidden: true, testID })
|
|
8613
|
+
},
|
|
8614
|
+
i
|
|
8615
|
+
);
|
|
8616
|
+
}),
|
|
8617
|
+
marker ? /* @__PURE__ */ jsx(
|
|
8618
|
+
reactNative.View,
|
|
8619
|
+
{
|
|
8620
|
+
style: {
|
|
8621
|
+
position: "absolute",
|
|
8622
|
+
left: `${marker.position * 100}%`,
|
|
8623
|
+
top: 0,
|
|
8624
|
+
bottom: 0,
|
|
8625
|
+
width: 2,
|
|
8626
|
+
backgroundColor: marker.color
|
|
8627
|
+
},
|
|
8628
|
+
accessibilityElementsHidden: true,
|
|
8629
|
+
testID: "segmented-bar-marker"
|
|
8630
|
+
}
|
|
8631
|
+
) : null
|
|
8632
|
+
] });
|
|
8633
|
+
}
|
|
8634
|
+
|
|
8635
|
+
// src/components/custom/Workout/SetBar.tsx
|
|
8636
|
+
var SET_STRIP_ZONES = {
|
|
8637
|
+
slow: primitiveRamps.red[600],
|
|
8638
|
+
moderate: primitiveRamps.orange[400],
|
|
8639
|
+
fast: primitiveRamps.amber[300],
|
|
8640
|
+
fastest: primitiveRamps.green[300]
|
|
8641
|
+
};
|
|
8642
|
+
var TODO_COLOR = primitiveColors.charcoal[300];
|
|
8643
|
+
var SET_STRIP_VARIABLE_COLOR = primitiveRamps.cyan[900];
|
|
8644
|
+
var NOTCH_GAP = 2;
|
|
8645
|
+
var CLUSTER_GAP = 3;
|
|
8646
|
+
var MYO_UPCOMING_CLUSTERS = 3;
|
|
8647
|
+
var MYO_UPCOMING_CLUSTER_LEN = 5;
|
|
8648
|
+
var MYO_UPCOMING_FADE = 0.6;
|
|
8649
|
+
var PULSE_RANGE = {
|
|
8650
|
+
[SET_STRIP_ZONES.slow]: [primitiveRamps.red[500], primitiveRamps.red[700]],
|
|
8651
|
+
[SET_STRIP_ZONES.moderate]: [primitiveRamps.orange[300], primitiveRamps.orange[500]],
|
|
8652
|
+
[SET_STRIP_ZONES.fast]: [primitiveRamps.amber[200], primitiveRamps.amber[400]],
|
|
8653
|
+
[SET_STRIP_ZONES.fastest]: [primitiveRamps.green[200], primitiveRamps.green[400]]
|
|
8654
|
+
};
|
|
8655
|
+
function velocityZoneColor(v) {
|
|
8656
|
+
if (v < 0.5) return SET_STRIP_ZONES.slow;
|
|
8657
|
+
if (v < 0.75) return SET_STRIP_ZONES.moderate;
|
|
8658
|
+
if (v < 1) return SET_STRIP_ZONES.fast;
|
|
8659
|
+
return SET_STRIP_ZONES.fastest;
|
|
8660
|
+
}
|
|
8661
|
+
function chunkedSegments(chunks, gap) {
|
|
8662
|
+
return chunks.flatMap(
|
|
8663
|
+
(vels, ci) => vels.map((v, ri) => ({
|
|
8664
|
+
color: velocityZoneColor(v),
|
|
8665
|
+
...ci > 0 && ri === 0 ? { leadingGap: gap } : {}
|
|
8666
|
+
}))
|
|
8667
|
+
);
|
|
8668
|
+
}
|
|
8669
|
+
function rangeSegments(floor, max, doneVels) {
|
|
8670
|
+
return Array.from({ length: max }, (_, i) => {
|
|
8671
|
+
if (i < doneVels.length) return { color: velocityZoneColor(doneVels[i]) };
|
|
8672
|
+
return { color: i < floor ? TODO_COLOR : SET_STRIP_VARIABLE_COLOR };
|
|
8673
|
+
});
|
|
8674
|
+
}
|
|
8675
|
+
function myoUpcomingSegments(activationLen, clusterCount) {
|
|
8676
|
+
const activation = Array.from({ length: activationLen }, () => ({ color: TODO_COLOR }));
|
|
8677
|
+
const trail = Array.from(
|
|
8678
|
+
{ length: clusterCount },
|
|
8679
|
+
(_, ci) => Array.from({ length: MYO_UPCOMING_CLUSTER_LEN }, (_2, ri) => ({
|
|
8680
|
+
color: SET_STRIP_VARIABLE_COLOR,
|
|
8681
|
+
opacity: MYO_UPCOMING_FADE ** ci,
|
|
8682
|
+
...ri === 0 ? { leadingGap: CLUSTER_GAP } : {}
|
|
8683
|
+
}))
|
|
8684
|
+
).flat();
|
|
8685
|
+
return [...activation, ...trail];
|
|
8686
|
+
}
|
|
8687
|
+
function setSegments(set) {
|
|
8688
|
+
if (set.status === "todo") {
|
|
8689
|
+
return [{ color: TODO_COLOR }];
|
|
8690
|
+
}
|
|
8691
|
+
if (set.status === "done") {
|
|
8692
|
+
return set.velocities.map((v) => ({ color: velocityZoneColor(v) }));
|
|
8693
|
+
}
|
|
8694
|
+
if (set.status === "range") {
|
|
8695
|
+
return rangeSegments(set.floor, set.max, set.doneVels);
|
|
8696
|
+
}
|
|
8697
|
+
if (set.status === "drop") {
|
|
8698
|
+
return chunkedSegments(set.subloads, NOTCH_GAP);
|
|
8699
|
+
}
|
|
8700
|
+
if (set.status === "myo") {
|
|
8701
|
+
return chunkedSegments([set.activation, ...set.clusters], CLUSTER_GAP);
|
|
8702
|
+
}
|
|
8703
|
+
if (set.status === "myo-upcoming") {
|
|
8704
|
+
return myoUpcomingSegments(set.activationLen, set.clusterCount ?? MYO_UPCOMING_CLUSTERS);
|
|
8705
|
+
}
|
|
8706
|
+
const performed = set.velocities.map((v) => {
|
|
8707
|
+
const color = velocityZoneColor(v);
|
|
8708
|
+
return { color, pulse: true, pulseColor: PULSE_RANGE[color] };
|
|
8709
|
+
});
|
|
8710
|
+
const remaining = Math.max(0, set.planned - set.velocities.length);
|
|
8711
|
+
return [...performed, ...Array.from({ length: remaining }, () => ({ color: TODO_COLOR }))];
|
|
8712
|
+
}
|
|
8713
|
+
function setBarSegmentTestID(seg) {
|
|
8714
|
+
if (seg.pulse) return "set-strip-pulse";
|
|
8715
|
+
if (seg.color === SET_STRIP_VARIABLE_COLOR) return "set-strip-variable";
|
|
8716
|
+
return seg.color === TODO_COLOR ? "set-strip-empty" : "set-strip-fill";
|
|
8717
|
+
}
|
|
8718
|
+
function SetBar({ set, height = 8 }) {
|
|
8719
|
+
return /* @__PURE__ */ jsx(
|
|
8720
|
+
SegmentedBar,
|
|
8721
|
+
{
|
|
8722
|
+
segments: setSegments(set),
|
|
8723
|
+
height,
|
|
8724
|
+
gap: 0,
|
|
8725
|
+
radius: 0,
|
|
8726
|
+
segmentTestID: setBarSegmentTestID,
|
|
8727
|
+
style: { flex: 1, minWidth: 0, borderRadius: 2, overflow: "hidden" },
|
|
8728
|
+
testID: "set-strip-set"
|
|
8729
|
+
}
|
|
8730
|
+
);
|
|
8731
|
+
}
|
|
8732
|
+
|
|
8733
|
+
// src/components/custom/Workout/SetStrip.tsx
|
|
8734
|
+
var SET_STRIP_GAP = 5;
|
|
8735
|
+
function setCategory(set) {
|
|
8736
|
+
switch (set.status) {
|
|
8737
|
+
case "done":
|
|
8738
|
+
case "drop":
|
|
8739
|
+
case "myo":
|
|
8740
|
+
return "done";
|
|
8741
|
+
case "active":
|
|
8742
|
+
return "active";
|
|
8743
|
+
case "range":
|
|
8744
|
+
return set.doneVels.length > 0 ? "active" : "upcoming";
|
|
8745
|
+
case "todo":
|
|
8746
|
+
case "myo-upcoming":
|
|
8747
|
+
return "upcoming";
|
|
8748
|
+
}
|
|
8749
|
+
}
|
|
8750
|
+
function describeSets(sets) {
|
|
8751
|
+
const done = sets.filter((s) => setCategory(s) === "done").length;
|
|
8752
|
+
const active = sets.filter((s) => setCategory(s) === "active").length;
|
|
8753
|
+
const upcoming = sets.filter((s) => setCategory(s) === "upcoming").length;
|
|
8754
|
+
return `Set progress: ${done} done, ${active} in progress, ${upcoming} upcoming`;
|
|
8755
|
+
}
|
|
8756
|
+
function SetStrip({ sets, height = 8, className, ...props }) {
|
|
8757
|
+
return /* @__PURE__ */ jsx(
|
|
8758
|
+
reactNative.View,
|
|
8759
|
+
{
|
|
8760
|
+
className,
|
|
8761
|
+
style: { flexDirection: "row", width: "100%", height, gap: SET_STRIP_GAP },
|
|
8762
|
+
accessibilityRole: "image",
|
|
8763
|
+
accessibilityLabel: describeSets(sets),
|
|
8764
|
+
testID: "set-strip",
|
|
8765
|
+
...props,
|
|
8766
|
+
children: sets.map((set, i) => /* @__PURE__ */ jsx(SetBar, { set, height }, i))
|
|
8767
|
+
}
|
|
8768
|
+
);
|
|
8769
|
+
}
|
|
8770
|
+
|
|
8771
|
+
// src/components/custom/Workout/VelocityStrip.tsx
|
|
8451
8772
|
var VEL_COLORS = WORKOUT_TOKENS.scale;
|
|
8452
|
-
var
|
|
8453
|
-
speed:
|
|
8454
|
-
|
|
8455
|
-
|
|
8456
|
-
|
|
8457
|
-
|
|
8773
|
+
var bandIdToEffortColor = {
|
|
8774
|
+
speed: sequentialEffort[0],
|
|
8775
|
+
// green-300
|
|
8776
|
+
power: sequentialEffort[2],
|
|
8777
|
+
// amber-300 (gold)
|
|
8778
|
+
strengthSpeed: sequentialEffort[3],
|
|
8779
|
+
// orange-400
|
|
8780
|
+
maximalStrength: sequentialEffort[4],
|
|
8781
|
+
// red-600
|
|
8782
|
+
grinding: sequentialEffort[5]
|
|
8783
|
+
// red-700 — the distinct 5th band
|
|
8458
8784
|
};
|
|
8459
8785
|
function getVelocityZoneColor(velocity) {
|
|
8460
8786
|
if (velocity >= 1) return "vel-green";
|
|
@@ -8493,14 +8819,132 @@ function classifyBand(velocity, bands) {
|
|
|
8493
8819
|
return bands[bands.length - 1];
|
|
8494
8820
|
}
|
|
8495
8821
|
function bandColor(band) {
|
|
8496
|
-
|
|
8497
|
-
return token ? VEL_COLORS[token] : VEL_COLORS.green;
|
|
8822
|
+
return bandIdToEffortColor[band.id] ?? VEL_COLORS.green;
|
|
8498
8823
|
}
|
|
8499
8824
|
function getLossStyle(loss) {
|
|
8500
8825
|
if (loss > 25) return { color: VEL_COLORS.red };
|
|
8501
8826
|
if (loss > 20) return { color: VEL_COLORS.orange };
|
|
8502
8827
|
return null;
|
|
8503
8828
|
}
|
|
8829
|
+
var REP_GAP = 2;
|
|
8830
|
+
var WIDE_GAP = 8;
|
|
8831
|
+
var TODO_COLOR2 = primitiveColors.charcoal[300];
|
|
8832
|
+
var CONTINUE_OUTLINE = primitiveRamps.cyan[800];
|
|
8833
|
+
function deriveDoneVelocities(set) {
|
|
8834
|
+
switch (set.type) {
|
|
8835
|
+
case "drop":
|
|
8836
|
+
return set.subloads.flat();
|
|
8837
|
+
case "myo":
|
|
8838
|
+
return [...set.activation, ...set.clusters.flat()];
|
|
8839
|
+
default:
|
|
8840
|
+
return set.velocities;
|
|
8841
|
+
}
|
|
8842
|
+
}
|
|
8843
|
+
function chunkedRepSlots(chunks) {
|
|
8844
|
+
const slots = [];
|
|
8845
|
+
chunks.forEach((chunk) => {
|
|
8846
|
+
chunk.forEach((velocity, ri) => {
|
|
8847
|
+
const first = slots.length === 0;
|
|
8848
|
+
slots.push({ kind: "rep", velocity, leadingGap: first ? 0 : ri === 0 ? WIDE_GAP : REP_GAP });
|
|
8849
|
+
});
|
|
8850
|
+
});
|
|
8851
|
+
return slots;
|
|
8852
|
+
}
|
|
8853
|
+
function buildSlots(set) {
|
|
8854
|
+
switch (set.type) {
|
|
8855
|
+
case "straight": {
|
|
8856
|
+
const total = Math.max(set.velocities.length, set.planned ?? set.velocities.length);
|
|
8857
|
+
return Array.from({ length: total }, (_, i) => {
|
|
8858
|
+
const done = i < set.velocities.length;
|
|
8859
|
+
return {
|
|
8860
|
+
kind: done ? "rep" : "todo",
|
|
8861
|
+
velocity: done ? set.velocities[i] : void 0,
|
|
8862
|
+
leadingGap: i === 0 ? 0 : REP_GAP
|
|
8863
|
+
};
|
|
8864
|
+
});
|
|
8865
|
+
}
|
|
8866
|
+
case "range": {
|
|
8867
|
+
const total = Math.max(set.max, set.velocities.length);
|
|
8868
|
+
return Array.from({ length: total }, (_, i) => {
|
|
8869
|
+
const kind = i < set.velocities.length ? "rep" : i < set.floor ? "todo" : "variable";
|
|
8870
|
+
return {
|
|
8871
|
+
kind,
|
|
8872
|
+
velocity: kind === "rep" ? set.velocities[i] : void 0,
|
|
8873
|
+
leadingGap: i === 0 ? 0 : REP_GAP
|
|
8874
|
+
};
|
|
8875
|
+
});
|
|
8876
|
+
}
|
|
8877
|
+
case "amrap": {
|
|
8878
|
+
const reps = set.velocities.map((velocity, i) => ({
|
|
8879
|
+
kind: "rep",
|
|
8880
|
+
velocity,
|
|
8881
|
+
leadingGap: i === 0 ? 0 : REP_GAP
|
|
8882
|
+
}));
|
|
8883
|
+
reps.push({ kind: "continue", leadingGap: reps.length === 0 ? 0 : REP_GAP });
|
|
8884
|
+
return reps;
|
|
8885
|
+
}
|
|
8886
|
+
case "drop":
|
|
8887
|
+
return chunkedRepSlots(set.subloads);
|
|
8888
|
+
case "myo": {
|
|
8889
|
+
const slots = chunkedRepSlots([set.activation, ...set.clusters]);
|
|
8890
|
+
if (set.open) slots.push({ kind: "continue", leadingGap: slots.length === 0 ? 0 : REP_GAP });
|
|
8891
|
+
return slots;
|
|
8892
|
+
}
|
|
8893
|
+
case "cluster": {
|
|
8894
|
+
const total = Math.max(set.velocities.length, set.planned ?? set.velocities.length);
|
|
8895
|
+
return Array.from({ length: total }, (_, i) => {
|
|
8896
|
+
const done = i < set.velocities.length;
|
|
8897
|
+
const boundary = i > 0 && i % set.groupSize === 0;
|
|
8898
|
+
return {
|
|
8899
|
+
kind: done ? "rep" : "todo",
|
|
8900
|
+
velocity: done ? set.velocities[i] : void 0,
|
|
8901
|
+
leadingGap: i === 0 ? 0 : boundary ? WIDE_GAP : REP_GAP
|
|
8902
|
+
};
|
|
8903
|
+
});
|
|
8904
|
+
}
|
|
8905
|
+
}
|
|
8906
|
+
}
|
|
8907
|
+
function setAccessibilityLabel(set, repCount) {
|
|
8908
|
+
switch (set.type) {
|
|
8909
|
+
case "straight":
|
|
8910
|
+
return `Velocity strip, straight set, ${repCount} reps`;
|
|
8911
|
+
case "range":
|
|
8912
|
+
return `Velocity strip, rep-range set, floor ${set.floor} to ${set.max}, ${repCount} reps done`;
|
|
8913
|
+
case "amrap":
|
|
8914
|
+
return `Velocity strip, AMRAP set, ${repCount} reps and counting`;
|
|
8915
|
+
case "drop":
|
|
8916
|
+
return `Velocity strip, drop set, ${set.subloads.length} loads, ${repCount} reps`;
|
|
8917
|
+
case "myo":
|
|
8918
|
+
return `Velocity strip, myo-reps set, ${set.clusters.length} clusters${set.open ? ", open" : ""}, ${repCount} reps`;
|
|
8919
|
+
case "cluster":
|
|
8920
|
+
return `Velocity strip, cluster set, groups of ${set.groupSize}, ${repCount} reps`;
|
|
8921
|
+
}
|
|
8922
|
+
}
|
|
8923
|
+
function slotAccessibilityLabel(slot, index) {
|
|
8924
|
+
switch (slot.kind) {
|
|
8925
|
+
case "rep":
|
|
8926
|
+
return `Rep ${index + 1}: ${formatVelocity(slot.velocity ?? 0)} meters per second`;
|
|
8927
|
+
case "todo":
|
|
8928
|
+
return `Rep ${index + 1}: planned`;
|
|
8929
|
+
case "variable":
|
|
8930
|
+
return `Rep ${index + 1}: variable`;
|
|
8931
|
+
case "continue":
|
|
8932
|
+
return "Keep going";
|
|
8933
|
+
}
|
|
8934
|
+
}
|
|
8935
|
+
var EXPANDED_TODO_STUB_PCT = 16;
|
|
8936
|
+
var EXPANDED_ENCODED_PCT = 45;
|
|
8937
|
+
var EXPANDED_HEIGHT = 60;
|
|
8938
|
+
var FIXED_MAX_VELOCITY = 1.15;
|
|
8939
|
+
var BARE_STUB_HEIGHT = 3;
|
|
8940
|
+
function scaleDenominator(scale, maxVelocity) {
|
|
8941
|
+
return scale === "fixed" ? FIXED_MAX_VELOCITY : maxVelocity * 1.15;
|
|
8942
|
+
}
|
|
8943
|
+
function bareSlotHeight(slot, height, denom) {
|
|
8944
|
+
if (slot.kind !== "rep" || denom <= 0) return BARE_STUB_HEIGHT;
|
|
8945
|
+
const ratio = Math.min(1, (slot.velocity ?? 0) / denom);
|
|
8946
|
+
return Math.max(BARE_STUB_HEIGHT, ratio * height);
|
|
8947
|
+
}
|
|
8504
8948
|
function getReducedMotionPreference() {
|
|
8505
8949
|
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
|
|
8506
8950
|
return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
@@ -8522,31 +8966,48 @@ var ANIMATION_EASING = reactNative.Easing.bezier(0.22, 1, 0.36, 1);
|
|
|
8522
8966
|
var POP_EASING = reactNative.Easing.bezier(0.34, 1.56, 0.64, 1);
|
|
8523
8967
|
function VelocityStrip({
|
|
8524
8968
|
velocities,
|
|
8969
|
+
set,
|
|
8525
8970
|
zones,
|
|
8526
8971
|
liveRepIndex,
|
|
8527
|
-
expanded =
|
|
8972
|
+
expanded = true,
|
|
8528
8973
|
onToggle,
|
|
8529
8974
|
onRepPress,
|
|
8530
|
-
variant = "
|
|
8975
|
+
variant = "expanded",
|
|
8976
|
+
height = EXPANDED_HEIGHT,
|
|
8977
|
+
scale = "peak",
|
|
8978
|
+
showNumbers = true,
|
|
8531
8979
|
showInfo = true,
|
|
8532
8980
|
className,
|
|
8533
8981
|
...props
|
|
8534
8982
|
}) {
|
|
8535
|
-
const
|
|
8536
|
-
const
|
|
8537
|
-
|
|
8983
|
+
const doneVelocities = set ? deriveDoneVelocities(set) : velocities ?? [];
|
|
8984
|
+
const slots = set ? buildSlots(set) : doneVelocities.map((v, i) => ({
|
|
8985
|
+
kind: "rep",
|
|
8986
|
+
velocity: v,
|
|
8987
|
+
leadingGap: i === 0 ? 0 : REP_GAP
|
|
8988
|
+
}));
|
|
8989
|
+
const maxVelocity = Math.max(...doneVelocities, 0);
|
|
8990
|
+
const meanVelocity = calculateMeanVelocity(doneVelocities);
|
|
8991
|
+
const loss = calculateVelocityLoss(doneVelocities);
|
|
8992
|
+
const framed = showNumbers || showInfo;
|
|
8993
|
+
const scaleDenom = scaleDenominator(scale, maxVelocity);
|
|
8538
8994
|
const hasZones = zones != null && zones.length > 0;
|
|
8539
8995
|
const barColorFor = (v) => hasZones ? bandColor(classifyBand(v, zones)) : zoneHexMap[getVelocityZoneColor(v)];
|
|
8996
|
+
const slotColor = (slot) => {
|
|
8997
|
+
if (slot.kind === "rep") return barColorFor(slot.velocity ?? 0);
|
|
8998
|
+
if (slot.kind === "todo") return TODO_COLOR2;
|
|
8999
|
+
return SET_STRIP_VARIABLE_COLOR;
|
|
9000
|
+
};
|
|
8540
9001
|
const meanZone = hasZones ? classifyBand(meanVelocity, zones)?.label ?? "" : getVelocityZoneName(meanVelocity);
|
|
8541
9002
|
const prefersReducedMotion = usePrefersReducedMotion();
|
|
8542
|
-
const [heightAnim] = React24.useState(() => new reactNative.Animated.Value(expanded ?
|
|
9003
|
+
const [heightAnim] = React24.useState(() => new reactNative.Animated.Value(expanded ? height : 3));
|
|
8543
9004
|
const [labelOpacity] = React24.useState(() => new reactNative.Animated.Value(expanded ? 1 : 0));
|
|
8544
9005
|
const [infoOpacity] = React24.useState(() => new reactNative.Animated.Value(expanded ? 1 : 0));
|
|
8545
9006
|
const [liveScale] = React24.useState(() => new reactNative.Animated.Value(1));
|
|
8546
|
-
const liveVelocity = liveRepIndex != null ?
|
|
9007
|
+
const liveVelocity = liveRepIndex != null ? doneVelocities[liveRepIndex] : void 0;
|
|
8547
9008
|
const isNewPeak = liveVelocity != null && maxVelocity > 0 && liveVelocity === maxVelocity;
|
|
8548
9009
|
React24.useEffect(() => {
|
|
8549
|
-
if (variant
|
|
9010
|
+
if (variant !== "expanded" || !framed || liveRepIndex == null || liveVelocity == null) return;
|
|
8550
9011
|
if (prefersReducedMotion) {
|
|
8551
9012
|
liveScale.setValue(1);
|
|
8552
9013
|
return;
|
|
@@ -8576,12 +9037,12 @@ function VelocityStrip({
|
|
|
8576
9037
|
useNativeDriver: true
|
|
8577
9038
|
}).start();
|
|
8578
9039
|
}
|
|
8579
|
-
}, [variant, liveRepIndex, liveVelocity, isNewPeak, prefersReducedMotion, liveScale]);
|
|
9040
|
+
}, [variant, framed, liveRepIndex, liveVelocity, isNewPeak, prefersReducedMotion, liveScale]);
|
|
8580
9041
|
React24.useEffect(() => {
|
|
8581
|
-
if (variant
|
|
9042
|
+
if (variant !== "expanded" || !framed) return;
|
|
8582
9043
|
reactNative.Animated.parallel([
|
|
8583
9044
|
reactNative.Animated.timing(heightAnim, {
|
|
8584
|
-
toValue: expanded ?
|
|
9045
|
+
toValue: expanded ? height : 3,
|
|
8585
9046
|
duration: ANIMATION_DURATION,
|
|
8586
9047
|
easing: ANIMATION_EASING,
|
|
8587
9048
|
useNativeDriver: false
|
|
@@ -8599,52 +9060,133 @@ function VelocityStrip({
|
|
|
8599
9060
|
useNativeDriver: false
|
|
8600
9061
|
})
|
|
8601
9062
|
]).start();
|
|
8602
|
-
}, [expanded, variant, heightAnim, labelOpacity, infoOpacity]);
|
|
9063
|
+
}, [expanded, variant, framed, height, heightAnim, labelOpacity, infoOpacity]);
|
|
9064
|
+
if (set == null && velocities == null) return null;
|
|
9065
|
+
const repCount = doneVelocities.length;
|
|
9066
|
+
const miniLabel = set ? setAccessibilityLabel(set, repCount) : `Velocity strip, ${repCount} reps`;
|
|
8603
9067
|
if (variant === "mini") {
|
|
8604
9068
|
const { style: externalStyle, ...restProps } = props;
|
|
8605
9069
|
return /* @__PURE__ */ jsx(
|
|
8606
9070
|
reactNative.View,
|
|
8607
9071
|
{
|
|
8608
9072
|
className,
|
|
8609
|
-
style: [
|
|
9073
|
+
style: [
|
|
9074
|
+
{ flexDirection: "row", height: 3, gap: REP_GAP, borderRadius: 2, overflow: "hidden" },
|
|
9075
|
+
externalStyle
|
|
9076
|
+
],
|
|
8610
9077
|
accessibilityRole: "image",
|
|
8611
|
-
accessibilityLabel:
|
|
9078
|
+
accessibilityLabel: miniLabel,
|
|
8612
9079
|
testID: "velocity-strip-mini",
|
|
8613
9080
|
...restProps,
|
|
8614
|
-
children:
|
|
9081
|
+
children: slots.map((slot, i) => /* @__PURE__ */ jsx(
|
|
8615
9082
|
reactNative.View,
|
|
8616
9083
|
{
|
|
8617
9084
|
style: {
|
|
8618
9085
|
flex: 1,
|
|
8619
|
-
backgroundColor:
|
|
9086
|
+
backgroundColor: slotColor(slot),
|
|
8620
9087
|
borderRadius: 1,
|
|
8621
9088
|
minWidth: 4,
|
|
8622
|
-
height: "100%"
|
|
9089
|
+
height: "100%",
|
|
9090
|
+
// The container's uniform 2px gap covers rep spacing; a wide slot adds
|
|
9091
|
+
// only the EXTRA (WIDE_GAP − REP_GAP) so the notch totals WIDE_GAP.
|
|
9092
|
+
marginLeft: Math.max(0, slot.leadingGap - REP_GAP),
|
|
9093
|
+
...slot.kind === "continue" ? { borderWidth: 1, borderColor: CONTINUE_OUTLINE } : {}
|
|
9094
|
+
},
|
|
9095
|
+
accessibilityElementsHidden: true,
|
|
9096
|
+
testID: slot.kind === "rep" ? `velocity-bar-${i}` : `velocity-slot-${slot.kind}`
|
|
9097
|
+
},
|
|
9098
|
+
i
|
|
9099
|
+
))
|
|
9100
|
+
}
|
|
9101
|
+
);
|
|
9102
|
+
}
|
|
9103
|
+
if (!framed) {
|
|
9104
|
+
const { style: externalStyle, ...restProps } = props;
|
|
9105
|
+
return /* @__PURE__ */ jsx(
|
|
9106
|
+
reactNative.View,
|
|
9107
|
+
{
|
|
9108
|
+
className,
|
|
9109
|
+
style: [
|
|
9110
|
+
{ flexDirection: "row", height, gap: REP_GAP, alignItems: "flex-end" },
|
|
9111
|
+
externalStyle
|
|
9112
|
+
],
|
|
9113
|
+
accessibilityRole: "image",
|
|
9114
|
+
accessibilityLabel: miniLabel,
|
|
9115
|
+
testID: "velocity-strip-compact",
|
|
9116
|
+
...restProps,
|
|
9117
|
+
children: slots.map((slot, i) => /* @__PURE__ */ jsx(
|
|
9118
|
+
reactNative.View,
|
|
9119
|
+
{
|
|
9120
|
+
style: {
|
|
9121
|
+
flex: 1,
|
|
9122
|
+
minWidth: 4,
|
|
9123
|
+
height: bareSlotHeight(slot, height, scaleDenom),
|
|
9124
|
+
// Top-rounded to match the framed chart's bars (the "rounded tops from
|
|
9125
|
+
// the numbers one"); square bottoms since bars sit on the baseline.
|
|
9126
|
+
borderTopLeftRadius: 2,
|
|
9127
|
+
borderTopRightRadius: 2,
|
|
9128
|
+
backgroundColor: slotColor(slot),
|
|
9129
|
+
marginLeft: Math.max(0, slot.leadingGap - REP_GAP),
|
|
9130
|
+
...slot.kind === "continue" ? { borderWidth: 1, borderColor: CONTINUE_OUTLINE } : {}
|
|
8623
9131
|
},
|
|
8624
9132
|
accessibilityElementsHidden: true,
|
|
8625
|
-
testID: `velocity-bar-${i}`
|
|
9133
|
+
testID: slot.kind === "rep" ? `velocity-bar-${i}` : `velocity-slot-${slot.kind}`
|
|
8626
9134
|
},
|
|
8627
9135
|
i
|
|
8628
9136
|
))
|
|
8629
9137
|
}
|
|
8630
9138
|
);
|
|
8631
9139
|
}
|
|
8632
|
-
const stripLabel = `Velocity chart for set, ${
|
|
9140
|
+
const stripLabel = set ? `${setAccessibilityLabel(set, repCount)}, tap to ${expanded ? "collapse" : "expand"}` : `Velocity chart for set, ${repCount} reps, tap to ${expanded ? "collapse" : "expand"}`;
|
|
8633
9141
|
const hasInteractiveContainer = onToggle != null;
|
|
8634
9142
|
const hasInteractiveReps = onRepPress != null && expanded;
|
|
8635
|
-
const
|
|
8636
|
-
|
|
8637
|
-
|
|
8638
|
-
|
|
8639
|
-
|
|
8640
|
-
|
|
8641
|
-
|
|
8642
|
-
|
|
8643
|
-
|
|
9143
|
+
const setBars = set != null && slots.map((slot, i) => {
|
|
9144
|
+
const isStraightRep = set.type === "straight" && slot.kind === "rep";
|
|
9145
|
+
const heightPct = isStraightRep ? scaleDenom > 0 ? Math.round((slot.velocity ?? 0) / scaleDenom * 100) : 0 : set.type === "straight" ? EXPANDED_TODO_STUB_PCT : EXPANDED_ENCODED_PCT;
|
|
9146
|
+
return /* @__PURE__ */ jsx(
|
|
9147
|
+
reactNative.View,
|
|
9148
|
+
{
|
|
9149
|
+
style: {
|
|
9150
|
+
flex: 1,
|
|
9151
|
+
height: "100%",
|
|
9152
|
+
justifyContent: "flex-end",
|
|
9153
|
+
marginLeft: slot.leadingGap
|
|
9154
|
+
},
|
|
9155
|
+
children: /* @__PURE__ */ jsx(
|
|
9156
|
+
reactNative.View,
|
|
9157
|
+
{
|
|
9158
|
+
style: {
|
|
9159
|
+
height: `${heightPct}%`,
|
|
9160
|
+
minHeight: 2,
|
|
9161
|
+
borderTopLeftRadius: 2,
|
|
9162
|
+
borderTopRightRadius: 2,
|
|
9163
|
+
backgroundColor: slotColor(slot),
|
|
9164
|
+
...slot.kind === "continue" ? { borderWidth: 1, borderColor: CONTINUE_OUTLINE } : {}
|
|
9165
|
+
},
|
|
9166
|
+
testID: slot.kind === "rep" ? `velocity-bar-${i}` : `velocity-slot-${slot.kind}`,
|
|
9167
|
+
accessibilityRole: "image",
|
|
9168
|
+
accessibilityLabel: slotAccessibilityLabel(slot, i)
|
|
9169
|
+
}
|
|
9170
|
+
)
|
|
9171
|
+
},
|
|
9172
|
+
i
|
|
9173
|
+
);
|
|
9174
|
+
});
|
|
9175
|
+
const stripContent = /* @__PURE__ */ jsxs(
|
|
9176
|
+
reactNative.Animated.View,
|
|
9177
|
+
{
|
|
9178
|
+
className: [className, expanded ? "bg-surface-raised" : "bg-transparent"].filter(Boolean).join(" "),
|
|
9179
|
+
style: [
|
|
9180
|
+
{
|
|
9181
|
+
height: heightAnim,
|
|
9182
|
+
width: "100%",
|
|
9183
|
+
gap: 2,
|
|
8644
9184
|
borderRadius: expanded ? 6 : 2,
|
|
8645
9185
|
paddingTop: expanded ? 16 : 0,
|
|
8646
9186
|
paddingBottom: expanded ? 24 : 0,
|
|
8647
|
-
|
|
9187
|
+
// No horizontal inset — the framed chart's bars span full width, matching
|
|
9188
|
+
// the bare spotlight and mini. Numbers/info sit in the vertical padding.
|
|
9189
|
+
paddingHorizontal: 0,
|
|
8648
9190
|
paddingVertical: expanded ? void 0 : 8,
|
|
8649
9191
|
overflow: expanded ? "visible" : "hidden"
|
|
8650
9192
|
}
|
|
@@ -8654,46 +9196,65 @@ function VelocityStrip({
|
|
|
8654
9196
|
testID: "velocity-strip",
|
|
8655
9197
|
...props,
|
|
8656
9198
|
children: [
|
|
8657
|
-
/* @__PURE__ */ jsx(
|
|
8658
|
-
|
|
8659
|
-
|
|
8660
|
-
|
|
8661
|
-
|
|
8662
|
-
|
|
8663
|
-
|
|
8664
|
-
|
|
8665
|
-
|
|
8666
|
-
|
|
8667
|
-
|
|
8668
|
-
|
|
8669
|
-
|
|
8670
|
-
|
|
8671
|
-
|
|
8672
|
-
|
|
8673
|
-
|
|
8674
|
-
|
|
8675
|
-
|
|
8676
|
-
|
|
8677
|
-
|
|
8678
|
-
|
|
8679
|
-
|
|
8680
|
-
|
|
8681
|
-
|
|
8682
|
-
|
|
8683
|
-
|
|
8684
|
-
|
|
8685
|
-
|
|
8686
|
-
|
|
8687
|
-
|
|
8688
|
-
|
|
8689
|
-
|
|
8690
|
-
)
|
|
8691
|
-
|
|
8692
|
-
|
|
8693
|
-
|
|
8694
|
-
|
|
8695
|
-
|
|
8696
|
-
|
|
9199
|
+
/* @__PURE__ */ jsx(reactNative.View, { style: { flexDirection: "row", flex: 1, gap: set ? 0 : 2, alignItems: "flex-end" }, children: set ? setBars : doneVelocities.map((v, i) => {
|
|
9200
|
+
const barBackground = barColorFor(v);
|
|
9201
|
+
const barHeightPct = scaleDenom > 0 ? Math.round(v / scaleDenom * 100) : 0;
|
|
9202
|
+
const isLive = liveRepIndex === i;
|
|
9203
|
+
const liveLabelSuffix = isLive ? isNewPeak ? ", latest rep, new set peak" : ", latest rep" : "";
|
|
9204
|
+
const barStyle = expanded ? {
|
|
9205
|
+
height: `${barHeightPct}%`,
|
|
9206
|
+
minHeight: 2,
|
|
9207
|
+
borderTopLeftRadius: 2,
|
|
9208
|
+
borderTopRightRadius: 2,
|
|
9209
|
+
backgroundColor: barBackground
|
|
9210
|
+
} : {
|
|
9211
|
+
minHeight: "100%",
|
|
9212
|
+
borderTopLeftRadius: 2,
|
|
9213
|
+
borderTopRightRadius: 2,
|
|
9214
|
+
borderBottomLeftRadius: 0,
|
|
9215
|
+
borderBottomRightRadius: 0,
|
|
9216
|
+
backgroundColor: barBackground
|
|
9217
|
+
};
|
|
9218
|
+
const barInner = isLive ? /* @__PURE__ */ jsx(
|
|
9219
|
+
reactNative.Animated.View,
|
|
9220
|
+
{
|
|
9221
|
+
style: [barStyle, { transform: [{ scale: liveScale }] }],
|
|
9222
|
+
testID: `velocity-bar-${i}`,
|
|
9223
|
+
accessibilityRole: "image",
|
|
9224
|
+
accessibilityLabel: `Rep ${i + 1}: ${formatVelocity(v)} meters per second${liveLabelSuffix}`
|
|
9225
|
+
}
|
|
9226
|
+
) : /* @__PURE__ */ jsx(
|
|
9227
|
+
reactNative.View,
|
|
9228
|
+
{
|
|
9229
|
+
style: barStyle,
|
|
9230
|
+
testID: `velocity-bar-${i}`,
|
|
9231
|
+
accessibilityRole: "image",
|
|
9232
|
+
accessibilityLabel: `Rep ${i + 1}: ${formatVelocity(v)} meters per second`
|
|
9233
|
+
}
|
|
9234
|
+
);
|
|
9235
|
+
const bar = /* @__PURE__ */ jsxs(
|
|
9236
|
+
reactNative.View,
|
|
9237
|
+
{
|
|
9238
|
+
style: {
|
|
9239
|
+
flex: 1,
|
|
9240
|
+
height: "100%",
|
|
9241
|
+
justifyContent: "flex-end",
|
|
9242
|
+
position: "relative"
|
|
9243
|
+
},
|
|
9244
|
+
children: [
|
|
9245
|
+
expanded && showNumbers && /* @__PURE__ */ jsx(
|
|
9246
|
+
reactNative.Animated.View,
|
|
9247
|
+
{
|
|
9248
|
+
style: {
|
|
9249
|
+
opacity: labelOpacity,
|
|
9250
|
+
alignItems: "center",
|
|
9251
|
+
position: "absolute",
|
|
9252
|
+
top: -13,
|
|
9253
|
+
left: 0,
|
|
9254
|
+
right: 0
|
|
9255
|
+
},
|
|
9256
|
+
accessibilityElementsHidden: true,
|
|
9257
|
+
children: /* @__PURE__ */ jsx(
|
|
8697
9258
|
reactNative.Text,
|
|
8698
9259
|
{
|
|
8699
9260
|
className: "text-text-secondary",
|
|
@@ -8701,30 +9262,30 @@ function VelocityStrip({
|
|
|
8701
9262
|
testID: `velocity-label-${i}`,
|
|
8702
9263
|
children: formatVelocity(v)
|
|
8703
9264
|
}
|
|
8704
|
-
)
|
|
8705
|
-
|
|
8706
|
-
|
|
8707
|
-
|
|
8708
|
-
|
|
8709
|
-
|
|
8710
|
-
|
|
8711
|
-
|
|
8712
|
-
|
|
8713
|
-
|
|
8714
|
-
|
|
8715
|
-
|
|
8716
|
-
|
|
8717
|
-
|
|
8718
|
-
|
|
8719
|
-
|
|
8720
|
-
|
|
8721
|
-
|
|
8722
|
-
|
|
8723
|
-
|
|
8724
|
-
|
|
8725
|
-
})
|
|
9265
|
+
)
|
|
9266
|
+
}
|
|
9267
|
+
),
|
|
9268
|
+
barInner
|
|
9269
|
+
]
|
|
9270
|
+
},
|
|
9271
|
+
i
|
|
9272
|
+
);
|
|
9273
|
+
if (onRepPress && expanded) {
|
|
9274
|
+
return /* @__PURE__ */ jsx(
|
|
9275
|
+
reactNative.Pressable,
|
|
9276
|
+
{
|
|
9277
|
+
style: { flex: 1, height: "100%" },
|
|
9278
|
+
onPress: () => onRepPress(i, v),
|
|
9279
|
+
accessibilityRole: "button",
|
|
9280
|
+
accessibilityLabel: `Rep ${i + 1}: ${formatVelocity(v)} meters per second, tap for details`,
|
|
9281
|
+
testID: `velocity-bar-pressable-${i}`,
|
|
9282
|
+
children: bar
|
|
9283
|
+
},
|
|
9284
|
+
i
|
|
9285
|
+
);
|
|
8726
9286
|
}
|
|
8727
|
-
|
|
9287
|
+
return bar;
|
|
9288
|
+
}) }),
|
|
8728
9289
|
expanded && showInfo && /* @__PURE__ */ jsxs(
|
|
8729
9290
|
reactNative.Animated.View,
|
|
8730
9291
|
{
|
|
@@ -9022,233 +9583,90 @@ function Sparkline({
|
|
|
9022
9583
|
}
|
|
9023
9584
|
);
|
|
9024
9585
|
}
|
|
9025
|
-
|
|
9026
|
-
|
|
9027
|
-
|
|
9028
|
-
|
|
9586
|
+
var DARK = getSemanticColors("dark");
|
|
9587
|
+
var TEXT_ACTIVE = DARK["text-primary"];
|
|
9588
|
+
var TEXT_MUTED = DARK["text-secondary"];
|
|
9589
|
+
var COL = { set: 36, reps: 44, load: 56, rpe: 36 };
|
|
9590
|
+
var cellText = { fontFamily: "Inter, sans-serif", fontSize: 13, fontWeight: "600" };
|
|
9591
|
+
var typeBadge = {
|
|
9592
|
+
fontSize: 10,
|
|
9593
|
+
fontWeight: "700",
|
|
9594
|
+
fontFamily: "Inter, sans-serif",
|
|
9595
|
+
color: WORKOUT_TOKENS.scale.orange,
|
|
9596
|
+
backgroundColor: "rgba(255,165,2,0.12)",
|
|
9597
|
+
paddingVertical: 1,
|
|
9598
|
+
paddingHorizontal: 5,
|
|
9599
|
+
borderRadius: 3,
|
|
9600
|
+
overflow: "hidden"
|
|
9601
|
+
};
|
|
9602
|
+
function displayReps(set) {
|
|
9603
|
+
return set.state === "done" ? set.reps : set.target.reps;
|
|
9604
|
+
}
|
|
9605
|
+
function displayWeight(set) {
|
|
9606
|
+
return set.state === "done" ? set.weight : set.target.weight;
|
|
9607
|
+
}
|
|
9608
|
+
function plannedReps(set) {
|
|
9609
|
+
if (set.state === "done") return set.velocities.length;
|
|
9610
|
+
if (set.state === "live") return set.target.reps;
|
|
9611
|
+
return set.planned ?? set.target.reps;
|
|
9612
|
+
}
|
|
9613
|
+
function accessibilityLabel(set) {
|
|
9614
|
+
const base = `Set ${set.setNumber}: ${displayReps(set)} reps at ${roundWeight(displayWeight(set))} ${set.unit}`;
|
|
9615
|
+
if (set.state === "live") return `${base}, in progress`;
|
|
9616
|
+
if (set.state === "todo") return `${base}, upcoming`;
|
|
9617
|
+
return base;
|
|
9618
|
+
}
|
|
9619
|
+
function Cell({
|
|
9620
|
+
width,
|
|
9621
|
+
children,
|
|
9622
|
+
testID
|
|
9623
|
+
}) {
|
|
9624
|
+
return /* @__PURE__ */ jsx(reactNative.View, { style: { width, alignItems: "center", justifyContent: "center" }, testID, children });
|
|
9029
9625
|
}
|
|
9030
|
-
function
|
|
9031
|
-
|
|
9032
|
-
|
|
9033
|
-
|
|
9034
|
-
|
|
9035
|
-
|
|
9036
|
-
|
|
9037
|
-
|
|
9038
|
-
|
|
9039
|
-
|
|
9040
|
-
|
|
9041
|
-
|
|
9042
|
-
|
|
9043
|
-
|
|
9044
|
-
|
|
9045
|
-
isNextSet,
|
|
9046
|
-
targets
|
|
9047
|
-
}) {
|
|
9048
|
-
const isActive = mode === "active";
|
|
9049
|
-
const isCompleted = mode === "completed";
|
|
9050
|
-
const rowStyle = {
|
|
9051
|
-
flexDirection: "row",
|
|
9052
|
-
alignItems: "center",
|
|
9053
|
-
padding: 7,
|
|
9054
|
-
paddingHorizontal: 8,
|
|
9055
|
-
gap: 8,
|
|
9056
|
-
borderRadius: 8
|
|
9057
|
-
};
|
|
9058
|
-
if (isActive && isNextSet) {
|
|
9059
|
-
rowStyle.backgroundColor = "rgba(255,121,0,0.06)";
|
|
9060
|
-
rowStyle.borderWidth = 1;
|
|
9061
|
-
rowStyle.borderColor = "rgba(255,121,0,0.15)";
|
|
9062
|
-
}
|
|
9063
|
-
if (isCompleted && !isNextSet) {
|
|
9064
|
-
rowStyle.opacity = 0.55;
|
|
9626
|
+
function RowStrip({ set }) {
|
|
9627
|
+
const zones = set.velocityZones;
|
|
9628
|
+
if (set.state === "live") {
|
|
9629
|
+
return /* @__PURE__ */ jsx(
|
|
9630
|
+
VelocityStrip,
|
|
9631
|
+
{
|
|
9632
|
+
variant: "expanded",
|
|
9633
|
+
showNumbers: false,
|
|
9634
|
+
showInfo: false,
|
|
9635
|
+
height: 24,
|
|
9636
|
+
scale: "fixed",
|
|
9637
|
+
set: { type: "straight", velocities: set.velocities, planned: set.target.reps },
|
|
9638
|
+
zones
|
|
9639
|
+
}
|
|
9640
|
+
);
|
|
9065
9641
|
}
|
|
9642
|
+
const velocities = set.state === "done" ? set.velocities : [];
|
|
9643
|
+
return /* @__PURE__ */ jsx(
|
|
9644
|
+
VelocityStrip,
|
|
9645
|
+
{
|
|
9646
|
+
variant: "mini",
|
|
9647
|
+
set: { type: "straight", velocities, planned: plannedReps(set) },
|
|
9648
|
+
zones
|
|
9649
|
+
}
|
|
9650
|
+
);
|
|
9651
|
+
}
|
|
9652
|
+
function SetRow(set) {
|
|
9653
|
+
const live = set.state === "live";
|
|
9654
|
+
const valueColor = live ? TEXT_ACTIVE : TEXT_MUTED;
|
|
9655
|
+
const rpe = set.state === "todo" ? null : set.rpe;
|
|
9066
9656
|
return /* @__PURE__ */ jsxs(
|
|
9067
9657
|
reactNative.View,
|
|
9068
9658
|
{
|
|
9069
|
-
style:
|
|
9070
|
-
accessibilityLabel:
|
|
9071
|
-
setNumber,
|
|
9072
|
-
reps,
|
|
9073
|
-
weight,
|
|
9074
|
-
unit
|
|
9075
|
-
),
|
|
9659
|
+
style: { paddingVertical: 6, paddingHorizontal: 8 },
|
|
9660
|
+
accessibilityLabel: accessibilityLabel(set),
|
|
9076
9661
|
testID: "set-row",
|
|
9077
9662
|
children: [
|
|
9078
|
-
/* @__PURE__ */
|
|
9079
|
-
reactNative.
|
|
9080
|
-
{
|
|
9081
|
-
|
|
9082
|
-
|
|
9083
|
-
|
|
9084
|
-
|
|
9085
|
-
{
|
|
9086
|
-
style: {
|
|
9087
|
-
fontSize: 10,
|
|
9088
|
-
fontWeight: "700",
|
|
9089
|
-
fontFamily: "Inter, sans-serif",
|
|
9090
|
-
color: WORKOUT_TOKENS.scale.orange,
|
|
9091
|
-
backgroundColor: "rgba(255,165,2,0.12)",
|
|
9092
|
-
paddingVertical: 1,
|
|
9093
|
-
paddingHorizontal: 5,
|
|
9094
|
-
borderRadius: 3,
|
|
9095
|
-
overflow: "hidden"
|
|
9096
|
-
},
|
|
9097
|
-
testID: "set-row-type-badge",
|
|
9098
|
-
children: setType
|
|
9099
|
-
}
|
|
9100
|
-
) : /* @__PURE__ */ jsx(
|
|
9101
|
-
reactNative.Text,
|
|
9102
|
-
{
|
|
9103
|
-
className: "text-text-secondary",
|
|
9104
|
-
style: {
|
|
9105
|
-
fontSize: 13,
|
|
9106
|
-
fontWeight: "600",
|
|
9107
|
-
fontFamily: "Inter, sans-serif"
|
|
9108
|
-
},
|
|
9109
|
-
children: setNumber
|
|
9110
|
-
}
|
|
9111
|
-
)
|
|
9112
|
-
}
|
|
9113
|
-
),
|
|
9114
|
-
/* @__PURE__ */ jsx(
|
|
9115
|
-
reactNative.View,
|
|
9116
|
-
{
|
|
9117
|
-
className: "flex-1",
|
|
9118
|
-
testID: "set-row-previous",
|
|
9119
|
-
children: /* @__PURE__ */ jsx(
|
|
9120
|
-
reactNative.Text,
|
|
9121
|
-
{
|
|
9122
|
-
className: "text-text-secondary",
|
|
9123
|
-
style: {
|
|
9124
|
-
fontSize: 12,
|
|
9125
|
-
fontFamily: "Inter, sans-serif"
|
|
9126
|
-
},
|
|
9127
|
-
children: previous ? `${previous.reps} x ${roundWeight(previous.weight)}` : "\u2014"
|
|
9128
|
-
}
|
|
9129
|
-
)
|
|
9130
|
-
}
|
|
9131
|
-
),
|
|
9132
|
-
/* @__PURE__ */ jsx(
|
|
9133
|
-
reactNative.View,
|
|
9134
|
-
{
|
|
9135
|
-
style: { width: 44, alignItems: "center", justifyContent: "center" },
|
|
9136
|
-
testID: "set-row-reps",
|
|
9137
|
-
children: isActive && reps == null && targets ? /* @__PURE__ */ jsx(
|
|
9138
|
-
reactNative.Text,
|
|
9139
|
-
{
|
|
9140
|
-
className: "text-text-tertiary",
|
|
9141
|
-
style: {
|
|
9142
|
-
fontSize: 13,
|
|
9143
|
-
fontStyle: "italic",
|
|
9144
|
-
fontFamily: "Inter, sans-serif"
|
|
9145
|
-
},
|
|
9146
|
-
testID: "set-row-target-reps",
|
|
9147
|
-
children: targets.reps
|
|
9148
|
-
}
|
|
9149
|
-
) : /* @__PURE__ */ jsx(
|
|
9150
|
-
reactNative.Text,
|
|
9151
|
-
{
|
|
9152
|
-
className: reps != null ? "text-text-primary" : "text-text-tertiary",
|
|
9153
|
-
style: {
|
|
9154
|
-
fontSize: 14,
|
|
9155
|
-
fontWeight: "600",
|
|
9156
|
-
fontFamily: "Inter, sans-serif"
|
|
9157
|
-
},
|
|
9158
|
-
children: reps != null ? reps : "\u2014"
|
|
9159
|
-
}
|
|
9160
|
-
)
|
|
9161
|
-
}
|
|
9162
|
-
),
|
|
9163
|
-
/* @__PURE__ */ jsx(
|
|
9164
|
-
reactNative.View,
|
|
9165
|
-
{
|
|
9166
|
-
style: { width: 56, alignItems: "center", justifyContent: "center" },
|
|
9167
|
-
testID: "set-row-weight",
|
|
9168
|
-
children: isActive && weight == null && targets ? /* @__PURE__ */ jsx(
|
|
9169
|
-
reactNative.Text,
|
|
9170
|
-
{
|
|
9171
|
-
className: "text-text-tertiary",
|
|
9172
|
-
style: {
|
|
9173
|
-
fontSize: 13,
|
|
9174
|
-
fontStyle: "italic",
|
|
9175
|
-
fontFamily: "Inter, sans-serif"
|
|
9176
|
-
},
|
|
9177
|
-
testID: "set-row-target-weight",
|
|
9178
|
-
children: roundWeight(targets.weight)
|
|
9179
|
-
}
|
|
9180
|
-
) : /* @__PURE__ */ jsx(
|
|
9181
|
-
reactNative.Text,
|
|
9182
|
-
{
|
|
9183
|
-
className: weight != null ? "text-text-primary" : "text-text-tertiary",
|
|
9184
|
-
style: {
|
|
9185
|
-
fontSize: 14,
|
|
9186
|
-
fontWeight: "600",
|
|
9187
|
-
fontFamily: "Inter, sans-serif"
|
|
9188
|
-
},
|
|
9189
|
-
children: weight != null ? roundWeight(weight) : "\u2014"
|
|
9190
|
-
}
|
|
9191
|
-
)
|
|
9192
|
-
}
|
|
9193
|
-
),
|
|
9194
|
-
/* @__PURE__ */ jsx(
|
|
9195
|
-
reactNative.View,
|
|
9196
|
-
{
|
|
9197
|
-
style: { width: 36, alignItems: "center", justifyContent: "center" },
|
|
9198
|
-
testID: "set-row-rpe",
|
|
9199
|
-
children: /* @__PURE__ */ jsx(
|
|
9200
|
-
reactNative.Text,
|
|
9201
|
-
{
|
|
9202
|
-
className: rpe != null ? void 0 : "text-text-tertiary",
|
|
9203
|
-
style: {
|
|
9204
|
-
fontSize: 13,
|
|
9205
|
-
fontWeight: "600",
|
|
9206
|
-
fontFamily: "Inter, sans-serif",
|
|
9207
|
-
// rpeColor returns a concrete hex (native-safe); only the tertiary
|
|
9208
|
-
// fallback needs the className token.
|
|
9209
|
-
color: rpe != null ? rpeColor(rpe) : void 0
|
|
9210
|
-
},
|
|
9211
|
-
children: rpe != null ? roundRpe(rpe) : "\u2014"
|
|
9212
|
-
}
|
|
9213
|
-
)
|
|
9214
|
-
}
|
|
9215
|
-
),
|
|
9216
|
-
prBadges && prBadges.length > 0 && /* @__PURE__ */ jsx(
|
|
9217
|
-
reactNative.View,
|
|
9218
|
-
{
|
|
9219
|
-
className: "flex-row items-center",
|
|
9220
|
-
style: { gap: 4 },
|
|
9221
|
-
testID: "set-row-pr-badges",
|
|
9222
|
-
children: prBadges.map((badge, i) => /* @__PURE__ */ jsx(
|
|
9223
|
-
PrBadge,
|
|
9224
|
-
{
|
|
9225
|
-
type: badge.type,
|
|
9226
|
-
label: badge.label,
|
|
9227
|
-
animate: false,
|
|
9228
|
-
compact: true
|
|
9229
|
-
},
|
|
9230
|
-
i
|
|
9231
|
-
))
|
|
9232
|
-
}
|
|
9233
|
-
),
|
|
9234
|
-
velocities && velocities.length > 0 && /* @__PURE__ */ jsx(
|
|
9235
|
-
reactNative.View,
|
|
9236
|
-
{
|
|
9237
|
-
style: { position: "absolute", bottom: 0, left: 8, right: 8 },
|
|
9238
|
-
testID: "set-row-velocity-strip",
|
|
9239
|
-
children: /* @__PURE__ */ jsx(
|
|
9240
|
-
VelocityStrip,
|
|
9241
|
-
{
|
|
9242
|
-
velocities,
|
|
9243
|
-
zones: velocityZones,
|
|
9244
|
-
liveRepIndex: velocityLiveRepIndex,
|
|
9245
|
-
expanded: velocityExpanded,
|
|
9246
|
-
onToggle: onVelocityToggle,
|
|
9247
|
-
variant: onVelocityToggle ? "full" : "mini"
|
|
9248
|
-
}
|
|
9249
|
-
)
|
|
9250
|
-
}
|
|
9251
|
-
)
|
|
9663
|
+
/* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row items-center", style: { justifyContent: "space-between" }, children: [
|
|
9664
|
+
/* @__PURE__ */ jsx(Cell, { width: COL.set, testID: "set-row-set-number", children: set.setType ? /* @__PURE__ */ jsx(reactNative.Text, { style: typeBadge, testID: "set-row-type-badge", children: set.setType }) : /* @__PURE__ */ jsx(reactNative.Text, { style: { ...cellText, color: valueColor, fontWeight: live ? "700" : "600" }, children: set.setNumber }) }),
|
|
9665
|
+
/* @__PURE__ */ jsx(Cell, { width: COL.reps, testID: "set-row-reps", children: /* @__PURE__ */ jsx(reactNative.Text, { style: { ...cellText, color: valueColor }, children: displayReps(set) }) }),
|
|
9666
|
+
/* @__PURE__ */ jsx(Cell, { width: COL.load, testID: "set-row-weight", children: /* @__PURE__ */ jsx(reactNative.Text, { style: { ...cellText, color: valueColor }, children: roundWeight(displayWeight(set)) }) }),
|
|
9667
|
+
/* @__PURE__ */ jsx(Cell, { width: COL.rpe, testID: "set-row-rpe", children: /* @__PURE__ */ jsx(reactNative.Text, { style: { ...cellText, color: TEXT_MUTED }, children: rpe != null ? roundRpe(rpe) : "\u2014" }) })
|
|
9668
|
+
] }),
|
|
9669
|
+
/* @__PURE__ */ jsx(reactNative.View, { style: { marginTop: live ? 6 : 4 }, testID: "set-row-strip", children: /* @__PURE__ */ jsx(RowStrip, { set }) })
|
|
9252
9670
|
]
|
|
9253
9671
|
}
|
|
9254
9672
|
);
|
|
@@ -9419,14 +9837,18 @@ function RestTimer({
|
|
|
9419
9837
|
visible,
|
|
9420
9838
|
displayOnly = false
|
|
9421
9839
|
}) {
|
|
9422
|
-
|
|
9423
|
-
|
|
9840
|
+
const {
|
|
9841
|
+
remainingMs,
|
|
9842
|
+
progress,
|
|
9843
|
+
label: timeDisplay
|
|
9844
|
+
} = useTimer({
|
|
9845
|
+
mode: "down",
|
|
9846
|
+
durationMs: totalSeconds * 1e3,
|
|
9847
|
+
elapsedMs
|
|
9848
|
+
});
|
|
9424
9849
|
const remainingSec = Math.ceil(remainingMs / 1e3);
|
|
9425
|
-
const
|
|
9426
|
-
|
|
9427
|
-
const timeDisplay = `${minutes}:${String(seconds).padStart(2, "0")}`;
|
|
9428
|
-
const totalMs = totalSeconds * 1e3;
|
|
9429
|
-
const progressPct = totalMs > 0 ? Math.min(100, elapsedMs / totalMs * 100) : 100;
|
|
9850
|
+
const progressPct = progress * 100;
|
|
9851
|
+
if (!visible) return null;
|
|
9430
9852
|
return /* @__PURE__ */ jsxs(
|
|
9431
9853
|
reactNative.View,
|
|
9432
9854
|
{
|
|
@@ -9584,15 +10006,319 @@ function RestTimer({
|
|
|
9584
10006
|
}
|
|
9585
10007
|
);
|
|
9586
10008
|
}
|
|
9587
|
-
|
|
9588
|
-
|
|
10009
|
+
var t8 = getSemanticColors("dark");
|
|
10010
|
+
var TIMES = "\xD7";
|
|
10011
|
+
var AT = "@";
|
|
10012
|
+
function SetsRepsLoad({
|
|
10013
|
+
sets,
|
|
10014
|
+
reps,
|
|
10015
|
+
load,
|
|
10016
|
+
unit = "lb",
|
|
10017
|
+
className,
|
|
10018
|
+
...props
|
|
10019
|
+
}) {
|
|
10020
|
+
const value = t8["text-primary"];
|
|
10021
|
+
const sep = t8["text-tertiary"];
|
|
10022
|
+
return /* @__PURE__ */ jsxs(
|
|
10023
|
+
reactNative.View,
|
|
10024
|
+
{
|
|
10025
|
+
className,
|
|
10026
|
+
style: { flexDirection: "row", alignItems: "baseline" },
|
|
10027
|
+
accessibilityLabel: `${sets} sets of ${reps} reps at ${load} ${unit}`,
|
|
10028
|
+
testID: "sets-reps-load",
|
|
10029
|
+
...props,
|
|
10030
|
+
children: [
|
|
10031
|
+
/* @__PURE__ */ jsx(MetricCell, { color: value, children: sets }),
|
|
10032
|
+
/* @__PURE__ */ jsx(MetricCell, { color: sep, children: ` ${TIMES} ` }),
|
|
10033
|
+
/* @__PURE__ */ jsx(MetricCell, { color: value, children: reps }),
|
|
10034
|
+
/* @__PURE__ */ jsx(MetricCell, { color: sep, children: ` ${AT} ` }),
|
|
10035
|
+
/* @__PURE__ */ jsx(MetricCell, { color: value, children: load }),
|
|
10036
|
+
/* @__PURE__ */ jsx(MetricCell, { color: sep, children: ` ${unit}` })
|
|
10037
|
+
]
|
|
10038
|
+
}
|
|
10039
|
+
);
|
|
9589
10040
|
}
|
|
9590
|
-
|
|
9591
|
-
|
|
9592
|
-
|
|
9593
|
-
|
|
9594
|
-
|
|
9595
|
-
|
|
10041
|
+
|
|
10042
|
+
// src/components/custom/Workout/paceTone.ts
|
|
10043
|
+
function paceTone(progress, target) {
|
|
10044
|
+
if (target === void 0) return "neutral";
|
|
10045
|
+
return progress >= target ? "ahead" : "behind";
|
|
10046
|
+
}
|
|
10047
|
+
var PACE_TONE_COLORS = {
|
|
10048
|
+
ahead: getSemanticColors("dark")["status-success"],
|
|
10049
|
+
behind: getSemanticColors("dark")["status-warning"],
|
|
10050
|
+
neutral: primitiveRamps.cyan[400]
|
|
10051
|
+
};
|
|
10052
|
+
function paceToneColor(tone) {
|
|
10053
|
+
return PACE_TONE_COLORS[tone];
|
|
10054
|
+
}
|
|
10055
|
+
|
|
10056
|
+
// src/components/custom/Workout/SegmentedProgressBar.tsx
|
|
10057
|
+
var DEFAULT_MARKER_COLOR = primitiveRamps.green[500];
|
|
10058
|
+
function SegmentedProgressBar({
|
|
10059
|
+
segments,
|
|
10060
|
+
value,
|
|
10061
|
+
target,
|
|
10062
|
+
markerColor = DEFAULT_MARKER_COLOR,
|
|
10063
|
+
color,
|
|
10064
|
+
height,
|
|
10065
|
+
gap,
|
|
10066
|
+
...props
|
|
10067
|
+
}) {
|
|
10068
|
+
const total = segments.reduce((sum, s) => sum + s.weight, 0);
|
|
10069
|
+
const fillColor = color ?? paceToneColor(paceTone(total > 0 ? value / total : 0, target));
|
|
10070
|
+
const barSegments = segments.map((seg, i) => {
|
|
10071
|
+
const cumulativeBefore = segments.slice(0, i).reduce((sum, s) => sum + s.weight, 0);
|
|
10072
|
+
const fill = Math.max(0, Math.min(1, (value - cumulativeBefore) / seg.weight));
|
|
10073
|
+
return { weight: seg.weight, fill, color: fillColor };
|
|
10074
|
+
});
|
|
10075
|
+
return /* @__PURE__ */ jsx(
|
|
10076
|
+
SegmentedBar,
|
|
10077
|
+
{
|
|
10078
|
+
segments: barSegments,
|
|
10079
|
+
marker: target != null ? { position: target, color: markerColor } : null,
|
|
10080
|
+
height,
|
|
10081
|
+
gap,
|
|
10082
|
+
...props
|
|
10083
|
+
}
|
|
10084
|
+
);
|
|
10085
|
+
}
|
|
10086
|
+
|
|
10087
|
+
// src/components/custom/Workout/MetricTiles.tsx
|
|
10088
|
+
function MetricTiles({ metrics, gap = 1, ...props }) {
|
|
10089
|
+
return /* @__PURE__ */ jsx(HStack, { gap, align: "stretch", ...props, children: metrics.map((metric) => /* @__PURE__ */ jsx(
|
|
10090
|
+
Tile,
|
|
10091
|
+
{
|
|
10092
|
+
label: metric.label,
|
|
10093
|
+
value: metric.value,
|
|
10094
|
+
valueColor: metric.valueColor
|
|
10095
|
+
},
|
|
10096
|
+
metric.label
|
|
10097
|
+
)) });
|
|
10098
|
+
}
|
|
10099
|
+
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
10100
|
+
var SOON_ACCENT = primitiveRamps.orange[400];
|
|
10101
|
+
function compactUntil(ms) {
|
|
10102
|
+
if (ms <= 0) return "now";
|
|
10103
|
+
const min = Math.round(ms / 6e4);
|
|
10104
|
+
if (min < 60) return `${min}m`;
|
|
10105
|
+
const hr = Math.round(min / 60);
|
|
10106
|
+
if (hr < 24) return `${hr}h`;
|
|
10107
|
+
const d = Math.round(hr / 24);
|
|
10108
|
+
if (d < 7) return `${d}d`;
|
|
10109
|
+
return `${Math.round(d / 7)}w`;
|
|
10110
|
+
}
|
|
10111
|
+
function ScheduleTiles({ when, now, gap = 1, ...props }) {
|
|
10112
|
+
const [mountNow] = React24.useState(() => Date.now());
|
|
10113
|
+
const reference = now ?? mountNow;
|
|
10114
|
+
const whenMs = when instanceof Date ? when.getTime() : when;
|
|
10115
|
+
const soon = whenMs - reference < DAY_MS;
|
|
10116
|
+
const time = new Date(whenMs).toLocaleTimeString(void 0, {
|
|
10117
|
+
hour: "numeric",
|
|
10118
|
+
minute: "2-digit",
|
|
10119
|
+
hour12: true
|
|
10120
|
+
});
|
|
10121
|
+
return /* @__PURE__ */ jsxs(HStack, { gap, ...props, children: [
|
|
10122
|
+
/* @__PURE__ */ jsx(Tile, { label: "Date", value: formatDateTime(when, "short") }),
|
|
10123
|
+
/* @__PURE__ */ jsx(Tile, { label: "Time", value: time }),
|
|
10124
|
+
/* @__PURE__ */ jsx(
|
|
10125
|
+
Tile,
|
|
10126
|
+
{
|
|
10127
|
+
label: "Until",
|
|
10128
|
+
value: compactUntil(whenMs - reference),
|
|
10129
|
+
valueColor: soon ? SOON_ACCENT : void 0
|
|
10130
|
+
}
|
|
10131
|
+
)
|
|
10132
|
+
] });
|
|
10133
|
+
}
|
|
10134
|
+
var t9 = getSemanticColors("dark");
|
|
10135
|
+
var INDICATOR_CONFIG = {
|
|
10136
|
+
imbalance: { Icon: ScaleIcon, color: t9["status-error"], label: "Left/right imbalance" },
|
|
10137
|
+
overshoot: { Icon: AlertTriangleIcon, color: t9["status-error"], label: "Load overshoot" },
|
|
10138
|
+
"velocity-loss": { Icon: TrendingDownIcon, color: t9["status-warning"], label: "Velocity loss" },
|
|
10139
|
+
"missed-reps": { Icon: CircleSlashIcon, color: t9["status-warning"], label: "Missed reps" },
|
|
10140
|
+
pr: { Icon: AwardIcon, color: t9["status-success"], label: "Personal record" },
|
|
10141
|
+
info: { Icon: InfoIcon, color: t9["status-info"], label: "More info" }
|
|
10142
|
+
};
|
|
10143
|
+
var INDICATOR_PRECEDENCE = [
|
|
10144
|
+
"imbalance",
|
|
10145
|
+
"overshoot",
|
|
10146
|
+
"velocity-loss",
|
|
10147
|
+
"missed-reps",
|
|
10148
|
+
"pr",
|
|
10149
|
+
"info"
|
|
10150
|
+
];
|
|
10151
|
+
function resolveIndicator(candidates) {
|
|
10152
|
+
return INDICATOR_PRECEDENCE.find((kind) => candidates.includes(kind));
|
|
10153
|
+
}
|
|
10154
|
+
function ExerciseIndicator({ kind, onPress, className, ...props }) {
|
|
10155
|
+
const { Icon, color, label } = INDICATOR_CONFIG[kind];
|
|
10156
|
+
const chip = /* @__PURE__ */ jsx(
|
|
10157
|
+
reactNative.View,
|
|
10158
|
+
{
|
|
10159
|
+
className,
|
|
10160
|
+
style: {
|
|
10161
|
+
width: 16,
|
|
10162
|
+
height: 16,
|
|
10163
|
+
borderRadius: 8,
|
|
10164
|
+
borderWidth: 1,
|
|
10165
|
+
borderColor: color,
|
|
10166
|
+
alignItems: "center",
|
|
10167
|
+
justifyContent: "center"
|
|
10168
|
+
},
|
|
10169
|
+
accessibilityRole: "image",
|
|
10170
|
+
accessibilityLabel: label,
|
|
10171
|
+
testID: "exercise-indicator",
|
|
10172
|
+
...props,
|
|
10173
|
+
children: /* @__PURE__ */ jsx(Icon, { size: 11, color })
|
|
10174
|
+
}
|
|
10175
|
+
);
|
|
10176
|
+
if (onPress) {
|
|
10177
|
+
return /* @__PURE__ */ jsx(reactNative.Pressable, { onPress, accessibilityRole: "button", accessibilityLabel: label, children: chip });
|
|
10178
|
+
}
|
|
10179
|
+
return chip;
|
|
10180
|
+
}
|
|
10181
|
+
function headingLabel(name, sets, reps, load, unit) {
|
|
10182
|
+
return `${name}, ${sets}\xD7${reps} @ ${roundWeight(load)} ${unit}`;
|
|
10183
|
+
}
|
|
10184
|
+
function ExerciseHeading({
|
|
10185
|
+
name,
|
|
10186
|
+
sets,
|
|
10187
|
+
reps,
|
|
10188
|
+
load,
|
|
10189
|
+
unit = "lbs",
|
|
10190
|
+
tempo,
|
|
10191
|
+
indicator,
|
|
10192
|
+
dimmed,
|
|
10193
|
+
onPress
|
|
10194
|
+
}) {
|
|
10195
|
+
return /* @__PURE__ */ jsxs(reactNative.View, { style: { opacity: dimmed ? 0.55 : 1 }, testID: "exercise-heading", children: [
|
|
10196
|
+
/* @__PURE__ */ jsxs(
|
|
10197
|
+
reactNative.Pressable,
|
|
10198
|
+
{
|
|
10199
|
+
onPress,
|
|
10200
|
+
accessibilityRole: "button",
|
|
10201
|
+
accessibilityLabel: headingLabel(name, sets, reps, load, unit),
|
|
10202
|
+
className: "flex-row items-center",
|
|
10203
|
+
style: { gap: 8 },
|
|
10204
|
+
testID: "exercise-card-header",
|
|
10205
|
+
children: [
|
|
10206
|
+
/* @__PURE__ */ jsx(
|
|
10207
|
+
reactNative.Text,
|
|
10208
|
+
{
|
|
10209
|
+
className: "text-text-primary",
|
|
10210
|
+
style: { fontSize: 14, fontFamily: '"Space Grotesk", sans-serif', fontWeight: "700" },
|
|
10211
|
+
testID: "exercise-card-name",
|
|
10212
|
+
children: name
|
|
10213
|
+
}
|
|
10214
|
+
),
|
|
10215
|
+
/* @__PURE__ */ jsx(reactNative.View, { className: "flex-1" }),
|
|
10216
|
+
indicator && /* @__PURE__ */ jsx(ExerciseIndicator, { kind: indicator })
|
|
10217
|
+
]
|
|
10218
|
+
}
|
|
10219
|
+
),
|
|
10220
|
+
/* @__PURE__ */ jsxs(
|
|
10221
|
+
reactNative.View,
|
|
10222
|
+
{
|
|
10223
|
+
className: "flex-row items-center",
|
|
10224
|
+
style: { gap: 8, marginTop: 1 },
|
|
10225
|
+
testID: "exercise-card-summary",
|
|
10226
|
+
children: [
|
|
10227
|
+
/* @__PURE__ */ jsx(SetsRepsLoad, { sets, reps, load, unit }),
|
|
10228
|
+
/* @__PURE__ */ jsx(reactNative.View, { className: "flex-1" }),
|
|
10229
|
+
tempo && /* @__PURE__ */ jsx(TempoDisplay, { tempo, size: "sm", showLabel: false, showInfo: false })
|
|
10230
|
+
]
|
|
10231
|
+
}
|
|
10232
|
+
)
|
|
10233
|
+
] });
|
|
10234
|
+
}
|
|
10235
|
+
function ExerciseCardHeading({
|
|
10236
|
+
setStates,
|
|
10237
|
+
stripHeight = 8,
|
|
10238
|
+
dimmed,
|
|
10239
|
+
testID = "exercise-card",
|
|
10240
|
+
...heading
|
|
10241
|
+
}) {
|
|
10242
|
+
return /* @__PURE__ */ jsxs(
|
|
10243
|
+
reactNative.View,
|
|
10244
|
+
{
|
|
10245
|
+
style: { paddingVertical: 9, paddingHorizontal: 12, opacity: dimmed ? 0.55 : 1 },
|
|
10246
|
+
testID,
|
|
10247
|
+
children: [
|
|
10248
|
+
/* @__PURE__ */ jsx(ExerciseHeading, { ...heading }),
|
|
10249
|
+
setStates.length > 0 && /* @__PURE__ */ jsx(reactNative.View, { style: { marginTop: 7 }, testID: "exercise-card-strip", children: /* @__PURE__ */ jsx(SetStrip, { sets: setStates, height: stripHeight }) })
|
|
10250
|
+
]
|
|
10251
|
+
}
|
|
10252
|
+
);
|
|
10253
|
+
}
|
|
10254
|
+
var PREV_COLUMN = { label: "PREV", width: void 0 };
|
|
10255
|
+
function buildColumns(unit, showPrevious) {
|
|
10256
|
+
return [
|
|
10257
|
+
{ label: "SET", width: 36 },
|
|
10258
|
+
...showPrevious ? [PREV_COLUMN] : [],
|
|
10259
|
+
{ label: "REPS", width: 44 },
|
|
10260
|
+
{ label: unit.toUpperCase(), width: 56 },
|
|
10261
|
+
{ label: "RPE", width: 36 }
|
|
10262
|
+
];
|
|
10263
|
+
}
|
|
10264
|
+
function SetTableHeader({
|
|
10265
|
+
unit = "lbs",
|
|
10266
|
+
showPrevious = true,
|
|
10267
|
+
testID = "table-header"
|
|
10268
|
+
}) {
|
|
10269
|
+
const columns = buildColumns(unit, showPrevious);
|
|
10270
|
+
return /* @__PURE__ */ jsx(
|
|
10271
|
+
reactNative.View,
|
|
10272
|
+
{
|
|
10273
|
+
className: "flex-row",
|
|
10274
|
+
style: {
|
|
10275
|
+
paddingVertical: 8,
|
|
10276
|
+
paddingHorizontal: 8,
|
|
10277
|
+
paddingBottom: 4,
|
|
10278
|
+
...showPrevious ? {} : { justifyContent: "space-between" }
|
|
10279
|
+
},
|
|
10280
|
+
testID,
|
|
10281
|
+
children: columns.map(({ label, width }) => {
|
|
10282
|
+
const isFlex = width === void 0;
|
|
10283
|
+
return /* @__PURE__ */ jsx(
|
|
10284
|
+
reactNative.View,
|
|
10285
|
+
{
|
|
10286
|
+
style: {
|
|
10287
|
+
...isFlex ? { minWidth: 44 } : { width, flexShrink: 1 },
|
|
10288
|
+
alignItems: "center",
|
|
10289
|
+
justifyContent: "center",
|
|
10290
|
+
gap: 8
|
|
10291
|
+
},
|
|
10292
|
+
...isFlex ? { className: "flex-1" } : {},
|
|
10293
|
+
children: /* @__PURE__ */ jsx(
|
|
10294
|
+
reactNative.Text,
|
|
10295
|
+
{
|
|
10296
|
+
className: "text-text-tertiary",
|
|
10297
|
+
style: {
|
|
10298
|
+
fontSize: 10,
|
|
10299
|
+
fontWeight: "600",
|
|
10300
|
+
fontFamily: "Inter, sans-serif",
|
|
10301
|
+
textTransform: "uppercase",
|
|
10302
|
+
letterSpacing: 0.8
|
|
10303
|
+
},
|
|
10304
|
+
children: label
|
|
10305
|
+
}
|
|
10306
|
+
)
|
|
10307
|
+
},
|
|
10308
|
+
label
|
|
10309
|
+
);
|
|
10310
|
+
})
|
|
10311
|
+
}
|
|
10312
|
+
);
|
|
10313
|
+
}
|
|
10314
|
+
|
|
10315
|
+
// src/components/custom/Workout/ExerciseCard.tsx
|
|
10316
|
+
function getSupersetBorderRadius(position) {
|
|
10317
|
+
switch (position) {
|
|
10318
|
+
case "first":
|
|
10319
|
+
return {
|
|
10320
|
+
borderTopLeftRadius: 12,
|
|
10321
|
+
borderTopRightRadius: 12,
|
|
9596
10322
|
borderBottomLeftRadius: 8,
|
|
9597
10323
|
borderBottomRightRadius: 8
|
|
9598
10324
|
};
|
|
@@ -9629,7 +10355,7 @@ function formatSummary(summary) {
|
|
|
9629
10355
|
if (!summary) return "";
|
|
9630
10356
|
return `${summary.sets}\xD7${summary.reps} @ ${roundWeight(summary.weight)} ${summary.unit}`;
|
|
9631
10357
|
}
|
|
9632
|
-
function
|
|
10358
|
+
function formatAccessibilityLabel(name, summary, prescription) {
|
|
9633
10359
|
if (summary) return `${name}, ${formatSummary(summary)}`;
|
|
9634
10360
|
if (prescription) return `${name}, ${prescription}`;
|
|
9635
10361
|
return name;
|
|
@@ -9638,7 +10364,6 @@ function CollapsedCard({
|
|
|
9638
10364
|
name,
|
|
9639
10365
|
onToggle,
|
|
9640
10366
|
summary,
|
|
9641
|
-
e1rm,
|
|
9642
10367
|
isPR,
|
|
9643
10368
|
setVelocities,
|
|
9644
10369
|
velocityZones,
|
|
@@ -9657,7 +10382,7 @@ function CollapsedCard({
|
|
|
9657
10382
|
onPressIn: () => setPressed(true),
|
|
9658
10383
|
onPressOut: () => setPressed(false),
|
|
9659
10384
|
accessibilityRole: "button",
|
|
9660
|
-
accessibilityLabel:
|
|
10385
|
+
accessibilityLabel: formatAccessibilityLabel(name, summary, void 0),
|
|
9661
10386
|
testID: "exercise-card",
|
|
9662
10387
|
children: /* @__PURE__ */ jsxs(
|
|
9663
10388
|
reactNative.View,
|
|
@@ -9671,100 +10396,93 @@ function CollapsedCard({
|
|
|
9671
10396
|
...supersetMargin
|
|
9672
10397
|
},
|
|
9673
10398
|
children: [
|
|
9674
|
-
/* @__PURE__ */ jsxs(
|
|
9675
|
-
|
|
9676
|
-
|
|
9677
|
-
|
|
9678
|
-
|
|
9679
|
-
|
|
9680
|
-
|
|
9681
|
-
|
|
9682
|
-
|
|
9683
|
-
|
|
9684
|
-
|
|
9685
|
-
|
|
9686
|
-
|
|
9687
|
-
|
|
9688
|
-
|
|
9689
|
-
|
|
9690
|
-
|
|
9691
|
-
|
|
9692
|
-
|
|
9693
|
-
|
|
9694
|
-
|
|
9695
|
-
|
|
9696
|
-
|
|
9697
|
-
|
|
9698
|
-
|
|
9699
|
-
|
|
9700
|
-
|
|
9701
|
-
|
|
9702
|
-
|
|
9703
|
-
|
|
9704
|
-
|
|
9705
|
-
|
|
9706
|
-
|
|
9707
|
-
|
|
9708
|
-
|
|
9709
|
-
|
|
9710
|
-
|
|
9711
|
-
|
|
9712
|
-
|
|
9713
|
-
|
|
9714
|
-
|
|
9715
|
-
|
|
9716
|
-
|
|
9717
|
-
|
|
9718
|
-
|
|
9719
|
-
|
|
9720
|
-
|
|
9721
|
-
|
|
9722
|
-
|
|
9723
|
-
{
|
|
9724
|
-
className: "flex-row",
|
|
9725
|
-
style: { marginTop: 6, gap: 4 },
|
|
9726
|
-
testID: "exercise-card-strips",
|
|
9727
|
-
children: [
|
|
9728
|
-
setVelocities?.map((velocities, i) => /* @__PURE__ */ jsx(
|
|
9729
|
-
VelocityStrip,
|
|
9730
|
-
{
|
|
9731
|
-
velocities,
|
|
9732
|
-
zones: velocityZones,
|
|
9733
|
-
variant: "mini",
|
|
9734
|
-
testID: `exercise-card-velocity-strip-${i}`
|
|
9735
|
-
},
|
|
9736
|
-
i
|
|
9737
|
-
)),
|
|
9738
|
-
Array.from({ length: remaining }, (_, i) => /* @__PURE__ */ jsx(
|
|
9739
|
-
PlaceholderStrip,
|
|
9740
|
-
{
|
|
9741
|
-
testID: `exercise-card-placeholder-${i}`
|
|
9742
|
-
},
|
|
9743
|
-
`placeholder-${i}`
|
|
9744
|
-
))
|
|
9745
|
-
]
|
|
9746
|
-
}
|
|
9747
|
-
)
|
|
10399
|
+
/* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row items-center", testID: "exercise-card-header", children: [
|
|
10400
|
+
/* @__PURE__ */ jsx(
|
|
10401
|
+
reactNative.Text,
|
|
10402
|
+
{
|
|
10403
|
+
className: "text-text-primary",
|
|
10404
|
+
style: {
|
|
10405
|
+
fontSize: 14,
|
|
10406
|
+
fontFamily: '"Space Grotesk", sans-serif',
|
|
10407
|
+
fontWeight: "700"
|
|
10408
|
+
},
|
|
10409
|
+
testID: "exercise-card-name",
|
|
10410
|
+
children: name
|
|
10411
|
+
}
|
|
10412
|
+
),
|
|
10413
|
+
/* @__PURE__ */ jsx(reactNative.View, { className: "flex-1" }),
|
|
10414
|
+
summary && /* @__PURE__ */ jsx(
|
|
10415
|
+
reactNative.Text,
|
|
10416
|
+
{
|
|
10417
|
+
className: "text-text-secondary",
|
|
10418
|
+
style: {
|
|
10419
|
+
fontSize: 12,
|
|
10420
|
+
fontFamily: "Inter, sans-serif",
|
|
10421
|
+
marginRight: 8
|
|
10422
|
+
},
|
|
10423
|
+
testID: "exercise-card-summary",
|
|
10424
|
+
children: formatSummary(summary)
|
|
10425
|
+
}
|
|
10426
|
+
),
|
|
10427
|
+
isPR && /* @__PURE__ */ jsx(reactNative.View, { style: { marginLeft: 4 }, children: /* @__PURE__ */ jsx(PrBadge, { type: "e1rm", compact: true, animate: false }) })
|
|
10428
|
+
] }),
|
|
10429
|
+
(completedSets > 0 || remaining > 0) && /* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row", style: { marginTop: 6, gap: 4 }, testID: "exercise-card-strips", children: [
|
|
10430
|
+
setVelocities?.map((velocities, i) => /* @__PURE__ */ jsx(
|
|
10431
|
+
VelocityStrip,
|
|
10432
|
+
{
|
|
10433
|
+
velocities,
|
|
10434
|
+
zones: velocityZones,
|
|
10435
|
+
variant: "mini",
|
|
10436
|
+
testID: `exercise-card-velocity-strip-${i}`
|
|
10437
|
+
},
|
|
10438
|
+
i
|
|
10439
|
+
)),
|
|
10440
|
+
Array.from({ length: remaining }, (_, i) => /* @__PURE__ */ jsx(
|
|
10441
|
+
PlaceholderStrip,
|
|
10442
|
+
{
|
|
10443
|
+
testID: `exercise-card-placeholder-${i}`
|
|
10444
|
+
},
|
|
10445
|
+
`placeholder-${i}`
|
|
10446
|
+
))
|
|
10447
|
+
] })
|
|
9748
10448
|
]
|
|
9749
10449
|
}
|
|
9750
10450
|
)
|
|
9751
10451
|
}
|
|
9752
10452
|
);
|
|
9753
10453
|
}
|
|
10454
|
+
var DARK2 = getSemanticColors("dark");
|
|
10455
|
+
var BODY_DIVIDER = DARK2["border-subtle"];
|
|
10456
|
+
function deriveHeaderSetStates(sets) {
|
|
10457
|
+
return sets.map((set) => {
|
|
10458
|
+
switch (set.state) {
|
|
10459
|
+
case "live":
|
|
10460
|
+
return { status: "active", velocities: set.velocities, planned: set.target.reps };
|
|
10461
|
+
case "done":
|
|
10462
|
+
return { status: "done", velocities: set.velocities };
|
|
10463
|
+
case "todo":
|
|
10464
|
+
return { status: "todo", planned: set.planned ?? set.target.reps };
|
|
10465
|
+
}
|
|
10466
|
+
});
|
|
10467
|
+
}
|
|
9754
10468
|
function ExpandedCard({
|
|
9755
10469
|
name,
|
|
9756
10470
|
onToggle,
|
|
9757
10471
|
summary,
|
|
9758
|
-
e1rm,
|
|
9759
10472
|
isPR,
|
|
9760
10473
|
sets,
|
|
9761
10474
|
tempo,
|
|
10475
|
+
indicator,
|
|
10476
|
+
setStates,
|
|
10477
|
+
stripHeight = 8,
|
|
10478
|
+
velocityZones,
|
|
9762
10479
|
supersetPosition
|
|
9763
10480
|
}) {
|
|
9764
10481
|
const borderRadius = getSupersetBorderRadius(supersetPosition);
|
|
9765
10482
|
const supersetMargin = getSupersetMargin(supersetPosition);
|
|
9766
10483
|
const unit = summary?.unit ?? sets?.[0]?.unit ?? "lbs";
|
|
9767
|
-
const
|
|
10484
|
+
const headerStates = setStates ?? (sets ? deriveHeaderSetStates(sets) : []);
|
|
10485
|
+
const headerIndicator = indicator ?? (isPR ? "pr" : void 0);
|
|
9768
10486
|
return /* @__PURE__ */ jsxs(
|
|
9769
10487
|
reactNative.View,
|
|
9770
10488
|
{
|
|
@@ -9777,114 +10495,32 @@ function ExpandedCard({
|
|
|
9777
10495
|
testID: "exercise-card",
|
|
9778
10496
|
children: [
|
|
9779
10497
|
/* @__PURE__ */ jsx(
|
|
9780
|
-
|
|
10498
|
+
ExerciseCardHeading,
|
|
9781
10499
|
{
|
|
10500
|
+
name,
|
|
10501
|
+
sets: summary?.sets ?? sets?.length ?? 0,
|
|
10502
|
+
reps: summary?.reps ?? 0,
|
|
10503
|
+
load: summary?.weight ?? 0,
|
|
10504
|
+
unit,
|
|
10505
|
+
tempo,
|
|
10506
|
+
indicator: headerIndicator,
|
|
10507
|
+
setStates: headerStates,
|
|
10508
|
+
stripHeight,
|
|
9782
10509
|
onPress: onToggle,
|
|
9783
|
-
|
|
9784
|
-
accessibilityLabel: formatAccessibilityLabel2(name, summary, void 0),
|
|
9785
|
-
"aria-expanded": true,
|
|
9786
|
-
testID: "exercise-card-header",
|
|
9787
|
-
children: /* @__PURE__ */ jsxs(
|
|
9788
|
-
reactNative.View,
|
|
9789
|
-
{
|
|
9790
|
-
className: "flex-row items-center",
|
|
9791
|
-
style: {
|
|
9792
|
-
padding: 12,
|
|
9793
|
-
paddingHorizontal: 14,
|
|
9794
|
-
paddingBottom: 10,
|
|
9795
|
-
borderBottomWidth: 1,
|
|
9796
|
-
borderBottomColor: resolveColor("border-default")
|
|
9797
|
-
},
|
|
9798
|
-
children: [
|
|
9799
|
-
/* @__PURE__ */ jsx(
|
|
9800
|
-
reactNative.Text,
|
|
9801
|
-
{
|
|
9802
|
-
className: "text-text-primary",
|
|
9803
|
-
style: {
|
|
9804
|
-
fontSize: 14,
|
|
9805
|
-
fontFamily: '"Space Grotesk", sans-serif',
|
|
9806
|
-
fontWeight: "700"
|
|
9807
|
-
},
|
|
9808
|
-
testID: "exercise-card-name",
|
|
9809
|
-
children: name
|
|
9810
|
-
}
|
|
9811
|
-
),
|
|
9812
|
-
/* @__PURE__ */ jsx(reactNative.View, { className: "flex-1" }),
|
|
9813
|
-
summary && /* @__PURE__ */ jsx(
|
|
9814
|
-
reactNative.Text,
|
|
9815
|
-
{
|
|
9816
|
-
className: "text-text-secondary",
|
|
9817
|
-
style: {
|
|
9818
|
-
fontSize: 12,
|
|
9819
|
-
fontFamily: "Inter, sans-serif",
|
|
9820
|
-
marginRight: 8
|
|
9821
|
-
},
|
|
9822
|
-
testID: "exercise-card-summary",
|
|
9823
|
-
children: formatSummary(summary)
|
|
9824
|
-
}
|
|
9825
|
-
),
|
|
9826
|
-
e1rm && /* @__PURE__ */ jsx(
|
|
9827
|
-
WeightBadge,
|
|
9828
|
-
{
|
|
9829
|
-
value: roundWeight(e1rm.value),
|
|
9830
|
-
unit: e1rm.unit,
|
|
9831
|
-
size: "sm",
|
|
9832
|
-
showIcon: false,
|
|
9833
|
-
testID: "exercise-card-e1rm"
|
|
9834
|
-
}
|
|
9835
|
-
),
|
|
9836
|
-
isPR && /* @__PURE__ */ jsx(reactNative.View, { style: { marginLeft: 4 }, children: /* @__PURE__ */ jsx(PrBadge, { type: "e1rm", compact: true, animate: false }) })
|
|
9837
|
-
]
|
|
9838
|
-
}
|
|
9839
|
-
)
|
|
10510
|
+
testID: "exercise-card-heading"
|
|
9840
10511
|
}
|
|
9841
10512
|
),
|
|
9842
|
-
|
|
9843
|
-
/* @__PURE__ */ jsx(
|
|
10513
|
+
/* @__PURE__ */ jsxs(
|
|
9844
10514
|
reactNative.View,
|
|
9845
10515
|
{
|
|
9846
|
-
|
|
9847
|
-
|
|
9848
|
-
|
|
9849
|
-
|
|
9850
|
-
|
|
9851
|
-
|
|
9852
|
-
testID: "exercise-card-column-headers",
|
|
9853
|
-
children: columnHeaders.map((header, i) => {
|
|
9854
|
-
const widths = [36, void 0, 44, 56, 36];
|
|
9855
|
-
const width = widths[i];
|
|
9856
|
-
const isFlex = width === void 0;
|
|
9857
|
-
return /* @__PURE__ */ jsx(
|
|
9858
|
-
reactNative.View,
|
|
9859
|
-
{
|
|
9860
|
-
style: {
|
|
9861
|
-
...isFlex ? {} : { width },
|
|
9862
|
-
alignItems: "center",
|
|
9863
|
-
justifyContent: "center",
|
|
9864
|
-
gap: 8
|
|
9865
|
-
},
|
|
9866
|
-
...isFlex ? { className: "flex-1" } : {},
|
|
9867
|
-
children: /* @__PURE__ */ jsx(
|
|
9868
|
-
reactNative.Text,
|
|
9869
|
-
{
|
|
9870
|
-
className: "text-text-tertiary",
|
|
9871
|
-
style: {
|
|
9872
|
-
fontSize: 10,
|
|
9873
|
-
fontWeight: "600",
|
|
9874
|
-
fontFamily: "Inter, sans-serif",
|
|
9875
|
-
textTransform: "uppercase",
|
|
9876
|
-
letterSpacing: 0.8
|
|
9877
|
-
},
|
|
9878
|
-
children: header
|
|
9879
|
-
}
|
|
9880
|
-
)
|
|
9881
|
-
},
|
|
9882
|
-
header
|
|
9883
|
-
);
|
|
9884
|
-
})
|
|
10516
|
+
style: { borderTopWidth: 1, borderTopColor: BODY_DIVIDER, paddingBottom: 6 },
|
|
10517
|
+
testID: "exercise-card-body",
|
|
10518
|
+
children: [
|
|
10519
|
+
/* @__PURE__ */ jsx(SetTableHeader, { unit, showPrevious: false, testID: "exercise-card-column-headers" }),
|
|
10520
|
+
sets && /* @__PURE__ */ jsx(reactNative.View, { testID: "exercise-card-sets", children: sets.map((set, i) => /* @__PURE__ */ jsx(SetRow, { ...set, velocityZones: set.velocityZones ?? velocityZones }, i)) })
|
|
10521
|
+
]
|
|
9885
10522
|
}
|
|
9886
|
-
)
|
|
9887
|
-
sets && /* @__PURE__ */ jsx(reactNative.View, { testID: "exercise-card-sets", children: sets.map((setProps, i) => /* @__PURE__ */ jsx(SetRow, { ...setProps }, i)) })
|
|
10523
|
+
)
|
|
9888
10524
|
]
|
|
9889
10525
|
}
|
|
9890
10526
|
);
|
|
@@ -9893,11 +10529,12 @@ function UpcomingCard({
|
|
|
9893
10529
|
name,
|
|
9894
10530
|
prescription,
|
|
9895
10531
|
previousBest,
|
|
9896
|
-
supersetPosition
|
|
10532
|
+
supersetPosition,
|
|
10533
|
+
onToggle
|
|
9897
10534
|
}) {
|
|
9898
10535
|
const borderRadius = getSupersetBorderRadius(supersetPosition);
|
|
9899
10536
|
const supersetMargin = getSupersetMargin(supersetPosition);
|
|
9900
|
-
return /* @__PURE__ */ jsx(
|
|
10537
|
+
return /* @__PURE__ */ jsx(reactNative.Pressable, { onPress: onToggle, accessibilityRole: "button", children: /* @__PURE__ */ jsx(
|
|
9901
10538
|
reactNative.View,
|
|
9902
10539
|
{
|
|
9903
10540
|
style: {
|
|
@@ -9907,7 +10544,7 @@ function UpcomingCard({
|
|
|
9907
10544
|
...borderRadius,
|
|
9908
10545
|
...supersetMargin
|
|
9909
10546
|
},
|
|
9910
|
-
accessibilityLabel:
|
|
10547
|
+
accessibilityLabel: formatAccessibilityLabel(name, void 0, prescription),
|
|
9911
10548
|
testID: "exercise-card",
|
|
9912
10549
|
children: /* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row items-center", children: [
|
|
9913
10550
|
/* @__PURE__ */ jsx(
|
|
@@ -9915,6 +10552,7 @@ function UpcomingCard({
|
|
|
9915
10552
|
{
|
|
9916
10553
|
className: "text-text-primary",
|
|
9917
10554
|
style: {
|
|
10555
|
+
flexShrink: 0,
|
|
9918
10556
|
fontSize: 14,
|
|
9919
10557
|
fontFamily: '"Space Grotesk", sans-serif',
|
|
9920
10558
|
fontWeight: "700"
|
|
@@ -9927,7 +10565,9 @@ function UpcomingCard({
|
|
|
9927
10565
|
reactNative.Text,
|
|
9928
10566
|
{
|
|
9929
10567
|
className: "text-text-secondary",
|
|
10568
|
+
numberOfLines: 1,
|
|
9930
10569
|
style: {
|
|
10570
|
+
flexShrink: 1,
|
|
9931
10571
|
fontSize: 12,
|
|
9932
10572
|
fontFamily: "Inter, sans-serif",
|
|
9933
10573
|
marginLeft: 8
|
|
@@ -9936,12 +10576,14 @@ function UpcomingCard({
|
|
|
9936
10576
|
children: prescription
|
|
9937
10577
|
}
|
|
9938
10578
|
),
|
|
9939
|
-
/* @__PURE__ */ jsx(reactNative.View, { className: "flex-1" }),
|
|
10579
|
+
/* @__PURE__ */ jsx(reactNative.View, { className: "flex-1", style: { minWidth: 8 } }),
|
|
9940
10580
|
previousBest && /* @__PURE__ */ jsx(
|
|
9941
10581
|
reactNative.Text,
|
|
9942
10582
|
{
|
|
9943
10583
|
className: "text-text-tertiary",
|
|
10584
|
+
numberOfLines: 1,
|
|
9944
10585
|
style: {
|
|
10586
|
+
flexShrink: 1,
|
|
9945
10587
|
fontSize: 11,
|
|
9946
10588
|
fontFamily: "Inter, sans-serif"
|
|
9947
10589
|
},
|
|
@@ -9951,17 +10593,208 @@ function UpcomingCard({
|
|
|
9951
10593
|
)
|
|
9952
10594
|
] })
|
|
9953
10595
|
}
|
|
10596
|
+
) });
|
|
10597
|
+
}
|
|
10598
|
+
function ExerciseCard({
|
|
10599
|
+
upcoming,
|
|
10600
|
+
expanded,
|
|
10601
|
+
defaultExpanded,
|
|
10602
|
+
onExpandedChange,
|
|
10603
|
+
...rest
|
|
10604
|
+
}) {
|
|
10605
|
+
const [internalExpanded, setInternalExpanded] = React24.useState(defaultExpanded ?? false);
|
|
10606
|
+
const isControlled = expanded !== void 0;
|
|
10607
|
+
const isExpanded = isControlled ? expanded : internalExpanded;
|
|
10608
|
+
const onToggle = () => {
|
|
10609
|
+
const next = !isExpanded;
|
|
10610
|
+
onExpandedChange?.(next);
|
|
10611
|
+
if (!isControlled) setInternalExpanded(next);
|
|
10612
|
+
};
|
|
10613
|
+
if (upcoming) return /* @__PURE__ */ jsx(UpcomingCard, { ...rest, onToggle });
|
|
10614
|
+
return isExpanded ? /* @__PURE__ */ jsx(ExpandedCard, { ...rest, onToggle }) : /* @__PURE__ */ jsx(CollapsedCard, { ...rest, onToggle });
|
|
10615
|
+
}
|
|
10616
|
+
var t10 = getSemanticColors("dark");
|
|
10617
|
+
var RAISED = primitiveColors.charcoal[400];
|
|
10618
|
+
var BAR_HEIGHT = 9;
|
|
10619
|
+
var BAR_GAP = 3;
|
|
10620
|
+
function SessionHeader({
|
|
10621
|
+
title,
|
|
10622
|
+
plan,
|
|
10623
|
+
setsDone = 0,
|
|
10624
|
+
elapsedMs,
|
|
10625
|
+
budgetMs,
|
|
10626
|
+
running = true,
|
|
10627
|
+
metrics = [],
|
|
10628
|
+
next,
|
|
10629
|
+
className,
|
|
10630
|
+
style,
|
|
10631
|
+
...props
|
|
10632
|
+
}) {
|
|
10633
|
+
const upcoming = next != null;
|
|
10634
|
+
const totalSets = plan.reduce((sum, e) => sum + e.sets, 0);
|
|
10635
|
+
const hasPace = !upcoming && budgetMs != null && elapsedMs != null;
|
|
10636
|
+
const target = hasPace ? elapsedMs / budgetMs : void 0;
|
|
10637
|
+
const setsLabel = upcoming ? `${totalSets} sets` : `${Math.floor(setsDone)}/${totalSets} sets`;
|
|
10638
|
+
const setsLabelColor = upcoming ? t10["text-secondary"] : paceToneColor(paceTone(totalSets > 0 ? setsDone / totalSets : 0, target));
|
|
10639
|
+
return /* @__PURE__ */ jsxs(
|
|
10640
|
+
reactNative.View,
|
|
10641
|
+
{
|
|
10642
|
+
className,
|
|
10643
|
+
style: [
|
|
10644
|
+
{
|
|
10645
|
+
backgroundColor: RAISED,
|
|
10646
|
+
paddingTop: 11,
|
|
10647
|
+
paddingRight: 12,
|
|
10648
|
+
paddingBottom: 12,
|
|
10649
|
+
paddingLeft: 12,
|
|
10650
|
+
zIndex: 2
|
|
10651
|
+
},
|
|
10652
|
+
style
|
|
10653
|
+
],
|
|
10654
|
+
testID: "session-rail-header",
|
|
10655
|
+
...props,
|
|
10656
|
+
children: [
|
|
10657
|
+
/* @__PURE__ */ jsx(
|
|
10658
|
+
reactNative.Text,
|
|
10659
|
+
{
|
|
10660
|
+
accessibilityRole: "header",
|
|
10661
|
+
style: {
|
|
10662
|
+
fontSize: 16,
|
|
10663
|
+
lineHeight: 18,
|
|
10664
|
+
fontWeight: "700",
|
|
10665
|
+
fontFamily: '"Space Grotesk", sans-serif',
|
|
10666
|
+
color: t10["text-primary"],
|
|
10667
|
+
marginBottom: 10
|
|
10668
|
+
},
|
|
10669
|
+
testID: "session-rail-title",
|
|
10670
|
+
children: title
|
|
10671
|
+
}
|
|
10672
|
+
),
|
|
10673
|
+
upcoming ? /* @__PURE__ */ jsx(ScheduleTiles, { when: next }) : /* @__PURE__ */ jsx(MetricTiles, { metrics }),
|
|
10674
|
+
/* @__PURE__ */ jsxs(reactNative.View, { style: { marginTop: 10 }, children: [
|
|
10675
|
+
/* @__PURE__ */ jsx(
|
|
10676
|
+
SegmentedProgressBar,
|
|
10677
|
+
{
|
|
10678
|
+
segments: plan.map((e) => ({ weight: e.sets })),
|
|
10679
|
+
value: upcoming ? 0 : setsDone,
|
|
10680
|
+
target,
|
|
10681
|
+
height: BAR_HEIGHT,
|
|
10682
|
+
gap: BAR_GAP
|
|
10683
|
+
}
|
|
10684
|
+
),
|
|
10685
|
+
/* @__PURE__ */ jsxs(
|
|
10686
|
+
reactNative.View,
|
|
10687
|
+
{
|
|
10688
|
+
style: {
|
|
10689
|
+
flexDirection: "row",
|
|
10690
|
+
justifyContent: "space-between",
|
|
10691
|
+
alignItems: "baseline",
|
|
10692
|
+
marginTop: 5
|
|
10693
|
+
},
|
|
10694
|
+
children: [
|
|
10695
|
+
/* @__PURE__ */ jsx(
|
|
10696
|
+
Typography,
|
|
10697
|
+
{
|
|
10698
|
+
variant: "mono",
|
|
10699
|
+
style: { fontSize: 10, color: setsLabelColor },
|
|
10700
|
+
testID: "session-rail-sets",
|
|
10701
|
+
children: setsLabel
|
|
10702
|
+
}
|
|
10703
|
+
),
|
|
10704
|
+
/* @__PURE__ */ jsx(
|
|
10705
|
+
TimerReadout,
|
|
10706
|
+
{
|
|
10707
|
+
mode: "up",
|
|
10708
|
+
elapsedMs: upcoming ? budgetMs : elapsedMs,
|
|
10709
|
+
durationMs: budgetMs,
|
|
10710
|
+
running: running && !upcoming,
|
|
10711
|
+
showTotal: budgetMs != null && !upcoming
|
|
10712
|
+
}
|
|
10713
|
+
)
|
|
10714
|
+
]
|
|
10715
|
+
}
|
|
10716
|
+
)
|
|
10717
|
+
] })
|
|
10718
|
+
]
|
|
10719
|
+
}
|
|
9954
10720
|
);
|
|
9955
10721
|
}
|
|
9956
|
-
|
|
9957
|
-
|
|
9958
|
-
|
|
9959
|
-
|
|
9960
|
-
|
|
9961
|
-
|
|
9962
|
-
|
|
9963
|
-
|
|
9964
|
-
|
|
10722
|
+
var INSET = primitiveColors.charcoal[800];
|
|
10723
|
+
var DIVIDER = primitiveColors.charcoal[500];
|
|
10724
|
+
var DEFAULT_WIDTH = 246;
|
|
10725
|
+
function SessionRail({
|
|
10726
|
+
title,
|
|
10727
|
+
exercises,
|
|
10728
|
+
setsDone,
|
|
10729
|
+
elapsedMs,
|
|
10730
|
+
budgetMs,
|
|
10731
|
+
running,
|
|
10732
|
+
metrics,
|
|
10733
|
+
next,
|
|
10734
|
+
stripHeight = 8,
|
|
10735
|
+
width = DEFAULT_WIDTH,
|
|
10736
|
+
onExercisePress,
|
|
10737
|
+
className,
|
|
10738
|
+
style,
|
|
10739
|
+
...props
|
|
10740
|
+
}) {
|
|
10741
|
+
return /* @__PURE__ */ jsxs(
|
|
10742
|
+
reactNative.View,
|
|
10743
|
+
{
|
|
10744
|
+
className,
|
|
10745
|
+
style: [{ width, flexDirection: "column", backgroundColor: INSET }, style],
|
|
10746
|
+
testID: "session-rail",
|
|
10747
|
+
...props,
|
|
10748
|
+
children: [
|
|
10749
|
+
/* @__PURE__ */ jsx(
|
|
10750
|
+
SessionHeader,
|
|
10751
|
+
{
|
|
10752
|
+
title,
|
|
10753
|
+
plan: exercises.map((ex) => ({ name: ex.name, sets: ex.summary.sets })),
|
|
10754
|
+
setsDone,
|
|
10755
|
+
elapsedMs,
|
|
10756
|
+
budgetMs,
|
|
10757
|
+
running,
|
|
10758
|
+
metrics,
|
|
10759
|
+
next
|
|
10760
|
+
}
|
|
10761
|
+
),
|
|
10762
|
+
/* @__PURE__ */ jsx(
|
|
10763
|
+
reactNative.View,
|
|
10764
|
+
{
|
|
10765
|
+
style: [{ flex: 1, backgroundColor: INSET }, neumorphicShadows.charcoal.pressed.subtle],
|
|
10766
|
+
testID: "session-rail-list",
|
|
10767
|
+
children: exercises.map((ex, i) => /* @__PURE__ */ jsx(
|
|
10768
|
+
reactNative.View,
|
|
10769
|
+
{
|
|
10770
|
+
style: {
|
|
10771
|
+
borderBottomWidth: i < exercises.length - 1 ? 1 : 0,
|
|
10772
|
+
borderBottomColor: DIVIDER
|
|
10773
|
+
},
|
|
10774
|
+
children: /* @__PURE__ */ jsx(
|
|
10775
|
+
ExerciseCardHeading,
|
|
10776
|
+
{
|
|
10777
|
+
name: ex.name,
|
|
10778
|
+
sets: ex.summary.sets,
|
|
10779
|
+
reps: ex.summary.reps,
|
|
10780
|
+
load: ex.summary.weight,
|
|
10781
|
+
unit: ex.summary.unit,
|
|
10782
|
+
tempo: ex.tempo,
|
|
10783
|
+
indicator: ex.indicator,
|
|
10784
|
+
setStates: ex.setStates,
|
|
10785
|
+
stripHeight,
|
|
10786
|
+
dimmed: ex.upcoming,
|
|
10787
|
+
onPress: () => onExercisePress?.(ex, i)
|
|
10788
|
+
}
|
|
10789
|
+
)
|
|
10790
|
+
},
|
|
10791
|
+
ex.id ?? i
|
|
10792
|
+
))
|
|
10793
|
+
}
|
|
10794
|
+
)
|
|
10795
|
+
]
|
|
10796
|
+
}
|
|
10797
|
+
);
|
|
9965
10798
|
}
|
|
9966
10799
|
var DEFAULTS = {
|
|
9967
10800
|
label: "SS",
|
|
@@ -10225,10 +11058,10 @@ function WeekRow({
|
|
|
10225
11058
|
}
|
|
10226
11059
|
);
|
|
10227
11060
|
}
|
|
10228
|
-
var
|
|
11061
|
+
var t11 = getSemanticColors("dark");
|
|
10229
11062
|
var COLORS = {
|
|
10230
|
-
statusSuccess:
|
|
10231
|
-
brandPrimary:
|
|
11063
|
+
statusSuccess: t11["status-success"],
|
|
11064
|
+
brandPrimary: t11["brand-primary"],
|
|
10232
11065
|
borderDefault: "#1F1F1F",
|
|
10233
11066
|
brandPrimarySubtle: "rgba(255,121,0,0.06)"
|
|
10234
11067
|
};
|
|
@@ -10712,12 +11545,12 @@ function PrHistoryModal({
|
|
|
10712
11545
|
}
|
|
10713
11546
|
);
|
|
10714
11547
|
}
|
|
10715
|
-
var
|
|
10716
|
-
var BRAND_PRIMARY9 =
|
|
11548
|
+
var t12 = getSemanticColors("dark");
|
|
11549
|
+
var BRAND_PRIMARY9 = t12["brand-primary"];
|
|
10717
11550
|
var BRAND_PRIMARY_SUBTLE = "rgba(255,121,0,0.12)";
|
|
10718
|
-
var STATUS_SUCCESS =
|
|
10719
|
-
var STATUS_WARNING =
|
|
10720
|
-
var STATUS_ERROR2 =
|
|
11551
|
+
var STATUS_SUCCESS = t12["status-success"];
|
|
11552
|
+
var STATUS_WARNING = t12["status-warning"];
|
|
11553
|
+
var STATUS_ERROR2 = t12["status-error"];
|
|
10721
11554
|
var EMOJI_SETS = {
|
|
10722
11555
|
sleep: ["\u{1F634}", "\u{1F610}", "\u{1F642}", "\u{1F60A}", "\u{1F929}"],
|
|
10723
11556
|
soreness: ["\u{1F635}", "\u{1F623}", "\u{1F610}", "\u{1F642}", "\u{1F4AA}"],
|
|
@@ -10907,13 +11740,13 @@ function ReadinessCheck({
|
|
|
10907
11740
|
)
|
|
10908
11741
|
] }) });
|
|
10909
11742
|
}
|
|
10910
|
-
var
|
|
10911
|
-
var BRAND_PRIMARY10 =
|
|
11743
|
+
var t13 = getSemanticColors("dark");
|
|
11744
|
+
var BRAND_PRIMARY10 = t13["brand-primary"];
|
|
10912
11745
|
var BRAND_PRIMARY_DARK2 = MESO_ACCENT_GRADIENT_DARK;
|
|
10913
11746
|
var BRAND_PRIMARY_LIGHT2 = MESO_ACCENT_GRADIENT_LIGHT;
|
|
10914
|
-
var SUCCESS =
|
|
10915
|
-
var WARNING =
|
|
10916
|
-
var ERROR =
|
|
11747
|
+
var SUCCESS = t13["status-success"];
|
|
11748
|
+
var WARNING = t13["status-warning"];
|
|
11749
|
+
var ERROR = t13["status-error"];
|
|
10917
11750
|
var ACCENT_STOPS2 = [BRAND_PRIMARY_DARK2, BRAND_PRIMARY10, BRAND_PRIMARY_LIGHT2];
|
|
10918
11751
|
var CARD_GRADIENT = `linear-gradient(135deg, ${resolveColor("surface-elevated")} 0%, ${resolveColor("surface-raised")} 100%)`;
|
|
10919
11752
|
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%)";
|
|
@@ -10985,7 +11818,7 @@ function StatusPill({ badge }) {
|
|
|
10985
11818
|
}
|
|
10986
11819
|
);
|
|
10987
11820
|
}
|
|
10988
|
-
function
|
|
11821
|
+
function MetricCell2({ metric }) {
|
|
10989
11822
|
return /* @__PURE__ */ jsxs(reactNative.View, { style: { width: "48%" }, testID: "meso-status-card-metric", children: [
|
|
10990
11823
|
/* @__PURE__ */ jsx(
|
|
10991
11824
|
reactNative.Text,
|
|
@@ -11203,7 +12036,7 @@ function MesoStatusCard({
|
|
|
11203
12036
|
className: "flex-row flex-wrap",
|
|
11204
12037
|
style: { gap: 8 },
|
|
11205
12038
|
testID: "meso-status-card-metrics",
|
|
11206
|
-
children: metrics.map((metric, index) => /* @__PURE__ */ jsx(
|
|
12039
|
+
children: metrics.map((metric, index) => /* @__PURE__ */ jsx(MetricCell2, { metric }, `${metric.label}-${index}`))
|
|
11207
12040
|
}
|
|
11208
12041
|
),
|
|
11209
12042
|
gauges.length > 0 && /* @__PURE__ */ jsx(reactNative.View, { style: { gap: 12 }, testID: "meso-status-card-gauges", children: gauges.map((gauge, index) => /* @__PURE__ */ jsx(Gauge, { gauge }, `${gauge.label}-${index}`)) }),
|
|
@@ -11284,8 +12117,8 @@ var ERROR_PILL_BORDER = "rgba(209,67,67,0.20)";
|
|
|
11284
12117
|
var PLOT_LEFT = 26;
|
|
11285
12118
|
var TOOLTIP_WIDTH = 124;
|
|
11286
12119
|
function timeOf(dateStr) {
|
|
11287
|
-
const
|
|
11288
|
-
return Number.isNaN(
|
|
12120
|
+
const t18 = Date.parse(dateStr);
|
|
12121
|
+
return Number.isNaN(t18) ? 0 : t18;
|
|
11289
12122
|
}
|
|
11290
12123
|
function formatValue(value) {
|
|
11291
12124
|
return `${Math.round(value)}`;
|
|
@@ -11774,10 +12607,10 @@ function StrengthTrendChart({
|
|
|
11774
12607
|
}
|
|
11775
12608
|
);
|
|
11776
12609
|
}
|
|
11777
|
-
var
|
|
11778
|
-
var STATUS_SUCCESS3 =
|
|
11779
|
-
var STATUS_WARNING3 =
|
|
11780
|
-
var STATUS_INFO =
|
|
12610
|
+
var t14 = getSemanticColors("dark");
|
|
12611
|
+
var STATUS_SUCCESS3 = t14["status-success"];
|
|
12612
|
+
var STATUS_WARNING3 = t14["status-warning"];
|
|
12613
|
+
var STATUS_INFO = t14["status-info"];
|
|
11781
12614
|
var DOT_BORDER = "#F3F4F6";
|
|
11782
12615
|
var BAND_FILL = "rgba(46,213,115,0.1)";
|
|
11783
12616
|
var BAND_EDGE = "rgba(46,213,115,0.45)";
|
|
@@ -11820,13 +12653,13 @@ function interpEdge(pixels, x, key) {
|
|
|
11820
12653
|
const a = pixels[i];
|
|
11821
12654
|
const b = pixels[i + 1];
|
|
11822
12655
|
if (x >= a.x && x <= b.x) {
|
|
11823
|
-
const
|
|
11824
|
-
return a[key] +
|
|
12656
|
+
const t18 = b.x === a.x ? 0 : (x - a.x) / (b.x - a.x);
|
|
12657
|
+
return a[key] + t18 * (b[key] - a[key]);
|
|
11825
12658
|
}
|
|
11826
12659
|
}
|
|
11827
12660
|
return pixels[pixels.length - 1][key];
|
|
11828
12661
|
}
|
|
11829
|
-
function
|
|
12662
|
+
function buildColumns2(pixels, step) {
|
|
11830
12663
|
if (pixels.length < 2) return [];
|
|
11831
12664
|
const start = pixels[0].x;
|
|
11832
12665
|
const end = pixels[pixels.length - 1].x;
|
|
@@ -11938,7 +12771,7 @@ function CapacityBandChart({
|
|
|
11938
12771
|
}
|
|
11939
12772
|
const { toX, toY } = scale;
|
|
11940
12773
|
const bandPixels = toPixels(band, toX, toY);
|
|
11941
|
-
const columns =
|
|
12774
|
+
const columns = buildColumns2(bandPixels, COLUMN_STEP);
|
|
11942
12775
|
const topEdge = buildEdges(bandPixels, "yHigh");
|
|
11943
12776
|
const bottomEdge = buildEdges(bandPixels, "yLow");
|
|
11944
12777
|
const lastBandPoint = band[band.length - 1];
|
|
@@ -12013,8 +12846,8 @@ function CapacityBandChart({
|
|
|
12013
12846
|
testID: "capacity-band-chart-reveal",
|
|
12014
12847
|
children: /* @__PURE__ */ jsxs(reactNative.View, { style: { width, height, position: "relative" }, children: [
|
|
12015
12848
|
projection && /* @__PURE__ */ jsxs(reactNative.View, { testID: "capacity-band-chart-projection", children: [
|
|
12016
|
-
renderColumns(
|
|
12017
|
-
renderColumns(
|
|
12849
|
+
renderColumns(buildColumns2(trainingPixels, COLUMN_STEP), PROJECTION_FILL, "capacity-band-chart-projection-fill"),
|
|
12850
|
+
renderColumns(buildColumns2(restPixels, COLUMN_STEP), PROJECTION_FILL, "capacity-band-chart-projection-fill"),
|
|
12018
12851
|
renderEdges(buildEdges(trainingPixels, "yHigh"), STATUS_SUCCESS3, true, "capacity-band-chart-projection-training"),
|
|
12019
12852
|
renderEdges(buildEdges(trainingPixels, "yLow"), STATUS_SUCCESS3, true, "capacity-band-chart-projection-training"),
|
|
12020
12853
|
renderEdges(buildEdges(restPixels, "yHigh"), STATUS_INFO, true, "capacity-band-chart-projection-rest"),
|
|
@@ -12081,72 +12914,546 @@ function CapacityBandChart({
|
|
|
12081
12914
|
return /* @__PURE__ */ jsx(
|
|
12082
12915
|
reactNative.Text,
|
|
12083
12916
|
{
|
|
12084
|
-
className: "text-text-tertiary",
|
|
12917
|
+
className: "text-text-tertiary",
|
|
12918
|
+
style: {
|
|
12919
|
+
position: "absolute",
|
|
12920
|
+
left: toX(point.date) - 14,
|
|
12921
|
+
top: height - PADDING_BOTTOM + 2,
|
|
12922
|
+
width: 28,
|
|
12923
|
+
textAlign: "center",
|
|
12924
|
+
fontSize: 10,
|
|
12925
|
+
fontFamily: "Inter, sans-serif"
|
|
12926
|
+
},
|
|
12927
|
+
accessibilityElementsHidden: true,
|
|
12928
|
+
importantForAccessibility: "no-hide-descendants",
|
|
12929
|
+
testID: "capacity-band-chart-x-label",
|
|
12930
|
+
children: formatDate2(point.date)
|
|
12931
|
+
},
|
|
12932
|
+
`x-${point.date}`
|
|
12933
|
+
);
|
|
12934
|
+
})
|
|
12935
|
+
]
|
|
12936
|
+
}
|
|
12937
|
+
),
|
|
12938
|
+
workouts.map((workout, i) => {
|
|
12939
|
+
const cx = toX(workout.date);
|
|
12940
|
+
const cy = toY(workout.load);
|
|
12941
|
+
const color = DOT_COLORS[workout.status];
|
|
12942
|
+
const dotStyle = {
|
|
12943
|
+
width: DOT_SIZE,
|
|
12944
|
+
height: DOT_SIZE,
|
|
12945
|
+
borderRadius: DOT_SIZE / 2,
|
|
12946
|
+
backgroundColor: color,
|
|
12947
|
+
borderWidth: 2,
|
|
12948
|
+
borderColor: DOT_BORDER
|
|
12949
|
+
};
|
|
12950
|
+
const label = `Workout on ${formatDate2(workout.date)}, load ${workout.load}, ${STATUS_PHRASES[workout.status]}`;
|
|
12951
|
+
return /* @__PURE__ */ jsx(
|
|
12952
|
+
reactNative.Animated.View,
|
|
12953
|
+
{
|
|
12954
|
+
style: {
|
|
12955
|
+
position: "absolute",
|
|
12956
|
+
left: cx - DOT_SIZE / 2,
|
|
12957
|
+
top: cy - DOT_SIZE / 2,
|
|
12958
|
+
transform: [{ scale: dotAnims[i] ?? 1 }]
|
|
12959
|
+
},
|
|
12960
|
+
testID: "capacity-band-chart-dot-wrapper",
|
|
12961
|
+
children: onWorkoutPress ? /* @__PURE__ */ jsx(
|
|
12962
|
+
reactNative.Pressable,
|
|
12963
|
+
{
|
|
12964
|
+
onPress: () => onWorkoutPress(workout),
|
|
12965
|
+
accessibilityRole: "button",
|
|
12966
|
+
accessibilityLabel: label,
|
|
12967
|
+
style: dotStyle,
|
|
12968
|
+
testID: "capacity-band-chart-dot"
|
|
12969
|
+
}
|
|
12970
|
+
) : /* @__PURE__ */ jsx(
|
|
12971
|
+
reactNative.View,
|
|
12972
|
+
{
|
|
12973
|
+
accessibilityElementsHidden: true,
|
|
12974
|
+
importantForAccessibility: "no-hide-descendants",
|
|
12975
|
+
style: dotStyle,
|
|
12976
|
+
testID: "capacity-band-chart-dot"
|
|
12977
|
+
}
|
|
12978
|
+
)
|
|
12979
|
+
},
|
|
12980
|
+
`dot-${workout.date}-${i}`
|
|
12981
|
+
);
|
|
12982
|
+
})
|
|
12983
|
+
]
|
|
12984
|
+
}
|
|
12985
|
+
);
|
|
12986
|
+
}
|
|
12987
|
+
var t15 = getSemanticColors("dark");
|
|
12988
|
+
function glowShadow(color) {
|
|
12989
|
+
return `0 0 5px 1px ${alpha(color, 0.3)}, 0 0 10px 3px ${alpha(color, 0.12)}`;
|
|
12990
|
+
}
|
|
12991
|
+
var DEFAULT_TRACK_COLOR = primitiveColors.charcoal[200];
|
|
12992
|
+
var DEFAULT_MARKER_COLOR2 = primitiveColors.white;
|
|
12993
|
+
var TICK_COLOR = t15["text-tertiary"];
|
|
12994
|
+
var DEFAULT_TRACK_HEIGHT = 14;
|
|
12995
|
+
var DEFAULT_NEEDLE_OVERHANG = 6;
|
|
12996
|
+
var NEEDLE_WIDTH = 4;
|
|
12997
|
+
function fraction(value, min, max) {
|
|
12998
|
+
if (max <= min) return 0;
|
|
12999
|
+
return Math.max(0, Math.min(1, (value - min) / (max - min)));
|
|
13000
|
+
}
|
|
13001
|
+
function pct(frac) {
|
|
13002
|
+
return `${frac * 100}%`;
|
|
13003
|
+
}
|
|
13004
|
+
function ZoneTrack({
|
|
13005
|
+
zones,
|
|
13006
|
+
max,
|
|
13007
|
+
min = 0,
|
|
13008
|
+
marker = null,
|
|
13009
|
+
ticks,
|
|
13010
|
+
band = null,
|
|
13011
|
+
trackHeight = DEFAULT_TRACK_HEIGHT,
|
|
13012
|
+
needleOverhang = DEFAULT_NEEDLE_OVERHANG,
|
|
13013
|
+
trackColor = DEFAULT_TRACK_COLOR,
|
|
13014
|
+
className,
|
|
13015
|
+
style,
|
|
13016
|
+
accessibilityLabel: accessibilityLabel2,
|
|
13017
|
+
...props
|
|
13018
|
+
}) {
|
|
13019
|
+
const isNeedle = marker?.type === "needle";
|
|
13020
|
+
const markerHeight = isNeedle ? trackHeight + needleOverhang * 2 : trackHeight;
|
|
13021
|
+
const markerFrac = marker != null ? fraction(marker.value, min, max) : 0;
|
|
13022
|
+
const bands = zones.map((zone, i) => {
|
|
13023
|
+
const prev = i === 0 ? min : zones[i - 1].upTo;
|
|
13024
|
+
const start = Math.max(min, Math.min(max, prev));
|
|
13025
|
+
const end = Math.max(min, Math.min(max, zone.upTo));
|
|
13026
|
+
return { color: zone.color, weight: Math.max(0, end - start) };
|
|
13027
|
+
});
|
|
13028
|
+
return /* @__PURE__ */ jsxs(
|
|
13029
|
+
reactNative.View,
|
|
13030
|
+
{
|
|
13031
|
+
className,
|
|
13032
|
+
style: [{ width: "100%" }, style],
|
|
13033
|
+
accessibilityRole: "progressbar",
|
|
13034
|
+
accessibilityValue: marker != null ? { min, max, now: marker.value } : { min, max, now: min },
|
|
13035
|
+
accessibilityLabel: accessibilityLabel2 ?? `Zone track: ${marker?.value ?? min}`,
|
|
13036
|
+
testID: "zone-track",
|
|
13037
|
+
...props,
|
|
13038
|
+
children: [
|
|
13039
|
+
/* @__PURE__ */ jsxs(reactNative.View, { style: { position: "relative", height: markerHeight }, children: [
|
|
13040
|
+
/* @__PURE__ */ jsxs(
|
|
13041
|
+
reactNative.View,
|
|
13042
|
+
{
|
|
13043
|
+
testID: "zone-track-track",
|
|
13044
|
+
accessibilityElementsHidden: true,
|
|
13045
|
+
style: {
|
|
13046
|
+
position: "absolute",
|
|
13047
|
+
top: (markerHeight - trackHeight) / 2,
|
|
13048
|
+
left: 0,
|
|
13049
|
+
right: 0,
|
|
13050
|
+
height: trackHeight,
|
|
13051
|
+
borderRadius: trackHeight / 2,
|
|
13052
|
+
backgroundColor: trackColor,
|
|
13053
|
+
overflow: "hidden",
|
|
13054
|
+
flexDirection: "row",
|
|
13055
|
+
// The pill's own box-shadow renders outside its overflow:hidden, so
|
|
13056
|
+
// `glow` haloes the whole track in the marker colour.
|
|
13057
|
+
...marker?.glow ? { boxShadow: glowShadow(marker.color ?? DEFAULT_MARKER_COLOR2) } : null
|
|
13058
|
+
},
|
|
13059
|
+
children: [
|
|
13060
|
+
bands.map((b, i) => /* @__PURE__ */ jsx(
|
|
13061
|
+
reactNative.View,
|
|
13062
|
+
{
|
|
13063
|
+
testID: "zone-track-band",
|
|
13064
|
+
style: {
|
|
13065
|
+
flexGrow: b.weight,
|
|
13066
|
+
flexShrink: 1,
|
|
13067
|
+
flexBasis: 0,
|
|
13068
|
+
minWidth: 0,
|
|
13069
|
+
height: "100%",
|
|
13070
|
+
backgroundColor: b.color
|
|
13071
|
+
}
|
|
13072
|
+
},
|
|
13073
|
+
i
|
|
13074
|
+
)),
|
|
13075
|
+
band != null && /* @__PURE__ */ jsx(
|
|
13076
|
+
reactNative.View,
|
|
13077
|
+
{
|
|
13078
|
+
testID: "zone-track-band-highlight",
|
|
13079
|
+
style: {
|
|
13080
|
+
position: "absolute",
|
|
13081
|
+
top: 0,
|
|
13082
|
+
bottom: 0,
|
|
13083
|
+
left: pct(fraction(band.from, min, max)),
|
|
13084
|
+
width: pct(fraction(band.to, min, max) - fraction(band.from, min, max)),
|
|
13085
|
+
backgroundColor: band.color
|
|
13086
|
+
}
|
|
13087
|
+
}
|
|
13088
|
+
),
|
|
13089
|
+
marker?.type === "fill" && /* @__PURE__ */ jsx(
|
|
13090
|
+
reactNative.View,
|
|
13091
|
+
{
|
|
13092
|
+
testID: "zone-track-unfilled",
|
|
13093
|
+
style: {
|
|
13094
|
+
position: "absolute",
|
|
13095
|
+
top: 0,
|
|
13096
|
+
bottom: 0,
|
|
13097
|
+
left: pct(markerFrac),
|
|
13098
|
+
right: 0,
|
|
13099
|
+
backgroundColor: trackColor
|
|
13100
|
+
}
|
|
13101
|
+
}
|
|
13102
|
+
),
|
|
13103
|
+
marker?.type === "fill" && marker.color != null && /* @__PURE__ */ jsx(
|
|
13104
|
+
reactNative.View,
|
|
13105
|
+
{
|
|
13106
|
+
testID: "zone-track-fill",
|
|
12085
13107
|
style: {
|
|
12086
13108
|
position: "absolute",
|
|
12087
|
-
|
|
12088
|
-
|
|
12089
|
-
|
|
12090
|
-
|
|
12091
|
-
|
|
12092
|
-
|
|
12093
|
-
|
|
12094
|
-
|
|
12095
|
-
|
|
12096
|
-
|
|
12097
|
-
|
|
12098
|
-
|
|
12099
|
-
|
|
12100
|
-
|
|
12101
|
-
|
|
12102
|
-
|
|
12103
|
-
|
|
12104
|
-
|
|
12105
|
-
|
|
12106
|
-
|
|
12107
|
-
|
|
12108
|
-
|
|
12109
|
-
|
|
12110
|
-
|
|
12111
|
-
|
|
12112
|
-
|
|
12113
|
-
|
|
12114
|
-
|
|
12115
|
-
|
|
12116
|
-
|
|
12117
|
-
|
|
13109
|
+
top: 0,
|
|
13110
|
+
bottom: 0,
|
|
13111
|
+
left: 0,
|
|
13112
|
+
width: pct(markerFrac),
|
|
13113
|
+
backgroundColor: marker.color
|
|
13114
|
+
}
|
|
13115
|
+
}
|
|
13116
|
+
)
|
|
13117
|
+
]
|
|
13118
|
+
}
|
|
13119
|
+
),
|
|
13120
|
+
isNeedle && /* @__PURE__ */ jsx(
|
|
13121
|
+
reactNative.View,
|
|
13122
|
+
{
|
|
13123
|
+
testID: "zone-track-needle",
|
|
13124
|
+
style: {
|
|
13125
|
+
position: "absolute",
|
|
13126
|
+
top: 0,
|
|
13127
|
+
left: pct(markerFrac),
|
|
13128
|
+
width: NEEDLE_WIDTH,
|
|
13129
|
+
height: markerHeight,
|
|
13130
|
+
borderRadius: NEEDLE_WIDTH / 2,
|
|
13131
|
+
backgroundColor: marker.color ?? DEFAULT_MARKER_COLOR2,
|
|
13132
|
+
transform: [{ translateX: -NEEDLE_WIDTH / 2 }]
|
|
13133
|
+
}
|
|
13134
|
+
}
|
|
13135
|
+
),
|
|
13136
|
+
ticks?.map((tick, i) => {
|
|
13137
|
+
const emphasized = tick.emphasized === true;
|
|
13138
|
+
const lineColor = tick.color ?? (emphasized ? t15["brand-primary"] : TICK_COLOR);
|
|
13139
|
+
const lineWidth = emphasized ? 2 : 1.5;
|
|
13140
|
+
return /* @__PURE__ */ jsx(
|
|
13141
|
+
reactNative.View,
|
|
13142
|
+
{
|
|
13143
|
+
testID: "zone-track-tick-line",
|
|
13144
|
+
accessibilityElementsHidden: true,
|
|
13145
|
+
style: {
|
|
13146
|
+
position: "absolute",
|
|
13147
|
+
top: (markerHeight - trackHeight) / 2 - 2,
|
|
13148
|
+
left: pct(fraction(tick.value, min, max)),
|
|
13149
|
+
width: lineWidth,
|
|
13150
|
+
height: trackHeight + 4,
|
|
13151
|
+
borderRadius: lineWidth / 2,
|
|
13152
|
+
backgroundColor: lineColor,
|
|
13153
|
+
transform: [{ translateX: -lineWidth / 2 }],
|
|
13154
|
+
zIndex: 3
|
|
13155
|
+
}
|
|
13156
|
+
},
|
|
13157
|
+
`line-${i}`
|
|
13158
|
+
);
|
|
13159
|
+
})
|
|
13160
|
+
] }),
|
|
13161
|
+
ticks != null && ticks.length > 0 && /* @__PURE__ */ jsx(reactNative.View, { style: { position: "relative", height: 16, marginTop: 5 }, children: ticks.map((tick, i) => {
|
|
13162
|
+
if (tick.label == null) return null;
|
|
13163
|
+
const emphasized = tick.emphasized === true;
|
|
13164
|
+
const labelColor = tick.color ?? (emphasized ? t15["brand-primary"] : TICK_COLOR);
|
|
13165
|
+
const labelNode = /* @__PURE__ */ jsx(
|
|
13166
|
+
reactNative.Text,
|
|
13167
|
+
{
|
|
13168
|
+
testID: "zone-track-tick-label",
|
|
13169
|
+
style: {
|
|
13170
|
+
fontSize: 9,
|
|
13171
|
+
letterSpacing: 0.5,
|
|
13172
|
+
color: labelColor,
|
|
13173
|
+
fontFamily: "monospace",
|
|
13174
|
+
fontWeight: emphasized ? "700" : "400"
|
|
13175
|
+
},
|
|
13176
|
+
children: tick.label
|
|
13177
|
+
}
|
|
13178
|
+
);
|
|
12118
13179
|
return /* @__PURE__ */ jsx(
|
|
12119
|
-
reactNative.
|
|
13180
|
+
reactNative.View,
|
|
12120
13181
|
{
|
|
13182
|
+
testID: "zone-track-tick",
|
|
12121
13183
|
style: {
|
|
12122
13184
|
position: "absolute",
|
|
12123
|
-
left:
|
|
12124
|
-
|
|
12125
|
-
|
|
13185
|
+
left: pct(fraction(tick.value, min, max)),
|
|
13186
|
+
transform: [{ translateX: -14 }],
|
|
13187
|
+
width: 28,
|
|
13188
|
+
alignItems: "center"
|
|
12126
13189
|
},
|
|
12127
|
-
|
|
12128
|
-
children: onWorkoutPress ? /* @__PURE__ */ jsx(
|
|
12129
|
-
reactNative.Pressable,
|
|
12130
|
-
{
|
|
12131
|
-
onPress: () => onWorkoutPress(workout),
|
|
12132
|
-
accessibilityRole: "button",
|
|
12133
|
-
accessibilityLabel: label,
|
|
12134
|
-
style: dotStyle,
|
|
12135
|
-
testID: "capacity-band-chart-dot"
|
|
12136
|
-
}
|
|
12137
|
-
) : /* @__PURE__ */ jsx(
|
|
12138
|
-
reactNative.View,
|
|
12139
|
-
{
|
|
12140
|
-
accessibilityElementsHidden: true,
|
|
12141
|
-
importantForAccessibility: "no-hide-descendants",
|
|
12142
|
-
style: dotStyle,
|
|
12143
|
-
testID: "capacity-band-chart-dot"
|
|
12144
|
-
}
|
|
12145
|
-
)
|
|
13190
|
+
children: tick.tooltip != null ? /* @__PURE__ */ jsx(Tooltip, { label: tick.tooltip, placement: "top", children: labelNode }) : labelNode
|
|
12146
13191
|
},
|
|
12147
|
-
`
|
|
13192
|
+
`label-${i}`
|
|
12148
13193
|
);
|
|
12149
|
-
})
|
|
13194
|
+
}) })
|
|
13195
|
+
]
|
|
13196
|
+
}
|
|
13197
|
+
);
|
|
13198
|
+
}
|
|
13199
|
+
|
|
13200
|
+
// src/components/custom/Workout/FatigueMeter.tsx
|
|
13201
|
+
var { green, yellow, orange, red } = WORKOUT_TOKENS.scale;
|
|
13202
|
+
var DEFAULT_ZONE_COLORS = [green, yellow, orange, red];
|
|
13203
|
+
var DEFAULT_THRESHOLDS = [10, 20, 30];
|
|
13204
|
+
var DEFAULT_LABELS = ["fresh", "VL10", "VL20", "VL30", "stop"];
|
|
13205
|
+
var DEFAULT_MAX = 40;
|
|
13206
|
+
function FatigueMeter({
|
|
13207
|
+
value,
|
|
13208
|
+
max = DEFAULT_MAX,
|
|
13209
|
+
thresholds = DEFAULT_THRESHOLDS,
|
|
13210
|
+
zoneColors = DEFAULT_ZONE_COLORS,
|
|
13211
|
+
labels = DEFAULT_LABELS,
|
|
13212
|
+
trackHeight = 14,
|
|
13213
|
+
needleColor,
|
|
13214
|
+
className,
|
|
13215
|
+
...props
|
|
13216
|
+
}) {
|
|
13217
|
+
const [t1, t22, t32] = thresholds;
|
|
13218
|
+
const zones = [
|
|
13219
|
+
{ upTo: t1, color: zoneColors[0] },
|
|
13220
|
+
{ upTo: t22, color: zoneColors[1] },
|
|
13221
|
+
{ upTo: t32, color: zoneColors[2] },
|
|
13222
|
+
{ upTo: max, color: zoneColors[3] }
|
|
13223
|
+
];
|
|
13224
|
+
const tickValues = [0, t1, t22, t32, max];
|
|
13225
|
+
const ticks = tickValues.map((v, i) => ({ value: v, label: labels[i] }));
|
|
13226
|
+
return /* @__PURE__ */ jsx(
|
|
13227
|
+
ZoneTrack,
|
|
13228
|
+
{
|
|
13229
|
+
className,
|
|
13230
|
+
zones,
|
|
13231
|
+
min: 0,
|
|
13232
|
+
max,
|
|
13233
|
+
trackHeight,
|
|
13234
|
+
ticks,
|
|
13235
|
+
marker: { type: "needle", value, color: needleColor },
|
|
13236
|
+
accessibilityLabel: `Fatigue: ${value}% velocity loss`,
|
|
13237
|
+
testID: "fatigue-meter",
|
|
13238
|
+
...props
|
|
13239
|
+
}
|
|
13240
|
+
);
|
|
13241
|
+
}
|
|
13242
|
+
var HEAT = WORKOUT_TOKENS.heatmap;
|
|
13243
|
+
var NEUTRAL_TRACK = primitiveColors.charcoal[200];
|
|
13244
|
+
var MONO = "monospace";
|
|
13245
|
+
var HEAT_BY_ZONE = {
|
|
13246
|
+
under: HEAT.under,
|
|
13247
|
+
maintenance: HEAT.maintenance,
|
|
13248
|
+
productive: HEAT.productive,
|
|
13249
|
+
approaching: HEAT.approaching,
|
|
13250
|
+
over: HEAT.over
|
|
13251
|
+
};
|
|
13252
|
+
var ZONE_DESCRIPTION = {
|
|
13253
|
+
under: "below MEV",
|
|
13254
|
+
maintenance: "building toward MAV",
|
|
13255
|
+
productive: "in the productive zone",
|
|
13256
|
+
approaching: "approaching MRV",
|
|
13257
|
+
over: "over MRV"
|
|
13258
|
+
};
|
|
13259
|
+
var LANDMARK_NAME = {
|
|
13260
|
+
MEV: "Minimum Effective Volume",
|
|
13261
|
+
MAV: "Maximum Adaptive Volume",
|
|
13262
|
+
MRV: "Maximum Recoverable Volume"
|
|
13263
|
+
};
|
|
13264
|
+
function zoneForSets(sets, { mev, mav, mrv }) {
|
|
13265
|
+
if (sets < mev) return "under";
|
|
13266
|
+
if (sets < mav) return "maintenance";
|
|
13267
|
+
if (sets >= mrv) return "over";
|
|
13268
|
+
const midpoint = (mav + mrv) / 2;
|
|
13269
|
+
return sets < midpoint ? "productive" : "approaching";
|
|
13270
|
+
}
|
|
13271
|
+
function VolumeLandmarkBar({
|
|
13272
|
+
muscle,
|
|
13273
|
+
currentSets,
|
|
13274
|
+
landmarks,
|
|
13275
|
+
width = 220,
|
|
13276
|
+
trackHeight = 10,
|
|
13277
|
+
scaleMax,
|
|
13278
|
+
className,
|
|
13279
|
+
style,
|
|
13280
|
+
...props
|
|
13281
|
+
}) {
|
|
13282
|
+
const { mev, mav, mrv } = landmarks;
|
|
13283
|
+
const max = scaleMax ?? mrv * 1.2;
|
|
13284
|
+
const zone = zoneForSets(currentSets, landmarks);
|
|
13285
|
+
const fillColor = HEAT_BY_ZONE[zone];
|
|
13286
|
+
const pct2 = mav > 0 ? Math.round(currentSets / mav * 100) : 0;
|
|
13287
|
+
return /* @__PURE__ */ jsxs(
|
|
13288
|
+
reactNative.View,
|
|
13289
|
+
{
|
|
13290
|
+
className,
|
|
13291
|
+
style: [{ width, gap: 3 }, style],
|
|
13292
|
+
testID: "volume-landmark-bar",
|
|
13293
|
+
...props,
|
|
13294
|
+
children: [
|
|
13295
|
+
/* @__PURE__ */ jsx(
|
|
13296
|
+
DataRow,
|
|
13297
|
+
{
|
|
13298
|
+
label: muscle,
|
|
13299
|
+
value: /* @__PURE__ */ jsxs(
|
|
13300
|
+
reactNative.Text,
|
|
13301
|
+
{
|
|
13302
|
+
testID: "volume-landmark-pct",
|
|
13303
|
+
style: { fontSize: 14, fontWeight: "700", fontFamily: MONO, color: fillColor },
|
|
13304
|
+
children: [
|
|
13305
|
+
pct2,
|
|
13306
|
+
"%"
|
|
13307
|
+
]
|
|
13308
|
+
}
|
|
13309
|
+
),
|
|
13310
|
+
className: "py-0",
|
|
13311
|
+
testID: "volume-landmark-header"
|
|
13312
|
+
}
|
|
13313
|
+
),
|
|
13314
|
+
/* @__PURE__ */ jsx(
|
|
13315
|
+
ZoneTrack,
|
|
13316
|
+
{
|
|
13317
|
+
zones: [{ upTo: max, color: NEUTRAL_TRACK }],
|
|
13318
|
+
max,
|
|
13319
|
+
marker: {
|
|
13320
|
+
type: "fill",
|
|
13321
|
+
value: currentSets,
|
|
13322
|
+
color: fillColor,
|
|
13323
|
+
// Glow when in the optimal productive band — the "sweet spot" cue.
|
|
13324
|
+
glow: zone === "productive"
|
|
13325
|
+
},
|
|
13326
|
+
trackColor: NEUTRAL_TRACK,
|
|
13327
|
+
trackHeight,
|
|
13328
|
+
ticks: [
|
|
13329
|
+
{ value: mev, label: "MEV", tooltip: `${LANDMARK_NAME.MEV} \xB7 ${mev} sets/wk` },
|
|
13330
|
+
{
|
|
13331
|
+
value: mav,
|
|
13332
|
+
label: "MAV",
|
|
13333
|
+
emphasized: true,
|
|
13334
|
+
tooltip: `${LANDMARK_NAME.MAV} (target) \xB7 ${mav} sets/wk`
|
|
13335
|
+
},
|
|
13336
|
+
{ value: mrv, label: "MRV", tooltip: `${LANDMARK_NAME.MRV} \xB7 ${mrv} sets/wk` }
|
|
13337
|
+
],
|
|
13338
|
+
accessibilityLabel: `${muscle} weekly volume: ${currentSets} sets, ${pct2}% of MAV target, ${ZONE_DESCRIPTION[zone]}`,
|
|
13339
|
+
testID: "volume-landmark-track"
|
|
13340
|
+
}
|
|
13341
|
+
)
|
|
13342
|
+
]
|
|
13343
|
+
}
|
|
13344
|
+
);
|
|
13345
|
+
}
|
|
13346
|
+
var t16 = getSemanticColors("dark");
|
|
13347
|
+
var statusToken = {
|
|
13348
|
+
productive: "status-success",
|
|
13349
|
+
threshold: "status-warning",
|
|
13350
|
+
stop: "status-error"
|
|
13351
|
+
};
|
|
13352
|
+
var statusDotVariant = {
|
|
13353
|
+
productive: "success",
|
|
13354
|
+
threshold: "warning",
|
|
13355
|
+
stop: "error"
|
|
13356
|
+
};
|
|
13357
|
+
var defaultLabel = {
|
|
13358
|
+
productive: "Productive",
|
|
13359
|
+
threshold: "Threshold",
|
|
13360
|
+
stop: "Stop"
|
|
13361
|
+
};
|
|
13362
|
+
function statusPillColor(status) {
|
|
13363
|
+
return t16[statusToken[status]];
|
|
13364
|
+
}
|
|
13365
|
+
function StatusPill2({ status, label, className, style, ...props }) {
|
|
13366
|
+
const color = statusPillColor(status);
|
|
13367
|
+
const text = label ?? defaultLabel[status];
|
|
13368
|
+
return /* @__PURE__ */ jsxs(
|
|
13369
|
+
reactNative.View,
|
|
13370
|
+
{
|
|
13371
|
+
testID: "status-pill",
|
|
13372
|
+
className,
|
|
13373
|
+
style: [
|
|
13374
|
+
{
|
|
13375
|
+
flexDirection: "row",
|
|
13376
|
+
alignItems: "center",
|
|
13377
|
+
gap: 7,
|
|
13378
|
+
alignSelf: "flex-start",
|
|
13379
|
+
paddingVertical: 7,
|
|
13380
|
+
paddingHorizontal: 13,
|
|
13381
|
+
borderRadius: 9,
|
|
13382
|
+
backgroundColor: alpha(color, 0.14),
|
|
13383
|
+
borderWidth: 1,
|
|
13384
|
+
borderColor: alpha(color, 0.4)
|
|
13385
|
+
},
|
|
13386
|
+
style
|
|
13387
|
+
],
|
|
13388
|
+
...props,
|
|
13389
|
+
children: [
|
|
13390
|
+
/* @__PURE__ */ jsx(reactNative.View, { accessibilityElementsHidden: true, children: /* @__PURE__ */ jsx(StatusDot, { variant: statusDotVariant[status], glow: true }) }),
|
|
13391
|
+
/* @__PURE__ */ jsx(reactNative.Text, { testID: "status-pill-label", style: { fontSize: 13, fontWeight: "800", color }, children: text })
|
|
13392
|
+
]
|
|
13393
|
+
}
|
|
13394
|
+
);
|
|
13395
|
+
}
|
|
13396
|
+
var t17 = getSemanticColors("dark");
|
|
13397
|
+
var categoryToken = {
|
|
13398
|
+
productive: null,
|
|
13399
|
+
threshold: "status-warning",
|
|
13400
|
+
stop: "status-error"
|
|
13401
|
+
};
|
|
13402
|
+
var floodOpacity = {
|
|
13403
|
+
productive: 0,
|
|
13404
|
+
threshold: 0.12,
|
|
13405
|
+
stop: 0.18
|
|
13406
|
+
};
|
|
13407
|
+
function liveAuraColor(category) {
|
|
13408
|
+
const token = categoryToken[category];
|
|
13409
|
+
return token ? t17[token] : null;
|
|
13410
|
+
}
|
|
13411
|
+
function LiveAuraFrame({
|
|
13412
|
+
category,
|
|
13413
|
+
pulse = true,
|
|
13414
|
+
className,
|
|
13415
|
+
children,
|
|
13416
|
+
style,
|
|
13417
|
+
...props
|
|
13418
|
+
}) {
|
|
13419
|
+
const color = liveAuraColor(category);
|
|
13420
|
+
const tint = color ? alpha(color, floodOpacity[category]) : "transparent";
|
|
13421
|
+
const floodStyle = {
|
|
13422
|
+
...reactNative.StyleSheet.absoluteFillObject,
|
|
13423
|
+
...reactNative.Platform.OS === "web" ? (
|
|
13424
|
+
// backgroundImage is a runtime RNW style, not in RN ViewStyle types.
|
|
13425
|
+
{ backgroundImage: `radial-gradient(130% 95% at 50% 0%, ${tint}, transparent 62%)` }
|
|
13426
|
+
) : { backgroundColor: tint }
|
|
13427
|
+
};
|
|
13428
|
+
return /* @__PURE__ */ jsxs(
|
|
13429
|
+
reactNative.View,
|
|
13430
|
+
{
|
|
13431
|
+
testID: "live-aura-frame",
|
|
13432
|
+
className,
|
|
13433
|
+
style: [
|
|
13434
|
+
{
|
|
13435
|
+
position: "relative",
|
|
13436
|
+
overflow: "hidden",
|
|
13437
|
+
borderRadius: 10,
|
|
13438
|
+
backgroundColor: t17["background-base"],
|
|
13439
|
+
borderWidth: 1,
|
|
13440
|
+
borderColor: t17["border-default"]
|
|
13441
|
+
},
|
|
13442
|
+
style
|
|
13443
|
+
],
|
|
13444
|
+
...props,
|
|
13445
|
+
children: [
|
|
13446
|
+
color && /* @__PURE__ */ jsx(
|
|
13447
|
+
reactNative.View,
|
|
13448
|
+
{
|
|
13449
|
+
testID: "live-aura-flood",
|
|
13450
|
+
accessibilityElementsHidden: true,
|
|
13451
|
+
pointerEvents: "none",
|
|
13452
|
+
className: category === "stop" && pulse ? "animate-pulse" : void 0,
|
|
13453
|
+
style: floodStyle
|
|
13454
|
+
}
|
|
13455
|
+
),
|
|
13456
|
+
children
|
|
12150
13457
|
]
|
|
12151
13458
|
}
|
|
12152
13459
|
);
|
|
@@ -12357,28 +13664,28 @@ function Scatter({
|
|
|
12357
13664
|
testID: "scatter-canvas",
|
|
12358
13665
|
style: { position: "absolute", top: 0, left: 0, width, height, overflow: "hidden" },
|
|
12359
13666
|
children: [
|
|
12360
|
-
ticksOf(yd, TICK_COUNT).map((
|
|
12361
|
-
const y = toY(
|
|
13667
|
+
ticksOf(yd, TICK_COUNT).map((t18, i) => {
|
|
13668
|
+
const y = toY(t18);
|
|
12362
13669
|
return /* @__PURE__ */ jsx(
|
|
12363
13670
|
reactNative.View,
|
|
12364
13671
|
{
|
|
12365
13672
|
accessibilityElementsHidden: true,
|
|
12366
13673
|
testID: "scatter-gridline-y",
|
|
12367
13674
|
style: { position: "absolute", left: PLOT_LEFT2, width: innerW, top: y, height: 1, backgroundColor: GRID_LINE2 },
|
|
12368
|
-
children: /* @__PURE__ */ jsx(reactNative.Text, { className: "text-[9px] text-text-tertiary", style: { position: "absolute", left: -PLOT_LEFT2, top: -6, width: PLOT_LEFT2 - 6, textAlign: "right" }, children: formatTick(
|
|
13675
|
+
children: /* @__PURE__ */ jsx(reactNative.Text, { className: "text-[9px] text-text-tertiary", style: { position: "absolute", left: -PLOT_LEFT2, top: -6, width: PLOT_LEFT2 - 6, textAlign: "right" }, children: formatTick(t18) })
|
|
12369
13676
|
},
|
|
12370
13677
|
`y-${i}`
|
|
12371
13678
|
);
|
|
12372
13679
|
}),
|
|
12373
|
-
ticksOf(xd, TICK_COUNT).map((
|
|
12374
|
-
const x = toX(
|
|
13680
|
+
ticksOf(xd, TICK_COUNT).map((t18, i) => {
|
|
13681
|
+
const x = toX(t18);
|
|
12375
13682
|
return /* @__PURE__ */ jsx(
|
|
12376
13683
|
reactNative.View,
|
|
12377
13684
|
{
|
|
12378
13685
|
accessibilityElementsHidden: true,
|
|
12379
13686
|
testID: "scatter-gridline-x",
|
|
12380
13687
|
style: { position: "absolute", top: PLOT_TOP, height: innerH, left: x, width: 1, backgroundColor: GRID_LINE2 },
|
|
12381
|
-
children: /* @__PURE__ */ jsx(reactNative.Text, { className: "text-[9px] text-text-tertiary", style: { position: "absolute", top: innerH + 4, left: -16, width: 32, textAlign: "center" }, children: formatTick(
|
|
13688
|
+
children: /* @__PURE__ */ jsx(reactNative.Text, { className: "text-[9px] text-text-tertiary", style: { position: "absolute", top: innerH + 4, left: -16, width: 32, textAlign: "center" }, children: formatTick(t18) })
|
|
12382
13689
|
},
|
|
12383
13690
|
`x-${i}`
|
|
12384
13691
|
);
|
|
@@ -12443,7 +13750,7 @@ var STATUS_SUCCESS4 = sem2["status-success"];
|
|
|
12443
13750
|
var STATUS_WARNING4 = sem2["status-warning"];
|
|
12444
13751
|
var STATUS_ERROR4 = sem2["status-error"];
|
|
12445
13752
|
var TRACK = "rgba(255,255,255,0.08)";
|
|
12446
|
-
var
|
|
13753
|
+
var DEFAULT_THRESHOLDS2 = [
|
|
12447
13754
|
{ value: 0, color: STATUS_ERROR4 },
|
|
12448
13755
|
{ value: 60, color: STATUS_WARNING4 },
|
|
12449
13756
|
{ value: 80, color: STATUS_SUCCESS4 }
|
|
@@ -12458,8 +13765,8 @@ function clamp012(n) {
|
|
|
12458
13765
|
function formatValue2(v) {
|
|
12459
13766
|
return Number.isInteger(v) ? `${v}` : v.toFixed(1);
|
|
12460
13767
|
}
|
|
12461
|
-
function bandColor2(
|
|
12462
|
-
const value = min +
|
|
13768
|
+
function bandColor2(fraction2, bands, min, max) {
|
|
13769
|
+
const value = min + fraction2 * (max - min);
|
|
12463
13770
|
const sorted = [...bands].sort((a, b) => a.value - b.value);
|
|
12464
13771
|
let color = sorted[0]?.color ?? STATUS_SUCCESS4;
|
|
12465
13772
|
for (const band of sorted) {
|
|
@@ -12471,15 +13778,15 @@ function buildTicks(fill, size, activeColor) {
|
|
|
12471
13778
|
const center = size / 2;
|
|
12472
13779
|
const radius = center - size * 0.08;
|
|
12473
13780
|
return Array.from({ length: SEGMENTS }, (_, i) => {
|
|
12474
|
-
const
|
|
12475
|
-
const deg = ARC_START +
|
|
13781
|
+
const fraction2 = (i + 0.5) / SEGMENTS;
|
|
13782
|
+
const deg = ARC_START + fraction2 * ARC_SWEEP;
|
|
12476
13783
|
const rad = deg * Math.PI / 180;
|
|
12477
13784
|
return {
|
|
12478
13785
|
key: i,
|
|
12479
13786
|
left: center + radius * Math.cos(rad),
|
|
12480
13787
|
top: center + radius * Math.sin(rad),
|
|
12481
13788
|
rotate: `${deg - 90}deg`,
|
|
12482
|
-
color:
|
|
13789
|
+
color: fraction2 <= fill ? activeColor(fraction2) : TRACK
|
|
12483
13790
|
};
|
|
12484
13791
|
});
|
|
12485
13792
|
}
|
|
@@ -12490,7 +13797,7 @@ function Gauge2({
|
|
|
12490
13797
|
size = 160,
|
|
12491
13798
|
label,
|
|
12492
13799
|
unit,
|
|
12493
|
-
thresholds =
|
|
13800
|
+
thresholds = DEFAULT_THRESHOLDS2,
|
|
12494
13801
|
color,
|
|
12495
13802
|
className,
|
|
12496
13803
|
...props
|
|
@@ -12513,23 +13820,23 @@ function Gauge2({
|
|
|
12513
13820
|
testID: "gauge",
|
|
12514
13821
|
...props,
|
|
12515
13822
|
children: [
|
|
12516
|
-
ticks.map((
|
|
13823
|
+
ticks.map((t18) => /* @__PURE__ */ jsx(
|
|
12517
13824
|
reactNative.View,
|
|
12518
13825
|
{
|
|
12519
13826
|
accessibilityElementsHidden: true,
|
|
12520
13827
|
testID: "gauge-segment",
|
|
12521
13828
|
style: {
|
|
12522
13829
|
position: "absolute",
|
|
12523
|
-
left:
|
|
12524
|
-
top:
|
|
13830
|
+
left: t18.left - tickThickness / 2,
|
|
13831
|
+
top: t18.top - tickLength / 2,
|
|
12525
13832
|
width: tickThickness,
|
|
12526
13833
|
height: tickLength,
|
|
12527
13834
|
borderRadius: tickThickness / 2,
|
|
12528
|
-
backgroundColor:
|
|
12529
|
-
transform: [{ rotate:
|
|
13835
|
+
backgroundColor: t18.color,
|
|
13836
|
+
transform: [{ rotate: t18.rotate }]
|
|
12530
13837
|
}
|
|
12531
13838
|
},
|
|
12532
|
-
|
|
13839
|
+
t18.key
|
|
12533
13840
|
)),
|
|
12534
13841
|
/* @__PURE__ */ jsxs(reactNative.View, { style: { position: "absolute", top: 0, left: 0, width: size, height: size }, className: "items-center justify-center", children: [
|
|
12535
13842
|
/* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row items-baseline gap-0.5", children: [
|
|
@@ -12575,7 +13882,7 @@ function BrandLockup({
|
|
|
12575
13882
|
}
|
|
12576
13883
|
var stateConfig = {
|
|
12577
13884
|
// live = vivid green with an expanding ring; rest = solid amber (no pulse — operator); idle is dim.
|
|
12578
|
-
live: { label: "LIVE", color: "
|
|
13885
|
+
live: { label: "LIVE", color: "live", pulse: "ping", textColor: "primary" },
|
|
12579
13886
|
rest: { label: "REST", color: "warning", pulse: false, textColor: "primary" },
|
|
12580
13887
|
idle: { label: "IDLE", color: "default", pulse: false, textColor: "secondary" }
|
|
12581
13888
|
};
|
|
@@ -12739,6 +14046,127 @@ function TopBar({
|
|
|
12739
14046
|
}
|
|
12740
14047
|
);
|
|
12741
14048
|
}
|
|
14049
|
+
function NavItem({
|
|
14050
|
+
icon,
|
|
14051
|
+
label,
|
|
14052
|
+
active = false,
|
|
14053
|
+
live = false,
|
|
14054
|
+
onPress,
|
|
14055
|
+
className
|
|
14056
|
+
}) {
|
|
14057
|
+
const glyphColor = active ? "text-brand-primary" : "text-text-tertiary";
|
|
14058
|
+
const labelColor = active ? "text-brand-primary" : live ? "text-status-live-muted" : "text-text-tertiary";
|
|
14059
|
+
return /* @__PURE__ */ jsxs(
|
|
14060
|
+
reactNative.Pressable,
|
|
14061
|
+
{
|
|
14062
|
+
accessibilityRole: "tab",
|
|
14063
|
+
accessibilityState: { selected: active },
|
|
14064
|
+
accessibilityLabel: label,
|
|
14065
|
+
onPress,
|
|
14066
|
+
className: cn("relative h-[46px] w-[60px] items-center justify-center", className),
|
|
14067
|
+
children: [
|
|
14068
|
+
active ? /* @__PURE__ */ jsx(
|
|
14069
|
+
reactNative.View,
|
|
14070
|
+
{
|
|
14071
|
+
testID: "nav-item-accent",
|
|
14072
|
+
className: "absolute left-0 top-[13px] bottom-[13px] w-[3px] rounded-r-[3px] bg-brand-primary"
|
|
14073
|
+
}
|
|
14074
|
+
) : null,
|
|
14075
|
+
/* @__PURE__ */ jsxs(
|
|
14076
|
+
reactNative.View,
|
|
14077
|
+
{
|
|
14078
|
+
className: cn(
|
|
14079
|
+
"h-[46px] w-[46px] items-center justify-center gap-[3px] rounded-[11px]",
|
|
14080
|
+
glyphColor
|
|
14081
|
+
),
|
|
14082
|
+
children: [
|
|
14083
|
+
icon,
|
|
14084
|
+
/* @__PURE__ */ jsx(
|
|
14085
|
+
Typography,
|
|
14086
|
+
{
|
|
14087
|
+
variant: "button",
|
|
14088
|
+
color: "inherit",
|
|
14089
|
+
className: cn("text-[8.5px] font-bold uppercase tracking-[0.4px]", labelColor),
|
|
14090
|
+
children: label
|
|
14091
|
+
}
|
|
14092
|
+
)
|
|
14093
|
+
]
|
|
14094
|
+
}
|
|
14095
|
+
)
|
|
14096
|
+
]
|
|
14097
|
+
}
|
|
14098
|
+
);
|
|
14099
|
+
}
|
|
14100
|
+
var defaultNavItems = [
|
|
14101
|
+
{ key: "live", label: "Live", icon: /* @__PURE__ */ jsx(ActivityIcon, { size: 20, color: "currentColor" }) },
|
|
14102
|
+
{ key: "review", label: "Review", icon: /* @__PURE__ */ jsx(HistoryIcon, { size: 20, color: "currentColor" }) },
|
|
14103
|
+
{ key: "program", label: "Plan", icon: /* @__PURE__ */ jsx(LayersIcon, { size: 20, color: "currentColor" }) },
|
|
14104
|
+
{ key: "body", label: "Body", icon: /* @__PURE__ */ jsx(PersonStandingIcon, { size: 20, color: "currentColor" }) }
|
|
14105
|
+
];
|
|
14106
|
+
function SideNav({
|
|
14107
|
+
items = defaultNavItems,
|
|
14108
|
+
activeKey,
|
|
14109
|
+
onNavigate,
|
|
14110
|
+
liveKey = null,
|
|
14111
|
+
className
|
|
14112
|
+
}) {
|
|
14113
|
+
return /* @__PURE__ */ jsx(
|
|
14114
|
+
reactNative.View,
|
|
14115
|
+
{
|
|
14116
|
+
accessibilityRole: "tablist",
|
|
14117
|
+
className: cn(
|
|
14118
|
+
"w-[60px] items-center gap-[6px] border-r border-border bg-background-base py-3",
|
|
14119
|
+
className
|
|
14120
|
+
),
|
|
14121
|
+
children: items.map((item) => /* @__PURE__ */ jsx(
|
|
14122
|
+
NavItem,
|
|
14123
|
+
{
|
|
14124
|
+
icon: item.icon,
|
|
14125
|
+
label: item.label,
|
|
14126
|
+
active: item.key === activeKey,
|
|
14127
|
+
live: item.key === liveKey && item.key !== activeKey,
|
|
14128
|
+
onPress: () => onNavigate?.(item.key)
|
|
14129
|
+
},
|
|
14130
|
+
item.key
|
|
14131
|
+
))
|
|
14132
|
+
}
|
|
14133
|
+
);
|
|
14134
|
+
}
|
|
14135
|
+
var DEMO_DEVICES = [
|
|
14136
|
+
{ id: "Voltra-A3F2", nickname: "Left Cable", slot: "L", state: "connected" },
|
|
14137
|
+
{ id: "Voltra-9B1C", nickname: "Right Cable", slot: "R", state: "connected" }
|
|
14138
|
+
];
|
|
14139
|
+
function ContentPlaceholder() {
|
|
14140
|
+
return /* @__PURE__ */ jsx(reactNative.View, { className: "flex-1 items-center justify-center", children: /* @__PURE__ */ jsx(reactNative.Text, { className: "font-heading text-sm text-text-tertiary", children: "main content region" }) });
|
|
14141
|
+
}
|
|
14142
|
+
function DashboardShell({
|
|
14143
|
+
activeKey = "live",
|
|
14144
|
+
navItems,
|
|
14145
|
+
liveKey = null,
|
|
14146
|
+
state = "live",
|
|
14147
|
+
devices = DEMO_DEVICES,
|
|
14148
|
+
subtitle = "wall dashboard",
|
|
14149
|
+
onNavigate,
|
|
14150
|
+
onSelectDevice,
|
|
14151
|
+
children,
|
|
14152
|
+
className
|
|
14153
|
+
}) {
|
|
14154
|
+
return /* @__PURE__ */ jsxs(reactNative.View, { className: cn("flex-1 flex-row bg-surface-base", className), children: [
|
|
14155
|
+
/* @__PURE__ */ jsx(SideNav, { activeKey, items: navItems, liveKey, onNavigate }),
|
|
14156
|
+
/* @__PURE__ */ jsxs(reactNative.View, { className: "flex-1", children: [
|
|
14157
|
+
/* @__PURE__ */ jsx(
|
|
14158
|
+
TopBar,
|
|
14159
|
+
{
|
|
14160
|
+
state,
|
|
14161
|
+
devices,
|
|
14162
|
+
subtitle,
|
|
14163
|
+
onSelectDevice
|
|
14164
|
+
}
|
|
14165
|
+
),
|
|
14166
|
+
/* @__PURE__ */ jsx(reactNative.View, { className: "flex-1", children: children ?? /* @__PURE__ */ jsx(ContentPlaceholder, {}) })
|
|
14167
|
+
] })
|
|
14168
|
+
] });
|
|
14169
|
+
}
|
|
12742
14170
|
|
|
12743
14171
|
// src/utils/form.ts
|
|
12744
14172
|
function getFieldValidationProps(state) {
|
|
@@ -12832,13 +14260,16 @@ exports.Accordion = Accordion;
|
|
|
12832
14260
|
exports.AccordionButton = AccordionButton;
|
|
12833
14261
|
exports.AccordionItem = AccordionItem;
|
|
12834
14262
|
exports.AccordionPanel = AccordionPanel;
|
|
14263
|
+
exports.ActivityIcon = ActivityIcon;
|
|
12835
14264
|
exports.Alert = Alert;
|
|
12836
14265
|
exports.AlertDescription = AlertDescription;
|
|
12837
14266
|
exports.AlertTitle = AlertTitle;
|
|
14267
|
+
exports.AlertTriangleIcon = AlertTriangleIcon;
|
|
12838
14268
|
exports.Autocomplete = Autocomplete;
|
|
12839
14269
|
exports.Avatar = Avatar;
|
|
12840
14270
|
exports.AvatarBadge = AvatarBadge;
|
|
12841
14271
|
exports.AvatarGroup = AvatarGroup;
|
|
14272
|
+
exports.AwardIcon = AwardIcon;
|
|
12842
14273
|
exports.Badge = Badge;
|
|
12843
14274
|
exports.BadgeText = BadgeText;
|
|
12844
14275
|
exports.BaseBadge = BaseBadge;
|
|
@@ -12863,10 +14294,12 @@ exports.CardTitle = CardTitle;
|
|
|
12863
14294
|
exports.Checkbox = Checkbox;
|
|
12864
14295
|
exports.CheckboxGroup = CheckboxGroup;
|
|
12865
14296
|
exports.Chip = Chip;
|
|
14297
|
+
exports.CircleSlashIcon = CircleSlashIcon;
|
|
12866
14298
|
exports.CircularProgress = CircularProgress;
|
|
12867
14299
|
exports.Collapse = Collapse;
|
|
12868
14300
|
exports.CollapseButton = CollapseButton;
|
|
12869
14301
|
exports.CollapseContent = CollapseContent;
|
|
14302
|
+
exports.DashboardShell = DashboardShell;
|
|
12870
14303
|
exports.DataRow = DataRow;
|
|
12871
14304
|
exports.DateTime = DateTime;
|
|
12872
14305
|
exports.DeviationBar = DeviationBar;
|
|
@@ -12881,6 +14314,10 @@ exports.DrawerHeader = DrawerHeader;
|
|
|
12881
14314
|
exports.DumbbellIcon = DumbbellIcon;
|
|
12882
14315
|
exports.EmptyState = EmptyState;
|
|
12883
14316
|
exports.ExerciseCard = ExerciseCard;
|
|
14317
|
+
exports.ExerciseCardHeading = ExerciseCardHeading;
|
|
14318
|
+
exports.ExerciseHeading = ExerciseHeading;
|
|
14319
|
+
exports.ExerciseIndicator = ExerciseIndicator;
|
|
14320
|
+
exports.FatigueMeter = FatigueMeter;
|
|
12884
14321
|
exports.FormActions = FormActions;
|
|
12885
14322
|
exports.FormField = FormField;
|
|
12886
14323
|
exports.FormRow = FormRow;
|
|
@@ -12889,20 +14326,26 @@ exports.Gauge = Gauge2;
|
|
|
12889
14326
|
exports.HStack = HStack;
|
|
12890
14327
|
exports.Heading = Heading;
|
|
12891
14328
|
exports.HelpTip = HelpTip;
|
|
14329
|
+
exports.HistoryIcon = HistoryIcon;
|
|
14330
|
+
exports.INDICATOR_PRECEDENCE = INDICATOR_PRECEDENCE;
|
|
12892
14331
|
exports.IconBox = IconBox;
|
|
12893
14332
|
exports.Indicator = Indicator;
|
|
14333
|
+
exports.InfoIcon = InfoIcon;
|
|
12894
14334
|
exports.Input = Input;
|
|
12895
14335
|
exports.InputBar = InputBar;
|
|
12896
14336
|
exports.InputGroup = InputGroup;
|
|
12897
14337
|
exports.IntensityBar = IntensityBar;
|
|
12898
14338
|
exports.Label = Label;
|
|
12899
14339
|
exports.LabelWithHelp = LabelWithHelp;
|
|
14340
|
+
exports.LayersIcon = LayersIcon;
|
|
12900
14341
|
exports.Link = Link;
|
|
12901
14342
|
exports.ListItem = ListItem;
|
|
12902
14343
|
exports.ListItemContent = ListItemContent;
|
|
12903
14344
|
exports.ListItemDivider = ListItemDivider;
|
|
12904
14345
|
exports.ListItemIcon = ListItemIcon;
|
|
12905
14346
|
exports.ListItemTrailing = ListItemTrailing;
|
|
14347
|
+
exports.LiveAuraFrame = LiveAuraFrame;
|
|
14348
|
+
exports.METRIC_FONT = METRIC_FONT;
|
|
12906
14349
|
exports.Menu = Menu;
|
|
12907
14350
|
exports.MenuDivider = MenuDivider;
|
|
12908
14351
|
exports.MenuGroup = MenuGroup;
|
|
@@ -12913,7 +14356,9 @@ exports.MesoCard = MesoCard;
|
|
|
12913
14356
|
exports.MesoProgressBar = MesoProgressBar;
|
|
12914
14357
|
exports.MesoStatusCard = MesoStatusCard;
|
|
12915
14358
|
exports.Metric = Metric;
|
|
14359
|
+
exports.MetricCell = MetricCell;
|
|
12916
14360
|
exports.MetricGroup = MetricGroup;
|
|
14361
|
+
exports.MetricTiles = MetricTiles;
|
|
12917
14362
|
exports.Modal = Modal;
|
|
12918
14363
|
exports.ModalBody = ModalBody;
|
|
12919
14364
|
exports.ModalCloseButton = ModalCloseButton;
|
|
@@ -12922,9 +14367,11 @@ exports.ModalFooter = ModalFooter;
|
|
|
12922
14367
|
exports.ModalHeader = ModalHeader;
|
|
12923
14368
|
exports.ModalTitle = ModalTitle;
|
|
12924
14369
|
exports.MuscleGroupChip = MuscleGroupChip;
|
|
14370
|
+
exports.NavItem = NavItem;
|
|
12925
14371
|
exports.Overline = Overline;
|
|
12926
14372
|
exports.Paragraph = Paragraph;
|
|
12927
14373
|
exports.PasswordInput = PasswordInput;
|
|
14374
|
+
exports.PersonStandingIcon = PersonStandingIcon;
|
|
12928
14375
|
exports.Pill = Pill;
|
|
12929
14376
|
exports.PlaceholderStrip = PlaceholderStrip;
|
|
12930
14377
|
exports.Popover = Popover;
|
|
@@ -12939,13 +14386,26 @@ exports.Radio = Radio;
|
|
|
12939
14386
|
exports.RadioGroup = RadioGroup;
|
|
12940
14387
|
exports.ReadinessCheck = ReadinessCheck;
|
|
12941
14388
|
exports.RestTimer = RestTimer;
|
|
14389
|
+
exports.SET_STRIP_VARIABLE_COLOR = SET_STRIP_VARIABLE_COLOR;
|
|
14390
|
+
exports.SET_STRIP_ZONES = SET_STRIP_ZONES;
|
|
14391
|
+
exports.ScaleIcon = ScaleIcon;
|
|
12942
14392
|
exports.Scatter = Scatter;
|
|
14393
|
+
exports.ScheduleTiles = ScheduleTiles;
|
|
12943
14394
|
exports.Section = Section;
|
|
12944
14395
|
exports.SectionContent = SectionContent;
|
|
12945
14396
|
exports.SectionHeader = SectionHeader;
|
|
14397
|
+
exports.SegmentedBar = SegmentedBar;
|
|
14398
|
+
exports.SegmentedProgressBar = SegmentedProgressBar;
|
|
12946
14399
|
exports.Select = Select;
|
|
14400
|
+
exports.SessionHeader = SessionHeader;
|
|
14401
|
+
exports.SessionRail = SessionRail;
|
|
12947
14402
|
exports.SessionStatePill = SessionStatePill;
|
|
14403
|
+
exports.SetBar = SetBar;
|
|
12948
14404
|
exports.SetRow = SetRow;
|
|
14405
|
+
exports.SetStrip = SetStrip;
|
|
14406
|
+
exports.SetTableHeader = SetTableHeader;
|
|
14407
|
+
exports.SetsRepsLoad = SetsRepsLoad;
|
|
14408
|
+
exports.SideNav = SideNav;
|
|
12949
14409
|
exports.Sidebar = Sidebar;
|
|
12950
14410
|
exports.SidebarContent = SidebarContent;
|
|
12951
14411
|
exports.SidebarDivider = SidebarDivider;
|
|
@@ -12963,6 +14423,7 @@ exports.Spinner = Spinner;
|
|
|
12963
14423
|
exports.Stack = Stack;
|
|
12964
14424
|
exports.StarIcon = StarIcon;
|
|
12965
14425
|
exports.StatusDot = StatusDot;
|
|
14426
|
+
exports.StatusPill = StatusPill2;
|
|
12966
14427
|
exports.StepContent = StepContent;
|
|
12967
14428
|
exports.StepIndicator = StepIndicator;
|
|
12968
14429
|
exports.StepLabel = StepLabel;
|
|
@@ -12988,6 +14449,8 @@ exports.TableRow = TableRow;
|
|
|
12988
14449
|
exports.Tabs = Tabs;
|
|
12989
14450
|
exports.TempoBar = TempoBar;
|
|
12990
14451
|
exports.TempoDisplay = TempoDisplay;
|
|
14452
|
+
exports.Tile = Tile;
|
|
14453
|
+
exports.TimerReadout = TimerReadout;
|
|
12991
14454
|
exports.Toast = Toast;
|
|
12992
14455
|
exports.ToastProvider = ToastProvider;
|
|
12993
14456
|
exports.ToolbarButton = ToolbarButton;
|
|
@@ -12995,14 +14458,17 @@ exports.ToolbarButtonGroup = ToolbarButtonGroup;
|
|
|
12995
14458
|
exports.Tooltip = Tooltip;
|
|
12996
14459
|
exports.TopBar = TopBar;
|
|
12997
14460
|
exports.Treemap = Treemap;
|
|
14461
|
+
exports.TrendingDownIcon = TrendingDownIcon;
|
|
12998
14462
|
exports.Typography = Typography;
|
|
12999
14463
|
exports.VStack = VStack;
|
|
13000
14464
|
exports.VelocityStrip = VelocityStrip;
|
|
13001
14465
|
exports.VoltrasMark = VoltrasMark;
|
|
14466
|
+
exports.VolumeLandmarkBar = VolumeLandmarkBar;
|
|
13002
14467
|
exports.WeekRow = WeekRow;
|
|
13003
14468
|
exports.WeightBadge = WeightBadge;
|
|
13004
14469
|
exports.WorkoutCard = WorkoutCard;
|
|
13005
14470
|
exports.WorkoutPill = WorkoutPill;
|
|
14471
|
+
exports.ZoneTrack = ZoneTrack;
|
|
13006
14472
|
exports.alpha = alpha;
|
|
13007
14473
|
exports.applyThemePreset = applyThemePreset;
|
|
13008
14474
|
exports.audiobookPreset = audiobookPreset;
|
|
@@ -13022,11 +14488,13 @@ exports.createRaisedHoverShadow = createRaisedHoverShadow;
|
|
|
13022
14488
|
exports.createRaisedShadow = createRaisedShadow;
|
|
13023
14489
|
exports.darkThemeCSSVars = darkThemeCSSVars;
|
|
13024
14490
|
exports.darken = darken;
|
|
14491
|
+
exports.defaultNavItems = defaultNavItems;
|
|
13025
14492
|
exports.defaultPreset = defaultPreset;
|
|
13026
14493
|
exports.discreteRainbow = discreteRainbow;
|
|
13027
14494
|
exports.divergingScale = divergingScale;
|
|
13028
14495
|
exports.elevationSystem = elevationSystem;
|
|
13029
14496
|
exports.formatDateTime = formatDateTime;
|
|
14497
|
+
exports.formatDuration = formatDuration;
|
|
13030
14498
|
exports.formatPrescription = formatPrescription;
|
|
13031
14499
|
exports.formatSignedPct = formatSignedPct;
|
|
13032
14500
|
exports.formatVelocity = formatVelocity;
|
|
@@ -13063,7 +14531,10 @@ exports.isValidEmail = isValidEmail;
|
|
|
13063
14531
|
exports.lightThemeCSSVars = lightThemeCSSVars;
|
|
13064
14532
|
exports.lighten = lighten;
|
|
13065
14533
|
exports.linearGradient = linearGradient;
|
|
14534
|
+
exports.liveAuraColor = liveAuraColor;
|
|
13066
14535
|
exports.neumorphicShadows = neumorphicShadows;
|
|
14536
|
+
exports.paceTone = paceTone;
|
|
14537
|
+
exports.paceToneColor = paceToneColor;
|
|
13067
14538
|
exports.primitiveBorderRadius = primitiveBorderRadius;
|
|
13068
14539
|
exports.primitiveBreakpoints = primitiveBreakpoints;
|
|
13069
14540
|
exports.primitiveColors = primitiveColors;
|
|
@@ -13073,6 +14544,7 @@ exports.primitiveSpacing = primitiveSpacing;
|
|
|
13073
14544
|
exports.primitiveTypography = primitiveTypography;
|
|
13074
14545
|
exports.primitiveZIndex = primitiveZIndex;
|
|
13075
14546
|
exports.resolveColor = resolveColor;
|
|
14547
|
+
exports.resolveIndicator = resolveIndicator;
|
|
13076
14548
|
exports.rgbToHex = rgbToHex;
|
|
13077
14549
|
exports.rgbToHsv = rgbToHsv;
|
|
13078
14550
|
exports.roundRpe = roundRpe;
|
|
@@ -13084,11 +14556,14 @@ exports.semanticColorsLight = semanticColorsLight;
|
|
|
13084
14556
|
exports.semanticTypography = semanticTypography;
|
|
13085
14557
|
exports.sequentialEffort = sequentialEffort;
|
|
13086
14558
|
exports.shadowColors = shadowColors;
|
|
14559
|
+
exports.statusPillColor = statusPillColor;
|
|
13087
14560
|
exports.surfaceGradient = surfaceGradient;
|
|
13088
14561
|
exports.useTable = useTable;
|
|
14562
|
+
exports.useTimer = useTimer;
|
|
13089
14563
|
exports.useToast = useToast;
|
|
13090
14564
|
exports.useToolbarButton = useToolbarButton;
|
|
13091
14565
|
exports.validateCssPropertyManifest = validateCssPropertyManifest;
|
|
13092
14566
|
exports.validationRules = validationRules;
|
|
14567
|
+
exports.velocityZoneColor = velocityZoneColor;
|
|
13093
14568
|
//# sourceMappingURL=index.js.map
|
|
13094
14569
|
//# sourceMappingURL=index.js.map
|