@titan-design/react-ui 0.2.7 → 0.4.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-BhG55xHM.d.mts +318 -0
- package/dist/bodymap-BhG55xHM.d.ts +318 -0
- package/dist/bodymap.d.mts +3 -0
- package/dist/bodymap.d.ts +3 -0
- package/dist/bodymap.js +3014 -0
- package/dist/bodymap.js.map +1 -0
- package/dist/bodymap.mjs +1088 -0
- package/dist/bodymap.mjs.map +1 -0
- package/dist/chunk-2SISKTWM.mjs +1027 -0
- package/dist/chunk-2SISKTWM.mjs.map +1 -0
- package/dist/{chunk-5SSKGS6E.mjs → chunk-7XPB4NAO.mjs} +124 -4
- package/dist/chunk-7XPB4NAO.mjs.map +1 -0
- package/dist/{chunk-3VLOBS6O.mjs → chunk-SNVKL5WZ.mjs} +42 -5
- package/dist/chunk-SNVKL5WZ.mjs.map +1 -0
- package/dist/index.d.mts +416 -250
- package/dist/index.d.ts +416 -250
- package/dist/index.js +1567 -1123
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1491 -2201
- package/dist/index.mjs.map +1 -1
- package/dist/theme/index.d.mts +288 -2
- package/dist/theme/index.d.ts +288 -2
- package/dist/theme/index.js +159 -2
- package/dist/theme/index.js.map +1 -1
- package/dist/theme/index.mjs +2 -2
- package/dist/theme/tokens-css.d.mts +5 -3
- package/dist/theme/tokens-css.d.ts +5 -3
- package/dist/theme/tokens-css.js +318 -9
- package/dist/theme/tokens-css.js.map +1 -1
- package/dist/theme/tokens-css.mjs +1 -1
- package/dist/theme/tokens-css.mjs.map +1 -1
- package/package.json +10 -1
- package/src/bodymap.ts +37 -0
- package/src/components/custom/Table/Table.test.tsx +27 -0
- package/src/components/custom/Table/Table.tsx +33 -17
- package/src/components/custom/Workout/ActiveWorkoutPage.stories.tsx +118 -0
- package/src/components/custom/Workout/ActiveWorkoutPage.test.tsx +171 -0
- package/src/components/custom/Workout/ActiveWorkoutPage.tsx +442 -0
- package/src/components/custom/Workout/BodyMap.tsx +7 -9
- package/src/components/custom/Workout/BodyMapDetailPanel.tsx +25 -29
- package/src/components/custom/Workout/CapacityBandChart.tsx +2 -3
- package/src/components/custom/Workout/ExerciseCard.test.tsx +24 -0
- package/src/components/custom/Workout/ExerciseCard.tsx +24 -26
- package/src/components/custom/Workout/ExerciseDetailPage.stories.tsx +225 -0
- package/src/components/custom/Workout/ExerciseDetailPage.test.tsx +128 -0
- package/src/components/custom/Workout/ExerciseDetailPage.tsx +624 -0
- package/src/components/custom/Workout/InputBar.tsx +10 -12
- package/src/components/custom/Workout/MesoCard.tsx +2 -4
- package/src/components/custom/Workout/MesoProgressBar.test.tsx +12 -0
- package/src/components/custom/Workout/MesoProgressBar.tsx +5 -1
- 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 +2 -4
- package/src/components/custom/Workout/PrHistoryModal.tsx +12 -14
- package/src/components/custom/Workout/ProgramPlanningPage.stories.tsx +257 -0
- package/src/components/custom/Workout/ProgramPlanningPage.test.tsx +142 -0
- package/src/components/custom/Workout/ProgramPlanningPage.tsx +418 -0
- package/src/components/custom/Workout/ReadinessCheck.tsx +13 -12
- package/src/components/custom/Workout/RestTimer.test.tsx +10 -0
- package/src/components/custom/Workout/RestTimer.tsx +12 -9
- package/src/components/custom/Workout/SetRow.tsx +16 -19
- package/src/components/custom/Workout/StatusDot.tsx +1 -0
- 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.tsx +3 -1
- package/src/components/custom/Workout/TrainingStatusPage.stories.tsx +212 -0
- package/src/components/custom/Workout/TrainingStatusPage.test.tsx +131 -0
- package/src/components/custom/Workout/TrainingStatusPage.tsx +312 -0
- package/src/components/custom/Workout/VelocityStrip.test.tsx +10 -0
- package/src/components/custom/Workout/VelocityStrip.tsx +19 -16
- package/src/components/custom/Workout/WeekRow.tsx +2 -1
- package/src/components/custom/Workout/WorkoutCard.tsx +3 -6
- package/src/components/custom/Workout/index.ts +80 -32
- package/src/test/nativewind-stub.ts +13 -0
- package/src/theme/ThemeProvider.test.tsx +24 -0
- package/src/theme/ThemeProvider.tsx +50 -0
- package/src/theme/config.completeness.test.ts +77 -0
- package/src/theme/config.ts +151 -0
- package/src/theme/index.ts +2 -0
- package/src/theme/manifest/design-freeze.test.ts +155 -0
- package/src/theme/manifest/extract-css-properties.fixture.html +29 -0
- package/src/theme/manifest/extract-css-properties.test.ts +112 -0
- package/src/theme/resolve-color.ts +19 -0
- package/src/theme/tokens-css.ts +5 -3
- package/src/theme/workout-tokens.ts +12 -14
- package/src/utils/index.ts +10 -0
- package/src/utils/workout-format.test.ts +81 -0
- package/src/utils/workout-format.ts +83 -0
- package/dist/chunk-3VLOBS6O.mjs.map +0 -1
- package/dist/chunk-5SSKGS6E.mjs.map +0 -1
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
type ViewStyle,
|
|
12
12
|
} from 'react-native'
|
|
13
13
|
import { Badge, type BadgeColor } from '../../ui/badge'
|
|
14
|
-
import { WORKOUT_TOKENS } from '../../../theme/workout-tokens'
|
|
15
14
|
import { Sparkline } from './Sparkline'
|
|
16
15
|
import {
|
|
17
16
|
MuscleGroup,
|
|
@@ -20,14 +19,7 @@ import {
|
|
|
20
19
|
type VolumeStatus,
|
|
21
20
|
} from './muscleTaxonomy'
|
|
22
21
|
|
|
23
|
-
const SURFACE_ELEVATED = WORKOUT_TOKENS.surface.elevated
|
|
24
|
-
const SURFACE_RAISED = WORKOUT_TOKENS.surface.raised
|
|
25
|
-
const BORDER_STRONG = WORKOUT_TOKENS.border.strong
|
|
26
|
-
const BORDER_DEFAULT = WORKOUT_TOKENS.border.default
|
|
27
22
|
const BRAND_PRIMARY = '#FF7900'
|
|
28
|
-
const TEXT_PRIMARY = '#F3F4F6'
|
|
29
|
-
const TEXT_SECONDARY = '#9CA3AF'
|
|
30
|
-
const TEXT_TERTIARY = '#6B7280'
|
|
31
23
|
|
|
32
24
|
/** Volume track gradient: status-info (blue) -> result-improve (teal) -> status-error (red). */
|
|
33
25
|
const VOLUME_GRADIENT = 'linear-gradient(90deg, #2196F3 0%, #14B8A6 50%, #D14343 100%)'
|
|
@@ -206,8 +198,8 @@ export function BodyMapDetailPanel({
|
|
|
206
198
|
accessibilityRole={'dialog' as ViewProps['accessibilityRole']}
|
|
207
199
|
accessibilityLabel={dialogLabel}
|
|
208
200
|
aria-label={dialogLabel}
|
|
201
|
+
className="bg-surface-elevated"
|
|
209
202
|
style={{
|
|
210
|
-
backgroundColor: SURFACE_ELEVATED,
|
|
211
203
|
borderTopLeftRadius: 16,
|
|
212
204
|
borderTopRightRadius: 16,
|
|
213
205
|
maxHeight: '88%',
|
|
@@ -225,7 +217,8 @@ export function BodyMapDetailPanel({
|
|
|
225
217
|
testID="body-map-detail-panel-handle"
|
|
226
218
|
>
|
|
227
219
|
<View
|
|
228
|
-
|
|
220
|
+
className="bg-border-strong"
|
|
221
|
+
style={{ width: 40, height: 4, borderRadius: 2 }}
|
|
229
222
|
accessibilityElementsHidden
|
|
230
223
|
/>
|
|
231
224
|
</Pressable>
|
|
@@ -243,11 +236,11 @@ export function BodyMapDetailPanel({
|
|
|
243
236
|
<View className="flex-row items-center" style={{ gap: 8, flexShrink: 1 }}>
|
|
244
237
|
<Text
|
|
245
238
|
accessibilityRole="header"
|
|
239
|
+
className="text-text-primary"
|
|
246
240
|
style={{
|
|
247
241
|
fontSize: 16,
|
|
248
242
|
fontFamily: '"Space Grotesk", sans-serif',
|
|
249
243
|
fontWeight: '700',
|
|
250
|
-
color: TEXT_PRIMARY,
|
|
251
244
|
}}
|
|
252
245
|
testID="body-map-detail-panel-title"
|
|
253
246
|
>
|
|
@@ -270,13 +263,14 @@ export function BodyMapDetailPanel({
|
|
|
270
263
|
style={{ padding: 4, margin: -4 }}
|
|
271
264
|
testID="body-map-detail-panel-close"
|
|
272
265
|
>
|
|
273
|
-
<Text style={{ fontSize: 22, lineHeight: 22
|
|
266
|
+
<Text className="text-text-secondary" style={{ fontSize: 22, lineHeight: 22 }}>{'×'}</Text>
|
|
274
267
|
</Pressable>
|
|
275
268
|
</View>
|
|
276
269
|
|
|
277
270
|
{lastTrained != null && (
|
|
278
271
|
<Text
|
|
279
|
-
|
|
272
|
+
className="text-text-secondary"
|
|
273
|
+
style={{ fontSize: 12, fontFamily: 'Inter, sans-serif' }}
|
|
280
274
|
testID="body-map-detail-panel-last-trained"
|
|
281
275
|
>
|
|
282
276
|
{`Last trained ${lastTrained}`}
|
|
@@ -289,10 +283,10 @@ export function BodyMapDetailPanel({
|
|
|
289
283
|
style={{ marginBottom: 6 }}
|
|
290
284
|
accessibilityElementsHidden
|
|
291
285
|
>
|
|
292
|
-
<Text style={{ fontSize: 10, fontFamily: 'Inter, sans-serif', fontWeight: '600'
|
|
286
|
+
<Text className="text-text-tertiary" style={{ fontSize: 10, fontFamily: 'Inter, sans-serif', fontWeight: '600' }}>
|
|
293
287
|
{`MEV ${landmarks.mev}`}
|
|
294
288
|
</Text>
|
|
295
|
-
<Text style={{ fontSize: 10, fontFamily: 'Inter, sans-serif', fontWeight: '600'
|
|
289
|
+
<Text className="text-text-tertiary" style={{ fontSize: 10, fontFamily: 'Inter, sans-serif', fontWeight: '600' }}>
|
|
296
290
|
{`MRV ${landmarks.mrv}`}
|
|
297
291
|
</Text>
|
|
298
292
|
</View>
|
|
@@ -311,6 +305,7 @@ export function BodyMapDetailPanel({
|
|
|
311
305
|
accessibilityElementsHidden
|
|
312
306
|
/>
|
|
313
307
|
<View
|
|
308
|
+
className="border-text-primary bg-surface-elevated"
|
|
314
309
|
style={{
|
|
315
310
|
position: 'absolute',
|
|
316
311
|
left: `${markerLeft}%`,
|
|
@@ -319,8 +314,6 @@ export function BodyMapDetailPanel({
|
|
|
319
314
|
height: 14,
|
|
320
315
|
borderRadius: 7,
|
|
321
316
|
borderWidth: 2,
|
|
322
|
-
borderColor: TEXT_PRIMARY,
|
|
323
|
-
backgroundColor: SURFACE_ELEVATED,
|
|
324
317
|
boxShadow: '0 0 4px rgba(0,0,0,0.5)',
|
|
325
318
|
}}
|
|
326
319
|
accessibilityElementsHidden
|
|
@@ -331,18 +324,19 @@ export function BodyMapDetailPanel({
|
|
|
331
324
|
|
|
332
325
|
<View className="flex-row items-baseline" style={{ marginTop: 14, gap: 4 }}>
|
|
333
326
|
<Text
|
|
327
|
+
className="text-text-primary"
|
|
334
328
|
style={{
|
|
335
329
|
fontSize: 24,
|
|
336
330
|
fontFamily: '"Space Grotesk", sans-serif',
|
|
337
331
|
fontWeight: '700',
|
|
338
|
-
color: TEXT_PRIMARY,
|
|
339
332
|
}}
|
|
340
333
|
testID="body-map-detail-panel-set-count"
|
|
341
334
|
>
|
|
342
335
|
{weeklySets}
|
|
343
336
|
</Text>
|
|
344
337
|
<Text
|
|
345
|
-
|
|
338
|
+
className="text-text-secondary"
|
|
339
|
+
style={{ fontSize: 12, fontFamily: 'Inter, sans-serif' }}
|
|
346
340
|
testID="body-map-detail-panel-mrv-suffix"
|
|
347
341
|
>
|
|
348
342
|
{`/ ${landmarks.mrv} MRV`}
|
|
@@ -352,11 +346,11 @@ export function BodyMapDetailPanel({
|
|
|
352
346
|
{weeklyHistory != null && weeklyHistory.length > 0 && (
|
|
353
347
|
<View style={{ marginTop: 14 }} testID="body-map-detail-panel-sparkline">
|
|
354
348
|
<Text
|
|
349
|
+
className="text-text-tertiary"
|
|
355
350
|
style={{
|
|
356
351
|
fontSize: 10,
|
|
357
352
|
fontFamily: 'Inter, sans-serif',
|
|
358
353
|
fontWeight: '600',
|
|
359
|
-
color: TEXT_TERTIARY,
|
|
360
354
|
marginBottom: 6,
|
|
361
355
|
}}
|
|
362
356
|
accessibilityElementsHidden
|
|
@@ -370,11 +364,11 @@ export function BodyMapDetailPanel({
|
|
|
370
364
|
{hasContributing && (
|
|
371
365
|
<View style={{ marginTop: 16 }} testID="body-map-detail-panel-contributing">
|
|
372
366
|
<Text
|
|
367
|
+
className="text-text-tertiary"
|
|
373
368
|
style={{
|
|
374
369
|
fontSize: 10,
|
|
375
370
|
fontFamily: 'Inter, sans-serif',
|
|
376
371
|
fontWeight: '600',
|
|
377
|
-
color: TEXT_TERTIARY,
|
|
378
372
|
marginBottom: 8,
|
|
379
373
|
}}
|
|
380
374
|
>
|
|
@@ -383,27 +377,27 @@ export function BodyMapDetailPanel({
|
|
|
383
377
|
{contributingExercises!.map((exercise, index) => (
|
|
384
378
|
<View
|
|
385
379
|
key={`${exercise.name}-${index}`}
|
|
386
|
-
className="flex-row items-center justify-between"
|
|
380
|
+
className="flex-row items-center justify-between bg-surface-raised border-border"
|
|
387
381
|
style={{
|
|
388
382
|
gap: 10,
|
|
389
383
|
paddingVertical: 8,
|
|
390
384
|
paddingHorizontal: 12,
|
|
391
385
|
marginBottom: 6,
|
|
392
386
|
borderRadius: 8,
|
|
393
|
-
backgroundColor: SURFACE_RAISED,
|
|
394
387
|
borderWidth: 1,
|
|
395
|
-
borderColor: BORDER_DEFAULT,
|
|
396
388
|
}}
|
|
397
389
|
testID={`body-map-detail-panel-contributing-${index}`}
|
|
398
390
|
>
|
|
399
391
|
<Text
|
|
400
|
-
|
|
392
|
+
className="text-text-primary"
|
|
393
|
+
style={{ flex: 1, fontSize: 13, fontFamily: 'Inter, sans-serif', fontWeight: '600' }}
|
|
401
394
|
testID={`body-map-detail-panel-contributing-${index}-name`}
|
|
402
395
|
>
|
|
403
396
|
{exercise.name}
|
|
404
397
|
</Text>
|
|
405
398
|
<Text
|
|
406
|
-
|
|
399
|
+
className="text-text-secondary"
|
|
400
|
+
style={{ fontSize: 12, fontFamily: 'Inter, sans-serif' }}
|
|
407
401
|
testID={`body-map-detail-panel-contributing-${index}-detail`}
|
|
408
402
|
>
|
|
409
403
|
{`${exercise.sets} sets · ${Math.round(exercise.contributionWeight * 100)}%`}
|
|
@@ -416,11 +410,11 @@ export function BodyMapDetailPanel({
|
|
|
416
410
|
{hasUpcoming && (
|
|
417
411
|
<View style={{ marginTop: 16 }} testID="body-map-detail-panel-upcoming">
|
|
418
412
|
<Text
|
|
413
|
+
className="text-text-tertiary"
|
|
419
414
|
style={{
|
|
420
415
|
fontSize: 10,
|
|
421
416
|
fontFamily: 'Inter, sans-serif',
|
|
422
417
|
fontWeight: '600',
|
|
423
|
-
color: TEXT_TERTIARY,
|
|
424
418
|
marginBottom: 8,
|
|
425
419
|
}}
|
|
426
420
|
>
|
|
@@ -434,13 +428,15 @@ export function BodyMapDetailPanel({
|
|
|
434
428
|
testID={`body-map-detail-panel-upcoming-${index}`}
|
|
435
429
|
>
|
|
436
430
|
<Text
|
|
437
|
-
|
|
431
|
+
className="text-text-primary"
|
|
432
|
+
style={{ flex: 1, fontSize: 13, fontFamily: 'Inter, sans-serif', fontWeight: '600' }}
|
|
438
433
|
testID={`body-map-detail-panel-upcoming-${index}-name`}
|
|
439
434
|
>
|
|
440
435
|
{exercise.name}
|
|
441
436
|
</Text>
|
|
442
437
|
<Text
|
|
443
|
-
|
|
438
|
+
className="text-text-tertiary"
|
|
439
|
+
style={{ fontSize: 12, fontFamily: 'Inter, sans-serif' }}
|
|
444
440
|
testID={`body-map-detail-panel-upcoming-${index}-detail`}
|
|
445
441
|
>
|
|
446
442
|
{`${exercise.workoutName} · ${exercise.sets} sets`}
|
|
@@ -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 = '#6B7280'
|
|
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"
|
|
@@ -100,6 +100,30 @@ describe('ExerciseCard', () => {
|
|
|
100
100
|
expect(headers).toHaveTextContent('RPE')
|
|
101
101
|
})
|
|
102
102
|
|
|
103
|
+
it('shows the KG weight header when the unit is kg', () => {
|
|
104
|
+
render(
|
|
105
|
+
<ExerciseCard
|
|
106
|
+
{...expandedProps}
|
|
107
|
+
summary={{ sets: 3, reps: 8, weight: 84, unit: 'kg' }}
|
|
108
|
+
/>,
|
|
109
|
+
)
|
|
110
|
+
const headers = screen.getByTestId('exercise-card-column-headers')
|
|
111
|
+
expect(headers).toHaveTextContent('KG')
|
|
112
|
+
expect(headers).not.toHaveTextContent('LBS')
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
it('derives the weight header from the sets unit when summary is absent', () => {
|
|
116
|
+
render(
|
|
117
|
+
<ExerciseCard
|
|
118
|
+
name="Squat"
|
|
119
|
+
state="expanded"
|
|
120
|
+
onToggle={vi.fn()}
|
|
121
|
+
sets={[{ mode: 'completed', setNumber: 1, reps: 8, weight: 60, unit: 'kg' }]}
|
|
122
|
+
/>,
|
|
123
|
+
)
|
|
124
|
+
expect(screen.getByTestId('exercise-card-column-headers')).toHaveTextContent('KG')
|
|
125
|
+
})
|
|
126
|
+
|
|
103
127
|
it('renders SetRow components', () => {
|
|
104
128
|
render(<ExerciseCard {...expandedProps} />)
|
|
105
129
|
const setRows = screen.getAllByTestId('set-row')
|
|
@@ -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 { WORKOUT_TOKENS } from '../../../theme/workout-tokens'
|
|
5
4
|
import { VelocityStrip } from './VelocityStrip'
|
|
6
5
|
import { PlaceholderStrip } from './PlaceholderStrip'
|
|
7
6
|
import { WeightBadge } from './WeightBadge'
|
|
8
7
|
import { PrBadge } from './PrBadge'
|
|
9
8
|
import { TempoDisplay } from './TempoDisplay'
|
|
10
9
|
import { SetRow, type SetRowProps } from './SetRow'
|
|
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
|
|
|
@@ -34,15 +35,11 @@ export interface ExerciseCardProps {
|
|
|
34
35
|
supersetColor?: string
|
|
35
36
|
}
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
textTertiary: '#6B7280',
|
|
41
|
-
brandPrimary: '#FF7900',
|
|
38
|
+
/** Column headers; the weight column reflects the card's unit (LBS / KG). */
|
|
39
|
+
function buildColumnHeaders(unit: 'lbs' | 'kg'): string[] {
|
|
40
|
+
return ['SET', 'PREV', 'REPS', unit.toUpperCase(), 'RPE']
|
|
42
41
|
}
|
|
43
42
|
|
|
44
|
-
const COLUMN_HEADERS = ['SET', 'PREV', 'REPS', 'LBS', 'RPE'] as const
|
|
45
|
-
|
|
46
43
|
function getSupersetBorderRadius(
|
|
47
44
|
position: ExerciseCardProps['supersetPosition'],
|
|
48
45
|
): Record<string, number> {
|
|
@@ -89,7 +86,7 @@ function getSupersetMargin(
|
|
|
89
86
|
|
|
90
87
|
function formatSummary(summary: ExerciseCardProps['summary']): string {
|
|
91
88
|
if (!summary) return ''
|
|
92
|
-
return `${summary.sets}\u00D7${summary.reps} @ ${summary.weight} ${summary.unit}`
|
|
89
|
+
return `${summary.sets}\u00D7${summary.reps} @ ${roundWeight(summary.weight)} ${summary.unit}`
|
|
93
90
|
}
|
|
94
91
|
|
|
95
92
|
function formatAccessibilityLabel(
|
|
@@ -128,13 +125,11 @@ function CollapsedCard({
|
|
|
128
125
|
testID="exercise-card"
|
|
129
126
|
>
|
|
130
127
|
<View
|
|
128
|
+
className={pressed ? 'bg-surface-raised' : undefined}
|
|
131
129
|
style={{
|
|
132
130
|
padding: 12,
|
|
133
131
|
paddingHorizontal: 14,
|
|
134
132
|
cursor: 'pointer',
|
|
135
|
-
backgroundColor: pressed
|
|
136
|
-
? WORKOUT_TOKENS.surface.raised
|
|
137
|
-
: 'transparent',
|
|
138
133
|
...borderRadius,
|
|
139
134
|
...supersetMargin,
|
|
140
135
|
}}
|
|
@@ -144,11 +139,11 @@ function CollapsedCard({
|
|
|
144
139
|
testID="exercise-card-header"
|
|
145
140
|
>
|
|
146
141
|
<Text
|
|
142
|
+
className="text-text-primary"
|
|
147
143
|
style={{
|
|
148
144
|
fontSize: 14,
|
|
149
145
|
fontFamily: '"Space Grotesk", sans-serif',
|
|
150
146
|
fontWeight: '700',
|
|
151
|
-
color: COLORS.textPrimary,
|
|
152
147
|
}}
|
|
153
148
|
testID="exercise-card-name"
|
|
154
149
|
>
|
|
@@ -157,10 +152,10 @@ function CollapsedCard({
|
|
|
157
152
|
<View className="flex-1" />
|
|
158
153
|
{summary && (
|
|
159
154
|
<Text
|
|
155
|
+
className="text-text-secondary"
|
|
160
156
|
style={{
|
|
161
157
|
fontSize: 12,
|
|
162
158
|
fontFamily: 'Inter, sans-serif',
|
|
163
|
-
color: COLORS.textSecondary,
|
|
164
159
|
marginRight: 8,
|
|
165
160
|
}}
|
|
166
161
|
testID="exercise-card-summary"
|
|
@@ -170,7 +165,7 @@ function CollapsedCard({
|
|
|
170
165
|
)}
|
|
171
166
|
{e1rm && (
|
|
172
167
|
<WeightBadge
|
|
173
|
-
value={e1rm.value}
|
|
168
|
+
value={roundWeight(e1rm.value)}
|
|
174
169
|
unit={e1rm.unit}
|
|
175
170
|
size="sm"
|
|
176
171
|
showIcon={false}
|
|
@@ -223,13 +218,16 @@ function ExpandedCard({
|
|
|
223
218
|
}: ExerciseCardProps) {
|
|
224
219
|
const borderRadius = getSupersetBorderRadius(supersetPosition)
|
|
225
220
|
const supersetMargin = getSupersetMargin(supersetPosition)
|
|
221
|
+
// Summary is the card-level authority for the weight column; fall back to the
|
|
222
|
+
// first set's unit, then lbs. (Mixed per-set units keep the card-level label.)
|
|
223
|
+
const unit = summary?.unit ?? sets?.[0]?.unit ?? 'lbs'
|
|
224
|
+
const columnHeaders = buildColumnHeaders(unit)
|
|
226
225
|
|
|
227
226
|
return (
|
|
228
227
|
<View
|
|
228
|
+
className="bg-surface-elevated border-border"
|
|
229
229
|
style={{
|
|
230
|
-
backgroundColor: WORKOUT_TOKENS.surface.elevated,
|
|
231
230
|
borderWidth: 1,
|
|
232
|
-
borderColor: WORKOUT_TOKENS.border.default,
|
|
233
231
|
...borderRadius,
|
|
234
232
|
...supersetMargin,
|
|
235
233
|
}}
|
|
@@ -249,15 +247,15 @@ function ExpandedCard({
|
|
|
249
247
|
paddingHorizontal: 14,
|
|
250
248
|
paddingBottom: 10,
|
|
251
249
|
borderBottomWidth: 1,
|
|
252
|
-
borderBottomColor:
|
|
250
|
+
borderBottomColor: resolveColor('border-default'),
|
|
253
251
|
}}
|
|
254
252
|
>
|
|
255
253
|
<Text
|
|
254
|
+
className="text-text-primary"
|
|
256
255
|
style={{
|
|
257
256
|
fontSize: 14,
|
|
258
257
|
fontFamily: '"Space Grotesk", sans-serif',
|
|
259
258
|
fontWeight: '700',
|
|
260
|
-
color: COLORS.textPrimary,
|
|
261
259
|
}}
|
|
262
260
|
testID="exercise-card-name"
|
|
263
261
|
>
|
|
@@ -266,10 +264,10 @@ function ExpandedCard({
|
|
|
266
264
|
<View className="flex-1" />
|
|
267
265
|
{summary && (
|
|
268
266
|
<Text
|
|
267
|
+
className="text-text-secondary"
|
|
269
268
|
style={{
|
|
270
269
|
fontSize: 12,
|
|
271
270
|
fontFamily: 'Inter, sans-serif',
|
|
272
|
-
color: COLORS.textSecondary,
|
|
273
271
|
marginRight: 8,
|
|
274
272
|
}}
|
|
275
273
|
testID="exercise-card-summary"
|
|
@@ -279,7 +277,7 @@ function ExpandedCard({
|
|
|
279
277
|
)}
|
|
280
278
|
{e1rm && (
|
|
281
279
|
<WeightBadge
|
|
282
|
-
value={e1rm.value}
|
|
280
|
+
value={roundWeight(e1rm.value)}
|
|
283
281
|
unit={e1rm.unit}
|
|
284
282
|
size="sm"
|
|
285
283
|
showIcon={false}
|
|
@@ -309,7 +307,7 @@ function ExpandedCard({
|
|
|
309
307
|
}}
|
|
310
308
|
testID="exercise-card-column-headers"
|
|
311
309
|
>
|
|
312
|
-
{
|
|
310
|
+
{columnHeaders.map((header, i) => {
|
|
313
311
|
const widths = [36, undefined, 44, 56, 36]
|
|
314
312
|
const width = widths[i]
|
|
315
313
|
const isFlex = width === undefined
|
|
@@ -326,13 +324,13 @@ function ExpandedCard({
|
|
|
326
324
|
{...(isFlex ? { className: 'flex-1' } : {})}
|
|
327
325
|
>
|
|
328
326
|
<Text
|
|
327
|
+
className="text-text-tertiary"
|
|
329
328
|
style={{
|
|
330
329
|
fontSize: 10,
|
|
331
330
|
fontWeight: '600',
|
|
332
331
|
fontFamily: 'Inter, sans-serif',
|
|
333
332
|
textTransform: 'uppercase',
|
|
334
333
|
letterSpacing: 0.8,
|
|
335
|
-
color: COLORS.textTertiary,
|
|
336
334
|
}}
|
|
337
335
|
>
|
|
338
336
|
{header}
|
|
@@ -376,11 +374,11 @@ function UpcomingCard({
|
|
|
376
374
|
>
|
|
377
375
|
<View className="flex-row items-center">
|
|
378
376
|
<Text
|
|
377
|
+
className="text-text-primary"
|
|
379
378
|
style={{
|
|
380
379
|
fontSize: 14,
|
|
381
380
|
fontFamily: '"Space Grotesk", sans-serif',
|
|
382
381
|
fontWeight: '700',
|
|
383
|
-
color: COLORS.textPrimary,
|
|
384
382
|
}}
|
|
385
383
|
testID="exercise-card-name"
|
|
386
384
|
>
|
|
@@ -388,10 +386,10 @@ function UpcomingCard({
|
|
|
388
386
|
</Text>
|
|
389
387
|
{prescription && (
|
|
390
388
|
<Text
|
|
389
|
+
className="text-text-secondary"
|
|
391
390
|
style={{
|
|
392
391
|
fontSize: 12,
|
|
393
392
|
fontFamily: 'Inter, sans-serif',
|
|
394
|
-
color: COLORS.textSecondary,
|
|
395
393
|
marginLeft: 8,
|
|
396
394
|
}}
|
|
397
395
|
testID="exercise-card-prescription"
|
|
@@ -402,10 +400,10 @@ function UpcomingCard({
|
|
|
402
400
|
<View className="flex-1" />
|
|
403
401
|
{previousBest && (
|
|
404
402
|
<Text
|
|
403
|
+
className="text-text-tertiary"
|
|
405
404
|
style={{
|
|
406
405
|
fontSize: 11,
|
|
407
406
|
fontFamily: 'Inter, sans-serif',
|
|
408
|
-
color: COLORS.textTertiary,
|
|
409
407
|
}}
|
|
410
408
|
testID="exercise-card-previous-best"
|
|
411
409
|
>
|