@titan-design/react-ui 0.13.0 → 0.14.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/README.md +14 -0
- package/dist/bodymap.js +129 -76
- package/dist/bodymap.js.map +1 -1
- package/dist/bodymap.mjs +129 -76
- package/dist/bodymap.mjs.map +1 -1
- package/dist/index.d.mts +117 -26
- package/dist/index.d.ts +117 -26
- package/dist/index.js +677 -450
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +673 -451
- package/dist/index.mjs.map +1 -1
- package/dist/{pages-_3Ajpiqu.d.ts → pages-CsjsUn0G.d.ts} +30 -13
- package/dist/{pages-CpW3OB3g.d.mts → pages-ZbT_pD_U.d.mts} +30 -13
- package/dist/pages.d.mts +1 -1
- package/dist/pages.d.ts +1 -1
- package/dist/pages.js +676 -701
- package/dist/pages.js.map +1 -1
- package/dist/pages.mjs +676 -701
- package/dist/pages.mjs.map +1 -1
- package/dist/theme/index.d.mts +31 -7
- package/dist/theme/index.d.ts +31 -7
- package/dist/theme/index.js +84 -20
- package/dist/theme/index.js.map +1 -1
- package/dist/theme/index.mjs +83 -21
- package/dist/theme/index.mjs.map +1 -1
- package/dist/theme/tokens-css.js +84 -20
- package/dist/theme/tokens-css.js.map +1 -1
- package/dist/theme/tokens-css.mjs +84 -20
- package/dist/theme/tokens-css.mjs.map +1 -1
- package/dist/theme/tokens.d.mts +56 -7
- package/dist/theme/tokens.d.ts +56 -7
- package/dist/theme/tokens.js +72 -20
- package/dist/theme/tokens.js.map +1 -1
- package/dist/theme/tokens.mjs +71 -21
- package/dist/theme/tokens.mjs.map +1 -1
- package/docs/render-testing-pattern.md +130 -0
- package/package.json +1 -1
- package/src/arch/Architecture.stories.tsx +3 -1
- package/src/arch/arch-graph.json +74 -72
- package/src/components/custom/ActiveWork/InitiativeBrief.tsx +2 -1
- package/src/components/custom/CircularTimer/CircularTimer.stories.tsx +8 -3
- package/src/components/custom/EmptyState/EmptyState.stories.tsx +1 -1
- package/src/components/custom/Gauge/Gauge.stories.tsx +4 -1
- package/src/components/custom/Gauge/Gauge.tsx +3 -1
- package/src/components/custom/Scatter/Scatter.stories.tsx +10 -7
- package/src/components/custom/Scatter/Scatter.tsx +6 -4
- package/src/components/custom/Sidebar/Sidebar.stories.tsx +1 -1
- package/src/components/custom/Table/Table.stories.tsx +1 -1
- package/src/components/custom/Table/Table.tsx +2 -2
- package/src/components/custom/Treemap/Treemap.stories.tsx +13 -10
- package/src/components/custom/Treemap/Treemap.tsx +4 -1
- package/src/components/custom/Workout/ActiveWorkoutPage.stories.tsx +2 -1
- package/src/components/custom/Workout/BaseBadge.stories.tsx +11 -7
- package/src/components/custom/Workout/BaseBadge.tsx +3 -2
- package/src/components/custom/Workout/BodyMap.tsx +5 -3
- package/src/components/custom/Workout/BodyMapDetailPanel.stories.tsx +13 -3
- package/src/components/custom/Workout/BodyMapDetailPanel.tsx +5 -3
- package/src/components/custom/Workout/CapacityBandChart.stories.tsx +2 -1
- package/src/components/custom/Workout/CapacityBandChart.tsx +5 -3
- package/src/components/custom/Workout/DeviationBar.test.tsx +5 -2
- package/src/components/custom/Workout/DeviationBar.tsx +5 -2
- package/src/components/custom/Workout/DualVelocityStrip.stories.tsx +7 -6
- package/src/components/custom/Workout/DualVelocityStrip.test.tsx +29 -16
- package/src/components/custom/Workout/ExerciseCard.stories.tsx +8 -8
- package/src/components/custom/Workout/ExerciseCard.test.tsx +39 -19
- package/src/components/custom/Workout/ExerciseCard.tsx +92 -167
- package/src/components/custom/Workout/ExerciseCardHeading.stories.tsx +89 -6
- package/src/components/custom/Workout/ExerciseCardHeading.test.tsx +189 -1
- package/src/components/custom/Workout/ExerciseCardHeading.tsx +167 -18
- package/src/components/custom/Workout/ExerciseDetailPage.tsx +3 -2
- package/src/components/custom/Workout/ExerciseHeading.tsx +192 -47
- package/src/components/custom/Workout/ExerciseIndicator.stories.tsx +6 -5
- package/src/components/custom/Workout/InputBar.tsx +4 -2
- package/src/components/custom/Workout/IntensityBar.tsx +4 -1
- package/src/components/custom/Workout/MesoCard.tsx +2 -1
- package/src/components/custom/Workout/MesoStatusCard.tsx +11 -10
- package/src/components/custom/Workout/PlaceholderStrip.stories.tsx +8 -3
- package/src/components/custom/Workout/PrHistoryModal.stories.tsx +12 -2
- package/src/components/custom/Workout/README.md +58 -12
- package/src/components/custom/Workout/ReadinessCheck.tsx +4 -2
- package/src/components/custom/Workout/RestTimer.stories.tsx +13 -2
- package/src/components/custom/Workout/RestTimer.tsx +4 -2
- package/src/components/custom/Workout/SegmentedBar.stories.tsx +16 -12
- package/src/components/custom/Workout/SegmentedBar.tsx +4 -3
- package/src/components/custom/Workout/SetStrip.stories.tsx +3 -2
- package/src/components/custom/Workout/SetsRepsLoad.tsx +23 -9
- package/src/components/custom/Workout/Sparkline.stories.tsx +13 -10
- package/src/components/custom/Workout/StrengthTrendChart.stories.tsx +2 -1
- package/src/components/custom/Workout/StrengthTrendChart.tsx +8 -6
- package/src/components/custom/Workout/TempoDisplay.stories.tsx +4 -3
- package/src/components/custom/Workout/TempoDisplay.tsx +2 -0
- package/src/components/custom/Workout/VelocityStrip.dual.stories.tsx +3 -2
- package/src/components/custom/Workout/VelocityStrip.expanded.stories.tsx +1 -1
- package/src/components/custom/Workout/VelocityStrip.hero.stories.tsx +40 -0
- package/src/components/custom/Workout/VelocityStrip.test.tsx +48 -6
- package/src/components/custom/Workout/VelocityStrip.tsx +23 -14
- package/src/components/custom/Workout/ZoneTrack.stories.tsx +3 -1
- package/src/components/custom/Workout/exerciseRowState.ts +29 -0
- package/src/components/custom/Workout/index.ts +12 -2
- package/src/components/custom/Workout/metricText.stories.tsx +9 -8
- package/src/components/custom/Workout/metricText.tsx +4 -2
- package/src/components/custom/Workout/setHeadingKit.tsx +12 -10
- package/src/components/custom/Workout/velocity-story-kit.tsx +2 -2
- package/src/components/custom/charts/README.md +5 -0
- package/src/components/custom/charts/SetBarChart.test.tsx +16 -0
- package/src/components/custom/charts/SetBarChart.tsx +14 -5
- package/src/components/custom/charts/flatBarGeometry.test.ts +41 -0
- package/src/components/custom/charts/flatBarGeometry.ts +32 -0
- package/src/components/custom/stepper/Stepper.tsx +8 -6
- package/src/components/ui/alert/Alert.stories.tsx +14 -4
- package/src/components/ui/alert/Alert.tsx +24 -3
- package/src/components/ui/button/Button.tsx +12 -12
- package/src/components/ui/checkbox/Checkbox.tsx +2 -2
- package/src/components/ui/data-row/DataRow.stories.tsx +6 -2
- package/src/components/ui/drawer/Drawer.tsx +4 -1
- package/src/components/ui/icon-box/IconBox.tsx +8 -6
- package/src/components/ui/indicator/Indicator.stories.tsx +4 -3
- package/src/components/ui/list-item/ListItem.stories.tsx +7 -4
- package/src/components/ui/modal/Modal.tsx +2 -0
- package/src/components/ui/pill/Pill.stories.tsx +53 -0
- package/src/components/ui/pill/Pill.tsx +10 -6
- package/src/components/ui/popover/Popover.stories.tsx +1 -1
- package/src/components/ui/progress/Progress.tsx +9 -6
- package/src/components/ui/radio/Radio.tsx +1 -1
- package/src/components/ui/section/Section.stories.tsx +10 -10
- package/src/components/ui/select/Select.tsx +2 -1
- package/src/components/ui/spinner/Spinner.tsx +3 -1
- package/src/components/ui/stack/Stack.stories.tsx +7 -7
- package/src/components/ui/surface/Surface.stories.tsx +75 -28
- package/src/components/ui/switch/Switch.tsx +1 -1
- package/src/components/ui/tabs/Tabs.tsx +4 -2
- package/src/components/ui/toast/Toast.tsx +6 -4
- package/src/components/ui/toolbar-button/ToolbarButton.tsx +13 -6
- package/src/stories/PresetShowcase.stories.tsx +1 -1
- package/src/test/no-raw-color.test.ts +39 -0
- package/src/theme/Alpha.stories.tsx +103 -0
- package/src/theme/ProposedTokensVW82.stories.tsx +414 -0
- package/src/theme/config.ts +12 -0
- package/src/theme/global.css +15 -3
- package/src/theme/subtle-tone-weight.test.ts +163 -0
- package/src/theme/tailwind-var-opacity.test.ts +163 -0
- package/src/theme/tokens/primitives.ts +33 -0
- package/src/theme/tokens/semantic-hex-guard.test.ts +322 -0
- package/src/theme/tokens/semantic.ts +56 -20
- package/src/utils/colors.alpha-adoption.test.ts +408 -0
- package/tailwind.config.js +50 -9
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import type { Decorator, Meta, StoryObj } from '@storybook/react-vite'
|
|
2
2
|
import { View, Text } from 'react-native'
|
|
3
3
|
import { RestTimer } from './RestTimer'
|
|
4
|
+
import { greyRamp } from '../../../theme/tokens/primitives'
|
|
5
|
+
import { getSemanticColors } from '../../../theme/tokens/semantic'
|
|
6
|
+
|
|
7
|
+
const t = getSemanticColors('dark')
|
|
4
8
|
|
|
5
9
|
const meta: Meta<typeof RestTimer> = {
|
|
6
10
|
title: 'Custom/Workout/RestTimer',
|
|
@@ -100,10 +104,17 @@ export const JustStarted: Story = {
|
|
|
100
104
|
|
|
101
105
|
/** Wall-background frame for the ring stories (mirrors the north-star rest page). */
|
|
102
106
|
const ringDecorator: Decorator = (Story) => (
|
|
103
|
-
<View
|
|
107
|
+
<View
|
|
108
|
+
style={{
|
|
109
|
+
width: 420,
|
|
110
|
+
padding: 32,
|
|
111
|
+
alignItems: 'center',
|
|
112
|
+
backgroundColor: t['background-frame'],
|
|
113
|
+
}}
|
|
114
|
+
>
|
|
104
115
|
<Text
|
|
105
116
|
style={{
|
|
106
|
-
color:
|
|
117
|
+
color: greyRamp[700],
|
|
107
118
|
fontSize: 10,
|
|
108
119
|
fontWeight: '700',
|
|
109
120
|
letterSpacing: 1,
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
import { View, Text, Pressable } from 'react-native'
|
|
3
3
|
import { resolveColor } from '../../../theme/resolve-color'
|
|
4
4
|
import { getSemanticColors } from '../../../theme/tokens/semantic'
|
|
5
|
+
import { primitiveColors } from '../../../theme/tokens/primitives'
|
|
6
|
+
import { alpha } from '../../../utils/colors'
|
|
5
7
|
import { useTimer } from '../../../hooks/useTimer'
|
|
6
8
|
import { CircularTimer } from '../CircularTimer/CircularTimer'
|
|
7
9
|
|
|
@@ -37,7 +39,7 @@ function RestActions({ onAddTime, onSkip }: { onAddTime: () => void; onSkip: ()
|
|
|
37
39
|
<Pressable
|
|
38
40
|
onPress={onAddTime}
|
|
39
41
|
style={{
|
|
40
|
-
backgroundColor:
|
|
42
|
+
backgroundColor: alpha(primitiveColors.white, 0.06),
|
|
41
43
|
paddingVertical: 8,
|
|
42
44
|
paddingHorizontal: 20,
|
|
43
45
|
borderRadius: 8,
|
|
@@ -56,7 +58,7 @@ function RestActions({ onAddTime, onSkip }: { onAddTime: () => void; onSkip: ()
|
|
|
56
58
|
<Pressable
|
|
57
59
|
onPress={onSkip}
|
|
58
60
|
style={{
|
|
59
|
-
backgroundColor:
|
|
61
|
+
backgroundColor: alpha(BRAND_PRIMARY, 0.12),
|
|
60
62
|
paddingVertical: 8,
|
|
61
63
|
paddingHorizontal: 20,
|
|
62
64
|
borderRadius: 8,
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
2
|
import { SegmentedBar } from './SegmentedBar'
|
|
3
3
|
import { Surface } from '../../ui/surface'
|
|
4
|
+
import { primitiveColors, primitiveRamps } from '../../../theme/tokens/primitives'
|
|
5
|
+
import { getSemanticColors } from '../../../theme/tokens/semantic'
|
|
6
|
+
|
|
7
|
+
const t = getSemanticColors('dark')
|
|
4
8
|
|
|
5
9
|
/**
|
|
6
10
|
* `SegmentedBar` — a horizontal track split into weighted, individually-fillable
|
|
@@ -44,10 +48,10 @@ export const EqualSegments: Story = {
|
|
|
44
48
|
args: {
|
|
45
49
|
height: 8,
|
|
46
50
|
segments: [
|
|
47
|
-
{ color:
|
|
48
|
-
{ color: '
|
|
49
|
-
{ color: '
|
|
50
|
-
{ color: '
|
|
51
|
+
{ color: primitiveRamps.red[600] },
|
|
52
|
+
{ color: t['brand-primary'] },
|
|
53
|
+
{ color: t['status-warning'] },
|
|
54
|
+
{ color: t['status-success'] },
|
|
51
55
|
],
|
|
52
56
|
},
|
|
53
57
|
}
|
|
@@ -58,9 +62,9 @@ export const Weighted: Story = {
|
|
|
58
62
|
gap: 4,
|
|
59
63
|
radius: 2,
|
|
60
64
|
segments: [
|
|
61
|
-
{ color: '
|
|
62
|
-
{ color: '
|
|
63
|
-
{ color:
|
|
65
|
+
{ color: t['status-success'], weight: 3 },
|
|
66
|
+
{ color: t['status-warning'], weight: 2 },
|
|
67
|
+
{ color: primitiveRamps.red[600], weight: 1 },
|
|
64
68
|
],
|
|
65
69
|
},
|
|
66
70
|
parameters: {
|
|
@@ -74,9 +78,9 @@ export const PartialFills: Story = {
|
|
|
74
78
|
gap: 4,
|
|
75
79
|
radius: 2,
|
|
76
80
|
segments: [
|
|
77
|
-
{ color: '
|
|
78
|
-
{ color: '
|
|
79
|
-
{ color:
|
|
81
|
+
{ color: t['status-success'], fill: 1 },
|
|
82
|
+
{ color: t['status-warning'], fill: 0.6 },
|
|
83
|
+
{ color: primitiveRamps.cyan[400], fill: 0.3, pulse: true },
|
|
80
84
|
],
|
|
81
85
|
},
|
|
82
86
|
parameters: {
|
|
@@ -90,8 +94,8 @@ export const WithMarker: Story = {
|
|
|
90
94
|
args: {
|
|
91
95
|
height: 10,
|
|
92
96
|
radius: 2,
|
|
93
|
-
segments: [{ color:
|
|
94
|
-
marker: { position: 0.5, color:
|
|
97
|
+
segments: [{ color: primitiveRamps.cyan[400], fill: 0.7 }],
|
|
98
|
+
marker: { position: 0.5, color: primitiveColors.white },
|
|
95
99
|
},
|
|
96
100
|
parameters: {
|
|
97
101
|
docs: { description: { story: 'A single fill with a target marker at the halfway point.' } },
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
|
|
2
2
|
import { useEffect, useState } from 'react'
|
|
3
3
|
import { View, Animated, Easing, type ViewProps, type DimensionValue } from 'react-native'
|
|
4
|
+
import { SET_LEVEL_FLAT_BAR } from '../charts/flatBarGeometry'
|
|
4
5
|
|
|
5
6
|
/** ~1.9s full cycle: a pulsing segment eases up then back down (2 × half). */
|
|
6
7
|
const PULSE_HALF_MS = 950
|
|
@@ -9,7 +10,7 @@ const PULSE_HALF_MS = 950
|
|
|
9
10
|
const PULSE_MIN_OPACITY = 0.45
|
|
10
11
|
|
|
11
12
|
/** Default between-segment gap — matches SetStrip's between-set gap so strips read the same. */
|
|
12
|
-
const SEGMENTED_BAR_GAP =
|
|
13
|
+
const SEGMENTED_BAR_GAP = SET_LEVEL_FLAT_BAR.gap
|
|
13
14
|
|
|
14
15
|
export interface SegmentedBarSegment {
|
|
15
16
|
/** Flex weight of this segment's slot. Default 1 (all segments equal width). */
|
|
@@ -98,9 +99,9 @@ function usePulse(active: boolean): Animated.Value {
|
|
|
98
99
|
*/
|
|
99
100
|
export function SegmentedBar({
|
|
100
101
|
segments,
|
|
101
|
-
height =
|
|
102
|
+
height = SET_LEVEL_FLAT_BAR.height,
|
|
102
103
|
gap = SEGMENTED_BAR_GAP,
|
|
103
|
-
radius =
|
|
104
|
+
radius = SET_LEVEL_FLAT_BAR.radius,
|
|
104
105
|
marker = null,
|
|
105
106
|
segmentTestID,
|
|
106
107
|
style,
|
|
@@ -2,6 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
|
2
2
|
import { View, Text } from 'react-native'
|
|
3
3
|
import { SetStrip, type SetStripSet } from './SetStrip'
|
|
4
4
|
import { Surface } from '../../ui/surface'
|
|
5
|
+
import { greyRamp } from '../../../theme/tokens/primitives'
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* `SetStrip` — the per-set segmented performance strip. One continuous bar per
|
|
@@ -138,8 +139,8 @@ function SheetRow({ label, meta, sets }: { label: string; meta: string; sets: Se
|
|
|
138
139
|
return (
|
|
139
140
|
<View style={{ gap: 6 }}>
|
|
140
141
|
<View style={{ flexDirection: 'row', alignItems: 'baseline', gap: 8 }}>
|
|
141
|
-
<Text style={{ color:
|
|
142
|
-
<Text style={{ color:
|
|
142
|
+
<Text style={{ color: greyRamp[50], fontSize: 13, fontWeight: '800' }}>{label}</Text>
|
|
143
|
+
<Text style={{ color: greyRamp[600], fontSize: 10, fontFamily: 'monospace' }}>{meta}</Text>
|
|
143
144
|
</View>
|
|
144
145
|
<SetStrip sets={sets} />
|
|
145
146
|
</View>
|
|
@@ -15,12 +15,18 @@ export interface SetsRepsLoadProps extends ViewProps {
|
|
|
15
15
|
unit?: string
|
|
16
16
|
/** Cell font size (px). Default 11 — the compact rail/card scale. Raise for a wall read-out. */
|
|
17
17
|
fontSize?: number
|
|
18
|
+
/**
|
|
19
|
+
* Flatten the whole run to one dimmer, regular-weight tone (the collapsed/upcoming
|
|
20
|
+
* card row) instead of the bright bold value / muted separator split (the rail).
|
|
21
|
+
* Also drops the `×` separator's padding spaces — "3×6", not "3 × 6".
|
|
22
|
+
*/
|
|
23
|
+
muted?: boolean
|
|
18
24
|
className?: string
|
|
19
25
|
}
|
|
20
26
|
|
|
21
27
|
/**
|
|
22
28
|
* The `sets × reps @ load` prescription line, in the TempoDisplay visual language
|
|
23
|
-
* (Inter ·
|
|
29
|
+
* (Inter · letter-spacing 1 · value cells with muted `×` / `@` separators).
|
|
24
30
|
* Shares the {@link MetricCell} primitive with TempoDisplay so the two stay in step.
|
|
25
31
|
*/
|
|
26
32
|
export function SetsRepsLoad({
|
|
@@ -29,11 +35,17 @@ export function SetsRepsLoad({
|
|
|
29
35
|
load,
|
|
30
36
|
unit = 'lb',
|
|
31
37
|
fontSize,
|
|
38
|
+
muted = false,
|
|
32
39
|
className,
|
|
33
40
|
...props
|
|
34
41
|
}: SetsRepsLoadProps) {
|
|
35
|
-
const
|
|
36
|
-
const
|
|
42
|
+
const primary = useOnSurfaceColor('primary')
|
|
43
|
+
const tertiary = useOnSurfaceColor('tertiary')
|
|
44
|
+
const secondary = useOnSurfaceColor('secondary')
|
|
45
|
+
const value = muted ? secondary : primary
|
|
46
|
+
const sep = muted ? secondary : tertiary
|
|
47
|
+
const weight = muted ? 400 : 600
|
|
48
|
+
const times = muted ? TIMES : ` ${TIMES} `
|
|
37
49
|
|
|
38
50
|
return (
|
|
39
51
|
<View
|
|
@@ -43,18 +55,20 @@ export function SetsRepsLoad({
|
|
|
43
55
|
testID="sets-reps-load"
|
|
44
56
|
{...props}
|
|
45
57
|
>
|
|
46
|
-
<MetricCell color={value} fontSize={fontSize}>
|
|
58
|
+
<MetricCell color={value} fontSize={fontSize} weight={weight}>
|
|
47
59
|
{sets}
|
|
48
60
|
</MetricCell>
|
|
49
|
-
<MetricCell color={sep} fontSize={fontSize}
|
|
50
|
-
|
|
61
|
+
<MetricCell color={sep} fontSize={fontSize} weight={weight}>
|
|
62
|
+
{times}
|
|
63
|
+
</MetricCell>
|
|
64
|
+
<MetricCell color={value} fontSize={fontSize} weight={weight}>
|
|
51
65
|
{reps}
|
|
52
66
|
</MetricCell>
|
|
53
|
-
<MetricCell color={sep} fontSize={fontSize}>{` ${AT} `}</MetricCell>
|
|
54
|
-
<MetricCell color={value} fontSize={fontSize}>
|
|
67
|
+
<MetricCell color={sep} fontSize={fontSize} weight={weight}>{` ${AT} `}</MetricCell>
|
|
68
|
+
<MetricCell color={value} fontSize={fontSize} weight={weight}>
|
|
55
69
|
{load}
|
|
56
70
|
</MetricCell>
|
|
57
|
-
<MetricCell color={sep} fontSize={fontSize}>{` ${unit}`}</MetricCell>
|
|
71
|
+
<MetricCell color={sep} fontSize={fontSize} weight={weight}>{` ${unit}`}</MetricCell>
|
|
58
72
|
</View>
|
|
59
73
|
)
|
|
60
74
|
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
2
|
import { View } from 'react-native'
|
|
3
3
|
import { Sparkline } from './Sparkline'
|
|
4
|
+
import { getSemanticColors } from '../../../theme/tokens/semantic'
|
|
5
|
+
|
|
6
|
+
const t = getSemanticColors('dark')
|
|
4
7
|
|
|
5
8
|
const meta: Meta<typeof Sparkline> = {
|
|
6
9
|
title: 'Custom/Workout/DataViz/Sparkline',
|
|
@@ -56,7 +59,7 @@ export const Descending: Story = {
|
|
|
56
59
|
args: {
|
|
57
60
|
data: descending,
|
|
58
61
|
showDots: true,
|
|
59
|
-
color: '
|
|
62
|
+
color: t['status-error-vivid'],
|
|
60
63
|
},
|
|
61
64
|
}
|
|
62
65
|
|
|
@@ -64,7 +67,7 @@ export const UCurve: Story = {
|
|
|
64
67
|
args: {
|
|
65
68
|
data: uCurve,
|
|
66
69
|
showDots: true,
|
|
67
|
-
color: '
|
|
70
|
+
color: t['status-success'],
|
|
68
71
|
},
|
|
69
72
|
}
|
|
70
73
|
|
|
@@ -72,7 +75,7 @@ export const Flat: Story = {
|
|
|
72
75
|
args: {
|
|
73
76
|
data: flat,
|
|
74
77
|
showDots: true,
|
|
75
|
-
color: '
|
|
78
|
+
color: t['status-warning-light'],
|
|
76
79
|
},
|
|
77
80
|
}
|
|
78
81
|
|
|
@@ -81,8 +84,8 @@ export const WithReferenceLines: Story = {
|
|
|
81
84
|
data: ascending,
|
|
82
85
|
showDots: true,
|
|
83
86
|
referenceLines: [
|
|
84
|
-
{ value: 30, color: '
|
|
85
|
-
{ value: 45, color: '
|
|
87
|
+
{ value: 30, color: t['status-error-vivid'], dashed: true },
|
|
88
|
+
{ value: 45, color: t['status-success'], dashed: false },
|
|
86
89
|
],
|
|
87
90
|
},
|
|
88
91
|
}
|
|
@@ -99,7 +102,7 @@ export const DotsAndHighlightLast: Story = {
|
|
|
99
102
|
data: volatile,
|
|
100
103
|
showDots: true,
|
|
101
104
|
highlightLast: true,
|
|
102
|
-
color: '
|
|
105
|
+
color: t['status-warning'],
|
|
103
106
|
},
|
|
104
107
|
}
|
|
105
108
|
|
|
@@ -116,10 +119,10 @@ export const AllShapes: Story = {
|
|
|
116
119
|
render: () => (
|
|
117
120
|
<View style={{ gap: 16, padding: 16 }}>
|
|
118
121
|
<Sparkline data={ascending} showDots highlightLast />
|
|
119
|
-
<Sparkline data={descending} showDots color=
|
|
120
|
-
<Sparkline data={uCurve} showDots color=
|
|
121
|
-
<Sparkline data={flat} showDots color=
|
|
122
|
-
<Sparkline data={volatile} showDots color=
|
|
122
|
+
<Sparkline data={descending} showDots color={t['status-error-vivid']} />
|
|
123
|
+
<Sparkline data={uCurve} showDots color={t['status-success']} />
|
|
124
|
+
<Sparkline data={flat} showDots color={t['status-warning-light']} />
|
|
125
|
+
<Sparkline data={volatile} showDots color={t['status-warning']} highlightLast />
|
|
123
126
|
</View>
|
|
124
127
|
),
|
|
125
128
|
}
|
|
@@ -3,6 +3,7 @@ import { useState } from 'react'
|
|
|
3
3
|
import { View, Text } from 'react-native'
|
|
4
4
|
import { StrengthTrendChart } from './StrengthTrendChart'
|
|
5
5
|
import type { StrengthTrendDataPoint } from './StrengthTrendChart'
|
|
6
|
+
import { greyRamp } from '../../../theme/tokens/primitives'
|
|
6
7
|
|
|
7
8
|
const actual: StrengthTrendDataPoint[] = [
|
|
8
9
|
{ date: '2026-01-06', e1rm: 215, sessionLabel: 'Jan 6' },
|
|
@@ -122,7 +123,7 @@ function InteractiveChart() {
|
|
|
122
123
|
unit="lbs"
|
|
123
124
|
onPointPress={setSelected}
|
|
124
125
|
/>
|
|
125
|
-
<Text style={{ marginTop: 12, fontSize: 12, color:
|
|
126
|
+
<Text style={{ marginTop: 12, fontSize: 12, color: greyRamp[400] }}>
|
|
126
127
|
{selected
|
|
127
128
|
? `Tapped ${selected.sessionLabel ?? selected.date}: ${selected.e1rm} lbs`
|
|
128
129
|
: 'Tap a point on the chart.'}
|
|
@@ -4,6 +4,8 @@ import { View, Text, Pressable, Animated, Easing, type ViewProps } from 'react-n
|
|
|
4
4
|
import { cn } from '../../../utils/cn'
|
|
5
5
|
import { resolveColor } from '../../../theme/resolve-color'
|
|
6
6
|
import { getSemanticColors } from '../../../theme/tokens/semantic'
|
|
7
|
+
import { primitiveColors } from '../../../theme/tokens/primitives'
|
|
8
|
+
import { alpha } from '../../../utils/colors'
|
|
7
9
|
|
|
8
10
|
const sem = getSemanticColors('dark')
|
|
9
11
|
|
|
@@ -14,11 +16,11 @@ const PROJECTION_LINE_COLOR = resolveColor('text-tertiary')
|
|
|
14
16
|
const STATUS_SUCCESS = sem['status-success']
|
|
15
17
|
const STATUS_ERROR = sem['status-error']
|
|
16
18
|
const STATUS_WARNING = sem['status-warning']
|
|
17
|
-
const GRID_LINE =
|
|
18
|
-
const SUCCESS_PILL_BG =
|
|
19
|
-
const SUCCESS_PILL_BORDER =
|
|
20
|
-
const ERROR_PILL_BG =
|
|
21
|
-
const ERROR_PILL_BORDER =
|
|
19
|
+
const GRID_LINE = alpha(primitiveColors.white, 0.06)
|
|
20
|
+
const SUCCESS_PILL_BG = alpha(STATUS_SUCCESS, 0.1)
|
|
21
|
+
const SUCCESS_PILL_BORDER = alpha(STATUS_SUCCESS, 0.2)
|
|
22
|
+
const ERROR_PILL_BG = alpha(STATUS_ERROR, 0.1)
|
|
23
|
+
const ERROR_PILL_BORDER = alpha(STATUS_ERROR, 0.2)
|
|
22
24
|
|
|
23
25
|
/** Left gutter reserved for y-axis value labels. */
|
|
24
26
|
const PLOT_LEFT = 26
|
|
@@ -379,7 +381,7 @@ export function StrengthTrendChart({
|
|
|
379
381
|
height,
|
|
380
382
|
borderLeftWidth: 1,
|
|
381
383
|
borderStyle: 'dashed',
|
|
382
|
-
borderLeftColor:
|
|
384
|
+
borderLeftColor: alpha(primitiveColors.white, 0.1),
|
|
383
385
|
}}
|
|
384
386
|
/>
|
|
385
387
|
))}
|
|
@@ -3,6 +3,7 @@ import type { ComponentProps } from 'react'
|
|
|
3
3
|
import { useEffect, useState } from 'react'
|
|
4
4
|
import { View, Text } from 'react-native'
|
|
5
5
|
import { TempoDisplay, type TempoLivePhase, type TempoLiveState } from './TempoDisplay'
|
|
6
|
+
import { greyRamp } from '../../../theme/tokens/primitives'
|
|
6
7
|
|
|
7
8
|
// tempo = [eccentric, pauseBottom, concentric, pauseTop]
|
|
8
9
|
const meta: Meta<typeof TempoDisplay> = {
|
|
@@ -166,7 +167,7 @@ function Condition({
|
|
|
166
167
|
}: { caption: string } & ComponentProps<typeof TempoDisplay>) {
|
|
167
168
|
return (
|
|
168
169
|
<View style={{ gap: 5 }}>
|
|
169
|
-
<Text style={{ color:
|
|
170
|
+
<Text style={{ color: greyRamp[400], fontFamily: 'Inter, sans-serif', fontSize: 11 }}>
|
|
170
171
|
{caption}
|
|
171
172
|
</Text>
|
|
172
173
|
<TempoDisplay showLabel={false} showInfo={false} fontSize={26} {...props} />
|
|
@@ -179,7 +180,7 @@ function Section({ title, children }: { title: string; children: React.ReactNode
|
|
|
179
180
|
<View style={{ gap: 12 }}>
|
|
180
181
|
<Text
|
|
181
182
|
style={{
|
|
182
|
-
color:
|
|
183
|
+
color: greyRamp[100],
|
|
183
184
|
fontFamily: 'Inter, sans-serif',
|
|
184
185
|
fontSize: 12,
|
|
185
186
|
fontWeight: '700',
|
|
@@ -313,7 +314,7 @@ export const ActiveTempoConditions: Story = {
|
|
|
313
314
|
live={{ activePhase: null, phaseElapsedMs: 0 }}
|
|
314
315
|
/>
|
|
315
316
|
<View style={{ gap: 5 }}>
|
|
316
|
-
<Text style={{ color:
|
|
317
|
+
<Text style={{ color: greyRamp[400], fontFamily: 'Inter, sans-serif', fontSize: 11 }}>
|
|
317
318
|
Animated · Ecc → Pause → Con → 2s rest, banking each phase
|
|
318
319
|
</Text>
|
|
319
320
|
<LiveTempoDemo />
|
|
@@ -53,6 +53,8 @@ export interface TempoDisplayProps extends ViewProps {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
const INTER = 'Inter, sans-serif'
|
|
56
|
+
// VW-82: Tailwind gray-500, not titan's text-tertiary (#888684) despite the name.
|
|
57
|
+
// Left raw — re-pointing it at the ramp is a visual change awaiting sign-off.
|
|
56
58
|
const TEXT_TERTIARY = '#6B7280'
|
|
57
59
|
const STATUS_ERROR = t['status-error'] // slow — over the target time
|
|
58
60
|
const STATUS_SUCCESS = t['status-success'] // on target (within the band of 0.0)
|
|
@@ -57,9 +57,10 @@ const meta: Meta<typeof DualVelocityStrip> = {
|
|
|
57
57
|
},
|
|
58
58
|
variant: {
|
|
59
59
|
control: 'inline-radio',
|
|
60
|
-
options: ['hero', 'compact', '
|
|
60
|
+
options: ['hero', 'compact', 'dual-expanded'],
|
|
61
61
|
description:
|
|
62
|
-
'hero (wall: labels + reference lines), compact (flat resting fold), or
|
|
62
|
+
'hero (wall: labels + reference lines), compact (flat resting fold), or dual-expanded ' +
|
|
63
|
+
'(lean, neither — `"rail"` is a deprecated alias for the same renderer)',
|
|
63
64
|
},
|
|
64
65
|
scale: {
|
|
65
66
|
control: 'inline-radio',
|
|
@@ -199,7 +199,7 @@ export const Responsive: Story = {
|
|
|
199
199
|
<DualVelocityStrip
|
|
200
200
|
left={{ velocities: REP_SET, label: 'Left' }}
|
|
201
201
|
right={{ velocities: REP_SET_LAGGING, label: 'Right' }}
|
|
202
|
-
variant="
|
|
202
|
+
variant="dual-expanded"
|
|
203
203
|
height={h}
|
|
204
204
|
scale="fixed"
|
|
205
205
|
/>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
2
|
import { View } from 'react-native'
|
|
3
3
|
import { VelocityStrip, DualVelocityStrip } from './VelocityStrip'
|
|
4
|
+
import { FIXED_MAX_VALUE, PEAK_HEADROOM } from '../charts/SetBarChart'
|
|
4
5
|
import {
|
|
5
6
|
Sheet,
|
|
6
7
|
Note,
|
|
@@ -216,6 +217,45 @@ export const RepTypes: Story = {
|
|
|
216
217
|
),
|
|
217
218
|
}
|
|
218
219
|
|
|
220
|
+
/**
|
|
221
|
+
* VW-97: `scale="fixed"` used the bare `FIXED_MAX_VALUE` ceiling with no headroom multiplier, so a
|
|
222
|
+
* rep AT the ceiling filled the plot to its very top with no room for its own label — unlike an
|
|
223
|
+
* equally-tall `peak` bar, which always kept `PEAK_HEADROOM`'s margin. `scaleDenominator` now
|
|
224
|
+
* applies the same headroom to both scales.
|
|
225
|
+
*
|
|
226
|
+
* "Before" reproduces the old denominator through the public `scaleMax` prop (`scaleMax *
|
|
227
|
+
* PEAK_HEADROOM` cancels back down to the bare ceiling) rather than a hand-rolled duplicate, so
|
|
228
|
+
* this story exercises the real component on both sides.
|
|
229
|
+
*/
|
|
230
|
+
export const FixedScaleHeadroom: Story = {
|
|
231
|
+
name: 'Fixed Scale Headroom (VW-97)',
|
|
232
|
+
decorators: [wallDecorator],
|
|
233
|
+
render: () => (
|
|
234
|
+
<View style={{ flexDirection: 'row', gap: 24 }}>
|
|
235
|
+
<View style={{ gap: 8, flex: 1 }}>
|
|
236
|
+
<ViewLabel text="before · ceiling bar touches the plot top" />
|
|
237
|
+
<VelocityStrip
|
|
238
|
+
velocities={[FIXED_MAX_VALUE, FIXED_MAX_VALUE * 0.6]}
|
|
239
|
+
variant="hero"
|
|
240
|
+
label="Set"
|
|
241
|
+
height={140}
|
|
242
|
+
scaleMax={FIXED_MAX_VALUE / PEAK_HEADROOM}
|
|
243
|
+
/>
|
|
244
|
+
</View>
|
|
245
|
+
<View style={{ gap: 8, flex: 1 }}>
|
|
246
|
+
<ViewLabel text="after · fixed reserves the same headroom peak does" />
|
|
247
|
+
<VelocityStrip
|
|
248
|
+
velocities={[FIXED_MAX_VALUE, FIXED_MAX_VALUE * 0.6]}
|
|
249
|
+
variant="hero"
|
|
250
|
+
label="Set"
|
|
251
|
+
height={140}
|
|
252
|
+
scale="fixed"
|
|
253
|
+
/>
|
|
254
|
+
</View>
|
|
255
|
+
</View>
|
|
256
|
+
),
|
|
257
|
+
}
|
|
258
|
+
|
|
219
259
|
export const EdgeCases: Story = {
|
|
220
260
|
name: 'Edge Cases',
|
|
221
261
|
decorators: [wallDecorator],
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
calculateMeanVelocity,
|
|
10
10
|
getVelocityLossColor,
|
|
11
11
|
} from './VelocityStrip'
|
|
12
|
+
import { FIXED_MAX_VALUE } from '../charts/SetBarChart'
|
|
12
13
|
|
|
13
14
|
const sampleVelocities = [1.1, 0.95, 0.82, 0.68, 0.45]
|
|
14
15
|
|
|
@@ -583,28 +584,69 @@ describe('VelocityStrip expanded bare strip (spotlight: numbers + info off)', ()
|
|
|
583
584
|
})
|
|
584
585
|
|
|
585
586
|
it('scales bar height to velocity against the fixed ceiling at the given height', () => {
|
|
587
|
+
const barPx = (id: string) => parseFloat(getComputedStyle(screen.getByTestId(id)).height)
|
|
586
588
|
render(
|
|
587
589
|
<VelocityStrip
|
|
588
590
|
{...bareProps}
|
|
589
591
|
scale="fixed"
|
|
590
592
|
height={24}
|
|
591
|
-
set={{ type: 'straight', velocities: [
|
|
593
|
+
set={{ type: 'straight', velocities: [0.6, 0.3], planned: 2 }}
|
|
592
594
|
/>
|
|
593
595
|
)
|
|
594
|
-
|
|
595
|
-
|
|
596
|
+
// Proportional to velocity against the fixed ceiling, not the performed max — a 2:1 velocity
|
|
597
|
+
// ratio reads as a 2:1 bar-height ratio.
|
|
598
|
+
expect(barPx('velocity-bar-0')).toBeCloseTo(2 * barPx('velocity-bar-1'), 5)
|
|
596
599
|
})
|
|
597
600
|
|
|
598
601
|
it('honors height for the scaling', () => {
|
|
599
|
-
|
|
602
|
+
const barPx = (id: string) => parseFloat(getComputedStyle(screen.getByTestId(id)).height)
|
|
603
|
+
const { unmount } = render(
|
|
600
604
|
<VelocityStrip
|
|
601
605
|
{...bareProps}
|
|
602
606
|
scale="fixed"
|
|
603
607
|
height={40}
|
|
604
|
-
set={{ type: 'straight', velocities: [
|
|
608
|
+
set={{ type: 'straight', velocities: [0.6], planned: 1 }}
|
|
609
|
+
/>
|
|
610
|
+
)
|
|
611
|
+
const at40 = barPx('velocity-bar-0')
|
|
612
|
+
unmount()
|
|
613
|
+
render(
|
|
614
|
+
<VelocityStrip
|
|
615
|
+
{...bareProps}
|
|
616
|
+
scale="fixed"
|
|
617
|
+
height={80}
|
|
618
|
+
set={{ type: 'straight', velocities: [0.6], planned: 1 }}
|
|
619
|
+
/>
|
|
620
|
+
)
|
|
621
|
+
expect(barPx('velocity-bar-0')).toBeCloseTo(2 * at40, 5)
|
|
622
|
+
})
|
|
623
|
+
|
|
624
|
+
it('reserves the same headroom above the fixed ceiling that `peak` reserves above its own max (VW-97)', () => {
|
|
625
|
+
// A rep AT the fixed ceiling must NOT fill the plot — it needs the identical margin an
|
|
626
|
+
// equally-tall `peak` bar always keeps for its own value label.
|
|
627
|
+
const { unmount } = render(
|
|
628
|
+
<VelocityStrip
|
|
629
|
+
{...bareProps}
|
|
630
|
+
scale="fixed"
|
|
631
|
+
height={100}
|
|
632
|
+
set={{ type: 'straight', velocities: [FIXED_MAX_VALUE], planned: 1 }}
|
|
633
|
+
/>
|
|
634
|
+
)
|
|
635
|
+
const fixedAtCeilingPx = parseFloat(
|
|
636
|
+
getComputedStyle(screen.getByTestId('velocity-bar-0')).height
|
|
637
|
+
)
|
|
638
|
+
unmount()
|
|
639
|
+
render(
|
|
640
|
+
<VelocityStrip
|
|
641
|
+
{...bareProps}
|
|
642
|
+
scale="peak"
|
|
643
|
+
height={100}
|
|
644
|
+
set={{ type: 'straight', velocities: [1], planned: 1 }}
|
|
605
645
|
/>
|
|
606
646
|
)
|
|
607
|
-
|
|
647
|
+
const peakAtMaxPx = parseFloat(getComputedStyle(screen.getByTestId('velocity-bar-0')).height)
|
|
648
|
+
expect(fixedAtCeilingPx).toBeLessThan(100)
|
|
649
|
+
expect(fixedAtCeilingPx).toBeCloseTo(peakAtMaxPx, 5)
|
|
608
650
|
})
|
|
609
651
|
|
|
610
652
|
it('draws planned-but-undone reps as short grey todo stubs (set-type aware)', () => {
|
|
@@ -702,9 +702,14 @@ export interface DualVelocityStripProps extends ViewProps {
|
|
|
702
702
|
* `hero` — the across-the-room wall scale: tall wings, per-rep m/s value labels, and a dashed
|
|
703
703
|
* running-best reference line per side. `compact` — the flat resting form: dual-hero but flat +
|
|
704
704
|
* no labels (both wings are the `compact` VelocityStrip variant), same aligned structure + shared
|
|
705
|
-
* gutter + axis. `
|
|
705
|
+
* gutter + axis. `dual-expanded` — the compact rail-expanded lean renderer, no labels / reference
|
|
706
|
+
* lines (VW-97: `rail` was a verified misnomer — this variant is the value-height strip a session
|
|
707
|
+
* rail row expands into, not the rail itself).
|
|
708
|
+
*
|
|
709
|
+
* `rail` is a **deprecated alias** for `dual-expanded` (same renderer, same output) — kept for one
|
|
710
|
+
* release so existing call sites keep working; see the Workout README.
|
|
706
711
|
*/
|
|
707
|
-
variant?: 'hero' | 'compact' | 'rail'
|
|
712
|
+
variant?: 'hero' | 'compact' | 'dual-expanded' | 'rail'
|
|
708
713
|
/**
|
|
709
714
|
* Bar-height scaling, shared across BOTH wings so the L/R asymmetry reads as bar
|
|
710
715
|
* length against one scale. `peak` (default) = the pair's max +headroom; `fixed` =
|
|
@@ -926,12 +931,12 @@ function DualVelocityHero({
|
|
|
926
931
|
}
|
|
927
932
|
|
|
928
933
|
/**
|
|
929
|
-
* The `
|
|
930
|
-
* drag in its value labels, reference lines, paper, and label
|
|
931
|
-
*
|
|
932
|
-
* height scale + per-side loss coloring + mirrored radius + shared
|
|
933
|
-
* / reference lines / paper. (Set-type slot windows are a
|
|
934
|
-
* takes plain velocities.)
|
|
934
|
+
* The `dual-expanded` diverging chart (formerly `rail`) — a lean, compact dedicated renderer.
|
|
935
|
+
* Composing the hero here would drag in its value labels, reference lines, paper, and label
|
|
936
|
+
* headroom, none of which belong at this scale; instead it draws mirrored per-side bars at the
|
|
937
|
+
* compact metrics. Same shared height scale + per-side loss coloring + mirrored radius + shared
|
|
938
|
+
* axis as the hero, but no labels / reference lines / paper. (Set-type slot windows are a
|
|
939
|
+
* hero-composition concern; this renderer only ever takes plain velocities.)
|
|
935
940
|
*/
|
|
936
941
|
function DualVelocityRail({
|
|
937
942
|
leftDone,
|
|
@@ -1090,11 +1095,14 @@ export function DualVelocityStrip({
|
|
|
1090
1095
|
className,
|
|
1091
1096
|
...props
|
|
1092
1097
|
}: DualVelocityStripProps) {
|
|
1098
|
+
// `rail` is the deprecated alias for `dual-expanded` — normalize once so every branch below
|
|
1099
|
+
// reads one canonical value instead of re-checking both spellings.
|
|
1100
|
+
const resolvedVariant = variant === 'rail' ? 'dual-expanded' : variant
|
|
1093
1101
|
const resolvedHeight =
|
|
1094
1102
|
height ??
|
|
1095
|
-
(
|
|
1103
|
+
(resolvedVariant === 'hero'
|
|
1096
1104
|
? DUAL_HERO_HEIGHT
|
|
1097
|
-
:
|
|
1105
|
+
: resolvedVariant === 'compact'
|
|
1098
1106
|
? DUAL_COMPACT_HEIGHT
|
|
1099
1107
|
: DUAL_RAIL_HEIGHT)
|
|
1100
1108
|
// The done arrays drive the shared max + per-side best + the summary counts; the raw streams
|
|
@@ -1123,10 +1131,11 @@ export function DualVelocityStrip({
|
|
|
1123
1131
|
viewProps: props,
|
|
1124
1132
|
}
|
|
1125
1133
|
|
|
1126
|
-
// `liveRepIndex` reaches
|
|
1127
|
-
// from the midline on BOTH sides. Compact is flat, so a grow animation has nothing to animate.
|
|
1128
|
-
if (
|
|
1129
|
-
|
|
1134
|
+
// `liveRepIndex` reaches dual-expanded too: its wings are composed strips, so the newest rep
|
|
1135
|
+
// grows from the midline on BOTH sides. Compact is flat, so a grow animation has nothing to animate.
|
|
1136
|
+
if (resolvedVariant === 'dual-expanded')
|
|
1137
|
+
return <DualVelocityRail {...shared} liveRepIndex={liveRepIndex} />
|
|
1138
|
+
if (resolvedVariant === 'compact') return <DualVelocityCompactStrip {...shared} />
|
|
1130
1139
|
return <DualVelocityHero {...shared} liveRepIndex={liveRepIndex} />
|
|
1131
1140
|
}
|
|
1132
1141
|
|
|
@@ -3,8 +3,10 @@ import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
|
3
3
|
import { ZoneTrack, type ZoneTrackZone } from './ZoneTrack'
|
|
4
4
|
import { WORKOUT_TOKENS } from '../../../theme/workout-tokens'
|
|
5
5
|
import { Surface } from '../../ui/surface'
|
|
6
|
+
import { getSemanticColors } from '../../../theme/tokens/semantic'
|
|
6
7
|
|
|
7
8
|
const { green, yellow, orange, red } = WORKOUT_TOKENS.scale
|
|
9
|
+
const t = getSemanticColors('dark')
|
|
8
10
|
|
|
9
11
|
const FATIGUE_ZONES: ZoneTrackZone[] = [
|
|
10
12
|
{ upTo: 10, color: green },
|
|
@@ -86,7 +88,7 @@ export const BandWithMarker: Story = {
|
|
|
86
88
|
],
|
|
87
89
|
min: 6,
|
|
88
90
|
max: 10,
|
|
89
|
-
band: { from: 7.2, to: 8.4, color: '
|
|
91
|
+
band: { from: 7.2, to: 8.4, color: t['hairline-strong'] },
|
|
90
92
|
marker: { type: 'needle', value: 8.1 },
|
|
91
93
|
ticks: [
|
|
92
94
|
{ value: 6, label: '6' },
|