@titan-design/react-ui 0.2.6 → 0.3.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.
Files changed (191) hide show
  1. package/dist/bodymap-BhG55xHM.d.mts +318 -0
  2. package/dist/bodymap-BhG55xHM.d.ts +318 -0
  3. package/dist/bodymap.d.mts +3 -0
  4. package/dist/bodymap.d.ts +3 -0
  5. package/dist/bodymap.js +2670 -0
  6. package/dist/bodymap.js.map +1 -0
  7. package/dist/bodymap.mjs +1101 -0
  8. package/dist/bodymap.mjs.map +1 -0
  9. package/dist/{chunk-UXVRPOME.mjs → chunk-7XPB4NAO.mjs} +125 -785
  10. package/dist/chunk-7XPB4NAO.mjs.map +1 -0
  11. package/dist/chunk-P7TSQUN6.mjs +846 -0
  12. package/dist/chunk-P7TSQUN6.mjs.map +1 -0
  13. package/dist/chunk-TOD2WQBG.mjs +1048 -0
  14. package/dist/chunk-TOD2WQBG.mjs.map +1 -0
  15. package/dist/index.d.mts +1131 -4
  16. package/dist/index.d.ts +1131 -4
  17. package/dist/index.js +6396 -225
  18. package/dist/index.js.map +1 -1
  19. package/dist/index.mjs +5572 -641
  20. package/dist/index.mjs.map +1 -1
  21. package/dist/theme/index.d.mts +317 -1
  22. package/dist/theme/index.d.ts +317 -1
  23. package/dist/theme/index.js +185 -2
  24. package/dist/theme/index.js.map +1 -1
  25. package/dist/theme/index.mjs +2 -1
  26. package/dist/theme/tokens-css.d.mts +20 -0
  27. package/dist/theme/tokens-css.d.ts +20 -0
  28. package/dist/theme/tokens-css.js +598 -0
  29. package/dist/theme/tokens-css.js.map +1 -0
  30. package/dist/theme/tokens-css.mjs +24 -0
  31. package/dist/theme/tokens-css.mjs.map +1 -0
  32. package/package.json +25 -5
  33. package/src/bodymap.ts +37 -0
  34. package/src/components/custom/DateTime/DateTime.stories.tsx +1 -1
  35. package/src/components/custom/Gauge/Gauge.stories.tsx +45 -0
  36. package/src/components/custom/Gauge/Gauge.test.tsx +71 -0
  37. package/src/components/custom/Gauge/Gauge.tsx +167 -0
  38. package/src/components/custom/Gauge/index.ts +2 -0
  39. package/src/components/custom/Scatter/Scatter.stories.tsx +85 -0
  40. package/src/components/custom/Scatter/Scatter.test.tsx +96 -0
  41. package/src/components/custom/Scatter/Scatter.tsx +260 -0
  42. package/src/components/custom/Scatter/index.ts +2 -0
  43. package/src/components/custom/Sidebar/Sidebar.tsx +1 -1
  44. package/src/components/custom/Table/Table.test.tsx +27 -0
  45. package/src/components/custom/Table/Table.tsx +33 -17
  46. package/src/components/custom/Treemap/Treemap.stories.tsx +60 -0
  47. package/src/components/custom/Treemap/Treemap.test.tsx +67 -0
  48. package/src/components/custom/Treemap/Treemap.tsx +192 -0
  49. package/src/components/custom/Treemap/index.ts +2 -0
  50. package/src/components/custom/Workout/ActiveWorkoutPage.stories.tsx +118 -0
  51. package/src/components/custom/Workout/ActiveWorkoutPage.test.tsx +171 -0
  52. package/src/components/custom/Workout/ActiveWorkoutPage.tsx +447 -0
  53. package/src/components/custom/Workout/BaseBadge.stories.tsx +75 -0
  54. package/src/components/custom/Workout/BaseBadge.test.tsx +102 -0
  55. package/src/components/custom/Workout/BaseBadge.tsx +113 -0
  56. package/src/components/custom/Workout/BodyMap.stories.tsx +121 -0
  57. package/src/components/custom/Workout/BodyMap.test.tsx +124 -0
  58. package/src/components/custom/Workout/BodyMap.tsx +347 -0
  59. package/src/components/custom/Workout/BodyMapDetailPanel.stories.tsx +171 -0
  60. package/src/components/custom/Workout/BodyMapDetailPanel.test.tsx +225 -0
  61. package/src/components/custom/Workout/BodyMapDetailPanel.tsx +477 -0
  62. package/src/components/custom/Workout/CapacityBandChart.stories.tsx +125 -0
  63. package/src/components/custom/Workout/CapacityBandChart.test.tsx +193 -0
  64. package/src/components/custom/Workout/CapacityBandChart.tsx +493 -0
  65. package/src/components/custom/Workout/DeviationBar.stories.tsx +58 -0
  66. package/src/components/custom/Workout/DeviationBar.test.tsx +86 -0
  67. package/src/components/custom/Workout/DeviationBar.tsx +78 -0
  68. package/src/components/custom/Workout/ExerciseCard.stories.tsx +186 -0
  69. package/src/components/custom/Workout/ExerciseCard.test.tsx +343 -0
  70. package/src/components/custom/Workout/ExerciseCard.tsx +442 -0
  71. package/src/components/custom/Workout/ExerciseDetailPage.stories.tsx +225 -0
  72. package/src/components/custom/Workout/ExerciseDetailPage.test.tsx +128 -0
  73. package/src/components/custom/Workout/ExerciseDetailPage.tsx +630 -0
  74. package/src/components/custom/Workout/InputBar.stories.tsx +75 -0
  75. package/src/components/custom/Workout/InputBar.test.tsx +116 -0
  76. package/src/components/custom/Workout/InputBar.tsx +162 -0
  77. package/src/components/custom/Workout/IntensityBar.stories.tsx +60 -0
  78. package/src/components/custom/Workout/IntensityBar.test.tsx +113 -0
  79. package/src/components/custom/Workout/IntensityBar.tsx +166 -0
  80. package/src/components/custom/Workout/MesoCard.stories.tsx +152 -0
  81. package/src/components/custom/Workout/MesoCard.test.tsx +170 -0
  82. package/src/components/custom/Workout/MesoCard.tsx +237 -0
  83. package/src/components/custom/Workout/MesoProgressBar.stories.tsx +85 -0
  84. package/src/components/custom/Workout/MesoProgressBar.test.tsx +153 -0
  85. package/src/components/custom/Workout/MesoProgressBar.tsx +165 -0
  86. package/src/components/custom/Workout/MesoStatusCard.stories.tsx +126 -0
  87. package/src/components/custom/Workout/MesoStatusCard.test.tsx +209 -0
  88. package/src/components/custom/Workout/MesoStatusCard.tsx +456 -0
  89. package/src/components/custom/Workout/MuscleGroupChip.stories.tsx +118 -0
  90. package/src/components/custom/Workout/MuscleGroupChip.test.tsx +91 -0
  91. package/src/components/custom/Workout/MuscleGroupChip.tsx +92 -0
  92. package/src/components/custom/Workout/PlaceholderStrip.stories.tsx +94 -0
  93. package/src/components/custom/Workout/PlaceholderStrip.test.tsx +83 -0
  94. package/src/components/custom/Workout/PlaceholderStrip.tsx +90 -0
  95. package/src/components/custom/Workout/PrBadge.stories.tsx +105 -0
  96. package/src/components/custom/Workout/PrBadge.test.tsx +111 -0
  97. package/src/components/custom/Workout/PrBadge.tsx +103 -0
  98. package/src/components/custom/Workout/PrHistoryModal.stories.tsx +116 -0
  99. package/src/components/custom/Workout/PrHistoryModal.test.tsx +161 -0
  100. package/src/components/custom/Workout/PrHistoryModal.tsx +244 -0
  101. package/src/components/custom/Workout/ProgramPlanningPage.stories.tsx +257 -0
  102. package/src/components/custom/Workout/ProgramPlanningPage.test.tsx +142 -0
  103. package/src/components/custom/Workout/ProgramPlanningPage.tsx +422 -0
  104. package/src/components/custom/Workout/ReadinessCheck.stories.tsx +126 -0
  105. package/src/components/custom/Workout/ReadinessCheck.test.tsx +181 -0
  106. package/src/components/custom/Workout/ReadinessCheck.tsx +265 -0
  107. package/src/components/custom/Workout/RestTimer.test.tsx +10 -0
  108. package/src/components/custom/Workout/RestTimer.tsx +15 -11
  109. package/src/components/custom/Workout/SetRow.stories.tsx +165 -0
  110. package/src/components/custom/Workout/SetRow.test.tsx +222 -0
  111. package/src/components/custom/Workout/SetRow.tsx +247 -0
  112. package/src/components/custom/Workout/Sparkline.stories.tsx +125 -0
  113. package/src/components/custom/Workout/Sparkline.test.tsx +113 -0
  114. package/src/components/custom/Workout/Sparkline.tsx +188 -0
  115. package/src/components/custom/Workout/StatusDot.stories.tsx +150 -0
  116. package/src/components/custom/Workout/StatusDot.test.tsx +165 -0
  117. package/src/components/custom/Workout/StatusDot.tsx +160 -0
  118. package/src/components/custom/Workout/StrengthTrendChart.stories.tsx +130 -0
  119. package/src/components/custom/Workout/StrengthTrendChart.test.tsx +217 -0
  120. package/src/components/custom/Workout/StrengthTrendChart.tsx +628 -0
  121. package/src/components/custom/Workout/SupersetWrapper.tsx +1 -2
  122. package/src/components/custom/Workout/TempoDisplay.stories.tsx +66 -0
  123. package/src/components/custom/Workout/TempoDisplay.test.tsx +90 -0
  124. package/src/components/custom/Workout/TempoDisplay.tsx +209 -0
  125. package/src/components/custom/Workout/TrainingStatusPage.stories.tsx +212 -0
  126. package/src/components/custom/Workout/TrainingStatusPage.test.tsx +131 -0
  127. package/src/components/custom/Workout/TrainingStatusPage.tsx +321 -0
  128. package/src/components/custom/Workout/VelocityStrip.stories.tsx +125 -0
  129. package/src/components/custom/Workout/VelocityStrip.test.tsx +244 -0
  130. package/src/components/custom/Workout/VelocityStrip.tsx +288 -0
  131. package/src/components/custom/Workout/WeekRow.stories.tsx +121 -0
  132. package/src/components/custom/Workout/WeekRow.test.tsx +117 -0
  133. package/src/components/custom/Workout/WeekRow.tsx +140 -0
  134. package/src/components/custom/Workout/WeightBadge.stories.tsx +87 -0
  135. package/src/components/custom/Workout/WeightBadge.test.tsx +164 -0
  136. package/src/components/custom/Workout/WeightBadge.tsx +113 -0
  137. package/src/components/custom/Workout/WorkoutCard.stories.tsx +135 -0
  138. package/src/components/custom/Workout/WorkoutCard.test.tsx +172 -0
  139. package/src/components/custom/Workout/WorkoutCard.tsx +228 -0
  140. package/src/components/custom/Workout/WorkoutPill.stories.tsx +63 -0
  141. package/src/components/custom/Workout/WorkoutPill.test.tsx +153 -0
  142. package/src/components/custom/Workout/WorkoutPill.tsx +177 -0
  143. package/src/components/custom/Workout/index.ts +152 -0
  144. package/src/components/custom/Workout/muscleTaxonomy.ts +252 -0
  145. package/src/components/custom/index.ts +3 -0
  146. package/src/components/custom/stepper/Stepper.stories.tsx +1 -1
  147. package/src/components/custom/stepper/Stepper.tsx +2 -2
  148. package/src/components/ui/autocomplete/Autocomplete.stories.tsx +1 -1
  149. package/src/components/ui/autocomplete/Autocomplete.tsx +1 -1
  150. package/src/components/ui/card/Card.tsx +1 -1
  151. package/src/components/ui/checkbox/Checkbox.tsx +1 -1
  152. package/src/components/ui/collapse/Collapse.tsx +1 -1
  153. package/src/components/ui/data-row/DataRow.stories.tsx +1 -1
  154. package/src/components/ui/drawer/Drawer.tsx +3 -3
  155. package/src/components/ui/form-field/FormField.tsx +1 -1
  156. package/src/components/ui/help-tip/HelpTip.tsx +1 -1
  157. package/src/components/ui/menu/Menu.tsx +2 -2
  158. package/src/components/ui/pill/Pill.tsx +1 -1
  159. package/src/components/ui/popover/Popover.stories.tsx +2 -2
  160. package/src/components/ui/popover/Popover.tsx +1 -1
  161. package/src/components/ui/radio/Radio.stories.tsx +1 -1
  162. package/src/components/ui/section/Section.stories.tsx +4 -4
  163. package/src/components/ui/select/Select.tsx +1 -1
  164. package/src/components/ui/stack/Stack.stories.tsx +4 -4
  165. package/src/components/ui/tabs/Tabs.tsx +2 -2
  166. package/src/components/ui/toolbar-button/ToolbarButton.stories.tsx +1 -1
  167. package/src/components/ui/toolbar-button/ToolbarButton.tsx +1 -1
  168. package/src/examples/react-hook-form/ControlledFields.tsx +171 -0
  169. package/src/examples/react-hook-form/ReactHookForm.stories.tsx +51 -0
  170. package/src/examples/react-hook-form/ReactHookForm.test.tsx +61 -0
  171. package/src/examples/react-hook-form/RhfContactForm.tsx +99 -0
  172. package/src/stories/PresetShowcase.stories.tsx +15 -15
  173. package/src/theme/config.completeness.test.ts +77 -0
  174. package/src/theme/config.ts +151 -0
  175. package/src/theme/index.ts +2 -1
  176. package/src/theme/manifest/css-property-manifest.example.json +79 -0
  177. package/src/theme/manifest/css-property-manifest.schema.json +93 -0
  178. package/src/theme/manifest/css-property-manifest.test.ts +81 -0
  179. package/src/theme/manifest/css-property-manifest.types.ts +49 -0
  180. package/src/theme/manifest/css-property-manifest.validate.ts +84 -0
  181. package/src/theme/manifest/design-freeze.test.ts +155 -0
  182. package/src/theme/manifest/extract-css-properties.fixture.html +29 -0
  183. package/src/theme/manifest/extract-css-properties.test.ts +112 -0
  184. package/src/theme/manifest/index.ts +11 -0
  185. package/src/theme/tokens-css.test.ts +28 -0
  186. package/src/theme/tokens-css.ts +36 -0
  187. package/src/theme/workout-tokens.ts +59 -0
  188. package/src/utils/index.ts +10 -0
  189. package/src/utils/workout-format.test.ts +81 -0
  190. package/src/utils/workout-format.ts +83 -0
  191. package/dist/chunk-UXVRPOME.mjs.map +0 -1
