@titan-design/react-ui 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bodymap.js +241 -189
- package/dist/bodymap.js.map +1 -1
- package/dist/bodymap.mjs +241 -189
- package/dist/bodymap.mjs.map +1 -1
- package/dist/index.d.mts +1017 -130
- package/dist/index.d.ts +1017 -130
- package/dist/index.js +3058 -1090
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2992 -1091
- package/dist/index.mjs.map +1 -1
- package/dist/{pages-MO0JCySL.d.mts → pages-8u_4WbL-.d.mts} +280 -57
- package/dist/{pages-D7Jlssvp.d.ts → pages-DaWiBOGa.d.ts} +280 -57
- package/dist/pages.d.mts +1 -1
- package/dist/pages.d.ts +1 -1
- package/dist/pages.js +1797 -1292
- package/dist/pages.js.map +1 -1
- package/dist/pages.mjs +1797 -1292
- package/dist/pages.mjs.map +1 -1
- package/dist/theme/index.d.mts +464 -268
- package/dist/theme/index.d.ts +464 -268
- package/dist/theme/index.js +303 -191
- package/dist/theme/index.js.map +1 -1
- package/dist/theme/index.mjs +295 -192
- package/dist/theme/index.mjs.map +1 -1
- package/dist/theme/tokens-css.js +160 -143
- package/dist/theme/tokens-css.js.map +1 -1
- package/dist/theme/tokens-css.mjs +160 -143
- package/dist/theme/tokens-css.mjs.map +1 -1
- package/package.json +3 -1
- package/src/arch/Architecture.stories.tsx +870 -0
- package/src/arch/annotations.json +48 -0
- package/src/arch/arch-graph.json +4301 -0
- package/src/components/custom/DateTime/DateTime.stories.tsx +83 -7
- package/src/components/custom/DateTime/DateTime.test.tsx +29 -3
- package/src/components/custom/DateTime/DateTime.tsx +55 -15
- package/src/components/custom/EmptyState/EmptyState.stories.tsx +1 -1
- package/src/components/custom/Gauge/Gauge.stories.tsx +1 -1
- package/src/components/custom/Gauge/Gauge.test.tsx +2 -2
- package/src/components/custom/Gauge/Gauge.tsx +6 -3
- package/src/components/custom/Metric/Metric.stories.tsx +1 -1
- package/src/components/custom/Scatter/Scatter.stories.tsx +1 -1
- package/src/components/custom/Scatter/Scatter.tsx +3 -5
- 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/Treemap/Treemap.tsx +3 -5
- package/src/components/custom/Typography/Typography.stories.tsx +46 -28
- package/src/components/custom/Typography/Typography.test.tsx +13 -3
- package/src/components/custom/Typography/Typography.tsx +5 -0
- 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 +11 -16
- 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/BodyMap.tsx +6 -3
- package/src/components/custom/Workout/BodyMapDetailPanel.stories.tsx +1 -1
- package/src/components/custom/Workout/BodyMapDetailPanel.tsx +6 -3
- package/src/components/custom/Workout/CapacityBandChart.stories.tsx +1 -1
- package/src/components/custom/Workout/CapacityBandChart.test.tsx +2 -2
- package/src/components/custom/Workout/CapacityBandChart.tsx +9 -6
- package/src/components/custom/Workout/DeviationBar.stories.tsx +1 -1
- package/src/components/custom/Workout/DeviationBar.test.tsx +1 -1
- package/src/components/custom/Workout/DeviationBar.tsx +7 -4
- 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 +6 -6
- package/src/components/custom/Workout/ExerciseHeading.stories.tsx +81 -0
- package/src/components/custom/Workout/ExerciseHeading.test.tsx +77 -0
- package/src/components/custom/Workout/ExerciseHeading.tsx +85 -0
- package/src/components/custom/Workout/ExerciseIndicator.stories.tsx +110 -0
- package/src/components/custom/Workout/ExerciseIndicator.test.tsx +93 -0
- package/src/components/custom/Workout/ExerciseIndicator.tsx +120 -0
- package/src/components/custom/Workout/FatigueMeter.stories.tsx +51 -0
- package/src/components/custom/Workout/FatigueMeter.test.tsx +73 -0
- package/src/components/custom/Workout/FatigueMeter.tsx +82 -0
- package/src/components/custom/Workout/InputBar.stories.tsx +1 -1
- package/src/components/custom/Workout/InputBar.tsx +2 -1
- package/src/components/custom/Workout/IntensityBar.stories.tsx +1 -1
- package/src/components/custom/Workout/IntensityBar.test.tsx +8 -8
- package/src/components/custom/Workout/IntensityBar.tsx +10 -6
- 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/MesoCard.tsx +8 -3
- package/src/components/custom/Workout/MesoProgressBar.stories.tsx +1 -1
- package/src/components/custom/Workout/MesoProgressBar.test.tsx +1 -1
- package/src/components/custom/Workout/MesoProgressBar.tsx +3 -2
- package/src/components/custom/Workout/MesoStatusCard.stories.tsx +1 -1
- package/src/components/custom/Workout/MesoStatusCard.tsx +20 -13
- 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/MuscleGroupChip.tsx +7 -4
- 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/PrBadge.tsx +5 -2
- package/src/components/custom/Workout/PrHistoryModal.stories.tsx +1 -1
- package/src/components/custom/Workout/PrHistoryModal.tsx +4 -3
- package/src/components/custom/Workout/ProgramPlanningPage.stories.tsx +4 -16
- package/src/components/custom/Workout/ProgramPlanningPage.tsx +10 -7
- package/src/components/custom/Workout/README.md +146 -4
- package/src/components/custom/Workout/ReadinessCheck.stories.tsx +1 -1
- package/src/components/custom/Workout/ReadinessCheck.tsx +7 -4
- package/src/components/custom/Workout/RestTimer.stories.tsx +1 -1
- package/src/components/custom/Workout/RestTimer.tsx +17 -11
- 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 +170 -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/Sparkline.tsx +2 -1
- package/src/components/custom/Workout/StatusDot.stories.tsx +1 -1
- package/src/components/custom/Workout/StatusDot.test.tsx +2 -2
- package/src/components/custom/Workout/StatusDot.tsx +12 -9
- 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/StrengthTrendChart.tsx +9 -6
- package/src/components/custom/Workout/SupersetWrapper.stories.tsx +1 -1
- package/src/components/custom/Workout/SupersetWrapper.tsx +2 -1
- package/src/components/custom/Workout/TempoBar.stories.tsx +1 -1
- package/src/components/custom/Workout/TempoDisplay.stories.tsx +12 -1
- package/src/components/custom/Workout/TempoDisplay.tsx +29 -36
- package/src/components/custom/Workout/TrainingStatusPage.stories.tsx +1 -1
- package/src/components/custom/Workout/VelocityStrip.modalities.stories.tsx +254 -0
- package/src/components/custom/Workout/VelocityStrip.responsive.stories.tsx +145 -0
- package/src/components/custom/Workout/VelocityStrip.stories.tsx +129 -66
- package/src/components/custom/Workout/VelocityStrip.test.tsx +379 -144
- package/src/components/custom/Workout/VelocityStrip.tsx +508 -120
- package/src/components/custom/Workout/VolumeLandmarkBar.stories.tsx +73 -0
- package/src/components/custom/Workout/VolumeLandmarkBar.test.tsx +119 -0
- package/src/components/custom/Workout/VolumeLandmarkBar.tsx +154 -0
- package/src/components/custom/Workout/WeekRow.stories.tsx +1 -1
- package/src/components/custom/Workout/WeekRow.tsx +3 -2
- package/src/components/custom/Workout/WeightBadge.stories.tsx +1 -1
- package/src/components/custom/Workout/WeightBadge.tsx +6 -2
- 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/WorkoutCard.tsx +5 -2
- package/src/components/custom/Workout/WorkoutPill.stories.tsx +1 -1
- package/src/components/custom/Workout/WorkoutPill.tsx +12 -8
- package/src/components/custom/Workout/ZoneTrack.stories.tsx +140 -0
- package/src/components/custom/Workout/ZoneTrack.test.tsx +200 -0
- package/src/components/custom/Workout/ZoneTrack.tsx +315 -0
- package/src/components/custom/Workout/icons.tsx +6 -67
- package/src/components/custom/Workout/index.ts +55 -2
- package/src/components/custom/Workout/metricText.stories.tsx +69 -0
- package/src/components/custom/Workout/metricText.tsx +34 -0
- package/src/components/custom/Workout/paceTone.test.ts +29 -0
- package/src/components/custom/Workout/paceTone.ts +27 -0
- package/src/components/custom/Workout/setHeadingKit.tsx +177 -0
- package/src/components/custom/index.ts +1 -0
- package/src/components/custom/stepper/Stepper.stories.tsx +1 -1
- package/src/components/icons/SvgIcon.tsx +50 -0
- package/src/components/icons/icons.stories.tsx +122 -0
- package/src/components/icons/icons.test.tsx +73 -0
- package/src/components/icons/icons.tsx +153 -0
- package/src/components/icons/index.ts +4 -0
- package/src/components/index.ts +6 -0
- package/src/components/shell/BrandLockup.stories.tsx +27 -0
- package/src/components/shell/BrandLockup.test.tsx +23 -0
- package/src/components/shell/BrandLockup.tsx +47 -0
- 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 +28 -0
- package/src/components/shell/DeviceIndicator.test.tsx +26 -0
- package/src/components/shell/DeviceIndicator.tsx +52 -0
- package/src/components/shell/DeviceMenu.stories.tsx +47 -0
- package/src/components/shell/DeviceMenu.test.tsx +40 -0
- package/src/components/shell/DeviceMenu.tsx +65 -0
- package/src/components/shell/DeviceRow.stories.tsx +38 -0
- package/src/components/shell/DeviceRow.test.tsx +37 -0
- package/src/components/shell/DeviceRow.tsx +58 -0
- 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 +116 -0
- package/src/components/shell/SessionRailSpecimen.stories.tsx +753 -0
- package/src/components/shell/SessionStatePill.stories.tsx +27 -0
- package/src/components/shell/SessionStatePill.test.tsx +25 -0
- package/src/components/shell/SessionStatePill.tsx +46 -0
- 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 +49 -0
- package/src/components/shell/TopBar.test.tsx +36 -0
- package/src/components/shell/TopBar.tsx +89 -0
- package/src/components/shell/index.ts +13 -0
- package/src/components/ui/alert/Alert.stories.tsx +1 -1
- package/src/components/ui/autocomplete/Autocomplete.stories.tsx +1 -1
- package/src/components/ui/avatar/Avatar.stories.tsx +1 -1
- package/src/components/ui/badge/Badge.stories.tsx +1 -1
- package/src/components/ui/breadcrumbs/Breadcrumbs.stories.tsx +1 -1
- package/src/components/ui/button/Button.stories.tsx +1 -1
- package/src/components/ui/card/Card.stories.tsx +1 -1
- package/src/components/ui/checkbox/Checkbox.stories.tsx +1 -1
- package/src/components/ui/chip/Chip.stories.tsx +1 -1
- package/src/components/ui/collapse/Collapse.stories.tsx +1 -1
- package/src/components/ui/data-row/DataRow.stories.tsx +1 -1
- package/src/components/ui/divider/Divider.stories.tsx +1 -1
- package/src/components/ui/drawer/Drawer.stories.tsx +1 -1
- package/src/components/ui/form-field/FormField.stories.tsx +1 -1
- package/src/components/ui/help-tip/HelpTip.stories.tsx +1 -1
- package/src/components/ui/icon-box/IconBox.stories.tsx +1 -1
- package/src/components/ui/index.ts +1 -0
- package/src/components/ui/indicator/Indicator.stories.tsx +38 -2
- package/src/components/ui/indicator/Indicator.test.tsx +20 -1
- package/src/components/ui/indicator/Indicator.tsx +50 -6
- package/src/components/ui/indicator/index.ts +1 -1
- package/src/components/ui/input/Input.stories.tsx +1 -1
- package/src/components/ui/link/Link.stories.tsx +1 -1
- package/src/components/ui/list-item/ListItem.stories.tsx +1 -1
- package/src/components/ui/menu/Menu.stories.tsx +1 -1
- package/src/components/ui/modal/Modal.stories.tsx +1 -1
- package/src/components/ui/pill/Pill.stories.tsx +1 -1
- package/src/components/ui/popover/Popover.stories.tsx +1 -1
- package/src/components/ui/progress/Progress.stories.tsx +1 -1
- package/src/components/ui/radio/Radio.stories.tsx +1 -1
- package/src/components/ui/section/Section.stories.tsx +1 -1
- package/src/components/ui/select/Select.stories.tsx +1 -1
- package/src/components/ui/skeleton/Skeleton.stories.tsx +1 -1
- package/src/components/ui/spinner/Spinner.stories.tsx +1 -1
- package/src/components/ui/spinner/Spinner.tsx +3 -2
- 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/toolbar-button/ToolbarButton.tsx +2 -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 +323 -0
- package/src/theme/Colors.stories.tsx +74 -9
- package/src/theme/barrel.ts +1 -0
- package/src/theme/config.ts +20 -3
- package/src/theme/elevation.stories.tsx +1 -1
- package/src/theme/extracted-colors-dataviz.ts +21 -0
- package/src/theme/extracted-colors-ui.ts +20 -0
- package/src/theme/global.css +69 -53
- package/src/theme/gradients.test.ts +25 -0
- package/src/theme/gradients.ts +36 -0
- package/src/theme/shadows.stories.tsx +1 -1
- package/src/theme/tokens/primitives.test.ts +232 -0
- package/src/theme/tokens/primitives.ts +238 -87
- package/src/theme/tokens/semantic.ts +101 -81
- package/src/theme/workout-tokens.ts +26 -21
- package/src/utils/avatar-color.ts +3 -3
- package/tailwind.config.js +15 -3
|
@@ -1,68 +1,7 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
// round line caps). Rendered as raw `<svg>` DOM elements — the same web-SVG
|
|
6
|
-
// mechanism `Progress.tsx` uses, and the same surface `lucide-react` produced
|
|
7
|
-
// (both are web-only SVG; native icon rendering was never wired here).
|
|
1
|
+
// Workout icons were promoted to the shared icon primitive
|
|
2
|
+
// (`components/icons`). This shim re-exports them so existing Workout consumers
|
|
3
|
+
// keep their local `./icons` import unchanged.
|
|
4
|
+
export { DumbbellIcon, StarIcon } from '../../icons'
|
|
8
5
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
size?: number
|
|
12
|
-
/** Stroke color. */
|
|
13
|
-
color?: string
|
|
14
|
-
/** Fill color; defaults to `none` (outline only). */
|
|
15
|
-
fill?: string
|
|
16
|
-
/** Stroke width. */
|
|
17
|
-
strokeWidth?: number
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/** Dumbbell glyph (mirrors lucide-react `Dumbbell`). Outline-only by default. */
|
|
21
|
-
export function DumbbellIcon({
|
|
22
|
-
size = 24,
|
|
23
|
-
color = 'currentColor',
|
|
24
|
-
fill = 'none',
|
|
25
|
-
strokeWidth = 2,
|
|
26
|
-
}: WorkoutIconProps) {
|
|
27
|
-
return (
|
|
28
|
-
<svg
|
|
29
|
-
width={size}
|
|
30
|
-
height={size}
|
|
31
|
-
viewBox="0 0 24 24"
|
|
32
|
-
fill={fill}
|
|
33
|
-
stroke={color}
|
|
34
|
-
strokeWidth={strokeWidth}
|
|
35
|
-
strokeLinecap="round"
|
|
36
|
-
strokeLinejoin="round"
|
|
37
|
-
>
|
|
38
|
-
<path d="M14.4 14.4 9.6 9.6" />
|
|
39
|
-
<path d="M18.657 21.485a2 2 0 1 1-2.829-2.828l-1.767 1.768a2 2 0 1 1-2.829-2.829l6.364-6.364a2 2 0 1 1 2.829 2.829l-1.768 1.767a2 2 0 1 1 2.828 2.829z" />
|
|
40
|
-
<path d="m21.5 21.5-1.4-1.4" />
|
|
41
|
-
<path d="M3.9 3.9 2.5 2.5" />
|
|
42
|
-
<path d="M6.404 12.768a2 2 0 1 1-2.829-2.829l1.768-1.767a2 2 0 1 1-2.828-2.829l2.828-2.828a2 2 0 1 1 2.829 2.828l1.767-1.768a2 2 0 1 1 2.829 2.829z" />
|
|
43
|
-
</svg>
|
|
44
|
-
)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/** Star glyph (mirrors lucide-react `Star`). Pass `fill` for a solid star. */
|
|
48
|
-
export function StarIcon({
|
|
49
|
-
size = 24,
|
|
50
|
-
color = 'currentColor',
|
|
51
|
-
fill = 'none',
|
|
52
|
-
strokeWidth = 2,
|
|
53
|
-
}: WorkoutIconProps) {
|
|
54
|
-
return (
|
|
55
|
-
<svg
|
|
56
|
-
width={size}
|
|
57
|
-
height={size}
|
|
58
|
-
viewBox="0 0 24 24"
|
|
59
|
-
fill={fill}
|
|
60
|
-
stroke={color}
|
|
61
|
-
strokeWidth={strokeWidth}
|
|
62
|
-
strokeLinecap="round"
|
|
63
|
-
strokeLinejoin="round"
|
|
64
|
-
>
|
|
65
|
-
<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" />
|
|
66
|
-
</svg>
|
|
67
|
-
)
|
|
68
|
-
}
|
|
6
|
+
/** @deprecated Use `IconProps` from `components/icons`. */
|
|
7
|
+
export type { IconProps as WorkoutIconProps } from '../../icons'
|
|
@@ -29,6 +29,7 @@ export { WorkoutPill, type WorkoutPillProps, type WorkoutPillStatus } from './Wo
|
|
|
29
29
|
export {
|
|
30
30
|
VelocityStrip,
|
|
31
31
|
type VelocityStripProps,
|
|
32
|
+
type VelocitySet,
|
|
32
33
|
type VelocityZoneBandProp,
|
|
33
34
|
getVelocityZoneColor,
|
|
34
35
|
getVelocityZoneName,
|
|
@@ -37,10 +38,46 @@ export {
|
|
|
37
38
|
} from './VelocityStrip'
|
|
38
39
|
export { MuscleGroupChip, type MuscleGroupChipProps, type VolumeStatus } from './MuscleGroupChip'
|
|
39
40
|
export { Sparkline, type SparklineProps } from './Sparkline'
|
|
40
|
-
export { SetRow, type SetRowProps, type
|
|
41
|
+
export { SetRow, type SetRowProps, type SetRowState, type SetRowUnit } from './SetRow'
|
|
41
42
|
export { InputBar, type InputBarProps } from './InputBar'
|
|
42
43
|
export { RestTimer, type RestTimerProps } from './RestTimer'
|
|
43
|
-
export {
|
|
44
|
+
export { MetricCell, type MetricCellProps, METRIC_FONT } from './metricText'
|
|
45
|
+
export { SetsRepsLoad, type SetsRepsLoadProps } from './SetsRepsLoad'
|
|
46
|
+
export { SegmentedBar, type SegmentedBarProps, type SegmentedBarSegment } from './SegmentedBar'
|
|
47
|
+
export { paceTone, paceToneColor, type PaceTone } from './paceTone'
|
|
48
|
+
export {
|
|
49
|
+
SegmentedProgressBar,
|
|
50
|
+
type SegmentedProgressBarProps,
|
|
51
|
+
type SegmentedProgressBarSegment,
|
|
52
|
+
} from './SegmentedProgressBar'
|
|
53
|
+
export { MetricTiles, type MetricTilesProps, type MetricTileData } from './MetricTiles'
|
|
54
|
+
export { ScheduleTiles, type ScheduleTilesProps } from './ScheduleTiles'
|
|
55
|
+
export { SetBar, type SetBarProps } from './SetBar'
|
|
56
|
+
export {
|
|
57
|
+
SetStrip,
|
|
58
|
+
type SetStripProps,
|
|
59
|
+
type SetStripSet,
|
|
60
|
+
SET_STRIP_ZONES,
|
|
61
|
+
SET_STRIP_VARIABLE_COLOR,
|
|
62
|
+
velocityZoneColor,
|
|
63
|
+
} from './SetStrip'
|
|
64
|
+
export {
|
|
65
|
+
ExerciseIndicator,
|
|
66
|
+
resolveIndicator,
|
|
67
|
+
INDICATOR_PRECEDENCE,
|
|
68
|
+
type ExerciseIndicatorProps,
|
|
69
|
+
type ExerciseIndicatorKind,
|
|
70
|
+
} from './ExerciseIndicator'
|
|
71
|
+
export { ExerciseHeading, type ExerciseHeadingProps } from './ExerciseHeading'
|
|
72
|
+
export { ExerciseCardHeading, type ExerciseCardHeadingProps } from './ExerciseCardHeading'
|
|
73
|
+
export { ExerciseCard, type ExerciseCardProps } from './ExerciseCard'
|
|
74
|
+
export { SetTableHeader, type SetTableHeaderProps } from './SetTableHeader'
|
|
75
|
+
export {
|
|
76
|
+
SessionHeader,
|
|
77
|
+
type SessionHeaderProps,
|
|
78
|
+
type SessionHeaderPlanEntry,
|
|
79
|
+
} from './SessionHeader'
|
|
80
|
+
export { SessionRail, type SessionRailProps, type SessionRailExercise } from './SessionRail'
|
|
44
81
|
export { SupersetWrapper, type SupersetWrapperProps } from './SupersetWrapper'
|
|
45
82
|
export {
|
|
46
83
|
MesoProgressBar,
|
|
@@ -152,3 +189,19 @@ export type {
|
|
|
152
189
|
WorkoutProgress,
|
|
153
190
|
WorkoutGroup,
|
|
154
191
|
} from './ActiveWorkoutPage'
|
|
192
|
+
export { ZoneTrack } from './ZoneTrack'
|
|
193
|
+
export type {
|
|
194
|
+
ZoneTrackProps,
|
|
195
|
+
ZoneTrackZone,
|
|
196
|
+
ZoneTrackTick,
|
|
197
|
+
ZoneTrackMarker,
|
|
198
|
+
ZoneTrackBand,
|
|
199
|
+
} from './ZoneTrack'
|
|
200
|
+
export { FatigueMeter } from './FatigueMeter'
|
|
201
|
+
export type { FatigueMeterProps } from './FatigueMeter'
|
|
202
|
+
export { VolumeLandmarkBar } from './VolumeLandmarkBar'
|
|
203
|
+
export type { VolumeLandmarkBarProps, VolumeZone } from './VolumeLandmarkBar'
|
|
204
|
+
export { StatusPill, statusPillColor } from './StatusPill'
|
|
205
|
+
export type { StatusPillProps, StatusPillStatus } from './StatusPill'
|
|
206
|
+
export { LiveAuraFrame, liveAuraColor } from './LiveAuraFrame'
|
|
207
|
+
export type { LiveAuraFrameProps, LiveAuraCategory } from './LiveAuraFrame'
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import { View } from 'react-native'
|
|
3
|
+
import { MetricCell } from './metricText'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* `MetricCell` — the shared "segmented metric" typography primitive (Inter · 600 ·
|
|
7
|
+
* letter-spacing 1). Value cells take a primary color, separators a muted one. The
|
|
8
|
+
* single source of the value/separator look shared by SetsRepsLoad and TempoDisplay.
|
|
9
|
+
*/
|
|
10
|
+
const meta: Meta<typeof MetricCell> = {
|
|
11
|
+
title: 'Workout/MetricCell',
|
|
12
|
+
component: MetricCell,
|
|
13
|
+
tags: ['autodocs'],
|
|
14
|
+
parameters: {
|
|
15
|
+
docs: {
|
|
16
|
+
description: {
|
|
17
|
+
component:
|
|
18
|
+
'**Primitive.** The shared Inter·600·letter-spacing-1 value/separator cell. ' +
|
|
19
|
+
'Extracted so the two prescription readouts read as one visual language instead of ' +
|
|
20
|
+
'drifting copies. Used-by ↑ ' +
|
|
21
|
+
'[SetsRepsLoad](?path=/docs/workout-setsrepsload--docs) + ' +
|
|
22
|
+
'[TempoDisplay](?path=/docs/workout-tempodisplay--docs).',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
argTypes: {
|
|
27
|
+
color: { control: 'color' },
|
|
28
|
+
fontSize: { control: { type: 'range', min: 9, max: 24, step: 1 } },
|
|
29
|
+
},
|
|
30
|
+
decorators: [
|
|
31
|
+
(Story) => (
|
|
32
|
+
<View style={{ padding: 20, backgroundColor: '#131313' }}>
|
|
33
|
+
<Story />
|
|
34
|
+
</View>
|
|
35
|
+
),
|
|
36
|
+
],
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export default meta
|
|
40
|
+
type Story = StoryObj<typeof MetricCell>
|
|
41
|
+
|
|
42
|
+
export const ValueCell: Story = {
|
|
43
|
+
args: { children: '145', color: '#F3F4F6', fontSize: 11 },
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const Separator: Story = {
|
|
47
|
+
args: { children: '×', color: '#6B7280', fontSize: 11 },
|
|
48
|
+
parameters: { docs: { description: { story: 'A muted separator cell (× / @ / -).' } } },
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const InContext: Story = {
|
|
52
|
+
render: () => (
|
|
53
|
+
<View style={{ flexDirection: 'row', alignItems: 'baseline' }}>
|
|
54
|
+
<MetricCell color="#F3F4F6">5</MetricCell>
|
|
55
|
+
<MetricCell color="#6B7280"> × </MetricCell>
|
|
56
|
+
<MetricCell color="#F3F4F6">8</MetricCell>
|
|
57
|
+
<MetricCell color="#6B7280"> @ </MetricCell>
|
|
58
|
+
<MetricCell color="#F3F4F6">145</MetricCell>
|
|
59
|
+
<MetricCell color="#6B7280"> lb</MetricCell>
|
|
60
|
+
</View>
|
|
61
|
+
),
|
|
62
|
+
parameters: {
|
|
63
|
+
docs: {
|
|
64
|
+
description: {
|
|
65
|
+
story: 'Value + separator cells composed into the sets × reps @ load language.',
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
|
|
2
|
+
import type { ReactNode } from 'react'
|
|
3
|
+
import { Text } from 'react-native'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Shared "segmented metric" typography primitive — the Inter · 600 · letter-spacing 1
|
|
7
|
+
* value/separator cell used by both {@link TempoDisplay} (tempo digits + dashes) and
|
|
8
|
+
* {@link SetsRepsLoad} (sets × reps @ load). Extracted so the two read as one visual
|
|
9
|
+
* language instead of drifting copies.
|
|
10
|
+
*/
|
|
11
|
+
export const METRIC_FONT = 'Inter, sans-serif'
|
|
12
|
+
|
|
13
|
+
export interface MetricCellProps {
|
|
14
|
+
children: ReactNode
|
|
15
|
+
/** Cell text color — value cells take a primary color, separators a muted one. */
|
|
16
|
+
color: string
|
|
17
|
+
fontSize?: number
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function MetricCell({ children, color, fontSize = 11 }: MetricCellProps) {
|
|
21
|
+
return (
|
|
22
|
+
<Text
|
|
23
|
+
style={{
|
|
24
|
+
fontFamily: METRIC_FONT,
|
|
25
|
+
fontSize,
|
|
26
|
+
color,
|
|
27
|
+
fontWeight: '600',
|
|
28
|
+
letterSpacing: 1,
|
|
29
|
+
}}
|
|
30
|
+
>
|
|
31
|
+
{children}
|
|
32
|
+
</Text>
|
|
33
|
+
)
|
|
34
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { paceTone, paceToneColor } from './paceTone'
|
|
3
|
+
|
|
4
|
+
describe('paceTone', () => {
|
|
5
|
+
it('reads as neutral when there is no target', () => {
|
|
6
|
+
expect(paceTone(0.6)).toBe('neutral')
|
|
7
|
+
expect(paceTone(0)).toBe('neutral')
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
it('reads as ahead when progress equals the target (inclusive)', () => {
|
|
11
|
+
expect(paceTone(0.5, 0.5)).toBe('ahead')
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
it('reads as ahead when progress is past the target', () => {
|
|
15
|
+
expect(paceTone(0.8, 0.5)).toBe('ahead')
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('reads as behind when progress trails the target', () => {
|
|
19
|
+
expect(paceTone(0.3, 0.5)).toBe('behind')
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
describe('paceToneColor', () => {
|
|
23
|
+
it('maps each tone to its real titan literal hex', () => {
|
|
24
|
+
expect(paceToneColor('ahead')).toBe('#2ED573') // status-success (green 300)
|
|
25
|
+
expect(paceToneColor('behind')).toBe('#F9B415') // status-warning (amber 300)
|
|
26
|
+
expect(paceToneColor('neutral')).toBe('#01B5D1') // cyan 400
|
|
27
|
+
})
|
|
28
|
+
})
|
|
29
|
+
})
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
|
|
2
|
+
import { primitiveRamps } from '../../../theme/tokens/primitives'
|
|
3
|
+
import { getSemanticColors } from '../../../theme/tokens/semantic'
|
|
4
|
+
|
|
5
|
+
/** How progress compares to its target pace: `ahead`, `behind`, or `neutral` (no target). */
|
|
6
|
+
export type PaceTone = 'ahead' | 'behind' | 'neutral'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Classify `progress` (0..1) against an optional `target` (0..1). No target reads
|
|
10
|
+
* as `neutral`; at or past target is `ahead`, otherwise `behind`.
|
|
11
|
+
*/
|
|
12
|
+
export function paceTone(progress: number, target?: number): PaceTone {
|
|
13
|
+
if (target === undefined) return 'neutral'
|
|
14
|
+
return progress >= target ? 'ahead' : 'behind'
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Literal-hex fill for each tone (RNW-safe): success · warning · cyan pin. */
|
|
18
|
+
const PACE_TONE_COLORS: Record<PaceTone, string> = {
|
|
19
|
+
ahead: getSemanticColors('dark')['status-success'],
|
|
20
|
+
behind: getSemanticColors('dark')['status-warning'],
|
|
21
|
+
neutral: primitiveRamps.cyan[400],
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** The literal-hex colour for a tone. */
|
|
25
|
+
export function paceToneColor(tone: PaceTone): string {
|
|
26
|
+
return PACE_TONE_COLORS[tone]
|
|
27
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared kit for the S3 Session-Rail explorations (NOT a stories file — imported by the sheets under
|
|
3
|
+
* `Custom/Workout/Explorations/*`). Throwaway CSS R&D at the real 232px rail width, on real titan tokens.
|
|
4
|
+
* When these decisions harden they move into the real `ExerciseCard` + a titan `SessionRail` organism.
|
|
5
|
+
*/
|
|
6
|
+
import type { ReactNode } from 'react'
|
|
7
|
+
import { TempoDisplay } from './TempoDisplay'
|
|
8
|
+
|
|
9
|
+
// ---- geometry
|
|
10
|
+
export const RAIL_W = 232
|
|
11
|
+
export const SET_GAP = 5
|
|
12
|
+
|
|
13
|
+
// ---- surface + text tokens (dark theme; charcoal + neutral ramps)
|
|
14
|
+
export const GREY = '#2C2C2C' // charcoal 300 — upcoming/placeholder
|
|
15
|
+
export const BORDER_SUBTLE = '#1C1C1C' // charcoal 500 — row dividers
|
|
16
|
+
export const T_PRIMARY = '#F3F4F6' // neutral 100
|
|
17
|
+
export const T_SECONDARY = '#9CA3AF' // neutral 400
|
|
18
|
+
export const T_TERTIARY = '#6B7280' // neutral 500
|
|
19
|
+
export const RAISED = '#1F1F1F' // charcoal 400 — nav + heading raised plane
|
|
20
|
+
export const INSET = '#131313' // charcoal 800 — sunk exercise-list column
|
|
21
|
+
// depth lives on the sunk list: inner shadow from the heading (top) + nav (left). Raised plane is flat.
|
|
22
|
+
export const INSET_SHADOW =
|
|
23
|
+
'inset 0 9px 12px -6px rgba(0,0,0,0.85), inset 7px 0 10px -6px rgba(0,0,0,0.7), inset 0 -1px 0 rgba(255,255,255,0.02)'
|
|
24
|
+
|
|
25
|
+
// ---- zone colors = real titan ramp pins (primitiveRamps)
|
|
26
|
+
export const RED = '#D14343' // red 600
|
|
27
|
+
export const ORANGE = '#FF7900' // orange 400
|
|
28
|
+
export const AMBER = '#F9B415' // amber 300
|
|
29
|
+
export const GREEN = '#2ED573' // green 300
|
|
30
|
+
export const velColor = (v: number): string => (v < 0.5 ? RED : v < 0.75 ? ORANGE : v < 1.0 ? AMBER : GREEN)
|
|
31
|
+
|
|
32
|
+
// active-set pulse: each segment eases along its own ramp (adjacent lighter/darker steps) through the pin.
|
|
33
|
+
export const PULSE_CSS = `
|
|
34
|
+
@keyframes s3pRed{0%,100%{background:#E05254}50%{background:#A4221C}}
|
|
35
|
+
@keyframes s3pOrange{0%,100%{background:#FFA063}50%{background:#DA5F00}}
|
|
36
|
+
@keyframes s3pAmber{0%,100%{background:#FFD352}50%{background:#E08C00}}
|
|
37
|
+
@keyframes s3pGreen{0%,100%{background:#58F69E}50%{background:#21C05D}}`
|
|
38
|
+
export const pulseAnim = (c: string): string | undefined => {
|
|
39
|
+
const name = c === RED ? 's3pRed' : c === ORANGE ? 's3pOrange' : c === AMBER ? 's3pAmber' : c === GREEN ? 's3pGreen' : null
|
|
40
|
+
return name ? `${name} 1.9s ease-in-out infinite` : undefined
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ---- sets/reps/load in the TempoDisplay visual language (Inter · 600 · letter-spacing 1 · gray separators)
|
|
44
|
+
const INTER = 'Inter, sans-serif'
|
|
45
|
+
const SRL_SEP = '#6B7280'
|
|
46
|
+
function SRLCell({ children, color = T_PRIMARY }: { children: ReactNode; color?: string }) {
|
|
47
|
+
return <span style={{ fontFamily: INTER, fontSize: 11, fontWeight: 600, letterSpacing: 1, color }}>{children}</span>
|
|
48
|
+
}
|
|
49
|
+
export function SetsRepsLoad({ sets, reps, load, unit = 'lb' }: { sets: number; reps: number | string; load: number; unit?: string }) {
|
|
50
|
+
return (
|
|
51
|
+
<span style={{ display: 'inline-flex', alignItems: 'baseline', whiteSpace: 'nowrap' }}>
|
|
52
|
+
<SRLCell>{sets}</SRLCell>
|
|
53
|
+
<SRLCell color={SRL_SEP}> × </SRLCell>
|
|
54
|
+
<SRLCell>{reps}</SRLCell>
|
|
55
|
+
<SRLCell color={SRL_SEP}> @ </SRLCell>
|
|
56
|
+
<SRLCell>{load}</SRLCell>
|
|
57
|
+
<SRLCell color={SRL_SEP}> {unit}</SRLCell>
|
|
58
|
+
</span>
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ---- per-set bar: one continuous bar, rep intensities as butted segments (no gaps); active segments pulse
|
|
63
|
+
export type SetState = { status: 'done'; reps: number[] } | { status: 'active'; reps: number[]; planned: number } | { status: 'todo'; planned: number }
|
|
64
|
+
export function setColors(s: SetState): string[] {
|
|
65
|
+
if (s.status === 'todo') return [GREY]
|
|
66
|
+
if (s.status === 'done') return s.reps.map(velColor)
|
|
67
|
+
return [...s.reps.map(velColor), ...Array(Math.max(0, s.planned - s.reps.length)).fill(GREY)]
|
|
68
|
+
}
|
|
69
|
+
function SetBar({ colors, h, pulse }: { colors: string[]; h: number; pulse?: boolean }) {
|
|
70
|
+
return (
|
|
71
|
+
<div style={{ display: 'flex', flex: 1, minWidth: 0, height: h, borderRadius: 2, overflow: 'hidden' }}>
|
|
72
|
+
{colors.map((c, i) => (
|
|
73
|
+
<div key={i} style={{ flex: 1, height: '100%', background: c, animation: pulse && c !== GREY ? pulseAnim(c) : undefined }} />
|
|
74
|
+
))}
|
|
75
|
+
</div>
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
export function SetStrip({ sets, h }: { sets: SetState[]; h: number }) {
|
|
79
|
+
return (
|
|
80
|
+
<div style={{ display: 'flex', width: '100%', height: h, gap: SET_GAP }}>
|
|
81
|
+
{sets.map((s, i) => (
|
|
82
|
+
<SetBar key={i} colors={setColors(s)} h={h} pulse={s.status === 'active'} />
|
|
83
|
+
))}
|
|
84
|
+
</div>
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function Indicator({ kind }: { kind: 'pr' | 'issue' | 'info' }) {
|
|
89
|
+
const map = { pr: ['★', ORANGE], issue: ['!', RED], info: ['i', '#2196F3'] } as const
|
|
90
|
+
const [glyph, color] = map[kind]
|
|
91
|
+
return (
|
|
92
|
+
<span style={{ width: 16, height: 16, borderRadius: 8, border: `1px solid ${color}`, color, fontSize: 10, fontWeight: 800, display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}>{glyph}</span>
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const labelStyle = { fontSize: 14, fontWeight: 800, color: T_PRIMARY } as const
|
|
97
|
+
|
|
98
|
+
// ---- the finalized heading unit
|
|
99
|
+
export type Exercise = { name: string; sets: number; reps: number | string; load: number; tempo: [number, number, number, number]; indicator?: 'pr' | 'issue' | 'info'; setStates: SetState[]; upcoming?: boolean }
|
|
100
|
+
export function Heading({ ex, stripH }: { ex: Exercise; stripH: number }) {
|
|
101
|
+
return (
|
|
102
|
+
<div style={{ padding: '9px 12px', display: 'flex', flexDirection: 'column', opacity: ex.upcoming ? 0.55 : 1 }}>
|
|
103
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
|
104
|
+
<span style={labelStyle}>{ex.name}</span>
|
|
105
|
+
<div style={{ flex: 1 }} />
|
|
106
|
+
{ex.indicator && <Indicator kind={ex.indicator} />}
|
|
107
|
+
</div>
|
|
108
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 1 }}>
|
|
109
|
+
<SetsRepsLoad sets={ex.sets} reps={ex.reps} load={ex.load} />
|
|
110
|
+
<div style={{ flex: 1 }} />
|
|
111
|
+
<TempoDisplay tempo={ex.tempo} size="sm" showLabel={false} showInfo={false} />
|
|
112
|
+
</div>
|
|
113
|
+
<div style={{ marginTop: 7 }}>
|
|
114
|
+
<SetStrip sets={ex.setStates} h={stripH} />
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ---- sample session (per-rep mean velocities; sets fatigue across the workout, reps decay in-set)
|
|
121
|
+
export const reps = (n: number, start: number, span = 0.4): number[] => Array.from({ length: n }, (_, r) => +(start - (span * r) / Math.max(1, n - 1)).toFixed(3))
|
|
122
|
+
export const SESSION: Exercise[] = [
|
|
123
|
+
{ name: 'Seated Cable Row', sets: 5, reps: 8, load: 145, tempo: [3, 1, 2, 0], indicator: 'pr', setStates: [1.0, 0.9, 0.8, 0.7, 0.6].map((s) => ({ status: 'done', reps: reps(8, s) })) },
|
|
124
|
+
{ name: 'Incline DB Press', sets: 3, reps: 8, load: 70, tempo: [3, 0, 3, 0], setStates: [0.72, 0.62, 0.54].map((s) => ({ status: 'done', reps: reps(8, s) })) },
|
|
125
|
+
{ name: 'Cable Chest Press', sets: 3, reps: 10, load: 90, tempo: [2, 1, 2, 0], indicator: 'info', setStates: [{ status: 'done', reps: reps(10, 0.72) }, { status: 'active', reps: reps(5, 0.62), planned: 10 }, { status: 'todo', planned: 10 }] },
|
|
126
|
+
{ name: 'Standing Calf Raise', sets: 5, reps: 20, load: 25, tempo: [2, 1, 2, 0], upcoming: true, setStates: [1, 2, 3, 4, 5].map(() => ({ status: 'todo', planned: 20 })) },
|
|
127
|
+
{ name: 'Face Pull', sets: 3, reps: '15-20', load: 40, tempo: [2, 1, 2, 0], upcoming: true, setStates: [1, 2, 3].map(() => ({ status: 'todo', planned: 18 })) },
|
|
128
|
+
]
|
|
129
|
+
|
|
130
|
+
// ---- shell chrome (neumorphic): flat raised nav + heading plane; sunk inset exercise list
|
|
131
|
+
export function NavStub() {
|
|
132
|
+
return (
|
|
133
|
+
<div style={{ width: 60, alignSelf: 'stretch', position: 'relative', zIndex: 2, background: RAISED, display: 'flex', flexDirection: 'column', alignItems: 'center', paddingTop: 12, gap: 8 }}>
|
|
134
|
+
{['Live', 'Plan', 'Body'].map((t) => (
|
|
135
|
+
<span key={t} style={{ fontSize: 8, color: T_TERTIARY, fontWeight: 700 }}>{t}</span>
|
|
136
|
+
))}
|
|
137
|
+
</div>
|
|
138
|
+
)
|
|
139
|
+
}
|
|
140
|
+
export function Rail({ stripH, footer }: { stripH: number; footer?: ReactNode }) {
|
|
141
|
+
return (
|
|
142
|
+
<div style={{ width: 246, background: INSET, display: 'flex', flexDirection: 'column' }}>
|
|
143
|
+
<div style={{ position: 'relative', zIndex: 2, padding: '11px 12px 10px', background: RAISED }}>
|
|
144
|
+
<div style={{ fontSize: 9, fontWeight: 800, letterSpacing: 1, textTransform: 'uppercase', color: T_TERTIARY, display: 'flex', alignItems: 'center', gap: 5 }}>
|
|
145
|
+
<span style={{ width: 6, height: 6, borderRadius: 3, background: GREEN }} />
|
|
146
|
+
Live session
|
|
147
|
+
</div>
|
|
148
|
+
<div style={{ fontSize: 14, fontWeight: 800, marginTop: 3, color: T_PRIMARY }}>Pull A · Intensification</div>
|
|
149
|
+
<div style={{ fontSize: 10, color: T_SECONDARY, marginTop: 2, fontFamily: 'monospace' }}>ex 3/5 · set 2 live</div>
|
|
150
|
+
</div>
|
|
151
|
+
<div style={{ flex: 1, background: INSET, boxShadow: INSET_SHADOW }}>
|
|
152
|
+
{SESSION.map((ex, i) => (
|
|
153
|
+
<div key={i} style={{ borderBottom: i < SESSION.length - 1 ? `1px solid ${BORDER_SUBTLE}` : undefined }}>
|
|
154
|
+
<Heading ex={ex} stripH={stripH} />
|
|
155
|
+
</div>
|
|
156
|
+
))}
|
|
157
|
+
</div>
|
|
158
|
+
{footer}
|
|
159
|
+
</div>
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ---- page scaffold shared by the sheets
|
|
164
|
+
export function Page({ title, note, children }: { title: string; note: string; children: ReactNode }) {
|
|
165
|
+
return (
|
|
166
|
+
<div style={{ minHeight: '100vh', background: '#0A0A0A', color: T_PRIMARY, padding: 28, display: 'flex', flexDirection: 'column', gap: 22, fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif' }}>
|
|
167
|
+
<style>{PULSE_CSS}</style>
|
|
168
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
|
|
169
|
+
<span style={{ fontSize: 20, fontWeight: 800 }}>{title}</span>
|
|
170
|
+
<span style={{ fontSize: 12, color: T_SECONDARY, maxWidth: 820 }}>{note}</span>
|
|
171
|
+
</div>
|
|
172
|
+
{children}
|
|
173
|
+
</div>
|
|
174
|
+
)
|
|
175
|
+
}
|
|
176
|
+
export const sectionTitle = { fontSize: 11, fontWeight: 800, letterSpacing: 1.2, textTransform: 'uppercase', color: ORANGE } as const
|
|
177
|
+
export const monoTag = { fontFamily: 'monospace', fontSize: 11, color: T_SECONDARY } as const
|
|
@@ -3,7 +3,7 @@ import { View, Text } from 'react-native'
|
|
|
3
3
|
import { Stepper, Step, StepIndicator, StepLabel, StepContent } from './Stepper'
|
|
4
4
|
|
|
5
5
|
const meta: Meta<typeof Stepper> = {
|
|
6
|
-
title: '
|
|
6
|
+
title: 'Components/Molecules/Stepper',
|
|
7
7
|
component: Stepper,
|
|
8
8
|
tags: ['autodocs'],
|
|
9
9
|
argTypes: {
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
export interface IconProps {
|
|
4
|
+
/** Rendered width/height in px (viewBox is fixed 24×24). */
|
|
5
|
+
size?: number
|
|
6
|
+
/** Stroke color (or fill, for solid icons). Defaults to `currentColor` so a wrapper's text token drives it. */
|
|
7
|
+
color?: string
|
|
8
|
+
/** Stroke width for outline icons. */
|
|
9
|
+
strokeWidth?: number
|
|
10
|
+
/** Fill color; defaults to `none` (outline). */
|
|
11
|
+
fill?: string
|
|
12
|
+
/** Accessible title — sets `role="img"` + `<title>`. Omit for a decorative icon (hidden from AT). */
|
|
13
|
+
title?: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface SvgIconProps extends IconProps {
|
|
17
|
+
children: ReactNode
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Base SVG wrapper for the titan icon set — a 24×24 web-`<svg>` with the shared
|
|
22
|
+
* accessibility contract (titled → `role="img"` + `<title>`; untitled →
|
|
23
|
+
* `aria-hidden`) and `currentColor` inheritance. Icons pass their paths as
|
|
24
|
+
* children plus their stroke/fill defaults.
|
|
25
|
+
*/
|
|
26
|
+
export function SvgIcon({
|
|
27
|
+
size = 24,
|
|
28
|
+
color = 'currentColor',
|
|
29
|
+
strokeWidth = 2,
|
|
30
|
+
fill = 'none',
|
|
31
|
+
title,
|
|
32
|
+
children,
|
|
33
|
+
}: SvgIconProps) {
|
|
34
|
+
return (
|
|
35
|
+
<svg
|
|
36
|
+
width={size}
|
|
37
|
+
height={size}
|
|
38
|
+
viewBox="0 0 24 24"
|
|
39
|
+
fill={fill}
|
|
40
|
+
stroke={color}
|
|
41
|
+
strokeWidth={strokeWidth}
|
|
42
|
+
strokeLinecap="round"
|
|
43
|
+
strokeLinejoin="round"
|
|
44
|
+
{...(title ? { role: 'img', 'aria-label': title } : { 'aria-hidden': true })}
|
|
45
|
+
>
|
|
46
|
+
{title ? <title>{title}</title> : null}
|
|
47
|
+
{children}
|
|
48
|
+
</svg>
|
|
49
|
+
)
|
|
50
|
+
}
|