@titan-design/react-ui 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bodymap.js +12 -7
- package/dist/bodymap.js.map +1 -1
- package/dist/bodymap.mjs +12 -7
- package/dist/bodymap.mjs.map +1 -1
- package/dist/index.d.mts +956 -128
- package/dist/index.d.ts +956 -128
- package/dist/index.js +2873 -981
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2826 -982
- package/dist/index.mjs.map +1 -1
- package/dist/{pages-MO0JCySL.d.mts → pages-DCFBqp79.d.mts} +289 -57
- package/dist/{pages-D7Jlssvp.d.ts → pages-_fI3-x7Z.d.ts} +289 -57
- package/dist/pages.d.mts +1 -1
- package/dist/pages.d.ts +1 -1
- package/dist/pages.js +1659 -799
- package/dist/pages.js.map +1 -1
- package/dist/pages.mjs +1660 -800
- package/dist/pages.mjs.map +1 -1
- package/dist/theme/index.d.mts +16 -82
- package/dist/theme/index.d.ts +16 -82
- package/dist/theme/index.js +13 -115
- package/dist/theme/index.js.map +1 -1
- package/dist/theme/index.mjs +13 -115
- package/dist/theme/index.mjs.map +1 -1
- package/dist/theme/tokens-css.js +14 -6
- package/dist/theme/tokens-css.js.map +1 -1
- package/dist/theme/tokens-css.mjs +14 -6
- package/dist/theme/tokens-css.mjs.map +1 -1
- package/package.json +1 -1
- package/src/arch/Architecture.stories.tsx +870 -0
- package/src/arch/annotations.json +48 -0
- package/src/arch/arch-graph.json +4301 -0
- package/src/components/custom/CircularTimer/CircularTimer.stories.tsx +90 -0
- package/src/components/custom/CircularTimer/CircularTimer.test.tsx +62 -0
- package/src/components/custom/CircularTimer/CircularTimer.tsx +112 -0
- package/src/components/custom/CircularTimer/index.ts +1 -0
- package/src/components/custom/DateTime/DateTime.stories.tsx +1 -1
- package/src/components/custom/EmptyState/EmptyState.stories.tsx +1 -1
- package/src/components/custom/Gauge/Gauge.stories.tsx +1 -1
- package/src/components/custom/Metric/Metric.stories.tsx +1 -1
- package/src/components/custom/Scatter/Scatter.stories.tsx +1 -1
- package/src/components/custom/Sidebar/Sidebar.stories.tsx +1 -1
- package/src/components/custom/Table/Table.stories.tsx +1 -1
- package/src/components/custom/TimerReadout/TimerReadout.stories.tsx +62 -0
- package/src/components/custom/TimerReadout/TimerReadout.test.tsx +72 -0
- package/src/components/custom/TimerReadout/TimerReadout.tsx +75 -0
- package/src/components/custom/TimerReadout/index.ts +1 -0
- package/src/components/custom/Treemap/Treemap.stories.tsx +1 -1
- package/src/components/custom/Typography/Typography.stories.tsx +1 -1
- package/src/components/custom/Workout/ActiveWorkoutPage.stories.tsx +21 -13
- package/src/components/custom/Workout/ActiveWorkoutPage.test.tsx +16 -13
- package/src/components/custom/Workout/ActiveWorkoutPage.tsx +9 -15
- package/src/components/custom/Workout/BaseBadge.stories.tsx +1 -1
- package/src/components/custom/Workout/BodyMap.stories.tsx +1 -1
- package/src/components/custom/Workout/BodyMapDetailPanel.stories.tsx +1 -1
- package/src/components/custom/Workout/CapacityBandChart.stories.tsx +1 -1
- package/src/components/custom/Workout/DeviationBar.stories.tsx +1 -1
- package/src/components/custom/Workout/ExerciseCard.stories.tsx +51 -66
- package/src/components/custom/Workout/ExerciseCard.test.tsx +146 -143
- package/src/components/custom/Workout/ExerciseCard.tsx +176 -198
- package/src/components/custom/Workout/ExerciseCardHeading.stories.tsx +99 -0
- package/src/components/custom/Workout/ExerciseCardHeading.test.tsx +72 -0
- package/src/components/custom/Workout/ExerciseCardHeading.tsx +43 -0
- package/src/components/custom/Workout/ExerciseCardUnified.stories.tsx +151 -0
- package/src/components/custom/Workout/ExerciseDetailPage.stories.tsx +5 -3
- package/src/components/custom/Workout/ExerciseDetailPage.test.tsx +4 -4
- package/src/components/custom/Workout/ExerciseDetailPage.tsx +4 -5
- package/src/components/custom/Workout/ExerciseHeading.stories.tsx +81 -0
- package/src/components/custom/Workout/ExerciseHeading.test.tsx +77 -0
- package/src/components/custom/Workout/ExerciseHeading.tsx +85 -0
- package/src/components/custom/Workout/ExerciseIndicator.stories.tsx +110 -0
- package/src/components/custom/Workout/ExerciseIndicator.test.tsx +93 -0
- package/src/components/custom/Workout/ExerciseIndicator.tsx +120 -0
- package/src/components/custom/Workout/FatigueMeter.stories.tsx +51 -0
- package/src/components/custom/Workout/FatigueMeter.test.tsx +85 -0
- package/src/components/custom/Workout/FatigueMeter.tsx +86 -0
- package/src/components/custom/Workout/InputBar.stories.tsx +1 -1
- package/src/components/custom/Workout/IntensityBar.stories.tsx +1 -1
- package/src/components/custom/Workout/LiveAuraFrame.stories.tsx +153 -0
- package/src/components/custom/Workout/LiveAuraFrame.test.tsx +74 -0
- package/src/components/custom/Workout/LiveAuraFrame.tsx +105 -0
- package/src/components/custom/Workout/MesoCard.stories.tsx +1 -1
- package/src/components/custom/Workout/MesoProgressBar.stories.tsx +1 -1
- package/src/components/custom/Workout/MesoStatusCard.stories.tsx +1 -1
- package/src/components/custom/Workout/MetricTiles.stories.tsx +60 -0
- package/src/components/custom/Workout/MetricTiles.test.tsx +58 -0
- package/src/components/custom/Workout/MetricTiles.tsx +48 -0
- package/src/components/custom/Workout/MuscleGroupChip.stories.tsx +1 -1
- package/src/components/custom/Workout/PlaceholderStrip.stories.tsx +1 -1
- package/src/components/custom/Workout/PrBadge.stories.tsx +1 -1
- package/src/components/custom/Workout/PrHistoryModal.stories.tsx +1 -1
- package/src/components/custom/Workout/ProgramPlanningPage.stories.tsx +4 -16
- package/src/components/custom/Workout/ProgramPlanningPage.tsx +8 -6
- package/src/components/custom/Workout/README.md +193 -4
- package/src/components/custom/Workout/ReadinessCheck.stories.tsx +1 -1
- package/src/components/custom/Workout/RestTimer.stories.tsx +133 -3
- package/src/components/custom/Workout/RestTimer.test.tsx +73 -0
- package/src/components/custom/Workout/RestTimer.tsx +155 -59
- package/src/components/custom/Workout/RowInventory.stories.tsx +405 -0
- package/src/components/custom/Workout/S3FullRail.stories.tsx +34 -0
- package/src/components/custom/Workout/S3SessionPace.stories.tsx +95 -0
- package/src/components/custom/Workout/S3SessionRailHeading.stories.tsx +61 -0
- package/src/components/custom/Workout/S3SetTypes.stories.tsx +176 -0
- package/src/components/custom/Workout/S3WorkoutExpansion.stories.tsx +240 -0
- package/src/components/custom/Workout/ScheduleTiles.stories.tsx +40 -0
- package/src/components/custom/Workout/ScheduleTiles.test.tsx +34 -0
- package/src/components/custom/Workout/ScheduleTiles.tsx +70 -0
- package/src/components/custom/Workout/SegmentedBar.stories.tsx +99 -0
- package/src/components/custom/Workout/SegmentedBar.test.tsx +77 -0
- package/src/components/custom/Workout/SegmentedBar.tsx +172 -0
- package/src/components/custom/Workout/SegmentedProgressBar.stories.tsx +50 -0
- package/src/components/custom/Workout/SegmentedProgressBar.test.tsx +80 -0
- package/src/components/custom/Workout/SegmentedProgressBar.tsx +67 -0
- package/src/components/custom/Workout/SessionHeader.stories.tsx +121 -0
- package/src/components/custom/Workout/SessionHeader.test.tsx +122 -0
- package/src/components/custom/Workout/SessionHeader.tsx +152 -0
- package/src/components/custom/Workout/SessionRail.stories.tsx +153 -0
- package/src/components/custom/Workout/SessionRail.test.tsx +86 -0
- package/src/components/custom/Workout/SessionRail.tsx +126 -0
- package/src/components/custom/Workout/SetBar.stories.tsx +124 -0
- package/src/components/custom/Workout/SetBar.test.tsx +123 -0
- package/src/components/custom/Workout/SetBar.tsx +171 -0
- package/src/components/custom/Workout/SetRow.stories.tsx +50 -100
- package/src/components/custom/Workout/SetRow.test.tsx +94 -166
- package/src/components/custom/Workout/SetRow.tsx +169 -232
- package/src/components/custom/Workout/SetStrip.stories.tsx +154 -0
- package/src/components/custom/Workout/SetStrip.test.tsx +109 -0
- package/src/components/custom/Workout/SetStrip.tsx +71 -0
- package/src/components/custom/Workout/SetTableHeader.stories.tsx +66 -0
- package/src/components/custom/Workout/SetTableHeader.test.tsx +47 -0
- package/src/components/custom/Workout/SetTableHeader.tsx +88 -0
- package/src/components/custom/Workout/SetsRepsLoad.stories.tsx +47 -0
- package/src/components/custom/Workout/SetsRepsLoad.test.tsx +44 -0
- package/src/components/custom/Workout/SetsRepsLoad.tsx +52 -0
- package/src/components/custom/Workout/Sparkline.stories.tsx +1 -1
- package/src/components/custom/Workout/StatusDot.stories.tsx +1 -1
- package/src/components/custom/Workout/StatusPill.stories.tsx +59 -0
- package/src/components/custom/Workout/StatusPill.test.tsx +63 -0
- package/src/components/custom/Workout/StatusPill.tsx +88 -0
- package/src/components/custom/Workout/StrengthTrendChart.stories.tsx +1 -1
- package/src/components/custom/Workout/SupersetWrapper.stories.tsx +1 -1
- package/src/components/custom/Workout/TempoBar.stories.tsx +70 -1
- package/src/components/custom/Workout/TempoBar.test.tsx +119 -4
- package/src/components/custom/Workout/TempoBar.tsx +107 -22
- package/src/components/custom/Workout/TempoDisplay.stories.tsx +12 -1
- package/src/components/custom/Workout/TempoDisplay.tsx +23 -33
- package/src/components/custom/Workout/TrainingStatusPage.stories.tsx +1 -1
- package/src/components/custom/Workout/VelocityStrip.modalities.stories.tsx +254 -0
- package/src/components/custom/Workout/VelocityStrip.responsive.stories.tsx +145 -0
- package/src/components/custom/Workout/VelocityStrip.stories.tsx +262 -56
- package/src/components/custom/Workout/VelocityStrip.test.tsx +440 -141
- package/src/components/custom/Workout/VelocityStrip.tsx +776 -141
- package/src/components/custom/Workout/VolumeLandmarkBar.stories.tsx +73 -0
- package/src/components/custom/Workout/VolumeLandmarkBar.test.tsx +119 -0
- package/src/components/custom/Workout/VolumeLandmarkBar.tsx +154 -0
- package/src/components/custom/Workout/WeekRow.stories.tsx +1 -1
- package/src/components/custom/Workout/WeightBadge.stories.tsx +1 -1
- package/src/components/custom/Workout/WorkoutCard.stories.tsx +2 -6
- package/src/components/custom/Workout/WorkoutCard.test.tsx +13 -46
- package/src/components/custom/Workout/WorkoutPill.stories.tsx +1 -1
- package/src/components/custom/Workout/ZoneTrack.stories.tsx +139 -0
- package/src/components/custom/Workout/ZoneTrack.test.tsx +234 -0
- package/src/components/custom/Workout/ZoneTrack.tsx +373 -0
- package/src/components/custom/Workout/index.ts +55 -2
- package/src/components/custom/Workout/metricText.stories.tsx +69 -0
- package/src/components/custom/Workout/metricText.tsx +34 -0
- package/src/components/custom/Workout/paceTone.test.ts +29 -0
- package/src/components/custom/Workout/paceTone.ts +27 -0
- package/src/components/custom/Workout/setHeadingKit.tsx +177 -0
- package/src/components/custom/index.ts +2 -0
- package/src/components/custom/stepper/Stepper.stories.tsx +1 -1
- package/src/components/icons/icons.stories.tsx +67 -2
- package/src/components/icons/icons.test.tsx +32 -2
- package/src/components/icons/icons.tsx +108 -0
- package/src/components/shell/BrandLockup.stories.tsx +3 -3
- package/src/components/shell/DashboardShell.stories.tsx +65 -0
- package/src/components/shell/DashboardShell.test.tsx +42 -0
- package/src/components/shell/DashboardShell.tsx +74 -0
- package/src/components/shell/DeviceIndicator.stories.tsx +2 -2
- package/src/components/shell/DeviceMenu.stories.tsx +5 -5
- package/src/components/shell/DeviceRow.stories.tsx +3 -3
- package/src/components/shell/NavItem.stories.tsx +52 -0
- package/src/components/shell/NavItem.test.tsx +35 -0
- package/src/components/shell/NavItem.tsx +75 -0
- package/src/components/shell/README.md +30 -0
- package/src/components/shell/SessionRailSpecimen.stories.tsx +753 -0
- package/src/components/shell/SessionStatePill.stories.tsx +3 -3
- package/src/components/shell/SessionStatePill.tsx +1 -1
- package/src/components/shell/SideNav.stories.tsx +81 -0
- package/src/components/shell/SideNav.test.tsx +51 -0
- package/src/components/shell/SideNav.tsx +74 -0
- package/src/components/shell/TopBar.stories.tsx +5 -5
- package/src/components/shell/index.ts +5 -0
- package/src/components/ui/alert/Alert.stories.tsx +86 -3
- package/src/components/ui/alert/Alert.test.tsx +52 -0
- package/src/components/ui/alert/Alert.tsx +54 -20
- package/src/components/ui/autocomplete/Autocomplete.stories.tsx +1 -1
- package/src/components/ui/avatar/Avatar.stories.tsx +1 -1
- package/src/components/ui/badge/Badge.stories.tsx +1 -1
- package/src/components/ui/breadcrumbs/Breadcrumbs.stories.tsx +1 -1
- package/src/components/ui/button/Button.stories.tsx +1 -1
- package/src/components/ui/card/Card.stories.tsx +1 -1
- package/src/components/ui/checkbox/Checkbox.stories.tsx +1 -1
- package/src/components/ui/chip/Chip.stories.tsx +1 -1
- package/src/components/ui/collapse/Collapse.stories.tsx +1 -1
- package/src/components/ui/data-row/DataRow.stories.tsx +1 -1
- package/src/components/ui/divider/Divider.stories.tsx +1 -1
- package/src/components/ui/drawer/Drawer.stories.tsx +1 -1
- package/src/components/ui/form-field/FormField.stories.tsx +1 -1
- package/src/components/ui/help-tip/HelpTip.stories.tsx +1 -1
- package/src/components/ui/icon-box/IconBox.stories.tsx +1 -1
- package/src/components/ui/index.ts +1 -0
- package/src/components/ui/indicator/Indicator.stories.tsx +2 -2
- package/src/components/ui/indicator/Indicator.test.tsx +1 -0
- package/src/components/ui/indicator/Indicator.tsx +6 -1
- package/src/components/ui/input/Input.stories.tsx +1 -1
- package/src/components/ui/link/Link.stories.tsx +1 -1
- package/src/components/ui/list-item/ListItem.stories.tsx +1 -1
- package/src/components/ui/menu/Menu.stories.tsx +1 -1
- package/src/components/ui/modal/Modal.stories.tsx +1 -1
- package/src/components/ui/pill/Pill.stories.tsx +1 -1
- package/src/components/ui/popover/Popover.stories.tsx +1 -1
- package/src/components/ui/progress/Progress.stories.tsx +1 -1
- package/src/components/ui/progress/Progress.tsx +47 -22
- package/src/components/ui/radio/Radio.stories.tsx +1 -1
- package/src/components/ui/section/Section.stories.tsx +1 -1
- package/src/components/ui/select/Select.stories.tsx +1 -1
- package/src/components/ui/skeleton/Skeleton.stories.tsx +1 -1
- package/src/components/ui/spinner/Spinner.stories.tsx +1 -1
- package/src/components/ui/stack/Stack.stories.tsx +1 -1
- package/src/components/ui/surface/Surface.stories.tsx +1 -1
- package/src/components/ui/switch/Switch.stories.tsx +1 -1
- package/src/components/ui/tabs/Tabs.stories.tsx +1 -1
- package/src/components/ui/tile/Tile.stories.tsx +79 -0
- package/src/components/ui/tile/Tile.test.tsx +48 -0
- package/src/components/ui/tile/Tile.tsx +64 -0
- package/src/components/ui/tile/index.ts +2 -0
- package/src/components/ui/toast/Toast.stories.tsx +1 -1
- package/src/components/ui/toolbar-button/ToolbarButton.stories.tsx +1 -1
- package/src/components/ui/tooltip/Tooltip.stories.tsx +1 -1
- package/src/examples/react-hook-form/ReactHookForm.stories.tsx +1 -1
- package/src/hooks/index.ts +2 -0
- package/src/hooks/useTimer.test.ts +150 -0
- package/src/hooks/useTimer.ts +77 -0
- package/src/index.ts +3 -0
- package/src/pages.ts +1 -1
- package/src/stories/PresetShowcase.stories.tsx +1 -1
- package/src/stories/ThemePresets.stories.tsx +1 -1
- package/src/theme/ColorSystem.stories.tsx +1 -1
- package/src/theme/Colors.stories.tsx +5 -8
- package/src/theme/config.ts +4 -0
- package/src/theme/elevation.stories.tsx +1 -1
- package/src/theme/global.css +5 -1
- package/src/theme/shadows.stories.tsx +1 -1
- package/src/theme/tokens/primitives.ts +11 -107
- package/src/theme/tokens/semantic.ts +8 -0
- package/src/theme/workout-tokens.ts +11 -7
- package/tailwind.config.js +5 -0
package/dist/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
|
}
|
|
@@ -92,6 +158,7 @@ var statusColors = {
|
|
|
92
158
|
subtle: "bg-status-success-subtle",
|
|
93
159
|
outline: "border-2 border-status-success bg-transparent",
|
|
94
160
|
solid: "bg-status-success",
|
|
161
|
+
border: "border-status-success",
|
|
95
162
|
icon: "text-status-success",
|
|
96
163
|
text: "text-status-success"
|
|
97
164
|
},
|
|
@@ -99,6 +166,7 @@ var statusColors = {
|
|
|
99
166
|
subtle: "bg-status-info-subtle",
|
|
100
167
|
outline: "border-2 border-status-info bg-transparent",
|
|
101
168
|
solid: "bg-status-info",
|
|
169
|
+
border: "border-status-info",
|
|
102
170
|
icon: "text-status-info",
|
|
103
171
|
text: "text-status-info"
|
|
104
172
|
},
|
|
@@ -106,6 +174,7 @@ var statusColors = {
|
|
|
106
174
|
subtle: "bg-status-warning-subtle",
|
|
107
175
|
outline: "border-2 border-status-warning bg-transparent",
|
|
108
176
|
solid: "bg-status-warning",
|
|
177
|
+
border: "border-status-warning",
|
|
109
178
|
icon: "text-status-warning",
|
|
110
179
|
text: "text-status-warning"
|
|
111
180
|
},
|
|
@@ -113,6 +182,7 @@ var statusColors = {
|
|
|
113
182
|
subtle: "bg-status-error-subtle",
|
|
114
183
|
outline: "border-2 border-status-error bg-transparent",
|
|
115
184
|
solid: "bg-status-error",
|
|
185
|
+
border: "border-status-error",
|
|
116
186
|
icon: "text-status-error",
|
|
117
187
|
text: "text-status-error"
|
|
118
188
|
}
|
|
@@ -126,6 +196,8 @@ var defaultIcons = {
|
|
|
126
196
|
function Alert({
|
|
127
197
|
status = "info",
|
|
128
198
|
variant = "subtle",
|
|
199
|
+
size = "default",
|
|
200
|
+
message,
|
|
129
201
|
icon,
|
|
130
202
|
showIcon = true,
|
|
131
203
|
onClose,
|
|
@@ -135,28 +207,43 @@ function Alert({
|
|
|
135
207
|
}) {
|
|
136
208
|
const colors = statusColors[status];
|
|
137
209
|
const isSolid = variant === "solid";
|
|
210
|
+
const isCompact = size === "compact";
|
|
138
211
|
return /* @__PURE__ */ jsxs(
|
|
139
212
|
reactNative.View,
|
|
140
213
|
{
|
|
141
214
|
accessibilityRole: "alert",
|
|
142
215
|
className: cn(
|
|
143
|
-
"flex-row items-start p-4 rounded-lg",
|
|
216
|
+
isCompact ? "flex-row items-center px-3 py-2 rounded-lg" : "flex-row items-start p-4 rounded-lg",
|
|
144
217
|
colors[variant],
|
|
218
|
+
// A compact `subtle` pill gets a hairline status border so it reads as a
|
|
219
|
+
// defined cue on a dark wall (the CueFlag look); other variants carry their own.
|
|
220
|
+
isCompact && variant === "subtle" && cn("border", colors.border),
|
|
145
221
|
className
|
|
146
222
|
),
|
|
147
223
|
...props,
|
|
148
224
|
children: [
|
|
149
|
-
showIcon && /* @__PURE__ */ jsx(reactNative.View, { className: "mr-3 mt-0.5", children: icon || /* @__PURE__ */ jsx(
|
|
225
|
+
showIcon && /* @__PURE__ */ jsx(reactNative.View, { className: isCompact ? "mr-2" : "mr-3 mt-0.5", children: icon || /* @__PURE__ */ jsx(
|
|
150
226
|
reactNative.Text,
|
|
151
227
|
{
|
|
152
228
|
className: cn(
|
|
153
|
-
"
|
|
229
|
+
"font-bold",
|
|
230
|
+
isCompact ? "text-base" : "text-lg",
|
|
154
231
|
isSolid ? "text-white" : colors.icon
|
|
155
232
|
),
|
|
156
233
|
children: defaultIcons[status]
|
|
157
234
|
}
|
|
158
235
|
) }),
|
|
159
|
-
/* @__PURE__ */
|
|
236
|
+
/* @__PURE__ */ jsxs(reactNative.View, { className: "flex-1", children: [
|
|
237
|
+
message != null && /* @__PURE__ */ jsx(
|
|
238
|
+
reactNative.Text,
|
|
239
|
+
{
|
|
240
|
+
className: cn("text-sm font-semibold", isSolid ? "text-white" : colors.text),
|
|
241
|
+
testID: "alert-message",
|
|
242
|
+
children: message
|
|
243
|
+
}
|
|
244
|
+
),
|
|
245
|
+
children
|
|
246
|
+
] }),
|
|
160
247
|
onClose && /* @__PURE__ */ jsx(
|
|
161
248
|
reactNative.Pressable,
|
|
162
249
|
{
|
|
@@ -180,7 +267,7 @@ function AlertDescription({ children, className }) {
|
|
|
180
267
|
|
|
181
268
|
// src/theme/tokens/primitives.ts
|
|
182
269
|
var primitiveColors = {
|
|
183
|
-
// Blue scale
|
|
270
|
+
// Blue scale
|
|
184
271
|
blue: {
|
|
185
272
|
50: "#EFF6FF",
|
|
186
273
|
100: "#DBEAFE",
|
|
@@ -189,124 +276,36 @@ var primitiveColors = {
|
|
|
189
276
|
400: "#60A5FA",
|
|
190
277
|
500: "#3B82F6",
|
|
191
278
|
600: "#5048E5",
|
|
192
|
-
// Primary main
|
|
193
279
|
700: "#3832A0",
|
|
194
|
-
// Primary dark
|
|
195
280
|
800: "#1E40AF",
|
|
196
281
|
900: "#1E3A8A"
|
|
197
282
|
},
|
|
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)
|
|
283
|
+
// Red scale
|
|
231
284
|
red: {
|
|
232
285
|
50: "#FEF2F2",
|
|
233
286
|
100: "#FEE2E2",
|
|
234
287
|
200: "#FECACA",
|
|
235
288
|
300: "#FCA5A5",
|
|
236
289
|
400: "#DA6868",
|
|
237
|
-
// Error light
|
|
238
290
|
500: "#EF4444",
|
|
239
291
|
600: "#D14343",
|
|
240
|
-
// Error main
|
|
241
292
|
700: "#922E2E",
|
|
242
|
-
// Error dark
|
|
243
293
|
800: "#991B1B",
|
|
244
294
|
900: "#7F1D1D"
|
|
245
295
|
},
|
|
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)
|
|
296
|
+
// Vivid red scale
|
|
263
297
|
redVivid: {
|
|
264
298
|
50: "#FFECEE",
|
|
265
299
|
100: "#FFD6DB",
|
|
266
300
|
200: "#FFB3BC",
|
|
267
301
|
300: "#FF8593",
|
|
268
302
|
400: "#FF6070",
|
|
269
|
-
// Error vivid light
|
|
270
303
|
500: "#FF4757",
|
|
271
|
-
// Error vivid main
|
|
272
304
|
600: "#E63548",
|
|
273
305
|
700: "#C42539",
|
|
274
|
-
// Error vivid dark
|
|
275
306
|
800: "#9E1C2C",
|
|
276
307
|
900: "#7A1520"
|
|
277
308
|
},
|
|
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
309
|
// Neutral/Gray scale
|
|
311
310
|
neutral: {
|
|
312
311
|
50: "#FAFAFA",
|
|
@@ -321,18 +320,6 @@ var primitiveColors = {
|
|
|
321
320
|
900: "#111827",
|
|
322
321
|
950: "#030712"
|
|
323
322
|
},
|
|
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
323
|
// Charcoal scale (dark backgrounds)
|
|
337
324
|
charcoal: {
|
|
338
325
|
0: "#6E6E6E",
|
|
@@ -347,18 +334,6 @@ var primitiveColors = {
|
|
|
347
334
|
800: "#131313",
|
|
348
335
|
900: "#101010"
|
|
349
336
|
},
|
|
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
337
|
// Pure colors
|
|
363
338
|
white: "#FFFFFF",
|
|
364
339
|
black: "#000000",
|
|
@@ -749,6 +724,9 @@ var semanticColorsLight = {
|
|
|
749
724
|
"status-success-light": primitiveRamps.green[200],
|
|
750
725
|
"status-success-dark": primitiveRamps.green[600],
|
|
751
726
|
"status-success-subtle": primitiveRamps.green[50],
|
|
727
|
+
// Live-session accent — its OWN role, decoupled from success so the two can diverge
|
|
728
|
+
"status-live": primitiveRamps.green[300],
|
|
729
|
+
"status-live-muted": primitiveRamps.green[500],
|
|
752
730
|
"status-error": primitiveRamps.red[600],
|
|
753
731
|
"status-error-light": primitiveRamps.red[500],
|
|
754
732
|
"status-error-dark": primitiveRamps.red[700],
|
|
@@ -882,6 +860,9 @@ var semanticColorsDark = {
|
|
|
882
860
|
"status-success-light": primitiveRamps.green[200],
|
|
883
861
|
"status-success-dark": primitiveRamps.green[600],
|
|
884
862
|
"status-success-subtle": "rgba(46, 213, 115, 0.12)",
|
|
863
|
+
// Live-session accent — its OWN role, decoupled from success so the two can diverge
|
|
864
|
+
"status-live": primitiveRamps.green[300],
|
|
865
|
+
"status-live-muted": primitiveRamps.green[500],
|
|
885
866
|
"status-error": primitiveRamps.red[600],
|
|
886
867
|
"status-error-light": primitiveRamps.red[500],
|
|
887
868
|
"status-error-dark": primitiveRamps.red[700],
|
|
@@ -1476,6 +1457,8 @@ var lightThemeCSSVars = {
|
|
|
1476
1457
|
"--color-on-brand-secondary": semanticColorsLight["on-brand-secondary"],
|
|
1477
1458
|
"--color-status-success": semanticColorsLight["status-success"],
|
|
1478
1459
|
"--color-status-success-subtle": semanticColorsLight["status-success-subtle"],
|
|
1460
|
+
"--color-status-live": semanticColorsLight["status-live"],
|
|
1461
|
+
"--color-status-live-muted": semanticColorsLight["status-live-muted"],
|
|
1479
1462
|
"--color-status-error": semanticColorsLight["status-error"],
|
|
1480
1463
|
"--color-status-error-subtle": semanticColorsLight["status-error-subtle"],
|
|
1481
1464
|
"--color-status-warning": semanticColorsLight["status-warning"],
|
|
@@ -1572,6 +1555,8 @@ var darkThemeCSSVars = {
|
|
|
1572
1555
|
"--color-on-brand-secondary": semanticColorsDark["on-brand-secondary"],
|
|
1573
1556
|
"--color-status-success": semanticColorsDark["status-success"],
|
|
1574
1557
|
"--color-status-success-subtle": semanticColorsDark["status-success-subtle"],
|
|
1558
|
+
"--color-status-live": semanticColorsDark["status-live"],
|
|
1559
|
+
"--color-status-live-muted": semanticColorsDark["status-live-muted"],
|
|
1575
1560
|
"--color-status-error": semanticColorsDark["status-error"],
|
|
1576
1561
|
"--color-status-error-subtle": semanticColorsDark["status-error-subtle"],
|
|
1577
1562
|
"--color-status-warning": semanticColorsDark["status-warning"],
|
|
@@ -2789,6 +2774,7 @@ var colorStyles = {
|
|
|
2789
2774
|
default: "bg-text-tertiary",
|
|
2790
2775
|
primary: "bg-brand-primary",
|
|
2791
2776
|
success: "bg-status-success",
|
|
2777
|
+
live: "bg-status-live",
|
|
2792
2778
|
error: "bg-status-error",
|
|
2793
2779
|
warning: "bg-status-warning",
|
|
2794
2780
|
info: "bg-status-info",
|
|
@@ -2845,7 +2831,7 @@ function Indicator({
|
|
|
2845
2831
|
colorClass,
|
|
2846
2832
|
pulseMode === "opacity" && "animate-pulse",
|
|
2847
2833
|
ring && "border-2 border-background-base",
|
|
2848
|
-
glow && !customColor && color === "success" && "shadow-[0_0_6px_rgba(46,213,115,0.6)]",
|
|
2834
|
+
glow && !customColor && (color === "success" || color === "live") && "shadow-[0_0_6px_rgba(46,213,115,0.6)]",
|
|
2849
2835
|
glow && !customColor && color === "error" && "shadow-[0_0_6px_rgba(239,68,68,0.6)]",
|
|
2850
2836
|
glow && !customColor && color === "warning" && "shadow-[0_0_6px_rgba(245,158,11,0.6)]",
|
|
2851
2837
|
glow && !customColor && color === "primary" && "shadow-[0_0_6px_rgba(255,121,0,0.6)]",
|
|
@@ -5107,6 +5093,8 @@ function CircularProgress({
|
|
|
5107
5093
|
color = "primary",
|
|
5108
5094
|
showValue = false,
|
|
5109
5095
|
formatValue: formatValue3 = (v, m) => `${Math.round(v / m * 100)}%`,
|
|
5096
|
+
children,
|
|
5097
|
+
fill = false,
|
|
5110
5098
|
className,
|
|
5111
5099
|
...props
|
|
5112
5100
|
}) {
|
|
@@ -5115,11 +5103,12 @@ function CircularProgress({
|
|
|
5115
5103
|
const circumference = 2 * Math.PI * radius;
|
|
5116
5104
|
const strokeDashoffset = circumference - percentage / 100 * circumference;
|
|
5117
5105
|
const center = size / 2;
|
|
5106
|
+
const boxStyle = fill ? { width: "100%", aspectRatio: 1 } : { width: size, height: size };
|
|
5118
5107
|
return /* @__PURE__ */ jsxs(
|
|
5119
5108
|
reactNative.View,
|
|
5120
5109
|
{
|
|
5121
5110
|
className: cn("items-center justify-center", className),
|
|
5122
|
-
style:
|
|
5111
|
+
style: boxStyle,
|
|
5123
5112
|
accessibilityRole: "progressbar",
|
|
5124
5113
|
accessibilityValue: {
|
|
5125
5114
|
min: 0,
|
|
@@ -5128,52 +5117,56 @@ function CircularProgress({
|
|
|
5128
5117
|
},
|
|
5129
5118
|
...props,
|
|
5130
5119
|
children: [
|
|
5131
|
-
/* @__PURE__ */ jsx(
|
|
5120
|
+
/* @__PURE__ */ jsx(reactNative.View, { className: cn(isIndeterminate && "animate-spin"), style: boxStyle, children: /* @__PURE__ */ jsxs(
|
|
5121
|
+
"svg",
|
|
5122
|
+
{
|
|
5123
|
+
width: "100%",
|
|
5124
|
+
height: "100%",
|
|
5125
|
+
viewBox: `0 0 ${size} ${size}`,
|
|
5126
|
+
style: { position: "absolute" },
|
|
5127
|
+
children: [
|
|
5128
|
+
/* @__PURE__ */ jsx(
|
|
5129
|
+
"circle",
|
|
5130
|
+
{
|
|
5131
|
+
cx: center,
|
|
5132
|
+
cy: center,
|
|
5133
|
+
r: radius,
|
|
5134
|
+
fill: "none",
|
|
5135
|
+
stroke: "var(--color-border-default)",
|
|
5136
|
+
strokeWidth
|
|
5137
|
+
}
|
|
5138
|
+
),
|
|
5139
|
+
/* @__PURE__ */ jsx(
|
|
5140
|
+
"circle",
|
|
5141
|
+
{
|
|
5142
|
+
cx: center,
|
|
5143
|
+
cy: center,
|
|
5144
|
+
r: radius,
|
|
5145
|
+
fill: "none",
|
|
5146
|
+
stroke: colorVarMap[color],
|
|
5147
|
+
strokeWidth,
|
|
5148
|
+
strokeLinecap: "round",
|
|
5149
|
+
strokeDasharray: circumference,
|
|
5150
|
+
strokeDashoffset: isIndeterminate ? circumference * 0.75 : strokeDashoffset,
|
|
5151
|
+
transform: `rotate(-90 ${center} ${center})`,
|
|
5152
|
+
style: { transition: "stroke-dashoffset 300ms cubic-bezier(0.22, 1, 0.36, 1)" }
|
|
5153
|
+
}
|
|
5154
|
+
)
|
|
5155
|
+
]
|
|
5156
|
+
}
|
|
5157
|
+
) }),
|
|
5158
|
+
children != null ? /* @__PURE__ */ jsx(
|
|
5132
5159
|
reactNative.View,
|
|
5133
5160
|
{
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
"
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
viewBox: `0 0 ${size} ${size}`,
|
|
5142
|
-
style: { position: "absolute" },
|
|
5143
|
-
children: [
|
|
5144
|
-
/* @__PURE__ */ jsx(
|
|
5145
|
-
"circle",
|
|
5146
|
-
{
|
|
5147
|
-
cx: center,
|
|
5148
|
-
cy: center,
|
|
5149
|
-
r: radius,
|
|
5150
|
-
fill: "none",
|
|
5151
|
-
stroke: "var(--color-border-default)",
|
|
5152
|
-
strokeWidth
|
|
5153
|
-
}
|
|
5154
|
-
),
|
|
5155
|
-
/* @__PURE__ */ jsx(
|
|
5156
|
-
"circle",
|
|
5157
|
-
{
|
|
5158
|
-
cx: center,
|
|
5159
|
-
cy: center,
|
|
5160
|
-
r: radius,
|
|
5161
|
-
fill: "none",
|
|
5162
|
-
stroke: colorVarMap[color],
|
|
5163
|
-
strokeWidth,
|
|
5164
|
-
strokeLinecap: "round",
|
|
5165
|
-
strokeDasharray: circumference,
|
|
5166
|
-
strokeDashoffset: isIndeterminate ? circumference * 0.75 : strokeDashoffset,
|
|
5167
|
-
transform: `rotate(-90 ${center} ${center})`,
|
|
5168
|
-
style: { transition: "stroke-dashoffset 300ms cubic-bezier(0.22, 1, 0.36, 1)" }
|
|
5169
|
-
}
|
|
5170
|
-
)
|
|
5171
|
-
]
|
|
5172
|
-
}
|
|
5173
|
-
)
|
|
5161
|
+
style: {
|
|
5162
|
+
position: "absolute",
|
|
5163
|
+
alignItems: "center",
|
|
5164
|
+
justifyContent: "center",
|
|
5165
|
+
pointerEvents: "none"
|
|
5166
|
+
},
|
|
5167
|
+
children
|
|
5174
5168
|
}
|
|
5175
|
-
)
|
|
5176
|
-
showValue && !isIndeterminate && /* @__PURE__ */ jsx(
|
|
5169
|
+
) : showValue && !isIndeterminate && /* @__PURE__ */ jsx(
|
|
5177
5170
|
reactNative.Text,
|
|
5178
5171
|
{
|
|
5179
5172
|
className: "text-xs font-semibold text-text-primary absolute",
|
|
@@ -5269,7 +5262,7 @@ function ToastProvider({
|
|
|
5269
5262
|
return id;
|
|
5270
5263
|
}, [defaultDuration, maxToasts]);
|
|
5271
5264
|
const removeToast = React24.useCallback((id) => {
|
|
5272
|
-
setToasts((prev) => prev.filter((
|
|
5265
|
+
setToasts((prev) => prev.filter((t18) => t18.id !== id));
|
|
5273
5266
|
}, []);
|
|
5274
5267
|
const removeAllToasts = React24.useCallback(() => {
|
|
5275
5268
|
setToasts([]);
|
|
@@ -6138,6 +6131,50 @@ function Pill({
|
|
|
6138
6131
|
}
|
|
6139
6132
|
return /* @__PURE__ */ jsx(reactNative.View, { className: containerClasses, ...props, children: content });
|
|
6140
6133
|
}
|
|
6134
|
+
function Tile({
|
|
6135
|
+
label,
|
|
6136
|
+
value,
|
|
6137
|
+
valueColor,
|
|
6138
|
+
align = "center",
|
|
6139
|
+
className,
|
|
6140
|
+
...props
|
|
6141
|
+
}) {
|
|
6142
|
+
const isCenter = align === "center";
|
|
6143
|
+
return /* @__PURE__ */ jsxs(
|
|
6144
|
+
reactNative.View,
|
|
6145
|
+
{
|
|
6146
|
+
className: cn(
|
|
6147
|
+
"flex-1 rounded-md bg-surface-raised px-1.5 py-2",
|
|
6148
|
+
isCenter ? "items-center" : "items-start",
|
|
6149
|
+
className
|
|
6150
|
+
),
|
|
6151
|
+
...props,
|
|
6152
|
+
children: [
|
|
6153
|
+
/* @__PURE__ */ jsx(
|
|
6154
|
+
reactNative.Text,
|
|
6155
|
+
{
|
|
6156
|
+
className: cn(
|
|
6157
|
+
"text-[10px] font-bold uppercase tracking-wider text-text-tertiary",
|
|
6158
|
+
isCenter ? "text-center" : "text-left"
|
|
6159
|
+
),
|
|
6160
|
+
children: label
|
|
6161
|
+
}
|
|
6162
|
+
),
|
|
6163
|
+
/* @__PURE__ */ jsx(
|
|
6164
|
+
reactNative.Text,
|
|
6165
|
+
{
|
|
6166
|
+
className: cn(
|
|
6167
|
+
"mt-0.5 font-mono text-sm font-bold text-text-primary",
|
|
6168
|
+
isCenter ? "text-center" : "text-left"
|
|
6169
|
+
),
|
|
6170
|
+
style: valueColor ? { color: valueColor } : void 0,
|
|
6171
|
+
children: value
|
|
6172
|
+
}
|
|
6173
|
+
)
|
|
6174
|
+
]
|
|
6175
|
+
}
|
|
6176
|
+
);
|
|
6177
|
+
}
|
|
6141
6178
|
var variantStyles4 = {
|
|
6142
6179
|
h1: "font-heading text-5xl font-bold leading-tight",
|
|
6143
6180
|
h2: "font-heading text-4xl font-bold leading-tight",
|
|
@@ -6997,6 +7034,143 @@ function formatDateTime(value, format = "datetime", isUTC = false, fallback = "-
|
|
|
6997
7034
|
}
|
|
6998
7035
|
return formatDate(value, format, isUTC);
|
|
6999
7036
|
}
|
|
7037
|
+
function formatDuration(ms) {
|
|
7038
|
+
const totalSeconds = Math.max(0, Math.ceil(ms / 1e3));
|
|
7039
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
7040
|
+
const seconds = totalSeconds % 60;
|
|
7041
|
+
return `${minutes}:${String(seconds).padStart(2, "0")}`;
|
|
7042
|
+
}
|
|
7043
|
+
function useTimer(options = {}) {
|
|
7044
|
+
const {
|
|
7045
|
+
mode = "up",
|
|
7046
|
+
durationMs,
|
|
7047
|
+
elapsedMs: controlledElapsedMs,
|
|
7048
|
+
running = false,
|
|
7049
|
+
autoTick = false,
|
|
7050
|
+
tickMs = 1e3
|
|
7051
|
+
} = options;
|
|
7052
|
+
const isControlled = controlledElapsedMs != null;
|
|
7053
|
+
const [internalElapsedMs, setInternalElapsedMs] = React24.useState(0);
|
|
7054
|
+
React24.useEffect(() => {
|
|
7055
|
+
if (isControlled || !autoTick || !running) return;
|
|
7056
|
+
const id = setInterval(() => {
|
|
7057
|
+
setInternalElapsedMs((prev) => prev + tickMs);
|
|
7058
|
+
}, tickMs);
|
|
7059
|
+
return () => clearInterval(id);
|
|
7060
|
+
}, [isControlled, autoTick, running, tickMs]);
|
|
7061
|
+
const elapsedMs = isControlled ? controlledElapsedMs : internalElapsedMs;
|
|
7062
|
+
const hasDuration = durationMs != null && durationMs > 0;
|
|
7063
|
+
const remainingMs = durationMs != null ? Math.max(0, durationMs - elapsedMs) : 0;
|
|
7064
|
+
let progress;
|
|
7065
|
+
if (hasDuration) {
|
|
7066
|
+
progress = Math.min(1, Math.max(0, elapsedMs / durationMs));
|
|
7067
|
+
} else {
|
|
7068
|
+
progress = mode === "down" ? 1 : 0;
|
|
7069
|
+
}
|
|
7070
|
+
const label = formatDuration(mode === "down" ? remainingMs : elapsedMs);
|
|
7071
|
+
const done = durationMs != null && elapsedMs >= durationMs;
|
|
7072
|
+
return { elapsedMs, remainingMs, progress, label, done };
|
|
7073
|
+
}
|
|
7074
|
+
|
|
7075
|
+
// src/components/custom/TimerReadout/TimerReadout.tsx
|
|
7076
|
+
var semantic = getSemanticColors("dark");
|
|
7077
|
+
var LIVE_GREEN = primitiveRamps.green[500];
|
|
7078
|
+
var SECONDARY = semantic["text-secondary"];
|
|
7079
|
+
var TERTIARY = semantic["text-tertiary"];
|
|
7080
|
+
var DEFAULT_GLYPH = "\u23F1";
|
|
7081
|
+
function TimerReadout({
|
|
7082
|
+
mode = "up",
|
|
7083
|
+
elapsedMs,
|
|
7084
|
+
durationMs,
|
|
7085
|
+
running = false,
|
|
7086
|
+
autoTick = false,
|
|
7087
|
+
showTotal = false,
|
|
7088
|
+
glyph = DEFAULT_GLYPH,
|
|
7089
|
+
className,
|
|
7090
|
+
...rest
|
|
7091
|
+
}) {
|
|
7092
|
+
const { label } = useTimer({ mode, durationMs, elapsedMs, running, autoTick });
|
|
7093
|
+
const currentColor = running ? LIVE_GREEN : SECONDARY;
|
|
7094
|
+
const total = showTotal && durationMs != null ? formatDuration(durationMs) : null;
|
|
7095
|
+
return /* @__PURE__ */ jsx(
|
|
7096
|
+
reactNative.View,
|
|
7097
|
+
{
|
|
7098
|
+
accessibilityRole: "timer",
|
|
7099
|
+
accessibilityLabel: total != null ? `${label} of ${total}` : label,
|
|
7100
|
+
className: cn("flex-row items-baseline justify-end", className),
|
|
7101
|
+
...rest,
|
|
7102
|
+
children: /* @__PURE__ */ jsxs(Typography, { variant: "mono", style: { fontSize: 11, textAlign: "right" }, children: [
|
|
7103
|
+
/* @__PURE__ */ jsx(reactNative.Text, { testID: "timer-readout-glyph", style: { color: currentColor, marginRight: 3 }, children: glyph }),
|
|
7104
|
+
/* @__PURE__ */ jsx(reactNative.Text, { testID: "timer-readout-current", style: { color: currentColor }, children: label }),
|
|
7105
|
+
total != null ? /* @__PURE__ */ jsx(reactNative.Text, { testID: "timer-readout-total", style: { color: TERTIARY }, children: ` / ${total}` }) : null
|
|
7106
|
+
] })
|
|
7107
|
+
}
|
|
7108
|
+
);
|
|
7109
|
+
}
|
|
7110
|
+
function CircularTimer({
|
|
7111
|
+
durationMs,
|
|
7112
|
+
elapsedMs,
|
|
7113
|
+
mode = "down",
|
|
7114
|
+
size = 160,
|
|
7115
|
+
strokeWidth = 8,
|
|
7116
|
+
fill = false,
|
|
7117
|
+
color = "primary",
|
|
7118
|
+
doneLabel,
|
|
7119
|
+
doneColor = "success",
|
|
7120
|
+
controls,
|
|
7121
|
+
accessibilityLabel: accessibilityLabel2,
|
|
7122
|
+
testID
|
|
7123
|
+
}) {
|
|
7124
|
+
const { remainingMs, progress, label, done } = useTimer({ mode, durationMs, elapsedMs });
|
|
7125
|
+
const isDone = done && mode === "down";
|
|
7126
|
+
const showDone = isDone && doneLabel != null;
|
|
7127
|
+
const remainingFrac = durationMs > 0 ? Math.max(0, Math.min(1, remainingMs / durationMs)) : 0;
|
|
7128
|
+
const arc = mode === "down" ? showDone ? 1 : remainingFrac : progress;
|
|
7129
|
+
const ringColor = showDone ? doneColor : color;
|
|
7130
|
+
const remainingSec = Math.ceil(Math.max(0, remainingMs) / 1e3);
|
|
7131
|
+
const a11y = accessibilityLabel2 ?? (isDone ? "Timer complete" : `${remainingSec} seconds remaining`);
|
|
7132
|
+
return /* @__PURE__ */ jsxs(
|
|
7133
|
+
reactNative.View,
|
|
7134
|
+
{
|
|
7135
|
+
style: { alignItems: "center", gap: 16 },
|
|
7136
|
+
accessibilityRole: "timer",
|
|
7137
|
+
accessibilityLabel: a11y,
|
|
7138
|
+
testID: testID ?? "circular-timer",
|
|
7139
|
+
children: [
|
|
7140
|
+
/* @__PURE__ */ jsx(
|
|
7141
|
+
CircularProgress,
|
|
7142
|
+
{
|
|
7143
|
+
value: arc,
|
|
7144
|
+
max: 1,
|
|
7145
|
+
size,
|
|
7146
|
+
strokeWidth,
|
|
7147
|
+
fill,
|
|
7148
|
+
color: ringColor,
|
|
7149
|
+
accessibilityLabel: "Timer ring",
|
|
7150
|
+
testID: "circular-timer-ring",
|
|
7151
|
+
children: /* @__PURE__ */ jsx(
|
|
7152
|
+
reactNative.Text,
|
|
7153
|
+
{
|
|
7154
|
+
className: showDone ? void 0 : "text-text-primary",
|
|
7155
|
+
style: {
|
|
7156
|
+
fontSize: Math.round(size * (showDone ? 0.28 : 0.24)),
|
|
7157
|
+
fontFamily: '"Space Grotesk", sans-serif',
|
|
7158
|
+
fontWeight: "800",
|
|
7159
|
+
fontVariant: ["tabular-nums"],
|
|
7160
|
+
letterSpacing: showDone ? 1 : -1,
|
|
7161
|
+
...showDone ? { color: colorVarMap[doneColor] } : null
|
|
7162
|
+
},
|
|
7163
|
+
testID: "circular-timer-label",
|
|
7164
|
+
children: showDone ? doneLabel : label
|
|
7165
|
+
}
|
|
7166
|
+
)
|
|
7167
|
+
}
|
|
7168
|
+
),
|
|
7169
|
+
controls
|
|
7170
|
+
]
|
|
7171
|
+
}
|
|
7172
|
+
);
|
|
7173
|
+
}
|
|
7000
7174
|
var sizeConfig2 = {
|
|
7001
7175
|
sm: { value: "text-lg font-bold", label: "text-xs", unit: "text-xs" },
|
|
7002
7176
|
md: { value: "text-2xl font-bold", label: "text-xs", unit: "text-sm" },
|
|
@@ -7426,15 +7600,22 @@ var WORKOUT_TOKENS = {
|
|
|
7426
7600
|
// Canonical 4-band performance scale — the single source for BOTH the
|
|
7427
7601
|
// VelocityStrip zone bars and the SetRow RPE color (TD-03.43). The direction
|
|
7428
7602
|
// 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
|
-
//
|
|
7603
|
+
// fastest/best; for RPE, green = easiest. This is the [0,2,3,4] subsample of
|
|
7604
|
+
// the canonical `sequentialEffort` primitive: an EVEN hue walk across the ramp
|
|
7605
|
+
// (green → gold → orange → red). The earlier [0,1,2,4] spent two of its four
|
|
7606
|
+
// bands on adjacent yellows (amber-200 + amber-300) and skipped orange, reading
|
|
7607
|
+
// yellow-heavy with an abrupt jump to red; sampling amber-300 + orange-400
|
|
7608
|
+
// instead gives four cleanly separable bands that hold at 3px strip scale.
|
|
7609
|
+
// Consumed inline (RN).
|
|
7432
7610
|
scale: {
|
|
7433
7611
|
green: sequentialEffort[0],
|
|
7434
|
-
|
|
7435
|
-
|
|
7436
|
-
// gold
|
|
7612
|
+
// green-300
|
|
7613
|
+
yellow: sequentialEffort[2],
|
|
7614
|
+
// amber-300 (gold)
|
|
7615
|
+
orange: sequentialEffort[3],
|
|
7616
|
+
// orange-400 (true orange)
|
|
7437
7617
|
red: sequentialEffort[4]
|
|
7618
|
+
// red-600
|
|
7438
7619
|
},
|
|
7439
7620
|
// BodyMap volume heatmap — the canonical `divergingScale` (under → optimal →
|
|
7440
7621
|
// over): a true diverging shape with a light green center, cool-blue under-
|
|
@@ -7559,8 +7740,8 @@ function roundTempo(tempo) {
|
|
|
7559
7740
|
return tempo.map((v) => Math.round(v * 10) / 10);
|
|
7560
7741
|
}
|
|
7561
7742
|
function formatSignedPct(ratio) {
|
|
7562
|
-
const
|
|
7563
|
-
return `${
|
|
7743
|
+
const pct2 = Math.round(ratio * 100);
|
|
7744
|
+
return `${pct2 > 0 ? "+" : ""}${pct2}%`;
|
|
7564
7745
|
}
|
|
7565
7746
|
function formatPrescription(p) {
|
|
7566
7747
|
if (p == null) return null;
|
|
@@ -7640,6 +7821,11 @@ var TEMPO_PACING = {
|
|
|
7640
7821
|
behindThresholdPct: 0.15,
|
|
7641
7822
|
minPhaseDurationMs: 500
|
|
7642
7823
|
};
|
|
7824
|
+
function formatTempoDelta(remainingMs) {
|
|
7825
|
+
const s = remainingMs / 1e3;
|
|
7826
|
+
const rounded = Math.abs(s) < 0.05 ? 0 : s;
|
|
7827
|
+
return rounded.toFixed(1);
|
|
7828
|
+
}
|
|
7643
7829
|
function getTempoPacingState(elapsedMs, targetMs) {
|
|
7644
7830
|
if (!targetMs || targetMs < TEMPO_PACING.minPhaseDurationMs) return "none";
|
|
7645
7831
|
const ratio = elapsedMs / targetMs;
|
|
@@ -7652,24 +7838,33 @@ function getTempoFillPct(elapsedMs, targetMs) {
|
|
|
7652
7838
|
}
|
|
7653
7839
|
var PHASE_ORDER = ["concentric", "hold", "eccentric"];
|
|
7654
7840
|
var PHASE_CONFIG = {
|
|
7655
|
-
concentric: { label: "Con", color: t2["status-success"], flex: 2 },
|
|
7656
|
-
hold: { label: "Hold", color: t2["brand-primary"], flex: 1 },
|
|
7657
|
-
eccentric: { label: "Ecc", color: t2["status-warning"], flex: 3 }
|
|
7841
|
+
concentric: { label: "Con", wallLabel: "Concentric", color: t2["status-success"], flex: 2 },
|
|
7842
|
+
hold: { label: "Hold", wallLabel: "Hold", color: t2["brand-primary"], flex: 1 },
|
|
7843
|
+
eccentric: { label: "Ecc", wallLabel: "Eccentric", color: t2["status-warning"], flex: 3 }
|
|
7844
|
+
};
|
|
7845
|
+
var TEMPO_SIZES = {
|
|
7846
|
+
default: { labelMargin: 4, labelFont: 10, barGap: 2, barHeight: 20, radius: 4, segFont: 12 },
|
|
7847
|
+
wall: { labelMargin: 8, labelFont: 15, barGap: 4, barHeight: 40, radius: 6, segFont: 22 }
|
|
7658
7848
|
};
|
|
7659
7849
|
function TempoBar({
|
|
7660
7850
|
activePhase,
|
|
7661
7851
|
phaseElapsedMs,
|
|
7662
7852
|
completed,
|
|
7663
7853
|
target,
|
|
7854
|
+
size = "default",
|
|
7855
|
+
activeDisplay = "time",
|
|
7664
7856
|
className,
|
|
7665
7857
|
...props
|
|
7666
7858
|
}) {
|
|
7859
|
+
const s = TEMPO_SIZES[size];
|
|
7860
|
+
const [display, setDisplay] = React24.useState(activeDisplay);
|
|
7861
|
+
const toggleDisplay = () => setDisplay((d) => d === "time" ? "delta" : "time");
|
|
7667
7862
|
return /* @__PURE__ */ jsxs(reactNative.View, { className, testID: "tempo-bar", ...props, children: [
|
|
7668
|
-
/* @__PURE__ */ jsx(reactNative.View, { style: { flexDirection: "row", marginBottom:
|
|
7863
|
+
/* @__PURE__ */ jsx(reactNative.View, { style: { flexDirection: "row", marginBottom: s.labelMargin }, children: PHASE_ORDER.map((phase) => {
|
|
7669
7864
|
const config = PHASE_CONFIG[phase];
|
|
7670
|
-
return /* @__PURE__ */ jsx(reactNative.View, { style: { flex: config.flex, alignItems: "center" }, children: /* @__PURE__ */ jsx(reactNative.Text, { style: { fontSize:
|
|
7865
|
+
return /* @__PURE__ */ jsx(reactNative.View, { style: { flex: config.flex, alignItems: "center" }, children: /* @__PURE__ */ jsx(reactNative.Text, { numberOfLines: 1, style: { fontSize: s.labelFont, color: t2["text-disabled"] }, children: size === "wall" ? config.wallLabel : config.label }) }, phase);
|
|
7671
7866
|
}) }),
|
|
7672
|
-
/* @__PURE__ */ jsx(reactNative.View, { style: { flexDirection: "row", gap:
|
|
7867
|
+
/* @__PURE__ */ jsx(reactNative.View, { style: { flexDirection: "row", gap: s.barGap, height: s.barHeight }, children: PHASE_ORDER.map((phase) => {
|
|
7673
7868
|
const config = PHASE_CONFIG[phase];
|
|
7674
7869
|
const isActive = activePhase === phase;
|
|
7675
7870
|
const completedMs = completed?.[phase];
|
|
@@ -7681,7 +7876,7 @@ function TempoBar({
|
|
|
7681
7876
|
style: {
|
|
7682
7877
|
flex: config.flex,
|
|
7683
7878
|
backgroundColor: alpha("#ffffff", 0.06),
|
|
7684
|
-
borderRadius:
|
|
7879
|
+
borderRadius: s.radius,
|
|
7685
7880
|
overflow: "hidden"
|
|
7686
7881
|
},
|
|
7687
7882
|
children: isActive ? /* @__PURE__ */ jsx(
|
|
@@ -7689,9 +7884,21 @@ function TempoBar({
|
|
|
7689
7884
|
{
|
|
7690
7885
|
config,
|
|
7691
7886
|
phaseElapsedMs,
|
|
7692
|
-
targetMs
|
|
7887
|
+
targetMs,
|
|
7888
|
+
radius: s.radius,
|
|
7889
|
+
font: s.segFont,
|
|
7890
|
+
display,
|
|
7891
|
+
onToggleDisplay: toggleDisplay
|
|
7892
|
+
}
|
|
7893
|
+
) : completedMs != null ? /* @__PURE__ */ jsx(
|
|
7894
|
+
CompletedSegment,
|
|
7895
|
+
{
|
|
7896
|
+
config,
|
|
7897
|
+
completedMs,
|
|
7898
|
+
targetMs,
|
|
7899
|
+
font: s.segFont
|
|
7693
7900
|
}
|
|
7694
|
-
) :
|
|
7901
|
+
) : null
|
|
7695
7902
|
},
|
|
7696
7903
|
phase
|
|
7697
7904
|
);
|
|
@@ -7701,49 +7908,65 @@ function TempoBar({
|
|
|
7701
7908
|
function ActiveSegment({
|
|
7702
7909
|
config,
|
|
7703
7910
|
phaseElapsedMs,
|
|
7704
|
-
targetMs
|
|
7911
|
+
targetMs,
|
|
7912
|
+
radius,
|
|
7913
|
+
font,
|
|
7914
|
+
display,
|
|
7915
|
+
onToggleDisplay
|
|
7705
7916
|
}) {
|
|
7706
7917
|
const pacing = getTempoPacingState(phaseElapsedMs, targetMs);
|
|
7707
7918
|
const barColor = pacing === "behind" ? t2["status-error"] : config.color;
|
|
7708
7919
|
const fillPct = getTempoFillPct(phaseElapsedMs, targetMs);
|
|
7709
|
-
const label = targetMs ? `${
|
|
7710
|
-
return /* @__PURE__ */ jsxs(
|
|
7711
|
-
|
|
7712
|
-
|
|
7713
|
-
|
|
7714
|
-
|
|
7715
|
-
|
|
7716
|
-
|
|
7717
|
-
|
|
7718
|
-
|
|
7719
|
-
|
|
7720
|
-
|
|
7721
|
-
|
|
7722
|
-
|
|
7723
|
-
|
|
7724
|
-
|
|
7725
|
-
|
|
7726
|
-
|
|
7727
|
-
|
|
7728
|
-
|
|
7729
|
-
|
|
7730
|
-
|
|
7731
|
-
|
|
7732
|
-
|
|
7733
|
-
|
|
7734
|
-
|
|
7735
|
-
|
|
7736
|
-
|
|
7737
|
-
|
|
7920
|
+
const label = display === "delta" && targetMs != null ? formatTempoDelta(targetMs - phaseElapsedMs) : targetMs != null ? `${formatDuration2(phaseElapsedMs)} / ${formatDuration2(targetMs)}` : formatDuration2(phaseElapsedMs);
|
|
7921
|
+
return /* @__PURE__ */ jsxs(
|
|
7922
|
+
reactNative.Pressable,
|
|
7923
|
+
{
|
|
7924
|
+
onPress: onToggleDisplay,
|
|
7925
|
+
style: { height: "100%", position: "relative" },
|
|
7926
|
+
accessibilityRole: "button",
|
|
7927
|
+
accessibilityLabel: `Tempo readout: ${display === "delta" ? "delta" : "time"} \u2014 tap to switch`,
|
|
7928
|
+
testID: `tempo-segment-active-${config.label}`,
|
|
7929
|
+
children: [
|
|
7930
|
+
/* @__PURE__ */ jsx(
|
|
7931
|
+
reactNative.View,
|
|
7932
|
+
{
|
|
7933
|
+
style: {
|
|
7934
|
+
position: "absolute",
|
|
7935
|
+
left: 0,
|
|
7936
|
+
top: 0,
|
|
7937
|
+
bottom: 0,
|
|
7938
|
+
width: `${fillPct}%`,
|
|
7939
|
+
backgroundColor: alpha(barColor, 0.3),
|
|
7940
|
+
borderRadius: radius
|
|
7941
|
+
}
|
|
7942
|
+
}
|
|
7943
|
+
),
|
|
7944
|
+
/* @__PURE__ */ jsx(
|
|
7945
|
+
reactNative.View,
|
|
7946
|
+
{
|
|
7947
|
+
style: {
|
|
7948
|
+
height: "100%",
|
|
7949
|
+
flexDirection: "row",
|
|
7950
|
+
alignItems: "center",
|
|
7951
|
+
justifyContent: "center"
|
|
7952
|
+
},
|
|
7953
|
+
children: /* @__PURE__ */ jsx(reactNative.Text, { style: { fontSize: font, fontWeight: "700", color: barColor }, children: label })
|
|
7954
|
+
}
|
|
7955
|
+
)
|
|
7956
|
+
]
|
|
7957
|
+
}
|
|
7958
|
+
);
|
|
7738
7959
|
}
|
|
7739
7960
|
function CompletedSegment({
|
|
7740
7961
|
config,
|
|
7741
7962
|
completedMs,
|
|
7742
|
-
targetMs
|
|
7963
|
+
targetMs,
|
|
7964
|
+
font
|
|
7743
7965
|
}) {
|
|
7744
7966
|
const pacing = getTempoPacingState(completedMs, targetMs);
|
|
7745
7967
|
const hitTarget = pacing !== "behind";
|
|
7746
7968
|
const indicator = targetMs && targetMs >= TEMPO_PACING.minPhaseDurationMs ? hitTarget ? " \u2713" : " \u2717" : "";
|
|
7969
|
+
const cueColor = hitTarget ? config.color : t2["status-error"];
|
|
7747
7970
|
return /* @__PURE__ */ jsx(
|
|
7748
7971
|
reactNative.View,
|
|
7749
7972
|
{
|
|
@@ -7752,20 +7975,46 @@ function CompletedSegment({
|
|
|
7752
7975
|
flexDirection: "row",
|
|
7753
7976
|
alignItems: "center",
|
|
7754
7977
|
justifyContent: "center",
|
|
7755
|
-
backgroundColor: alpha(
|
|
7978
|
+
backgroundColor: alpha(cueColor, 0.15)
|
|
7756
7979
|
},
|
|
7757
7980
|
testID: `tempo-segment-completed-${config.label}`,
|
|
7758
|
-
children: /* @__PURE__ */ jsxs(
|
|
7759
|
-
|
|
7760
|
-
|
|
7761
|
-
|
|
7981
|
+
children: /* @__PURE__ */ jsxs(
|
|
7982
|
+
reactNative.Text,
|
|
7983
|
+
{
|
|
7984
|
+
style: {
|
|
7985
|
+
fontSize: font,
|
|
7986
|
+
fontWeight: "500",
|
|
7987
|
+
color: hitTarget ? alpha(config.color, 0.7) : cueColor
|
|
7988
|
+
},
|
|
7989
|
+
children: [
|
|
7990
|
+
formatDuration2(completedMs),
|
|
7991
|
+
indicator
|
|
7992
|
+
]
|
|
7993
|
+
}
|
|
7994
|
+
)
|
|
7762
7995
|
}
|
|
7763
7996
|
);
|
|
7764
7997
|
}
|
|
7765
|
-
function
|
|
7998
|
+
function formatDuration2(ms) {
|
|
7766
7999
|
const s = ms / 1e3;
|
|
7767
8000
|
return s < 10 ? s.toFixed(1) : Math.round(s).toString();
|
|
7768
8001
|
}
|
|
8002
|
+
var METRIC_FONT = "Inter, sans-serif";
|
|
8003
|
+
function MetricCell({ children, color, fontSize = 11 }) {
|
|
8004
|
+
return /* @__PURE__ */ jsx(
|
|
8005
|
+
reactNative.Text,
|
|
8006
|
+
{
|
|
8007
|
+
style: {
|
|
8008
|
+
fontFamily: METRIC_FONT,
|
|
8009
|
+
fontSize,
|
|
8010
|
+
color,
|
|
8011
|
+
fontWeight: "600",
|
|
8012
|
+
letterSpacing: 1
|
|
8013
|
+
},
|
|
8014
|
+
children
|
|
8015
|
+
}
|
|
8016
|
+
);
|
|
8017
|
+
}
|
|
7769
8018
|
|
|
7770
8019
|
// src/components/custom/Workout/TempoDisplay.tsx
|
|
7771
8020
|
var t3 = getSemanticColors("dark");
|
|
@@ -7784,34 +8033,10 @@ function TempoValue({
|
|
|
7784
8033
|
color,
|
|
7785
8034
|
fontSize
|
|
7786
8035
|
}) {
|
|
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
|
-
);
|
|
8036
|
+
return /* @__PURE__ */ jsx(MetricCell, { color, fontSize, children: value });
|
|
7800
8037
|
}
|
|
7801
8038
|
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
|
-
);
|
|
8039
|
+
return /* @__PURE__ */ jsx(MetricCell, { color, fontSize, children: "-" });
|
|
7815
8040
|
}
|
|
7816
8041
|
var LIVE_PHASES = [
|
|
7817
8042
|
{ key: "eccentric", color: phaseColors.eccentric },
|
|
@@ -7873,6 +8098,7 @@ function TempoDisplay({
|
|
|
7873
8098
|
tempo,
|
|
7874
8099
|
size = "md",
|
|
7875
8100
|
colored = true,
|
|
8101
|
+
showLabel = true,
|
|
7876
8102
|
showInfo = true,
|
|
7877
8103
|
live,
|
|
7878
8104
|
onPress,
|
|
@@ -7906,7 +8132,7 @@ function TempoDisplay({
|
|
|
7906
8132
|
},
|
|
7907
8133
|
...props,
|
|
7908
8134
|
children: [
|
|
7909
|
-
/* @__PURE__ */ jsx(
|
|
8135
|
+
showLabel && /* @__PURE__ */ jsx(
|
|
7910
8136
|
reactNative.Text,
|
|
7911
8137
|
{
|
|
7912
8138
|
style: {
|
|
@@ -8134,12 +8360,12 @@ var ZONE = {
|
|
|
8134
8360
|
// severe over-target
|
|
8135
8361
|
};
|
|
8136
8362
|
var BULGE_SIZE = { 1: 8, 2: 10, 3: 11 };
|
|
8137
|
-
function zoneForPct(
|
|
8138
|
-
if (
|
|
8139
|
-
if (
|
|
8140
|
-
if (
|
|
8141
|
-
if (
|
|
8142
|
-
if (
|
|
8363
|
+
function zoneForPct(pct2) {
|
|
8364
|
+
if (pct2 >= 125) return { color: ZONE.over3, over: 3 };
|
|
8365
|
+
if (pct2 >= 115) return { color: ZONE.over2, over: 2 };
|
|
8366
|
+
if (pct2 > 100) return { color: ZONE.over1, over: 1 };
|
|
8367
|
+
if (pct2 === 100) return { color: ZONE.target, over: 0 };
|
|
8368
|
+
if (pct2 >= 75) return { color: ZONE.approaching, over: 0 };
|
|
8143
8369
|
return { color: ZONE.building, over: 0 };
|
|
8144
8370
|
}
|
|
8145
8371
|
function clampFill(value) {
|
|
@@ -8147,9 +8373,9 @@ function clampFill(value) {
|
|
|
8147
8373
|
if (value > 1) return 1;
|
|
8148
8374
|
return value;
|
|
8149
8375
|
}
|
|
8150
|
-
function isAtTarget(
|
|
8376
|
+
function isAtTarget(pct2, threshold) {
|
|
8151
8377
|
if (threshold == null) return false;
|
|
8152
|
-
return Math.abs(
|
|
8378
|
+
return Math.abs(pct2 - Math.round(threshold * 100)) <= 5;
|
|
8153
8379
|
}
|
|
8154
8380
|
function IntensityBar({
|
|
8155
8381
|
level,
|
|
@@ -8163,9 +8389,9 @@ function IntensityBar({
|
|
|
8163
8389
|
}) {
|
|
8164
8390
|
const isVertical = orientation === "vertical";
|
|
8165
8391
|
const fillLevel = clampFill(level);
|
|
8166
|
-
const
|
|
8167
|
-
const zone = zoneForPct(
|
|
8168
|
-
const atTarget = isAtTarget(
|
|
8392
|
+
const pct2 = Math.round(Math.max(0, level) * 100);
|
|
8393
|
+
const zone = zoneForPct(pct2);
|
|
8394
|
+
const atTarget = isAtTarget(pct2, threshold);
|
|
8169
8395
|
const [fillAnim] = React24.useState(() => new reactNative.Animated.Value(fillLevel));
|
|
8170
8396
|
React24.useEffect(() => {
|
|
8171
8397
|
const animation = reactNative.Animated.timing(fillAnim, {
|
|
@@ -8216,8 +8442,8 @@ function IntensityBar({
|
|
|
8216
8442
|
className,
|
|
8217
8443
|
style: { alignItems: "center" },
|
|
8218
8444
|
accessibilityRole: "progressbar",
|
|
8219
|
-
accessibilityValue: { min: 0, max: 100, now:
|
|
8220
|
-
accessibilityLabel: `Intensity level: ${
|
|
8445
|
+
accessibilityValue: { min: 0, max: 100, now: pct2 },
|
|
8446
|
+
accessibilityLabel: `Intensity level: ${pct2}%`,
|
|
8221
8447
|
testID: "intensity-bar",
|
|
8222
8448
|
...props,
|
|
8223
8449
|
children: [
|
|
@@ -8263,7 +8489,7 @@ function IntensityBar({
|
|
|
8263
8489
|
accessibilityElementsHidden: true,
|
|
8264
8490
|
testID: "intensity-label",
|
|
8265
8491
|
children: [
|
|
8266
|
-
|
|
8492
|
+
pct2,
|
|
8267
8493
|
"%"
|
|
8268
8494
|
]
|
|
8269
8495
|
}
|
|
@@ -8448,13 +8674,257 @@ function WorkoutPill({
|
|
|
8448
8674
|
}
|
|
8449
8675
|
return pill;
|
|
8450
8676
|
}
|
|
8677
|
+
var PULSE_HALF_MS = 950;
|
|
8678
|
+
var PULSE_MIN_OPACITY = 0.45;
|
|
8679
|
+
var SEGMENTED_BAR_GAP = 5;
|
|
8680
|
+
function usePulse2(active) {
|
|
8681
|
+
const [value] = React24.useState(() => new reactNative.Animated.Value(0));
|
|
8682
|
+
React24.useEffect(() => {
|
|
8683
|
+
if (!active) return;
|
|
8684
|
+
const loop = reactNative.Animated.loop(
|
|
8685
|
+
reactNative.Animated.sequence([
|
|
8686
|
+
reactNative.Animated.timing(value, {
|
|
8687
|
+
toValue: 1,
|
|
8688
|
+
duration: PULSE_HALF_MS,
|
|
8689
|
+
easing: reactNative.Easing.inOut(reactNative.Easing.ease),
|
|
8690
|
+
useNativeDriver: false
|
|
8691
|
+
}),
|
|
8692
|
+
reactNative.Animated.timing(value, {
|
|
8693
|
+
toValue: 0,
|
|
8694
|
+
duration: PULSE_HALF_MS,
|
|
8695
|
+
easing: reactNative.Easing.inOut(reactNative.Easing.ease),
|
|
8696
|
+
useNativeDriver: false
|
|
8697
|
+
})
|
|
8698
|
+
])
|
|
8699
|
+
);
|
|
8700
|
+
loop.start();
|
|
8701
|
+
return () => loop.stop();
|
|
8702
|
+
}, [active, value]);
|
|
8703
|
+
return value;
|
|
8704
|
+
}
|
|
8705
|
+
function SegmentedBar({
|
|
8706
|
+
segments,
|
|
8707
|
+
height = 8,
|
|
8708
|
+
gap = SEGMENTED_BAR_GAP,
|
|
8709
|
+
radius = 2,
|
|
8710
|
+
marker = null,
|
|
8711
|
+
segmentTestID,
|
|
8712
|
+
style,
|
|
8713
|
+
...props
|
|
8714
|
+
}) {
|
|
8715
|
+
const pulse = usePulse2(segments.some((s) => s.pulse));
|
|
8716
|
+
return /* @__PURE__ */ jsxs(reactNative.View, { style: [{ flexDirection: "row", height, gap, position: "relative" }, style], ...props, children: [
|
|
8717
|
+
segments.map((seg, i) => {
|
|
8718
|
+
const fillStyle = {
|
|
8719
|
+
width: `${(seg.fill ?? 1) * 100}%`,
|
|
8720
|
+
height: "100%",
|
|
8721
|
+
backgroundColor: seg.color,
|
|
8722
|
+
opacity: seg.opacity
|
|
8723
|
+
};
|
|
8724
|
+
const testID = segmentTestID?.(seg, i) ?? "segmented-bar-segment";
|
|
8725
|
+
return /* @__PURE__ */ jsx(
|
|
8726
|
+
reactNative.View,
|
|
8727
|
+
{
|
|
8728
|
+
style: {
|
|
8729
|
+
flex: seg.weight ?? 1,
|
|
8730
|
+
minWidth: 0,
|
|
8731
|
+
height: "100%",
|
|
8732
|
+
marginLeft: seg.leadingGap,
|
|
8733
|
+
borderRadius: radius,
|
|
8734
|
+
overflow: "hidden"
|
|
8735
|
+
},
|
|
8736
|
+
children: seg.pulse ? /* @__PURE__ */ jsx(
|
|
8737
|
+
reactNative.Animated.View,
|
|
8738
|
+
{
|
|
8739
|
+
style: {
|
|
8740
|
+
...fillStyle,
|
|
8741
|
+
...seg.pulseColor ? {
|
|
8742
|
+
backgroundColor: pulse.interpolate({
|
|
8743
|
+
inputRange: [0, 1],
|
|
8744
|
+
outputRange: seg.pulseColor
|
|
8745
|
+
})
|
|
8746
|
+
} : {
|
|
8747
|
+
opacity: pulse.interpolate({
|
|
8748
|
+
inputRange: [0, 1],
|
|
8749
|
+
outputRange: [PULSE_MIN_OPACITY, 1]
|
|
8750
|
+
})
|
|
8751
|
+
}
|
|
8752
|
+
},
|
|
8753
|
+
accessibilityElementsHidden: true,
|
|
8754
|
+
testID
|
|
8755
|
+
}
|
|
8756
|
+
) : /* @__PURE__ */ jsx(reactNative.View, { style: fillStyle, accessibilityElementsHidden: true, testID })
|
|
8757
|
+
},
|
|
8758
|
+
i
|
|
8759
|
+
);
|
|
8760
|
+
}),
|
|
8761
|
+
marker ? /* @__PURE__ */ jsx(
|
|
8762
|
+
reactNative.View,
|
|
8763
|
+
{
|
|
8764
|
+
style: {
|
|
8765
|
+
position: "absolute",
|
|
8766
|
+
left: `${marker.position * 100}%`,
|
|
8767
|
+
top: 0,
|
|
8768
|
+
bottom: 0,
|
|
8769
|
+
width: 2,
|
|
8770
|
+
backgroundColor: marker.color
|
|
8771
|
+
},
|
|
8772
|
+
accessibilityElementsHidden: true,
|
|
8773
|
+
testID: "segmented-bar-marker"
|
|
8774
|
+
}
|
|
8775
|
+
) : null
|
|
8776
|
+
] });
|
|
8777
|
+
}
|
|
8778
|
+
|
|
8779
|
+
// src/components/custom/Workout/SetBar.tsx
|
|
8780
|
+
var SET_STRIP_ZONES = {
|
|
8781
|
+
slow: primitiveRamps.red[600],
|
|
8782
|
+
moderate: primitiveRamps.orange[400],
|
|
8783
|
+
fast: primitiveRamps.amber[300],
|
|
8784
|
+
fastest: primitiveRamps.green[300]
|
|
8785
|
+
};
|
|
8786
|
+
var TODO_COLOR = primitiveColors.charcoal[300];
|
|
8787
|
+
var SET_STRIP_VARIABLE_COLOR = primitiveRamps.cyan[900];
|
|
8788
|
+
var NOTCH_GAP = 2;
|
|
8789
|
+
var CLUSTER_GAP = 3;
|
|
8790
|
+
var MYO_UPCOMING_CLUSTERS = 3;
|
|
8791
|
+
var MYO_UPCOMING_CLUSTER_LEN = 5;
|
|
8792
|
+
var MYO_UPCOMING_FADE = 0.6;
|
|
8793
|
+
var PULSE_RANGE = {
|
|
8794
|
+
[SET_STRIP_ZONES.slow]: [primitiveRamps.red[500], primitiveRamps.red[700]],
|
|
8795
|
+
[SET_STRIP_ZONES.moderate]: [primitiveRamps.orange[300], primitiveRamps.orange[500]],
|
|
8796
|
+
[SET_STRIP_ZONES.fast]: [primitiveRamps.amber[200], primitiveRamps.amber[400]],
|
|
8797
|
+
[SET_STRIP_ZONES.fastest]: [primitiveRamps.green[200], primitiveRamps.green[400]]
|
|
8798
|
+
};
|
|
8799
|
+
function velocityZoneColor(v) {
|
|
8800
|
+
if (v < 0.5) return SET_STRIP_ZONES.slow;
|
|
8801
|
+
if (v < 0.75) return SET_STRIP_ZONES.moderate;
|
|
8802
|
+
if (v < 1) return SET_STRIP_ZONES.fast;
|
|
8803
|
+
return SET_STRIP_ZONES.fastest;
|
|
8804
|
+
}
|
|
8805
|
+
function chunkedSegments(chunks, gap) {
|
|
8806
|
+
return chunks.flatMap(
|
|
8807
|
+
(vels, ci) => vels.map((v, ri) => ({
|
|
8808
|
+
color: velocityZoneColor(v),
|
|
8809
|
+
...ci > 0 && ri === 0 ? { leadingGap: gap } : {}
|
|
8810
|
+
}))
|
|
8811
|
+
);
|
|
8812
|
+
}
|
|
8813
|
+
function rangeSegments(floor, max, doneVels) {
|
|
8814
|
+
return Array.from({ length: max }, (_, i) => {
|
|
8815
|
+
if (i < doneVels.length) return { color: velocityZoneColor(doneVels[i]) };
|
|
8816
|
+
return { color: i < floor ? TODO_COLOR : SET_STRIP_VARIABLE_COLOR };
|
|
8817
|
+
});
|
|
8818
|
+
}
|
|
8819
|
+
function myoUpcomingSegments(activationLen, clusterCount) {
|
|
8820
|
+
const activation = Array.from({ length: activationLen }, () => ({ color: TODO_COLOR }));
|
|
8821
|
+
const trail = Array.from(
|
|
8822
|
+
{ length: clusterCount },
|
|
8823
|
+
(_, ci) => Array.from({ length: MYO_UPCOMING_CLUSTER_LEN }, (_2, ri) => ({
|
|
8824
|
+
color: SET_STRIP_VARIABLE_COLOR,
|
|
8825
|
+
opacity: MYO_UPCOMING_FADE ** ci,
|
|
8826
|
+
...ri === 0 ? { leadingGap: CLUSTER_GAP } : {}
|
|
8827
|
+
}))
|
|
8828
|
+
).flat();
|
|
8829
|
+
return [...activation, ...trail];
|
|
8830
|
+
}
|
|
8831
|
+
function setSegments(set) {
|
|
8832
|
+
if (set.status === "todo") {
|
|
8833
|
+
return [{ color: TODO_COLOR }];
|
|
8834
|
+
}
|
|
8835
|
+
if (set.status === "done") {
|
|
8836
|
+
return set.velocities.map((v) => ({ color: velocityZoneColor(v) }));
|
|
8837
|
+
}
|
|
8838
|
+
if (set.status === "range") {
|
|
8839
|
+
return rangeSegments(set.floor, set.max, set.doneVels);
|
|
8840
|
+
}
|
|
8841
|
+
if (set.status === "drop") {
|
|
8842
|
+
return chunkedSegments(set.subloads, NOTCH_GAP);
|
|
8843
|
+
}
|
|
8844
|
+
if (set.status === "myo") {
|
|
8845
|
+
return chunkedSegments([set.activation, ...set.clusters], CLUSTER_GAP);
|
|
8846
|
+
}
|
|
8847
|
+
if (set.status === "myo-upcoming") {
|
|
8848
|
+
return myoUpcomingSegments(set.activationLen, set.clusterCount ?? MYO_UPCOMING_CLUSTERS);
|
|
8849
|
+
}
|
|
8850
|
+
const performed = set.velocities.map((v) => {
|
|
8851
|
+
const color = velocityZoneColor(v);
|
|
8852
|
+
return { color, pulse: true, pulseColor: PULSE_RANGE[color] };
|
|
8853
|
+
});
|
|
8854
|
+
const remaining = Math.max(0, set.planned - set.velocities.length);
|
|
8855
|
+
return [...performed, ...Array.from({ length: remaining }, () => ({ color: TODO_COLOR }))];
|
|
8856
|
+
}
|
|
8857
|
+
function setBarSegmentTestID(seg) {
|
|
8858
|
+
if (seg.pulse) return "set-strip-pulse";
|
|
8859
|
+
if (seg.color === SET_STRIP_VARIABLE_COLOR) return "set-strip-variable";
|
|
8860
|
+
return seg.color === TODO_COLOR ? "set-strip-empty" : "set-strip-fill";
|
|
8861
|
+
}
|
|
8862
|
+
function SetBar({ set, height = 8 }) {
|
|
8863
|
+
return /* @__PURE__ */ jsx(
|
|
8864
|
+
SegmentedBar,
|
|
8865
|
+
{
|
|
8866
|
+
segments: setSegments(set),
|
|
8867
|
+
height,
|
|
8868
|
+
gap: 0,
|
|
8869
|
+
radius: 0,
|
|
8870
|
+
segmentTestID: setBarSegmentTestID,
|
|
8871
|
+
style: { flex: 1, minWidth: 0, borderRadius: 2, overflow: "hidden" },
|
|
8872
|
+
testID: "set-strip-set"
|
|
8873
|
+
}
|
|
8874
|
+
);
|
|
8875
|
+
}
|
|
8876
|
+
|
|
8877
|
+
// src/components/custom/Workout/SetStrip.tsx
|
|
8878
|
+
var SET_STRIP_GAP = 5;
|
|
8879
|
+
function setCategory(set) {
|
|
8880
|
+
switch (set.status) {
|
|
8881
|
+
case "done":
|
|
8882
|
+
case "drop":
|
|
8883
|
+
case "myo":
|
|
8884
|
+
return "done";
|
|
8885
|
+
case "active":
|
|
8886
|
+
return "active";
|
|
8887
|
+
case "range":
|
|
8888
|
+
return set.doneVels.length > 0 ? "active" : "upcoming";
|
|
8889
|
+
case "todo":
|
|
8890
|
+
case "myo-upcoming":
|
|
8891
|
+
return "upcoming";
|
|
8892
|
+
}
|
|
8893
|
+
}
|
|
8894
|
+
function describeSets(sets) {
|
|
8895
|
+
const done = sets.filter((s) => setCategory(s) === "done").length;
|
|
8896
|
+
const active = sets.filter((s) => setCategory(s) === "active").length;
|
|
8897
|
+
const upcoming = sets.filter((s) => setCategory(s) === "upcoming").length;
|
|
8898
|
+
return `Set progress: ${done} done, ${active} in progress, ${upcoming} upcoming`;
|
|
8899
|
+
}
|
|
8900
|
+
function SetStrip({ sets, height = 8, className, ...props }) {
|
|
8901
|
+
return /* @__PURE__ */ jsx(
|
|
8902
|
+
reactNative.View,
|
|
8903
|
+
{
|
|
8904
|
+
className,
|
|
8905
|
+
style: { flexDirection: "row", width: "100%", height, gap: SET_STRIP_GAP },
|
|
8906
|
+
accessibilityRole: "image",
|
|
8907
|
+
accessibilityLabel: describeSets(sets),
|
|
8908
|
+
testID: "set-strip",
|
|
8909
|
+
...props,
|
|
8910
|
+
children: sets.map((set, i) => /* @__PURE__ */ jsx(SetBar, { set, height }, i))
|
|
8911
|
+
}
|
|
8912
|
+
);
|
|
8913
|
+
}
|
|
8914
|
+
|
|
8915
|
+
// src/components/custom/Workout/VelocityStrip.tsx
|
|
8451
8916
|
var VEL_COLORS = WORKOUT_TOKENS.scale;
|
|
8452
|
-
var
|
|
8453
|
-
speed:
|
|
8454
|
-
|
|
8455
|
-
|
|
8456
|
-
|
|
8457
|
-
|
|
8917
|
+
var bandIdToEffortColor = {
|
|
8918
|
+
speed: sequentialEffort[0],
|
|
8919
|
+
// green-300
|
|
8920
|
+
power: sequentialEffort[2],
|
|
8921
|
+
// amber-300 (gold)
|
|
8922
|
+
strengthSpeed: sequentialEffort[3],
|
|
8923
|
+
// orange-400
|
|
8924
|
+
maximalStrength: sequentialEffort[4],
|
|
8925
|
+
// red-600
|
|
8926
|
+
grinding: sequentialEffort[5]
|
|
8927
|
+
// red-700 — the distinct 5th band
|
|
8458
8928
|
};
|
|
8459
8929
|
function getVelocityZoneColor(velocity) {
|
|
8460
8930
|
if (velocity >= 1) return "vel-green";
|
|
@@ -8493,14 +8963,140 @@ function classifyBand(velocity, bands) {
|
|
|
8493
8963
|
return bands[bands.length - 1];
|
|
8494
8964
|
}
|
|
8495
8965
|
function bandColor(band) {
|
|
8496
|
-
|
|
8497
|
-
return token ? VEL_COLORS[token] : VEL_COLORS.green;
|
|
8966
|
+
return bandIdToEffortColor[band.id] ?? VEL_COLORS.green;
|
|
8498
8967
|
}
|
|
8499
8968
|
function getLossStyle(loss) {
|
|
8500
8969
|
if (loss > 25) return { color: VEL_COLORS.red };
|
|
8501
8970
|
if (loss > 20) return { color: VEL_COLORS.orange };
|
|
8502
8971
|
return null;
|
|
8503
8972
|
}
|
|
8973
|
+
var REP_GAP = 2;
|
|
8974
|
+
var WIDE_GAP = 8;
|
|
8975
|
+
var TODO_COLOR2 = primitiveColors.charcoal[300];
|
|
8976
|
+
var CONTINUE_OUTLINE = primitiveRamps.cyan[800];
|
|
8977
|
+
function deriveDoneVelocities(set) {
|
|
8978
|
+
switch (set.type) {
|
|
8979
|
+
case "drop":
|
|
8980
|
+
return set.subloads.flat();
|
|
8981
|
+
case "myo":
|
|
8982
|
+
return [...set.activation, ...set.clusters.flat()];
|
|
8983
|
+
default:
|
|
8984
|
+
return set.velocities;
|
|
8985
|
+
}
|
|
8986
|
+
}
|
|
8987
|
+
function chunkedRepSlots(chunks) {
|
|
8988
|
+
const slots = [];
|
|
8989
|
+
chunks.forEach((chunk) => {
|
|
8990
|
+
chunk.forEach((velocity, ri) => {
|
|
8991
|
+
const first = slots.length === 0;
|
|
8992
|
+
slots.push({ kind: "rep", velocity, leadingGap: first ? 0 : ri === 0 ? WIDE_GAP : REP_GAP });
|
|
8993
|
+
});
|
|
8994
|
+
});
|
|
8995
|
+
return slots;
|
|
8996
|
+
}
|
|
8997
|
+
function buildSlots(set) {
|
|
8998
|
+
switch (set.type) {
|
|
8999
|
+
case "straight": {
|
|
9000
|
+
const total = Math.max(set.velocities.length, set.planned ?? set.velocities.length);
|
|
9001
|
+
return Array.from({ length: total }, (_, i) => {
|
|
9002
|
+
const done = i < set.velocities.length;
|
|
9003
|
+
return {
|
|
9004
|
+
kind: done ? "rep" : "todo",
|
|
9005
|
+
velocity: done ? set.velocities[i] : void 0,
|
|
9006
|
+
leadingGap: i === 0 ? 0 : REP_GAP
|
|
9007
|
+
};
|
|
9008
|
+
});
|
|
9009
|
+
}
|
|
9010
|
+
case "range": {
|
|
9011
|
+
const total = Math.max(set.max, set.velocities.length);
|
|
9012
|
+
return Array.from({ length: total }, (_, i) => {
|
|
9013
|
+
const kind = i < set.velocities.length ? "rep" : i < set.floor ? "todo" : "variable";
|
|
9014
|
+
return {
|
|
9015
|
+
kind,
|
|
9016
|
+
velocity: kind === "rep" ? set.velocities[i] : void 0,
|
|
9017
|
+
leadingGap: i === 0 ? 0 : REP_GAP
|
|
9018
|
+
};
|
|
9019
|
+
});
|
|
9020
|
+
}
|
|
9021
|
+
case "amrap": {
|
|
9022
|
+
const reps = set.velocities.map((velocity, i) => ({
|
|
9023
|
+
kind: "rep",
|
|
9024
|
+
velocity,
|
|
9025
|
+
leadingGap: i === 0 ? 0 : REP_GAP
|
|
9026
|
+
}));
|
|
9027
|
+
reps.push({ kind: "continue", leadingGap: reps.length === 0 ? 0 : REP_GAP });
|
|
9028
|
+
return reps;
|
|
9029
|
+
}
|
|
9030
|
+
case "drop":
|
|
9031
|
+
return chunkedRepSlots(set.subloads);
|
|
9032
|
+
case "myo": {
|
|
9033
|
+
const slots = chunkedRepSlots([set.activation, ...set.clusters]);
|
|
9034
|
+
if (set.open) slots.push({ kind: "continue", leadingGap: slots.length === 0 ? 0 : REP_GAP });
|
|
9035
|
+
return slots;
|
|
9036
|
+
}
|
|
9037
|
+
case "cluster": {
|
|
9038
|
+
const total = Math.max(set.velocities.length, set.planned ?? set.velocities.length);
|
|
9039
|
+
return Array.from({ length: total }, (_, i) => {
|
|
9040
|
+
const done = i < set.velocities.length;
|
|
9041
|
+
const boundary = i > 0 && i % set.groupSize === 0;
|
|
9042
|
+
return {
|
|
9043
|
+
kind: done ? "rep" : "todo",
|
|
9044
|
+
velocity: done ? set.velocities[i] : void 0,
|
|
9045
|
+
leadingGap: i === 0 ? 0 : boundary ? WIDE_GAP : REP_GAP
|
|
9046
|
+
};
|
|
9047
|
+
});
|
|
9048
|
+
}
|
|
9049
|
+
}
|
|
9050
|
+
}
|
|
9051
|
+
function setAccessibilityLabel(set, repCount) {
|
|
9052
|
+
switch (set.type) {
|
|
9053
|
+
case "straight":
|
|
9054
|
+
return `Velocity strip, straight set, ${repCount} reps`;
|
|
9055
|
+
case "range":
|
|
9056
|
+
return `Velocity strip, rep-range set, floor ${set.floor} to ${set.max}, ${repCount} reps done`;
|
|
9057
|
+
case "amrap":
|
|
9058
|
+
return `Velocity strip, AMRAP set, ${repCount} reps and counting`;
|
|
9059
|
+
case "drop":
|
|
9060
|
+
return `Velocity strip, drop set, ${set.subloads.length} loads, ${repCount} reps`;
|
|
9061
|
+
case "myo":
|
|
9062
|
+
return `Velocity strip, myo-reps set, ${set.clusters.length} clusters${set.open ? ", open" : ""}, ${repCount} reps`;
|
|
9063
|
+
case "cluster":
|
|
9064
|
+
return `Velocity strip, cluster set, groups of ${set.groupSize}, ${repCount} reps`;
|
|
9065
|
+
}
|
|
9066
|
+
}
|
|
9067
|
+
function slotAccessibilityLabel(slot, index) {
|
|
9068
|
+
switch (slot.kind) {
|
|
9069
|
+
case "rep":
|
|
9070
|
+
return `Rep ${index + 1}: ${formatVelocity(slot.velocity ?? 0)} meters per second`;
|
|
9071
|
+
case "todo":
|
|
9072
|
+
return `Rep ${index + 1}: planned`;
|
|
9073
|
+
case "variable":
|
|
9074
|
+
return `Rep ${index + 1}: variable`;
|
|
9075
|
+
case "continue":
|
|
9076
|
+
return "Keep going";
|
|
9077
|
+
}
|
|
9078
|
+
}
|
|
9079
|
+
var EXPANDED_TODO_STUB_PCT = 16;
|
|
9080
|
+
var EXPANDED_ENCODED_PCT = 45;
|
|
9081
|
+
var EXPANDED_HEIGHT = 60;
|
|
9082
|
+
var FIXED_MAX_VELOCITY = 1.15;
|
|
9083
|
+
var BARE_STUB_HEIGHT = 3;
|
|
9084
|
+
var HERO_HEIGHT = 220;
|
|
9085
|
+
var HERO_LABEL_HEADROOM = 20;
|
|
9086
|
+
var HERO_BAR_GAP = 8;
|
|
9087
|
+
var HERO_BAR_MAX_WIDTH = 52;
|
|
9088
|
+
var HERO_BAR_RADIUS = 5;
|
|
9089
|
+
var HERO_MIN_BAR_HEIGHT = 4;
|
|
9090
|
+
var HERO_PENDING_COLOR = primitiveColors.charcoal[100];
|
|
9091
|
+
var HERO_REFERENCE_COLOR = primitiveColors.charcoal[0];
|
|
9092
|
+
function scaleDenominator(scale, maxVelocity) {
|
|
9093
|
+
return scale === "fixed" ? FIXED_MAX_VELOCITY : maxVelocity * 1.15;
|
|
9094
|
+
}
|
|
9095
|
+
function bareSlotHeight(slot, height, denom) {
|
|
9096
|
+
if (slot.kind !== "rep" || denom <= 0) return BARE_STUB_HEIGHT;
|
|
9097
|
+
const ratio = Math.min(1, (slot.velocity ?? 0) / denom);
|
|
9098
|
+
return Math.max(BARE_STUB_HEIGHT, ratio * height);
|
|
9099
|
+
}
|
|
8504
9100
|
function getReducedMotionPreference() {
|
|
8505
9101
|
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
|
|
8506
9102
|
return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
@@ -8520,33 +9116,11 @@ function usePrefersReducedMotion() {
|
|
|
8520
9116
|
var ANIMATION_DURATION = 400;
|
|
8521
9117
|
var ANIMATION_EASING = reactNative.Easing.bezier(0.22, 1, 0.36, 1);
|
|
8522
9118
|
var POP_EASING = reactNative.Easing.bezier(0.34, 1.56, 0.64, 1);
|
|
8523
|
-
function
|
|
8524
|
-
velocities,
|
|
8525
|
-
zones,
|
|
8526
|
-
liveRepIndex,
|
|
8527
|
-
expanded = false,
|
|
8528
|
-
onToggle,
|
|
8529
|
-
onRepPress,
|
|
8530
|
-
variant = "full",
|
|
8531
|
-
showInfo = true,
|
|
8532
|
-
className,
|
|
8533
|
-
...props
|
|
8534
|
-
}) {
|
|
8535
|
-
const maxVelocity = Math.max(...velocities, 0);
|
|
8536
|
-
const meanVelocity = calculateMeanVelocity(velocities);
|
|
8537
|
-
const loss = calculateVelocityLoss(velocities);
|
|
8538
|
-
const hasZones = zones != null && zones.length > 0;
|
|
8539
|
-
const barColorFor = (v) => hasZones ? bandColor(classifyBand(v, zones)) : zoneHexMap[getVelocityZoneColor(v)];
|
|
8540
|
-
const meanZone = hasZones ? classifyBand(meanVelocity, zones)?.label ?? "" : getVelocityZoneName(meanVelocity);
|
|
9119
|
+
function useLiveRepPop(active, liveRepIndex, liveVelocity, isNewPeak) {
|
|
8541
9120
|
const prefersReducedMotion = usePrefersReducedMotion();
|
|
8542
|
-
const [heightAnim] = React24.useState(() => new reactNative.Animated.Value(expanded ? 60 : 3));
|
|
8543
|
-
const [labelOpacity] = React24.useState(() => new reactNative.Animated.Value(expanded ? 1 : 0));
|
|
8544
|
-
const [infoOpacity] = React24.useState(() => new reactNative.Animated.Value(expanded ? 1 : 0));
|
|
8545
9121
|
const [liveScale] = React24.useState(() => new reactNative.Animated.Value(1));
|
|
8546
|
-
const liveVelocity = liveRepIndex != null ? velocities[liveRepIndex] : void 0;
|
|
8547
|
-
const isNewPeak = liveVelocity != null && maxVelocity > 0 && liveVelocity === maxVelocity;
|
|
8548
9122
|
React24.useEffect(() => {
|
|
8549
|
-
if (
|
|
9123
|
+
if (!active || liveRepIndex == null || liveVelocity == null) return;
|
|
8550
9124
|
if (prefersReducedMotion) {
|
|
8551
9125
|
liveScale.setValue(1);
|
|
8552
9126
|
return;
|
|
@@ -8576,12 +9150,201 @@ function VelocityStrip({
|
|
|
8576
9150
|
useNativeDriver: true
|
|
8577
9151
|
}).start();
|
|
8578
9152
|
}
|
|
8579
|
-
}, [
|
|
9153
|
+
}, [active, liveRepIndex, liveVelocity, isNewPeak, prefersReducedMotion, liveScale]);
|
|
9154
|
+
return liveScale;
|
|
9155
|
+
}
|
|
9156
|
+
function HeroVelocityChart({
|
|
9157
|
+
doneVelocities,
|
|
9158
|
+
barColorFor,
|
|
9159
|
+
scaleDenom,
|
|
9160
|
+
referenceVelocity,
|
|
9161
|
+
liveRepIndex,
|
|
9162
|
+
isNewPeak,
|
|
9163
|
+
targetReps,
|
|
9164
|
+
height,
|
|
9165
|
+
className,
|
|
9166
|
+
viewProps
|
|
9167
|
+
}) {
|
|
9168
|
+
const liveVelocity = liveRepIndex != null ? doneVelocities[liveRepIndex] : void 0;
|
|
9169
|
+
const liveScale = useLiveRepPop(true, liveRepIndex, liveVelocity, isNewPeak);
|
|
9170
|
+
const plotHeight = Math.max(0, height - HERO_LABEL_HEADROOM);
|
|
9171
|
+
const barHeight = (velocity) => scaleDenom > 0 ? Math.max(HERO_MIN_BAR_HEIGHT, Math.min(1, velocity / scaleDenom) * plotHeight) : HERO_MIN_BAR_HEIGHT;
|
|
9172
|
+
const pendingCount = Math.max(0, (targetReps ?? 0) - doneVelocities.length);
|
|
9173
|
+
const referencePx = referenceVelocity > 0 && scaleDenom > 0 ? Math.min(plotHeight, referenceVelocity / scaleDenom * plotHeight) : 0;
|
|
9174
|
+
const repCount = doneVelocities.length;
|
|
9175
|
+
const total = Math.max(repCount, targetReps ?? repCount);
|
|
9176
|
+
const label = referenceVelocity > 0 ? `Velocity chart, ${repCount} of ${total} reps, best ${formatVelocity(referenceVelocity)} meters per second` : `Velocity chart, ${repCount} of ${total} reps`;
|
|
9177
|
+
const { style: externalStyle, ...restProps } = viewProps;
|
|
9178
|
+
return /* @__PURE__ */ jsx(
|
|
9179
|
+
reactNative.View,
|
|
9180
|
+
{
|
|
9181
|
+
className,
|
|
9182
|
+
style: [{ height }, externalStyle],
|
|
9183
|
+
accessibilityRole: "image",
|
|
9184
|
+
accessibilityLabel: label,
|
|
9185
|
+
testID: "velocity-strip-hero",
|
|
9186
|
+
...restProps,
|
|
9187
|
+
children: /* @__PURE__ */ jsxs(
|
|
9188
|
+
reactNative.View,
|
|
9189
|
+
{
|
|
9190
|
+
style: {
|
|
9191
|
+
flex: 1,
|
|
9192
|
+
flexDirection: "row",
|
|
9193
|
+
alignItems: "flex-end",
|
|
9194
|
+
gap: HERO_BAR_GAP,
|
|
9195
|
+
position: "relative",
|
|
9196
|
+
borderBottomWidth: 2,
|
|
9197
|
+
borderBottomColor: HERO_PENDING_COLOR
|
|
9198
|
+
},
|
|
9199
|
+
children: [
|
|
9200
|
+
referencePx > 0 && /* @__PURE__ */ jsx(
|
|
9201
|
+
reactNative.View,
|
|
9202
|
+
{
|
|
9203
|
+
accessibilityElementsHidden: true,
|
|
9204
|
+
style: {
|
|
9205
|
+
position: "absolute",
|
|
9206
|
+
left: 0,
|
|
9207
|
+
right: 0,
|
|
9208
|
+
bottom: referencePx,
|
|
9209
|
+
borderTopWidth: 1,
|
|
9210
|
+
borderStyle: "dashed",
|
|
9211
|
+
borderColor: HERO_REFERENCE_COLOR,
|
|
9212
|
+
pointerEvents: "none"
|
|
9213
|
+
},
|
|
9214
|
+
testID: "velocity-hero-reference"
|
|
9215
|
+
}
|
|
9216
|
+
),
|
|
9217
|
+
doneVelocities.map((velocity, i) => {
|
|
9218
|
+
const isLive = liveRepIndex === i;
|
|
9219
|
+
return /* @__PURE__ */ jsxs(
|
|
9220
|
+
reactNative.View,
|
|
9221
|
+
{
|
|
9222
|
+
accessibilityElementsHidden: true,
|
|
9223
|
+
style: {
|
|
9224
|
+
flex: 1,
|
|
9225
|
+
maxWidth: HERO_BAR_MAX_WIDTH,
|
|
9226
|
+
height: "100%",
|
|
9227
|
+
alignItems: "center",
|
|
9228
|
+
justifyContent: "flex-end"
|
|
9229
|
+
},
|
|
9230
|
+
children: [
|
|
9231
|
+
/* @__PURE__ */ jsx(
|
|
9232
|
+
reactNative.Text,
|
|
9233
|
+
{
|
|
9234
|
+
className: "text-text-primary",
|
|
9235
|
+
style: { fontSize: 12, fontWeight: "800", marginBottom: 4 },
|
|
9236
|
+
testID: `velocity-label-${i}`,
|
|
9237
|
+
children: formatVelocity(velocity)
|
|
9238
|
+
}
|
|
9239
|
+
),
|
|
9240
|
+
/* @__PURE__ */ jsx(
|
|
9241
|
+
reactNative.Animated.View,
|
|
9242
|
+
{
|
|
9243
|
+
style: [
|
|
9244
|
+
{
|
|
9245
|
+
width: "100%",
|
|
9246
|
+
height: barHeight(velocity),
|
|
9247
|
+
borderTopLeftRadius: HERO_BAR_RADIUS,
|
|
9248
|
+
borderTopRightRadius: HERO_BAR_RADIUS,
|
|
9249
|
+
backgroundColor: barColorFor(velocity)
|
|
9250
|
+
},
|
|
9251
|
+
isLive ? { transform: [{ scale: liveScale }] } : null
|
|
9252
|
+
],
|
|
9253
|
+
testID: `velocity-bar-${i}`
|
|
9254
|
+
}
|
|
9255
|
+
)
|
|
9256
|
+
]
|
|
9257
|
+
},
|
|
9258
|
+
i
|
|
9259
|
+
);
|
|
9260
|
+
}),
|
|
9261
|
+
Array.from({ length: pendingCount }, (_, i) => /* @__PURE__ */ jsx(
|
|
9262
|
+
reactNative.View,
|
|
9263
|
+
{
|
|
9264
|
+
accessibilityElementsHidden: true,
|
|
9265
|
+
style: {
|
|
9266
|
+
flex: 1,
|
|
9267
|
+
maxWidth: HERO_BAR_MAX_WIDTH,
|
|
9268
|
+
height: "100%",
|
|
9269
|
+
alignItems: "center",
|
|
9270
|
+
justifyContent: "flex-end"
|
|
9271
|
+
},
|
|
9272
|
+
children: /* @__PURE__ */ jsx(
|
|
9273
|
+
reactNative.View,
|
|
9274
|
+
{
|
|
9275
|
+
style: {
|
|
9276
|
+
width: "100%",
|
|
9277
|
+
height: HERO_MIN_BAR_HEIGHT * 3,
|
|
9278
|
+
borderWidth: 1,
|
|
9279
|
+
borderStyle: "dashed",
|
|
9280
|
+
borderColor: HERO_PENDING_COLOR,
|
|
9281
|
+
borderTopLeftRadius: HERO_BAR_RADIUS,
|
|
9282
|
+
borderTopRightRadius: HERO_BAR_RADIUS
|
|
9283
|
+
},
|
|
9284
|
+
testID: "velocity-slot-todo"
|
|
9285
|
+
}
|
|
9286
|
+
)
|
|
9287
|
+
},
|
|
9288
|
+
`pending-${i}`
|
|
9289
|
+
))
|
|
9290
|
+
]
|
|
9291
|
+
}
|
|
9292
|
+
)
|
|
9293
|
+
}
|
|
9294
|
+
);
|
|
9295
|
+
}
|
|
9296
|
+
function VelocityStrip({
|
|
9297
|
+
velocities,
|
|
9298
|
+
set,
|
|
9299
|
+
zones,
|
|
9300
|
+
liveRepIndex,
|
|
9301
|
+
expanded = true,
|
|
9302
|
+
onToggle,
|
|
9303
|
+
onRepPress,
|
|
9304
|
+
variant = "expanded",
|
|
9305
|
+
height = EXPANDED_HEIGHT,
|
|
9306
|
+
targetReps,
|
|
9307
|
+
scale = "peak",
|
|
9308
|
+
showNumbers = true,
|
|
9309
|
+
showInfo = true,
|
|
9310
|
+
className,
|
|
9311
|
+
...props
|
|
9312
|
+
}) {
|
|
9313
|
+
const doneVelocities = set ? deriveDoneVelocities(set) : velocities ?? [];
|
|
9314
|
+
const slots = set ? buildSlots(set) : doneVelocities.map((v, i) => ({
|
|
9315
|
+
kind: "rep",
|
|
9316
|
+
velocity: v,
|
|
9317
|
+
leadingGap: i === 0 ? 0 : REP_GAP
|
|
9318
|
+
}));
|
|
9319
|
+
const maxVelocity = Math.max(...doneVelocities, 0);
|
|
9320
|
+
const meanVelocity = calculateMeanVelocity(doneVelocities);
|
|
9321
|
+
const loss = calculateVelocityLoss(doneVelocities);
|
|
9322
|
+
const framed = showNumbers || showInfo;
|
|
9323
|
+
const scaleDenom = scaleDenominator(scale, maxVelocity);
|
|
9324
|
+
const hasZones = zones != null && zones.length > 0;
|
|
9325
|
+
const barColorFor = (v) => hasZones ? bandColor(classifyBand(v, zones)) : zoneHexMap[getVelocityZoneColor(v)];
|
|
9326
|
+
const slotColor = (slot) => {
|
|
9327
|
+
if (slot.kind === "rep") return barColorFor(slot.velocity ?? 0);
|
|
9328
|
+
if (slot.kind === "todo") return TODO_COLOR2;
|
|
9329
|
+
return SET_STRIP_VARIABLE_COLOR;
|
|
9330
|
+
};
|
|
9331
|
+
const meanZone = hasZones ? classifyBand(meanVelocity, zones)?.label ?? "" : getVelocityZoneName(meanVelocity);
|
|
9332
|
+
const [heightAnim] = React24.useState(() => new reactNative.Animated.Value(expanded ? height : 3));
|
|
9333
|
+
const [labelOpacity] = React24.useState(() => new reactNative.Animated.Value(expanded ? 1 : 0));
|
|
9334
|
+
const [infoOpacity] = React24.useState(() => new reactNative.Animated.Value(expanded ? 1 : 0));
|
|
9335
|
+
const liveVelocity = liveRepIndex != null ? doneVelocities[liveRepIndex] : void 0;
|
|
9336
|
+
const isNewPeak = liveVelocity != null && maxVelocity > 0 && liveVelocity === maxVelocity;
|
|
9337
|
+
const liveScale = useLiveRepPop(
|
|
9338
|
+
variant === "expanded" && framed,
|
|
9339
|
+
liveRepIndex,
|
|
9340
|
+
liveVelocity,
|
|
9341
|
+
isNewPeak
|
|
9342
|
+
);
|
|
8580
9343
|
React24.useEffect(() => {
|
|
8581
|
-
if (variant
|
|
9344
|
+
if (variant !== "expanded" || !framed) return;
|
|
8582
9345
|
reactNative.Animated.parallel([
|
|
8583
9346
|
reactNative.Animated.timing(heightAnim, {
|
|
8584
|
-
toValue: expanded ?
|
|
9347
|
+
toValue: expanded ? height : 3,
|
|
8585
9348
|
duration: ANIMATION_DURATION,
|
|
8586
9349
|
easing: ANIMATION_EASING,
|
|
8587
9350
|
useNativeDriver: false
|
|
@@ -8599,101 +9362,219 @@ function VelocityStrip({
|
|
|
8599
9362
|
useNativeDriver: false
|
|
8600
9363
|
})
|
|
8601
9364
|
]).start();
|
|
8602
|
-
}, [expanded, variant, heightAnim, labelOpacity, infoOpacity]);
|
|
9365
|
+
}, [expanded, variant, framed, height, heightAnim, labelOpacity, infoOpacity]);
|
|
9366
|
+
if (set == null && velocities == null) return null;
|
|
9367
|
+
const repCount = doneVelocities.length;
|
|
9368
|
+
const miniLabel = set ? setAccessibilityLabel(set, repCount) : `Velocity strip, ${repCount} reps`;
|
|
9369
|
+
if (variant === "hero") {
|
|
9370
|
+
const heroHeight = height === EXPANDED_HEIGHT ? HERO_HEIGHT : height;
|
|
9371
|
+
return /* @__PURE__ */ jsx(
|
|
9372
|
+
HeroVelocityChart,
|
|
9373
|
+
{
|
|
9374
|
+
doneVelocities,
|
|
9375
|
+
barColorFor,
|
|
9376
|
+
scaleDenom,
|
|
9377
|
+
referenceVelocity: maxVelocity,
|
|
9378
|
+
liveRepIndex,
|
|
9379
|
+
isNewPeak,
|
|
9380
|
+
targetReps,
|
|
9381
|
+
height: heroHeight,
|
|
9382
|
+
className,
|
|
9383
|
+
viewProps: props
|
|
9384
|
+
}
|
|
9385
|
+
);
|
|
9386
|
+
}
|
|
8603
9387
|
if (variant === "mini") {
|
|
8604
9388
|
const { style: externalStyle, ...restProps } = props;
|
|
8605
9389
|
return /* @__PURE__ */ jsx(
|
|
8606
9390
|
reactNative.View,
|
|
8607
9391
|
{
|
|
8608
9392
|
className,
|
|
8609
|
-
style: [
|
|
9393
|
+
style: [
|
|
9394
|
+
{ flexDirection: "row", height: 3, gap: REP_GAP, borderRadius: 2, overflow: "hidden" },
|
|
9395
|
+
externalStyle
|
|
9396
|
+
],
|
|
8610
9397
|
accessibilityRole: "image",
|
|
8611
|
-
accessibilityLabel:
|
|
9398
|
+
accessibilityLabel: miniLabel,
|
|
8612
9399
|
testID: "velocity-strip-mini",
|
|
8613
9400
|
...restProps,
|
|
8614
|
-
children:
|
|
9401
|
+
children: slots.map((slot, i) => /* @__PURE__ */ jsx(
|
|
8615
9402
|
reactNative.View,
|
|
8616
9403
|
{
|
|
8617
9404
|
style: {
|
|
8618
9405
|
flex: 1,
|
|
8619
|
-
backgroundColor:
|
|
9406
|
+
backgroundColor: slotColor(slot),
|
|
8620
9407
|
borderRadius: 1,
|
|
8621
9408
|
minWidth: 4,
|
|
8622
|
-
height: "100%"
|
|
9409
|
+
height: "100%",
|
|
9410
|
+
// The container's uniform 2px gap covers rep spacing; a wide slot adds
|
|
9411
|
+
// only the EXTRA (WIDE_GAP − REP_GAP) so the notch totals WIDE_GAP.
|
|
9412
|
+
marginLeft: Math.max(0, slot.leadingGap - REP_GAP),
|
|
9413
|
+
...slot.kind === "continue" ? { borderWidth: 1, borderColor: CONTINUE_OUTLINE } : {}
|
|
8623
9414
|
},
|
|
8624
9415
|
accessibilityElementsHidden: true,
|
|
8625
|
-
testID: `velocity-bar-${i}`
|
|
9416
|
+
testID: slot.kind === "rep" ? `velocity-bar-${i}` : `velocity-slot-${slot.kind}`
|
|
8626
9417
|
},
|
|
8627
9418
|
i
|
|
8628
9419
|
))
|
|
8629
9420
|
}
|
|
8630
9421
|
);
|
|
8631
9422
|
}
|
|
8632
|
-
|
|
8633
|
-
|
|
8634
|
-
|
|
8635
|
-
|
|
8636
|
-
|
|
8637
|
-
|
|
8638
|
-
|
|
8639
|
-
|
|
8640
|
-
|
|
8641
|
-
|
|
8642
|
-
|
|
8643
|
-
|
|
8644
|
-
|
|
8645
|
-
|
|
8646
|
-
|
|
8647
|
-
paddingHorizontal: expanded ? 6 : 0,
|
|
8648
|
-
paddingVertical: expanded ? void 0 : 8,
|
|
8649
|
-
overflow: expanded ? "visible" : "hidden"
|
|
8650
|
-
}
|
|
8651
|
-
],
|
|
8652
|
-
accessibilityRole: hasInteractiveContainer || hasInteractiveReps ? "none" : "button",
|
|
8653
|
-
accessibilityLabel: hasInteractiveContainer || hasInteractiveReps ? void 0 : stripLabel,
|
|
8654
|
-
testID: "velocity-strip",
|
|
8655
|
-
...props,
|
|
8656
|
-
children: [
|
|
8657
|
-
/* @__PURE__ */ jsx(
|
|
9423
|
+
if (!framed) {
|
|
9424
|
+
const { style: externalStyle, ...restProps } = props;
|
|
9425
|
+
return /* @__PURE__ */ jsx(
|
|
9426
|
+
reactNative.View,
|
|
9427
|
+
{
|
|
9428
|
+
className,
|
|
9429
|
+
style: [
|
|
9430
|
+
{ flexDirection: "row", height, gap: REP_GAP, alignItems: "flex-end" },
|
|
9431
|
+
externalStyle
|
|
9432
|
+
],
|
|
9433
|
+
accessibilityRole: "image",
|
|
9434
|
+
accessibilityLabel: miniLabel,
|
|
9435
|
+
testID: "velocity-strip-compact",
|
|
9436
|
+
...restProps,
|
|
9437
|
+
children: slots.map((slot, i) => /* @__PURE__ */ jsx(
|
|
8658
9438
|
reactNative.View,
|
|
8659
9439
|
{
|
|
8660
|
-
style: {
|
|
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
|
-
|
|
9440
|
+
style: {
|
|
9441
|
+
flex: 1,
|
|
9442
|
+
minWidth: 4,
|
|
9443
|
+
height: bareSlotHeight(slot, height, scaleDenom),
|
|
9444
|
+
// Top-rounded to match the framed chart's bars (the "rounded tops from
|
|
9445
|
+
// the numbers one"); square bottoms since bars sit on the baseline.
|
|
9446
|
+
borderTopLeftRadius: 2,
|
|
9447
|
+
borderTopRightRadius: 2,
|
|
9448
|
+
backgroundColor: slotColor(slot),
|
|
9449
|
+
marginLeft: Math.max(0, slot.leadingGap - REP_GAP),
|
|
9450
|
+
...slot.kind === "continue" ? { borderWidth: 1, borderColor: CONTINUE_OUTLINE } : {}
|
|
9451
|
+
},
|
|
9452
|
+
accessibilityElementsHidden: true,
|
|
9453
|
+
testID: slot.kind === "rep" ? `velocity-bar-${i}` : `velocity-slot-${slot.kind}`
|
|
9454
|
+
},
|
|
9455
|
+
i
|
|
9456
|
+
))
|
|
9457
|
+
}
|
|
9458
|
+
);
|
|
9459
|
+
}
|
|
9460
|
+
const stripLabel = set ? `${setAccessibilityLabel(set, repCount)}, tap to ${expanded ? "collapse" : "expand"}` : `Velocity chart for set, ${repCount} reps, tap to ${expanded ? "collapse" : "expand"}`;
|
|
9461
|
+
const hasInteractiveContainer = onToggle != null;
|
|
9462
|
+
const hasInteractiveReps = onRepPress != null && expanded;
|
|
9463
|
+
const setBars = set != null && slots.map((slot, i) => {
|
|
9464
|
+
const isStraightRep = set.type === "straight" && slot.kind === "rep";
|
|
9465
|
+
const heightPct = isStraightRep ? scaleDenom > 0 ? Math.round((slot.velocity ?? 0) / scaleDenom * 100) : 0 : set.type === "straight" ? EXPANDED_TODO_STUB_PCT : EXPANDED_ENCODED_PCT;
|
|
9466
|
+
return /* @__PURE__ */ jsx(
|
|
9467
|
+
reactNative.View,
|
|
9468
|
+
{
|
|
9469
|
+
style: {
|
|
9470
|
+
flex: 1,
|
|
9471
|
+
height: "100%",
|
|
9472
|
+
justifyContent: "flex-end",
|
|
9473
|
+
marginLeft: slot.leadingGap
|
|
9474
|
+
},
|
|
9475
|
+
children: /* @__PURE__ */ jsx(
|
|
9476
|
+
reactNative.View,
|
|
9477
|
+
{
|
|
9478
|
+
style: {
|
|
9479
|
+
height: `${heightPct}%`,
|
|
9480
|
+
minHeight: 2,
|
|
9481
|
+
borderTopLeftRadius: 2,
|
|
9482
|
+
borderTopRightRadius: 2,
|
|
9483
|
+
backgroundColor: slotColor(slot),
|
|
9484
|
+
...slot.kind === "continue" ? { borderWidth: 1, borderColor: CONTINUE_OUTLINE } : {}
|
|
9485
|
+
},
|
|
9486
|
+
testID: slot.kind === "rep" ? `velocity-bar-${i}` : `velocity-slot-${slot.kind}`,
|
|
9487
|
+
accessibilityRole: "image",
|
|
9488
|
+
accessibilityLabel: slotAccessibilityLabel(slot, i)
|
|
9489
|
+
}
|
|
9490
|
+
)
|
|
9491
|
+
},
|
|
9492
|
+
i
|
|
9493
|
+
);
|
|
9494
|
+
});
|
|
9495
|
+
const stripContent = /* @__PURE__ */ jsxs(
|
|
9496
|
+
reactNative.Animated.View,
|
|
9497
|
+
{
|
|
9498
|
+
className: [className, expanded ? "bg-surface-raised" : "bg-transparent"].filter(Boolean).join(" "),
|
|
9499
|
+
style: [
|
|
9500
|
+
{
|
|
9501
|
+
height: heightAnim,
|
|
9502
|
+
width: "100%",
|
|
9503
|
+
gap: 2,
|
|
9504
|
+
borderRadius: expanded ? 6 : 2,
|
|
9505
|
+
paddingTop: expanded ? 16 : 0,
|
|
9506
|
+
paddingBottom: expanded ? 24 : 0,
|
|
9507
|
+
// No horizontal inset — the framed chart's bars span full width, matching
|
|
9508
|
+
// the bare spotlight and mini. Numbers/info sit in the vertical padding.
|
|
9509
|
+
paddingHorizontal: 0,
|
|
9510
|
+
paddingVertical: expanded ? void 0 : 8,
|
|
9511
|
+
overflow: expanded ? "visible" : "hidden"
|
|
9512
|
+
}
|
|
9513
|
+
],
|
|
9514
|
+
accessibilityRole: hasInteractiveContainer || hasInteractiveReps ? "none" : "button",
|
|
9515
|
+
accessibilityLabel: hasInteractiveContainer || hasInteractiveReps ? void 0 : stripLabel,
|
|
9516
|
+
testID: "velocity-strip",
|
|
9517
|
+
...props,
|
|
9518
|
+
children: [
|
|
9519
|
+
/* @__PURE__ */ jsx(reactNative.View, { style: { flexDirection: "row", flex: 1, gap: set ? 0 : 2, alignItems: "flex-end" }, children: set ? setBars : doneVelocities.map((v, i) => {
|
|
9520
|
+
const barBackground = barColorFor(v);
|
|
9521
|
+
const barHeightPct = scaleDenom > 0 ? Math.round(v / scaleDenom * 100) : 0;
|
|
9522
|
+
const isLive = liveRepIndex === i;
|
|
9523
|
+
const liveLabelSuffix = isLive ? isNewPeak ? ", latest rep, new set peak" : ", latest rep" : "";
|
|
9524
|
+
const barStyle = expanded ? {
|
|
9525
|
+
height: `${barHeightPct}%`,
|
|
9526
|
+
minHeight: 2,
|
|
9527
|
+
borderTopLeftRadius: 2,
|
|
9528
|
+
borderTopRightRadius: 2,
|
|
9529
|
+
backgroundColor: barBackground
|
|
9530
|
+
} : {
|
|
9531
|
+
minHeight: "100%",
|
|
9532
|
+
borderTopLeftRadius: 2,
|
|
9533
|
+
borderTopRightRadius: 2,
|
|
9534
|
+
borderBottomLeftRadius: 0,
|
|
9535
|
+
borderBottomRightRadius: 0,
|
|
9536
|
+
backgroundColor: barBackground
|
|
9537
|
+
};
|
|
9538
|
+
const barInner = isLive ? /* @__PURE__ */ jsx(
|
|
9539
|
+
reactNative.Animated.View,
|
|
9540
|
+
{
|
|
9541
|
+
style: [barStyle, { transform: [{ scale: liveScale }] }],
|
|
9542
|
+
testID: `velocity-bar-${i}`,
|
|
9543
|
+
accessibilityRole: "image",
|
|
9544
|
+
accessibilityLabel: `Rep ${i + 1}: ${formatVelocity(v)} meters per second${liveLabelSuffix}`
|
|
9545
|
+
}
|
|
9546
|
+
) : /* @__PURE__ */ jsx(
|
|
9547
|
+
reactNative.View,
|
|
9548
|
+
{
|
|
9549
|
+
style: barStyle,
|
|
9550
|
+
testID: `velocity-bar-${i}`,
|
|
9551
|
+
accessibilityRole: "image",
|
|
9552
|
+
accessibilityLabel: `Rep ${i + 1}: ${formatVelocity(v)} meters per second`
|
|
9553
|
+
}
|
|
9554
|
+
);
|
|
9555
|
+
const bar = /* @__PURE__ */ jsxs(
|
|
9556
|
+
reactNative.View,
|
|
9557
|
+
{
|
|
9558
|
+
style: {
|
|
9559
|
+
flex: 1,
|
|
9560
|
+
height: "100%",
|
|
9561
|
+
justifyContent: "flex-end",
|
|
9562
|
+
position: "relative"
|
|
9563
|
+
},
|
|
9564
|
+
children: [
|
|
9565
|
+
expanded && showNumbers && /* @__PURE__ */ jsx(
|
|
9566
|
+
reactNative.Animated.View,
|
|
9567
|
+
{
|
|
9568
|
+
style: {
|
|
9569
|
+
opacity: labelOpacity,
|
|
9570
|
+
alignItems: "center",
|
|
9571
|
+
position: "absolute",
|
|
9572
|
+
top: -13,
|
|
9573
|
+
left: 0,
|
|
9574
|
+
right: 0
|
|
9575
|
+
},
|
|
9576
|
+
accessibilityElementsHidden: true,
|
|
9577
|
+
children: /* @__PURE__ */ jsx(
|
|
8697
9578
|
reactNative.Text,
|
|
8698
9579
|
{
|
|
8699
9580
|
className: "text-text-secondary",
|
|
@@ -8701,30 +9582,30 @@ function VelocityStrip({
|
|
|
8701
9582
|
testID: `velocity-label-${i}`,
|
|
8702
9583
|
children: formatVelocity(v)
|
|
8703
9584
|
}
|
|
8704
|
-
)
|
|
8705
|
-
|
|
8706
|
-
|
|
8707
|
-
|
|
8708
|
-
|
|
8709
|
-
|
|
8710
|
-
|
|
8711
|
-
|
|
8712
|
-
|
|
8713
|
-
|
|
8714
|
-
|
|
8715
|
-
|
|
8716
|
-
|
|
8717
|
-
|
|
8718
|
-
|
|
8719
|
-
|
|
8720
|
-
|
|
8721
|
-
|
|
8722
|
-
|
|
8723
|
-
|
|
8724
|
-
|
|
8725
|
-
})
|
|
9585
|
+
)
|
|
9586
|
+
}
|
|
9587
|
+
),
|
|
9588
|
+
barInner
|
|
9589
|
+
]
|
|
9590
|
+
},
|
|
9591
|
+
i
|
|
9592
|
+
);
|
|
9593
|
+
if (onRepPress && expanded) {
|
|
9594
|
+
return /* @__PURE__ */ jsx(
|
|
9595
|
+
reactNative.Pressable,
|
|
9596
|
+
{
|
|
9597
|
+
style: { flex: 1, height: "100%" },
|
|
9598
|
+
onPress: () => onRepPress(i, v),
|
|
9599
|
+
accessibilityRole: "button",
|
|
9600
|
+
accessibilityLabel: `Rep ${i + 1}: ${formatVelocity(v)} meters per second, tap for details`,
|
|
9601
|
+
testID: `velocity-bar-pressable-${i}`,
|
|
9602
|
+
children: bar
|
|
9603
|
+
},
|
|
9604
|
+
i
|
|
9605
|
+
);
|
|
8726
9606
|
}
|
|
8727
|
-
|
|
9607
|
+
return bar;
|
|
9608
|
+
}) }),
|
|
8728
9609
|
expanded && showInfo && /* @__PURE__ */ jsxs(
|
|
8729
9610
|
reactNative.Animated.View,
|
|
8730
9611
|
{
|
|
@@ -9022,233 +9903,90 @@ function Sparkline({
|
|
|
9022
9903
|
}
|
|
9023
9904
|
);
|
|
9024
9905
|
}
|
|
9025
|
-
|
|
9026
|
-
|
|
9027
|
-
|
|
9028
|
-
|
|
9906
|
+
var DARK = getSemanticColors("dark");
|
|
9907
|
+
var TEXT_ACTIVE = DARK["text-primary"];
|
|
9908
|
+
var TEXT_MUTED = DARK["text-secondary"];
|
|
9909
|
+
var COL = { set: 36, reps: 44, load: 56, rpe: 36 };
|
|
9910
|
+
var cellText = { fontFamily: "Inter, sans-serif", fontSize: 13, fontWeight: "600" };
|
|
9911
|
+
var typeBadge = {
|
|
9912
|
+
fontSize: 10,
|
|
9913
|
+
fontWeight: "700",
|
|
9914
|
+
fontFamily: "Inter, sans-serif",
|
|
9915
|
+
color: WORKOUT_TOKENS.scale.orange,
|
|
9916
|
+
backgroundColor: "rgba(255,165,2,0.12)",
|
|
9917
|
+
paddingVertical: 1,
|
|
9918
|
+
paddingHorizontal: 5,
|
|
9919
|
+
borderRadius: 3,
|
|
9920
|
+
overflow: "hidden"
|
|
9921
|
+
};
|
|
9922
|
+
function displayReps(set) {
|
|
9923
|
+
return set.state === "done" ? set.reps : set.target.reps;
|
|
9924
|
+
}
|
|
9925
|
+
function displayWeight(set) {
|
|
9926
|
+
return set.state === "done" ? set.weight : set.target.weight;
|
|
9927
|
+
}
|
|
9928
|
+
function plannedReps(set) {
|
|
9929
|
+
if (set.state === "done") return set.velocities.length;
|
|
9930
|
+
if (set.state === "live") return set.target.reps;
|
|
9931
|
+
return set.planned ?? set.target.reps;
|
|
9932
|
+
}
|
|
9933
|
+
function accessibilityLabel(set) {
|
|
9934
|
+
const base = `Set ${set.setNumber}: ${displayReps(set)} reps at ${roundWeight(displayWeight(set))} ${set.unit}`;
|
|
9935
|
+
if (set.state === "live") return `${base}, in progress`;
|
|
9936
|
+
if (set.state === "todo") return `${base}, upcoming`;
|
|
9937
|
+
return base;
|
|
9938
|
+
}
|
|
9939
|
+
function Cell({
|
|
9940
|
+
width,
|
|
9941
|
+
children,
|
|
9942
|
+
testID
|
|
9943
|
+
}) {
|
|
9944
|
+
return /* @__PURE__ */ jsx(reactNative.View, { style: { width, alignItems: "center", justifyContent: "center" }, testID, children });
|
|
9029
9945
|
}
|
|
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;
|
|
9946
|
+
function RowStrip({ set }) {
|
|
9947
|
+
const zones = set.velocityZones;
|
|
9948
|
+
if (set.state === "live") {
|
|
9949
|
+
return /* @__PURE__ */ jsx(
|
|
9950
|
+
VelocityStrip,
|
|
9951
|
+
{
|
|
9952
|
+
variant: "expanded",
|
|
9953
|
+
showNumbers: false,
|
|
9954
|
+
showInfo: false,
|
|
9955
|
+
height: 24,
|
|
9956
|
+
scale: "fixed",
|
|
9957
|
+
set: { type: "straight", velocities: set.velocities, planned: set.target.reps },
|
|
9958
|
+
zones
|
|
9959
|
+
}
|
|
9960
|
+
);
|
|
9065
9961
|
}
|
|
9962
|
+
const velocities = set.state === "done" ? set.velocities : [];
|
|
9963
|
+
return /* @__PURE__ */ jsx(
|
|
9964
|
+
VelocityStrip,
|
|
9965
|
+
{
|
|
9966
|
+
variant: "mini",
|
|
9967
|
+
set: { type: "straight", velocities, planned: plannedReps(set) },
|
|
9968
|
+
zones
|
|
9969
|
+
}
|
|
9970
|
+
);
|
|
9971
|
+
}
|
|
9972
|
+
function SetRow(set) {
|
|
9973
|
+
const live = set.state === "live";
|
|
9974
|
+
const valueColor = live ? TEXT_ACTIVE : TEXT_MUTED;
|
|
9975
|
+
const rpe = set.state === "todo" ? null : set.rpe;
|
|
9066
9976
|
return /* @__PURE__ */ jsxs(
|
|
9067
9977
|
reactNative.View,
|
|
9068
9978
|
{
|
|
9069
|
-
style:
|
|
9070
|
-
accessibilityLabel:
|
|
9071
|
-
setNumber,
|
|
9072
|
-
reps,
|
|
9073
|
-
weight,
|
|
9074
|
-
unit
|
|
9075
|
-
),
|
|
9979
|
+
style: { paddingVertical: 6, paddingHorizontal: 8 },
|
|
9980
|
+
accessibilityLabel: accessibilityLabel(set),
|
|
9076
9981
|
testID: "set-row",
|
|
9077
9982
|
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
|
-
)
|
|
9983
|
+
/* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row items-center", style: { justifyContent: "space-between" }, children: [
|
|
9984
|
+
/* @__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 }) }),
|
|
9985
|
+
/* @__PURE__ */ jsx(Cell, { width: COL.reps, testID: "set-row-reps", children: /* @__PURE__ */ jsx(reactNative.Text, { style: { ...cellText, color: valueColor }, children: displayReps(set) }) }),
|
|
9986
|
+
/* @__PURE__ */ jsx(Cell, { width: COL.load, testID: "set-row-weight", children: /* @__PURE__ */ jsx(reactNative.Text, { style: { ...cellText, color: valueColor }, children: roundWeight(displayWeight(set)) }) }),
|
|
9987
|
+
/* @__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" }) })
|
|
9988
|
+
] }),
|
|
9989
|
+
/* @__PURE__ */ jsx(reactNative.View, { style: { marginTop: live ? 6 : 4 }, testID: "set-row-strip", children: /* @__PURE__ */ jsx(RowStrip, { set }) })
|
|
9252
9990
|
]
|
|
9253
9991
|
}
|
|
9254
9992
|
);
|
|
@@ -9410,23 +10148,139 @@ function InputBar({
|
|
|
9410
10148
|
);
|
|
9411
10149
|
}
|
|
9412
10150
|
var BRAND_PRIMARY4 = getSemanticColors("dark")["brand-primary"];
|
|
9413
|
-
|
|
9414
|
-
|
|
9415
|
-
|
|
10151
|
+
var RING_DEFAULT_SIZE = 180;
|
|
10152
|
+
function RestActions({ onAddTime, onSkip }) {
|
|
10153
|
+
return /* @__PURE__ */ jsxs(reactNative.View, { style: { flexDirection: "row", gap: 8 }, children: [
|
|
10154
|
+
/* @__PURE__ */ jsx(
|
|
10155
|
+
reactNative.Pressable,
|
|
10156
|
+
{
|
|
10157
|
+
onPress: onAddTime,
|
|
10158
|
+
style: {
|
|
10159
|
+
backgroundColor: "rgba(255,255,255,0.06)",
|
|
10160
|
+
paddingVertical: 8,
|
|
10161
|
+
paddingHorizontal: 20,
|
|
10162
|
+
borderRadius: 8
|
|
10163
|
+
},
|
|
10164
|
+
accessibilityRole: "button",
|
|
10165
|
+
accessibilityLabel: "Add 30 seconds",
|
|
10166
|
+
testID: "rest-timer-add-time",
|
|
10167
|
+
children: /* @__PURE__ */ jsx(
|
|
10168
|
+
reactNative.Text,
|
|
10169
|
+
{
|
|
10170
|
+
className: "text-text-secondary",
|
|
10171
|
+
style: { fontSize: 11, fontFamily: "Inter, sans-serif", fontWeight: "600" },
|
|
10172
|
+
children: "+30s"
|
|
10173
|
+
}
|
|
10174
|
+
)
|
|
10175
|
+
}
|
|
10176
|
+
),
|
|
10177
|
+
/* @__PURE__ */ jsx(
|
|
10178
|
+
reactNative.Pressable,
|
|
10179
|
+
{
|
|
10180
|
+
onPress: onSkip,
|
|
10181
|
+
style: {
|
|
10182
|
+
backgroundColor: "rgba(255,121,0,0.12)",
|
|
10183
|
+
paddingVertical: 8,
|
|
10184
|
+
paddingHorizontal: 20,
|
|
10185
|
+
borderRadius: 8
|
|
10186
|
+
},
|
|
10187
|
+
accessibilityRole: "button",
|
|
10188
|
+
accessibilityLabel: "Skip rest",
|
|
10189
|
+
testID: "rest-timer-skip",
|
|
10190
|
+
children: /* @__PURE__ */ jsx(
|
|
10191
|
+
reactNative.Text,
|
|
10192
|
+
{
|
|
10193
|
+
style: {
|
|
10194
|
+
fontSize: 11,
|
|
10195
|
+
fontFamily: "Inter, sans-serif",
|
|
10196
|
+
fontWeight: "600",
|
|
10197
|
+
color: BRAND_PRIMARY4
|
|
10198
|
+
},
|
|
10199
|
+
children: "Skip"
|
|
10200
|
+
}
|
|
10201
|
+
)
|
|
10202
|
+
}
|
|
10203
|
+
)
|
|
10204
|
+
] });
|
|
10205
|
+
}
|
|
10206
|
+
function RestTimerRing({
|
|
10207
|
+
totalSeconds,
|
|
10208
|
+
elapsedMs,
|
|
10209
|
+
remainingSec,
|
|
10210
|
+
done,
|
|
10211
|
+
size,
|
|
10212
|
+
nextSetInfo,
|
|
10213
|
+
displayOnly,
|
|
10214
|
+
onSkip,
|
|
10215
|
+
onAddTime
|
|
10216
|
+
}) {
|
|
10217
|
+
const a11yLabel = done ? "Rest complete, next set ready" : `Rest timer, ${Math.max(0, remainingSec)} seconds remaining`;
|
|
10218
|
+
return /* @__PURE__ */ jsxs(reactNative.View, { style: { alignItems: "center", gap: 16 }, testID: "rest-timer", children: [
|
|
10219
|
+
/* @__PURE__ */ jsx(
|
|
10220
|
+
CircularTimer,
|
|
10221
|
+
{
|
|
10222
|
+
durationMs: totalSeconds * 1e3,
|
|
10223
|
+
elapsedMs,
|
|
10224
|
+
mode: "down",
|
|
10225
|
+
size,
|
|
10226
|
+
doneLabel: "GO",
|
|
10227
|
+
doneColor: "success",
|
|
10228
|
+
accessibilityLabel: a11yLabel,
|
|
10229
|
+
controls: !displayOnly ? /* @__PURE__ */ jsx(RestActions, { onAddTime, onSkip }) : void 0,
|
|
10230
|
+
testID: "rest-timer-ring"
|
|
10231
|
+
}
|
|
10232
|
+
),
|
|
10233
|
+
nextSetInfo != null && /* @__PURE__ */ jsx(
|
|
10234
|
+
reactNative.Text,
|
|
10235
|
+
{
|
|
10236
|
+
className: "text-text-tertiary",
|
|
10237
|
+
style: { fontSize: 13, fontFamily: "Inter, sans-serif" },
|
|
10238
|
+
testID: "rest-timer-next-set",
|
|
10239
|
+
children: nextSetInfo
|
|
10240
|
+
}
|
|
10241
|
+
)
|
|
10242
|
+
] });
|
|
10243
|
+
}
|
|
10244
|
+
function RestTimer({
|
|
10245
|
+
totalSeconds,
|
|
10246
|
+
elapsedMs,
|
|
9416
10247
|
onSkip,
|
|
9417
10248
|
onAddTime,
|
|
9418
10249
|
nextSetInfo,
|
|
9419
10250
|
visible,
|
|
9420
|
-
displayOnly = false
|
|
10251
|
+
displayOnly = false,
|
|
10252
|
+
variant = "bar",
|
|
10253
|
+
size = RING_DEFAULT_SIZE
|
|
9421
10254
|
}) {
|
|
9422
|
-
|
|
9423
|
-
|
|
10255
|
+
const {
|
|
10256
|
+
remainingMs,
|
|
10257
|
+
progress,
|
|
10258
|
+
label: timeDisplay,
|
|
10259
|
+
done
|
|
10260
|
+
} = useTimer({
|
|
10261
|
+
mode: "down",
|
|
10262
|
+
durationMs: totalSeconds * 1e3,
|
|
10263
|
+
elapsedMs
|
|
10264
|
+
});
|
|
9424
10265
|
const remainingSec = Math.ceil(remainingMs / 1e3);
|
|
9425
|
-
const
|
|
9426
|
-
|
|
9427
|
-
|
|
9428
|
-
|
|
9429
|
-
|
|
10266
|
+
const progressPct = progress * 100;
|
|
10267
|
+
if (!visible) return null;
|
|
10268
|
+
if (variant === "ring") {
|
|
10269
|
+
return /* @__PURE__ */ jsx(
|
|
10270
|
+
RestTimerRing,
|
|
10271
|
+
{
|
|
10272
|
+
totalSeconds,
|
|
10273
|
+
elapsedMs,
|
|
10274
|
+
remainingSec,
|
|
10275
|
+
done,
|
|
10276
|
+
size,
|
|
10277
|
+
nextSetInfo,
|
|
10278
|
+
displayOnly,
|
|
10279
|
+
onSkip,
|
|
10280
|
+
onAddTime
|
|
10281
|
+
}
|
|
10282
|
+
);
|
|
10283
|
+
}
|
|
9430
10284
|
return /* @__PURE__ */ jsxs(
|
|
9431
10285
|
reactNative.View,
|
|
9432
10286
|
{
|
|
@@ -9524,69 +10378,318 @@ function RestTimer({
|
|
|
9524
10378
|
)
|
|
9525
10379
|
}
|
|
9526
10380
|
),
|
|
9527
|
-
!displayOnly && /* @__PURE__ */
|
|
10381
|
+
!displayOnly && /* @__PURE__ */ jsx(RestActions, { onAddTime, onSkip })
|
|
10382
|
+
]
|
|
10383
|
+
}
|
|
10384
|
+
);
|
|
10385
|
+
}
|
|
10386
|
+
var t8 = getSemanticColors("dark");
|
|
10387
|
+
var TIMES = "\xD7";
|
|
10388
|
+
var AT = "@";
|
|
10389
|
+
function SetsRepsLoad({
|
|
10390
|
+
sets,
|
|
10391
|
+
reps,
|
|
10392
|
+
load,
|
|
10393
|
+
unit = "lb",
|
|
10394
|
+
className,
|
|
10395
|
+
...props
|
|
10396
|
+
}) {
|
|
10397
|
+
const value = t8["text-primary"];
|
|
10398
|
+
const sep = t8["text-tertiary"];
|
|
10399
|
+
return /* @__PURE__ */ jsxs(
|
|
10400
|
+
reactNative.View,
|
|
10401
|
+
{
|
|
10402
|
+
className,
|
|
10403
|
+
style: { flexDirection: "row", alignItems: "baseline" },
|
|
10404
|
+
accessibilityLabel: `${sets} sets of ${reps} reps at ${load} ${unit}`,
|
|
10405
|
+
testID: "sets-reps-load",
|
|
10406
|
+
...props,
|
|
10407
|
+
children: [
|
|
10408
|
+
/* @__PURE__ */ jsx(MetricCell, { color: value, children: sets }),
|
|
10409
|
+
/* @__PURE__ */ jsx(MetricCell, { color: sep, children: ` ${TIMES} ` }),
|
|
10410
|
+
/* @__PURE__ */ jsx(MetricCell, { color: value, children: reps }),
|
|
10411
|
+
/* @__PURE__ */ jsx(MetricCell, { color: sep, children: ` ${AT} ` }),
|
|
10412
|
+
/* @__PURE__ */ jsx(MetricCell, { color: value, children: load }),
|
|
10413
|
+
/* @__PURE__ */ jsx(MetricCell, { color: sep, children: ` ${unit}` })
|
|
10414
|
+
]
|
|
10415
|
+
}
|
|
10416
|
+
);
|
|
10417
|
+
}
|
|
10418
|
+
|
|
10419
|
+
// src/components/custom/Workout/paceTone.ts
|
|
10420
|
+
function paceTone(progress, target) {
|
|
10421
|
+
if (target === void 0) return "neutral";
|
|
10422
|
+
return progress >= target ? "ahead" : "behind";
|
|
10423
|
+
}
|
|
10424
|
+
var PACE_TONE_COLORS = {
|
|
10425
|
+
ahead: getSemanticColors("dark")["status-success"],
|
|
10426
|
+
behind: getSemanticColors("dark")["status-warning"],
|
|
10427
|
+
neutral: primitiveRamps.cyan[400]
|
|
10428
|
+
};
|
|
10429
|
+
function paceToneColor(tone) {
|
|
10430
|
+
return PACE_TONE_COLORS[tone];
|
|
10431
|
+
}
|
|
10432
|
+
|
|
10433
|
+
// src/components/custom/Workout/SegmentedProgressBar.tsx
|
|
10434
|
+
var DEFAULT_MARKER_COLOR = primitiveRamps.green[500];
|
|
10435
|
+
function SegmentedProgressBar({
|
|
10436
|
+
segments,
|
|
10437
|
+
value,
|
|
10438
|
+
target,
|
|
10439
|
+
markerColor = DEFAULT_MARKER_COLOR,
|
|
10440
|
+
color,
|
|
10441
|
+
height,
|
|
10442
|
+
gap,
|
|
10443
|
+
...props
|
|
10444
|
+
}) {
|
|
10445
|
+
const total = segments.reduce((sum, s) => sum + s.weight, 0);
|
|
10446
|
+
const fillColor = color ?? paceToneColor(paceTone(total > 0 ? value / total : 0, target));
|
|
10447
|
+
const barSegments = segments.map((seg, i) => {
|
|
10448
|
+
const cumulativeBefore = segments.slice(0, i).reduce((sum, s) => sum + s.weight, 0);
|
|
10449
|
+
const fill = Math.max(0, Math.min(1, (value - cumulativeBefore) / seg.weight));
|
|
10450
|
+
return { weight: seg.weight, fill, color: fillColor };
|
|
10451
|
+
});
|
|
10452
|
+
return /* @__PURE__ */ jsx(
|
|
10453
|
+
SegmentedBar,
|
|
10454
|
+
{
|
|
10455
|
+
segments: barSegments,
|
|
10456
|
+
marker: target != null ? { position: target, color: markerColor } : null,
|
|
10457
|
+
height,
|
|
10458
|
+
gap,
|
|
10459
|
+
...props
|
|
10460
|
+
}
|
|
10461
|
+
);
|
|
10462
|
+
}
|
|
10463
|
+
|
|
10464
|
+
// src/components/custom/Workout/MetricTiles.tsx
|
|
10465
|
+
function MetricTiles({ metrics, gap = 1, ...props }) {
|
|
10466
|
+
return /* @__PURE__ */ jsx(HStack, { gap, align: "stretch", ...props, children: metrics.map((metric) => /* @__PURE__ */ jsx(
|
|
10467
|
+
Tile,
|
|
10468
|
+
{
|
|
10469
|
+
label: metric.label,
|
|
10470
|
+
value: metric.value,
|
|
10471
|
+
valueColor: metric.valueColor
|
|
10472
|
+
},
|
|
10473
|
+
metric.label
|
|
10474
|
+
)) });
|
|
10475
|
+
}
|
|
10476
|
+
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
10477
|
+
var SOON_ACCENT = primitiveRamps.orange[400];
|
|
10478
|
+
function compactUntil(ms) {
|
|
10479
|
+
if (ms <= 0) return "now";
|
|
10480
|
+
const min = Math.round(ms / 6e4);
|
|
10481
|
+
if (min < 60) return `${min}m`;
|
|
10482
|
+
const hr = Math.round(min / 60);
|
|
10483
|
+
if (hr < 24) return `${hr}h`;
|
|
10484
|
+
const d = Math.round(hr / 24);
|
|
10485
|
+
if (d < 7) return `${d}d`;
|
|
10486
|
+
return `${Math.round(d / 7)}w`;
|
|
10487
|
+
}
|
|
10488
|
+
function ScheduleTiles({ when, now, gap = 1, ...props }) {
|
|
10489
|
+
const [mountNow] = React24.useState(() => Date.now());
|
|
10490
|
+
const reference = now ?? mountNow;
|
|
10491
|
+
const whenMs = when instanceof Date ? when.getTime() : when;
|
|
10492
|
+
const soon = whenMs - reference < DAY_MS;
|
|
10493
|
+
const time = new Date(whenMs).toLocaleTimeString(void 0, {
|
|
10494
|
+
hour: "numeric",
|
|
10495
|
+
minute: "2-digit",
|
|
10496
|
+
hour12: true
|
|
10497
|
+
});
|
|
10498
|
+
return /* @__PURE__ */ jsxs(HStack, { gap, ...props, children: [
|
|
10499
|
+
/* @__PURE__ */ jsx(Tile, { label: "Date", value: formatDateTime(when, "short") }),
|
|
10500
|
+
/* @__PURE__ */ jsx(Tile, { label: "Time", value: time }),
|
|
10501
|
+
/* @__PURE__ */ jsx(
|
|
10502
|
+
Tile,
|
|
10503
|
+
{
|
|
10504
|
+
label: "Until",
|
|
10505
|
+
value: compactUntil(whenMs - reference),
|
|
10506
|
+
valueColor: soon ? SOON_ACCENT : void 0
|
|
10507
|
+
}
|
|
10508
|
+
)
|
|
10509
|
+
] });
|
|
10510
|
+
}
|
|
10511
|
+
var t9 = getSemanticColors("dark");
|
|
10512
|
+
var INDICATOR_CONFIG = {
|
|
10513
|
+
imbalance: { Icon: ScaleIcon, color: t9["status-error"], label: "Left/right imbalance" },
|
|
10514
|
+
overshoot: { Icon: AlertTriangleIcon, color: t9["status-error"], label: "Load overshoot" },
|
|
10515
|
+
"velocity-loss": { Icon: TrendingDownIcon, color: t9["status-warning"], label: "Velocity loss" },
|
|
10516
|
+
"missed-reps": { Icon: CircleSlashIcon, color: t9["status-warning"], label: "Missed reps" },
|
|
10517
|
+
pr: { Icon: AwardIcon, color: t9["status-success"], label: "Personal record" },
|
|
10518
|
+
info: { Icon: InfoIcon, color: t9["status-info"], label: "More info" }
|
|
10519
|
+
};
|
|
10520
|
+
var INDICATOR_PRECEDENCE = [
|
|
10521
|
+
"imbalance",
|
|
10522
|
+
"overshoot",
|
|
10523
|
+
"velocity-loss",
|
|
10524
|
+
"missed-reps",
|
|
10525
|
+
"pr",
|
|
10526
|
+
"info"
|
|
10527
|
+
];
|
|
10528
|
+
function resolveIndicator(candidates) {
|
|
10529
|
+
return INDICATOR_PRECEDENCE.find((kind) => candidates.includes(kind));
|
|
10530
|
+
}
|
|
10531
|
+
function ExerciseIndicator({ kind, onPress, className, ...props }) {
|
|
10532
|
+
const { Icon, color, label } = INDICATOR_CONFIG[kind];
|
|
10533
|
+
const chip = /* @__PURE__ */ jsx(
|
|
10534
|
+
reactNative.View,
|
|
10535
|
+
{
|
|
10536
|
+
className,
|
|
10537
|
+
style: {
|
|
10538
|
+
width: 16,
|
|
10539
|
+
height: 16,
|
|
10540
|
+
borderRadius: 8,
|
|
10541
|
+
borderWidth: 1,
|
|
10542
|
+
borderColor: color,
|
|
10543
|
+
alignItems: "center",
|
|
10544
|
+
justifyContent: "center"
|
|
10545
|
+
},
|
|
10546
|
+
accessibilityRole: "image",
|
|
10547
|
+
accessibilityLabel: label,
|
|
10548
|
+
testID: "exercise-indicator",
|
|
10549
|
+
...props,
|
|
10550
|
+
children: /* @__PURE__ */ jsx(Icon, { size: 11, color })
|
|
10551
|
+
}
|
|
10552
|
+
);
|
|
10553
|
+
if (onPress) {
|
|
10554
|
+
return /* @__PURE__ */ jsx(reactNative.Pressable, { onPress, accessibilityRole: "button", accessibilityLabel: label, children: chip });
|
|
10555
|
+
}
|
|
10556
|
+
return chip;
|
|
10557
|
+
}
|
|
10558
|
+
function headingLabel(name, sets, reps, load, unit) {
|
|
10559
|
+
return `${name}, ${sets}\xD7${reps} @ ${roundWeight(load)} ${unit}`;
|
|
10560
|
+
}
|
|
10561
|
+
function ExerciseHeading({
|
|
10562
|
+
name,
|
|
10563
|
+
sets,
|
|
10564
|
+
reps,
|
|
10565
|
+
load,
|
|
10566
|
+
unit = "lbs",
|
|
10567
|
+
tempo,
|
|
10568
|
+
indicator,
|
|
10569
|
+
dimmed,
|
|
10570
|
+
onPress
|
|
10571
|
+
}) {
|
|
10572
|
+
return /* @__PURE__ */ jsxs(reactNative.View, { style: { opacity: dimmed ? 0.55 : 1 }, testID: "exercise-heading", children: [
|
|
10573
|
+
/* @__PURE__ */ jsxs(
|
|
10574
|
+
reactNative.Pressable,
|
|
10575
|
+
{
|
|
10576
|
+
onPress,
|
|
10577
|
+
accessibilityRole: "button",
|
|
10578
|
+
accessibilityLabel: headingLabel(name, sets, reps, load, unit),
|
|
10579
|
+
className: "flex-row items-center",
|
|
10580
|
+
style: { gap: 8 },
|
|
10581
|
+
testID: "exercise-card-header",
|
|
10582
|
+
children: [
|
|
9528
10583
|
/* @__PURE__ */ jsx(
|
|
9529
|
-
reactNative.
|
|
10584
|
+
reactNative.Text,
|
|
9530
10585
|
{
|
|
9531
|
-
|
|
9532
|
-
style: {
|
|
9533
|
-
|
|
9534
|
-
|
|
9535
|
-
paddingHorizontal: 20,
|
|
9536
|
-
borderRadius: 8
|
|
9537
|
-
},
|
|
9538
|
-
accessibilityRole: "button",
|
|
9539
|
-
accessibilityLabel: "Add 30 seconds",
|
|
9540
|
-
testID: "rest-timer-add-time",
|
|
9541
|
-
children: /* @__PURE__ */ jsx(
|
|
9542
|
-
reactNative.Text,
|
|
9543
|
-
{
|
|
9544
|
-
className: "text-text-secondary",
|
|
9545
|
-
style: {
|
|
9546
|
-
fontSize: 11,
|
|
9547
|
-
fontFamily: "Inter, sans-serif",
|
|
9548
|
-
fontWeight: "600"
|
|
9549
|
-
},
|
|
9550
|
-
children: "+30s"
|
|
9551
|
-
}
|
|
9552
|
-
)
|
|
10586
|
+
className: "text-text-primary",
|
|
10587
|
+
style: { fontSize: 14, fontFamily: '"Space Grotesk", sans-serif', fontWeight: "700" },
|
|
10588
|
+
testID: "exercise-card-name",
|
|
10589
|
+
children: name
|
|
9553
10590
|
}
|
|
9554
10591
|
),
|
|
9555
|
-
/* @__PURE__ */ jsx(
|
|
9556
|
-
|
|
9557
|
-
|
|
9558
|
-
|
|
9559
|
-
|
|
9560
|
-
|
|
9561
|
-
|
|
9562
|
-
|
|
9563
|
-
|
|
9564
|
-
|
|
9565
|
-
|
|
9566
|
-
|
|
9567
|
-
|
|
9568
|
-
|
|
9569
|
-
|
|
9570
|
-
|
|
9571
|
-
|
|
9572
|
-
|
|
9573
|
-
|
|
9574
|
-
|
|
9575
|
-
|
|
9576
|
-
|
|
9577
|
-
|
|
9578
|
-
|
|
9579
|
-
|
|
9580
|
-
|
|
9581
|
-
|
|
9582
|
-
|
|
10592
|
+
/* @__PURE__ */ jsx(reactNative.View, { className: "flex-1" }),
|
|
10593
|
+
indicator && /* @__PURE__ */ jsx(ExerciseIndicator, { kind: indicator })
|
|
10594
|
+
]
|
|
10595
|
+
}
|
|
10596
|
+
),
|
|
10597
|
+
/* @__PURE__ */ jsxs(
|
|
10598
|
+
reactNative.View,
|
|
10599
|
+
{
|
|
10600
|
+
className: "flex-row items-center",
|
|
10601
|
+
style: { gap: 8, marginTop: 1 },
|
|
10602
|
+
testID: "exercise-card-summary",
|
|
10603
|
+
children: [
|
|
10604
|
+
/* @__PURE__ */ jsx(SetsRepsLoad, { sets, reps, load, unit }),
|
|
10605
|
+
/* @__PURE__ */ jsx(reactNative.View, { className: "flex-1" }),
|
|
10606
|
+
tempo && /* @__PURE__ */ jsx(TempoDisplay, { tempo, size: "sm", showLabel: false, showInfo: false })
|
|
10607
|
+
]
|
|
10608
|
+
}
|
|
10609
|
+
)
|
|
10610
|
+
] });
|
|
10611
|
+
}
|
|
10612
|
+
function ExerciseCardHeading({
|
|
10613
|
+
setStates,
|
|
10614
|
+
stripHeight = 8,
|
|
10615
|
+
dimmed,
|
|
10616
|
+
testID = "exercise-card",
|
|
10617
|
+
...heading
|
|
10618
|
+
}) {
|
|
10619
|
+
return /* @__PURE__ */ jsxs(
|
|
10620
|
+
reactNative.View,
|
|
10621
|
+
{
|
|
10622
|
+
style: { paddingVertical: 9, paddingHorizontal: 12, opacity: dimmed ? 0.55 : 1 },
|
|
10623
|
+
testID,
|
|
10624
|
+
children: [
|
|
10625
|
+
/* @__PURE__ */ jsx(ExerciseHeading, { ...heading }),
|
|
10626
|
+
setStates.length > 0 && /* @__PURE__ */ jsx(reactNative.View, { style: { marginTop: 7 }, testID: "exercise-card-strip", children: /* @__PURE__ */ jsx(SetStrip, { sets: setStates, height: stripHeight }) })
|
|
9583
10627
|
]
|
|
9584
10628
|
}
|
|
9585
10629
|
);
|
|
9586
10630
|
}
|
|
9587
|
-
|
|
9588
|
-
|
|
10631
|
+
var PREV_COLUMN = { label: "PREV", width: void 0 };
|
|
10632
|
+
function buildColumns(unit, showPrevious) {
|
|
10633
|
+
return [
|
|
10634
|
+
{ label: "SET", width: 36 },
|
|
10635
|
+
...showPrevious ? [PREV_COLUMN] : [],
|
|
10636
|
+
{ label: "REPS", width: 44 },
|
|
10637
|
+
{ label: unit.toUpperCase(), width: 56 },
|
|
10638
|
+
{ label: "RPE", width: 36 }
|
|
10639
|
+
];
|
|
9589
10640
|
}
|
|
10641
|
+
function SetTableHeader({
|
|
10642
|
+
unit = "lbs",
|
|
10643
|
+
showPrevious = true,
|
|
10644
|
+
testID = "table-header"
|
|
10645
|
+
}) {
|
|
10646
|
+
const columns = buildColumns(unit, showPrevious);
|
|
10647
|
+
return /* @__PURE__ */ jsx(
|
|
10648
|
+
reactNative.View,
|
|
10649
|
+
{
|
|
10650
|
+
className: "flex-row",
|
|
10651
|
+
style: {
|
|
10652
|
+
paddingVertical: 8,
|
|
10653
|
+
paddingHorizontal: 8,
|
|
10654
|
+
paddingBottom: 4,
|
|
10655
|
+
...showPrevious ? {} : { justifyContent: "space-between" }
|
|
10656
|
+
},
|
|
10657
|
+
testID,
|
|
10658
|
+
children: columns.map(({ label, width }) => {
|
|
10659
|
+
const isFlex = width === void 0;
|
|
10660
|
+
return /* @__PURE__ */ jsx(
|
|
10661
|
+
reactNative.View,
|
|
10662
|
+
{
|
|
10663
|
+
style: {
|
|
10664
|
+
...isFlex ? { minWidth: 44 } : { width, flexShrink: 1 },
|
|
10665
|
+
alignItems: "center",
|
|
10666
|
+
justifyContent: "center",
|
|
10667
|
+
gap: 8
|
|
10668
|
+
},
|
|
10669
|
+
...isFlex ? { className: "flex-1" } : {},
|
|
10670
|
+
children: /* @__PURE__ */ jsx(
|
|
10671
|
+
reactNative.Text,
|
|
10672
|
+
{
|
|
10673
|
+
className: "text-text-tertiary",
|
|
10674
|
+
style: {
|
|
10675
|
+
fontSize: 10,
|
|
10676
|
+
fontWeight: "600",
|
|
10677
|
+
fontFamily: "Inter, sans-serif",
|
|
10678
|
+
textTransform: "uppercase",
|
|
10679
|
+
letterSpacing: 0.8
|
|
10680
|
+
},
|
|
10681
|
+
children: label
|
|
10682
|
+
}
|
|
10683
|
+
)
|
|
10684
|
+
},
|
|
10685
|
+
label
|
|
10686
|
+
);
|
|
10687
|
+
})
|
|
10688
|
+
}
|
|
10689
|
+
);
|
|
10690
|
+
}
|
|
10691
|
+
|
|
10692
|
+
// src/components/custom/Workout/ExerciseCard.tsx
|
|
9590
10693
|
function getSupersetBorderRadius(position) {
|
|
9591
10694
|
switch (position) {
|
|
9592
10695
|
case "first":
|
|
@@ -9629,7 +10732,7 @@ function formatSummary(summary) {
|
|
|
9629
10732
|
if (!summary) return "";
|
|
9630
10733
|
return `${summary.sets}\xD7${summary.reps} @ ${roundWeight(summary.weight)} ${summary.unit}`;
|
|
9631
10734
|
}
|
|
9632
|
-
function
|
|
10735
|
+
function formatAccessibilityLabel(name, summary, prescription) {
|
|
9633
10736
|
if (summary) return `${name}, ${formatSummary(summary)}`;
|
|
9634
10737
|
if (prescription) return `${name}, ${prescription}`;
|
|
9635
10738
|
return name;
|
|
@@ -9638,7 +10741,6 @@ function CollapsedCard({
|
|
|
9638
10741
|
name,
|
|
9639
10742
|
onToggle,
|
|
9640
10743
|
summary,
|
|
9641
|
-
e1rm,
|
|
9642
10744
|
isPR,
|
|
9643
10745
|
setVelocities,
|
|
9644
10746
|
velocityZones,
|
|
@@ -9657,7 +10759,7 @@ function CollapsedCard({
|
|
|
9657
10759
|
onPressIn: () => setPressed(true),
|
|
9658
10760
|
onPressOut: () => setPressed(false),
|
|
9659
10761
|
accessibilityRole: "button",
|
|
9660
|
-
accessibilityLabel:
|
|
10762
|
+
accessibilityLabel: formatAccessibilityLabel(name, summary, void 0),
|
|
9661
10763
|
testID: "exercise-card",
|
|
9662
10764
|
children: /* @__PURE__ */ jsxs(
|
|
9663
10765
|
reactNative.View,
|
|
@@ -9671,220 +10773,131 @@ function CollapsedCard({
|
|
|
9671
10773
|
...supersetMargin
|
|
9672
10774
|
},
|
|
9673
10775
|
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
|
-
)
|
|
10776
|
+
/* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row items-center", testID: "exercise-card-header", children: [
|
|
10777
|
+
/* @__PURE__ */ jsx(
|
|
10778
|
+
reactNative.Text,
|
|
10779
|
+
{
|
|
10780
|
+
className: "text-text-primary",
|
|
10781
|
+
style: {
|
|
10782
|
+
fontSize: 14,
|
|
10783
|
+
fontFamily: '"Space Grotesk", sans-serif',
|
|
10784
|
+
fontWeight: "700"
|
|
10785
|
+
},
|
|
10786
|
+
testID: "exercise-card-name",
|
|
10787
|
+
children: name
|
|
10788
|
+
}
|
|
10789
|
+
),
|
|
10790
|
+
/* @__PURE__ */ jsx(reactNative.View, { className: "flex-1" }),
|
|
10791
|
+
summary && /* @__PURE__ */ jsx(
|
|
10792
|
+
reactNative.Text,
|
|
10793
|
+
{
|
|
10794
|
+
className: "text-text-secondary",
|
|
10795
|
+
style: {
|
|
10796
|
+
fontSize: 12,
|
|
10797
|
+
fontFamily: "Inter, sans-serif",
|
|
10798
|
+
marginRight: 8
|
|
10799
|
+
},
|
|
10800
|
+
testID: "exercise-card-summary",
|
|
10801
|
+
children: formatSummary(summary)
|
|
10802
|
+
}
|
|
10803
|
+
),
|
|
10804
|
+
isPR && /* @__PURE__ */ jsx(reactNative.View, { style: { marginLeft: 4 }, children: /* @__PURE__ */ jsx(PrBadge, { type: "e1rm", compact: true, animate: false }) })
|
|
10805
|
+
] }),
|
|
10806
|
+
(completedSets > 0 || remaining > 0) && /* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row", style: { marginTop: 6, gap: 4 }, testID: "exercise-card-strips", children: [
|
|
10807
|
+
setVelocities?.map((velocities, i) => /* @__PURE__ */ jsx(
|
|
10808
|
+
VelocityStrip,
|
|
10809
|
+
{
|
|
10810
|
+
velocities,
|
|
10811
|
+
zones: velocityZones,
|
|
10812
|
+
variant: "mini",
|
|
10813
|
+
testID: `exercise-card-velocity-strip-${i}`
|
|
10814
|
+
},
|
|
10815
|
+
i
|
|
10816
|
+
)),
|
|
10817
|
+
Array.from({ length: remaining }, (_, i) => /* @__PURE__ */ jsx(
|
|
10818
|
+
PlaceholderStrip,
|
|
10819
|
+
{
|
|
10820
|
+
testID: `exercise-card-placeholder-${i}`
|
|
10821
|
+
},
|
|
10822
|
+
`placeholder-${i}`
|
|
10823
|
+
))
|
|
10824
|
+
] })
|
|
9748
10825
|
]
|
|
9749
10826
|
}
|
|
9750
10827
|
)
|
|
9751
10828
|
}
|
|
9752
10829
|
);
|
|
9753
10830
|
}
|
|
10831
|
+
var DARK2 = getSemanticColors("dark");
|
|
10832
|
+
var BODY_DIVIDER = DARK2["border-subtle"];
|
|
10833
|
+
function deriveHeaderSetStates(sets) {
|
|
10834
|
+
return sets.map((set) => {
|
|
10835
|
+
switch (set.state) {
|
|
10836
|
+
case "live":
|
|
10837
|
+
return { status: "active", velocities: set.velocities, planned: set.target.reps };
|
|
10838
|
+
case "done":
|
|
10839
|
+
return { status: "done", velocities: set.velocities };
|
|
10840
|
+
case "todo":
|
|
10841
|
+
return { status: "todo", planned: set.planned ?? set.target.reps };
|
|
10842
|
+
}
|
|
10843
|
+
});
|
|
10844
|
+
}
|
|
9754
10845
|
function ExpandedCard({
|
|
9755
10846
|
name,
|
|
9756
10847
|
onToggle,
|
|
9757
10848
|
summary,
|
|
9758
|
-
e1rm,
|
|
9759
10849
|
isPR,
|
|
9760
10850
|
sets,
|
|
9761
10851
|
tempo,
|
|
10852
|
+
indicator,
|
|
10853
|
+
setStates,
|
|
10854
|
+
stripHeight = 8,
|
|
10855
|
+
velocityZones,
|
|
9762
10856
|
supersetPosition
|
|
9763
10857
|
}) {
|
|
9764
10858
|
const borderRadius = getSupersetBorderRadius(supersetPosition);
|
|
9765
10859
|
const supersetMargin = getSupersetMargin(supersetPosition);
|
|
9766
10860
|
const unit = summary?.unit ?? sets?.[0]?.unit ?? "lbs";
|
|
9767
|
-
const
|
|
10861
|
+
const headerStates = setStates ?? (sets ? deriveHeaderSetStates(sets) : []);
|
|
10862
|
+
const headerIndicator = indicator ?? (isPR ? "pr" : void 0);
|
|
9768
10863
|
return /* @__PURE__ */ jsxs(
|
|
9769
|
-
reactNative.View,
|
|
9770
|
-
{
|
|
9771
|
-
className: "bg-surface-elevated border-border",
|
|
9772
|
-
style: {
|
|
9773
|
-
borderWidth: 1,
|
|
9774
|
-
...borderRadius,
|
|
9775
|
-
...supersetMargin
|
|
9776
|
-
},
|
|
9777
|
-
testID: "exercise-card",
|
|
9778
|
-
children: [
|
|
9779
|
-
/* @__PURE__ */ jsx(
|
|
9780
|
-
|
|
9781
|
-
{
|
|
9782
|
-
|
|
9783
|
-
|
|
9784
|
-
|
|
9785
|
-
|
|
9786
|
-
|
|
9787
|
-
|
|
9788
|
-
|
|
9789
|
-
|
|
9790
|
-
|
|
9791
|
-
|
|
9792
|
-
|
|
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
|
-
)
|
|
10864
|
+
reactNative.View,
|
|
10865
|
+
{
|
|
10866
|
+
className: "bg-surface-elevated border-border",
|
|
10867
|
+
style: {
|
|
10868
|
+
borderWidth: 1,
|
|
10869
|
+
...borderRadius,
|
|
10870
|
+
...supersetMargin
|
|
10871
|
+
},
|
|
10872
|
+
testID: "exercise-card",
|
|
10873
|
+
children: [
|
|
10874
|
+
/* @__PURE__ */ jsx(
|
|
10875
|
+
ExerciseCardHeading,
|
|
10876
|
+
{
|
|
10877
|
+
name,
|
|
10878
|
+
sets: summary?.sets ?? sets?.length ?? 0,
|
|
10879
|
+
reps: summary?.reps ?? 0,
|
|
10880
|
+
load: summary?.weight ?? 0,
|
|
10881
|
+
unit,
|
|
10882
|
+
tempo,
|
|
10883
|
+
indicator: headerIndicator,
|
|
10884
|
+
setStates: headerStates,
|
|
10885
|
+
stripHeight,
|
|
10886
|
+
onPress: onToggle,
|
|
10887
|
+
testID: "exercise-card-heading"
|
|
9840
10888
|
}
|
|
9841
10889
|
),
|
|
9842
|
-
|
|
9843
|
-
/* @__PURE__ */ jsx(
|
|
10890
|
+
/* @__PURE__ */ jsxs(
|
|
9844
10891
|
reactNative.View,
|
|
9845
10892
|
{
|
|
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
|
-
})
|
|
10893
|
+
style: { borderTopWidth: 1, borderTopColor: BODY_DIVIDER, paddingBottom: 6 },
|
|
10894
|
+
testID: "exercise-card-body",
|
|
10895
|
+
children: [
|
|
10896
|
+
/* @__PURE__ */ jsx(SetTableHeader, { unit, showPrevious: false, testID: "exercise-card-column-headers" }),
|
|
10897
|
+
sets && /* @__PURE__ */ jsx(reactNative.View, { testID: "exercise-card-sets", children: sets.map((set, i) => /* @__PURE__ */ jsx(SetRow, { ...set, velocityZones: set.velocityZones ?? velocityZones }, i)) })
|
|
10898
|
+
]
|
|
9885
10899
|
}
|
|
9886
|
-
)
|
|
9887
|
-
sets && /* @__PURE__ */ jsx(reactNative.View, { testID: "exercise-card-sets", children: sets.map((setProps, i) => /* @__PURE__ */ jsx(SetRow, { ...setProps }, i)) })
|
|
10900
|
+
)
|
|
9888
10901
|
]
|
|
9889
10902
|
}
|
|
9890
10903
|
);
|
|
@@ -9893,11 +10906,12 @@ function UpcomingCard({
|
|
|
9893
10906
|
name,
|
|
9894
10907
|
prescription,
|
|
9895
10908
|
previousBest,
|
|
9896
|
-
supersetPosition
|
|
10909
|
+
supersetPosition,
|
|
10910
|
+
onToggle
|
|
9897
10911
|
}) {
|
|
9898
10912
|
const borderRadius = getSupersetBorderRadius(supersetPosition);
|
|
9899
10913
|
const supersetMargin = getSupersetMargin(supersetPosition);
|
|
9900
|
-
return /* @__PURE__ */ jsx(
|
|
10914
|
+
return /* @__PURE__ */ jsx(reactNative.Pressable, { onPress: onToggle, accessibilityRole: "button", children: /* @__PURE__ */ jsx(
|
|
9901
10915
|
reactNative.View,
|
|
9902
10916
|
{
|
|
9903
10917
|
style: {
|
|
@@ -9907,7 +10921,7 @@ function UpcomingCard({
|
|
|
9907
10921
|
...borderRadius,
|
|
9908
10922
|
...supersetMargin
|
|
9909
10923
|
},
|
|
9910
|
-
accessibilityLabel:
|
|
10924
|
+
accessibilityLabel: formatAccessibilityLabel(name, void 0, prescription),
|
|
9911
10925
|
testID: "exercise-card",
|
|
9912
10926
|
children: /* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row items-center", children: [
|
|
9913
10927
|
/* @__PURE__ */ jsx(
|
|
@@ -9915,6 +10929,7 @@ function UpcomingCard({
|
|
|
9915
10929
|
{
|
|
9916
10930
|
className: "text-text-primary",
|
|
9917
10931
|
style: {
|
|
10932
|
+
flexShrink: 0,
|
|
9918
10933
|
fontSize: 14,
|
|
9919
10934
|
fontFamily: '"Space Grotesk", sans-serif',
|
|
9920
10935
|
fontWeight: "700"
|
|
@@ -9927,7 +10942,9 @@ function UpcomingCard({
|
|
|
9927
10942
|
reactNative.Text,
|
|
9928
10943
|
{
|
|
9929
10944
|
className: "text-text-secondary",
|
|
10945
|
+
numberOfLines: 1,
|
|
9930
10946
|
style: {
|
|
10947
|
+
flexShrink: 1,
|
|
9931
10948
|
fontSize: 12,
|
|
9932
10949
|
fontFamily: "Inter, sans-serif",
|
|
9933
10950
|
marginLeft: 8
|
|
@@ -9936,12 +10953,14 @@ function UpcomingCard({
|
|
|
9936
10953
|
children: prescription
|
|
9937
10954
|
}
|
|
9938
10955
|
),
|
|
9939
|
-
/* @__PURE__ */ jsx(reactNative.View, { className: "flex-1" }),
|
|
10956
|
+
/* @__PURE__ */ jsx(reactNative.View, { className: "flex-1", style: { minWidth: 8 } }),
|
|
9940
10957
|
previousBest && /* @__PURE__ */ jsx(
|
|
9941
10958
|
reactNative.Text,
|
|
9942
10959
|
{
|
|
9943
10960
|
className: "text-text-tertiary",
|
|
10961
|
+
numberOfLines: 1,
|
|
9944
10962
|
style: {
|
|
10963
|
+
flexShrink: 1,
|
|
9945
10964
|
fontSize: 11,
|
|
9946
10965
|
fontFamily: "Inter, sans-serif"
|
|
9947
10966
|
},
|
|
@@ -9951,17 +10970,208 @@ function UpcomingCard({
|
|
|
9951
10970
|
)
|
|
9952
10971
|
] })
|
|
9953
10972
|
}
|
|
10973
|
+
) });
|
|
10974
|
+
}
|
|
10975
|
+
function ExerciseCard({
|
|
10976
|
+
upcoming,
|
|
10977
|
+
expanded,
|
|
10978
|
+
defaultExpanded,
|
|
10979
|
+
onExpandedChange,
|
|
10980
|
+
...rest
|
|
10981
|
+
}) {
|
|
10982
|
+
const [internalExpanded, setInternalExpanded] = React24.useState(defaultExpanded ?? false);
|
|
10983
|
+
const isControlled = expanded !== void 0;
|
|
10984
|
+
const isExpanded = isControlled ? expanded : internalExpanded;
|
|
10985
|
+
const onToggle = () => {
|
|
10986
|
+
const next = !isExpanded;
|
|
10987
|
+
onExpandedChange?.(next);
|
|
10988
|
+
if (!isControlled) setInternalExpanded(next);
|
|
10989
|
+
};
|
|
10990
|
+
if (upcoming) return /* @__PURE__ */ jsx(UpcomingCard, { ...rest, onToggle });
|
|
10991
|
+
return isExpanded ? /* @__PURE__ */ jsx(ExpandedCard, { ...rest, onToggle }) : /* @__PURE__ */ jsx(CollapsedCard, { ...rest, onToggle });
|
|
10992
|
+
}
|
|
10993
|
+
var t10 = getSemanticColors("dark");
|
|
10994
|
+
var RAISED = primitiveColors.charcoal[400];
|
|
10995
|
+
var BAR_HEIGHT = 9;
|
|
10996
|
+
var BAR_GAP = 3;
|
|
10997
|
+
function SessionHeader({
|
|
10998
|
+
title,
|
|
10999
|
+
plan,
|
|
11000
|
+
setsDone = 0,
|
|
11001
|
+
elapsedMs,
|
|
11002
|
+
budgetMs,
|
|
11003
|
+
running = true,
|
|
11004
|
+
metrics = [],
|
|
11005
|
+
next,
|
|
11006
|
+
className,
|
|
11007
|
+
style,
|
|
11008
|
+
...props
|
|
11009
|
+
}) {
|
|
11010
|
+
const upcoming = next != null;
|
|
11011
|
+
const totalSets = plan.reduce((sum, e) => sum + e.sets, 0);
|
|
11012
|
+
const hasPace = !upcoming && budgetMs != null && elapsedMs != null;
|
|
11013
|
+
const target = hasPace ? elapsedMs / budgetMs : void 0;
|
|
11014
|
+
const setsLabel = upcoming ? `${totalSets} sets` : `${Math.floor(setsDone)}/${totalSets} sets`;
|
|
11015
|
+
const setsLabelColor = upcoming ? t10["text-secondary"] : paceToneColor(paceTone(totalSets > 0 ? setsDone / totalSets : 0, target));
|
|
11016
|
+
return /* @__PURE__ */ jsxs(
|
|
11017
|
+
reactNative.View,
|
|
11018
|
+
{
|
|
11019
|
+
className,
|
|
11020
|
+
style: [
|
|
11021
|
+
{
|
|
11022
|
+
backgroundColor: RAISED,
|
|
11023
|
+
paddingTop: 11,
|
|
11024
|
+
paddingRight: 12,
|
|
11025
|
+
paddingBottom: 12,
|
|
11026
|
+
paddingLeft: 12,
|
|
11027
|
+
zIndex: 2
|
|
11028
|
+
},
|
|
11029
|
+
style
|
|
11030
|
+
],
|
|
11031
|
+
testID: "session-rail-header",
|
|
11032
|
+
...props,
|
|
11033
|
+
children: [
|
|
11034
|
+
/* @__PURE__ */ jsx(
|
|
11035
|
+
reactNative.Text,
|
|
11036
|
+
{
|
|
11037
|
+
accessibilityRole: "header",
|
|
11038
|
+
style: {
|
|
11039
|
+
fontSize: 16,
|
|
11040
|
+
lineHeight: 18,
|
|
11041
|
+
fontWeight: "700",
|
|
11042
|
+
fontFamily: '"Space Grotesk", sans-serif',
|
|
11043
|
+
color: t10["text-primary"],
|
|
11044
|
+
marginBottom: 10
|
|
11045
|
+
},
|
|
11046
|
+
testID: "session-rail-title",
|
|
11047
|
+
children: title
|
|
11048
|
+
}
|
|
11049
|
+
),
|
|
11050
|
+
upcoming ? /* @__PURE__ */ jsx(ScheduleTiles, { when: next }) : /* @__PURE__ */ jsx(MetricTiles, { metrics }),
|
|
11051
|
+
/* @__PURE__ */ jsxs(reactNative.View, { style: { marginTop: 10 }, children: [
|
|
11052
|
+
/* @__PURE__ */ jsx(
|
|
11053
|
+
SegmentedProgressBar,
|
|
11054
|
+
{
|
|
11055
|
+
segments: plan.map((e) => ({ weight: e.sets })),
|
|
11056
|
+
value: upcoming ? 0 : setsDone,
|
|
11057
|
+
target,
|
|
11058
|
+
height: BAR_HEIGHT,
|
|
11059
|
+
gap: BAR_GAP
|
|
11060
|
+
}
|
|
11061
|
+
),
|
|
11062
|
+
/* @__PURE__ */ jsxs(
|
|
11063
|
+
reactNative.View,
|
|
11064
|
+
{
|
|
11065
|
+
style: {
|
|
11066
|
+
flexDirection: "row",
|
|
11067
|
+
justifyContent: "space-between",
|
|
11068
|
+
alignItems: "baseline",
|
|
11069
|
+
marginTop: 5
|
|
11070
|
+
},
|
|
11071
|
+
children: [
|
|
11072
|
+
/* @__PURE__ */ jsx(
|
|
11073
|
+
Typography,
|
|
11074
|
+
{
|
|
11075
|
+
variant: "mono",
|
|
11076
|
+
style: { fontSize: 10, color: setsLabelColor },
|
|
11077
|
+
testID: "session-rail-sets",
|
|
11078
|
+
children: setsLabel
|
|
11079
|
+
}
|
|
11080
|
+
),
|
|
11081
|
+
/* @__PURE__ */ jsx(
|
|
11082
|
+
TimerReadout,
|
|
11083
|
+
{
|
|
11084
|
+
mode: "up",
|
|
11085
|
+
elapsedMs: upcoming ? budgetMs : elapsedMs,
|
|
11086
|
+
durationMs: budgetMs,
|
|
11087
|
+
running: running && !upcoming,
|
|
11088
|
+
showTotal: budgetMs != null && !upcoming
|
|
11089
|
+
}
|
|
11090
|
+
)
|
|
11091
|
+
]
|
|
11092
|
+
}
|
|
11093
|
+
)
|
|
11094
|
+
] })
|
|
11095
|
+
]
|
|
11096
|
+
}
|
|
9954
11097
|
);
|
|
9955
11098
|
}
|
|
9956
|
-
|
|
9957
|
-
|
|
9958
|
-
|
|
9959
|
-
|
|
9960
|
-
|
|
9961
|
-
|
|
9962
|
-
|
|
9963
|
-
|
|
9964
|
-
|
|
11099
|
+
var INSET = primitiveColors.charcoal[800];
|
|
11100
|
+
var DIVIDER = primitiveColors.charcoal[500];
|
|
11101
|
+
var DEFAULT_WIDTH = 246;
|
|
11102
|
+
function SessionRail({
|
|
11103
|
+
title,
|
|
11104
|
+
exercises,
|
|
11105
|
+
setsDone,
|
|
11106
|
+
elapsedMs,
|
|
11107
|
+
budgetMs,
|
|
11108
|
+
running,
|
|
11109
|
+
metrics,
|
|
11110
|
+
next,
|
|
11111
|
+
stripHeight = 8,
|
|
11112
|
+
width = DEFAULT_WIDTH,
|
|
11113
|
+
onExercisePress,
|
|
11114
|
+
className,
|
|
11115
|
+
style,
|
|
11116
|
+
...props
|
|
11117
|
+
}) {
|
|
11118
|
+
return /* @__PURE__ */ jsxs(
|
|
11119
|
+
reactNative.View,
|
|
11120
|
+
{
|
|
11121
|
+
className,
|
|
11122
|
+
style: [{ width, flexDirection: "column", backgroundColor: INSET }, style],
|
|
11123
|
+
testID: "session-rail",
|
|
11124
|
+
...props,
|
|
11125
|
+
children: [
|
|
11126
|
+
/* @__PURE__ */ jsx(
|
|
11127
|
+
SessionHeader,
|
|
11128
|
+
{
|
|
11129
|
+
title,
|
|
11130
|
+
plan: exercises.map((ex) => ({ name: ex.name, sets: ex.summary.sets })),
|
|
11131
|
+
setsDone,
|
|
11132
|
+
elapsedMs,
|
|
11133
|
+
budgetMs,
|
|
11134
|
+
running,
|
|
11135
|
+
metrics,
|
|
11136
|
+
next
|
|
11137
|
+
}
|
|
11138
|
+
),
|
|
11139
|
+
/* @__PURE__ */ jsx(
|
|
11140
|
+
reactNative.View,
|
|
11141
|
+
{
|
|
11142
|
+
style: [{ flex: 1, backgroundColor: INSET }, neumorphicShadows.charcoal.pressed.subtle],
|
|
11143
|
+
testID: "session-rail-list",
|
|
11144
|
+
children: exercises.map((ex, i) => /* @__PURE__ */ jsx(
|
|
11145
|
+
reactNative.View,
|
|
11146
|
+
{
|
|
11147
|
+
style: {
|
|
11148
|
+
borderBottomWidth: i < exercises.length - 1 ? 1 : 0,
|
|
11149
|
+
borderBottomColor: DIVIDER
|
|
11150
|
+
},
|
|
11151
|
+
children: /* @__PURE__ */ jsx(
|
|
11152
|
+
ExerciseCardHeading,
|
|
11153
|
+
{
|
|
11154
|
+
name: ex.name,
|
|
11155
|
+
sets: ex.summary.sets,
|
|
11156
|
+
reps: ex.summary.reps,
|
|
11157
|
+
load: ex.summary.weight,
|
|
11158
|
+
unit: ex.summary.unit,
|
|
11159
|
+
tempo: ex.tempo,
|
|
11160
|
+
indicator: ex.indicator,
|
|
11161
|
+
setStates: ex.setStates,
|
|
11162
|
+
stripHeight,
|
|
11163
|
+
dimmed: ex.upcoming,
|
|
11164
|
+
onPress: () => onExercisePress?.(ex, i)
|
|
11165
|
+
}
|
|
11166
|
+
)
|
|
11167
|
+
},
|
|
11168
|
+
ex.id ?? i
|
|
11169
|
+
))
|
|
11170
|
+
}
|
|
11171
|
+
)
|
|
11172
|
+
]
|
|
11173
|
+
}
|
|
11174
|
+
);
|
|
9965
11175
|
}
|
|
9966
11176
|
var DEFAULTS = {
|
|
9967
11177
|
label: "SS",
|
|
@@ -10225,10 +11435,10 @@ function WeekRow({
|
|
|
10225
11435
|
}
|
|
10226
11436
|
);
|
|
10227
11437
|
}
|
|
10228
|
-
var
|
|
11438
|
+
var t11 = getSemanticColors("dark");
|
|
10229
11439
|
var COLORS = {
|
|
10230
|
-
statusSuccess:
|
|
10231
|
-
brandPrimary:
|
|
11440
|
+
statusSuccess: t11["status-success"],
|
|
11441
|
+
brandPrimary: t11["brand-primary"],
|
|
10232
11442
|
borderDefault: "#1F1F1F",
|
|
10233
11443
|
brandPrimarySubtle: "rgba(255,121,0,0.06)"
|
|
10234
11444
|
};
|
|
@@ -10712,12 +11922,12 @@ function PrHistoryModal({
|
|
|
10712
11922
|
}
|
|
10713
11923
|
);
|
|
10714
11924
|
}
|
|
10715
|
-
var
|
|
10716
|
-
var BRAND_PRIMARY9 =
|
|
11925
|
+
var t12 = getSemanticColors("dark");
|
|
11926
|
+
var BRAND_PRIMARY9 = t12["brand-primary"];
|
|
10717
11927
|
var BRAND_PRIMARY_SUBTLE = "rgba(255,121,0,0.12)";
|
|
10718
|
-
var STATUS_SUCCESS =
|
|
10719
|
-
var STATUS_WARNING =
|
|
10720
|
-
var STATUS_ERROR2 =
|
|
11928
|
+
var STATUS_SUCCESS = t12["status-success"];
|
|
11929
|
+
var STATUS_WARNING = t12["status-warning"];
|
|
11930
|
+
var STATUS_ERROR2 = t12["status-error"];
|
|
10721
11931
|
var EMOJI_SETS = {
|
|
10722
11932
|
sleep: ["\u{1F634}", "\u{1F610}", "\u{1F642}", "\u{1F60A}", "\u{1F929}"],
|
|
10723
11933
|
soreness: ["\u{1F635}", "\u{1F623}", "\u{1F610}", "\u{1F642}", "\u{1F4AA}"],
|
|
@@ -10907,13 +12117,13 @@ function ReadinessCheck({
|
|
|
10907
12117
|
)
|
|
10908
12118
|
] }) });
|
|
10909
12119
|
}
|
|
10910
|
-
var
|
|
10911
|
-
var BRAND_PRIMARY10 =
|
|
12120
|
+
var t13 = getSemanticColors("dark");
|
|
12121
|
+
var BRAND_PRIMARY10 = t13["brand-primary"];
|
|
10912
12122
|
var BRAND_PRIMARY_DARK2 = MESO_ACCENT_GRADIENT_DARK;
|
|
10913
12123
|
var BRAND_PRIMARY_LIGHT2 = MESO_ACCENT_GRADIENT_LIGHT;
|
|
10914
|
-
var SUCCESS =
|
|
10915
|
-
var WARNING =
|
|
10916
|
-
var ERROR =
|
|
12124
|
+
var SUCCESS = t13["status-success"];
|
|
12125
|
+
var WARNING = t13["status-warning"];
|
|
12126
|
+
var ERROR = t13["status-error"];
|
|
10917
12127
|
var ACCENT_STOPS2 = [BRAND_PRIMARY_DARK2, BRAND_PRIMARY10, BRAND_PRIMARY_LIGHT2];
|
|
10918
12128
|
var CARD_GRADIENT = `linear-gradient(135deg, ${resolveColor("surface-elevated")} 0%, ${resolveColor("surface-raised")} 100%)`;
|
|
10919
12129
|
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 +12195,7 @@ function StatusPill({ badge }) {
|
|
|
10985
12195
|
}
|
|
10986
12196
|
);
|
|
10987
12197
|
}
|
|
10988
|
-
function
|
|
12198
|
+
function MetricCell2({ metric }) {
|
|
10989
12199
|
return /* @__PURE__ */ jsxs(reactNative.View, { style: { width: "48%" }, testID: "meso-status-card-metric", children: [
|
|
10990
12200
|
/* @__PURE__ */ jsx(
|
|
10991
12201
|
reactNative.Text,
|
|
@@ -11203,7 +12413,7 @@ function MesoStatusCard({
|
|
|
11203
12413
|
className: "flex-row flex-wrap",
|
|
11204
12414
|
style: { gap: 8 },
|
|
11205
12415
|
testID: "meso-status-card-metrics",
|
|
11206
|
-
children: metrics.map((metric, index) => /* @__PURE__ */ jsx(
|
|
12416
|
+
children: metrics.map((metric, index) => /* @__PURE__ */ jsx(MetricCell2, { metric }, `${metric.label}-${index}`))
|
|
11207
12417
|
}
|
|
11208
12418
|
),
|
|
11209
12419
|
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 +12494,8 @@ var ERROR_PILL_BORDER = "rgba(209,67,67,0.20)";
|
|
|
11284
12494
|
var PLOT_LEFT = 26;
|
|
11285
12495
|
var TOOLTIP_WIDTH = 124;
|
|
11286
12496
|
function timeOf(dateStr) {
|
|
11287
|
-
const
|
|
11288
|
-
return Number.isNaN(
|
|
12497
|
+
const t18 = Date.parse(dateStr);
|
|
12498
|
+
return Number.isNaN(t18) ? 0 : t18;
|
|
11289
12499
|
}
|
|
11290
12500
|
function formatValue(value) {
|
|
11291
12501
|
return `${Math.round(value)}`;
|
|
@@ -11774,10 +12984,10 @@ function StrengthTrendChart({
|
|
|
11774
12984
|
}
|
|
11775
12985
|
);
|
|
11776
12986
|
}
|
|
11777
|
-
var
|
|
11778
|
-
var STATUS_SUCCESS3 =
|
|
11779
|
-
var STATUS_WARNING3 =
|
|
11780
|
-
var STATUS_INFO =
|
|
12987
|
+
var t14 = getSemanticColors("dark");
|
|
12988
|
+
var STATUS_SUCCESS3 = t14["status-success"];
|
|
12989
|
+
var STATUS_WARNING3 = t14["status-warning"];
|
|
12990
|
+
var STATUS_INFO = t14["status-info"];
|
|
11781
12991
|
var DOT_BORDER = "#F3F4F6";
|
|
11782
12992
|
var BAND_FILL = "rgba(46,213,115,0.1)";
|
|
11783
12993
|
var BAND_EDGE = "rgba(46,213,115,0.45)";
|
|
@@ -11820,13 +13030,13 @@ function interpEdge(pixels, x, key) {
|
|
|
11820
13030
|
const a = pixels[i];
|
|
11821
13031
|
const b = pixels[i + 1];
|
|
11822
13032
|
if (x >= a.x && x <= b.x) {
|
|
11823
|
-
const
|
|
11824
|
-
return a[key] +
|
|
13033
|
+
const t18 = b.x === a.x ? 0 : (x - a.x) / (b.x - a.x);
|
|
13034
|
+
return a[key] + t18 * (b[key] - a[key]);
|
|
11825
13035
|
}
|
|
11826
13036
|
}
|
|
11827
13037
|
return pixels[pixels.length - 1][key];
|
|
11828
13038
|
}
|
|
11829
|
-
function
|
|
13039
|
+
function buildColumns2(pixels, step) {
|
|
11830
13040
|
if (pixels.length < 2) return [];
|
|
11831
13041
|
const start = pixels[0].x;
|
|
11832
13042
|
const end = pixels[pixels.length - 1].x;
|
|
@@ -11938,7 +13148,7 @@ function CapacityBandChart({
|
|
|
11938
13148
|
}
|
|
11939
13149
|
const { toX, toY } = scale;
|
|
11940
13150
|
const bandPixels = toPixels(band, toX, toY);
|
|
11941
|
-
const columns =
|
|
13151
|
+
const columns = buildColumns2(bandPixels, COLUMN_STEP);
|
|
11942
13152
|
const topEdge = buildEdges(bandPixels, "yHigh");
|
|
11943
13153
|
const bottomEdge = buildEdges(bandPixels, "yLow");
|
|
11944
13154
|
const lastBandPoint = band[band.length - 1];
|
|
@@ -12013,8 +13223,8 @@ function CapacityBandChart({
|
|
|
12013
13223
|
testID: "capacity-band-chart-reveal",
|
|
12014
13224
|
children: /* @__PURE__ */ jsxs(reactNative.View, { style: { width, height, position: "relative" }, children: [
|
|
12015
13225
|
projection && /* @__PURE__ */ jsxs(reactNative.View, { testID: "capacity-band-chart-projection", children: [
|
|
12016
|
-
renderColumns(
|
|
12017
|
-
renderColumns(
|
|
13226
|
+
renderColumns(buildColumns2(trainingPixels, COLUMN_STEP), PROJECTION_FILL, "capacity-band-chart-projection-fill"),
|
|
13227
|
+
renderColumns(buildColumns2(restPixels, COLUMN_STEP), PROJECTION_FILL, "capacity-band-chart-projection-fill"),
|
|
12018
13228
|
renderEdges(buildEdges(trainingPixels, "yHigh"), STATUS_SUCCESS3, true, "capacity-band-chart-projection-training"),
|
|
12019
13229
|
renderEdges(buildEdges(trainingPixels, "yLow"), STATUS_SUCCESS3, true, "capacity-band-chart-projection-training"),
|
|
12020
13230
|
renderEdges(buildEdges(restPixels, "yHigh"), STATUS_INFO, true, "capacity-band-chart-projection-rest"),
|
|
@@ -12081,72 +13291,585 @@ function CapacityBandChart({
|
|
|
12081
13291
|
return /* @__PURE__ */ jsx(
|
|
12082
13292
|
reactNative.Text,
|
|
12083
13293
|
{
|
|
12084
|
-
className: "text-text-tertiary",
|
|
13294
|
+
className: "text-text-tertiary",
|
|
13295
|
+
style: {
|
|
13296
|
+
position: "absolute",
|
|
13297
|
+
left: toX(point.date) - 14,
|
|
13298
|
+
top: height - PADDING_BOTTOM + 2,
|
|
13299
|
+
width: 28,
|
|
13300
|
+
textAlign: "center",
|
|
13301
|
+
fontSize: 10,
|
|
13302
|
+
fontFamily: "Inter, sans-serif"
|
|
13303
|
+
},
|
|
13304
|
+
accessibilityElementsHidden: true,
|
|
13305
|
+
importantForAccessibility: "no-hide-descendants",
|
|
13306
|
+
testID: "capacity-band-chart-x-label",
|
|
13307
|
+
children: formatDate2(point.date)
|
|
13308
|
+
},
|
|
13309
|
+
`x-${point.date}`
|
|
13310
|
+
);
|
|
13311
|
+
})
|
|
13312
|
+
]
|
|
13313
|
+
}
|
|
13314
|
+
),
|
|
13315
|
+
workouts.map((workout, i) => {
|
|
13316
|
+
const cx = toX(workout.date);
|
|
13317
|
+
const cy = toY(workout.load);
|
|
13318
|
+
const color = DOT_COLORS[workout.status];
|
|
13319
|
+
const dotStyle = {
|
|
13320
|
+
width: DOT_SIZE,
|
|
13321
|
+
height: DOT_SIZE,
|
|
13322
|
+
borderRadius: DOT_SIZE / 2,
|
|
13323
|
+
backgroundColor: color,
|
|
13324
|
+
borderWidth: 2,
|
|
13325
|
+
borderColor: DOT_BORDER
|
|
13326
|
+
};
|
|
13327
|
+
const label = `Workout on ${formatDate2(workout.date)}, load ${workout.load}, ${STATUS_PHRASES[workout.status]}`;
|
|
13328
|
+
return /* @__PURE__ */ jsx(
|
|
13329
|
+
reactNative.Animated.View,
|
|
13330
|
+
{
|
|
13331
|
+
style: {
|
|
13332
|
+
position: "absolute",
|
|
13333
|
+
left: cx - DOT_SIZE / 2,
|
|
13334
|
+
top: cy - DOT_SIZE / 2,
|
|
13335
|
+
transform: [{ scale: dotAnims[i] ?? 1 }]
|
|
13336
|
+
},
|
|
13337
|
+
testID: "capacity-band-chart-dot-wrapper",
|
|
13338
|
+
children: onWorkoutPress ? /* @__PURE__ */ jsx(
|
|
13339
|
+
reactNative.Pressable,
|
|
13340
|
+
{
|
|
13341
|
+
onPress: () => onWorkoutPress(workout),
|
|
13342
|
+
accessibilityRole: "button",
|
|
13343
|
+
accessibilityLabel: label,
|
|
13344
|
+
style: dotStyle,
|
|
13345
|
+
testID: "capacity-band-chart-dot"
|
|
13346
|
+
}
|
|
13347
|
+
) : /* @__PURE__ */ jsx(
|
|
13348
|
+
reactNative.View,
|
|
13349
|
+
{
|
|
13350
|
+
accessibilityElementsHidden: true,
|
|
13351
|
+
importantForAccessibility: "no-hide-descendants",
|
|
13352
|
+
style: dotStyle,
|
|
13353
|
+
testID: "capacity-band-chart-dot"
|
|
13354
|
+
}
|
|
13355
|
+
)
|
|
13356
|
+
},
|
|
13357
|
+
`dot-${workout.date}-${i}`
|
|
13358
|
+
);
|
|
13359
|
+
})
|
|
13360
|
+
]
|
|
13361
|
+
}
|
|
13362
|
+
);
|
|
13363
|
+
}
|
|
13364
|
+
var t15 = getSemanticColors("dark");
|
|
13365
|
+
function glowShadow(color) {
|
|
13366
|
+
return `0 0 5px 1px ${alpha(color, 0.3)}, 0 0 10px 3px ${alpha(color, 0.12)}`;
|
|
13367
|
+
}
|
|
13368
|
+
var DEFAULT_TRACK_COLOR = primitiveColors.charcoal[200];
|
|
13369
|
+
var DEFAULT_MARKER_COLOR2 = primitiveColors.white;
|
|
13370
|
+
var TICK_COLOR = t15["text-tertiary"];
|
|
13371
|
+
var ZONE_SIZES = {
|
|
13372
|
+
default: {
|
|
13373
|
+
trackHeight: 14,
|
|
13374
|
+
needleOverhang: 6,
|
|
13375
|
+
needleWidth: 4,
|
|
13376
|
+
tickLineNormal: 1.5,
|
|
13377
|
+
tickLineEmphasized: 2,
|
|
13378
|
+
tickLineHeightPad: 4,
|
|
13379
|
+
tickLineTopPad: 2,
|
|
13380
|
+
labelRowHeight: 16,
|
|
13381
|
+
labelMarginTop: 5,
|
|
13382
|
+
tickFont: 9,
|
|
13383
|
+
tickLetterSpacing: 0.5,
|
|
13384
|
+
tickCellWidth: 28
|
|
13385
|
+
},
|
|
13386
|
+
wall: {
|
|
13387
|
+
trackHeight: 24,
|
|
13388
|
+
needleOverhang: 9,
|
|
13389
|
+
needleWidth: 7,
|
|
13390
|
+
tickLineNormal: 2.5,
|
|
13391
|
+
tickLineEmphasized: 3.5,
|
|
13392
|
+
tickLineHeightPad: 6,
|
|
13393
|
+
tickLineTopPad: 3,
|
|
13394
|
+
labelRowHeight: 24,
|
|
13395
|
+
labelMarginTop: 8,
|
|
13396
|
+
tickFont: 14,
|
|
13397
|
+
tickLetterSpacing: 0.5,
|
|
13398
|
+
tickCellWidth: 48
|
|
13399
|
+
}
|
|
13400
|
+
};
|
|
13401
|
+
function fraction(value, min, max) {
|
|
13402
|
+
if (max <= min) return 0;
|
|
13403
|
+
return Math.max(0, Math.min(1, (value - min) / (max - min)));
|
|
13404
|
+
}
|
|
13405
|
+
function pct(frac) {
|
|
13406
|
+
return `${frac * 100}%`;
|
|
13407
|
+
}
|
|
13408
|
+
function ZoneTrack({
|
|
13409
|
+
zones,
|
|
13410
|
+
max,
|
|
13411
|
+
min = 0,
|
|
13412
|
+
marker = null,
|
|
13413
|
+
ticks,
|
|
13414
|
+
band = null,
|
|
13415
|
+
size = "default",
|
|
13416
|
+
trackHeight: trackHeightProp,
|
|
13417
|
+
needleOverhang: needleOverhangProp,
|
|
13418
|
+
trackColor = DEFAULT_TRACK_COLOR,
|
|
13419
|
+
className,
|
|
13420
|
+
style,
|
|
13421
|
+
accessibilityLabel: accessibilityLabel2,
|
|
13422
|
+
...props
|
|
13423
|
+
}) {
|
|
13424
|
+
const s = ZONE_SIZES[size];
|
|
13425
|
+
const trackHeight = trackHeightProp ?? s.trackHeight;
|
|
13426
|
+
const needleOverhang = needleOverhangProp ?? s.needleOverhang;
|
|
13427
|
+
const isNeedle = marker?.type === "needle";
|
|
13428
|
+
const markerHeight = isNeedle ? trackHeight + needleOverhang * 2 : trackHeight;
|
|
13429
|
+
const markerFrac = marker != null ? fraction(marker.value, min, max) : 0;
|
|
13430
|
+
const bands = zones.map((zone, i) => {
|
|
13431
|
+
const prev = i === 0 ? min : zones[i - 1].upTo;
|
|
13432
|
+
const start = Math.max(min, Math.min(max, prev));
|
|
13433
|
+
const end = Math.max(min, Math.min(max, zone.upTo));
|
|
13434
|
+
return { color: zone.color, weight: Math.max(0, end - start) };
|
|
13435
|
+
});
|
|
13436
|
+
return /* @__PURE__ */ jsxs(
|
|
13437
|
+
reactNative.View,
|
|
13438
|
+
{
|
|
13439
|
+
className,
|
|
13440
|
+
style: [{ width: "100%" }, style],
|
|
13441
|
+
accessibilityRole: "progressbar",
|
|
13442
|
+
accessibilityValue: marker != null ? { min, max, now: marker.value } : { min, max, now: min },
|
|
13443
|
+
accessibilityLabel: accessibilityLabel2 ?? `Zone track: ${marker?.value ?? min}`,
|
|
13444
|
+
testID: "zone-track",
|
|
13445
|
+
...props,
|
|
13446
|
+
children: [
|
|
13447
|
+
/* @__PURE__ */ jsxs(reactNative.View, { style: { position: "relative", height: markerHeight }, children: [
|
|
13448
|
+
/* @__PURE__ */ jsxs(
|
|
13449
|
+
reactNative.View,
|
|
13450
|
+
{
|
|
13451
|
+
testID: "zone-track-track",
|
|
13452
|
+
accessibilityElementsHidden: true,
|
|
13453
|
+
style: {
|
|
13454
|
+
position: "absolute",
|
|
13455
|
+
top: (markerHeight - trackHeight) / 2,
|
|
13456
|
+
left: 0,
|
|
13457
|
+
right: 0,
|
|
13458
|
+
height: trackHeight,
|
|
13459
|
+
borderRadius: trackHeight / 2,
|
|
13460
|
+
backgroundColor: trackColor,
|
|
13461
|
+
overflow: "hidden",
|
|
13462
|
+
flexDirection: "row",
|
|
13463
|
+
// The pill's own box-shadow renders outside its overflow:hidden, so
|
|
13464
|
+
// `glow` haloes the whole track in the marker colour.
|
|
13465
|
+
...marker?.glow ? { boxShadow: glowShadow(marker.color ?? DEFAULT_MARKER_COLOR2) } : null
|
|
13466
|
+
},
|
|
13467
|
+
children: [
|
|
13468
|
+
bands.map((b, i) => /* @__PURE__ */ jsx(
|
|
13469
|
+
reactNative.View,
|
|
13470
|
+
{
|
|
13471
|
+
testID: "zone-track-band",
|
|
13472
|
+
style: {
|
|
13473
|
+
flexGrow: b.weight,
|
|
13474
|
+
flexShrink: 1,
|
|
13475
|
+
flexBasis: 0,
|
|
13476
|
+
minWidth: 0,
|
|
13477
|
+
height: "100%",
|
|
13478
|
+
backgroundColor: b.color
|
|
13479
|
+
}
|
|
13480
|
+
},
|
|
13481
|
+
i
|
|
13482
|
+
)),
|
|
13483
|
+
band != null && /* @__PURE__ */ jsx(
|
|
13484
|
+
reactNative.View,
|
|
13485
|
+
{
|
|
13486
|
+
testID: "zone-track-band-highlight",
|
|
13487
|
+
style: {
|
|
13488
|
+
position: "absolute",
|
|
13489
|
+
top: 0,
|
|
13490
|
+
bottom: 0,
|
|
13491
|
+
left: pct(fraction(band.from, min, max)),
|
|
13492
|
+
width: pct(fraction(band.to, min, max) - fraction(band.from, min, max)),
|
|
13493
|
+
backgroundColor: band.color
|
|
13494
|
+
}
|
|
13495
|
+
}
|
|
13496
|
+
),
|
|
13497
|
+
marker?.type === "fill" && /* @__PURE__ */ jsx(
|
|
13498
|
+
reactNative.View,
|
|
13499
|
+
{
|
|
13500
|
+
testID: "zone-track-unfilled",
|
|
13501
|
+
style: {
|
|
13502
|
+
position: "absolute",
|
|
13503
|
+
top: 0,
|
|
13504
|
+
bottom: 0,
|
|
13505
|
+
left: pct(markerFrac),
|
|
13506
|
+
right: 0,
|
|
13507
|
+
backgroundColor: trackColor
|
|
13508
|
+
}
|
|
13509
|
+
}
|
|
13510
|
+
),
|
|
13511
|
+
marker?.type === "fill" && marker.color != null && /* @__PURE__ */ jsx(
|
|
13512
|
+
reactNative.View,
|
|
13513
|
+
{
|
|
13514
|
+
testID: "zone-track-fill",
|
|
12085
13515
|
style: {
|
|
12086
13516
|
position: "absolute",
|
|
12087
|
-
|
|
12088
|
-
|
|
12089
|
-
|
|
12090
|
-
|
|
12091
|
-
|
|
12092
|
-
|
|
12093
|
-
|
|
12094
|
-
|
|
12095
|
-
|
|
12096
|
-
|
|
12097
|
-
|
|
12098
|
-
|
|
12099
|
-
|
|
12100
|
-
);
|
|
12101
|
-
})
|
|
12102
|
-
]
|
|
12103
|
-
}
|
|
12104
|
-
),
|
|
12105
|
-
workouts.map((workout, i) => {
|
|
12106
|
-
const cx = toX(workout.date);
|
|
12107
|
-
const cy = toY(workout.load);
|
|
12108
|
-
const color = DOT_COLORS[workout.status];
|
|
12109
|
-
const dotStyle = {
|
|
12110
|
-
width: DOT_SIZE,
|
|
12111
|
-
height: DOT_SIZE,
|
|
12112
|
-
borderRadius: DOT_SIZE / 2,
|
|
12113
|
-
backgroundColor: color,
|
|
12114
|
-
borderWidth: 2,
|
|
12115
|
-
borderColor: DOT_BORDER
|
|
12116
|
-
};
|
|
12117
|
-
const label = `Workout on ${formatDate2(workout.date)}, load ${workout.load}, ${STATUS_PHRASES[workout.status]}`;
|
|
12118
|
-
return /* @__PURE__ */ jsx(
|
|
12119
|
-
reactNative.Animated.View,
|
|
13517
|
+
top: 0,
|
|
13518
|
+
bottom: 0,
|
|
13519
|
+
left: 0,
|
|
13520
|
+
width: pct(markerFrac),
|
|
13521
|
+
backgroundColor: marker.color
|
|
13522
|
+
}
|
|
13523
|
+
}
|
|
13524
|
+
)
|
|
13525
|
+
]
|
|
13526
|
+
}
|
|
13527
|
+
),
|
|
13528
|
+
isNeedle && /* @__PURE__ */ jsx(
|
|
13529
|
+
reactNative.View,
|
|
12120
13530
|
{
|
|
13531
|
+
testID: "zone-track-needle",
|
|
12121
13532
|
style: {
|
|
12122
13533
|
position: "absolute",
|
|
12123
|
-
|
|
12124
|
-
|
|
12125
|
-
|
|
13534
|
+
top: 0,
|
|
13535
|
+
left: pct(markerFrac),
|
|
13536
|
+
width: s.needleWidth,
|
|
13537
|
+
height: markerHeight,
|
|
13538
|
+
borderRadius: s.needleWidth / 2,
|
|
13539
|
+
backgroundColor: marker.color ?? DEFAULT_MARKER_COLOR2,
|
|
13540
|
+
transform: [{ translateX: -s.needleWidth / 2 }]
|
|
13541
|
+
}
|
|
13542
|
+
}
|
|
13543
|
+
),
|
|
13544
|
+
ticks?.map((tick, i) => {
|
|
13545
|
+
const emphasized = tick.emphasized === true;
|
|
13546
|
+
const lineColor = tick.color ?? (emphasized ? t15["brand-primary"] : TICK_COLOR);
|
|
13547
|
+
const lineWidth = emphasized ? s.tickLineEmphasized : s.tickLineNormal;
|
|
13548
|
+
return /* @__PURE__ */ jsx(
|
|
13549
|
+
reactNative.View,
|
|
13550
|
+
{
|
|
13551
|
+
testID: "zone-track-tick-line",
|
|
13552
|
+
accessibilityElementsHidden: true,
|
|
13553
|
+
style: {
|
|
13554
|
+
position: "absolute",
|
|
13555
|
+
top: (markerHeight - trackHeight) / 2 - s.tickLineTopPad,
|
|
13556
|
+
left: pct(fraction(tick.value, min, max)),
|
|
13557
|
+
width: lineWidth,
|
|
13558
|
+
height: trackHeight + s.tickLineHeightPad,
|
|
13559
|
+
borderRadius: lineWidth / 2,
|
|
13560
|
+
backgroundColor: lineColor,
|
|
13561
|
+
transform: [{ translateX: -lineWidth / 2 }],
|
|
13562
|
+
zIndex: 3
|
|
13563
|
+
}
|
|
12126
13564
|
},
|
|
12127
|
-
|
|
12128
|
-
|
|
12129
|
-
|
|
13565
|
+
`line-${i}`
|
|
13566
|
+
);
|
|
13567
|
+
})
|
|
13568
|
+
] }),
|
|
13569
|
+
ticks != null && ticks.length > 0 && /* @__PURE__ */ jsx(
|
|
13570
|
+
reactNative.View,
|
|
13571
|
+
{
|
|
13572
|
+
style: { position: "relative", height: s.labelRowHeight, marginTop: s.labelMarginTop },
|
|
13573
|
+
children: ticks.map((tick, i) => {
|
|
13574
|
+
if (tick.label == null) return null;
|
|
13575
|
+
const emphasized = tick.emphasized === true;
|
|
13576
|
+
const labelColor = tick.color ?? (emphasized ? t15["brand-primary"] : TICK_COLOR);
|
|
13577
|
+
const labelNode = /* @__PURE__ */ jsx(
|
|
13578
|
+
reactNative.Text,
|
|
12130
13579
|
{
|
|
12131
|
-
|
|
12132
|
-
|
|
12133
|
-
|
|
12134
|
-
|
|
12135
|
-
|
|
13580
|
+
testID: "zone-track-tick-label",
|
|
13581
|
+
style: {
|
|
13582
|
+
fontSize: s.tickFont,
|
|
13583
|
+
letterSpacing: s.tickLetterSpacing,
|
|
13584
|
+
color: labelColor,
|
|
13585
|
+
fontFamily: "monospace",
|
|
13586
|
+
fontWeight: emphasized ? "700" : "400"
|
|
13587
|
+
},
|
|
13588
|
+
children: tick.label
|
|
12136
13589
|
}
|
|
12137
|
-
)
|
|
13590
|
+
);
|
|
13591
|
+
return /* @__PURE__ */ jsx(
|
|
12138
13592
|
reactNative.View,
|
|
12139
13593
|
{
|
|
12140
|
-
|
|
12141
|
-
|
|
12142
|
-
|
|
12143
|
-
|
|
12144
|
-
|
|
12145
|
-
|
|
13594
|
+
testID: "zone-track-tick",
|
|
13595
|
+
style: {
|
|
13596
|
+
position: "absolute",
|
|
13597
|
+
left: pct(fraction(tick.value, min, max)),
|
|
13598
|
+
transform: [{ translateX: -s.tickCellWidth / 2 }],
|
|
13599
|
+
width: s.tickCellWidth,
|
|
13600
|
+
alignItems: "center"
|
|
13601
|
+
},
|
|
13602
|
+
children: tick.tooltip != null ? /* @__PURE__ */ jsx(Tooltip, { label: tick.tooltip, placement: "top", children: labelNode }) : labelNode
|
|
13603
|
+
},
|
|
13604
|
+
`label-${i}`
|
|
13605
|
+
);
|
|
13606
|
+
})
|
|
13607
|
+
}
|
|
13608
|
+
)
|
|
13609
|
+
]
|
|
13610
|
+
}
|
|
13611
|
+
);
|
|
13612
|
+
}
|
|
13613
|
+
|
|
13614
|
+
// src/components/custom/Workout/FatigueMeter.tsx
|
|
13615
|
+
var { green, yellow, orange, red } = WORKOUT_TOKENS.scale;
|
|
13616
|
+
var DEFAULT_ZONE_COLORS = [green, yellow, orange, red];
|
|
13617
|
+
var DEFAULT_THRESHOLDS = [10, 20, 30];
|
|
13618
|
+
var DEFAULT_LABELS = ["fresh", "VL10", "VL20", "VL30", "stop"];
|
|
13619
|
+
var DEFAULT_MAX = 40;
|
|
13620
|
+
function FatigueMeter({
|
|
13621
|
+
value,
|
|
13622
|
+
max = DEFAULT_MAX,
|
|
13623
|
+
thresholds = DEFAULT_THRESHOLDS,
|
|
13624
|
+
zoneColors = DEFAULT_ZONE_COLORS,
|
|
13625
|
+
labels = DEFAULT_LABELS,
|
|
13626
|
+
size = "default",
|
|
13627
|
+
trackHeight,
|
|
13628
|
+
needleColor,
|
|
13629
|
+
className,
|
|
13630
|
+
...props
|
|
13631
|
+
}) {
|
|
13632
|
+
const [t1, t22, t32] = thresholds;
|
|
13633
|
+
const zones = [
|
|
13634
|
+
{ upTo: t1, color: zoneColors[0] },
|
|
13635
|
+
{ upTo: t22, color: zoneColors[1] },
|
|
13636
|
+
{ upTo: t32, color: zoneColors[2] },
|
|
13637
|
+
{ upTo: max, color: zoneColors[3] }
|
|
13638
|
+
];
|
|
13639
|
+
const tickValues = [0, t1, t22, t32, max];
|
|
13640
|
+
const ticks = tickValues.map((v, i) => ({ value: v, label: labels[i] }));
|
|
13641
|
+
return /* @__PURE__ */ jsx(
|
|
13642
|
+
ZoneTrack,
|
|
13643
|
+
{
|
|
13644
|
+
className,
|
|
13645
|
+
zones,
|
|
13646
|
+
min: 0,
|
|
13647
|
+
max,
|
|
13648
|
+
size,
|
|
13649
|
+
trackHeight,
|
|
13650
|
+
ticks,
|
|
13651
|
+
marker: { type: "needle", value, color: needleColor },
|
|
13652
|
+
accessibilityLabel: `Fatigue: ${value}% velocity loss`,
|
|
13653
|
+
testID: "fatigue-meter",
|
|
13654
|
+
...props
|
|
13655
|
+
}
|
|
13656
|
+
);
|
|
13657
|
+
}
|
|
13658
|
+
var HEAT = WORKOUT_TOKENS.heatmap;
|
|
13659
|
+
var NEUTRAL_TRACK = primitiveColors.charcoal[200];
|
|
13660
|
+
var MONO = "monospace";
|
|
13661
|
+
var HEAT_BY_ZONE = {
|
|
13662
|
+
under: HEAT.under,
|
|
13663
|
+
maintenance: HEAT.maintenance,
|
|
13664
|
+
productive: HEAT.productive,
|
|
13665
|
+
approaching: HEAT.approaching,
|
|
13666
|
+
over: HEAT.over
|
|
13667
|
+
};
|
|
13668
|
+
var ZONE_DESCRIPTION = {
|
|
13669
|
+
under: "below MEV",
|
|
13670
|
+
maintenance: "building toward MAV",
|
|
13671
|
+
productive: "in the productive zone",
|
|
13672
|
+
approaching: "approaching MRV",
|
|
13673
|
+
over: "over MRV"
|
|
13674
|
+
};
|
|
13675
|
+
var LANDMARK_NAME = {
|
|
13676
|
+
MEV: "Minimum Effective Volume",
|
|
13677
|
+
MAV: "Maximum Adaptive Volume",
|
|
13678
|
+
MRV: "Maximum Recoverable Volume"
|
|
13679
|
+
};
|
|
13680
|
+
function zoneForSets(sets, { mev, mav, mrv }) {
|
|
13681
|
+
if (sets < mev) return "under";
|
|
13682
|
+
if (sets < mav) return "maintenance";
|
|
13683
|
+
if (sets >= mrv) return "over";
|
|
13684
|
+
const midpoint = (mav + mrv) / 2;
|
|
13685
|
+
return sets < midpoint ? "productive" : "approaching";
|
|
13686
|
+
}
|
|
13687
|
+
function VolumeLandmarkBar({
|
|
13688
|
+
muscle,
|
|
13689
|
+
currentSets,
|
|
13690
|
+
landmarks,
|
|
13691
|
+
width = 220,
|
|
13692
|
+
trackHeight = 10,
|
|
13693
|
+
scaleMax,
|
|
13694
|
+
className,
|
|
13695
|
+
style,
|
|
13696
|
+
...props
|
|
13697
|
+
}) {
|
|
13698
|
+
const { mev, mav, mrv } = landmarks;
|
|
13699
|
+
const max = scaleMax ?? mrv * 1.2;
|
|
13700
|
+
const zone = zoneForSets(currentSets, landmarks);
|
|
13701
|
+
const fillColor = HEAT_BY_ZONE[zone];
|
|
13702
|
+
const pct2 = mav > 0 ? Math.round(currentSets / mav * 100) : 0;
|
|
13703
|
+
return /* @__PURE__ */ jsxs(
|
|
13704
|
+
reactNative.View,
|
|
13705
|
+
{
|
|
13706
|
+
className,
|
|
13707
|
+
style: [{ width, gap: 3 }, style],
|
|
13708
|
+
testID: "volume-landmark-bar",
|
|
13709
|
+
...props,
|
|
13710
|
+
children: [
|
|
13711
|
+
/* @__PURE__ */ jsx(
|
|
13712
|
+
DataRow,
|
|
13713
|
+
{
|
|
13714
|
+
label: muscle,
|
|
13715
|
+
value: /* @__PURE__ */ jsxs(
|
|
13716
|
+
reactNative.Text,
|
|
13717
|
+
{
|
|
13718
|
+
testID: "volume-landmark-pct",
|
|
13719
|
+
style: { fontSize: 14, fontWeight: "700", fontFamily: MONO, color: fillColor },
|
|
13720
|
+
children: [
|
|
13721
|
+
pct2,
|
|
13722
|
+
"%"
|
|
13723
|
+
]
|
|
13724
|
+
}
|
|
13725
|
+
),
|
|
13726
|
+
className: "py-0",
|
|
13727
|
+
testID: "volume-landmark-header"
|
|
13728
|
+
}
|
|
13729
|
+
),
|
|
13730
|
+
/* @__PURE__ */ jsx(
|
|
13731
|
+
ZoneTrack,
|
|
13732
|
+
{
|
|
13733
|
+
zones: [{ upTo: max, color: NEUTRAL_TRACK }],
|
|
13734
|
+
max,
|
|
13735
|
+
marker: {
|
|
13736
|
+
type: "fill",
|
|
13737
|
+
value: currentSets,
|
|
13738
|
+
color: fillColor,
|
|
13739
|
+
// Glow when in the optimal productive band — the "sweet spot" cue.
|
|
13740
|
+
glow: zone === "productive"
|
|
12146
13741
|
},
|
|
12147
|
-
|
|
12148
|
-
|
|
12149
|
-
|
|
13742
|
+
trackColor: NEUTRAL_TRACK,
|
|
13743
|
+
trackHeight,
|
|
13744
|
+
ticks: [
|
|
13745
|
+
{ value: mev, label: "MEV", tooltip: `${LANDMARK_NAME.MEV} \xB7 ${mev} sets/wk` },
|
|
13746
|
+
{
|
|
13747
|
+
value: mav,
|
|
13748
|
+
label: "MAV",
|
|
13749
|
+
emphasized: true,
|
|
13750
|
+
tooltip: `${LANDMARK_NAME.MAV} (target) \xB7 ${mav} sets/wk`
|
|
13751
|
+
},
|
|
13752
|
+
{ value: mrv, label: "MRV", tooltip: `${LANDMARK_NAME.MRV} \xB7 ${mrv} sets/wk` }
|
|
13753
|
+
],
|
|
13754
|
+
accessibilityLabel: `${muscle} weekly volume: ${currentSets} sets, ${pct2}% of MAV target, ${ZONE_DESCRIPTION[zone]}`,
|
|
13755
|
+
testID: "volume-landmark-track"
|
|
13756
|
+
}
|
|
13757
|
+
)
|
|
13758
|
+
]
|
|
13759
|
+
}
|
|
13760
|
+
);
|
|
13761
|
+
}
|
|
13762
|
+
var t16 = getSemanticColors("dark");
|
|
13763
|
+
var statusToken = {
|
|
13764
|
+
productive: "status-success",
|
|
13765
|
+
threshold: "status-warning",
|
|
13766
|
+
stop: "status-error"
|
|
13767
|
+
};
|
|
13768
|
+
var statusDotVariant = {
|
|
13769
|
+
productive: "success",
|
|
13770
|
+
threshold: "warning",
|
|
13771
|
+
stop: "error"
|
|
13772
|
+
};
|
|
13773
|
+
var defaultLabel = {
|
|
13774
|
+
productive: "Productive",
|
|
13775
|
+
threshold: "Threshold",
|
|
13776
|
+
stop: "Stop"
|
|
13777
|
+
};
|
|
13778
|
+
function statusPillColor(status) {
|
|
13779
|
+
return t16[statusToken[status]];
|
|
13780
|
+
}
|
|
13781
|
+
function StatusPill2({ status, label, className, style, ...props }) {
|
|
13782
|
+
const color = statusPillColor(status);
|
|
13783
|
+
const text = label ?? defaultLabel[status];
|
|
13784
|
+
return /* @__PURE__ */ jsxs(
|
|
13785
|
+
reactNative.View,
|
|
13786
|
+
{
|
|
13787
|
+
testID: "status-pill",
|
|
13788
|
+
className,
|
|
13789
|
+
style: [
|
|
13790
|
+
{
|
|
13791
|
+
flexDirection: "row",
|
|
13792
|
+
alignItems: "center",
|
|
13793
|
+
gap: 7,
|
|
13794
|
+
alignSelf: "flex-start",
|
|
13795
|
+
paddingVertical: 7,
|
|
13796
|
+
paddingHorizontal: 13,
|
|
13797
|
+
borderRadius: 9,
|
|
13798
|
+
backgroundColor: alpha(color, 0.14),
|
|
13799
|
+
borderWidth: 1,
|
|
13800
|
+
borderColor: alpha(color, 0.4)
|
|
13801
|
+
},
|
|
13802
|
+
style
|
|
13803
|
+
],
|
|
13804
|
+
...props,
|
|
13805
|
+
children: [
|
|
13806
|
+
/* @__PURE__ */ jsx(reactNative.View, { accessibilityElementsHidden: true, children: /* @__PURE__ */ jsx(StatusDot, { variant: statusDotVariant[status], glow: true }) }),
|
|
13807
|
+
/* @__PURE__ */ jsx(reactNative.Text, { testID: "status-pill-label", style: { fontSize: 13, fontWeight: "800", color }, children: text })
|
|
13808
|
+
]
|
|
13809
|
+
}
|
|
13810
|
+
);
|
|
13811
|
+
}
|
|
13812
|
+
var t17 = getSemanticColors("dark");
|
|
13813
|
+
var categoryToken = {
|
|
13814
|
+
productive: null,
|
|
13815
|
+
threshold: "status-warning",
|
|
13816
|
+
stop: "status-error"
|
|
13817
|
+
};
|
|
13818
|
+
var floodOpacity = {
|
|
13819
|
+
productive: 0,
|
|
13820
|
+
threshold: 0.12,
|
|
13821
|
+
stop: 0.18
|
|
13822
|
+
};
|
|
13823
|
+
function liveAuraColor(category) {
|
|
13824
|
+
const token = categoryToken[category];
|
|
13825
|
+
return token ? t17[token] : null;
|
|
13826
|
+
}
|
|
13827
|
+
function LiveAuraFrame({
|
|
13828
|
+
category,
|
|
13829
|
+
pulse = true,
|
|
13830
|
+
className,
|
|
13831
|
+
children,
|
|
13832
|
+
style,
|
|
13833
|
+
...props
|
|
13834
|
+
}) {
|
|
13835
|
+
const color = liveAuraColor(category);
|
|
13836
|
+
const tint = color ? alpha(color, floodOpacity[category]) : "transparent";
|
|
13837
|
+
const floodStyle = {
|
|
13838
|
+
...reactNative.StyleSheet.absoluteFillObject,
|
|
13839
|
+
...reactNative.Platform.OS === "web" ? (
|
|
13840
|
+
// backgroundImage is a runtime RNW style, not in RN ViewStyle types.
|
|
13841
|
+
{ backgroundImage: `radial-gradient(130% 95% at 50% 0%, ${tint}, transparent 62%)` }
|
|
13842
|
+
) : { backgroundColor: tint }
|
|
13843
|
+
};
|
|
13844
|
+
return /* @__PURE__ */ jsxs(
|
|
13845
|
+
reactNative.View,
|
|
13846
|
+
{
|
|
13847
|
+
testID: "live-aura-frame",
|
|
13848
|
+
className,
|
|
13849
|
+
style: [
|
|
13850
|
+
{
|
|
13851
|
+
position: "relative",
|
|
13852
|
+
overflow: "hidden",
|
|
13853
|
+
borderRadius: 10,
|
|
13854
|
+
backgroundColor: t17["background-base"],
|
|
13855
|
+
borderWidth: 1,
|
|
13856
|
+
borderColor: t17["border-default"]
|
|
13857
|
+
},
|
|
13858
|
+
style
|
|
13859
|
+
],
|
|
13860
|
+
...props,
|
|
13861
|
+
children: [
|
|
13862
|
+
color && /* @__PURE__ */ jsx(
|
|
13863
|
+
reactNative.View,
|
|
13864
|
+
{
|
|
13865
|
+
testID: "live-aura-flood",
|
|
13866
|
+
accessibilityElementsHidden: true,
|
|
13867
|
+
pointerEvents: "none",
|
|
13868
|
+
className: category === "stop" && pulse ? "animate-pulse" : void 0,
|
|
13869
|
+
style: floodStyle
|
|
13870
|
+
}
|
|
13871
|
+
),
|
|
13872
|
+
children
|
|
12150
13873
|
]
|
|
12151
13874
|
}
|
|
12152
13875
|
);
|
|
@@ -12357,28 +14080,28 @@ function Scatter({
|
|
|
12357
14080
|
testID: "scatter-canvas",
|
|
12358
14081
|
style: { position: "absolute", top: 0, left: 0, width, height, overflow: "hidden" },
|
|
12359
14082
|
children: [
|
|
12360
|
-
ticksOf(yd, TICK_COUNT).map((
|
|
12361
|
-
const y = toY(
|
|
14083
|
+
ticksOf(yd, TICK_COUNT).map((t18, i) => {
|
|
14084
|
+
const y = toY(t18);
|
|
12362
14085
|
return /* @__PURE__ */ jsx(
|
|
12363
14086
|
reactNative.View,
|
|
12364
14087
|
{
|
|
12365
14088
|
accessibilityElementsHidden: true,
|
|
12366
14089
|
testID: "scatter-gridline-y",
|
|
12367
14090
|
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(
|
|
14091
|
+
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
14092
|
},
|
|
12370
14093
|
`y-${i}`
|
|
12371
14094
|
);
|
|
12372
14095
|
}),
|
|
12373
|
-
ticksOf(xd, TICK_COUNT).map((
|
|
12374
|
-
const x = toX(
|
|
14096
|
+
ticksOf(xd, TICK_COUNT).map((t18, i) => {
|
|
14097
|
+
const x = toX(t18);
|
|
12375
14098
|
return /* @__PURE__ */ jsx(
|
|
12376
14099
|
reactNative.View,
|
|
12377
14100
|
{
|
|
12378
14101
|
accessibilityElementsHidden: true,
|
|
12379
14102
|
testID: "scatter-gridline-x",
|
|
12380
14103
|
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(
|
|
14104
|
+
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
14105
|
},
|
|
12383
14106
|
`x-${i}`
|
|
12384
14107
|
);
|
|
@@ -12443,7 +14166,7 @@ var STATUS_SUCCESS4 = sem2["status-success"];
|
|
|
12443
14166
|
var STATUS_WARNING4 = sem2["status-warning"];
|
|
12444
14167
|
var STATUS_ERROR4 = sem2["status-error"];
|
|
12445
14168
|
var TRACK = "rgba(255,255,255,0.08)";
|
|
12446
|
-
var
|
|
14169
|
+
var DEFAULT_THRESHOLDS2 = [
|
|
12447
14170
|
{ value: 0, color: STATUS_ERROR4 },
|
|
12448
14171
|
{ value: 60, color: STATUS_WARNING4 },
|
|
12449
14172
|
{ value: 80, color: STATUS_SUCCESS4 }
|
|
@@ -12458,8 +14181,8 @@ function clamp012(n) {
|
|
|
12458
14181
|
function formatValue2(v) {
|
|
12459
14182
|
return Number.isInteger(v) ? `${v}` : v.toFixed(1);
|
|
12460
14183
|
}
|
|
12461
|
-
function bandColor2(
|
|
12462
|
-
const value = min +
|
|
14184
|
+
function bandColor2(fraction2, bands, min, max) {
|
|
14185
|
+
const value = min + fraction2 * (max - min);
|
|
12463
14186
|
const sorted = [...bands].sort((a, b) => a.value - b.value);
|
|
12464
14187
|
let color = sorted[0]?.color ?? STATUS_SUCCESS4;
|
|
12465
14188
|
for (const band of sorted) {
|
|
@@ -12471,15 +14194,15 @@ function buildTicks(fill, size, activeColor) {
|
|
|
12471
14194
|
const center = size / 2;
|
|
12472
14195
|
const radius = center - size * 0.08;
|
|
12473
14196
|
return Array.from({ length: SEGMENTS }, (_, i) => {
|
|
12474
|
-
const
|
|
12475
|
-
const deg = ARC_START +
|
|
14197
|
+
const fraction2 = (i + 0.5) / SEGMENTS;
|
|
14198
|
+
const deg = ARC_START + fraction2 * ARC_SWEEP;
|
|
12476
14199
|
const rad = deg * Math.PI / 180;
|
|
12477
14200
|
return {
|
|
12478
14201
|
key: i,
|
|
12479
14202
|
left: center + radius * Math.cos(rad),
|
|
12480
14203
|
top: center + radius * Math.sin(rad),
|
|
12481
14204
|
rotate: `${deg - 90}deg`,
|
|
12482
|
-
color:
|
|
14205
|
+
color: fraction2 <= fill ? activeColor(fraction2) : TRACK
|
|
12483
14206
|
};
|
|
12484
14207
|
});
|
|
12485
14208
|
}
|
|
@@ -12490,7 +14213,7 @@ function Gauge2({
|
|
|
12490
14213
|
size = 160,
|
|
12491
14214
|
label,
|
|
12492
14215
|
unit,
|
|
12493
|
-
thresholds =
|
|
14216
|
+
thresholds = DEFAULT_THRESHOLDS2,
|
|
12494
14217
|
color,
|
|
12495
14218
|
className,
|
|
12496
14219
|
...props
|
|
@@ -12513,23 +14236,23 @@ function Gauge2({
|
|
|
12513
14236
|
testID: "gauge",
|
|
12514
14237
|
...props,
|
|
12515
14238
|
children: [
|
|
12516
|
-
ticks.map((
|
|
14239
|
+
ticks.map((t18) => /* @__PURE__ */ jsx(
|
|
12517
14240
|
reactNative.View,
|
|
12518
14241
|
{
|
|
12519
14242
|
accessibilityElementsHidden: true,
|
|
12520
14243
|
testID: "gauge-segment",
|
|
12521
14244
|
style: {
|
|
12522
14245
|
position: "absolute",
|
|
12523
|
-
left:
|
|
12524
|
-
top:
|
|
14246
|
+
left: t18.left - tickThickness / 2,
|
|
14247
|
+
top: t18.top - tickLength / 2,
|
|
12525
14248
|
width: tickThickness,
|
|
12526
14249
|
height: tickLength,
|
|
12527
14250
|
borderRadius: tickThickness / 2,
|
|
12528
|
-
backgroundColor:
|
|
12529
|
-
transform: [{ rotate:
|
|
14251
|
+
backgroundColor: t18.color,
|
|
14252
|
+
transform: [{ rotate: t18.rotate }]
|
|
12530
14253
|
}
|
|
12531
14254
|
},
|
|
12532
|
-
|
|
14255
|
+
t18.key
|
|
12533
14256
|
)),
|
|
12534
14257
|
/* @__PURE__ */ jsxs(reactNative.View, { style: { position: "absolute", top: 0, left: 0, width: size, height: size }, className: "items-center justify-center", children: [
|
|
12535
14258
|
/* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row items-baseline gap-0.5", children: [
|
|
@@ -12575,7 +14298,7 @@ function BrandLockup({
|
|
|
12575
14298
|
}
|
|
12576
14299
|
var stateConfig = {
|
|
12577
14300
|
// live = vivid green with an expanding ring; rest = solid amber (no pulse — operator); idle is dim.
|
|
12578
|
-
live: { label: "LIVE", color: "
|
|
14301
|
+
live: { label: "LIVE", color: "live", pulse: "ping", textColor: "primary" },
|
|
12579
14302
|
rest: { label: "REST", color: "warning", pulse: false, textColor: "primary" },
|
|
12580
14303
|
idle: { label: "IDLE", color: "default", pulse: false, textColor: "secondary" }
|
|
12581
14304
|
};
|
|
@@ -12739,6 +14462,127 @@ function TopBar({
|
|
|
12739
14462
|
}
|
|
12740
14463
|
);
|
|
12741
14464
|
}
|
|
14465
|
+
function NavItem({
|
|
14466
|
+
icon,
|
|
14467
|
+
label,
|
|
14468
|
+
active = false,
|
|
14469
|
+
live = false,
|
|
14470
|
+
onPress,
|
|
14471
|
+
className
|
|
14472
|
+
}) {
|
|
14473
|
+
const glyphColor = active ? "text-brand-primary" : "text-text-tertiary";
|
|
14474
|
+
const labelColor = active ? "text-brand-primary" : live ? "text-status-live-muted" : "text-text-tertiary";
|
|
14475
|
+
return /* @__PURE__ */ jsxs(
|
|
14476
|
+
reactNative.Pressable,
|
|
14477
|
+
{
|
|
14478
|
+
accessibilityRole: "tab",
|
|
14479
|
+
accessibilityState: { selected: active },
|
|
14480
|
+
accessibilityLabel: label,
|
|
14481
|
+
onPress,
|
|
14482
|
+
className: cn("relative h-[46px] w-[60px] items-center justify-center", className),
|
|
14483
|
+
children: [
|
|
14484
|
+
active ? /* @__PURE__ */ jsx(
|
|
14485
|
+
reactNative.View,
|
|
14486
|
+
{
|
|
14487
|
+
testID: "nav-item-accent",
|
|
14488
|
+
className: "absolute left-0 top-[13px] bottom-[13px] w-[3px] rounded-r-[3px] bg-brand-primary"
|
|
14489
|
+
}
|
|
14490
|
+
) : null,
|
|
14491
|
+
/* @__PURE__ */ jsxs(
|
|
14492
|
+
reactNative.View,
|
|
14493
|
+
{
|
|
14494
|
+
className: cn(
|
|
14495
|
+
"h-[46px] w-[46px] items-center justify-center gap-[3px] rounded-[11px]",
|
|
14496
|
+
glyphColor
|
|
14497
|
+
),
|
|
14498
|
+
children: [
|
|
14499
|
+
icon,
|
|
14500
|
+
/* @__PURE__ */ jsx(
|
|
14501
|
+
Typography,
|
|
14502
|
+
{
|
|
14503
|
+
variant: "button",
|
|
14504
|
+
color: "inherit",
|
|
14505
|
+
className: cn("text-[8.5px] font-bold uppercase tracking-[0.4px]", labelColor),
|
|
14506
|
+
children: label
|
|
14507
|
+
}
|
|
14508
|
+
)
|
|
14509
|
+
]
|
|
14510
|
+
}
|
|
14511
|
+
)
|
|
14512
|
+
]
|
|
14513
|
+
}
|
|
14514
|
+
);
|
|
14515
|
+
}
|
|
14516
|
+
var defaultNavItems = [
|
|
14517
|
+
{ key: "live", label: "Live", icon: /* @__PURE__ */ jsx(ActivityIcon, { size: 20, color: "currentColor" }) },
|
|
14518
|
+
{ key: "review", label: "Review", icon: /* @__PURE__ */ jsx(HistoryIcon, { size: 20, color: "currentColor" }) },
|
|
14519
|
+
{ key: "program", label: "Plan", icon: /* @__PURE__ */ jsx(LayersIcon, { size: 20, color: "currentColor" }) },
|
|
14520
|
+
{ key: "body", label: "Body", icon: /* @__PURE__ */ jsx(PersonStandingIcon, { size: 20, color: "currentColor" }) }
|
|
14521
|
+
];
|
|
14522
|
+
function SideNav({
|
|
14523
|
+
items = defaultNavItems,
|
|
14524
|
+
activeKey,
|
|
14525
|
+
onNavigate,
|
|
14526
|
+
liveKey = null,
|
|
14527
|
+
className
|
|
14528
|
+
}) {
|
|
14529
|
+
return /* @__PURE__ */ jsx(
|
|
14530
|
+
reactNative.View,
|
|
14531
|
+
{
|
|
14532
|
+
accessibilityRole: "tablist",
|
|
14533
|
+
className: cn(
|
|
14534
|
+
"w-[60px] items-center gap-[6px] border-r border-border bg-background-base py-3",
|
|
14535
|
+
className
|
|
14536
|
+
),
|
|
14537
|
+
children: items.map((item) => /* @__PURE__ */ jsx(
|
|
14538
|
+
NavItem,
|
|
14539
|
+
{
|
|
14540
|
+
icon: item.icon,
|
|
14541
|
+
label: item.label,
|
|
14542
|
+
active: item.key === activeKey,
|
|
14543
|
+
live: item.key === liveKey && item.key !== activeKey,
|
|
14544
|
+
onPress: () => onNavigate?.(item.key)
|
|
14545
|
+
},
|
|
14546
|
+
item.key
|
|
14547
|
+
))
|
|
14548
|
+
}
|
|
14549
|
+
);
|
|
14550
|
+
}
|
|
14551
|
+
var DEMO_DEVICES = [
|
|
14552
|
+
{ id: "Voltra-A3F2", nickname: "Left Cable", slot: "L", state: "connected" },
|
|
14553
|
+
{ id: "Voltra-9B1C", nickname: "Right Cable", slot: "R", state: "connected" }
|
|
14554
|
+
];
|
|
14555
|
+
function ContentPlaceholder() {
|
|
14556
|
+
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" }) });
|
|
14557
|
+
}
|
|
14558
|
+
function DashboardShell({
|
|
14559
|
+
activeKey = "live",
|
|
14560
|
+
navItems,
|
|
14561
|
+
liveKey = null,
|
|
14562
|
+
state = "live",
|
|
14563
|
+
devices = DEMO_DEVICES,
|
|
14564
|
+
subtitle = "wall dashboard",
|
|
14565
|
+
onNavigate,
|
|
14566
|
+
onSelectDevice,
|
|
14567
|
+
children,
|
|
14568
|
+
className
|
|
14569
|
+
}) {
|
|
14570
|
+
return /* @__PURE__ */ jsxs(reactNative.View, { className: cn("flex-1 flex-row bg-surface-base", className), children: [
|
|
14571
|
+
/* @__PURE__ */ jsx(SideNav, { activeKey, items: navItems, liveKey, onNavigate }),
|
|
14572
|
+
/* @__PURE__ */ jsxs(reactNative.View, { className: "flex-1", children: [
|
|
14573
|
+
/* @__PURE__ */ jsx(
|
|
14574
|
+
TopBar,
|
|
14575
|
+
{
|
|
14576
|
+
state,
|
|
14577
|
+
devices,
|
|
14578
|
+
subtitle,
|
|
14579
|
+
onSelectDevice
|
|
14580
|
+
}
|
|
14581
|
+
),
|
|
14582
|
+
/* @__PURE__ */ jsx(reactNative.View, { className: "flex-1", children: children ?? /* @__PURE__ */ jsx(ContentPlaceholder, {}) })
|
|
14583
|
+
] })
|
|
14584
|
+
] });
|
|
14585
|
+
}
|
|
12742
14586
|
|
|
12743
14587
|
// src/utils/form.ts
|
|
12744
14588
|
function getFieldValidationProps(state) {
|
|
@@ -12832,13 +14676,16 @@ exports.Accordion = Accordion;
|
|
|
12832
14676
|
exports.AccordionButton = AccordionButton;
|
|
12833
14677
|
exports.AccordionItem = AccordionItem;
|
|
12834
14678
|
exports.AccordionPanel = AccordionPanel;
|
|
14679
|
+
exports.ActivityIcon = ActivityIcon;
|
|
12835
14680
|
exports.Alert = Alert;
|
|
12836
14681
|
exports.AlertDescription = AlertDescription;
|
|
12837
14682
|
exports.AlertTitle = AlertTitle;
|
|
14683
|
+
exports.AlertTriangleIcon = AlertTriangleIcon;
|
|
12838
14684
|
exports.Autocomplete = Autocomplete;
|
|
12839
14685
|
exports.Avatar = Avatar;
|
|
12840
14686
|
exports.AvatarBadge = AvatarBadge;
|
|
12841
14687
|
exports.AvatarGroup = AvatarGroup;
|
|
14688
|
+
exports.AwardIcon = AwardIcon;
|
|
12842
14689
|
exports.Badge = Badge;
|
|
12843
14690
|
exports.BadgeText = BadgeText;
|
|
12844
14691
|
exports.BaseBadge = BaseBadge;
|
|
@@ -12863,10 +14710,13 @@ exports.CardTitle = CardTitle;
|
|
|
12863
14710
|
exports.Checkbox = Checkbox;
|
|
12864
14711
|
exports.CheckboxGroup = CheckboxGroup;
|
|
12865
14712
|
exports.Chip = Chip;
|
|
14713
|
+
exports.CircleSlashIcon = CircleSlashIcon;
|
|
12866
14714
|
exports.CircularProgress = CircularProgress;
|
|
14715
|
+
exports.CircularTimer = CircularTimer;
|
|
12867
14716
|
exports.Collapse = Collapse;
|
|
12868
14717
|
exports.CollapseButton = CollapseButton;
|
|
12869
14718
|
exports.CollapseContent = CollapseContent;
|
|
14719
|
+
exports.DashboardShell = DashboardShell;
|
|
12870
14720
|
exports.DataRow = DataRow;
|
|
12871
14721
|
exports.DateTime = DateTime;
|
|
12872
14722
|
exports.DeviationBar = DeviationBar;
|
|
@@ -12881,6 +14731,10 @@ exports.DrawerHeader = DrawerHeader;
|
|
|
12881
14731
|
exports.DumbbellIcon = DumbbellIcon;
|
|
12882
14732
|
exports.EmptyState = EmptyState;
|
|
12883
14733
|
exports.ExerciseCard = ExerciseCard;
|
|
14734
|
+
exports.ExerciseCardHeading = ExerciseCardHeading;
|
|
14735
|
+
exports.ExerciseHeading = ExerciseHeading;
|
|
14736
|
+
exports.ExerciseIndicator = ExerciseIndicator;
|
|
14737
|
+
exports.FatigueMeter = FatigueMeter;
|
|
12884
14738
|
exports.FormActions = FormActions;
|
|
12885
14739
|
exports.FormField = FormField;
|
|
12886
14740
|
exports.FormRow = FormRow;
|
|
@@ -12889,20 +14743,26 @@ exports.Gauge = Gauge2;
|
|
|
12889
14743
|
exports.HStack = HStack;
|
|
12890
14744
|
exports.Heading = Heading;
|
|
12891
14745
|
exports.HelpTip = HelpTip;
|
|
14746
|
+
exports.HistoryIcon = HistoryIcon;
|
|
14747
|
+
exports.INDICATOR_PRECEDENCE = INDICATOR_PRECEDENCE;
|
|
12892
14748
|
exports.IconBox = IconBox;
|
|
12893
14749
|
exports.Indicator = Indicator;
|
|
14750
|
+
exports.InfoIcon = InfoIcon;
|
|
12894
14751
|
exports.Input = Input;
|
|
12895
14752
|
exports.InputBar = InputBar;
|
|
12896
14753
|
exports.InputGroup = InputGroup;
|
|
12897
14754
|
exports.IntensityBar = IntensityBar;
|
|
12898
14755
|
exports.Label = Label;
|
|
12899
14756
|
exports.LabelWithHelp = LabelWithHelp;
|
|
14757
|
+
exports.LayersIcon = LayersIcon;
|
|
12900
14758
|
exports.Link = Link;
|
|
12901
14759
|
exports.ListItem = ListItem;
|
|
12902
14760
|
exports.ListItemContent = ListItemContent;
|
|
12903
14761
|
exports.ListItemDivider = ListItemDivider;
|
|
12904
14762
|
exports.ListItemIcon = ListItemIcon;
|
|
12905
14763
|
exports.ListItemTrailing = ListItemTrailing;
|
|
14764
|
+
exports.LiveAuraFrame = LiveAuraFrame;
|
|
14765
|
+
exports.METRIC_FONT = METRIC_FONT;
|
|
12906
14766
|
exports.Menu = Menu;
|
|
12907
14767
|
exports.MenuDivider = MenuDivider;
|
|
12908
14768
|
exports.MenuGroup = MenuGroup;
|
|
@@ -12913,7 +14773,9 @@ exports.MesoCard = MesoCard;
|
|
|
12913
14773
|
exports.MesoProgressBar = MesoProgressBar;
|
|
12914
14774
|
exports.MesoStatusCard = MesoStatusCard;
|
|
12915
14775
|
exports.Metric = Metric;
|
|
14776
|
+
exports.MetricCell = MetricCell;
|
|
12916
14777
|
exports.MetricGroup = MetricGroup;
|
|
14778
|
+
exports.MetricTiles = MetricTiles;
|
|
12917
14779
|
exports.Modal = Modal;
|
|
12918
14780
|
exports.ModalBody = ModalBody;
|
|
12919
14781
|
exports.ModalCloseButton = ModalCloseButton;
|
|
@@ -12922,9 +14784,11 @@ exports.ModalFooter = ModalFooter;
|
|
|
12922
14784
|
exports.ModalHeader = ModalHeader;
|
|
12923
14785
|
exports.ModalTitle = ModalTitle;
|
|
12924
14786
|
exports.MuscleGroupChip = MuscleGroupChip;
|
|
14787
|
+
exports.NavItem = NavItem;
|
|
12925
14788
|
exports.Overline = Overline;
|
|
12926
14789
|
exports.Paragraph = Paragraph;
|
|
12927
14790
|
exports.PasswordInput = PasswordInput;
|
|
14791
|
+
exports.PersonStandingIcon = PersonStandingIcon;
|
|
12928
14792
|
exports.Pill = Pill;
|
|
12929
14793
|
exports.PlaceholderStrip = PlaceholderStrip;
|
|
12930
14794
|
exports.Popover = Popover;
|
|
@@ -12939,13 +14803,26 @@ exports.Radio = Radio;
|
|
|
12939
14803
|
exports.RadioGroup = RadioGroup;
|
|
12940
14804
|
exports.ReadinessCheck = ReadinessCheck;
|
|
12941
14805
|
exports.RestTimer = RestTimer;
|
|
14806
|
+
exports.SET_STRIP_VARIABLE_COLOR = SET_STRIP_VARIABLE_COLOR;
|
|
14807
|
+
exports.SET_STRIP_ZONES = SET_STRIP_ZONES;
|
|
14808
|
+
exports.ScaleIcon = ScaleIcon;
|
|
12942
14809
|
exports.Scatter = Scatter;
|
|
14810
|
+
exports.ScheduleTiles = ScheduleTiles;
|
|
12943
14811
|
exports.Section = Section;
|
|
12944
14812
|
exports.SectionContent = SectionContent;
|
|
12945
14813
|
exports.SectionHeader = SectionHeader;
|
|
14814
|
+
exports.SegmentedBar = SegmentedBar;
|
|
14815
|
+
exports.SegmentedProgressBar = SegmentedProgressBar;
|
|
12946
14816
|
exports.Select = Select;
|
|
14817
|
+
exports.SessionHeader = SessionHeader;
|
|
14818
|
+
exports.SessionRail = SessionRail;
|
|
12947
14819
|
exports.SessionStatePill = SessionStatePill;
|
|
14820
|
+
exports.SetBar = SetBar;
|
|
12948
14821
|
exports.SetRow = SetRow;
|
|
14822
|
+
exports.SetStrip = SetStrip;
|
|
14823
|
+
exports.SetTableHeader = SetTableHeader;
|
|
14824
|
+
exports.SetsRepsLoad = SetsRepsLoad;
|
|
14825
|
+
exports.SideNav = SideNav;
|
|
12949
14826
|
exports.Sidebar = Sidebar;
|
|
12950
14827
|
exports.SidebarContent = SidebarContent;
|
|
12951
14828
|
exports.SidebarDivider = SidebarDivider;
|
|
@@ -12963,6 +14840,7 @@ exports.Spinner = Spinner;
|
|
|
12963
14840
|
exports.Stack = Stack;
|
|
12964
14841
|
exports.StarIcon = StarIcon;
|
|
12965
14842
|
exports.StatusDot = StatusDot;
|
|
14843
|
+
exports.StatusPill = StatusPill2;
|
|
12966
14844
|
exports.StepContent = StepContent;
|
|
12967
14845
|
exports.StepIndicator = StepIndicator;
|
|
12968
14846
|
exports.StepLabel = StepLabel;
|
|
@@ -12988,6 +14866,8 @@ exports.TableRow = TableRow;
|
|
|
12988
14866
|
exports.Tabs = Tabs;
|
|
12989
14867
|
exports.TempoBar = TempoBar;
|
|
12990
14868
|
exports.TempoDisplay = TempoDisplay;
|
|
14869
|
+
exports.Tile = Tile;
|
|
14870
|
+
exports.TimerReadout = TimerReadout;
|
|
12991
14871
|
exports.Toast = Toast;
|
|
12992
14872
|
exports.ToastProvider = ToastProvider;
|
|
12993
14873
|
exports.ToolbarButton = ToolbarButton;
|
|
@@ -12995,14 +14875,17 @@ exports.ToolbarButtonGroup = ToolbarButtonGroup;
|
|
|
12995
14875
|
exports.Tooltip = Tooltip;
|
|
12996
14876
|
exports.TopBar = TopBar;
|
|
12997
14877
|
exports.Treemap = Treemap;
|
|
14878
|
+
exports.TrendingDownIcon = TrendingDownIcon;
|
|
12998
14879
|
exports.Typography = Typography;
|
|
12999
14880
|
exports.VStack = VStack;
|
|
13000
14881
|
exports.VelocityStrip = VelocityStrip;
|
|
13001
14882
|
exports.VoltrasMark = VoltrasMark;
|
|
14883
|
+
exports.VolumeLandmarkBar = VolumeLandmarkBar;
|
|
13002
14884
|
exports.WeekRow = WeekRow;
|
|
13003
14885
|
exports.WeightBadge = WeightBadge;
|
|
13004
14886
|
exports.WorkoutCard = WorkoutCard;
|
|
13005
14887
|
exports.WorkoutPill = WorkoutPill;
|
|
14888
|
+
exports.ZoneTrack = ZoneTrack;
|
|
13006
14889
|
exports.alpha = alpha;
|
|
13007
14890
|
exports.applyThemePreset = applyThemePreset;
|
|
13008
14891
|
exports.audiobookPreset = audiobookPreset;
|
|
@@ -13022,11 +14905,13 @@ exports.createRaisedHoverShadow = createRaisedHoverShadow;
|
|
|
13022
14905
|
exports.createRaisedShadow = createRaisedShadow;
|
|
13023
14906
|
exports.darkThemeCSSVars = darkThemeCSSVars;
|
|
13024
14907
|
exports.darken = darken;
|
|
14908
|
+
exports.defaultNavItems = defaultNavItems;
|
|
13025
14909
|
exports.defaultPreset = defaultPreset;
|
|
13026
14910
|
exports.discreteRainbow = discreteRainbow;
|
|
13027
14911
|
exports.divergingScale = divergingScale;
|
|
13028
14912
|
exports.elevationSystem = elevationSystem;
|
|
13029
14913
|
exports.formatDateTime = formatDateTime;
|
|
14914
|
+
exports.formatDuration = formatDuration;
|
|
13030
14915
|
exports.formatPrescription = formatPrescription;
|
|
13031
14916
|
exports.formatSignedPct = formatSignedPct;
|
|
13032
14917
|
exports.formatVelocity = formatVelocity;
|
|
@@ -13063,7 +14948,10 @@ exports.isValidEmail = isValidEmail;
|
|
|
13063
14948
|
exports.lightThemeCSSVars = lightThemeCSSVars;
|
|
13064
14949
|
exports.lighten = lighten;
|
|
13065
14950
|
exports.linearGradient = linearGradient;
|
|
14951
|
+
exports.liveAuraColor = liveAuraColor;
|
|
13066
14952
|
exports.neumorphicShadows = neumorphicShadows;
|
|
14953
|
+
exports.paceTone = paceTone;
|
|
14954
|
+
exports.paceToneColor = paceToneColor;
|
|
13067
14955
|
exports.primitiveBorderRadius = primitiveBorderRadius;
|
|
13068
14956
|
exports.primitiveBreakpoints = primitiveBreakpoints;
|
|
13069
14957
|
exports.primitiveColors = primitiveColors;
|
|
@@ -13073,6 +14961,7 @@ exports.primitiveSpacing = primitiveSpacing;
|
|
|
13073
14961
|
exports.primitiveTypography = primitiveTypography;
|
|
13074
14962
|
exports.primitiveZIndex = primitiveZIndex;
|
|
13075
14963
|
exports.resolveColor = resolveColor;
|
|
14964
|
+
exports.resolveIndicator = resolveIndicator;
|
|
13076
14965
|
exports.rgbToHex = rgbToHex;
|
|
13077
14966
|
exports.rgbToHsv = rgbToHsv;
|
|
13078
14967
|
exports.roundRpe = roundRpe;
|
|
@@ -13084,11 +14973,14 @@ exports.semanticColorsLight = semanticColorsLight;
|
|
|
13084
14973
|
exports.semanticTypography = semanticTypography;
|
|
13085
14974
|
exports.sequentialEffort = sequentialEffort;
|
|
13086
14975
|
exports.shadowColors = shadowColors;
|
|
14976
|
+
exports.statusPillColor = statusPillColor;
|
|
13087
14977
|
exports.surfaceGradient = surfaceGradient;
|
|
13088
14978
|
exports.useTable = useTable;
|
|
14979
|
+
exports.useTimer = useTimer;
|
|
13089
14980
|
exports.useToast = useToast;
|
|
13090
14981
|
exports.useToolbarButton = useToolbarButton;
|
|
13091
14982
|
exports.validateCssPropertyManifest = validateCssPropertyManifest;
|
|
13092
14983
|
exports.validationRules = validationRules;
|
|
14984
|
+
exports.velocityZoneColor = velocityZoneColor;
|
|
13093
14985
|
//# sourceMappingURL=index.js.map
|
|
13094
14986
|
//# sourceMappingURL=index.js.map
|