@@ -0,0 +1,58 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+ import { View } from 'react-native'
3
+ import { DeviationBar } from './DeviationBar'
4
+
5
+ const meta: Meta<typeof DeviationBar> = {
6
+ title: 'Custom/Workout/DeviationBar',
7
+ component: DeviationBar,
8
+ tags: ['autodocs'],
9
+ argTypes: {
10
+ deviation: {
11
+ control: { type: 'range', min: -1, max: 1, step: 0.1 },
12
+ description: 'Deviation from plan (-1 to +1)',
13
+ },
14
+ width: {
15
+ control: 'number',
16
+ description: 'Width of the bar in pixels',
17
+ },
18
+ },
19
+ }
20
+
21
+ export default meta
22
+ type Story = StoryObj<typeof DeviationBar>
23
+
24
+ export const OnPlan: Story = {
25
+ args: { deviation: 0 },
26
+ }
27
+
28
+ export const SlightlyLighter: Story = {
29
+ args: { deviation: -0.2 },
30
+ }
31
+
32
+ export const MuchLighter: Story = {
33
+ args: { deviation: -0.8 },
34
+ }
35
+
36
+ export const SlightlyHarder: Story = {
37
+ args: { deviation: 0.3 },
38
+ }
39
+
40
+ export const MuchHarder: Story = {
41
+ args: { deviation: 0.9 },
42
+ }
43
+
44
+ export const FullRange: Story = {
45
+ render: () => (
46
+ <View style={{ gap: 8 }}>
47
+ {[-1, -0.7, -0.3, 0, 0.3, 0.7, 1].map((d) => (
48
+ <View key={d} style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
49
+ <DeviationBar deviation={d} />
50
+ </View>
51
+ ))}
52
+ </View>
53
+ ),
54
+ }
55
+
56
+ export const WideBar: Story = {
57
+ args: { deviation: 0.5, width: 80 },
58
+ }
@@ -0,0 +1,86 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { render, screen } from '@testing-library/react'
3
+ import { axe } from 'jest-axe'
4
+ import { DeviationBar } from './DeviationBar'
5
+
6
+ describe('DeviationBar', () => {
7
+ it('renders the bar and dot', () => {
8
+ render(<DeviationBar deviation={0} />)
9
+ expect(screen.getByTestId('deviation-bar')).toBeInTheDocument()
10
+ expect(screen.getByTestId('deviation-dot')).toBeInTheDocument()
11
+ })
12
+
13
+ it('labels on-plan deviation correctly', () => {
14
+ render(<DeviationBar deviation={0} />)
15
+ expect(screen.getByLabelText('Session deviation: on plan')).toBeInTheDocument()
16
+ })
17
+
18
+ it('labels lighter deviation correctly', () => {
19
+ render(<DeviationBar deviation={-0.5} />)
20
+ expect(screen.getByLabelText('Session deviation: lighter than planned')).toBeInTheDocument()
21
+ })
22
+
23
+ it('labels harder deviation correctly', () => {
24
+ render(<DeviationBar deviation={0.5} />)
25
+ expect(screen.getByLabelText('Session deviation: harder than planned')).toBeInTheDocument()
26
+ })
27
+
28
+ it('clamps deviation to -1', () => {
29
+ render(<DeviationBar deviation={-2} />)
30
+ expect(screen.getByTestId('deviation-bar')).toBeInTheDocument()
31
+ })
32
+
33
+ it('clamps deviation to +1', () => {
34
+ render(<DeviationBar deviation={2} />)
35
+ expect(screen.getByTestId('deviation-bar')).toBeInTheDocument()
36
+ })
37
+
38
+ it('accepts custom width', () => {
39
+ render(<DeviationBar deviation={0} width={80} />)
40
+ expect(screen.getByTestId('deviation-bar')).toBeInTheDocument()
41
+ })
42
+
43
+ it('renders dot at left edge for -1 deviation', () => {
44
+ const { getByTestId } = render(<DeviationBar deviation={-1} width={40} />)
45
+ const dot = getByTestId('deviation-dot')
46
+ expect(dot).toBeInTheDocument()
47
+ })
48
+
49
+ it('renders dot at right edge for +1 deviation', () => {
50
+ const { getByTestId } = render(<DeviationBar deviation={1} width={40} />)
51
+ const dot = getByTestId('deviation-dot')
52
+ expect(dot).toBeInTheDocument()
53
+ })
54
+
55
+ it('has adjustable accessibility role', () => {
56
+ render(<DeviationBar deviation={0} />)
57
+ const bar = screen.getByTestId('deviation-bar')
58
+ expect(bar).toHaveAttribute('role', 'slider')
59
+ })
60
+
61
+ it('renders an 8px dot with a text-primary border and shadow', () => {
62
+ const { getByTestId } = render(<DeviationBar deviation={0} />)
63
+ const dot = getByTestId('deviation-dot')
64
+ expect(dot).toHaveStyle({ width: '8px', height: '8px' })
65
+ expect(dot).toHaveStyle({ borderTopWidth: '1.5px', borderTopColor: '#F3F4F6' })
66
+ expect(dot).toHaveStyle({ boxShadow: '0 0 4px rgba(0,0,0,0.5)' })
67
+ })
68
+
69
+ it('renders the track with the deviation gradient background', () => {
70
+ const { getByTestId } = render(<DeviationBar deviation={0} />)
71
+ const bar = getByTestId('deviation-bar')
72
+ const track = bar.firstElementChild as HTMLElement
73
+ expect(track).toHaveStyle({
74
+ backgroundImage:
75
+ 'linear-gradient(90deg, rgba(20,184,166,0.25) 0%, rgba(107,114,128,0.15) 50%, rgba(255,176,32,0.25) 100%)',
76
+ })
77
+ })
78
+
79
+ describe('accessibility', () => {
80
+ it('has no accessibility violations', async () => {
81
+ const { container } = render(<DeviationBar deviation={0.3} />)
82
+ const results = await axe(container)
83
+ expect(results).toHaveNoViolations()
84
+ })
85
+ })
86
+ })
@@ -0,0 +1,78 @@
1
+ // Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
2
+ import { View, type ViewProps, type ViewStyle } from 'react-native'
3
+
4
+ export interface DeviationBarProps extends ViewProps {
5
+ deviation: number
6
+ width?: number
7
+ className?: string
8
+ }
9
+
10
+ function getDotColor(deviation: number): string {
11
+ const abs = Math.abs(deviation)
12
+ if (deviation < -0.3) return '#14B8A6'
13
+ if (abs <= 0.3) return '#6B7280'
14
+ if (abs <= 0.7) return '#FFB020'
15
+ return '#D14343'
16
+ }
17
+
18
+ function getDeviationDescription(deviation: number): string {
19
+ if (deviation < -0.3) return 'lighter than planned'
20
+ if (deviation > 0.3) return 'harder than planned'
21
+ return 'on plan'
22
+ }
23
+
24
+ export function DeviationBar({
25
+ deviation,
26
+ width,
27
+ className,
28
+ ...props
29
+ }: DeviationBarProps) {
30
+ const clamped = Math.max(-1, Math.min(1, deviation))
31
+ const resolvedWidth = width ?? 40
32
+ const dotPosition = ((clamped + 1) / 2) * resolvedWidth
33
+ const dotSize = 8
34
+ const trackHeight = 6
35
+ const containerHeight = 10
36
+ const valueNow = Math.round(clamped * 100)
37
+
38
+ return (
39
+ <View
40
+ className={className}
41
+ style={{ flexDirection: 'row', alignItems: 'center', height: containerHeight, width: resolvedWidth }}
42
+ accessibilityRole="adjustable"
43
+ accessibilityValue={{ min: -100, max: 100, now: valueNow }}
44
+ accessibilityLabel={`Session deviation: ${getDeviationDescription(clamped)}`}
45
+ testID="deviation-bar"
46
+ aria-valuenow={valueNow}
47
+ aria-valuemin={-100}
48
+ aria-valuemax={100}
49
+ {...props}
50
+ >
51
+ <View
52
+ style={{
53
+ height: trackHeight,
54
+ width: resolvedWidth,
55
+ borderRadius: 3,
56
+ // react-native-web renders backgroundImage at runtime; not in RN ViewStyle types
57
+ backgroundImage:
58
+ 'linear-gradient(90deg, rgba(20,184,166,0.25) 0%, rgba(107,114,128,0.15) 50%, rgba(255,176,32,0.25) 100%)',
59
+ } as ViewStyle}
60
+ />
61
+ <View
62
+ style={{
63
+ position: 'absolute',
64
+ width: dotSize,
65
+ height: dotSize,
66
+ borderRadius: 9999,
67
+ borderWidth: 1.5,
68
+ borderColor: '#F3F4F6',
69
+ boxShadow: '0 0 4px rgba(0,0,0,0.5)',
70
+ backgroundColor: getDotColor(clamped),
71
+ left: Math.max(0, Math.min(dotPosition - dotSize / 2, resolvedWidth - dotSize)),
72
+ top: (containerHeight - dotSize) / 2,
73
+ }}
74
+ testID="deviation-dot"
75
+ />
76
+ </View>
77
+ )
78
+ }
@@ -0,0 +1,186 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+ import { View } from 'react-native'
3
+ import { ExerciseCard } from './ExerciseCard'
4
+ import type { SetRowProps } from './SetRow'
5
+
6
+ const meta: Meta<typeof ExerciseCard> = {
7
+ title: 'Custom/Workout/ExerciseCard',
8
+ component: ExerciseCard,
9
+ tags: ['autodocs'],
10
+ argTypes: {
11
+ state: {
12
+ control: 'select',
13
+ options: ['collapsed', 'expanded', 'upcoming'],
14
+ description: 'Card display state',
15
+ },
16
+ },
17
+ decorators: [
18
+ (Story) => (
19
+ <View style={{ maxWidth: 400, padding: 16, backgroundColor: '#121212' }}>
20
+ <Story />
21
+ </View>
22
+ ),
23
+ ],
24
+ }
25
+
26
+ export default meta
27
+ type Story = StoryObj<typeof ExerciseCard>
28
+
29
+ const sampleSets: SetRowProps[] = [
30
+ {
31
+ mode: 'completed',
32
+ setNumber: 1,
33
+ reps: 8,
34
+ weight: 135,
35
+ unit: 'lbs',
36
+ rpe: 7,
37
+ previous: { reps: 8, weight: 130 },
38
+ velocities: [1.1, 0.95, 0.82, 0.68, 0.55, 0.48, 0.42, 0.38],
39
+ },
40
+ {
41
+ mode: 'completed',
42
+ setNumber: 2,
43
+ reps: 8,
44
+ weight: 135,
45
+ unit: 'lbs',
46
+ rpe: 8.5,
47
+ previous: { reps: 8, weight: 135 },
48
+ velocities: [0.95, 0.88, 0.78, 0.65, 0.52, 0.45, 0.40, 0.35],
49
+ },
50
+ {
51
+ mode: 'active',
52
+ setNumber: 3,
53
+ reps: null,
54
+ weight: null,
55
+ unit: 'lbs',
56
+ isNextSet: true,
57
+ targets: { reps: 8, weight: 135 },
58
+ previous: { reps: 8, weight: 135 },
59
+ },
60
+ {
61
+ mode: 'active',
62
+ setNumber: 4,
63
+ reps: null,
64
+ weight: null,
65
+ unit: 'lbs',
66
+ targets: { reps: 8, weight: 135 },
67
+ previous: { reps: 8, weight: 135 },
68
+ },
69
+ ]
70
+
71
+ export const CollapsedWithVelocity: Story = {
72
+ args: {
73
+ name: 'Bench Press',
74
+ state: 'collapsed',
75
+ onToggle: () => {},
76
+ summary: { sets: 4, reps: 8, weight: 135, unit: 'lbs' },
77
+ setVelocities: [
78
+ [1.1, 0.95, 0.82, 0.68, 0.55, 0.48, 0.42, 0.38],
79
+ [0.95, 0.88, 0.78, 0.65, 0.52, 0.45, 0.40, 0.35],
80
+ ],
81
+ totalPlannedSets: 4,
82
+ e1rm: { value: 225, unit: 'lbs' },
83
+ },
84
+ }
85
+
86
+ export const CollapsedWithPR: Story = {
87
+ args: {
88
+ name: 'Squat',
89
+ state: 'collapsed',
90
+ onToggle: () => {},
91
+ summary: { sets: 3, reps: 5, weight: 275, unit: 'lbs' },
92
+ setVelocities: [
93
+ [0.65, 0.55, 0.45, 0.38, 0.32],
94
+ [0.60, 0.50, 0.42, 0.35, 0.30],
95
+ [0.58, 0.48, 0.40, 0.33, 0.28],
96
+ ],
97
+ totalPlannedSets: 3,
98
+ e1rm: { value: 365, unit: 'lbs' },
99
+ isPR: true,
100
+ },
101
+ }
102
+
103
+ export const ExpandedWithSets: Story = {
104
+ args: {
105
+ name: 'Bench Press',
106
+ state: 'expanded',
107
+ onToggle: () => {},
108
+ summary: { sets: 4, reps: 8, weight: 135, unit: 'lbs' },
109
+ sets: sampleSets,
110
+ e1rm: { value: 225, unit: 'lbs' },
111
+ },
112
+ }
113
+
114
+ export const ExpandedWithTempo: Story = {
115
+ args: {
116
+ name: 'Romanian Deadlift',
117
+ state: 'expanded',
118
+ onToggle: () => {},
119
+ summary: { sets: 3, reps: 10, weight: 185, unit: 'lbs' },
120
+ tempo: [2, 1, 3, 0],
121
+ sets: [
122
+ {
123
+ mode: 'completed',
124
+ setNumber: 1,
125
+ reps: 10,
126
+ weight: 185,
127
+ unit: 'lbs',
128
+ rpe: 7,
129
+ previous: { reps: 10, weight: 175 },
130
+ },
131
+ {
132
+ mode: 'active',
133
+ setNumber: 2,
134
+ reps: null,
135
+ weight: null,
136
+ unit: 'lbs',
137
+ isNextSet: true,
138
+ targets: { reps: 10, weight: 185 },
139
+ previous: { reps: 10, weight: 185 },
140
+ },
141
+ {
142
+ mode: 'active',
143
+ setNumber: 3,
144
+ reps: null,
145
+ weight: null,
146
+ unit: 'lbs',
147
+ targets: { reps: 10, weight: 185 },
148
+ previous: { reps: 10, weight: 185 },
149
+ },
150
+ ],
151
+ },
152
+ }
153
+
154
+ export const Upcoming: Story = {
155
+ args: {
156
+ name: 'Lat Pulldown',
157
+ state: 'upcoming',
158
+ onToggle: () => {},
159
+ prescription: '3\u00D78-12 @ RPE 8',
160
+ previousBest: '135 lbs \u00D7 10',
161
+ },
162
+ }
163
+
164
+ export const SupersetFirst: Story = {
165
+ args: {
166
+ name: 'Bench Press',
167
+ state: 'collapsed',
168
+ onToggle: () => {},
169
+ summary: { sets: 3, reps: 8, weight: 185, unit: 'lbs' },
170
+ supersetPosition: 'first',
171
+ setVelocities: [[1.0, 0.9, 0.8]],
172
+ totalPlannedSets: 3,
173
+ },
174
+ }
175
+
176
+ export const SupersetLast: Story = {
177
+ args: {
178
+ name: 'Barbell Row',
179
+ state: 'collapsed',
180
+ onToggle: () => {},
181
+ summary: { sets: 3, reps: 8, weight: 155, unit: 'lbs' },
182
+ supersetPosition: 'last',
183
+ setVelocities: [[0.9, 0.8, 0.7]],
184
+ totalPlannedSets: 3,
185
+ },
186
+ }