@titan-design/react-ui 0.9.3 → 0.11.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 +78 -24
- package/dist/bodymap.js.map +1 -1
- package/dist/bodymap.mjs +78 -24
- package/dist/bodymap.mjs.map +1 -1
- package/dist/index.d.mts +429 -11
- package/dist/index.d.ts +429 -11
- package/dist/index.js +2386 -885
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2306 -836
- package/dist/index.mjs.map +1 -1
- package/dist/{pages-DyEx-lBf.d.ts → pages-B1hqnGIC.d.ts} +149 -15
- package/dist/{pages-D7hOD4Vj.d.mts → pages-CvsFqNtx.d.mts} +149 -15
- package/dist/pages.d.mts +1 -1
- package/dist/pages.d.ts +1 -1
- package/dist/pages.js +1900 -1609
- package/dist/pages.js.map +1 -1
- package/dist/pages.mjs +1901 -1610
- package/dist/pages.mjs.map +1 -1
- package/dist/theme/index.d.mts +129 -34
- package/dist/theme/index.d.ts +129 -34
- package/dist/theme/index.js +95 -19
- package/dist/theme/index.js.map +1 -1
- package/dist/theme/index.mjs +92 -20
- package/dist/theme/index.mjs.map +1 -1
- package/dist/theme/tokens-css.js +88 -24
- package/dist/theme/tokens-css.js.map +1 -1
- package/dist/theme/tokens-css.mjs +88 -24
- package/dist/theme/tokens-css.mjs.map +1 -1
- package/package.json +8 -3
- package/src/components/custom/Fatigue/DualGhostSpark.stories.tsx +258 -0
- package/src/components/custom/Fatigue/DualGhostSpark.test.tsx +138 -0
- package/src/components/custom/Fatigue/DualGhostSpark.tsx +208 -0
- package/src/components/custom/Fatigue/FatigueLights.stories.tsx +74 -0
- package/src/components/custom/Fatigue/FatigueLights.test.tsx +33 -0
- package/src/components/custom/Fatigue/FatigueLights.tsx +83 -0
- package/src/components/custom/Fatigue/GhostBand.test.tsx +235 -0
- package/src/components/custom/Fatigue/GhostBand.tsx +246 -0
- package/src/components/custom/Fatigue/GhostBloom.tsx +132 -0
- package/src/components/custom/Fatigue/GhostSpark.stories.tsx +175 -0
- package/src/components/custom/Fatigue/GhostSpark.test.tsx +45 -0
- package/src/components/custom/Fatigue/GhostSpark.tsx +126 -0
- package/src/components/custom/Fatigue/LiveFatigueCard.stories.tsx +126 -0
- package/src/components/custom/Fatigue/LiveFatigueCard.test.tsx +28 -0
- package/src/components/custom/Fatigue/LiveFatigueCard.tsx +86 -0
- package/src/components/custom/Fatigue/LiveFatiguePanel.stories.tsx +95 -0
- package/src/components/custom/Fatigue/LiveFatiguePanel.tsx +151 -0
- package/src/components/custom/Fatigue/README.md +93 -0
- package/src/components/custom/Fatigue/RomProgressionChart.stories.tsx +94 -0
- package/src/components/custom/Fatigue/RomProgressionChart.test.tsx +31 -0
- package/src/components/custom/Fatigue/RomProgressionChart.tsx +132 -0
- package/src/components/custom/Fatigue/SilverRedPalette.stories.tsx +226 -0
- package/src/components/custom/Fatigue/VelocityHero.stories.tsx +70 -0
- package/src/components/custom/Fatigue/VelocityHero.test.tsx +26 -0
- package/src/components/custom/Fatigue/VelocityHero.tsx +46 -0
- package/src/components/custom/Fatigue/VerdictHero.stories.tsx +59 -0
- package/src/components/custom/Fatigue/VerdictHero.test.tsx +49 -0
- package/src/components/custom/Fatigue/VerdictHero.tsx +79 -0
- package/src/components/custom/Fatigue/fatigue-mock.test.ts +59 -0
- package/src/components/custom/Fatigue/fatigue-mock.ts +268 -0
- package/src/components/custom/Fatigue/fatigue-model.ts +129 -0
- package/src/components/custom/Fatigue/fatigue-tokens.test.ts +70 -0
- package/src/components/custom/Fatigue/fatigue-tokens.ts +180 -0
- package/src/components/custom/Fatigue/index.ts +41 -0
- package/src/components/custom/Fatigue/tempo-pacing.test.ts +176 -0
- package/src/components/custom/Fatigue/tempo-pacing.ts +112 -0
- package/src/components/custom/Workout/BaseBadge.tsx +5 -5
- package/src/components/custom/Workout/DualVelocityStrip.test.tsx +685 -0
- package/src/components/custom/Workout/ExerciseCard.test.tsx +4 -4
- package/src/components/custom/Workout/ExerciseCard.tsx +3 -1
- package/src/components/custom/Workout/ExerciseDetailPage.tsx +7 -1
- package/src/components/custom/Workout/README.md +62 -16
- package/src/components/custom/Workout/RowInventory.stories.tsx +5 -2
- package/src/components/custom/Workout/S3FullRail.stories.tsx +13 -1
- package/src/components/custom/Workout/SegmentedBar.stories.tsx +3 -3
- package/src/components/custom/Workout/SessionHeader.tsx +2 -2
- package/src/components/custom/Workout/SessionRail.stories.tsx +4 -1
- package/src/components/custom/Workout/SessionRail.test.tsx +43 -0
- package/src/components/custom/Workout/SessionRail.tsx +55 -18
- package/src/components/custom/Workout/SetRow.test.tsx +6 -6
- package/src/components/custom/Workout/SetRow.tsx +9 -2
- package/src/components/custom/Workout/SupersetWrapper.stories.tsx +16 -25
- package/src/components/custom/Workout/SupersetWrapper.tsx +1 -1
- package/src/components/custom/Workout/TempoDisplay.tsx +3 -7
- package/src/components/custom/Workout/VelocityStrip.compact.stories.tsx +191 -0
- package/src/components/custom/Workout/VelocityStrip.dual.stories.tsx +186 -0
- package/src/components/custom/Workout/VelocityStrip.expanded.stories.tsx +296 -0
- package/src/components/custom/Workout/VelocityStrip.hero.stories.tsx +259 -0
- package/src/components/custom/Workout/VelocityStrip.stories.tsx +217 -250
- package/src/components/custom/Workout/VelocityStrip.test.tsx +196 -79
- package/src/components/custom/Workout/VelocityStrip.tsx +953 -609
- package/src/components/custom/Workout/VolumeLandmarkBar.stories.tsx +4 -1
- package/src/components/custom/Workout/WorkoutPill.tsx +2 -2
- package/src/components/custom/Workout/index.ts +3 -0
- package/src/components/custom/Workout/velocity-story-kit.tsx +438 -0
- package/src/components/custom/charts/SetBarChart.test.tsx +275 -0
- package/src/components/custom/charts/SetBarChart.tsx +651 -0
- package/src/components/custom/charts/live-rep-growth.ts +91 -0
- package/src/components/custom/index.ts +1 -0
- package/src/components/ui/surface/Surface.stories.tsx +195 -1
- package/src/components/ui/surface/Surface.test.tsx +148 -9
- package/src/components/ui/surface/Surface.tsx +37 -9
- package/src/components/ui/surface/SurfaceContext.ts +48 -8
- package/src/components/ui/surface/index.ts +1 -0
- package/src/components/ui/surface/surface.contract.test.ts +283 -0
- package/src/test/raw-color-patterns.test.ts +71 -0
- package/src/theme/ColorPalettes.stories.tsx +871 -0
- package/src/theme/ColorPrimitives.stories.tsx +300 -0
- package/src/theme/bar-paper.ts +59 -0
- package/src/theme/color-stories.coverage.test.ts +130 -0
- package/src/theme/color-story-kit.tsx +170 -0
- package/src/theme/config.ts +12 -0
- package/src/theme/elevation.ts +90 -67
- package/src/theme/global.css +35 -9
- package/src/theme/tokens/primitives.ts +40 -0
- package/src/theme/tokens/semantic.ts +63 -14
- package/tailwind.config.js +8 -0
- package/src/components/custom/Workout/VelocityStrip.modalities.stories.tsx +0 -254
- package/src/components/custom/Workout/VelocityStrip.responsive.stories.tsx +0 -145
- package/src/theme/ColorSystem.stories.tsx +0 -323
- package/src/theme/Colors.stories.tsx +0 -323
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
3
|
+
import { View, Pressable } from 'react-native'
|
|
4
|
+
import { VelocityStrip, DualVelocityStrip } from './VelocityStrip'
|
|
5
|
+
import {
|
|
6
|
+
Sheet,
|
|
7
|
+
Note,
|
|
8
|
+
ViewLabel,
|
|
9
|
+
ScenarioPair,
|
|
10
|
+
SetTypeBoard,
|
|
11
|
+
RepTypeBoard,
|
|
12
|
+
REP_SET,
|
|
13
|
+
REP_SET_LAGGING,
|
|
14
|
+
FATIGUE_SET,
|
|
15
|
+
FATIGUE_SET_LAGGING,
|
|
16
|
+
IN_PROGRESS_SET,
|
|
17
|
+
IN_PROGRESS_LAGGING,
|
|
18
|
+
} from './velocity-story-kit'
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* `expanded` — the velocity-HEIGHT bar chart. Bars are scaled to their value,
|
|
22
|
+
* rounded on top and flat on the bottom so the set reads as grounded.
|
|
23
|
+
*
|
|
24
|
+
* Chrome is a prop spectrum rather than a variant: with `showNumbers`/`showInfo`
|
|
25
|
+
* ON it is the framed chart (per-bar m/s labels, mean/loss info row, tap to
|
|
26
|
+
* expand); with both OFF it is the bare active-set spotlight. The framed form is
|
|
27
|
+
* SINGLE-ONLY — there is no dual framed chart, and this group does not fake one.
|
|
28
|
+
*
|
|
29
|
+
* The dual expanded is the lean `rail` renderer: value-height wings separated by
|
|
30
|
+
* the shared 2px gap, with no gutter, slot labels or axis. It is the one view
|
|
31
|
+
* where the dual genuinely needs 2x the height, because here height carries
|
|
32
|
+
* meaning.
|
|
33
|
+
*/
|
|
34
|
+
const meta: Meta<typeof VelocityStrip> = {
|
|
35
|
+
title: 'Workout/DataViz/VelocityStrip/Expanded',
|
|
36
|
+
component: VelocityStrip,
|
|
37
|
+
tags: ['autodocs'],
|
|
38
|
+
}
|
|
39
|
+
export default meta
|
|
40
|
+
type Story = StoryObj<typeof VelocityStrip>
|
|
41
|
+
|
|
42
|
+
export const Default: Story = {
|
|
43
|
+
name: 'Default',
|
|
44
|
+
render: () => (
|
|
45
|
+
<Sheet>
|
|
46
|
+
<Note>
|
|
47
|
+
The representative set at value height. Bars sit at identical x-positions to the compact
|
|
48
|
+
view — only the heights change.
|
|
49
|
+
</Note>
|
|
50
|
+
<ScenarioPair
|
|
51
|
+
view="expanded"
|
|
52
|
+
title="Default"
|
|
53
|
+
single={REP_SET}
|
|
54
|
+
left={REP_SET}
|
|
55
|
+
right={REP_SET_LAGGING}
|
|
56
|
+
/>
|
|
57
|
+
</Sheet>
|
|
58
|
+
),
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export const SetTypes: Story = {
|
|
62
|
+
name: 'Set Types',
|
|
63
|
+
render: () => (
|
|
64
|
+
<Sheet>
|
|
65
|
+
<Note>
|
|
66
|
+
The set-type vocabulary at value height — to-do remainders, the cyan variable window, the
|
|
67
|
+
AMRAP continue window, and drop sub-loads split by the chunk-notch.
|
|
68
|
+
</Note>
|
|
69
|
+
<SetTypeBoard view="expanded" />
|
|
70
|
+
</Sheet>
|
|
71
|
+
),
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export const InProgress: Story = {
|
|
75
|
+
name: 'In Progress',
|
|
76
|
+
render: () => (
|
|
77
|
+
<Sheet>
|
|
78
|
+
<Note>
|
|
79
|
+
Mid-set: performed reps, the newest bar live, and the planned remainder still to come. Use
|
|
80
|
+
`scale="fixed"` while a set is live so bar heights never reflow as reps land.
|
|
81
|
+
</Note>
|
|
82
|
+
<ScenarioPair
|
|
83
|
+
view="expanded"
|
|
84
|
+
title="4 of 8 done"
|
|
85
|
+
single={IN_PROGRESS_SET}
|
|
86
|
+
left={IN_PROGRESS_SET}
|
|
87
|
+
right={IN_PROGRESS_LAGGING}
|
|
88
|
+
scale="fixed"
|
|
89
|
+
targetReps={8}
|
|
90
|
+
liveRepIndex={3}
|
|
91
|
+
/>
|
|
92
|
+
</Sheet>
|
|
93
|
+
),
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export const FatigueDecline: Story = {
|
|
97
|
+
name: 'Fatigue Decline',
|
|
98
|
+
render: () => (
|
|
99
|
+
<Sheet>
|
|
100
|
+
<Note>
|
|
101
|
+
Loss colouring against the set's own best. At value height the colour and the height
|
|
102
|
+
agree, which is what makes the compact view legible on colour alone.
|
|
103
|
+
</Note>
|
|
104
|
+
<ScenarioPair
|
|
105
|
+
view="expanded"
|
|
106
|
+
title="Fatigue decline"
|
|
107
|
+
single={FATIGUE_SET}
|
|
108
|
+
left={FATIGUE_SET}
|
|
109
|
+
right={FATIGUE_SET_LAGGING}
|
|
110
|
+
barColor="loss"
|
|
111
|
+
/>
|
|
112
|
+
</Sheet>
|
|
113
|
+
),
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export const EmptyColdBoot: Story = {
|
|
117
|
+
name: 'Empty / Cold Boot',
|
|
118
|
+
render: () => (
|
|
119
|
+
<Sheet>
|
|
120
|
+
<Note>
|
|
121
|
+
Pre-first-rep. A planned set draws its upcoming reps as placeholders, so the plot holds its
|
|
122
|
+
height AND its columns — the card does not jump, and the bars do not re-space, on rep one.
|
|
123
|
+
</Note>
|
|
124
|
+
<ScenarioPair
|
|
125
|
+
view="expanded"
|
|
126
|
+
title="Cold boot · 6 planned, none logged"
|
|
127
|
+
singleSet={{ type: 'straight', velocities: [], planned: 6 }}
|
|
128
|
+
leftSet={{ type: 'straight', velocities: [], planned: 6 }}
|
|
129
|
+
rightSet={{ type: 'straight', velocities: [], planned: 6 }}
|
|
130
|
+
/>
|
|
131
|
+
<ScenarioPair
|
|
132
|
+
view="expanded"
|
|
133
|
+
title="Truly empty · no plan to draw"
|
|
134
|
+
single={[]}
|
|
135
|
+
left={[]}
|
|
136
|
+
right={[]}
|
|
137
|
+
/>
|
|
138
|
+
</Sheet>
|
|
139
|
+
),
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export const DualLaggingSide: Story = {
|
|
143
|
+
name: 'Dual · Lagging Side',
|
|
144
|
+
render: () => (
|
|
145
|
+
<Sheet>
|
|
146
|
+
<Note>
|
|
147
|
+
The right slot trails. Its missing reps hold their columns as empties rather than shifting
|
|
148
|
+
the remaining bars left.
|
|
149
|
+
</Note>
|
|
150
|
+
<ScenarioPair
|
|
151
|
+
view="expanded"
|
|
152
|
+
title="Lagging right"
|
|
153
|
+
single={REP_SET}
|
|
154
|
+
left={REP_SET}
|
|
155
|
+
right={REP_SET_LAGGING.slice(0, 2)}
|
|
156
|
+
/>
|
|
157
|
+
</Sheet>
|
|
158
|
+
),
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export const Responsive: Story = {
|
|
162
|
+
name: 'Responsive',
|
|
163
|
+
render: () => (
|
|
164
|
+
<Sheet width={520}>
|
|
165
|
+
<Note>
|
|
166
|
+
Two ladders. WIDTH first — bars and gaps scale together from the shared layout maths.
|
|
167
|
+
</Note>
|
|
168
|
+
{[440, 300, 180, 110].map((w) => (
|
|
169
|
+
<ScenarioPair
|
|
170
|
+
key={w}
|
|
171
|
+
view="expanded"
|
|
172
|
+
title={`${w}px`}
|
|
173
|
+
width={w}
|
|
174
|
+
single={REP_SET}
|
|
175
|
+
left={REP_SET}
|
|
176
|
+
right={REP_SET_LAGGING}
|
|
177
|
+
/>
|
|
178
|
+
))}
|
|
179
|
+
|
|
180
|
+
<Note>
|
|
181
|
+
Then HEIGHT. The expanded plot compresses toward the compact language as it shrinks — at the
|
|
182
|
+
bottom of the ladder it is effectively the resting strip, which is what makes the
|
|
183
|
+
compact↔expanded toggle a morph rather than a swap.
|
|
184
|
+
</Note>
|
|
185
|
+
{[80, 60, 40, 24, 12].map((h) => (
|
|
186
|
+
<View key={h} style={{ gap: 8 }}>
|
|
187
|
+
<ViewLabel text={`${h}px`} />
|
|
188
|
+
<VelocityStrip
|
|
189
|
+
velocities={REP_SET}
|
|
190
|
+
variant="expanded"
|
|
191
|
+
height={h}
|
|
192
|
+
scale="fixed"
|
|
193
|
+
showNumbers={false}
|
|
194
|
+
showInfo={false}
|
|
195
|
+
/>
|
|
196
|
+
<DualVelocityStrip
|
|
197
|
+
left={{ velocities: REP_SET, label: 'Left' }}
|
|
198
|
+
right={{ velocities: REP_SET_LAGGING, label: 'Right' }}
|
|
199
|
+
variant="rail"
|
|
200
|
+
height={h}
|
|
201
|
+
scale="fixed"
|
|
202
|
+
/>
|
|
203
|
+
</View>
|
|
204
|
+
))}
|
|
205
|
+
</Sheet>
|
|
206
|
+
),
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// ── Expanded-only: the framed chrome spectrum ───────────────────────────────
|
|
210
|
+
|
|
211
|
+
export const FramedChrome: Story = {
|
|
212
|
+
name: 'Framed Chrome (single-only)',
|
|
213
|
+
render: () => (
|
|
214
|
+
<Sheet width={420}>
|
|
215
|
+
<Note>
|
|
216
|
+
The chrome spectrum, single-only. There is no dual framed chart — the diverging form uses
|
|
217
|
+
the lean rail instead.
|
|
218
|
+
</Note>
|
|
219
|
+
|
|
220
|
+
<View style={{ gap: 8 }}>
|
|
221
|
+
<ViewLabel text="framed · numbers + info" />
|
|
222
|
+
<VelocityStrip velocities={REP_SET} variant="expanded" expanded showNumbers showInfo />
|
|
223
|
+
</View>
|
|
224
|
+
|
|
225
|
+
<View style={{ gap: 8 }}>
|
|
226
|
+
<ViewLabel text="numbers only" />
|
|
227
|
+
<VelocityStrip
|
|
228
|
+
velocities={REP_SET}
|
|
229
|
+
variant="expanded"
|
|
230
|
+
expanded
|
|
231
|
+
showNumbers
|
|
232
|
+
showInfo={false}
|
|
233
|
+
/>
|
|
234
|
+
</View>
|
|
235
|
+
|
|
236
|
+
<View style={{ gap: 8 }}>
|
|
237
|
+
<ViewLabel text="bare spotlight · numbers + info off" />
|
|
238
|
+
<VelocityStrip
|
|
239
|
+
velocities={REP_SET}
|
|
240
|
+
variant="expanded"
|
|
241
|
+
showNumbers={false}
|
|
242
|
+
showInfo={false}
|
|
243
|
+
height={24}
|
|
244
|
+
scale="fixed"
|
|
245
|
+
/>
|
|
246
|
+
</View>
|
|
247
|
+
</Sheet>
|
|
248
|
+
),
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function TapToToggle() {
|
|
252
|
+
const [open, setOpen] = useState(false)
|
|
253
|
+
return (
|
|
254
|
+
<Pressable onPress={() => setOpen((v) => !v)} accessibilityRole="button">
|
|
255
|
+
<VelocityStrip
|
|
256
|
+
velocities={REP_SET}
|
|
257
|
+
variant={open ? 'expanded' : 'compact'}
|
|
258
|
+
height={open ? 60 : 8}
|
|
259
|
+
scale="fixed"
|
|
260
|
+
showNumbers={false}
|
|
261
|
+
showInfo={false}
|
|
262
|
+
/>
|
|
263
|
+
</Pressable>
|
|
264
|
+
)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export const Interactive: Story = {
|
|
268
|
+
name: 'Interactive · compact ↔ expanded',
|
|
269
|
+
render: () => (
|
|
270
|
+
<Sheet width={420}>
|
|
271
|
+
<Note>
|
|
272
|
+
Tap to toggle between the resting compact strip and the value-height expanded one. Because
|
|
273
|
+
both go through the same geometry, only the bar HEIGHTS change — the columns never move, so
|
|
274
|
+
the transition reads as a morph rather than a swap.
|
|
275
|
+
</Note>
|
|
276
|
+
<TapToToggle />
|
|
277
|
+
<Note>
|
|
278
|
+
Compact sits at its real 8px resting height here, not a padded stand-in — the toggle is only
|
|
279
|
+
honest if the closed state is the size it ships at.
|
|
280
|
+
</Note>
|
|
281
|
+
</Sheet>
|
|
282
|
+
),
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export const RepTypes: Story = {
|
|
286
|
+
name: 'Rep Types',
|
|
287
|
+
render: () => (
|
|
288
|
+
<Sheet>
|
|
289
|
+
<Note>
|
|
290
|
+
Every state a rep column can be in at value height. Here height and colour agree, which is
|
|
291
|
+
the reference the compact view is judged against.
|
|
292
|
+
</Note>
|
|
293
|
+
<RepTypeBoard view="expanded" />
|
|
294
|
+
</Sheet>
|
|
295
|
+
),
|
|
296
|
+
}
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import { View } from 'react-native'
|
|
3
|
+
import { VelocityStrip, DualVelocityStrip } from './VelocityStrip'
|
|
4
|
+
import {
|
|
5
|
+
Sheet,
|
|
6
|
+
Note,
|
|
7
|
+
ViewLabel,
|
|
8
|
+
ScenarioPair,
|
|
9
|
+
SetTypeBoard,
|
|
10
|
+
RepTypeBoard,
|
|
11
|
+
REP_SET,
|
|
12
|
+
REP_SET_LAGGING,
|
|
13
|
+
FATIGUE_SET,
|
|
14
|
+
FATIGUE_SET_LAGGING,
|
|
15
|
+
IN_PROGRESS_SET,
|
|
16
|
+
IN_PROGRESS_LAGGING,
|
|
17
|
+
wallDecorator,
|
|
18
|
+
} from './velocity-story-kit'
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* `hero` — the across-the-room wall treatment. Tall bars, per-bar value labels,
|
|
22
|
+
* a dashed running-best reference line, and dashed placeholders for the reps
|
|
23
|
+
* still to come via `targetReps`.
|
|
24
|
+
*
|
|
25
|
+
* No horizontal axis: the single baseline and the dual centre axis were both
|
|
26
|
+
* removed. The dual hero is two composed heroes — an up-oriented strip over a
|
|
27
|
+
* mirrored down-oriented one — separated by the same 2px gap the expanded rail
|
|
28
|
+
* uses, so the pair reads as one lockup without needing a rule between them.
|
|
29
|
+
*
|
|
30
|
+
* A live hero should run `scale="fixed"` so bar heights stay comparable across
|
|
31
|
+
* sets and do not reflow as each rep lands.
|
|
32
|
+
*/
|
|
33
|
+
const meta: Meta<typeof VelocityStrip> = {
|
|
34
|
+
title: 'Workout/DataViz/VelocityStrip/Hero',
|
|
35
|
+
component: VelocityStrip,
|
|
36
|
+
tags: ['autodocs'],
|
|
37
|
+
}
|
|
38
|
+
export default meta
|
|
39
|
+
type Story = StoryObj<typeof VelocityStrip>
|
|
40
|
+
|
|
41
|
+
export const Default: Story = {
|
|
42
|
+
name: 'Default',
|
|
43
|
+
render: () => (
|
|
44
|
+
<Sheet width={700}>
|
|
45
|
+
<Note>The representative set on the wall — same data, same geometry, wall scale.</Note>
|
|
46
|
+
<ScenarioPair
|
|
47
|
+
view="hero"
|
|
48
|
+
title="Default"
|
|
49
|
+
single={REP_SET}
|
|
50
|
+
left={REP_SET}
|
|
51
|
+
right={REP_SET_LAGGING}
|
|
52
|
+
/>
|
|
53
|
+
</Sheet>
|
|
54
|
+
),
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const SetTypes: Story = {
|
|
58
|
+
name: 'Set Types',
|
|
59
|
+
render: () => (
|
|
60
|
+
<Sheet width={700}>
|
|
61
|
+
<Note>The set-type vocabulary at wall scale, each type paired single above dual.</Note>
|
|
62
|
+
<SetTypeBoard view="hero" />
|
|
63
|
+
</Sheet>
|
|
64
|
+
),
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export const InProgress: Story = {
|
|
68
|
+
name: 'In Progress',
|
|
69
|
+
render: () => (
|
|
70
|
+
<Sheet width={700}>
|
|
71
|
+
<Note>
|
|
72
|
+
4 of 8 done, newest bar live, the remaining four drawn as dashed placeholders. Fixed scale,
|
|
73
|
+
so nothing reflows as reps land.
|
|
74
|
+
</Note>
|
|
75
|
+
<ScenarioPair
|
|
76
|
+
view="hero"
|
|
77
|
+
title="Mid set"
|
|
78
|
+
single={IN_PROGRESS_SET}
|
|
79
|
+
left={IN_PROGRESS_SET}
|
|
80
|
+
right={IN_PROGRESS_LAGGING}
|
|
81
|
+
scale="fixed"
|
|
82
|
+
targetReps={8}
|
|
83
|
+
liveRepIndex={3}
|
|
84
|
+
/>
|
|
85
|
+
</Sheet>
|
|
86
|
+
),
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export const FatigueDecline: Story = {
|
|
90
|
+
name: 'Fatigue Decline',
|
|
91
|
+
render: () => (
|
|
92
|
+
<Sheet width={700}>
|
|
93
|
+
<Note>
|
|
94
|
+
A hard decline with loss colouring and the VL bands — the reading the wall exists to make
|
|
95
|
+
obvious from across the room.
|
|
96
|
+
</Note>
|
|
97
|
+
<ScenarioPair
|
|
98
|
+
view="hero"
|
|
99
|
+
title="Fatigue decline"
|
|
100
|
+
single={FATIGUE_SET}
|
|
101
|
+
left={FATIGUE_SET}
|
|
102
|
+
right={FATIGUE_SET_LAGGING}
|
|
103
|
+
scale="fixed"
|
|
104
|
+
barColor="loss"
|
|
105
|
+
/>
|
|
106
|
+
</Sheet>
|
|
107
|
+
),
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export const EmptyColdBoot: Story = {
|
|
111
|
+
name: 'Empty / Cold Boot',
|
|
112
|
+
render: () => (
|
|
113
|
+
<Sheet width={700}>
|
|
114
|
+
<Note>
|
|
115
|
+
Cold boot: the set is planned but no rep has landed. Placeholders carry the shape so the
|
|
116
|
+
wall is never blank mid-session.
|
|
117
|
+
</Note>
|
|
118
|
+
<ScenarioPair
|
|
119
|
+
view="hero"
|
|
120
|
+
title="Zero reps · 8 planned"
|
|
121
|
+
single={[]}
|
|
122
|
+
left={[]}
|
|
123
|
+
right={[]}
|
|
124
|
+
scale="fixed"
|
|
125
|
+
targetReps={8}
|
|
126
|
+
/>
|
|
127
|
+
</Sheet>
|
|
128
|
+
),
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export const DualLaggingSide: Story = {
|
|
132
|
+
name: 'Dual · Lagging Side',
|
|
133
|
+
render: () => (
|
|
134
|
+
<Sheet width={700}>
|
|
135
|
+
<Note>
|
|
136
|
+
Index-locked empties at wall scale — the lagging slot's gaps sit directly under the
|
|
137
|
+
leading slot's reps, so the imbalance is the thing you see.
|
|
138
|
+
</Note>
|
|
139
|
+
<ScenarioPair
|
|
140
|
+
view="hero"
|
|
141
|
+
title="Lagging right"
|
|
142
|
+
single={REP_SET}
|
|
143
|
+
left={REP_SET}
|
|
144
|
+
right={REP_SET_LAGGING.slice(0, 2)}
|
|
145
|
+
scale="fixed"
|
|
146
|
+
/>
|
|
147
|
+
</Sheet>
|
|
148
|
+
),
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export const Responsive: Story = {
|
|
152
|
+
name: 'Responsive',
|
|
153
|
+
render: () => (
|
|
154
|
+
<Sheet width={760}>
|
|
155
|
+
<Note>
|
|
156
|
+
HEIGHT first. The side labels are sized to the WING, not the chart, so the collapse
|
|
157
|
+
threshold is per-wing: below 70px of wing extent a slot name degrades to its initial. A dual
|
|
158
|
+
splits its height into two wings, so the ladder below crosses that boundary between 160 and
|
|
159
|
+
120 — at 120 the names are "L" / "R". Dashed VL lines and washes always
|
|
160
|
+
survive; only their labels go.
|
|
161
|
+
</Note>
|
|
162
|
+
{[220, 160, 140, 120, 90].map((h) => (
|
|
163
|
+
<View key={h} style={{ gap: 8 }}>
|
|
164
|
+
<ViewLabel text={`${h}px · wings ${Math.round((h - 2) / 2)}px each`} />
|
|
165
|
+
<DualVelocityStrip
|
|
166
|
+
left={{ velocities: REP_SET, label: 'Left' }}
|
|
167
|
+
right={{ velocities: REP_SET_LAGGING, label: 'Right' }}
|
|
168
|
+
variant="hero"
|
|
169
|
+
height={h}
|
|
170
|
+
scale="fixed"
|
|
171
|
+
/>
|
|
172
|
+
</View>
|
|
173
|
+
))}
|
|
174
|
+
|
|
175
|
+
<Note>
|
|
176
|
+
Then WIDTH. Bars and gaps come from the shared layout maths, so they thin together rather
|
|
177
|
+
than the gaps collapsing first; the per-bar value labels drop out once a column can no
|
|
178
|
+
longer seat one.
|
|
179
|
+
</Note>
|
|
180
|
+
{[700, 460, 300, 190].map((w) => (
|
|
181
|
+
<View key={w} style={{ gap: 8, width: w }}>
|
|
182
|
+
<ViewLabel text={`${w}px`} />
|
|
183
|
+
<VelocityStrip
|
|
184
|
+
velocities={REP_SET}
|
|
185
|
+
variant="hero"
|
|
186
|
+
label="Set"
|
|
187
|
+
height={180}
|
|
188
|
+
scale="fixed"
|
|
189
|
+
/>
|
|
190
|
+
<DualVelocityStrip
|
|
191
|
+
left={{ velocities: REP_SET, label: 'Left' }}
|
|
192
|
+
right={{ velocities: REP_SET_LAGGING, label: 'Right' }}
|
|
193
|
+
variant="hero"
|
|
194
|
+
height={200}
|
|
195
|
+
scale="fixed"
|
|
196
|
+
/>
|
|
197
|
+
</View>
|
|
198
|
+
))}
|
|
199
|
+
</Sheet>
|
|
200
|
+
),
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export const RepTypes: Story = {
|
|
204
|
+
name: 'Rep Types',
|
|
205
|
+
render: () => (
|
|
206
|
+
<Sheet width={700}>
|
|
207
|
+
<Note>
|
|
208
|
+
Every state a rep column can be in at wall scale — where there is room for all of it to be
|
|
209
|
+
unambiguous, and therefore the reference the smaller views are judged against.
|
|
210
|
+
</Note>
|
|
211
|
+
<RepTypeBoard view="hero" />
|
|
212
|
+
</Sheet>
|
|
213
|
+
),
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export const EdgeCases: Story = {
|
|
217
|
+
name: 'Edge Cases',
|
|
218
|
+
decorators: [wallDecorator],
|
|
219
|
+
render: () => (
|
|
220
|
+
<View style={{ gap: 26 }}>
|
|
221
|
+
<View style={{ gap: 8 }}>
|
|
222
|
+
<ViewLabel text="near complete · 7 of 8" />
|
|
223
|
+
<VelocityStrip
|
|
224
|
+
velocities={[0.82, 0.79, 0.81, 0.76, 0.74, 0.71, 0.68]}
|
|
225
|
+
variant="hero"
|
|
226
|
+
label="Set"
|
|
227
|
+
targetReps={8}
|
|
228
|
+
scale="fixed"
|
|
229
|
+
/>
|
|
230
|
+
</View>
|
|
231
|
+
|
|
232
|
+
<View style={{ gap: 8 }}>
|
|
233
|
+
<ViewLabel text="ends on the best rep · reference line sits on the last bar" />
|
|
234
|
+
<VelocityStrip
|
|
235
|
+
velocities={[0.7, 0.74, 0.78, 0.86]}
|
|
236
|
+
variant="hero"
|
|
237
|
+
label="Set"
|
|
238
|
+
scale="fixed"
|
|
239
|
+
/>
|
|
240
|
+
</View>
|
|
241
|
+
|
|
242
|
+
<View style={{ gap: 8 }}>
|
|
243
|
+
<ViewLabel text="exceeds target · more reps performed than planned" />
|
|
244
|
+
<VelocityStrip
|
|
245
|
+
velocities={[0.9, 0.88, 0.85, 0.83, 0.8, 0.78, 0.75]}
|
|
246
|
+
variant="hero"
|
|
247
|
+
label="Set"
|
|
248
|
+
targetReps={5}
|
|
249
|
+
scale="fixed"
|
|
250
|
+
/>
|
|
251
|
+
</View>
|
|
252
|
+
|
|
253
|
+
<View style={{ gap: 8 }}>
|
|
254
|
+
<ViewLabel text="single rep · no decline to draw" />
|
|
255
|
+
<VelocityStrip velocities={[0.88]} variant="hero" label="Set" scale="fixed" />
|
|
256
|
+
</View>
|
|
257
|
+
</View>
|
|
258
|
+
),
|
|
259
|
+
}
|