@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
|
@@ -1,7 +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 {
|
|
4
|
+
import { resolveColor } from '../../../theme/resolve-color'
|
|
5
5
|
|
|
6
6
|
export interface InputBarProps {
|
|
7
7
|
exerciseName: string
|
|
@@ -18,19 +18,15 @@ export interface InputBarProps {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
const BRAND_PRIMARY = '#FF7900'
|
|
21
|
-
const
|
|
22
|
-
const TEXT_TERTIARY = '#6B7280'
|
|
21
|
+
const INPUT_CLASSNAME = 'bg-surface-raised border-border-strong text-text-primary'
|
|
23
22
|
|
|
24
23
|
const inputStyle = {
|
|
25
24
|
fontSize: 14,
|
|
26
25
|
fontWeight: '600' as const,
|
|
27
26
|
fontFamily: 'Inter, sans-serif',
|
|
28
|
-
backgroundColor: WORKOUT_TOKENS.surface.raised,
|
|
29
27
|
borderWidth: 1,
|
|
30
|
-
borderColor: WORKOUT_TOKENS.border.strong,
|
|
31
28
|
borderRadius: 6,
|
|
32
29
|
textAlign: 'center' as const,
|
|
33
|
-
color: TEXT_PRIMARY,
|
|
34
30
|
paddingVertical: 5,
|
|
35
31
|
paddingHorizontal: 2,
|
|
36
32
|
}
|
|
@@ -54,11 +50,11 @@ export function InputBar({
|
|
|
54
50
|
|
|
55
51
|
return (
|
|
56
52
|
<View
|
|
53
|
+
className="bg-surface-elevated"
|
|
57
54
|
style={{
|
|
58
55
|
width: '100%',
|
|
59
|
-
backgroundColor: WORKOUT_TOKENS.surface.elevated,
|
|
60
56
|
borderTopWidth: 1,
|
|
61
|
-
borderTopColor:
|
|
57
|
+
borderTopColor: resolveColor('border-default'),
|
|
62
58
|
paddingTop: 10,
|
|
63
59
|
paddingHorizontal: 16,
|
|
64
60
|
paddingBottom: 12,
|
|
@@ -71,11 +67,11 @@ export function InputBar({
|
|
|
71
67
|
>
|
|
72
68
|
<View style={{ minWidth: 80, flexDirection: 'column' }}>
|
|
73
69
|
<Text
|
|
70
|
+
className="text-text-primary"
|
|
74
71
|
style={{
|
|
75
72
|
fontSize: 12,
|
|
76
73
|
fontWeight: '700',
|
|
77
74
|
fontFamily: '"Space Grotesk", sans-serif',
|
|
78
|
-
color: TEXT_PRIMARY,
|
|
79
75
|
}}
|
|
80
76
|
testID="input-bar-exercise-name"
|
|
81
77
|
numberOfLines={1}
|
|
@@ -83,10 +79,10 @@ export function InputBar({
|
|
|
83
79
|
{exerciseName}
|
|
84
80
|
</Text>
|
|
85
81
|
<Text
|
|
82
|
+
className="text-text-tertiary"
|
|
86
83
|
style={{
|
|
87
84
|
fontSize: 10,
|
|
88
85
|
fontFamily: 'Inter, sans-serif',
|
|
89
|
-
color: TEXT_TERTIARY,
|
|
90
86
|
}}
|
|
91
87
|
testID="input-bar-set-info"
|
|
92
88
|
>
|
|
@@ -98,16 +94,17 @@ export function InputBar({
|
|
|
98
94
|
<TextInput
|
|
99
95
|
value={reps}
|
|
100
96
|
onChangeText={onRepsChange}
|
|
97
|
+
className={INPUT_CLASSNAME}
|
|
101
98
|
style={{ ...inputStyle, width: 36 }}
|
|
102
99
|
accessibilityLabel="Reps"
|
|
103
100
|
testID="input-bar-reps"
|
|
104
101
|
keyboardType="numeric"
|
|
105
102
|
/>
|
|
106
103
|
<Text
|
|
104
|
+
className="text-text-tertiary"
|
|
107
105
|
style={{
|
|
108
106
|
fontSize: 12,
|
|
109
107
|
fontFamily: 'Inter, sans-serif',
|
|
110
|
-
color: TEXT_TERTIARY,
|
|
111
108
|
}}
|
|
112
109
|
>
|
|
113
110
|
{'\u00D7'}
|
|
@@ -115,17 +112,18 @@ export function InputBar({
|
|
|
115
112
|
<TextInput
|
|
116
113
|
value={weight}
|
|
117
114
|
onChangeText={onWeightChange}
|
|
115
|
+
className={INPUT_CLASSNAME}
|
|
118
116
|
style={{ ...inputStyle, width: 52 }}
|
|
119
117
|
accessibilityLabel={`Weight in ${unit}`}
|
|
120
118
|
testID="input-bar-weight"
|
|
121
119
|
keyboardType="numeric"
|
|
122
120
|
/>
|
|
123
121
|
<Text
|
|
122
|
+
className="text-text-tertiary"
|
|
124
123
|
style={{
|
|
125
124
|
fontSize: 12,
|
|
126
125
|
fontWeight: '500',
|
|
127
126
|
fontFamily: 'Inter, sans-serif',
|
|
128
|
-
color: TEXT_TERTIARY,
|
|
129
127
|
}}
|
|
130
128
|
testID="input-bar-unit"
|
|
131
129
|
>
|
|
@@ -9,8 +9,6 @@ const BRAND_PRIMARY = '#FF7900'
|
|
|
9
9
|
const BRAND_PRIMARY_DARK = '#C45E00'
|
|
10
10
|
const BRAND_PRIMARY_LIGHT = '#FFB066'
|
|
11
11
|
const BORDER_DEFAULT = '#1F1F1F'
|
|
12
|
-
const TEXT_PRIMARY = '#F3F4F6'
|
|
13
|
-
const TEXT_SECONDARY = '#9CA3AF'
|
|
14
12
|
|
|
15
13
|
/** Gradient stops for the 3px top accent: dark -> primary -> light. */
|
|
16
14
|
const ACCENT_STOPS = [BRAND_PRIMARY_DARK, BRAND_PRIMARY, BRAND_PRIMARY_LIGHT]
|
|
@@ -117,11 +115,11 @@ export function MesoCard({
|
|
|
117
115
|
<View style={{ paddingHorizontal: 14, paddingTop: 12, paddingBottom: 10 }} testID="meso-card-body">
|
|
118
116
|
<View className="flex-row items-center" style={{ gap: 8 }} testID="meso-card-header">
|
|
119
117
|
<Text
|
|
118
|
+
className="text-text-primary"
|
|
120
119
|
style={{
|
|
121
120
|
fontSize: 15,
|
|
122
121
|
fontFamily: '"Space Grotesk", sans-serif',
|
|
123
122
|
fontWeight: '700',
|
|
124
|
-
color: TEXT_PRIMARY,
|
|
125
123
|
}}
|
|
126
124
|
testID="meso-card-name"
|
|
127
125
|
>
|
|
@@ -133,11 +131,11 @@ export function MesoCard({
|
|
|
133
131
|
</View>
|
|
134
132
|
|
|
135
133
|
<Text
|
|
134
|
+
className="text-text-secondary"
|
|
136
135
|
style={{
|
|
137
136
|
marginTop: 4,
|
|
138
137
|
fontSize: 12,
|
|
139
138
|
fontFamily: 'Inter, sans-serif',
|
|
140
|
-
color: TEXT_SECONDARY,
|
|
141
139
|
}}
|
|
142
140
|
testID="meso-card-subheader"
|
|
143
141
|
>
|
|
@@ -139,3 +139,15 @@ describe('MesoProgressBar', () => {
|
|
|
139
139
|
})
|
|
140
140
|
})
|
|
141
141
|
})
|
|
142
|
+
|
|
143
|
+
describe('MesoProgressBar invalid weekCount (flex guard)', () => {
|
|
144
|
+
it('clamps zero and negative weekCount to a valid flex of 1', () => {
|
|
145
|
+
const edgeMesos: Meso[] = [
|
|
146
|
+
{ id: 'z', name: 'Zero', weekCount: 0, status: 'upcoming' },
|
|
147
|
+
{ id: 'n', name: 'Neg', weekCount: -3, status: 'upcoming' },
|
|
148
|
+
]
|
|
149
|
+
render(<MesoProgressBar mesos={edgeMesos} activeMesoId={null} onMesoPress={vi.fn()} />)
|
|
150
|
+
expect(screen.getByTestId('meso-segment-z')).toHaveStyle({ flex: 1 })
|
|
151
|
+
expect(screen.getByTestId('meso-segment-n')).toHaveStyle({ flex: 1 })
|
|
152
|
+
})
|
|
153
|
+
})
|
|
@@ -66,9 +66,13 @@ function MesoSegment({ meso, isActive, onPress }: SegmentProps) {
|
|
|
66
66
|
? clampProgress(meso.currentWeek, meso.weekCount)
|
|
67
67
|
: 0
|
|
68
68
|
|
|
69
|
+
// weekCount drives the segment's flex width; guard 0 / negative so flex stays
|
|
70
|
+
// a valid positive value and the segment never collapses out of the layout.
|
|
71
|
+
const segmentFlex = Math.max(1, meso.weekCount)
|
|
72
|
+
|
|
69
73
|
return (
|
|
70
74
|
<Pressable
|
|
71
|
-
style={{ flex:
|
|
75
|
+
style={{ flex: segmentFlex }}
|
|
72
76
|
onPress={() => onPress(meso.id)}
|
|
73
77
|
onPressIn={handlePressIn}
|
|
74
78
|
onPressOut={handlePressOut}
|
|
@@ -3,13 +3,11 @@ import { Fragment, type ReactNode } from 'react'
|
|
|
3
3
|
import { View, Text, type ViewProps, type ViewStyle } from 'react-native'
|
|
4
4
|
import { Card } from '../../ui/card'
|
|
5
5
|
import { StatusDot } from './StatusDot'
|
|
6
|
+
import { resolveColor } from '../../../theme/resolve-color'
|
|
6
7
|
|
|
7
8
|
const BRAND_PRIMARY = '#FF7900'
|
|
8
9
|
const BRAND_PRIMARY_DARK = '#C45E00'
|
|
9
10
|
const BRAND_PRIMARY_LIGHT = '#FFB066'
|
|
10
|
-
const TEXT_PRIMARY = '#F3F4F6'
|
|
11
|
-
const TEXT_SECONDARY = '#9CA3AF'
|
|
12
|
-
const TEXT_TERTIARY = '#6B7280'
|
|
13
11
|
|
|
14
12
|
const SUCCESS = '#14B8A6'
|
|
15
13
|
const WARNING = '#FFB020'
|
|
@@ -18,9 +16,12 @@ const ERROR = '#D14343'
|
|
|
18
16
|
/** Gradient stops for the 3px top accent: dark -> primary -> light (matches MesoCard). */
|
|
19
17
|
const ACCENT_STOPS = [BRAND_PRIMARY_DARK, BRAND_PRIMARY, BRAND_PRIMARY_LIGHT]
|
|
20
18
|
|
|
21
|
-
/**
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Card surface gradient: elevated -> raised at 135deg. Uses resolved dark-theme
|
|
21
|
+
* hexes (a gradient string can't carry a className token, and var() renders
|
|
22
|
+
* black on native).
|
|
23
|
+
*/
|
|
24
|
+
const CARD_GRADIENT = `linear-gradient(135deg, ${resolveColor('surface-elevated')} 0%, ${resolveColor('surface-raised')} 100%)`
|
|
24
25
|
|
|
25
26
|
/** Gauge track gradient (teal -> amber -> red) at 0.25 alpha. */
|
|
26
27
|
const GAUGE_GRADIENT =
|
|
@@ -118,7 +119,8 @@ function renderCoachingText(text: string, highlights?: string[]): ReactNode {
|
|
|
118
119
|
highlights.includes(part) ? (
|
|
119
120
|
<Text
|
|
120
121
|
key={`${part}-${index}`}
|
|
121
|
-
|
|
122
|
+
className="text-text-primary"
|
|
123
|
+
style={{ fontWeight: '700' }}
|
|
122
124
|
>
|
|
123
125
|
{part}
|
|
124
126
|
</Text>
|
|
@@ -166,24 +168,24 @@ function MetricCell({ metric }: { metric: MesoStatusMetric }) {
|
|
|
166
168
|
return (
|
|
167
169
|
<View style={{ width: '48%' }} testID="meso-status-card-metric">
|
|
168
170
|
<Text
|
|
171
|
+
className="text-text-tertiary"
|
|
169
172
|
style={{
|
|
170
173
|
fontSize: 10,
|
|
171
174
|
fontFamily: 'Inter, sans-serif',
|
|
172
175
|
fontWeight: '600',
|
|
173
176
|
letterSpacing: 0.5,
|
|
174
177
|
textTransform: 'uppercase',
|
|
175
|
-
color: TEXT_TERTIARY,
|
|
176
178
|
}}
|
|
177
179
|
>
|
|
178
180
|
{metric.label}
|
|
179
181
|
</Text>
|
|
180
182
|
<Text
|
|
183
|
+
className="text-text-primary"
|
|
181
184
|
style={{
|
|
182
185
|
marginTop: 2,
|
|
183
186
|
fontSize: 13,
|
|
184
187
|
fontFamily: 'Inter, sans-serif',
|
|
185
188
|
fontWeight: '600',
|
|
186
|
-
color: TEXT_PRIMARY,
|
|
187
189
|
}}
|
|
188
190
|
>
|
|
189
191
|
{metric.value}
|
|
@@ -212,24 +214,24 @@ function Gauge({ gauge }: { gauge: MesoStatusGauge }) {
|
|
|
212
214
|
accessibilityElementsHidden
|
|
213
215
|
>
|
|
214
216
|
<Text
|
|
217
|
+
className="text-text-secondary"
|
|
215
218
|
style={{
|
|
216
219
|
fontSize: 10,
|
|
217
220
|
fontFamily: 'Inter, sans-serif',
|
|
218
221
|
fontWeight: '500',
|
|
219
222
|
letterSpacing: 0.5,
|
|
220
223
|
textTransform: 'uppercase',
|
|
221
|
-
color: TEXT_SECONDARY,
|
|
222
224
|
}}
|
|
223
225
|
>
|
|
224
226
|
{gauge.label}
|
|
225
227
|
</Text>
|
|
226
228
|
{gauge.sublabel != null && (
|
|
227
229
|
<Text
|
|
230
|
+
className="text-text-tertiary"
|
|
228
231
|
style={{
|
|
229
232
|
fontSize: 10,
|
|
230
233
|
fontFamily: 'Inter, sans-serif',
|
|
231
234
|
fontWeight: '500',
|
|
232
|
-
color: TEXT_TERTIARY,
|
|
233
235
|
}}
|
|
234
236
|
>
|
|
235
237
|
{gauge.sublabel}
|
|
@@ -261,6 +263,7 @@ function Gauge({ gauge }: { gauge: MesoStatusGauge }) {
|
|
|
261
263
|
testID="meso-status-card-gauge-center"
|
|
262
264
|
/>
|
|
263
265
|
<View
|
|
266
|
+
className="border-text-primary"
|
|
264
267
|
style={{
|
|
265
268
|
position: 'absolute',
|
|
266
269
|
left: `${percentage}%`,
|
|
@@ -270,7 +273,6 @@ function Gauge({ gauge }: { gauge: MesoStatusGauge }) {
|
|
|
270
273
|
height: 14,
|
|
271
274
|
borderRadius: 9999,
|
|
272
275
|
borderWidth: 2,
|
|
273
|
-
borderColor: TEXT_PRIMARY,
|
|
274
276
|
backgroundColor: markerColor,
|
|
275
277
|
boxShadow: '0 0 4px rgba(0,0,0,0.5)',
|
|
276
278
|
}}
|
|
@@ -349,12 +351,12 @@ export function MesoStatusCard({
|
|
|
349
351
|
style={{ gap: 8 }}
|
|
350
352
|
>
|
|
351
353
|
<Text
|
|
354
|
+
className="text-text-primary"
|
|
352
355
|
style={{
|
|
353
356
|
flexShrink: 1,
|
|
354
357
|
fontSize: 15,
|
|
355
358
|
fontFamily: '"Space Grotesk", sans-serif',
|
|
356
359
|
fontWeight: '700',
|
|
357
|
-
color: TEXT_PRIMARY,
|
|
358
360
|
}}
|
|
359
361
|
testID="meso-status-card-name"
|
|
360
362
|
>
|
|
@@ -363,11 +365,11 @@ export function MesoStatusCard({
|
|
|
363
365
|
<StatusPill badge={statusBadge} />
|
|
364
366
|
</View>
|
|
365
367
|
<Text
|
|
368
|
+
className="text-text-secondary"
|
|
366
369
|
style={{
|
|
367
370
|
marginTop: 4,
|
|
368
371
|
fontSize: 12,
|
|
369
372
|
fontFamily: 'Inter, sans-serif',
|
|
370
|
-
color: TEXT_SECONDARY,
|
|
371
373
|
}}
|
|
372
374
|
testID="meso-status-card-subtitle"
|
|
373
375
|
>
|
|
@@ -408,11 +410,11 @@ export function MesoStatusCard({
|
|
|
408
410
|
testID="meso-status-card-coaching"
|
|
409
411
|
>
|
|
410
412
|
<Text
|
|
413
|
+
className="text-text-secondary"
|
|
411
414
|
style={{
|
|
412
415
|
fontSize: 12,
|
|
413
416
|
lineHeight: 17,
|
|
414
417
|
fontFamily: 'Inter, sans-serif',
|
|
415
|
-
color: TEXT_SECONDARY,
|
|
416
418
|
}}
|
|
417
419
|
>
|
|
418
420
|
{renderCoachingText(coaching.text, coaching.highlights)}
|
|
@@ -34,16 +34,14 @@ export function MuscleGroupChip({
|
|
|
34
34
|
|
|
35
35
|
const content = (
|
|
36
36
|
<View
|
|
37
|
-
className={className}
|
|
37
|
+
className={['bg-surface-raised border-border', className].filter(Boolean).join(' ')}
|
|
38
38
|
style={{
|
|
39
39
|
flexDirection: 'row',
|
|
40
40
|
alignItems: 'center',
|
|
41
41
|
borderRadius: 9999,
|
|
42
42
|
paddingHorizontal: 9,
|
|
43
43
|
paddingVertical: 3,
|
|
44
|
-
backgroundColor: 'var(--color-surface-raised)',
|
|
45
44
|
borderWidth: 1,
|
|
46
|
-
borderColor: 'var(--color-border-default)',
|
|
47
45
|
}}
|
|
48
46
|
accessibilityLabel={onPress ? undefined : `${name}, volume status: ${statusLabel}`}
|
|
49
47
|
testID="muscle-group-chip"
|
|
@@ -62,11 +60,11 @@ export function MuscleGroupChip({
|
|
|
62
60
|
testID="muscle-group-chip-dot"
|
|
63
61
|
/>
|
|
64
62
|
<Text
|
|
63
|
+
className="text-text-secondary"
|
|
65
64
|
style={{
|
|
66
65
|
fontFamily: 'Inter, sans-serif',
|
|
67
66
|
fontWeight: '500',
|
|
68
67
|
fontSize: 11,
|
|
69
|
-
color: 'var(--color-text-secondary)',
|
|
70
68
|
}}
|
|
71
69
|
accessibilityElementsHidden
|
|
72
70
|
>
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
|
|
2
2
|
import { View, type ViewProps } from 'react-native'
|
|
3
3
|
import { cn } from '../../../utils/cn'
|
|
4
|
-
import { WORKOUT_TOKENS } from '../../../theme/workout-tokens'
|
|
5
4
|
|
|
6
5
|
export interface PlaceholderStripProps extends ViewProps {
|
|
7
6
|
width?: number | string
|
|
@@ -19,14 +18,13 @@ function SingleStrip({
|
|
|
19
18
|
}: Omit<PlaceholderStripProps, 'mode' | 'segments'>) {
|
|
20
19
|
return (
|
|
21
20
|
<View
|
|
22
|
-
className={cn(className)}
|
|
21
|
+
className={cn('border-border-strong', className)}
|
|
23
22
|
style={[
|
|
24
23
|
{
|
|
25
24
|
height: 3,
|
|
26
25
|
backgroundColor: 'transparent',
|
|
27
26
|
borderWidth: 1,
|
|
28
27
|
borderStyle: 'dashed',
|
|
29
|
-
borderColor: WORKOUT_TOKENS.border.strong,
|
|
30
28
|
borderRadius: 1,
|
|
31
29
|
opacity: 0.5,
|
|
32
30
|
},
|
|
@@ -61,13 +59,13 @@ function SegmentedStrip({
|
|
|
61
59
|
{Array.from({ length: segments }, (_, i) => (
|
|
62
60
|
<View
|
|
63
61
|
key={i}
|
|
62
|
+
className="border-border-strong"
|
|
64
63
|
style={{
|
|
65
64
|
flex: 1,
|
|
66
65
|
height: 3,
|
|
67
66
|
backgroundColor: 'transparent',
|
|
68
67
|
borderWidth: 1,
|
|
69
68
|
borderStyle: 'dashed',
|
|
70
|
-
borderColor: WORKOUT_TOKENS.border.strong,
|
|
71
69
|
borderRadius: 1,
|
|
72
70
|
minWidth: 4,
|
|
73
71
|
}}
|
|
@@ -2,15 +2,14 @@
|
|
|
2
2
|
import { View, Text, Pressable, type ViewProps } from 'react-native'
|
|
3
3
|
import { Star } from 'lucide-react'
|
|
4
4
|
import { Drawer, DrawerBody } from '../../ui/drawer'
|
|
5
|
+
import { resolveColor } from '../../../theme/resolve-color'
|
|
5
6
|
|
|
6
7
|
const BRAND_PRIMARY = '#FF7900'
|
|
7
8
|
const RECENT_BORDER = 'rgba(255,176,32,0.3)'
|
|
8
9
|
const RECENT_GLOW = '0 0 12px rgba(255,176,32,0.06)'
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const
|
|
12
|
-
const TEXT_SECONDARY = '#9CA3AF'
|
|
13
|
-
const TEXT_TERTIARY = '#6B7280'
|
|
10
|
+
// Native-safe fallback for the conditional row border (recent uses a computed
|
|
11
|
+
// rgba, so className can't express both branches).
|
|
12
|
+
const BORDER_DEFAULT = resolveColor('border-default')
|
|
14
13
|
|
|
15
14
|
export type PrRecordType = 'e1rm' | 'weight' | 'reps' | 'volume' | 'velocity'
|
|
16
15
|
|
|
@@ -72,14 +71,13 @@ function PrRecordRow({ record, index }: { record: PrRecord; index: number }) {
|
|
|
72
71
|
<View
|
|
73
72
|
accessibilityRole="text"
|
|
74
73
|
accessibilityLabel={a11yLabel}
|
|
75
|
-
className="flex-row items-center"
|
|
74
|
+
className="flex-row items-center bg-surface-raised"
|
|
76
75
|
style={{
|
|
77
76
|
gap: 10,
|
|
78
77
|
paddingVertical: 10,
|
|
79
78
|
paddingHorizontal: 12,
|
|
80
79
|
marginBottom: 8,
|
|
81
80
|
borderRadius: 8,
|
|
82
|
-
backgroundColor: SURFACE_RAISED,
|
|
83
81
|
borderWidth: 1,
|
|
84
82
|
borderColor: record.isRecent ? RECENT_BORDER : BORDER_DEFAULT,
|
|
85
83
|
...(record.isRecent ? { boxShadow: RECENT_GLOW } : {}),
|
|
@@ -88,35 +86,35 @@ function PrRecordRow({ record, index }: { record: PrRecord; index: number }) {
|
|
|
88
86
|
>
|
|
89
87
|
<Star size={16} color={BRAND_PRIMARY} fill={BRAND_PRIMARY} strokeWidth={2} />
|
|
90
88
|
<Text
|
|
89
|
+
className="text-text-secondary"
|
|
91
90
|
style={{
|
|
92
91
|
flex: 1,
|
|
93
92
|
fontSize: 13,
|
|
94
93
|
fontFamily: 'Inter, sans-serif',
|
|
95
94
|
fontWeight: '600',
|
|
96
|
-
color: TEXT_SECONDARY,
|
|
97
95
|
}}
|
|
98
96
|
testID={`pr-history-modal-record-${index}-type`}
|
|
99
97
|
>
|
|
100
98
|
{label}
|
|
101
99
|
</Text>
|
|
102
100
|
<Text
|
|
101
|
+
className="text-text-primary"
|
|
103
102
|
style={{
|
|
104
103
|
fontSize: 14,
|
|
105
104
|
fontFamily: '"Space Grotesk", sans-serif',
|
|
106
105
|
fontWeight: '700',
|
|
107
|
-
color: TEXT_PRIMARY,
|
|
108
106
|
}}
|
|
109
107
|
testID={`pr-history-modal-record-${index}-value`}
|
|
110
108
|
>
|
|
111
109
|
{valueText}
|
|
112
110
|
</Text>
|
|
113
111
|
<Text
|
|
112
|
+
className="text-text-tertiary"
|
|
114
113
|
style={{
|
|
115
114
|
minWidth: 64,
|
|
116
115
|
textAlign: 'right',
|
|
117
116
|
fontSize: 11,
|
|
118
117
|
fontFamily: 'Inter, sans-serif',
|
|
119
|
-
color: TEXT_TERTIARY,
|
|
120
118
|
}}
|
|
121
119
|
testID={`pr-history-modal-record-${index}-date`}
|
|
122
120
|
>
|
|
@@ -173,13 +171,13 @@ export function PrHistoryModal({
|
|
|
173
171
|
>
|
|
174
172
|
<View
|
|
175
173
|
accessibilityElementsHidden
|
|
174
|
+
className="bg-border"
|
|
176
175
|
style={{
|
|
177
176
|
alignSelf: 'center',
|
|
178
177
|
width: 40,
|
|
179
178
|
height: 4,
|
|
180
179
|
borderRadius: 2,
|
|
181
180
|
marginTop: 8,
|
|
182
|
-
backgroundColor: BORDER_DEFAULT,
|
|
183
181
|
}}
|
|
184
182
|
testID="pr-history-modal-handle"
|
|
185
183
|
/>
|
|
@@ -191,11 +189,11 @@ export function PrHistoryModal({
|
|
|
191
189
|
>
|
|
192
190
|
<Text
|
|
193
191
|
accessibilityRole="header"
|
|
192
|
+
className="text-text-primary"
|
|
194
193
|
style={{
|
|
195
194
|
fontSize: 16,
|
|
196
195
|
fontFamily: '"Space Grotesk", sans-serif',
|
|
197
196
|
fontWeight: '700',
|
|
198
|
-
color: TEXT_PRIMARY,
|
|
199
197
|
}}
|
|
200
198
|
testID="pr-history-modal-title"
|
|
201
199
|
>
|
|
@@ -209,7 +207,7 @@ export function PrHistoryModal({
|
|
|
209
207
|
style={{ padding: 4, margin: -4 }}
|
|
210
208
|
testID="pr-history-modal-close"
|
|
211
209
|
>
|
|
212
|
-
<Text style={{ fontSize: 22, lineHeight: 22
|
|
210
|
+
<Text className="text-text-secondary" style={{ fontSize: 22, lineHeight: 22 }}>
|
|
213
211
|
{'×'}
|
|
214
212
|
</Text>
|
|
215
213
|
</Pressable>
|
|
@@ -219,11 +217,11 @@ export function PrHistoryModal({
|
|
|
219
217
|
{records.length === 0 ? (
|
|
220
218
|
<View style={{ paddingVertical: 24 }} testID="pr-history-modal-empty">
|
|
221
219
|
<Text
|
|
220
|
+
className="text-text-tertiary"
|
|
222
221
|
style={{
|
|
223
222
|
textAlign: 'center',
|
|
224
223
|
fontSize: 13,
|
|
225
224
|
fontFamily: 'Inter, sans-serif',
|
|
226
|
-
color: TEXT_TERTIARY,
|
|
227
225
|
}}
|
|
228
226
|
>
|
|
229
227
|
No personal records yet
|