@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
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Row Inventory — a READ-ONLY survey of every existing way the Workout library renders
|
|
3
|
+
* "an exercise / set / workout / week / mesocycle" as a row / list-entry / card.
|
|
4
|
+
*
|
|
5
|
+
* Built from the 2026-07-08 duplication audit (S3 SessionRail reuse question). This pane only
|
|
6
|
+
* NAMES and DISPLAYS what exists — no extraction, no changes to the components. It's here so we
|
|
7
|
+
* can eyeball the overlapping row representations together before deciding a reuse strategy.
|
|
8
|
+
*/
|
|
9
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
10
|
+
import { Component, type ReactNode } from 'react'
|
|
11
|
+
import { View, Text } from 'react-native'
|
|
12
|
+
import { ExerciseCard } from './ExerciseCard'
|
|
13
|
+
import { SetRow } from './SetRow'
|
|
14
|
+
import { WorkoutCard } from './WorkoutCard'
|
|
15
|
+
import { WorkoutPill } from './WorkoutPill'
|
|
16
|
+
import { WeekRow } from './WeekRow'
|
|
17
|
+
import { MesoCard } from './MesoCard'
|
|
18
|
+
import { MesoProgressBar } from './MesoProgressBar'
|
|
19
|
+
import { MuscleGroupChip } from './MuscleGroupChip'
|
|
20
|
+
import { StatusDot } from './StatusDot'
|
|
21
|
+
import { WeightBadge } from './WeightBadge'
|
|
22
|
+
import { PrBadge } from './PrBadge'
|
|
23
|
+
import { PlaceholderStrip } from './PlaceholderStrip'
|
|
24
|
+
import { SupersetWrapper } from './SupersetWrapper'
|
|
25
|
+
|
|
26
|
+
// ----------------------------------------------------------------------------- gallery scaffold
|
|
27
|
+
class Boundary extends Component<{ children: ReactNode }, { err: string | null }> {
|
|
28
|
+
state = { err: null as string | null }
|
|
29
|
+
static getDerivedStateFromError(e: Error) {
|
|
30
|
+
return { err: e.message }
|
|
31
|
+
}
|
|
32
|
+
render() {
|
|
33
|
+
if (this.state.err)
|
|
34
|
+
return (
|
|
35
|
+
<div style={{ color: '#ff6b6b', fontFamily: 'monospace', fontSize: 11, padding: 8 }}>
|
|
36
|
+
render error: {this.state.err}
|
|
37
|
+
</div>
|
|
38
|
+
)
|
|
39
|
+
return this.props.children
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function Cell({
|
|
44
|
+
name,
|
|
45
|
+
entity,
|
|
46
|
+
expand,
|
|
47
|
+
width = 340,
|
|
48
|
+
children,
|
|
49
|
+
}: {
|
|
50
|
+
name: string
|
|
51
|
+
entity: string
|
|
52
|
+
expand: string
|
|
53
|
+
width?: number
|
|
54
|
+
children: ReactNode
|
|
55
|
+
}) {
|
|
56
|
+
return (
|
|
57
|
+
<div
|
|
58
|
+
style={{
|
|
59
|
+
width,
|
|
60
|
+
display: 'flex',
|
|
61
|
+
flexDirection: 'column',
|
|
62
|
+
gap: 8,
|
|
63
|
+
padding: 14,
|
|
64
|
+
borderRadius: 12,
|
|
65
|
+
border: '1px solid #24262d',
|
|
66
|
+
background: '#131418',
|
|
67
|
+
}}
|
|
68
|
+
>
|
|
69
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
|
70
|
+
<span style={{ fontSize: 13, fontWeight: 800, color: '#E8EAED' }}>{name}</span>
|
|
71
|
+
<span
|
|
72
|
+
style={{ fontFamily: 'monospace', fontSize: 10, color: '#5b616d', letterSpacing: 0.3 }}
|
|
73
|
+
>
|
|
74
|
+
{entity} · {expand}
|
|
75
|
+
</span>
|
|
76
|
+
</div>
|
|
77
|
+
<div style={{ marginTop: 2 }}>
|
|
78
|
+
<Boundary>{children}</Boundary>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function Section({ title, note, children }: { title: string; note?: string; children: ReactNode }) {
|
|
85
|
+
return (
|
|
86
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
|
87
|
+
<div style={{ display: 'flex', alignItems: 'baseline', gap: 10 }}>
|
|
88
|
+
<span
|
|
89
|
+
style={{
|
|
90
|
+
fontSize: 11,
|
|
91
|
+
fontWeight: 800,
|
|
92
|
+
letterSpacing: 1.2,
|
|
93
|
+
textTransform: 'uppercase',
|
|
94
|
+
color: '#FF7900',
|
|
95
|
+
}}
|
|
96
|
+
>
|
|
97
|
+
{title}
|
|
98
|
+
</span>
|
|
99
|
+
{note && <span style={{ fontSize: 11, color: '#8D95A3' }}>{note}</span>}
|
|
100
|
+
</div>
|
|
101
|
+
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 14, alignItems: 'flex-start' }}>
|
|
102
|
+
{children}
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ----------------------------------------------------------------------------- sample data
|
|
109
|
+
const setVel = [
|
|
110
|
+
[0.6, 0.58, 0.55, 0.52],
|
|
111
|
+
[0.58, 0.55, 0.53, 0.5],
|
|
112
|
+
[0.55, 0.52, 0.5, 0.47],
|
|
113
|
+
]
|
|
114
|
+
const exSets = [1, 2, 3, 4].map((n) => ({
|
|
115
|
+
state: 'done' as const,
|
|
116
|
+
setNumber: n,
|
|
117
|
+
reps: 8,
|
|
118
|
+
weight: 145,
|
|
119
|
+
rpe: 6.5 + n * 0.5,
|
|
120
|
+
unit: 'lbs' as const,
|
|
121
|
+
velocities: setVel[(n - 1) % 3],
|
|
122
|
+
}))
|
|
123
|
+
const weeks = [
|
|
124
|
+
{
|
|
125
|
+
weekNumber: 1,
|
|
126
|
+
workouts: [
|
|
127
|
+
{ name: 'Upper', status: 'completed' as const },
|
|
128
|
+
{ name: 'Lower', status: 'completed' as const },
|
|
129
|
+
],
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
weekNumber: 2,
|
|
133
|
+
workouts: [
|
|
134
|
+
{ name: 'Upper', status: 'current' as const },
|
|
135
|
+
{ name: 'Lower', status: 'upcoming' as const },
|
|
136
|
+
],
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
weekNumber: 3,
|
|
140
|
+
workouts: [
|
|
141
|
+
{ name: 'Upper', status: 'upcoming' as const },
|
|
142
|
+
{ name: 'Lower', status: 'upcoming' as const },
|
|
143
|
+
],
|
|
144
|
+
},
|
|
145
|
+
]
|
|
146
|
+
const mesos = [
|
|
147
|
+
{ id: 'm1', name: 'Accumulation', weekCount: 4, status: 'completed' as const },
|
|
148
|
+
{ id: 'm2', name: 'Intensification', weekCount: 3, status: 'current' as const, currentWeek: 2 },
|
|
149
|
+
{ id: 'm3', name: 'Peak', weekCount: 2, status: 'upcoming' as const },
|
|
150
|
+
]
|
|
151
|
+
|
|
152
|
+
// ----------------------------------------------------------------------------- the pane
|
|
153
|
+
const meta: Meta = {
|
|
154
|
+
title: 'Lab/Audits/Row Inventory',
|
|
155
|
+
parameters: { layout: 'fullscreen' },
|
|
156
|
+
}
|
|
157
|
+
export default meta
|
|
158
|
+
type Story = StoryObj
|
|
159
|
+
|
|
160
|
+
export const AllRows: Story = {
|
|
161
|
+
name: 'All row representations',
|
|
162
|
+
render: () => (
|
|
163
|
+
<div
|
|
164
|
+
style={{
|
|
165
|
+
minHeight: '100vh',
|
|
166
|
+
background: '#0c0d0f',
|
|
167
|
+
padding: 28,
|
|
168
|
+
display: 'flex',
|
|
169
|
+
flexDirection: 'column',
|
|
170
|
+
gap: 34,
|
|
171
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
172
|
+
}}
|
|
173
|
+
>
|
|
174
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
|
|
175
|
+
<span style={{ fontSize: 20, fontWeight: 800, color: '#E8EAED' }}>
|
|
176
|
+
Workout library · row representations
|
|
177
|
+
</span>
|
|
178
|
+
<span style={{ fontSize: 12, color: '#8D95A3', maxWidth: 720 }}>
|
|
179
|
+
Every existing component that draws an exercise / set / workout / week / meso as a list
|
|
180
|
+
entry. Read-only survey for the S3 reuse decision — names + entity + expand behavior, no
|
|
181
|
+
code changes.
|
|
182
|
+
</span>
|
|
183
|
+
</div>
|
|
184
|
+
|
|
185
|
+
<Section
|
|
186
|
+
title="Exercise"
|
|
187
|
+
note="3 representations of the one ExerciseCard: collapsed glance, expanded body, upcoming"
|
|
188
|
+
>
|
|
189
|
+
<Cell name="ExerciseCard" entity="exercise" expand="collapsed · expandable">
|
|
190
|
+
<ExerciseCard
|
|
191
|
+
name="Seated Cable Row"
|
|
192
|
+
summary={{ sets: 4, reps: 8, weight: 145, unit: 'lbs' }}
|
|
193
|
+
isPR
|
|
194
|
+
totalPlannedSets={5}
|
|
195
|
+
setVelocities={setVel}
|
|
196
|
+
/>
|
|
197
|
+
</Cell>
|
|
198
|
+
<Cell name="ExerciseCard" entity="exercise" expand="expanded → reveals SetRows">
|
|
199
|
+
<ExerciseCard
|
|
200
|
+
name="Seated Cable Row"
|
|
201
|
+
defaultExpanded
|
|
202
|
+
summary={{ sets: 4, reps: 8, weight: 145, unit: 'lbs' }}
|
|
203
|
+
tempo={[3, 1, 2, 0]}
|
|
204
|
+
sets={exSets}
|
|
205
|
+
/>
|
|
206
|
+
</Cell>
|
|
207
|
+
<Cell name="ExerciseCard" entity="exercise" expand="upcoming (static)">
|
|
208
|
+
<ExerciseCard
|
|
209
|
+
name="Face Pull"
|
|
210
|
+
upcoming
|
|
211
|
+
prescription="3 × 12-15 @ RPE 7"
|
|
212
|
+
previousBest="15 × 35 lb"
|
|
213
|
+
/>
|
|
214
|
+
</Cell>
|
|
215
|
+
</Section>
|
|
216
|
+
|
|
217
|
+
<Section
|
|
218
|
+
title="Set"
|
|
219
|
+
note="SetRow is the primitive — one row per set lifecycle state (done / live / todo)"
|
|
220
|
+
>
|
|
221
|
+
<Cell name="SetRow" entity="set · done" expand="no · flat mini strip" width={360}>
|
|
222
|
+
<SetRow
|
|
223
|
+
state="done"
|
|
224
|
+
setNumber={1}
|
|
225
|
+
reps={8}
|
|
226
|
+
weight={145}
|
|
227
|
+
rpe={8}
|
|
228
|
+
unit="lbs"
|
|
229
|
+
velocities={[0.6, 0.58, 0.55, 0.52]}
|
|
230
|
+
/>
|
|
231
|
+
</Cell>
|
|
232
|
+
<Cell name="SetRow" entity="set · live" expand="no · compact spotlight" width={360}>
|
|
233
|
+
<SetRow
|
|
234
|
+
state="live"
|
|
235
|
+
setNumber={2}
|
|
236
|
+
unit="lbs"
|
|
237
|
+
target={{ reps: 8, weight: 145 }}
|
|
238
|
+
reps={8}
|
|
239
|
+
weight={145}
|
|
240
|
+
velocities={[0.58, 0.55, 0.53, 0.5]}
|
|
241
|
+
/>
|
|
242
|
+
</Cell>
|
|
243
|
+
<Cell name="SetRow" entity="set · todo" expand="no · grey stub strip" width={360}>
|
|
244
|
+
<SetRow state="todo" setNumber={4} unit="lbs" target={{ reps: 8, weight: 145 }} />
|
|
245
|
+
</Cell>
|
|
246
|
+
</Section>
|
|
247
|
+
|
|
248
|
+
<Section
|
|
249
|
+
title="Workout"
|
|
250
|
+
note="WorkoutCard (full) and WorkoutPill (chip) = two legit densities; ExerciseDetailPage overloads ExerciseCard as a 3rd"
|
|
251
|
+
>
|
|
252
|
+
<Cell
|
|
253
|
+
name="WorkoutCard"
|
|
254
|
+
entity="workout"
|
|
255
|
+
expand="expandable · expanded+onToggle → ExerciseCards"
|
|
256
|
+
width={380}
|
|
257
|
+
>
|
|
258
|
+
<WorkoutCard
|
|
259
|
+
name="Push Day A"
|
|
260
|
+
date="Mon · Jul 6"
|
|
261
|
+
duration="52 min"
|
|
262
|
+
status="today"
|
|
263
|
+
muscleGroups={[
|
|
264
|
+
{ group: 'chest', label: 'Chest', volumeStatus: 'productive' },
|
|
265
|
+
{ group: 'delts', label: 'Delts', volumeStatus: 'maintenance' },
|
|
266
|
+
{ group: 'triceps', label: 'Triceps', volumeStatus: 'under' },
|
|
267
|
+
]}
|
|
268
|
+
totalSets={18}
|
|
269
|
+
totalVolume={7340}
|
|
270
|
+
unit="lbs"
|
|
271
|
+
onToggle={() => {}}
|
|
272
|
+
/>
|
|
273
|
+
</Cell>
|
|
274
|
+
<Cell name="WorkoutPill" entity="workout · chip" expand="no">
|
|
275
|
+
<View style={{ flexDirection: 'row', gap: 8, flexWrap: 'wrap' }}>
|
|
276
|
+
<WorkoutPill name="Push" status="completed" />
|
|
277
|
+
<WorkoutPill name="Pull" status="current" />
|
|
278
|
+
<WorkoutPill name="Legs" status="upcoming" />
|
|
279
|
+
<WorkoutPill name="Deload" status="deload" />
|
|
280
|
+
</View>
|
|
281
|
+
</Cell>
|
|
282
|
+
</Section>
|
|
283
|
+
|
|
284
|
+
<Section
|
|
285
|
+
title="Week"
|
|
286
|
+
note="WeekRow is the primitive; ExerciseDetailPage overloads ExerciseCard as a competing week row"
|
|
287
|
+
>
|
|
288
|
+
<Cell
|
|
289
|
+
name="WeekRow"
|
|
290
|
+
entity="week"
|
|
291
|
+
expand="no · row of WorkoutPills + intensity bar"
|
|
292
|
+
width={380}
|
|
293
|
+
>
|
|
294
|
+
<WeekRow
|
|
295
|
+
weekNumber={3}
|
|
296
|
+
totalWeeks={4}
|
|
297
|
+
isCurrent
|
|
298
|
+
intensityLevel={0.55}
|
|
299
|
+
intensityThreshold={0.8}
|
|
300
|
+
workouts={weeks[1].workouts}
|
|
301
|
+
/>
|
|
302
|
+
</Cell>
|
|
303
|
+
</Section>
|
|
304
|
+
|
|
305
|
+
<Section
|
|
306
|
+
title="Mesocycle"
|
|
307
|
+
note="3 views for different jobs (list card / timeline / detail) — each with its own status enum"
|
|
308
|
+
>
|
|
309
|
+
<Cell
|
|
310
|
+
name="MesoCard"
|
|
311
|
+
entity="meso"
|
|
312
|
+
expand="expandable · expanded+onToggle → WeekRows"
|
|
313
|
+
width={380}
|
|
314
|
+
>
|
|
315
|
+
<MesoCard
|
|
316
|
+
name="Accumulation"
|
|
317
|
+
goal="Hypertrophy"
|
|
318
|
+
split="Upper/Lower"
|
|
319
|
+
weekRange="Weeks 1-4"
|
|
320
|
+
currentWeek={2}
|
|
321
|
+
totalWeeks={4}
|
|
322
|
+
weeks={weeks}
|
|
323
|
+
volumeHeatmap={[
|
|
324
|
+
{ group: 'chest', percentage: 80 },
|
|
325
|
+
{ group: 'back', percentage: 70 },
|
|
326
|
+
{ group: 'legs', percentage: 90 },
|
|
327
|
+
]}
|
|
328
|
+
/>
|
|
329
|
+
</Cell>
|
|
330
|
+
<Cell name="MesoProgressBar" entity="meso · timeline" expand="no · segments" width={380}>
|
|
331
|
+
<MesoProgressBar mesos={mesos} />
|
|
332
|
+
</Cell>
|
|
333
|
+
</Section>
|
|
334
|
+
|
|
335
|
+
<Section
|
|
336
|
+
title="Shared sub-parts"
|
|
337
|
+
note="appear inside the rows above — not entity rows themselves"
|
|
338
|
+
>
|
|
339
|
+
<Cell name="MuscleGroupChip" entity="muscle-group" expand="no">
|
|
340
|
+
<View style={{ flexDirection: 'row', gap: 8, flexWrap: 'wrap' }}>
|
|
341
|
+
<MuscleGroupChip name="Chest" volumeStatus="ontrack" />
|
|
342
|
+
<MuscleGroupChip name="Quads" volumeStatus="behind" />
|
|
343
|
+
<MuscleGroupChip name="Lats" volumeStatus="over" />
|
|
344
|
+
</View>
|
|
345
|
+
</Cell>
|
|
346
|
+
<Cell name="StatusDot" entity="status glyph" expand="no">
|
|
347
|
+
<StatusDot variant="success" label="On track" />
|
|
348
|
+
</Cell>
|
|
349
|
+
<Cell name="WeightBadge" entity="badge" expand="no">
|
|
350
|
+
<View style={{ flexDirection: 'row', gap: 8 }}>
|
|
351
|
+
<WeightBadge value={205} unit="lbs" />
|
|
352
|
+
<WeightBadge value={217} unit="lbs" isPr />
|
|
353
|
+
</View>
|
|
354
|
+
</Cell>
|
|
355
|
+
<Cell name="PrBadge" entity="badge" expand="no">
|
|
356
|
+
<PrBadge type="e1rm" animate={false} />
|
|
357
|
+
</Cell>
|
|
358
|
+
<Cell name="PlaceholderStrip" entity="pending-set strip" expand="no">
|
|
359
|
+
<View style={{ flexDirection: 'row', gap: 4 }}>
|
|
360
|
+
<PlaceholderStrip />
|
|
361
|
+
<PlaceholderStrip />
|
|
362
|
+
<PlaceholderStrip />
|
|
363
|
+
</View>
|
|
364
|
+
</Cell>
|
|
365
|
+
<Cell name="SupersetWrapper" entity="grouping bracket" expand="no">
|
|
366
|
+
<SupersetWrapper label="SS1" color="#FF7900">
|
|
367
|
+
<View style={{ padding: 10, backgroundColor: '#1C1C1C', borderRadius: 8 }}>
|
|
368
|
+
<Text style={{ color: '#F3F4F6' }}>Exercise A</Text>
|
|
369
|
+
</View>
|
|
370
|
+
<View style={{ padding: 10, backgroundColor: '#1C1C1C', borderRadius: 8 }}>
|
|
371
|
+
<Text style={{ color: '#F3F4F6' }}>Exercise B</Text>
|
|
372
|
+
</View>
|
|
373
|
+
</SupersetWrapper>
|
|
374
|
+
</Cell>
|
|
375
|
+
</Section>
|
|
376
|
+
|
|
377
|
+
<Section
|
|
378
|
+
title="Page-level organisms"
|
|
379
|
+
note="composition roots (390px+), not rows — listed for reference, see their own stories"
|
|
380
|
+
>
|
|
381
|
+
{[
|
|
382
|
+
['ActiveWorkoutPage', 'renders ExerciseCards + SupersetWrapper + InputBar/RestTimer'],
|
|
383
|
+
['ProgramPlanningPage', 'MesoProgressBar + MesoCard → WeekRow → WorkoutCard drill-down'],
|
|
384
|
+
['ExerciseDetailPage', 'EntryList overloads ExerciseCard as week AND session rows'],
|
|
385
|
+
['MesoStatusCard', 'per-exercise meso detail card (StatusDot + metric grid + gauges)'],
|
|
386
|
+
['ReadinessCheck', 'pre-workout factor rows + score gauge'],
|
|
387
|
+
].map(([n, d]) => (
|
|
388
|
+
<div
|
|
389
|
+
key={n}
|
|
390
|
+
style={{
|
|
391
|
+
width: 340,
|
|
392
|
+
padding: 14,
|
|
393
|
+
borderRadius: 12,
|
|
394
|
+
border: '1px dashed #31343d',
|
|
395
|
+
background: '#0f1012',
|
|
396
|
+
}}
|
|
397
|
+
>
|
|
398
|
+
<div style={{ fontSize: 13, fontWeight: 800, color: '#E8EAED' }}>{n}</div>
|
|
399
|
+
<div style={{ fontSize: 11, color: '#8D95A3', marginTop: 4 }}>{d}</div>
|
|
400
|
+
</div>
|
|
401
|
+
))}
|
|
402
|
+
</Section>
|
|
403
|
+
</div>
|
|
404
|
+
),
|
|
405
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SHEET · Finalization — the full session rail next to the left nav (neumorphic).
|
|
3
|
+
* Open item: depth tuning · 4-vs-8px strip height · indicator taxonomy. See DECISIONS-ExerciseRow doc.
|
|
4
|
+
*/
|
|
5
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
6
|
+
import { NavStub, Rail, Page, T_TERTIARY } from './setHeadingKit'
|
|
7
|
+
|
|
8
|
+
type Args = { stripHeight: number }
|
|
9
|
+
const meta: Meta<Args> = {
|
|
10
|
+
title: 'Lab/Explorations/Full Rail',
|
|
11
|
+
parameters: { layout: 'fullscreen' },
|
|
12
|
+
argTypes: { stripHeight: { control: { type: 'range', min: 3, max: 14, step: 1 } } },
|
|
13
|
+
args: { stripHeight: 8 },
|
|
14
|
+
}
|
|
15
|
+
export default meta
|
|
16
|
+
type Story = StoryObj<Args>
|
|
17
|
+
|
|
18
|
+
export const NextToNav: Story = {
|
|
19
|
+
name: 'Full look · next to left nav',
|
|
20
|
+
render: (args) => (
|
|
21
|
+
<Page
|
|
22
|
+
title="Session rail — full look next to left nav"
|
|
23
|
+
note="Neumorphic: flat raised nav + heading plane (#1F1F1F); sunk inset exercise list (#131313), depth from the list's inset shadows only. Per-set segmented strip · active pulse · tempo language · no e1RM. Drag stripHeight. OPEN: 4 vs 8px · depth tuning · left-inset strength · indicator taxonomy."
|
|
24
|
+
>
|
|
25
|
+
<div style={{ display: 'flex', alignItems: 'stretch', width: 'fit-content' }}>
|
|
26
|
+
<NavStub />
|
|
27
|
+
<Rail stripH={args.stripHeight} />
|
|
28
|
+
<div style={{ width: 140, background: '#161616', alignSelf: 'stretch', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
|
29
|
+
<span style={{ fontSize: 10, color: T_TERTIARY, fontFamily: 'monospace' }}>stage</span>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
</Page>
|
|
33
|
+
),
|
|
34
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SHEET · Session Pace — the rail-footer pace tile (NAV-D05: VMCP-primary, plan-aware trim/add).
|
|
3
|
+
* PRESENTATIONAL only; the derivation (session time-model + trim/add heuristic) is a separate VMCP/WA
|
|
4
|
+
* ticket (no WA support today). OPEN: which metrics earn the row · behind/ahead visual · idle treatment ·
|
|
5
|
+
* whether it restores a CapacityBandChart. See DECISIONS-ExerciseRow doc.
|
|
6
|
+
*/
|
|
7
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
8
|
+
import type { ReactNode } from 'react'
|
|
9
|
+
import { AMBER, GREEN, INSET, RAISED, T_PRIMARY, T_SECONDARY, T_TERTIARY, ORANGE, Page, monoTag } from './setHeadingKit'
|
|
10
|
+
|
|
11
|
+
type PaceState = 'behind' | 'ahead' | 'idle'
|
|
12
|
+
|
|
13
|
+
function Metric({ k, v, color }: { k: string; v: string; color?: string }) {
|
|
14
|
+
return (
|
|
15
|
+
<div style={{ flex: 1, textAlign: 'center', background: RAISED, borderRadius: 7, padding: '6px 3px' }}>
|
|
16
|
+
<div style={{ fontSize: 8, textTransform: 'uppercase', letterSpacing: 0.4, color: T_TERTIARY, fontWeight: 800 }}>{k}</div>
|
|
17
|
+
<div style={{ fontFamily: 'monospace', fontSize: 13, fontWeight: 800, marginTop: 2, color: color ?? T_PRIMARY }}>{v}</div>
|
|
18
|
+
</div>
|
|
19
|
+
)
|
|
20
|
+
}
|
|
21
|
+
function Suggest({ tone, children }: { tone: 'behind' | 'ahead'; children: ReactNode }) {
|
|
22
|
+
const color = tone === 'behind' ? AMBER : GREEN
|
|
23
|
+
return (
|
|
24
|
+
<div style={{ fontSize: 10.5, lineHeight: 1.4, borderRadius: 8, padding: '8px 9px', display: 'flex', gap: 7, alignItems: 'flex-start', color, background: tone === 'behind' ? 'rgba(249,180,21,0.10)' : 'rgba(46,213,115,0.10)', border: `1px solid ${tone === 'behind' ? 'rgba(249,180,21,0.30)' : 'rgba(46,213,115,0.28)'}` }}>
|
|
25
|
+
<span style={{ flex: '0 0 auto', fontSize: 12 }}>{tone === 'behind' ? '▲' : '+'}</span>
|
|
26
|
+
<div>{children}</div>
|
|
27
|
+
</div>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
const b = (t: string) => <b style={{ color: T_PRIMARY }}>{t}</b>
|
|
31
|
+
|
|
32
|
+
// The pace tile — lives pinned in the rail footer (inset surface).
|
|
33
|
+
function SessionPaceTile({ state }: { state: PaceState }) {
|
|
34
|
+
if (state === 'idle') {
|
|
35
|
+
return (
|
|
36
|
+
<div style={{ width: 246, background: INSET, borderTop: `1px solid #1C1C1C`, padding: '11px 12px', display: 'flex', flexDirection: 'column', gap: 8 }}>
|
|
37
|
+
<div style={{ fontSize: 10, fontWeight: 800, letterSpacing: 0.6, textTransform: 'uppercase', color: ORANGE }}>Next session budget</div>
|
|
38
|
+
<div style={{ display: 'flex', alignItems: 'baseline', gap: 6, fontFamily: 'monospace' }}>
|
|
39
|
+
<span style={{ fontSize: 20, fontWeight: 800, color: T_PRIMARY }}>~52:00</span>
|
|
40
|
+
<span style={{ fontSize: 11, color: T_SECONDARY }}>planned · Lower B</span>
|
|
41
|
+
</div>
|
|
42
|
+
<Suggest tone="ahead">6 exercises · ~20 sets. Wall wakes to Live when the first set streams.</Suggest>
|
|
43
|
+
</div>
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
const behind = state === 'behind'
|
|
47
|
+
return (
|
|
48
|
+
<div style={{ width: 246, background: INSET, borderTop: `1px solid #1C1C1C`, padding: '11px 12px', display: 'flex', flexDirection: 'column', gap: 8 }}>
|
|
49
|
+
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
|
50
|
+
<span style={{ fontSize: 10, fontWeight: 800, letterSpacing: 0.6, textTransform: 'uppercase', color: ORANGE }}>Session Pace</span>
|
|
51
|
+
<span style={{ fontSize: 8, fontFamily: 'monospace', color: T_TERTIARY, border: `1px solid #2C2C2C`, borderRadius: 5, padding: '2px 5px' }}>{behind ? 'show ahead ▸' : 'show behind ▸'}</span>
|
|
52
|
+
</div>
|
|
53
|
+
<div style={{ display: 'flex', alignItems: 'baseline', gap: 6, fontFamily: 'monospace' }}>
|
|
54
|
+
<span style={{ fontSize: 20, fontWeight: 800, color: T_PRIMARY }}>42:18</span>
|
|
55
|
+
<span style={{ fontSize: 11, color: T_SECONDARY }}>of 60:00 budget</span>
|
|
56
|
+
</div>
|
|
57
|
+
<div style={{ height: 6, borderRadius: 3, background: '#22252c', position: 'relative', overflow: 'hidden' }}>
|
|
58
|
+
<div style={{ position: 'absolute', left: 0, top: 0, bottom: 0, width: '70%', borderRadius: 3, background: behind ? AMBER : GREEN }} />
|
|
59
|
+
</div>
|
|
60
|
+
<div style={{ display: 'flex', gap: 4 }}>
|
|
61
|
+
<Metric k="Volume" v="76%" />
|
|
62
|
+
<Metric k="Load" v="7.3k" />
|
|
63
|
+
<Metric k="Fatigue" v="MOD" color={AMBER} />
|
|
64
|
+
</div>
|
|
65
|
+
{behind ? (
|
|
66
|
+
<Suggest tone="behind">{b('Behind pace')} — ~9 min over budget at this rate. Drop {b('Face Pull')} to 2 sets to finish on time.</Suggest>
|
|
67
|
+
) : (
|
|
68
|
+
<Suggest tone="ahead">{b('Ahead of pace')} — ~7 min of headroom. Room to add a {b('Cable Fly')} drop-set or a back-off set on Row.</Suggest>
|
|
69
|
+
)}
|
|
70
|
+
</div>
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const meta: Meta = { title: 'Lab/Explorations/Session Pace', parameters: { layout: 'fullscreen' } }
|
|
75
|
+
export default meta
|
|
76
|
+
type Story = StoryObj
|
|
77
|
+
|
|
78
|
+
export const States: Story = {
|
|
79
|
+
name: 'Pace tile · behind / ahead / idle',
|
|
80
|
+
render: () => (
|
|
81
|
+
<Page
|
|
82
|
+
title="Session Pace — rail footer tile"
|
|
83
|
+
note="VMCP-primary (NAV-D05): behind → what to trim · ahead → what to add · idle → next-session budget. Presentational; derivation is a separate VMCP/WA ticket (no WA session time-model today). Surfaces on real tokens."
|
|
84
|
+
>
|
|
85
|
+
<div style={{ display: 'flex', gap: 32, flexWrap: 'wrap' }}>
|
|
86
|
+
{(['behind', 'ahead', 'idle'] as PaceState[]).map((s) => (
|
|
87
|
+
<div key={s} style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
|
88
|
+
<span style={monoTag}>{s}</span>
|
|
89
|
+
<SessionPaceTile state={s} />
|
|
90
|
+
</div>
|
|
91
|
+
))}
|
|
92
|
+
</div>
|
|
93
|
+
</Page>
|
|
94
|
+
),
|
|
95
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SHEET · Session Rail Heading — the always-shown exercise heading unit, in isolation.
|
|
3
|
+
* Locked: name row + indicator · tight sets/reps/load (tempo language) + tempo · per-set segmented strip
|
|
4
|
+
* (completed colored · active pulse + grey remainder · upcoming grey). OPEN: strip height · indicator taxonomy
|
|
5
|
+
* · rep-range treatment · set-types (drop/myo). See DECISIONS-ExerciseRow doc.
|
|
6
|
+
*/
|
|
7
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
8
|
+
import { Heading, INSET, INSET_SHADOW, Page, monoTag, reps, type Exercise } from './setHeadingKit'
|
|
9
|
+
|
|
10
|
+
const DONE: Exercise = { 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) })) }
|
|
11
|
+
const ACTIVE: Exercise = { 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 }] }
|
|
12
|
+
const UPCOMING: Exercise = { 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 })) }
|
|
13
|
+
const RANGE: Exercise = { name: 'Face Pull', sets: 3, reps: '15-20', load: 40, tempo: [2, 1, 2, 0], setStates: [1, 2, 3].map(() => ({ status: 'todo', planned: 18 })) }
|
|
14
|
+
|
|
15
|
+
// a heading on the sunk inset surface, at the real rail width
|
|
16
|
+
function Row({ ex, h }: { ex: Exercise; h: number }) {
|
|
17
|
+
return (
|
|
18
|
+
<div style={{ width: 246, background: INSET, boxShadow: INSET_SHADOW }}>
|
|
19
|
+
<Heading ex={ex} stripH={h} />
|
|
20
|
+
</div>
|
|
21
|
+
)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
type Args = { stripHeight: number }
|
|
25
|
+
const meta: Meta<Args> = {
|
|
26
|
+
title: 'Lab/Explorations/Session Rail Heading',
|
|
27
|
+
parameters: { layout: 'fullscreen' },
|
|
28
|
+
argTypes: { stripHeight: { control: { type: 'range', min: 3, max: 14, step: 1 } } },
|
|
29
|
+
args: { stripHeight: 8 },
|
|
30
|
+
}
|
|
31
|
+
export default meta
|
|
32
|
+
type Story = StoryObj<Args>
|
|
33
|
+
|
|
34
|
+
export const States: Story = {
|
|
35
|
+
name: 'Heading · states + heights',
|
|
36
|
+
render: (args) => (
|
|
37
|
+
<Page
|
|
38
|
+
title="Session rail heading — the exercise unit"
|
|
39
|
+
note="The always-shown heading: name + indicator · tight sets/reps/load (tempo language) + tempo · per-set strip. Left column = the three states at the current height; right = the completed heading across 4 / 6 / 8 / 10px."
|
|
40
|
+
>
|
|
41
|
+
<div style={{ display: 'flex', gap: 48, flexWrap: 'wrap' }}>
|
|
42
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
|
|
43
|
+
{([['completed', DONE], ['active (pulsing)', ACTIVE], ['upcoming', UPCOMING], ['rep range', RANGE]] as [string, Exercise][]).map(([lbl, ex]) => (
|
|
44
|
+
<div key={lbl} style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
|
|
45
|
+
<span style={monoTag}>{lbl}</span>
|
|
46
|
+
<Row ex={ex} h={args.stripHeight} />
|
|
47
|
+
</div>
|
|
48
|
+
))}
|
|
49
|
+
</div>
|
|
50
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
|
|
51
|
+
{[4, 6, 8, 10].map((h) => (
|
|
52
|
+
<div key={h} style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
|
|
53
|
+
<span style={monoTag}>strip {h}px</span>
|
|
54
|
+
<Row ex={DONE} h={h} />
|
|
55
|
+
</div>
|
|
56
|
+
))}
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
</Page>
|
|
60
|
+
),
|
|
61
|
+
}
|