@titan-design/react-ui 0.12.1 → 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 +60 -47
- package/dist/bodymap.js +856 -736
- package/dist/bodymap.js.map +1 -1
- package/dist/bodymap.mjs +858 -738
- package/dist/bodymap.mjs.map +1 -1
- package/dist/index.d.mts +1431 -237
- package/dist/index.d.ts +1431 -237
- package/dist/index.js +6506 -4781
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +6280 -4642
- package/dist/index.mjs.map +1 -1
- package/dist/{pages-B1hqnGIC.d.ts → pages-CsjsUn0G.d.ts} +51 -16
- package/dist/{pages-CvsFqNtx.d.mts → pages-ZbT_pD_U.d.mts} +51 -16
- package/dist/pages.d.mts +1 -1
- package/dist/pages.d.ts +1 -1
- package/dist/pages.js +1919 -1937
- package/dist/pages.js.map +1 -1
- package/dist/pages.mjs +1927 -1945
- package/dist/pages.mjs.map +1 -1
- package/dist/theme/index.d.mts +225 -140
- package/dist/theme/index.d.ts +225 -140
- package/dist/theme/index.js +298 -319
- package/dist/theme/index.js.map +1 -1
- package/dist/theme/index.mjs +283 -315
- package/dist/theme/index.mjs.map +1 -1
- package/dist/theme/tokens-css.js +145 -24
- package/dist/theme/tokens-css.js.map +1 -1
- package/dist/theme/tokens-css.mjs +145 -24
- package/dist/theme/tokens-css.mjs.map +1 -1
- package/dist/theme/tokens.d.mts +128 -11
- package/dist/theme/tokens.d.ts +128 -11
- package/dist/theme/tokens.js +126 -26
- package/dist/theme/tokens.js.map +1 -1
- package/dist/theme/tokens.mjs +124 -27
- package/dist/theme/tokens.mjs.map +1 -1
- package/docs/audits/2026-09-08-critique-addenda.md +51 -0
- package/docs/audits/2026-09-08-library-critique.md +212 -0
- package/docs/library-roadmap.md +99 -0
- 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.freshness.test.ts +52 -0
- package/src/arch/arch-graph.json +4989 -1583
- package/src/components/custom/ActiveWork/CoChangeChip.stories.tsx +44 -0
- package/src/components/custom/ActiveWork/CoChangeChip.test.tsx +29 -0
- package/src/components/custom/ActiveWork/CoChangeChip.tsx +48 -0
- package/src/components/custom/ActiveWork/Eyebrow.stories.tsx +25 -0
- package/src/components/custom/ActiveWork/Eyebrow.test.tsx +16 -0
- package/src/components/custom/ActiveWork/Eyebrow.tsx +26 -0
- package/src/components/custom/ActiveWork/FileActivityDetail.stories.tsx +50 -0
- package/src/components/custom/ActiveWork/FileActivityDetail.test.tsx +66 -0
- package/src/components/custom/ActiveWork/FileActivityDetail.tsx +202 -0
- package/src/components/custom/ActiveWork/FileActivityRow.stories.tsx +70 -0
- package/src/components/custom/ActiveWork/FileActivityRow.test.tsx +67 -0
- package/src/components/custom/ActiveWork/FileActivityRow.tsx +130 -0
- package/src/components/custom/ActiveWork/FileHistoryExplorer.stories.tsx +84 -0
- package/src/components/custom/ActiveWork/FileHistoryExplorer.test.tsx +99 -0
- package/src/components/custom/ActiveWork/FileHistoryExplorer.tsx +138 -0
- package/src/components/custom/ActiveWork/FilePathLabel.stories.tsx +39 -0
- package/src/components/custom/ActiveWork/FilePathLabel.test.tsx +53 -0
- package/src/components/custom/ActiveWork/FilePathLabel.tsx +63 -0
- package/src/components/custom/ActiveWork/InitiativeBrief.stories.tsx +50 -0
- package/src/components/custom/ActiveWork/InitiativeBrief.test.tsx +62 -0
- package/src/components/custom/ActiveWork/InitiativeBrief.tsx +182 -0
- package/src/components/custom/ActiveWork/InitiativeCard.stories.tsx +73 -0
- package/src/components/custom/ActiveWork/InitiativeCard.test.tsx +68 -0
- package/src/components/custom/ActiveWork/InitiativeCard.tsx +136 -0
- package/src/components/custom/ActiveWork/InitiativeHeader.stories.tsx +59 -0
- package/src/components/custom/ActiveWork/InitiativeHeader.test.tsx +40 -0
- package/src/components/custom/ActiveWork/InitiativeHeader.tsx +73 -0
- package/src/components/custom/ActiveWork/InitiativeReader.stories.tsx +145 -0
- package/src/components/custom/ActiveWork/OpenLoops.stories.tsx +58 -0
- package/src/components/custom/ActiveWork/OpenLoops.test.tsx +61 -0
- package/src/components/custom/ActiveWork/OpenLoops.tsx +107 -0
- package/src/components/custom/ActiveWork/PortfolioOverview.stories.tsx +101 -0
- package/src/components/custom/ActiveWork/PortfolioOverview.test.tsx +81 -0
- package/src/components/custom/ActiveWork/PortfolioOverview.tsx +83 -0
- package/src/components/custom/ActiveWork/README.md +305 -0
- package/src/components/custom/ActiveWork/SessionDetail.stories.tsx +71 -0
- package/src/components/custom/ActiveWork/SessionDetail.test.tsx +84 -0
- package/src/components/custom/ActiveWork/SessionDetail.tsx +180 -0
- package/src/components/custom/ActiveWork/SessionList.stories.tsx +69 -0
- package/src/components/custom/ActiveWork/SessionList.test.tsx +78 -0
- package/src/components/custom/ActiveWork/SessionList.tsx +103 -0
- package/src/components/custom/ActiveWork/SessionListItem.stories.tsx +64 -0
- package/src/components/custom/ActiveWork/SessionListItem.test.tsx +62 -0
- package/src/components/custom/ActiveWork/SessionListItem.tsx +152 -0
- package/src/components/custom/ActiveWork/SessionReader.stories.tsx +113 -0
- package/src/components/custom/ActiveWork/SeverityLabel.stories.tsx +51 -0
- package/src/components/custom/ActiveWork/SeverityLabel.test.tsx +68 -0
- package/src/components/custom/ActiveWork/SeverityLabel.tsx +97 -0
- package/src/components/custom/ActiveWork/TaskRow.stories.tsx +63 -0
- package/src/components/custom/ActiveWork/TaskRow.test.tsx +112 -0
- package/src/components/custom/ActiveWork/TaskRow.tsx +176 -0
- package/src/components/custom/ActiveWork/TaskTable.stories.tsx +123 -0
- package/src/components/custom/ActiveWork/TaskTable.test.tsx +233 -0
- package/src/components/custom/ActiveWork/TaskTable.tsx +321 -0
- package/src/components/custom/ActiveWork/active-work-depth.test.tsx +102 -0
- package/src/components/custom/ActiveWork/file-history-fixture.ts +91 -0
- package/src/components/custom/ActiveWork/format-time.test.ts +23 -0
- package/src/components/custom/ActiveWork/format-time.ts +32 -0
- package/src/components/custom/ActiveWork/index.ts +19 -0
- package/src/components/custom/ActiveWork/initiative-fixture.ts +104 -0
- package/src/components/custom/ActiveWork/session-fixture.ts +267 -0
- package/src/components/custom/ActiveWork/session-linkers.ts +56 -0
- package/src/components/custom/ActiveWork/task-list-fixture.ts +97 -0
- package/src/components/custom/CircularTimer/CircularTimer.stories.tsx +12 -6
- package/src/components/custom/DateTime/DateTime.stories.tsx +2 -1
- package/src/components/custom/DateTime/DateTime.test.tsx +3 -1
- package/src/components/custom/EmptyState/EmptyState.stories.tsx +22 -9
- package/src/components/custom/EmptyState/EmptyState.test.tsx +7 -13
- package/src/components/custom/EmptyState/EmptyState.tsx +4 -18
- package/src/components/custom/Fatigue/DualGhostSpark.stories.tsx +4 -3
- package/src/components/custom/Fatigue/FatigueLights.stories.tsx +2 -2
- package/src/components/custom/Fatigue/GhostSpark.stories.tsx +4 -3
- package/src/components/custom/Fatigue/LiveFatigueCard.stories.tsx +2 -2
- package/src/components/custom/Fatigue/LiveFatigueCard.tsx +8 -4
- package/src/components/custom/Fatigue/LiveFatiguePanel.stories.tsx +116 -6
- package/src/components/custom/Fatigue/LiveFatiguePanel.test.tsx +134 -0
- package/src/components/custom/Fatigue/LiveFatiguePanel.tsx +54 -83
- package/src/components/custom/Fatigue/README.md +40 -3
- package/src/components/custom/Fatigue/RomProgressionChart.stories.tsx +2 -2
- package/src/components/custom/Fatigue/SilverRedPalette.stories.tsx +2 -1
- package/src/components/custom/Fatigue/VelocityHero.stories.tsx +4 -3
- package/src/components/custom/Fatigue/VerdictHero.stories.tsx +2 -2
- package/src/components/custom/Fatigue/fatigue-mock.test.ts +0 -5
- package/src/components/custom/Fatigue/fatigue-mock.ts +2 -10
- package/src/components/custom/Fatigue/fatigue-tokens.ts +12 -0
- package/src/components/custom/Fatigue/index.ts +17 -5
- package/src/components/custom/Fatigue/panel-layout.test.ts +149 -0
- package/src/components/custom/Fatigue/panel-layout.ts +185 -0
- package/src/components/custom/Gauge/Gauge.stories.tsx +6 -3
- package/src/components/custom/Gauge/Gauge.test.tsx +1 -1
- package/src/components/custom/Gauge/Gauge.tsx +7 -2
- package/src/components/custom/Metric/Metric.stories.tsx +10 -1
- package/src/components/custom/Metric/Metric.test.tsx +1 -3
- package/src/components/custom/Metric/Metric.tsx +5 -20
- package/src/components/custom/Prose/MarkdownProse.stories.tsx +88 -0
- package/src/components/custom/Prose/MarkdownProse.test.tsx +87 -0
- package/src/components/custom/Prose/MarkdownProse.tsx +201 -0
- package/src/components/custom/Prose/README.md +58 -0
- package/src/components/custom/Prose/index.ts +1 -0
- package/src/components/custom/Scatter/Scatter.stories.tsx +55 -14
- package/src/components/custom/Scatter/Scatter.test.tsx +2 -2
- package/src/components/custom/Scatter/Scatter.tsx +122 -18
- package/src/components/custom/Sidebar/Sidebar.stories.tsx +41 -8
- package/src/components/custom/Sidebar/Sidebar.test.tsx +5 -1
- package/src/components/custom/Sidebar/Sidebar.tsx +16 -33
- package/src/components/custom/Table/Table.stories.tsx +99 -16
- package/src/components/custom/Table/Table.test.tsx +273 -86
- package/src/components/custom/Table/Table.tsx +159 -67
- package/src/components/custom/Table/column-fit.test.ts +75 -0
- package/src/components/custom/Table/column-fit.ts +116 -0
- package/src/components/custom/Table/index.ts +9 -0
- package/src/components/custom/TimerReadout/TimerReadout.stories.tsx +8 -6
- package/src/components/custom/Treemap/Treemap.stories.tsx +15 -12
- package/src/components/custom/Treemap/Treemap.test.tsx +1 -1
- package/src/components/custom/Treemap/Treemap.tsx +11 -3
- package/src/components/custom/Typography/Typography.stories.tsx +28 -1
- package/src/components/custom/Typography/Typography.test.tsx +34 -0
- package/src/components/custom/Typography/Typography.tsx +6 -0
- package/src/components/custom/Typography/index.ts +1 -8
- package/src/components/custom/Workout/ActiveWorkoutPage.stories.tsx +11 -2
- package/src/components/custom/Workout/BaseBadge.stories.tsx +27 -11
- package/src/components/custom/Workout/BaseBadge.test.tsx +31 -11
- package/src/components/custom/Workout/BaseBadge.tsx +6 -2
- package/src/components/custom/Workout/BodyMap.stories.tsx +4 -1
- package/src/components/custom/Workout/BodyMap.tsx +8 -4
- package/src/components/custom/Workout/BodyMapDetailPanel.stories.tsx +28 -6
- package/src/components/custom/Workout/BodyMapDetailPanel.test.tsx +11 -15
- package/src/components/custom/Workout/BodyMapDetailPanel.tsx +50 -12
- package/src/components/custom/Workout/CapacityBandChart.stories.tsx +12 -9
- package/src/components/custom/Workout/CapacityBandChart.test.tsx +24 -51
- package/src/components/custom/Workout/CapacityBandChart.tsx +144 -111
- package/src/components/custom/Workout/DeviationBar.stories.tsx +1 -1
- package/src/components/custom/Workout/DeviationBar.test.tsx +10 -4
- package/src/components/custom/Workout/DeviationBar.tsx +24 -16
- package/src/components/custom/Workout/DualVelocityStrip.stories.tsx +183 -0
- package/src/components/custom/Workout/DualVelocityStrip.test.tsx +32 -18
- package/src/components/custom/Workout/ExerciseCard.stories.tsx +12 -11
- package/src/components/custom/Workout/ExerciseCard.test.tsx +52 -22
- package/src/components/custom/Workout/ExerciseCard.tsx +99 -183
- package/src/components/custom/Workout/ExerciseCardHeading.stories.tsx +95 -12
- 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.stories.tsx +9 -1
- package/src/components/custom/Workout/ExerciseDetailPage.tsx +3 -2
- package/src/components/custom/Workout/ExerciseHeading.stories.tsx +8 -8
- package/src/components/custom/Workout/ExerciseHeading.tsx +194 -46
- package/src/components/custom/Workout/ExerciseIndicator.stories.tsx +12 -9
- package/src/components/custom/Workout/ExerciseIndicator.test.tsx +27 -11
- package/src/components/custom/Workout/ExerciseIndicator.tsx +14 -15
- package/src/components/custom/Workout/FatigueMeter.stories.tsx +5 -5
- package/src/components/custom/Workout/InputBar.stories.tsx +4 -5
- package/src/components/custom/Workout/InputBar.tsx +4 -2
- package/src/components/custom/Workout/IntensityBar.stories.tsx +1 -1
- package/src/components/custom/Workout/IntensityBar.test.tsx +5 -1
- package/src/components/custom/Workout/IntensityBar.tsx +8 -3
- package/src/components/custom/Workout/LiveAuraFrame.stories.tsx +5 -4
- package/src/components/custom/Workout/MesoCard.stories.tsx +4 -1
- package/src/components/custom/Workout/MesoCard.test.tsx +7 -18
- package/src/components/custom/Workout/MesoCard.tsx +14 -10
- package/src/components/custom/Workout/MesoProgressBar.stories.tsx +1 -1
- package/src/components/custom/Workout/MesoProgressBar.test.tsx +18 -11
- package/src/components/custom/Workout/MesoProgressBar.tsx +15 -13
- package/src/components/custom/Workout/MesoStatusCard.stories.tsx +2 -1
- package/src/components/custom/Workout/MesoStatusCard.test.tsx +14 -44
- package/src/components/custom/Workout/MesoStatusCard.tsx +20 -33
- package/src/components/custom/Workout/MetricTiles.stories.tsx +3 -2
- package/src/components/custom/Workout/MuscleGroupChip.stories.tsx +2 -8
- package/src/components/custom/Workout/MuscleGroupChip.test.tsx +23 -31
- package/src/components/custom/Workout/MuscleGroupChip.tsx +28 -67
- package/src/components/custom/Workout/PlaceholderStrip.stories.tsx +9 -4
- package/src/components/custom/Workout/PlaceholderStrip.test.tsx +17 -10
- package/src/components/custom/Workout/PlaceholderStrip.tsx +9 -8
- package/src/components/custom/Workout/PrBadge.stories.tsx +2 -1
- package/src/components/custom/Workout/PrBadge.test.tsx +3 -9
- package/src/components/custom/Workout/PrBadge.tsx +13 -17
- package/src/components/custom/Workout/PrHistoryModal.stories.tsx +16 -3
- package/src/components/custom/Workout/PrHistoryModal.test.tsx +13 -36
- package/src/components/custom/Workout/PrHistoryModal.tsx +8 -4
- package/src/components/custom/Workout/ProgramPlanningPage.stories.tsx +6 -1
- package/src/components/custom/Workout/README.md +68 -17
- package/src/components/custom/Workout/ReadinessCheck.stories.tsx +3 -2
- package/src/components/custom/Workout/ReadinessCheck.test.tsx +12 -18
- package/src/components/custom/Workout/ReadinessCheck.tsx +22 -4
- package/src/components/custom/Workout/RestTimer.stories.tsx +16 -4
- package/src/components/custom/Workout/RestTimer.tsx +4 -2
- package/src/components/custom/Workout/ScheduleTiles.stories.tsx +3 -2
- package/src/components/custom/Workout/ScheduleTiles.test.tsx +3 -4
- package/src/components/custom/Workout/ScheduleTiles.tsx +4 -7
- package/src/components/custom/Workout/SegmentedBar.stories.tsx +21 -17
- package/src/components/custom/Workout/SegmentedBar.tsx +10 -5
- package/src/components/custom/Workout/SegmentedProgressBar.stories.tsx +5 -5
- package/src/components/custom/Workout/SessionHeader.stories.tsx +6 -6
- package/src/components/custom/Workout/SessionHeader.tsx +12 -12
- package/src/components/custom/Workout/SessionRail.stories.tsx +12 -11
- package/src/components/custom/Workout/SessionRail.tsx +7 -9
- package/src/components/custom/Workout/SetBar.stories.tsx +10 -6
- package/src/components/custom/Workout/SetBar.test.tsx +49 -4
- package/src/components/custom/Workout/SetBar.tsx +38 -12
- package/src/components/custom/Workout/SetRow.stories.tsx +3 -2
- package/src/components/custom/Workout/SetRow.test.tsx +16 -2
- package/src/components/custom/Workout/SetRow.tsx +61 -23
- package/src/components/custom/Workout/SetStrip.stories.tsx +63 -7
- package/src/components/custom/Workout/SetStrip.test.tsx +25 -0
- package/src/components/custom/Workout/SetStrip.tsx +24 -4
- package/src/components/custom/Workout/SetTableHeader.stories.tsx +6 -6
- package/src/components/custom/Workout/SetTableHeader.tsx +9 -12
- package/src/components/custom/Workout/SetsRepsLoad.stories.tsx +7 -7
- package/src/components/custom/Workout/SetsRepsLoad.tsx +24 -12
- package/src/components/custom/Workout/Sparkline.stories.tsx +14 -11
- package/src/components/custom/Workout/Sparkline.test.tsx +37 -19
- package/src/components/custom/Workout/Sparkline.tsx +14 -18
- package/src/components/custom/Workout/StatusDot.stories.tsx +1 -1
- package/src/components/custom/Workout/StatusDot.test.tsx +44 -5
- package/src/components/custom/Workout/StatusDot.tsx +93 -63
- package/src/components/custom/Workout/StatusPill.stories.tsx +1 -1
- package/src/components/custom/Workout/StatusPill.test.tsx +6 -15
- package/src/components/custom/Workout/StatusPill.tsx +27 -38
- package/src/components/custom/Workout/StrengthTrendChart.stories.tsx +11 -4
- package/src/components/custom/Workout/StrengthTrendChart.test.tsx +15 -37
- package/src/components/custom/Workout/StrengthTrendChart.tsx +39 -36
- package/src/components/custom/Workout/SupersetWrapper.stories.tsx +27 -64
- package/src/components/custom/Workout/SupersetWrapper.test.tsx +16 -14
- package/src/components/custom/Workout/SupersetWrapper.tsx +17 -21
- package/src/components/custom/Workout/TempoDisplay.stories.tsx +26 -10
- package/src/components/custom/Workout/TempoDisplay.test.tsx +18 -4
- package/src/components/custom/Workout/TempoDisplay.tsx +2 -0
- package/src/components/custom/Workout/TrainingStatusPage.stories.tsx +9 -1
- package/src/components/custom/Workout/VelocityStrip.compact.stories.tsx +4 -1
- package/src/components/custom/Workout/VelocityStrip.dual.stories.tsx +10 -6
- package/src/components/custom/Workout/VelocityStrip.expanded.stories.tsx +5 -2
- package/src/components/custom/Workout/VelocityStrip.hero.stories.tsx +44 -1
- package/src/components/custom/Workout/VelocityStrip.stories.tsx +12 -6
- package/src/components/custom/Workout/VelocityStrip.test.tsx +48 -6
- package/src/components/custom/Workout/VelocityStrip.tsx +26 -14
- package/src/components/custom/Workout/VolumeLandmarkBar.stories.tsx +6 -7
- package/src/components/custom/Workout/VolumeLandmarkBar.tsx +15 -6
- package/src/components/custom/Workout/WeekRow.stories.tsx +4 -1
- package/src/components/custom/Workout/WeekRow.test.tsx +18 -15
- package/src/components/custom/Workout/WeekRow.tsx +23 -16
- package/src/components/custom/Workout/WeightBadge.stories.tsx +2 -1
- package/src/components/custom/Workout/WeightBadge.test.tsx +50 -19
- package/src/components/custom/Workout/WeightBadge.tsx +28 -27
- package/src/components/custom/Workout/WorkoutCard.stories.tsx +6 -1
- package/src/components/custom/Workout/WorkoutCard.test.tsx +18 -0
- package/src/components/custom/Workout/WorkoutCard.tsx +35 -57
- package/src/components/custom/Workout/WorkoutPill.stories.tsx +5 -2
- package/src/components/custom/Workout/WorkoutPill.test.tsx +39 -7
- package/src/components/custom/Workout/WorkoutPill.tsx +88 -57
- package/src/components/custom/Workout/ZoneTrack.stories.tsx +10 -7
- package/src/components/custom/Workout/ZoneTrack.tsx +10 -9
- package/src/components/custom/Workout/exerciseRowState.ts +29 -0
- package/src/components/custom/Workout/index.ts +17 -2
- package/src/components/custom/Workout/metricText.stories.tsx +15 -13
- package/src/components/custom/Workout/metricText.tsx +8 -2
- package/src/components/custom/Workout/setHeadingKit.tsx +219 -36
- package/src/components/custom/Workout/velocity-story-kit.tsx +2 -2
- package/src/components/custom/charts/README.md +66 -0
- package/src/components/custom/charts/SetBarChart.test.tsx +46 -1
- package/src/components/custom/charts/SetBarChart.tsx +56 -9
- package/src/components/custom/charts/SparkBars.stories.tsx +55 -0
- package/src/components/custom/charts/SparkBars.test.tsx +69 -0
- package/src/components/custom/charts/SparkBars.tsx +85 -0
- package/src/components/custom/charts/flatBarGeometry.test.ts +41 -0
- package/src/components/custom/charts/flatBarGeometry.ts +32 -0
- package/src/components/custom/charts/index.ts +3 -0
- package/src/components/custom/index.ts +3 -0
- package/src/components/custom/stepper/Stepper.stories.tsx +13 -10
- package/src/components/custom/stepper/Stepper.test.tsx +1 -7
- package/src/components/custom/stepper/Stepper.tsx +21 -34
- package/src/components/icons/icons.stories.tsx +1 -1
- package/src/components/icons/icons.tsx +51 -0
- package/src/components/shell/AppShell.stories.tsx +150 -0
- package/src/components/shell/AppShell.test.tsx +70 -0
- package/src/components/shell/AppShell.tsx +90 -0
- package/src/components/shell/BrandLockup.stories.tsx +66 -5
- package/src/components/shell/BrandLockup.test.tsx +34 -1
- package/src/components/shell/BrandLockup.tsx +27 -11
- package/src/components/shell/NavItem.stories.tsx +4 -4
- package/src/components/shell/NavItem.test.tsx +17 -0
- package/src/components/shell/NavItem.tsx +17 -5
- package/src/components/shell/README.md +89 -26
- package/src/components/shell/SideNav.stories.tsx +38 -7
- package/src/components/shell/SideNav.test.tsx +16 -8
- package/src/components/shell/SideNav.tsx +17 -17
- package/src/components/shell/TopBar.stories.tsx +44 -19
- package/src/components/shell/TopBar.test.tsx +34 -12
- package/src/components/shell/TopBar.tsx +61 -40
- package/src/components/shell/brands.tsx +66 -0
- package/src/components/shell/index.ts +9 -8
- package/src/components/shell/{DeviceIndicator.stories.tsx → workout/DeviceIndicator.stories.tsx} +2 -2
- package/src/components/shell/{DeviceIndicator.tsx → workout/DeviceIndicator.tsx} +2 -2
- package/src/components/shell/{DeviceMenu.stories.tsx → workout/DeviceMenu.stories.tsx} +2 -2
- package/src/components/shell/{DeviceMenu.tsx → workout/DeviceMenu.tsx} +3 -3
- package/src/components/shell/{DeviceRow.stories.tsx → workout/DeviceRow.stories.tsx} +2 -2
- package/src/components/shell/{DeviceRow.tsx → workout/DeviceRow.tsx} +3 -3
- package/src/components/shell/{SessionStatePill.stories.tsx → workout/SessionStatePill.stories.tsx} +2 -2
- package/src/components/shell/{SessionStatePill.tsx → workout/SessionStatePill.tsx} +5 -3
- package/src/components/shell/workout/WorkoutShell.stories.tsx +71 -0
- package/src/components/shell/{DashboardShell.test.tsx → workout/WorkoutShell.test.tsx} +19 -8
- package/src/components/shell/workout/WorkoutShell.tsx +77 -0
- package/src/components/shell/workout/WorkoutTopBar.stories.tsx +58 -0
- package/src/components/shell/workout/WorkoutTopBar.test.tsx +39 -0
- package/src/components/shell/workout/WorkoutTopBar.tsx +52 -0
- package/src/components/shell/workout/index.ts +9 -0
- package/src/components/shell/workout/workoutNavItems.tsx +16 -0
- package/src/components/ui/README.md +110 -0
- package/src/components/ui/alert/Alert.stories.tsx +15 -5
- package/src/components/ui/alert/Alert.tsx +24 -3
- package/src/components/ui/autocomplete/Autocomplete.stories.tsx +11 -13
- package/src/components/ui/autocomplete/Autocomplete.test.tsx +7 -25
- package/src/components/ui/autocomplete/Autocomplete.tsx +21 -28
- package/src/components/ui/avatar/Avatar.stories.tsx +1 -1
- package/src/components/ui/avatar/Avatar.tsx +2 -10
- package/src/components/ui/badge/Badge.stories.tsx +83 -28
- package/src/components/ui/badge/Badge.test.tsx +19 -3
- package/src/components/ui/badge/Badge.tsx +48 -55
- package/src/components/ui/breadcrumbs/Breadcrumbs.stories.tsx +1 -1
- package/src/components/ui/breadcrumbs/Breadcrumbs.tsx +4 -16
- package/src/components/ui/button/Button.stories.tsx +46 -12
- package/src/components/ui/button/Button.tsx +44 -37
- package/src/components/ui/card/Card.stories.tsx +90 -94
- package/src/components/ui/card/Card.test.tsx +80 -6
- package/src/components/ui/card/Card.tsx +150 -203
- package/src/components/ui/checkbox/Checkbox.stories.tsx +1 -1
- package/src/components/ui/checkbox/Checkbox.tsx +7 -27
- package/src/components/ui/chip/Chip.stories.tsx +37 -13
- package/src/components/ui/chip/Chip.test.tsx +10 -6
- package/src/components/ui/chip/Chip.tsx +76 -91
- package/src/components/ui/collapse/Collapse.stories.tsx +23 -23
- package/src/components/ui/collapse/Collapse.tsx +6 -11
- package/src/components/ui/data-row/DataRow.stories.tsx +14 -3
- package/src/components/ui/data-row/DataRow.test.tsx +5 -15
- package/src/components/ui/data-row/DataRow.tsx +18 -7
- package/src/components/ui/divider/Divider.stories.tsx +1 -1
- package/src/components/ui/divider/Divider.tsx +1 -5
- package/src/components/ui/drawer/Drawer.stories.tsx +5 -16
- package/src/components/ui/drawer/Drawer.tsx +13 -27
- package/src/components/ui/floating-overlay-depth.test.tsx +143 -0
- package/src/components/ui/form-field/FormField.stories.tsx +6 -20
- package/src/components/ui/form-field/FormField.test.tsx +1 -5
- package/src/components/ui/form-field/FormField.tsx +10 -46
- package/src/components/ui/help-tip/HelpTip.stories.tsx +8 -19
- package/src/components/ui/help-tip/HelpTip.test.tsx +1 -3
- package/src/components/ui/help-tip/HelpTip.tsx +21 -22
- package/src/components/ui/help-tip/index.ts +7 -1
- package/src/components/ui/icon-box/IconBox.stories.tsx +1 -1
- package/src/components/ui/icon-box/IconBox.test.tsx +1 -7
- package/src/components/ui/icon-box/IconBox.tsx +10 -18
- package/src/components/ui/indicator/Indicator.stories.tsx +5 -4
- package/src/components/ui/indicator/Indicator.tsx +20 -14
- package/src/components/ui/input/Input.stories.tsx +3 -11
- package/src/components/ui/input/Input.test.tsx +2 -10
- package/src/components/ui/input/Input.tsx +10 -25
- package/src/components/ui/input/index.ts +7 -1
- package/src/components/ui/link/Link.stories.tsx +19 -7
- package/src/components/ui/list-item/ListItem.stories.tsx +17 -5
- package/src/components/ui/list-item/ListItem.tsx +2 -9
- package/src/components/ui/menu/Menu.stories.tsx +21 -15
- package/src/components/ui/menu/Menu.test.tsx +2 -11
- package/src/components/ui/menu/Menu.tsx +12 -14
- package/src/components/ui/menu/index.ts +8 -1
- package/src/components/ui/modal/Modal.stories.tsx +2 -2
- package/src/components/ui/modal/Modal.tsx +31 -19
- package/src/components/ui/pill/Pill.stories.tsx +111 -23
- package/src/components/ui/pill/Pill.test.tsx +101 -3
- package/src/components/ui/pill/Pill.tsx +168 -49
- package/src/components/ui/pill/index.ts +1 -1
- package/src/components/ui/popover/Popover.stories.tsx +6 -8
- package/src/components/ui/popover/Popover.test.tsx +1 -6
- package/src/components/ui/popover/Popover.tsx +20 -15
- package/src/components/ui/popover/index.ts +7 -1
- package/src/components/ui/progress/Progress.stories.tsx +3 -12
- package/src/components/ui/progress/Progress.test.tsx +1 -5
- package/src/components/ui/progress/Progress.tsx +9 -6
- package/src/components/ui/radio/Radio.stories.tsx +42 -21
- package/src/components/ui/radio/Radio.test.tsx +6 -2
- package/src/components/ui/radio/Radio.tsx +23 -22
- package/src/components/ui/section/Section.stories.tsx +12 -21
- package/src/components/ui/section/Section.test.tsx +1 -6
- package/src/components/ui/section/Section.tsx +1 -3
- package/src/components/ui/section/index.ts +1 -5
- package/src/components/ui/select/Select.stories.tsx +3 -13
- package/src/components/ui/select/Select.test.tsx +2 -11
- package/src/components/ui/select/Select.tsx +16 -15
- package/src/components/ui/skeleton/Skeleton.stories.tsx +3 -11
- package/src/components/ui/skeleton/Skeleton.tsx +3 -14
- package/src/components/ui/skeleton/index.ts +1 -7
- package/src/components/ui/spinner/Spinner.stories.tsx +1 -1
- package/src/components/ui/spinner/Spinner.tsx +4 -5
- package/src/components/ui/stack/Stack.stories.tsx +8 -8
- package/src/components/ui/stack/Stack.tsx +1 -1
- package/src/components/ui/surface/Surface.stories.tsx +92 -33
- package/src/components/ui/surface/Surface.test.tsx +88 -28
- package/src/components/ui/surface/Surface.tsx +37 -87
- package/src/components/ui/surface/SurfaceContext.ts +22 -62
- package/src/components/ui/surface/index.ts +8 -0
- package/src/components/ui/surface/resolveSurface.ts +98 -0
- package/src/components/ui/surface/surface.contract.test.ts +6 -1
- package/src/components/ui/switch/Switch.stories.tsx +1 -1
- package/src/components/ui/switch/Switch.tsx +7 -6
- package/src/components/ui/tabs/Tabs.stories.tsx +1 -1
- package/src/components/ui/tabs/Tabs.tsx +8 -21
- package/src/components/ui/tabs/index.ts +1 -7
- package/src/components/ui/tile/Tile.stories.tsx +1 -1
- package/src/components/ui/tile/Tile.tsx +3 -0
- package/src/components/ui/toast/Toast.stories.tsx +1 -1
- package/src/components/ui/toast/Toast.test.tsx +2 -8
- package/src/components/ui/toast/Toast.tsx +49 -57
- package/src/components/ui/toast/index.ts +0 -1
- package/src/components/ui/toolbar-button/ToolbarButton.stories.tsx +92 -71
- package/src/components/ui/toolbar-button/ToolbarButton.test.tsx +3 -18
- package/src/components/ui/toolbar-button/ToolbarButton.tsx +38 -49
- package/src/components/ui/tooltip/Tooltip.stories.tsx +12 -3
- package/src/components/ui/tooltip/Tooltip.test.tsx +21 -3
- package/src/components/ui/tooltip/Tooltip.tsx +55 -36
- package/src/components/ui/tooltip/index.ts +1 -1
- package/src/components/ui/trigger/TriggerSurface.test.tsx +215 -0
- package/src/components/ui/trigger/TriggerSurface.tsx +81 -0
- package/src/components/ui/trigger/index.ts +2 -0
- package/src/stories/PresetShowcase.stories.tsx +101 -48
- package/src/test/no-device-internals.test.ts +3 -3
- package/src/test/no-raw-color.test.ts +39 -0
- package/src/theme/Alpha.stories.tsx +103 -0
- package/src/theme/ChoosingTokens.stories.tsx +48 -0
- package/src/theme/ColorPalettes.stories.tsx +62 -45
- package/src/theme/ColorPrimitives.stories.tsx +35 -35
- package/src/theme/Depth.stories.tsx +132 -65
- package/src/theme/DepthCalibration.stories.tsx +23 -21
- package/src/theme/ProposedTokensVW82.stories.tsx +414 -0
- package/src/theme/ThemeProvider.test.tsx +4 -4
- package/src/theme/barrel.ts +7 -6
- package/src/theme/color-stories.coverage.test.ts +1 -1
- package/src/theme/color-utils.ts +41 -29
- package/src/theme/config.completeness.test.ts +1 -1
- package/src/theme/config.ts +40 -0
- package/src/theme/elevation.test.ts +82 -56
- package/src/theme/elevation.ts +77 -561
- package/src/theme/global.css +45 -5
- package/src/theme/grey-ramp.test.ts +24 -8
- package/src/theme/lift.test.ts +39 -0
- package/src/theme/lift.ts +102 -0
- package/src/theme/materials.test.ts +5 -2
- package/src/theme/materials.ts +38 -4
- package/src/theme/native-theming-guard.test.ts +5 -5
- package/src/theme/presets/apply.ts +4 -0
- package/src/theme/presets/audiobook.ts +5 -2
- package/src/theme/presets/default.ts +4 -0
- package/src/theme/presets/index.ts +14 -1
- package/src/theme/presets/types.ts +1 -1
- package/src/theme/semantic-contrast.test.ts +13 -4
- package/src/theme/status-distinctness.test.ts +66 -0
- package/src/theme/subtle-tone-weight.test.ts +163 -0
- package/src/theme/surface-planes.test.ts +37 -0
- package/src/theme/surface-planes.ts +68 -0
- package/src/theme/tailwind-var-opacity.test.ts +163 -0
- package/src/theme/token-export-guard.test.ts +1 -3
- package/src/theme/tokens/primitives.test.ts +15 -7
- package/src/theme/tokens/primitives.ts +98 -33
- package/src/theme/tokens/semantic-hex-guard.test.ts +322 -0
- package/src/theme/tokens/semantic.ts +124 -54
- package/src/theme/wash-ladder.test.ts +58 -0
- package/src/theme/workout-tokens.ts +1 -7
- package/src/types/jest-axe.d.ts +2 -6
- package/src/types/raw-md.d.ts +5 -0
- package/src/utils/colors.alpha-adoption.test.ts +408 -0
- package/src/utils/colors.ts +4 -4
- package/src/utils/form.ts +1 -3
- package/src/utils/index.ts +2 -7
- package/src/utils/number-format.ts +17 -0
- package/src/utils/workout-format.ts +15 -6
- package/src/web-jsx/jsx-runtime.ts +1 -5
- package/tailwind.config.js +55 -9
- package/src/components/custom/Workout/ExerciseCardUnified.stories.tsx +0 -151
- package/src/components/custom/Workout/RowInventory.stories.tsx +0 -408
- package/src/components/custom/Workout/S3FullRail.stories.tsx +0 -46
- package/src/components/custom/Workout/S3SessionPace.stories.tsx +0 -95
- package/src/components/custom/Workout/S3SessionRailHeading.stories.tsx +0 -61
- package/src/components/custom/Workout/S3SetTypes.stories.tsx +0 -176
- package/src/components/custom/Workout/S3WorkoutExpansion.stories.tsx +0 -240
- package/src/components/shell/DashboardShell.stories.tsx +0 -65
- package/src/components/shell/DashboardShell.tsx +0 -75
- package/src/components/shell/SessionRailSpecimen.stories.tsx +0 -753
- package/src/theme/elevation.stories.tsx +0 -346
- package/src/utils/useTheme.ts +0 -72
- /package/src/components/shell/{DeviceIndicator.test.tsx → workout/DeviceIndicator.test.tsx} +0 -0
- /package/src/components/shell/{DeviceMenu.test.tsx → workout/DeviceMenu.test.tsx} +0 -0
- /package/src/components/shell/{DeviceRow.test.tsx → workout/DeviceRow.test.tsx} +0 -0
- /package/src/components/shell/{SessionStatePill.test.tsx → workout/SessionStatePill.test.tsx} +0 -0
|
@@ -1,74 +1,164 @@
|
|
|
1
1
|
// Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
|
|
2
|
-
import {
|
|
2
|
+
import type { ReactNode } from 'react'
|
|
3
|
+
import { View, Pressable } from 'react-native'
|
|
4
|
+
import { Typography } from '../Typography'
|
|
5
|
+
import { cn } from '../../../utils/cn'
|
|
3
6
|
import { roundWeight } from '../../../utils/workout-format'
|
|
7
|
+
import { useSurfaceMode } from '../../ui/surface/SurfaceContext'
|
|
4
8
|
import { SetsRepsLoad } from './SetsRepsLoad'
|
|
5
9
|
import { TempoDisplay } from './TempoDisplay'
|
|
6
10
|
import { ExerciseIndicator, type ExerciseIndicatorKind } from './ExerciseIndicator'
|
|
11
|
+
import { exerciseLiveColor } from './exerciseRowState'
|
|
7
12
|
|
|
8
|
-
|
|
13
|
+
/** Dim depth for a not-yet-reached exercise, when the block dims itself. */
|
|
14
|
+
const DIMMED_OPACITY = 0.55
|
|
15
|
+
|
|
16
|
+
/** `stacked` puts the prescription on its own line under the name; `inline` keeps one row. */
|
|
17
|
+
export type ExerciseHeadingLayout = 'stacked' | 'inline'
|
|
18
|
+
|
|
19
|
+
interface ExerciseHeadingBaseProps {
|
|
9
20
|
name: string
|
|
10
|
-
/** Prescription line values (sets × reps @ load). */
|
|
11
|
-
sets: number
|
|
12
|
-
reps: number | string
|
|
13
|
-
/** Load value; a string (e.g. "—") passes through verbatim for an unset/discovery load. */
|
|
14
|
-
load: number | string
|
|
15
21
|
unit?: 'lbs' | 'kg'
|
|
16
|
-
/** Tempo tuple [eccentric, pauseBottom, concentric, pauseTop]
|
|
22
|
+
/** Tempo tuple [eccentric, pauseBottom, concentric, pauseTop]. `stacked` layout only. */
|
|
17
23
|
tempo?: [number, number, number, number]
|
|
18
24
|
/** Small PR / issue / info chip in the title row. */
|
|
19
25
|
indicator?: ExerciseIndicatorKind
|
|
20
26
|
/** Dim the block as a not-yet-reached exercise (standalone use). */
|
|
21
27
|
dimmed?: boolean
|
|
28
|
+
/** Row shape. Default `stacked`. */
|
|
29
|
+
layout?: ExerciseHeadingLayout
|
|
30
|
+
/**
|
|
31
|
+
* Render the structured `sets × reps @ load` prescription in one dimmer, regular
|
|
32
|
+
* tone instead of the rail's bright/bold value cells. `stacked` (rail) never sets
|
|
33
|
+
* this; `inline` (compact, upcoming) always does.
|
|
34
|
+
*/
|
|
35
|
+
mutedPrescription?: boolean
|
|
36
|
+
/** Trailing right-edge caption (a previous best). `inline` layout only. */
|
|
37
|
+
previousBest?: string
|
|
38
|
+
/** This exercise is being performed right now — the name takes the live tone, never animated. */
|
|
39
|
+
isLive?: boolean
|
|
40
|
+
/**
|
|
41
|
+
* Who owns the press. `self` (default) makes the name row the button. `ancestor`
|
|
42
|
+
* renders it as a plain row, for a wrapper that presses as a whole — legal only in
|
|
43
|
+
* `inline` layout, since `stacked`'s TempoDisplay sibling is itself a Pressable.
|
|
44
|
+
*/
|
|
45
|
+
pressTarget?: 'self' | 'ancestor'
|
|
22
46
|
/** Press target for the name row (expand / select). */
|
|
23
47
|
onPress?: () => void
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
reps: number | string,
|
|
30
|
-
load: number | string,
|
|
31
|
-
unit: string
|
|
32
|
-
) {
|
|
33
|
-
const loadLabel = typeof load === 'number' ? roundWeight(load) : load
|
|
34
|
-
return `${name}, ${sets}×${reps} @ ${loadLabel} ${unit}`
|
|
48
|
+
onPressIn?: () => void
|
|
49
|
+
onPressOut?: () => void
|
|
50
|
+
/** Web-only; on a touch surface these never fire and the row has no hover state. */
|
|
51
|
+
onHoverIn?: () => void
|
|
52
|
+
onHoverOut?: () => void
|
|
35
53
|
}
|
|
36
54
|
|
|
37
55
|
/**
|
|
38
|
-
* The
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* target — TempoDisplay is a sibling, never a descendant, since it is itself a
|
|
42
|
-
* Pressable (nesting would be an a11y nested-interactive violation).
|
|
56
|
+
* The prescription is EITHER the structured `sets × reps @ load` triple, OR a
|
|
57
|
+
* free-text line for an exercise whose numbers aren't loaded yet, OR absent.
|
|
58
|
+
* Mixing the two is a type error rather than a silently half-rendered line.
|
|
43
59
|
*/
|
|
44
|
-
|
|
60
|
+
type ExerciseHeadingPrescription =
|
|
61
|
+
| { sets: number; reps: number | string; load: number | string; prescription?: never }
|
|
62
|
+
| { prescription: string; sets?: never; reps?: never; load?: never }
|
|
63
|
+
| { sets?: never; reps?: never; load?: never; prescription?: never }
|
|
64
|
+
|
|
65
|
+
export type ExerciseHeadingProps = ExerciseHeadingBaseProps & ExerciseHeadingPrescription
|
|
66
|
+
|
|
67
|
+
function prescriptionText(p: ExerciseHeadingProps, unit: string): string | undefined {
|
|
68
|
+
if (p.prescription !== undefined) return p.prescription
|
|
69
|
+
if (p.sets === undefined) return undefined
|
|
70
|
+
const loadLabel = typeof p.load === 'number' ? roundWeight(p.load) : p.load
|
|
71
|
+
return `${p.sets}×${p.reps} @ ${loadLabel} ${unit}`
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** The row's accessible name. Exported so a wrapper that owns the press can reuse it. */
|
|
75
|
+
export function exerciseHeadingLabel(p: ExerciseHeadingProps, unit: string = p.unit ?? 'lbs') {
|
|
76
|
+
const prescription = prescriptionText(p, unit)
|
|
77
|
+
return prescription ? `${p.name}, ${prescription}` : p.name
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function pressProps(p: ExerciseHeadingProps, unit: string) {
|
|
81
|
+
return {
|
|
82
|
+
onPress: p.onPress,
|
|
83
|
+
onPressIn: p.onPressIn,
|
|
84
|
+
onPressOut: p.onPressOut,
|
|
85
|
+
onHoverIn: p.onHoverIn,
|
|
86
|
+
onHoverOut: p.onHoverOut,
|
|
87
|
+
accessibilityRole: 'button' as const,
|
|
88
|
+
accessibilityLabel: exerciseHeadingLabel(p, unit),
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
interface NameStyle {
|
|
93
|
+
color: string
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function HeadingName({
|
|
45
97
|
name,
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
98
|
+
nameStyle,
|
|
99
|
+
className,
|
|
100
|
+
}: {
|
|
101
|
+
name: string
|
|
102
|
+
nameStyle?: NameStyle
|
|
103
|
+
className?: string
|
|
104
|
+
}) {
|
|
105
|
+
return (
|
|
106
|
+
// The row is a button label, not a heading, so `boldLabel` carries the weight and
|
|
107
|
+
// no header role has to be undone. Size and the Space Grotesk face are pinned.
|
|
108
|
+
<Typography
|
|
109
|
+
variant="boldLabel"
|
|
110
|
+
color={nameStyle ? 'inherit' : 'primary'}
|
|
111
|
+
style={nameStyle}
|
|
112
|
+
className={cn('font-heading text-sm leading-[normal]', className)}
|
|
113
|
+
testID="exercise-card-name"
|
|
114
|
+
>
|
|
115
|
+
{name}
|
|
116
|
+
</Typography>
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function prescriptionNode(p: ExerciseHeadingProps, unit: string): ReactNode {
|
|
121
|
+
if (p.prescription !== undefined) {
|
|
122
|
+
return (
|
|
123
|
+
<Typography
|
|
124
|
+
variant="caption"
|
|
125
|
+
color="secondary"
|
|
126
|
+
maxLines={1}
|
|
127
|
+
className="font-sans shrink leading-[normal]"
|
|
128
|
+
testID="exercise-card-prescription"
|
|
129
|
+
>
|
|
130
|
+
{p.prescription}
|
|
131
|
+
</Typography>
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
if (p.sets === undefined) return null
|
|
55
135
|
return (
|
|
56
|
-
<
|
|
136
|
+
<SetsRepsLoad
|
|
137
|
+
sets={p.sets}
|
|
138
|
+
reps={p.reps}
|
|
139
|
+
load={p.load}
|
|
140
|
+
unit={unit}
|
|
141
|
+
muted={p.mutedPrescription}
|
|
142
|
+
/>
|
|
143
|
+
)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
interface LayoutProps {
|
|
147
|
+
heading: ExerciseHeadingProps
|
|
148
|
+
nameStyle?: NameStyle
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function StackedHeading({ heading, nameStyle }: LayoutProps) {
|
|
152
|
+
const { unit = 'lbs', tempo, indicator } = heading
|
|
153
|
+
return (
|
|
154
|
+
<>
|
|
57
155
|
<Pressable
|
|
58
|
-
|
|
59
|
-
accessibilityRole="button"
|
|
60
|
-
accessibilityLabel={headingLabel(name, sets, reps, load, unit)}
|
|
156
|
+
{...pressProps(heading, unit)}
|
|
61
157
|
className="flex-row items-center"
|
|
62
158
|
style={{ gap: 8 }}
|
|
63
159
|
testID="exercise-card-header"
|
|
64
160
|
>
|
|
65
|
-
<
|
|
66
|
-
className="text-text-primary"
|
|
67
|
-
style={{ fontSize: 14, fontFamily: '"Space Grotesk", sans-serif', fontWeight: '700' }}
|
|
68
|
-
testID="exercise-card-name"
|
|
69
|
-
>
|
|
70
|
-
{name}
|
|
71
|
-
</Text>
|
|
161
|
+
<HeadingName name={heading.name} nameStyle={nameStyle} />
|
|
72
162
|
<View className="flex-1" />
|
|
73
163
|
{indicator && <ExerciseIndicator kind={indicator} />}
|
|
74
164
|
</Pressable>
|
|
@@ -78,10 +168,68 @@ export function ExerciseHeading({
|
|
|
78
168
|
style={{ gap: 8, marginTop: 1 }}
|
|
79
169
|
testID="exercise-card-summary"
|
|
80
170
|
>
|
|
81
|
-
|
|
171
|
+
{prescriptionNode(heading, unit)}
|
|
82
172
|
<View className="flex-1" />
|
|
83
173
|
{tempo && <TempoDisplay tempo={tempo} size="sm" showLabel={false} showInfo={false} />}
|
|
84
174
|
</View>
|
|
175
|
+
</>
|
|
176
|
+
)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function InlineHeading({ heading, nameStyle }: LayoutProps) {
|
|
180
|
+
const { unit = 'lbs', indicator, previousBest } = heading
|
|
181
|
+
const Row = heading.pressTarget === 'ancestor' ? View : Pressable
|
|
182
|
+
return (
|
|
183
|
+
<Row
|
|
184
|
+
{...(heading.pressTarget === 'ancestor' ? {} : pressProps(heading, unit))}
|
|
185
|
+
className="flex-row items-center"
|
|
186
|
+
style={{ gap: 8 }}
|
|
187
|
+
testID="exercise-card-header"
|
|
188
|
+
>
|
|
189
|
+
{/* Name never truncates (no numberOfLines); prescription + previousBest ellipsize first. */}
|
|
190
|
+
<HeadingName name={heading.name} nameStyle={nameStyle} className="shrink-0" />
|
|
191
|
+
<View className="shrink flex-row items-center" testID="exercise-card-summary">
|
|
192
|
+
{prescriptionNode(heading, unit)}
|
|
193
|
+
</View>
|
|
194
|
+
<View className="flex-1" style={{ minWidth: 8 }} />
|
|
195
|
+
{indicator && <ExerciseIndicator kind={indicator} />}
|
|
196
|
+
{previousBest && (
|
|
197
|
+
<Typography
|
|
198
|
+
variant="caption"
|
|
199
|
+
color="tertiary"
|
|
200
|
+
maxLines={1}
|
|
201
|
+
className="font-sans shrink text-2xs leading-[normal]"
|
|
202
|
+
testID="exercise-card-previous-best"
|
|
203
|
+
>
|
|
204
|
+
{previousBest}
|
|
205
|
+
</Typography>
|
|
206
|
+
)}
|
|
207
|
+
</Row>
|
|
208
|
+
)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* The exercise-heading info block (no strip): the name + {@link ExerciseIndicator}
|
|
213
|
+
* title row, its prescription line, and — `inline` only — a trailing previous-best
|
|
214
|
+
* caption. The name row is the sole press target: TempoDisplay and the previous-best
|
|
215
|
+
* caption are siblings of it, never descendants, since TempoDisplay is itself a
|
|
216
|
+
* Pressable (nesting would be an a11y nested-interactive violation).
|
|
217
|
+
*
|
|
218
|
+
* `layout` is the one axis that separates the session-rail row (`stacked`) from the
|
|
219
|
+
* collapsed and upcoming card rows (`inline`) — same parts, same vocabulary, one row
|
|
220
|
+
* instead of two. `inline` drops the tempo: it has no room for a second metric lockup.
|
|
221
|
+
*/
|
|
222
|
+
export function ExerciseHeading(props: ExerciseHeadingProps) {
|
|
223
|
+
const mode = useSurfaceMode()
|
|
224
|
+
const nameStyle = props.isLive ? { color: exerciseLiveColor(mode) } : undefined
|
|
225
|
+
|
|
226
|
+
return (
|
|
227
|
+
<View style={{ opacity: props.dimmed ? DIMMED_OPACITY : 1 }} testID="exercise-heading">
|
|
228
|
+
{props.layout === 'inline' ? (
|
|
229
|
+
<InlineHeading heading={props} nameStyle={nameStyle} />
|
|
230
|
+
) : (
|
|
231
|
+
<StackedHeading heading={props} nameStyle={nameStyle} />
|
|
232
|
+
)}
|
|
85
233
|
</View>
|
|
86
234
|
)
|
|
87
235
|
}
|
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
INDICATOR_PRECEDENCE,
|
|
7
7
|
type ExerciseIndicatorKind,
|
|
8
8
|
} from './ExerciseIndicator'
|
|
9
|
+
import { Surface } from '../../ui/surface'
|
|
10
|
+
import { greyRamp } from '../../../theme/tokens/primitives'
|
|
9
11
|
|
|
10
12
|
/**
|
|
11
13
|
* `ExerciseIndicator` — a small circular OUTLINED chip in an exercise heading's
|
|
@@ -15,7 +17,7 @@ import {
|
|
|
15
17
|
* picks the highest-precedence active signal.
|
|
16
18
|
*/
|
|
17
19
|
const meta: Meta<typeof ExerciseIndicator> = {
|
|
18
|
-
title: 'Workout/ExerciseIndicator',
|
|
20
|
+
title: 'Custom/Workout/ExerciseIndicator',
|
|
19
21
|
component: ExerciseIndicator,
|
|
20
22
|
tags: ['autodocs'],
|
|
21
23
|
parameters: {
|
|
@@ -26,7 +28,8 @@ const meta: Meta<typeof ExerciseIndicator> = {
|
|
|
26
28
|
'precedence-ranked kinds (imbalance › overshoot › velocity-loss › missed-reps › pr › ' +
|
|
27
29
|
'info) over four status-token tier colors; glyph = signal, color = severity tier. ' +
|
|
28
30
|
'`resolveIndicator()` selects the single chip to show. Used-by ↑ ' +
|
|
29
|
-
'[ExerciseHeading](?path=/docs/workout-exerciseheading--docs).'
|
|
31
|
+
'[ExerciseHeading](?path=/docs/custom-workout-exerciseheading--docs).' +
|
|
32
|
+
' Composes **Icons**.',
|
|
30
33
|
},
|
|
31
34
|
},
|
|
32
35
|
},
|
|
@@ -36,9 +39,9 @@ const meta: Meta<typeof ExerciseIndicator> = {
|
|
|
36
39
|
},
|
|
37
40
|
decorators: [
|
|
38
41
|
(Story) => (
|
|
39
|
-
<
|
|
42
|
+
<Surface level="base" style={{ padding: 24 }}>
|
|
40
43
|
<Story />
|
|
41
|
-
</
|
|
44
|
+
</Surface>
|
|
42
45
|
),
|
|
43
46
|
],
|
|
44
47
|
}
|
|
@@ -68,9 +71,9 @@ export const AllKinds: Story = {
|
|
|
68
71
|
<View style={{ gap: 12 }}>
|
|
69
72
|
{INDICATOR_PRECEDENCE.map((kind, i) => (
|
|
70
73
|
<View key={kind} style={{ flexDirection: 'row', alignItems: 'center', gap: 10 }}>
|
|
71
|
-
<Text style={{ color:
|
|
74
|
+
<Text style={{ color: greyRamp[500], fontSize: 11, width: 14 }}>{i + 1}</Text>
|
|
72
75
|
<ExerciseIndicator kind={kind} />
|
|
73
|
-
<Text style={{ color:
|
|
76
|
+
<Text style={{ color: greyRamp[200], fontSize: 12 }}>{KIND_LABELS[kind]}</Text>
|
|
74
77
|
</View>
|
|
75
78
|
))}
|
|
76
79
|
</View>
|
|
@@ -95,12 +98,12 @@ export const ResolvePrecedence: Story = {
|
|
|
95
98
|
const winner = resolveIndicator(candidates)
|
|
96
99
|
return (
|
|
97
100
|
<View key={i} style={{ flexDirection: 'row', alignItems: 'center', gap: 10 }}>
|
|
98
|
-
<Text style={{ color:
|
|
101
|
+
<Text style={{ color: greyRamp[500], fontSize: 12, width: 200 }}>
|
|
99
102
|
{candidates.join(', ')}
|
|
100
103
|
</Text>
|
|
101
|
-
<Text style={{ color:
|
|
104
|
+
<Text style={{ color: greyRamp[500], fontSize: 12 }}>→</Text>
|
|
102
105
|
{winner ? <ExerciseIndicator kind={winner} /> : null}
|
|
103
|
-
<Text style={{ color:
|
|
106
|
+
<Text style={{ color: greyRamp[200], fontSize: 12 }}>{winner}</Text>
|
|
104
107
|
</View>
|
|
105
108
|
)
|
|
106
109
|
})}
|
|
@@ -7,18 +7,30 @@ import {
|
|
|
7
7
|
INDICATOR_PRECEDENCE,
|
|
8
8
|
type ExerciseIndicatorKind,
|
|
9
9
|
} from './ExerciseIndicator'
|
|
10
|
+
import { getSemanticColors } from '../../../theme/tokens/semantic'
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
{ kind: '
|
|
18
|
-
{ kind: '
|
|
19
|
-
{ kind: '
|
|
12
|
+
type StatusToken = 'status-error' | 'status-warning' | 'status-success' | 'status-info'
|
|
13
|
+
|
|
14
|
+
// tier color = titan status token, applied as the glyph's SVG stroke. The component
|
|
15
|
+
// resolves it through `resolveColor`, so on web the stroke is the CSS var and the
|
|
16
|
+
// theme drives the value; the dark hex is asserted separately below.
|
|
17
|
+
const kinds: { kind: ExerciseIndicatorKind; label: string; tier: StatusToken }[] = [
|
|
18
|
+
{ kind: 'imbalance', label: 'Left/right imbalance', tier: 'status-error' },
|
|
19
|
+
{ kind: 'overshoot', label: 'Load overshoot', tier: 'status-error' },
|
|
20
|
+
{ kind: 'velocity-loss', label: 'Velocity loss', tier: 'status-warning' },
|
|
21
|
+
{ kind: 'missed-reps', label: 'Missed reps', tier: 'status-warning' },
|
|
22
|
+
{ kind: 'pr', label: 'Personal record', tier: 'status-success' },
|
|
23
|
+
{ kind: 'info', label: 'More info', tier: 'status-info' },
|
|
20
24
|
]
|
|
21
25
|
|
|
26
|
+
// The dark-mode values the tier tokens must keep resolving to (the pre-port literals).
|
|
27
|
+
const DARK_TIER: Record<StatusToken, string> = {
|
|
28
|
+
'status-error': '#D14343',
|
|
29
|
+
'status-warning': '#F9B415',
|
|
30
|
+
'status-success': '#2ED573',
|
|
31
|
+
'status-info': '#2196F3',
|
|
32
|
+
}
|
|
33
|
+
|
|
22
34
|
describe('ExerciseIndicator', () => {
|
|
23
35
|
it.each(kinds)('renders the $kind chip glyph and accessible label', ({ kind, label }) => {
|
|
24
36
|
const { container } = render(<ExerciseIndicator kind={kind} />)
|
|
@@ -27,9 +39,13 @@ describe('ExerciseIndicator', () => {
|
|
|
27
39
|
expect(screen.getByLabelText(label)).toBeInTheDocument()
|
|
28
40
|
})
|
|
29
41
|
|
|
30
|
-
it.each(kinds)('tints the $kind glyph with its tier
|
|
42
|
+
it.each(kinds)('tints the $kind glyph with its tier token', ({ kind, tier }) => {
|
|
31
43
|
const { container } = render(<ExerciseIndicator kind={kind} />)
|
|
32
|
-
expect(container.querySelector('svg')!.getAttribute('stroke')).toBe(tier)
|
|
44
|
+
expect(container.querySelector('svg')!.getAttribute('stroke')).toBe(`var(--color-${tier})`)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it.each(kinds)('keeps the $kind tier on its dark-mode value', ({ tier }) => {
|
|
48
|
+
expect(getSemanticColors('dark')[tier]).toBe(DARK_TIER[tier])
|
|
33
49
|
})
|
|
34
50
|
|
|
35
51
|
it('renders a decorative (aria-hidden) glyph so the chip owns the label', () => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
|
|
2
2
|
import type { ComponentType } from 'react'
|
|
3
3
|
import { View, Pressable, type ViewProps } from 'react-native'
|
|
4
|
-
import {
|
|
4
|
+
import { resolveColor } from '../../../theme/resolve-color'
|
|
5
5
|
import {
|
|
6
6
|
ScaleIcon,
|
|
7
7
|
AlertTriangleIcon,
|
|
@@ -12,10 +12,6 @@ import {
|
|
|
12
12
|
type IconProps,
|
|
13
13
|
} from '../../icons'
|
|
14
14
|
|
|
15
|
-
// Literal-hex status pins (getSemanticColors, not resolveColor) — `resolveColor()` yields a
|
|
16
|
-
// `var(...)` under the RNW vitest alias, which breaks `toHaveStyle` in tests.
|
|
17
|
-
const t = getSemanticColors('dark')
|
|
18
|
-
|
|
19
15
|
/**
|
|
20
16
|
* The LOCKED 6-kind indicator taxonomy (TD-03.51). One precedence-ranked slot is
|
|
21
17
|
* ever shown per exercise, resolved by {@link resolveIndicator}. Glyph = the
|
|
@@ -31,21 +27,23 @@ export type ExerciseIndicatorKind =
|
|
|
31
27
|
| 'pr'
|
|
32
28
|
| 'info'
|
|
33
29
|
|
|
30
|
+
type StatusToken = 'status-error' | 'status-warning' | 'status-success' | 'status-info'
|
|
31
|
+
|
|
34
32
|
interface IndicatorConfig {
|
|
35
33
|
Icon: ComponentType<IconProps>
|
|
36
|
-
/** Tier
|
|
37
|
-
|
|
34
|
+
/** Tier token — resolved per theme by {@link resolveColor} at render. */
|
|
35
|
+
token: StatusToken
|
|
38
36
|
/** Accessible description (also the intended tooltip subject). */
|
|
39
37
|
label: string
|
|
40
38
|
}
|
|
41
39
|
|
|
42
40
|
const INDICATOR_CONFIG: Record<ExerciseIndicatorKind, IndicatorConfig> = {
|
|
43
|
-
imbalance: { Icon: ScaleIcon,
|
|
44
|
-
overshoot: { Icon: AlertTriangleIcon,
|
|
45
|
-
'velocity-loss': { Icon: TrendingDownIcon,
|
|
46
|
-
'missed-reps': { Icon: CircleSlashIcon,
|
|
47
|
-
pr: { Icon: AwardIcon,
|
|
48
|
-
info: { Icon: InfoIcon,
|
|
41
|
+
imbalance: { Icon: ScaleIcon, token: 'status-error', label: 'Left/right imbalance' },
|
|
42
|
+
overshoot: { Icon: AlertTriangleIcon, token: 'status-error', label: 'Load overshoot' },
|
|
43
|
+
'velocity-loss': { Icon: TrendingDownIcon, token: 'status-warning', label: 'Velocity loss' },
|
|
44
|
+
'missed-reps': { Icon: CircleSlashIcon, token: 'status-warning', label: 'Missed reps' },
|
|
45
|
+
pr: { Icon: AwardIcon, token: 'status-success', label: 'Personal record' },
|
|
46
|
+
info: { Icon: InfoIcon, token: 'status-info', label: 'More info' },
|
|
49
47
|
}
|
|
50
48
|
|
|
51
49
|
/**
|
|
@@ -68,7 +66,7 @@ export const INDICATOR_PRECEDENCE: readonly ExerciseIndicatorKind[] = [
|
|
|
68
66
|
* duplicates in `candidates` don't matter — {@link INDICATOR_PRECEDENCE} decides.
|
|
69
67
|
*/
|
|
70
68
|
export function resolveIndicator(
|
|
71
|
-
candidates: ExerciseIndicatorKind[]
|
|
69
|
+
candidates: ExerciseIndicatorKind[]
|
|
72
70
|
): ExerciseIndicatorKind | undefined {
|
|
73
71
|
return INDICATOR_PRECEDENCE.find((kind) => candidates.includes(kind))
|
|
74
72
|
}
|
|
@@ -86,7 +84,8 @@ export interface ExerciseIndicatorProps extends Omit<ViewProps, 'children'> {
|
|
|
86
84
|
* by the consumer.
|
|
87
85
|
*/
|
|
88
86
|
export function ExerciseIndicator({ kind, onPress, className, ...props }: ExerciseIndicatorProps) {
|
|
89
|
-
const { Icon,
|
|
87
|
+
const { Icon, token, label } = INDICATOR_CONFIG[kind]
|
|
88
|
+
const color = resolveColor(token)
|
|
90
89
|
|
|
91
90
|
const chip = (
|
|
92
91
|
<View
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
|
|
2
2
|
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
3
|
-
import { View } from 'react-native'
|
|
4
3
|
import { FatigueMeter } from './FatigueMeter'
|
|
4
|
+
import { Surface } from '../../ui/surface'
|
|
5
5
|
|
|
6
6
|
const meta: Meta<typeof FatigueMeter> = {
|
|
7
|
-
title: 'Workout/DataViz/FatigueMeter',
|
|
7
|
+
title: 'Custom/Workout/DataViz/FatigueMeter',
|
|
8
8
|
component: FatigueMeter,
|
|
9
9
|
tags: ['autodocs'],
|
|
10
10
|
argTypes: {
|
|
@@ -16,16 +16,16 @@ const meta: Meta<typeof FatigueMeter> = {
|
|
|
16
16
|
component:
|
|
17
17
|
'**Component.** The winning fatigue-visual lineage: a sliding needle over a fixed ' +
|
|
18
18
|
'green→gold→orange→red velocity-loss gradient with VL10/VL20/VL30/stop markers. ' +
|
|
19
|
-
'Composes the [ZoneTrack](?path=/docs/workout-dataviz-zonetrack--docs) primitive. ' +
|
|
19
|
+
'Composes the [ZoneTrack](?path=/docs/custom-workout-dataviz-zonetrack--docs) primitive. ' +
|
|
20
20
|
'Prop-driven: `value` plus overridable `thresholds` / `max` / `zoneColors` / `labels`.',
|
|
21
21
|
},
|
|
22
22
|
},
|
|
23
23
|
},
|
|
24
24
|
decorators: [
|
|
25
25
|
(Story) => (
|
|
26
|
-
<
|
|
26
|
+
<Surface level="base" style={{ width: 360, padding: 20 }}>
|
|
27
27
|
<Story />
|
|
28
|
-
</
|
|
28
|
+
</Surface>
|
|
29
29
|
),
|
|
30
30
|
],
|
|
31
31
|
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
1
|
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
3
|
-
import { View } from 'react-native'
|
|
4
2
|
import { InputBar } from './InputBar'
|
|
3
|
+
import { Surface } from '../../ui/surface'
|
|
5
4
|
|
|
6
5
|
const meta: Meta<typeof InputBar> = {
|
|
7
|
-
title: 'Workout/InputBar',
|
|
6
|
+
title: 'Custom/Workout/InputBar',
|
|
8
7
|
component: InputBar,
|
|
9
8
|
tags: ['autodocs'],
|
|
10
9
|
argTypes: {
|
|
@@ -24,9 +23,9 @@ const meta: Meta<typeof InputBar> = {
|
|
|
24
23
|
},
|
|
25
24
|
decorators: [
|
|
26
25
|
(Story) => (
|
|
27
|
-
<
|
|
26
|
+
<Surface level="base" style={{ width: 400 }}>
|
|
28
27
|
<Story />
|
|
29
|
-
</
|
|
28
|
+
</Surface>
|
|
30
29
|
),
|
|
31
30
|
],
|
|
32
31
|
}
|
|
@@ -18,7 +18,9 @@ export interface InputBarProps {
|
|
|
18
18
|
visible: boolean
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
const
|
|
21
|
+
const t = getSemanticColors('dark')
|
|
22
|
+
const BRAND_PRIMARY = t['brand-primary']
|
|
23
|
+
const ON_BRAND_PRIMARY = t['on-brand-primary']
|
|
22
24
|
const INPUT_CLASSNAME = 'bg-surface-raised border-hairline-strong text-text-primary'
|
|
23
25
|
|
|
24
26
|
const inputStyle = {
|
|
@@ -151,7 +153,7 @@ export function InputBar({
|
|
|
151
153
|
fontSize: 13,
|
|
152
154
|
fontWeight: '700',
|
|
153
155
|
fontFamily: 'Inter, sans-serif',
|
|
154
|
-
color:
|
|
156
|
+
color: ON_BRAND_PRIMARY,
|
|
155
157
|
}}
|
|
156
158
|
>
|
|
157
159
|
Record
|
|
@@ -3,7 +3,7 @@ import { View } from 'react-native'
|
|
|
3
3
|
import { IntensityBar } from './IntensityBar'
|
|
4
4
|
|
|
5
5
|
const meta: Meta<typeof IntensityBar> = {
|
|
6
|
-
title: 'Workout/DataViz/IntensityBar',
|
|
6
|
+
title: 'Custom/Workout/DataViz/IntensityBar',
|
|
7
7
|
component: IntensityBar,
|
|
8
8
|
tags: ['autodocs'],
|
|
9
9
|
argTypes: {
|
|
@@ -2,8 +2,12 @@ import { describe, it, expect } from 'vitest'
|
|
|
2
2
|
import { render, screen } from '@testing-library/react'
|
|
3
3
|
import { axe } from 'jest-axe'
|
|
4
4
|
import { IntensityBar } from './IntensityBar'
|
|
5
|
+
import { getGlowShadow } from '../../../theme/elevation'
|
|
6
|
+
import { getSemanticColors } from '../../../theme/tokens/semantic'
|
|
5
7
|
|
|
6
|
-
const AT_TARGET_GLOW =
|
|
8
|
+
const AT_TARGET_GLOW = (
|
|
9
|
+
getGlowShadow(getSemanticColors('dark')['status-info'], 'subtle') as { boxShadow: string }
|
|
10
|
+
).boxShadow
|
|
7
11
|
|
|
8
12
|
describe('IntensityBar', () => {
|
|
9
13
|
it('renders the bar', () => {
|
|
@@ -9,7 +9,9 @@ import {
|
|
|
9
9
|
type DimensionValue,
|
|
10
10
|
} from 'react-native'
|
|
11
11
|
import { getSemanticColors } from '../../../theme/tokens/semantic'
|
|
12
|
+
import { getGlowShadow } from '../../../theme/elevation'
|
|
12
13
|
import { WORKOUT_TOKENS } from '../../../theme/workout-tokens'
|
|
14
|
+
import { alpha } from '../../../utils/colors'
|
|
13
15
|
|
|
14
16
|
const t = getSemanticColors('dark')
|
|
15
17
|
|
|
@@ -31,10 +33,13 @@ export interface IntensityBarProps extends ViewProps {
|
|
|
31
33
|
className?: string
|
|
32
34
|
}
|
|
33
35
|
|
|
36
|
+
// VW-82: neither value is on a ramp (nearest surface-raised #31302F / greyRamp[600]
|
|
37
|
+
// #72716F). Left raw pending the proposed roles — snapping them is a visual change.
|
|
34
38
|
const TRACK_BG = '#333333'
|
|
35
39
|
const LABEL_COLOR = '#6B7280'
|
|
36
|
-
const TARGET_LINE_COLOR = '
|
|
37
|
-
|
|
40
|
+
const TARGET_LINE_COLOR = alpha(t['status-info'], 0.5)
|
|
41
|
+
/** At-target is EMPHASIS, not depth: an info-toned glow through the shared builder. */
|
|
42
|
+
const AT_TARGET_GLOW = getGlowShadow(t['status-info'], 'subtle')
|
|
38
43
|
|
|
39
44
|
// Zone colors graded by TRUE percentage (level * 100).
|
|
40
45
|
const ZONE = {
|
|
@@ -178,7 +183,7 @@ export function IntensityBar({
|
|
|
178
183
|
borderRadius: 3,
|
|
179
184
|
backgroundColor: zone.color,
|
|
180
185
|
zIndex: 2,
|
|
181
|
-
...(atTarget ?
|
|
186
|
+
...(atTarget ? AT_TARGET_GLOW : null),
|
|
182
187
|
}}
|
|
183
188
|
testID="intensity-fill"
|
|
184
189
|
/>
|
|
@@ -2,6 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
|
2
2
|
import { View, Text } from 'react-native'
|
|
3
3
|
import { LiveAuraFrame } from './LiveAuraFrame'
|
|
4
4
|
import { getSemanticColors } from '../../../theme/tokens/semantic'
|
|
5
|
+
import { Surface } from '../../ui/surface'
|
|
5
6
|
|
|
6
7
|
const t = getSemanticColors('dark')
|
|
7
8
|
|
|
@@ -74,7 +75,7 @@ function LiveHero({ caption }: { caption: string }) {
|
|
|
74
75
|
const PANEL = { width: 360, height: 620, borderRadius: 24, overflow: 'hidden' as const }
|
|
75
76
|
|
|
76
77
|
const meta: Meta<typeof LiveAuraFrame> = {
|
|
77
|
-
title: 'Workout/LiveAuraFrame',
|
|
78
|
+
title: 'Custom/Workout/LiveAuraFrame',
|
|
78
79
|
component: LiveAuraFrame,
|
|
79
80
|
tags: ['autodocs'],
|
|
80
81
|
parameters: {
|
|
@@ -100,17 +101,17 @@ const meta: Meta<typeof LiveAuraFrame> = {
|
|
|
100
101
|
},
|
|
101
102
|
decorators: [
|
|
102
103
|
(Story) => (
|
|
103
|
-
<
|
|
104
|
+
<Surface
|
|
105
|
+
level="base"
|
|
104
106
|
style={{
|
|
105
107
|
minHeight: 700,
|
|
106
|
-
backgroundColor: '#070707',
|
|
107
108
|
alignItems: 'center',
|
|
108
109
|
justifyContent: 'center',
|
|
109
110
|
padding: 32,
|
|
110
111
|
}}
|
|
111
112
|
>
|
|
112
113
|
<Story />
|
|
113
|
-
</
|
|
114
|
+
</Surface>
|
|
114
115
|
),
|
|
115
116
|
],
|
|
116
117
|
render: (args) => (
|