@titan-design/react-ui 0.3.0 → 0.5.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/MesoStatusCard-CQk71hSi.d.mts +77 -0
- package/dist/MesoStatusCard-CQk71hSi.d.ts +77 -0
- package/dist/bodymap.d.mts +246 -3
- package/dist/bodymap.d.ts +246 -3
- package/dist/bodymap.js +441 -97
- package/dist/bodymap.js.map +1 -1
- package/dist/bodymap.mjs +1958 -66
- package/dist/bodymap.mjs.map +1 -1
- package/dist/index.d.mts +71 -738
- package/dist/index.d.ts +71 -738
- package/dist/index.js +888 -1583
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +4515 -2348
- package/dist/index.mjs.map +1 -1
- package/dist/pages-D7Jlssvp.d.ts +791 -0
- package/dist/pages-MO0JCySL.d.mts +791 -0
- package/dist/pages.d.mts +4 -0
- package/dist/pages.d.ts +4 -0
- package/dist/pages.js +6646 -0
- package/dist/pages.js.map +1 -0
- package/dist/pages.mjs +6626 -0
- package/dist/pages.mjs.map +1 -0
- package/dist/theme/index.d.mts +28 -2
- package/dist/theme/index.d.ts +28 -2
- package/dist/theme/index.js +37 -0
- package/dist/theme/index.js.map +1 -1
- package/dist/theme/index.mjs +1880 -2
- package/dist/theme/index.mjs.map +1 -1
- package/dist/theme/tokens-css.mjs +573 -1
- package/dist/theme/tokens-css.mjs.map +1 -1
- package/package.json +9 -7
- package/src/components/custom/Workout/ActiveWorkoutPage.tsx +10 -15
- package/src/components/custom/Workout/BaseBadge.stories.tsx +3 -3
- package/src/components/custom/Workout/BaseBadge.test.tsx +1 -1
- package/src/components/custom/Workout/BaseBadge.tsx +1 -1
- package/src/components/custom/Workout/BodyMap.tsx +7 -8
- package/src/components/custom/Workout/BodyMapDetailPanel.tsx +25 -28
- package/src/components/custom/Workout/CapacityBandChart.tsx +2 -3
- package/src/components/custom/Workout/ExerciseCard.tsx +17 -28
- package/src/components/custom/Workout/ExerciseDetailPage.tsx +38 -31
- package/src/components/custom/Workout/InputBar.tsx +10 -11
- package/src/components/custom/Workout/IntensityBar.test.tsx +112 -60
- package/src/components/custom/Workout/IntensityBar.tsx +109 -58
- package/src/components/custom/Workout/MesoCard.tsx +2 -6
- package/src/components/custom/Workout/MesoStatusCard.tsx +17 -15
- package/src/components/custom/Workout/MuscleGroupChip.tsx +2 -4
- package/src/components/custom/Workout/PlaceholderStrip.tsx +5 -10
- package/src/components/custom/Workout/PrBadge.tsx +3 -3
- package/src/components/custom/Workout/PrHistoryModal.tsx +14 -16
- package/src/components/custom/Workout/ProgramPlanningPage.tsx +8 -12
- package/src/components/custom/Workout/README.md +50 -0
- package/src/components/custom/Workout/ReadinessCheck.tsx +13 -11
- package/src/components/custom/Workout/RestTimer.test.tsx +41 -10
- package/src/components/custom/Workout/RestTimer.tsx +55 -49
- package/src/components/custom/Workout/SetRow.tsx +20 -8
- package/src/components/custom/Workout/StrengthTrendChart.tsx +15 -17
- package/src/components/custom/Workout/TempoBar.stories.tsx +95 -0
- package/src/components/custom/Workout/TempoBar.test.tsx +103 -0
- package/src/components/custom/Workout/TempoBar.tsx +215 -0
- package/src/components/custom/Workout/TempoDisplay.stories.tsx +69 -2
- package/src/components/custom/Workout/TempoDisplay.test.tsx +35 -0
- package/src/components/custom/Workout/TempoDisplay.tsx +104 -1
- package/src/components/custom/Workout/TrainingStatusPage.tsx +8 -17
- package/src/components/custom/Workout/VelocityStrip.test.tsx +89 -2
- package/src/components/custom/Workout/VelocityStrip.tsx +210 -43
- package/src/components/custom/Workout/WeekRow.test.tsx +2 -2
- package/src/components/custom/Workout/WeekRow.tsx +2 -1
- package/src/components/custom/Workout/WeightBadge.test.tsx +2 -2
- package/src/components/custom/Workout/WeightBadge.tsx +2 -2
- package/src/components/custom/Workout/WorkoutCard.tsx +3 -8
- package/src/components/custom/Workout/icons.tsx +68 -0
- package/src/components/custom/Workout/index.ts +47 -47
- package/src/index.ts +3 -2
- package/src/pages.ts +61 -0
- package/src/test/nativewind-stub.ts +13 -0
- package/src/test/stories-smoke.test.tsx +50 -0
- package/src/theme/ThemeProvider.test.tsx +24 -0
- package/src/theme/ThemeProvider.tsx +50 -0
- package/src/theme/barrel.ts +53 -0
- package/src/theme/index.ts +8 -41
- package/src/theme/native-theming-guard.test.ts +117 -0
- package/src/theme/resolve-color.ts +19 -0
- package/dist/bodymap-BhG55xHM.d.mts +0 -318
- package/dist/bodymap-BhG55xHM.d.ts +0 -318
- package/dist/chunk-7XPB4NAO.mjs +0 -1013
- package/dist/chunk-7XPB4NAO.mjs.map +0 -1
- package/dist/chunk-P7TSQUN6.mjs +0 -846
- package/dist/chunk-P7TSQUN6.mjs.map +0 -1
- package/dist/chunk-TOD2WQBG.mjs +0 -1048
- package/dist/chunk-TOD2WQBG.mjs.map +0 -1
|
@@ -8,7 +8,6 @@ const STATUS_INFO = '#2196F3'
|
|
|
8
8
|
/** Dot outline: near-white ring so load dots read on the band fill and any
|
|
9
9
|
* surface (matches the MesoStatusCard gauge-marker convention). */
|
|
10
10
|
const DOT_BORDER = '#F3F4F6'
|
|
11
|
-
const TEXT_TERTIARY = 'var(--color-text-tertiary)'
|
|
12
11
|
const BAND_FILL = 'rgba(20,184,166,0.1)'
|
|
13
12
|
const BAND_EDGE = 'rgba(20,184,166,0.45)'
|
|
14
13
|
const PROJECTION_FILL = 'rgba(20,184,166,0.05)'
|
|
@@ -398,6 +397,7 @@ export function CapacityBandChart({
|
|
|
398
397
|
|
|
399
398
|
{/* Y axis conceptual label (no numbers). */}
|
|
400
399
|
<Text
|
|
400
|
+
className="text-text-tertiary"
|
|
401
401
|
style={{
|
|
402
402
|
position: 'absolute',
|
|
403
403
|
left: PADDING_LEFT / 2 - 14,
|
|
@@ -406,7 +406,6 @@ export function CapacityBandChart({
|
|
|
406
406
|
textAlign: 'center',
|
|
407
407
|
fontSize: 9,
|
|
408
408
|
fontFamily: 'Inter, sans-serif',
|
|
409
|
-
color: TEXT_TERTIARY,
|
|
410
409
|
transform: [{ rotate: '-90deg' }],
|
|
411
410
|
}}
|
|
412
411
|
accessibilityElementsHidden
|
|
@@ -422,6 +421,7 @@ export function CapacityBandChart({
|
|
|
422
421
|
return (
|
|
423
422
|
<Text
|
|
424
423
|
key={`x-${point.date}`}
|
|
424
|
+
className="text-text-tertiary"
|
|
425
425
|
style={{
|
|
426
426
|
position: 'absolute',
|
|
427
427
|
left: toX(point.date) - 14,
|
|
@@ -430,7 +430,6 @@ export function CapacityBandChart({
|
|
|
430
430
|
textAlign: 'center',
|
|
431
431
|
fontSize: 10,
|
|
432
432
|
fontFamily: 'Inter, sans-serif',
|
|
433
|
-
color: TEXT_TERTIARY,
|
|
434
433
|
}}
|
|
435
434
|
accessibilityElementsHidden
|
|
436
435
|
importantForAccessibility="no-hide-descendants"
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
// Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
|
|
2
2
|
import { useState } from 'react'
|
|
3
3
|
import { View, Text, Pressable } from 'react-native'
|
|
4
|
-
import { VelocityStrip } from './VelocityStrip'
|
|
4
|
+
import { VelocityStrip, type VelocityZoneBandProp } from './VelocityStrip'
|
|
5
5
|
import { PlaceholderStrip } from './PlaceholderStrip'
|
|
6
6
|
import { WeightBadge } from './WeightBadge'
|
|
7
7
|
import { PrBadge } from './PrBadge'
|
|
8
8
|
import { TempoDisplay } from './TempoDisplay'
|
|
9
9
|
import { SetRow, type SetRowProps } from './SetRow'
|
|
10
10
|
import { roundWeight } from '../../../utils/workout-format'
|
|
11
|
+
import { resolveColor } from '../../../theme/resolve-color'
|
|
11
12
|
|
|
12
13
|
export type ExerciseCardState = 'collapsed' | 'expanded' | 'upcoming'
|
|
13
14
|
|
|
@@ -25,6 +26,8 @@ export interface ExerciseCardProps {
|
|
|
25
26
|
e1rm?: { value: number; unit: 'lbs' | 'kg' }
|
|
26
27
|
isPR?: boolean
|
|
27
28
|
setVelocities?: number[][]
|
|
29
|
+
/** Optional velocity-zone bands shared across this exercise's sets (WA bands). */
|
|
30
|
+
velocityZones?: readonly VelocityZoneBandProp[]
|
|
28
31
|
totalPlannedSets?: number
|
|
29
32
|
sets?: SetRowProps[]
|
|
30
33
|
tempo?: [number, number, number, number]
|
|
@@ -34,21 +37,6 @@ export interface ExerciseCardProps {
|
|
|
34
37
|
supersetColor?: string
|
|
35
38
|
}
|
|
36
39
|
|
|
37
|
-
const COLORS = {
|
|
38
|
-
// Theme-aware tokens: text foregrounds and this card's own surfaces/borders
|
|
39
|
-
// must flip together so text stays readable in both light and dark. The
|
|
40
|
-
// expanded/pressed surfaces are self-owned here (not from a themed Card), so
|
|
41
|
-
// hardcoding dark values would leave dark-on-dark once the text flips.
|
|
42
|
-
// react-native-web passes the CSS var through to the inline style.
|
|
43
|
-
textPrimary: 'var(--color-text-primary)',
|
|
44
|
-
textSecondary: 'var(--color-text-secondary)',
|
|
45
|
-
textTertiary: 'var(--color-text-tertiary)',
|
|
46
|
-
brandPrimary: '#FF7900',
|
|
47
|
-
surfaceRaised: 'var(--color-surface-raised)',
|
|
48
|
-
surfaceElevated: 'var(--color-surface-elevated)',
|
|
49
|
-
borderDefault: 'var(--color-border-default)',
|
|
50
|
-
}
|
|
51
|
-
|
|
52
40
|
/** Column headers; the weight column reflects the card's unit (LBS / KG). */
|
|
53
41
|
function buildColumnHeaders(unit: 'lbs' | 'kg'): string[] {
|
|
54
42
|
return ['SET', 'PREV', 'REPS', unit.toUpperCase(), 'RPE']
|
|
@@ -120,6 +108,7 @@ function CollapsedCard({
|
|
|
120
108
|
e1rm,
|
|
121
109
|
isPR,
|
|
122
110
|
setVelocities,
|
|
111
|
+
velocityZones,
|
|
123
112
|
totalPlannedSets,
|
|
124
113
|
supersetPosition,
|
|
125
114
|
}: ExerciseCardProps) {
|
|
@@ -139,11 +128,11 @@ function CollapsedCard({
|
|
|
139
128
|
testID="exercise-card"
|
|
140
129
|
>
|
|
141
130
|
<View
|
|
131
|
+
className={pressed ? 'bg-surface-raised' : undefined}
|
|
142
132
|
style={{
|
|
143
133
|
padding: 12,
|
|
144
134
|
paddingHorizontal: 14,
|
|
145
135
|
cursor: 'pointer',
|
|
146
|
-
backgroundColor: pressed ? COLORS.surfaceRaised : 'transparent',
|
|
147
136
|
...borderRadius,
|
|
148
137
|
...supersetMargin,
|
|
149
138
|
}}
|
|
@@ -153,11 +142,11 @@ function CollapsedCard({
|
|
|
153
142
|
testID="exercise-card-header"
|
|
154
143
|
>
|
|
155
144
|
<Text
|
|
145
|
+
className="text-text-primary"
|
|
156
146
|
style={{
|
|
157
147
|
fontSize: 14,
|
|
158
148
|
fontFamily: '"Space Grotesk", sans-serif',
|
|
159
149
|
fontWeight: '700',
|
|
160
|
-
color: COLORS.textPrimary,
|
|
161
150
|
}}
|
|
162
151
|
testID="exercise-card-name"
|
|
163
152
|
>
|
|
@@ -166,10 +155,10 @@ function CollapsedCard({
|
|
|
166
155
|
<View className="flex-1" />
|
|
167
156
|
{summary && (
|
|
168
157
|
<Text
|
|
158
|
+
className="text-text-secondary"
|
|
169
159
|
style={{
|
|
170
160
|
fontSize: 12,
|
|
171
161
|
fontFamily: 'Inter, sans-serif',
|
|
172
|
-
color: COLORS.textSecondary,
|
|
173
162
|
marginRight: 8,
|
|
174
163
|
}}
|
|
175
164
|
testID="exercise-card-summary"
|
|
@@ -203,6 +192,7 @@ function CollapsedCard({
|
|
|
203
192
|
<VelocityStrip
|
|
204
193
|
key={i}
|
|
205
194
|
velocities={velocities}
|
|
195
|
+
zones={velocityZones}
|
|
206
196
|
variant="mini"
|
|
207
197
|
testID={`exercise-card-velocity-strip-${i}`}
|
|
208
198
|
/>
|
|
@@ -239,10 +229,9 @@ function ExpandedCard({
|
|
|
239
229
|
|
|
240
230
|
return (
|
|
241
231
|
<View
|
|
232
|
+
className="bg-surface-elevated border-border"
|
|
242
233
|
style={{
|
|
243
|
-
backgroundColor: COLORS.surfaceElevated,
|
|
244
234
|
borderWidth: 1,
|
|
245
|
-
borderColor: COLORS.borderDefault,
|
|
246
235
|
...borderRadius,
|
|
247
236
|
...supersetMargin,
|
|
248
237
|
}}
|
|
@@ -262,15 +251,15 @@ function ExpandedCard({
|
|
|
262
251
|
paddingHorizontal: 14,
|
|
263
252
|
paddingBottom: 10,
|
|
264
253
|
borderBottomWidth: 1,
|
|
265
|
-
borderBottomColor:
|
|
254
|
+
borderBottomColor: resolveColor('border-default'),
|
|
266
255
|
}}
|
|
267
256
|
>
|
|
268
257
|
<Text
|
|
258
|
+
className="text-text-primary"
|
|
269
259
|
style={{
|
|
270
260
|
fontSize: 14,
|
|
271
261
|
fontFamily: '"Space Grotesk", sans-serif',
|
|
272
262
|
fontWeight: '700',
|
|
273
|
-
color: COLORS.textPrimary,
|
|
274
263
|
}}
|
|
275
264
|
testID="exercise-card-name"
|
|
276
265
|
>
|
|
@@ -279,10 +268,10 @@ function ExpandedCard({
|
|
|
279
268
|
<View className="flex-1" />
|
|
280
269
|
{summary && (
|
|
281
270
|
<Text
|
|
271
|
+
className="text-text-secondary"
|
|
282
272
|
style={{
|
|
283
273
|
fontSize: 12,
|
|
284
274
|
fontFamily: 'Inter, sans-serif',
|
|
285
|
-
color: COLORS.textSecondary,
|
|
286
275
|
marginRight: 8,
|
|
287
276
|
}}
|
|
288
277
|
testID="exercise-card-summary"
|
|
@@ -339,13 +328,13 @@ function ExpandedCard({
|
|
|
339
328
|
{...(isFlex ? { className: 'flex-1' } : {})}
|
|
340
329
|
>
|
|
341
330
|
<Text
|
|
331
|
+
className="text-text-tertiary"
|
|
342
332
|
style={{
|
|
343
333
|
fontSize: 10,
|
|
344
334
|
fontWeight: '600',
|
|
345
335
|
fontFamily: 'Inter, sans-serif',
|
|
346
336
|
textTransform: 'uppercase',
|
|
347
337
|
letterSpacing: 0.8,
|
|
348
|
-
color: COLORS.textTertiary,
|
|
349
338
|
}}
|
|
350
339
|
>
|
|
351
340
|
{header}
|
|
@@ -389,11 +378,11 @@ function UpcomingCard({
|
|
|
389
378
|
>
|
|
390
379
|
<View className="flex-row items-center">
|
|
391
380
|
<Text
|
|
381
|
+
className="text-text-primary"
|
|
392
382
|
style={{
|
|
393
383
|
fontSize: 14,
|
|
394
384
|
fontFamily: '"Space Grotesk", sans-serif',
|
|
395
385
|
fontWeight: '700',
|
|
396
|
-
color: COLORS.textPrimary,
|
|
397
386
|
}}
|
|
398
387
|
testID="exercise-card-name"
|
|
399
388
|
>
|
|
@@ -401,10 +390,10 @@ function UpcomingCard({
|
|
|
401
390
|
</Text>
|
|
402
391
|
{prescription && (
|
|
403
392
|
<Text
|
|
393
|
+
className="text-text-secondary"
|
|
404
394
|
style={{
|
|
405
395
|
fontSize: 12,
|
|
406
396
|
fontFamily: 'Inter, sans-serif',
|
|
407
|
-
color: COLORS.textSecondary,
|
|
408
397
|
marginLeft: 8,
|
|
409
398
|
}}
|
|
410
399
|
testID="exercise-card-prescription"
|
|
@@ -415,10 +404,10 @@ function UpcomingCard({
|
|
|
415
404
|
<View className="flex-1" />
|
|
416
405
|
{previousBest && (
|
|
417
406
|
<Text
|
|
407
|
+
className="text-text-tertiary"
|
|
418
408
|
style={{
|
|
419
409
|
fontSize: 11,
|
|
420
410
|
fontFamily: 'Inter, sans-serif',
|
|
421
|
-
color: COLORS.textTertiary,
|
|
422
411
|
}}
|
|
423
412
|
testID="exercise-card-previous-best"
|
|
424
413
|
>
|
|
@@ -15,15 +15,16 @@ import {
|
|
|
15
15
|
} from './CapacityBandChart'
|
|
16
16
|
import { ExerciseCard, type ExerciseCardProps } from './ExerciseCard'
|
|
17
17
|
import { type SetRowProps } from './SetRow'
|
|
18
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
VelocityStrip,
|
|
20
|
+
calculateMeanVelocity,
|
|
21
|
+
calculateVelocityLoss,
|
|
22
|
+
type VelocityZoneBandProp,
|
|
23
|
+
} from './VelocityStrip'
|
|
24
|
+
import { resolveColor } from '../../../theme/resolve-color'
|
|
25
|
+
import { cn } from '../../../utils/cn'
|
|
19
26
|
|
|
20
|
-
const SURFACE_ELEVATED = 'var(--color-surface-elevated)'
|
|
21
|
-
const BORDER_DEFAULT = 'var(--color-border-default)'
|
|
22
27
|
const BRAND_PRIMARY = '#FF7900'
|
|
23
|
-
const TEXT_PRIMARY = 'var(--color-text-primary)'
|
|
24
|
-
const TEXT_SECONDARY = 'var(--color-text-secondary)'
|
|
25
|
-
const TEXT_TERTIARY = 'var(--color-text-tertiary)'
|
|
26
|
-
const PAGE_BG = 'var(--color-background-base)'
|
|
27
28
|
|
|
28
29
|
/** Inner plot width: page width 390 − 16 page padding − 12 card padding on each side. */
|
|
29
30
|
const CHART_WIDTH = 326
|
|
@@ -101,6 +102,8 @@ export interface ExerciseVbt {
|
|
|
101
102
|
projection?: CapacityBandProjection
|
|
102
103
|
/** Per-set velocity traces for the most recent session. */
|
|
103
104
|
sets: ExerciseVbtSet[]
|
|
105
|
+
/** Optional velocity-zone bands for this exercise (WA `VelocityZones.bands`). */
|
|
106
|
+
zones?: readonly VelocityZoneBandProp[]
|
|
104
107
|
}
|
|
105
108
|
|
|
106
109
|
/** Page-level roll-up of the logged sessions. */
|
|
@@ -190,7 +193,7 @@ function TabBar({ active, onSelect }: TabBarProps) {
|
|
|
190
193
|
className="flex-row"
|
|
191
194
|
style={{
|
|
192
195
|
borderBottomWidth: 1,
|
|
193
|
-
borderBottomColor:
|
|
196
|
+
borderBottomColor: resolveColor('border-default'),
|
|
194
197
|
}}
|
|
195
198
|
accessibilityRole={'tablist' as ViewProps['accessibilityRole']}
|
|
196
199
|
testID="exercise-detail-page-tabs"
|
|
@@ -213,11 +216,11 @@ function TabBar({ active, onSelect }: TabBarProps) {
|
|
|
213
216
|
testID={`exercise-detail-page-tab-${key}`}
|
|
214
217
|
>
|
|
215
218
|
<Text
|
|
219
|
+
className={isActive ? 'text-text-primary' : 'text-text-tertiary'}
|
|
216
220
|
style={{
|
|
217
221
|
fontSize: 13,
|
|
218
222
|
fontFamily: 'Inter, sans-serif',
|
|
219
223
|
fontWeight: isActive ? '700' : '500',
|
|
220
|
-
color: isActive ? TEXT_PRIMARY : TEXT_TERTIARY,
|
|
221
224
|
}}
|
|
222
225
|
>
|
|
223
226
|
{label}
|
|
@@ -244,27 +247,27 @@ function StatStrip({ stats, unit }: { stats: ExerciseDetailStats; unit: 'lbs' |
|
|
|
244
247
|
return (
|
|
245
248
|
<View
|
|
246
249
|
key={key}
|
|
250
|
+
className="bg-surface-elevated border-border"
|
|
247
251
|
style={{
|
|
248
252
|
flex: 1,
|
|
249
253
|
padding: 12,
|
|
250
254
|
borderRadius: 10,
|
|
251
|
-
backgroundColor: SURFACE_ELEVATED,
|
|
252
255
|
borderWidth: 1,
|
|
253
|
-
borderColor: BORDER_DEFAULT,
|
|
254
256
|
}}
|
|
255
257
|
testID={`exercise-detail-page-stat-${key}`}
|
|
256
258
|
>
|
|
257
259
|
<Text
|
|
260
|
+
className="text-text-primary"
|
|
258
261
|
style={{
|
|
259
262
|
fontSize: 18,
|
|
260
263
|
fontFamily: '"Space Grotesk", sans-serif',
|
|
261
264
|
fontWeight: '700',
|
|
262
|
-
color: TEXT_PRIMARY,
|
|
263
265
|
}}
|
|
264
266
|
>
|
|
265
267
|
{value}
|
|
266
268
|
</Text>
|
|
267
269
|
<Text
|
|
270
|
+
className="text-text-tertiary"
|
|
268
271
|
style={{
|
|
269
272
|
marginTop: 2,
|
|
270
273
|
fontSize: 10,
|
|
@@ -272,7 +275,6 @@ function StatStrip({ stats, unit }: { stats: ExerciseDetailStats; unit: 'lbs' |
|
|
|
272
275
|
fontWeight: '600',
|
|
273
276
|
letterSpacing: 0.4,
|
|
274
277
|
textTransform: 'uppercase',
|
|
275
|
-
color: TEXT_TERTIARY,
|
|
276
278
|
}}
|
|
277
279
|
>
|
|
278
280
|
{label}
|
|
@@ -295,10 +297,9 @@ function SectionCard({
|
|
|
295
297
|
}) {
|
|
296
298
|
return (
|
|
297
299
|
<View
|
|
300
|
+
className="bg-surface-elevated border-border"
|
|
298
301
|
style={{
|
|
299
|
-
backgroundColor: SURFACE_ELEVATED,
|
|
300
302
|
borderWidth: 1,
|
|
301
|
-
borderColor: BORDER_DEFAULT,
|
|
302
303
|
borderRadius: 12,
|
|
303
304
|
padding: 12,
|
|
304
305
|
gap: 10,
|
|
@@ -306,13 +307,13 @@ function SectionCard({
|
|
|
306
307
|
testID={testID}
|
|
307
308
|
>
|
|
308
309
|
<Text
|
|
310
|
+
className="text-text-tertiary"
|
|
309
311
|
style={{
|
|
310
312
|
fontSize: 11,
|
|
311
313
|
fontFamily: 'Inter, sans-serif',
|
|
312
314
|
fontWeight: '600',
|
|
313
315
|
letterSpacing: 0.6,
|
|
314
316
|
textTransform: 'uppercase',
|
|
315
|
-
color: TEXT_TERTIARY,
|
|
316
317
|
}}
|
|
317
318
|
>
|
|
318
319
|
{title}
|
|
@@ -334,7 +335,8 @@ function EntryList({ entries, expandedId, onToggle, emptyLabel, testID }: EntryL
|
|
|
334
335
|
if (entries.length === 0) {
|
|
335
336
|
return (
|
|
336
337
|
<Text
|
|
337
|
-
|
|
338
|
+
className="text-text-tertiary"
|
|
339
|
+
style={{ fontSize: 12, fontFamily: 'Inter, sans-serif' }}
|
|
338
340
|
testID={`${testID}-empty`}
|
|
339
341
|
>
|
|
340
342
|
{emptyLabel}
|
|
@@ -353,7 +355,13 @@ function EntryList({ entries, expandedId, onToggle, emptyLabel, testID }: EntryL
|
|
|
353
355
|
)
|
|
354
356
|
}
|
|
355
357
|
|
|
356
|
-
function VbtBreakdown({
|
|
358
|
+
function VbtBreakdown({
|
|
359
|
+
sets,
|
|
360
|
+
zones,
|
|
361
|
+
}: {
|
|
362
|
+
sets: ExerciseVbtSet[]
|
|
363
|
+
zones?: readonly VelocityZoneBandProp[]
|
|
364
|
+
}) {
|
|
357
365
|
return (
|
|
358
366
|
<View style={{ gap: 10 }} testID="exercise-detail-page-vbt-breakdown">
|
|
359
367
|
{sets.map((set) => {
|
|
@@ -367,25 +375,25 @@ function VbtBreakdown({ sets }: { sets: ExerciseVbtSet[] }) {
|
|
|
367
375
|
testID="exercise-detail-page-vbt-set"
|
|
368
376
|
>
|
|
369
377
|
<Text
|
|
378
|
+
className="text-text-secondary"
|
|
370
379
|
style={{
|
|
371
380
|
width: 44,
|
|
372
381
|
fontSize: 11,
|
|
373
382
|
fontFamily: 'Inter, sans-serif',
|
|
374
383
|
fontWeight: '600',
|
|
375
|
-
color: TEXT_SECONDARY,
|
|
376
384
|
}}
|
|
377
385
|
>
|
|
378
386
|
{set.label}
|
|
379
387
|
</Text>
|
|
380
388
|
<View className="flex-1" style={{ height: 8, justifyContent: 'center' }}>
|
|
381
|
-
<VelocityStrip velocities={set.velocities} variant="mini" />
|
|
389
|
+
<VelocityStrip velocities={set.velocities} zones={zones} variant="mini" />
|
|
382
390
|
</View>
|
|
383
391
|
<Text
|
|
392
|
+
className="text-text-tertiary"
|
|
384
393
|
style={{
|
|
385
394
|
fontSize: 11,
|
|
386
395
|
fontFamily: 'Inter, sans-serif',
|
|
387
396
|
fontVariant: ['tabular-nums'],
|
|
388
|
-
color: TEXT_TERTIARY,
|
|
389
397
|
}}
|
|
390
398
|
>
|
|
391
399
|
{`${mean.toFixed(2)} · -${loss}%`}
|
|
@@ -407,26 +415,26 @@ function VbtSummaryRow({ summary }: { summary: VbtSummary }) {
|
|
|
407
415
|
{cells.map((cell) => (
|
|
408
416
|
<View
|
|
409
417
|
key={cell.label}
|
|
418
|
+
className="bg-surface-elevated border-border"
|
|
410
419
|
style={{
|
|
411
420
|
flex: 1,
|
|
412
421
|
padding: 12,
|
|
413
422
|
borderRadius: 10,
|
|
414
|
-
backgroundColor: SURFACE_ELEVATED,
|
|
415
423
|
borderWidth: 1,
|
|
416
|
-
borderColor: BORDER_DEFAULT,
|
|
417
424
|
}}
|
|
418
425
|
>
|
|
419
426
|
<Text
|
|
427
|
+
className="text-text-primary"
|
|
420
428
|
style={{
|
|
421
429
|
fontSize: 16,
|
|
422
430
|
fontFamily: '"Space Grotesk", sans-serif',
|
|
423
431
|
fontWeight: '700',
|
|
424
|
-
color: TEXT_PRIMARY,
|
|
425
432
|
}}
|
|
426
433
|
>
|
|
427
434
|
{cell.value}
|
|
428
435
|
</Text>
|
|
429
436
|
<Text
|
|
437
|
+
className="text-text-tertiary"
|
|
430
438
|
style={{
|
|
431
439
|
marginTop: 2,
|
|
432
440
|
fontSize: 10,
|
|
@@ -434,7 +442,6 @@ function VbtSummaryRow({ summary }: { summary: VbtSummary }) {
|
|
|
434
442
|
fontWeight: '600',
|
|
435
443
|
letterSpacing: 0.4,
|
|
436
444
|
textTransform: 'uppercase',
|
|
437
|
-
color: TEXT_TERTIARY,
|
|
438
445
|
}}
|
|
439
446
|
>
|
|
440
447
|
{cell.label}
|
|
@@ -488,8 +495,8 @@ export function ExerciseDetailPage({
|
|
|
488
495
|
|
|
489
496
|
return (
|
|
490
497
|
<View
|
|
491
|
-
className={className}
|
|
492
|
-
style={{ width: 390
|
|
498
|
+
className={cn('bg-background-base', className)}
|
|
499
|
+
style={{ width: 390 }}
|
|
493
500
|
accessibilityRole={'main' as ViewProps['accessibilityRole']}
|
|
494
501
|
aria-label={exercise.name}
|
|
495
502
|
testID="exercise-detail-page"
|
|
@@ -504,22 +511,22 @@ export function ExerciseDetailPage({
|
|
|
504
511
|
<View style={{ flexShrink: 1 }}>
|
|
505
512
|
<Text
|
|
506
513
|
accessibilityRole="header"
|
|
514
|
+
className="text-text-primary"
|
|
507
515
|
style={{
|
|
508
516
|
fontSize: 20,
|
|
509
517
|
fontFamily: '"Space Grotesk", sans-serif',
|
|
510
518
|
fontWeight: '700',
|
|
511
|
-
color: TEXT_PRIMARY,
|
|
512
519
|
}}
|
|
513
520
|
testID="exercise-detail-page-title"
|
|
514
521
|
>
|
|
515
522
|
{exercise.name}
|
|
516
523
|
</Text>
|
|
517
524
|
<Text
|
|
525
|
+
className="text-text-secondary"
|
|
518
526
|
style={{
|
|
519
527
|
marginTop: 2,
|
|
520
528
|
fontSize: 12,
|
|
521
529
|
fontFamily: 'Inter, sans-serif',
|
|
522
|
-
color: TEXT_SECONDARY,
|
|
523
530
|
}}
|
|
524
531
|
testID="exercise-detail-page-subtitle"
|
|
525
532
|
>
|
|
@@ -539,13 +546,13 @@ export function ExerciseDetailPage({
|
|
|
539
546
|
testID="exercise-detail-page-e1rm"
|
|
540
547
|
>
|
|
541
548
|
<Text
|
|
549
|
+
className="text-text-tertiary"
|
|
542
550
|
style={{
|
|
543
551
|
fontSize: 9,
|
|
544
552
|
fontFamily: 'Inter, sans-serif',
|
|
545
553
|
fontWeight: '600',
|
|
546
554
|
letterSpacing: 0.4,
|
|
547
555
|
textTransform: 'uppercase',
|
|
548
|
-
color: TEXT_TERTIARY,
|
|
549
556
|
}}
|
|
550
557
|
>
|
|
551
558
|
e1RM
|
|
@@ -620,7 +627,7 @@ export function ExerciseDetailPage({
|
|
|
620
627
|
</SectionCard>
|
|
621
628
|
<VbtSummaryRow summary={vbtSummary} />
|
|
622
629
|
<SectionCard title="Velocity Breakdown" testID="exercise-detail-page-velocity">
|
|
623
|
-
<VbtBreakdown sets={vbt.sets} />
|
|
630
|
+
<VbtBreakdown sets={vbt.sets} zones={vbt.zones} />
|
|
624
631
|
</SectionCard>
|
|
625
632
|
</View>
|
|
626
633
|
)}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
|
|
2
2
|
import React from 'react'
|
|
3
3
|
import { View, Text, Pressable, TextInput } from 'react-native'
|
|
4
|
+
import { resolveColor } from '../../../theme/resolve-color'
|
|
4
5
|
|
|
5
6
|
export interface InputBarProps {
|
|
6
7
|
exerciseName: string
|
|
@@ -17,19 +18,15 @@ export interface InputBarProps {
|
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
const BRAND_PRIMARY = '#FF7900'
|
|
20
|
-
const
|
|
21
|
-
const TEXT_TERTIARY = 'var(--color-text-tertiary)'
|
|
21
|
+
const INPUT_CLASSNAME = 'bg-surface-raised border-border-strong text-text-primary'
|
|
22
22
|
|
|
23
23
|
const inputStyle = {
|
|
24
24
|
fontSize: 14,
|
|
25
25
|
fontWeight: '600' as const,
|
|
26
26
|
fontFamily: 'Inter, sans-serif',
|
|
27
|
-
backgroundColor: 'var(--color-surface-raised)',
|
|
28
27
|
borderWidth: 1,
|
|
29
|
-
borderColor: 'var(--color-border-strong)',
|
|
30
28
|
borderRadius: 6,
|
|
31
29
|
textAlign: 'center' as const,
|
|
32
|
-
color: TEXT_PRIMARY,
|
|
33
30
|
paddingVertical: 5,
|
|
34
31
|
paddingHorizontal: 2,
|
|
35
32
|
}
|
|
@@ -53,11 +50,11 @@ export function InputBar({
|
|
|
53
50
|
|
|
54
51
|
return (
|
|
55
52
|
<View
|
|
53
|
+
className="bg-surface-elevated"
|
|
56
54
|
style={{
|
|
57
55
|
width: '100%',
|
|
58
|
-
backgroundColor: 'var(--color-surface-elevated)',
|
|
59
56
|
borderTopWidth: 1,
|
|
60
|
-
borderTopColor: '
|
|
57
|
+
borderTopColor: resolveColor('border-default'),
|
|
61
58
|
paddingTop: 10,
|
|
62
59
|
paddingHorizontal: 16,
|
|
63
60
|
paddingBottom: 12,
|
|
@@ -70,11 +67,11 @@ export function InputBar({
|
|
|
70
67
|
>
|
|
71
68
|
<View style={{ minWidth: 80, flexDirection: 'column' }}>
|
|
72
69
|
<Text
|
|
70
|
+
className="text-text-primary"
|
|
73
71
|
style={{
|
|
74
72
|
fontSize: 12,
|
|
75
73
|
fontWeight: '700',
|
|
76
74
|
fontFamily: '"Space Grotesk", sans-serif',
|
|
77
|
-
color: TEXT_PRIMARY,
|
|
78
75
|
}}
|
|
79
76
|
testID="input-bar-exercise-name"
|
|
80
77
|
numberOfLines={1}
|
|
@@ -82,10 +79,10 @@ export function InputBar({
|
|
|
82
79
|
{exerciseName}
|
|
83
80
|
</Text>
|
|
84
81
|
<Text
|
|
82
|
+
className="text-text-tertiary"
|
|
85
83
|
style={{
|
|
86
84
|
fontSize: 10,
|
|
87
85
|
fontFamily: 'Inter, sans-serif',
|
|
88
|
-
color: TEXT_TERTIARY,
|
|
89
86
|
}}
|
|
90
87
|
testID="input-bar-set-info"
|
|
91
88
|
>
|
|
@@ -97,16 +94,17 @@ export function InputBar({
|
|
|
97
94
|
<TextInput
|
|
98
95
|
value={reps}
|
|
99
96
|
onChangeText={onRepsChange}
|
|
97
|
+
className={INPUT_CLASSNAME}
|
|
100
98
|
style={{ ...inputStyle, width: 36 }}
|
|
101
99
|
accessibilityLabel="Reps"
|
|
102
100
|
testID="input-bar-reps"
|
|
103
101
|
keyboardType="numeric"
|
|
104
102
|
/>
|
|
105
103
|
<Text
|
|
104
|
+
className="text-text-tertiary"
|
|
106
105
|
style={{
|
|
107
106
|
fontSize: 12,
|
|
108
107
|
fontFamily: 'Inter, sans-serif',
|
|
109
|
-
color: TEXT_TERTIARY,
|
|
110
108
|
}}
|
|
111
109
|
>
|
|
112
110
|
{'\u00D7'}
|
|
@@ -114,17 +112,18 @@ export function InputBar({
|
|
|
114
112
|
<TextInput
|
|
115
113
|
value={weight}
|
|
116
114
|
onChangeText={onWeightChange}
|
|
115
|
+
className={INPUT_CLASSNAME}
|
|
117
116
|
style={{ ...inputStyle, width: 52 }}
|
|
118
117
|
accessibilityLabel={`Weight in ${unit}`}
|
|
119
118
|
testID="input-bar-weight"
|
|
120
119
|
keyboardType="numeric"
|
|
121
120
|
/>
|
|
122
121
|
<Text
|
|
122
|
+
className="text-text-tertiary"
|
|
123
123
|
style={{
|
|
124
124
|
fontSize: 12,
|
|
125
125
|
fontWeight: '500',
|
|
126
126
|
fontFamily: 'Inter, sans-serif',
|
|
127
|
-
color: TEXT_TERTIARY,
|
|
128
127
|
}}
|
|
129
128
|
testID="input-bar-unit"
|
|
130
129
|
>
|