@titan-design/react-ui 0.12.0 → 0.13.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 +46 -47
- package/dist/bodymap.js +768 -701
- package/dist/bodymap.js.map +1 -1
- package/dist/bodymap.mjs +770 -703
- package/dist/bodymap.mjs.map +1 -1
- package/dist/index.d.mts +1320 -215
- package/dist/index.d.ts +1320 -215
- package/dist/index.js +5996 -4498
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +5773 -4357
- package/dist/index.mjs.map +1 -1
- package/dist/{pages-CvsFqNtx.d.mts → pages-CpW3OB3g.d.mts} +21 -3
- package/dist/{pages-B1hqnGIC.d.ts → pages-_3Ajpiqu.d.ts} +21 -3
- package/dist/pages.d.mts +1 -1
- package/dist/pages.d.ts +1 -1
- package/dist/pages.js +1435 -1428
- package/dist/pages.js.map +1 -1
- package/dist/pages.mjs +1443 -1436
- package/dist/pages.mjs.map +1 -1
- package/dist/theme/index.d.mts +202 -1042
- package/dist/theme/index.d.ts +202 -1042
- package/dist/theme/index.js +215 -300
- package/dist/theme/index.js.map +1 -1
- package/dist/theme/index.mjs +202 -296
- package/dist/theme/index.mjs.map +1 -1
- package/dist/theme/tokens-css.js +62 -5
- package/dist/theme/tokens-css.js.map +1 -1
- package/dist/theme/tokens-css.mjs +62 -5
- package/dist/theme/tokens-css.mjs.map +1 -1
- package/dist/theme/tokens.d.mts +972 -0
- package/dist/theme/tokens.d.ts +972 -0
- package/dist/theme/tokens.js +904 -0
- package/dist/theme/tokens.js.map +1 -0
- package/dist/theme/tokens.mjs +880 -0
- package/dist/theme/tokens.mjs.map +1 -0
- 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/package.json +7 -1
- package/src/arch/arch-graph.freshness.test.ts +52 -0
- package/src/arch/arch-graph.json +5021 -1617
- 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 +181 -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 +4 -3
- 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 +2 -2
- package/src/components/custom/Gauge/Gauge.test.tsx +1 -1
- package/src/components/custom/Gauge/Gauge.tsx +4 -1
- 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 +46 -8
- package/src/components/custom/Scatter/Scatter.test.tsx +2 -2
- package/src/components/custom/Scatter/Scatter.tsx +116 -14
- package/src/components/custom/Sidebar/Sidebar.stories.tsx +40 -7
- 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 +98 -15
- 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 +2 -2
- package/src/components/custom/Treemap/Treemap.test.tsx +1 -1
- package/src/components/custom/Treemap/Treemap.tsx +7 -2
- 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 +9 -1
- package/src/components/custom/Workout/BaseBadge.stories.tsx +19 -7
- package/src/components/custom/Workout/BaseBadge.test.tsx +31 -11
- package/src/components/custom/Workout/BaseBadge.tsx +3 -0
- package/src/components/custom/Workout/BodyMap.stories.tsx +4 -1
- package/src/components/custom/Workout/BodyMap.tsx +3 -1
- package/src/components/custom/Workout/BodyMapDetailPanel.stories.tsx +16 -4
- package/src/components/custom/Workout/BodyMapDetailPanel.test.tsx +11 -15
- package/src/components/custom/Workout/BodyMapDetailPanel.tsx +45 -9
- package/src/components/custom/Workout/CapacityBandChart.stories.tsx +10 -8
- package/src/components/custom/Workout/CapacityBandChart.test.tsx +24 -51
- package/src/components/custom/Workout/CapacityBandChart.tsx +139 -108
- package/src/components/custom/Workout/DeviationBar.stories.tsx +1 -1
- package/src/components/custom/Workout/DeviationBar.test.tsx +5 -2
- package/src/components/custom/Workout/DeviationBar.tsx +21 -16
- package/src/components/custom/Workout/DualVelocityStrip.stories.tsx +182 -0
- package/src/components/custom/Workout/DualVelocityStrip.test.tsx +3 -2
- package/src/components/custom/Workout/ExerciseCard.stories.tsx +8 -7
- package/src/components/custom/Workout/ExerciseCard.test.tsx +13 -3
- package/src/components/custom/Workout/ExerciseCard.tsx +42 -51
- package/src/components/custom/Workout/ExerciseCardHeading.stories.tsx +7 -7
- package/src/components/custom/Workout/ExerciseDetailPage.stories.tsx +9 -1
- package/src/components/custom/Workout/ExerciseHeading.stories.tsx +8 -8
- package/src/components/custom/Workout/ExerciseHeading.tsx +8 -5
- package/src/components/custom/Workout/ExerciseIndicator.stories.tsx +6 -4
- 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/IntensityBar.stories.tsx +1 -1
- package/src/components/custom/Workout/IntensityBar.test.tsx +5 -1
- package/src/components/custom/Workout/IntensityBar.tsx +4 -2
- 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 +12 -9
- 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 +9 -23
- 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 +1 -1
- 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 +4 -1
- 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 +12 -7
- 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 +19 -3
- package/src/components/custom/Workout/RestTimer.stories.tsx +3 -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 +5 -5
- package/src/components/custom/Workout/SegmentedBar.tsx +6 -2
- 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 +60 -5
- 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 +3 -5
- package/src/components/custom/Workout/Sparkline.stories.tsx +1 -1
- 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 +9 -3
- package/src/components/custom/Workout/StrengthTrendChart.test.tsx +15 -37
- package/src/components/custom/Workout/StrengthTrendChart.tsx +31 -30
- 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 +22 -7
- package/src/components/custom/Workout/TempoDisplay.test.tsx +18 -4
- 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 +7 -4
- package/src/components/custom/Workout/VelocityStrip.expanded.stories.tsx +4 -1
- package/src/components/custom/Workout/VelocityStrip.hero.stories.tsx +4 -1
- package/src/components/custom/Workout/VelocityStrip.stories.tsx +12 -6
- package/src/components/custom/Workout/VelocityStrip.tsx +3 -0
- 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 +7 -6
- package/src/components/custom/Workout/ZoneTrack.tsx +10 -9
- package/src/components/custom/Workout/index.ts +5 -0
- package/src/components/custom/Workout/metricText.stories.tsx +6 -5
- package/src/components/custom/Workout/metricText.tsx +4 -0
- package/src/components/custom/Workout/setHeadingKit.tsx +207 -26
- package/src/components/custom/charts/README.md +61 -0
- package/src/components/custom/charts/SetBarChart.test.tsx +30 -1
- package/src/components/custom/charts/SetBarChart.tsx +42 -4
- 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/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 +13 -28
- 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 +90 -27
- 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 +1 -1
- 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 +32 -25
- 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 +6 -26
- 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 +9 -2
- 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 -30
- 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 +2 -12
- package/src/components/ui/indicator/Indicator.stories.tsx +1 -1
- 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 +11 -2
- 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 +29 -19
- package/src/components/ui/pill/Pill.stories.tsx +58 -23
- package/src/components/ui/pill/Pill.test.tsx +101 -3
- package/src/components/ui/pill/Pill.tsx +164 -49
- package/src/components/ui/pill/index.ts +1 -1
- package/src/components/ui/popover/Popover.stories.tsx +5 -7
- 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/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 +5 -14
- 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 +14 -14
- 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 +1 -4
- package/src/components/ui/stack/Stack.stories.tsx +1 -1
- package/src/components/ui/stack/Stack.tsx +1 -1
- package/src/components/ui/surface/Surface.stories.tsx +17 -5
- 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 +7 -22
- 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 +43 -53
- 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 +25 -43
- 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 +100 -47
- package/src/test/no-device-internals.test.ts +3 -3
- 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 +24 -22
- 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 +28 -0
- package/src/theme/elevation.test.ts +82 -56
- package/src/theme/elevation.ts +77 -561
- package/src/theme/global.css +30 -2
- 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/surface-planes.test.ts +37 -0
- package/src/theme/surface-planes.ts +68 -0
- package/src/theme/token-export-guard.test.ts +102 -0
- package/src/theme/tokens/primitives.test.ts +15 -7
- package/src/theme/tokens/primitives.ts +67 -35
- package/src/theme/tokens/semantic.ts +71 -37
- 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.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 +5 -0
- 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,6 +1,7 @@
|
|
|
1
1
|
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
|
+
import { Surface } from '../../ui/surface'
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* `SetStrip` — the per-set segmented performance strip. One continuous bar per
|
|
@@ -9,7 +10,7 @@ import { SetStrip, type SetStripSet } from './SetStrip'
|
|
|
9
10
|
* amber-300 · green-300).
|
|
10
11
|
*/
|
|
11
12
|
const meta: Meta<typeof SetStrip> = {
|
|
12
|
-
title: 'Workout/SetStrip',
|
|
13
|
+
title: 'Custom/Workout/SetStrip',
|
|
13
14
|
component: SetStrip,
|
|
14
15
|
tags: ['autodocs'],
|
|
15
16
|
parameters: {
|
|
@@ -18,8 +19,8 @@ const meta: Meta<typeof SetStrip> = {
|
|
|
18
19
|
component:
|
|
19
20
|
'**Molecule.** The per-set segmented performance strip — one bar per set, rep ' +
|
|
20
21
|
'intensities as butted ramp-pin segments (no rep gaps), active set pulses. Composes ' +
|
|
21
|
-
'[SetBar](?path=/docs/workout-setbar--docs) × N. ' +
|
|
22
|
-
'Used-by ↑ [ExerciseCardHeading](?path=/docs/workout-exercisecardheading--docs).',
|
|
22
|
+
'[SetBar](?path=/docs/custom-workout-setbar--docs) × N. ' +
|
|
23
|
+
'Used-by ↑ [ExerciseCardHeading](?path=/docs/custom-workout-exercisecardheading--docs).',
|
|
23
24
|
},
|
|
24
25
|
},
|
|
25
26
|
},
|
|
@@ -28,9 +29,9 @@ const meta: Meta<typeof SetStrip> = {
|
|
|
28
29
|
},
|
|
29
30
|
decorators: [
|
|
30
31
|
(Story) => (
|
|
31
|
-
<
|
|
32
|
+
<Surface level="base" style={{ width: 210, padding: 16 }}>
|
|
32
33
|
<Story />
|
|
33
|
-
</
|
|
34
|
+
</Surface>
|
|
34
35
|
),
|
|
35
36
|
],
|
|
36
37
|
}
|
|
@@ -78,6 +79,60 @@ export const ShortStrip: Story = {
|
|
|
78
79
|
},
|
|
79
80
|
}
|
|
80
81
|
|
|
82
|
+
// ---- prescribed rep range (VMCP-03.04) — optional on `todo`/`active` only
|
|
83
|
+
|
|
84
|
+
export const RepRange: Story = {
|
|
85
|
+
args: {
|
|
86
|
+
height: 8,
|
|
87
|
+
sets: [
|
|
88
|
+
{ status: 'done', velocities: decay(8, 0.9) },
|
|
89
|
+
{ status: 'active', velocities: decay(3, 0.75), planned: 8, repsLow: 8, repsHigh: 12 },
|
|
90
|
+
{ status: 'todo', planned: 8, repsLow: 8, repsHigh: 12 },
|
|
91
|
+
],
|
|
92
|
+
},
|
|
93
|
+
parameters: {
|
|
94
|
+
docs: {
|
|
95
|
+
description: {
|
|
96
|
+
story:
|
|
97
|
+
"The plan's prescribed rep RANGE (VMCP-03.04): `8–12` on the active and todo sets. " +
|
|
98
|
+
"Independent of the `range` set-type's value range (isokinetic bands).",
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export const RepRangeSingleNumber: Story = {
|
|
105
|
+
args: {
|
|
106
|
+
height: 8,
|
|
107
|
+
sets: [
|
|
108
|
+
{ status: 'active', velocities: decay(3, 0.75), planned: 8, repsLow: 8, repsHigh: 8 },
|
|
109
|
+
{ status: 'todo', planned: 8, repsLow: 8 },
|
|
110
|
+
],
|
|
111
|
+
},
|
|
112
|
+
parameters: {
|
|
113
|
+
docs: {
|
|
114
|
+
description: {
|
|
115
|
+
story: 'Equal bounds (or only one given) collapse to a single number: `8`, not `8–8`.',
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export const RepRangeAbsent: Story = {
|
|
122
|
+
args: {
|
|
123
|
+
height: 8,
|
|
124
|
+
sets: [
|
|
125
|
+
{ status: 'active', velocities: decay(3, 0.75), planned: 8 },
|
|
126
|
+
{ status: 'todo', planned: 8 },
|
|
127
|
+
],
|
|
128
|
+
},
|
|
129
|
+
parameters: {
|
|
130
|
+
docs: {
|
|
131
|
+
description: { story: 'No prescription on the set — no label renders (the default today).' },
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
}
|
|
135
|
+
|
|
81
136
|
// ---- set-type variant sheet: every variant at the real rail width, for eyeballing
|
|
82
137
|
function SheetRow({ label, meta, sets }: { label: string; meta: string; sets: SetStripSet[] }) {
|
|
83
138
|
return (
|
|
@@ -94,6 +94,31 @@ describe('SetStrip', () => {
|
|
|
94
94
|
expect(screen.getAllByTestId('set-strip-set')).toHaveLength(2)
|
|
95
95
|
})
|
|
96
96
|
|
|
97
|
+
describe('prescribed rep range (VMCP-03.04)', () => {
|
|
98
|
+
it('todo: renders the range when both bounds are given', () => {
|
|
99
|
+
render(<SetStrip sets={[{ status: 'todo', planned: 10, repsLow: 8, repsHigh: 12 }]} />)
|
|
100
|
+
expect(screen.getByTestId('set-strip-reps-label')).toHaveTextContent('8–12')
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it('active: renders a single number when the bounds are equal or only one is given', () => {
|
|
104
|
+
const { rerender } = render(
|
|
105
|
+
<SetStrip
|
|
106
|
+
sets={[{ status: 'active', velocities: [0.9], planned: 8, repsLow: 8, repsHigh: 8 }]}
|
|
107
|
+
/>
|
|
108
|
+
)
|
|
109
|
+
expect(screen.getByTestId('set-strip-reps-label')).toHaveTextContent('8')
|
|
110
|
+
rerender(
|
|
111
|
+
<SetStrip sets={[{ status: 'active', velocities: [0.9], planned: 8, repsLow: 8 }]} />
|
|
112
|
+
)
|
|
113
|
+
expect(screen.getByTestId('set-strip-reps-label')).toHaveTextContent('8')
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
it('renders no label when neither bound is given', () => {
|
|
117
|
+
render(<SetStrip sets={[{ status: 'todo', planned: 10 }]} />)
|
|
118
|
+
expect(screen.queryByTestId('set-strip-reps-label')).not.toBeInTheDocument()
|
|
119
|
+
})
|
|
120
|
+
})
|
|
121
|
+
|
|
97
122
|
describe('accessibility', () => {
|
|
98
123
|
it('has no accessibility violations', async () => {
|
|
99
124
|
const sets: SetStripSet[] = [
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
|
|
2
|
-
import { View, type ViewProps } from 'react-native'
|
|
2
|
+
import { View, Text, type ViewProps } from 'react-native'
|
|
3
3
|
import { SetBar, type SetStripSet } from './SetBar'
|
|
4
|
+
import { formatRepsRange } from '../../../utils/workout-format'
|
|
4
5
|
|
|
5
6
|
// Re-exported so the per-set colour vocabulary stays importable from either the
|
|
6
7
|
// molecule (SetStrip) or the atom (SetBar) it now lives in.
|
|
@@ -40,6 +41,12 @@ function describeSets(sets: SetStripSet[]): string {
|
|
|
40
41
|
return `Set progress: ${done} done, ${active} in progress, ${upcoming} upcoming`
|
|
41
42
|
}
|
|
42
43
|
|
|
44
|
+
/** The prescribed rep-range label for a `todo`/`active` set, or `null` when unset. */
|
|
45
|
+
function repsRangeLabel(set: SetStripSet): string | null {
|
|
46
|
+
if (set.status !== 'todo' && set.status !== 'active') return null
|
|
47
|
+
return formatRepsRange(set.repsLow, set.repsHigh)
|
|
48
|
+
}
|
|
49
|
+
|
|
43
50
|
export interface SetStripProps extends ViewProps {
|
|
44
51
|
/** Per-set performance data, in set order. */
|
|
45
52
|
sets: SetStripSet[]
|
|
@@ -63,9 +70,22 @@ export function SetStrip({ sets, height = 8, className, ...props }: SetStripProp
|
|
|
63
70
|
testID="set-strip"
|
|
64
71
|
{...props}
|
|
65
72
|
>
|
|
66
|
-
{sets.map((set, i) =>
|
|
67
|
-
|
|
68
|
-
|
|
73
|
+
{sets.map((set, i) => {
|
|
74
|
+
const label = repsRangeLabel(set)
|
|
75
|
+
if (label == null) return <SetBar key={i} set={set} height={height} />
|
|
76
|
+
return (
|
|
77
|
+
<View key={i} style={{ flex: 1, minWidth: 0, position: 'relative' }}>
|
|
78
|
+
<Text
|
|
79
|
+
className="text-text-secondary text-2xs font-semibold text-center"
|
|
80
|
+
style={{ position: 'absolute', bottom: height + 3, left: 0, right: 0 }}
|
|
81
|
+
testID="set-strip-reps-label"
|
|
82
|
+
>
|
|
83
|
+
{label}
|
|
84
|
+
</Text>
|
|
85
|
+
<SetBar set={set} height={height} />
|
|
86
|
+
</View>
|
|
87
|
+
)
|
|
88
|
+
})}
|
|
69
89
|
</View>
|
|
70
90
|
)
|
|
71
91
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
-
import { View } from 'react-native'
|
|
3
2
|
import { SetTableHeader } from './SetTableHeader'
|
|
3
|
+
import { Surface } from '../../ui/surface'
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* 🚧 WIP / placeholder — `SetTableHeader` (exported as `SetTableHeader`; the generic
|
|
@@ -10,7 +10,7 @@ import { SetTableHeader } from './SetTableHeader'
|
|
|
10
10
|
* unification (TD-03.56).
|
|
11
11
|
*/
|
|
12
12
|
const meta: Meta<typeof SetTableHeader> = {
|
|
13
|
-
title: 'Workout/SetTableHeader',
|
|
13
|
+
title: 'Custom/Workout/SetTableHeader',
|
|
14
14
|
component: SetTableHeader,
|
|
15
15
|
tags: ['autodocs'],
|
|
16
16
|
parameters: {
|
|
@@ -18,8 +18,8 @@ const meta: Meta<typeof SetTableHeader> = {
|
|
|
18
18
|
description: {
|
|
19
19
|
component:
|
|
20
20
|
'**Atom.** The expanded-set-table column header row; per-column widths mirror ' +
|
|
21
|
-
'[SetRow](?path=/docs/workout-setrow--docs) so headers align over the rows. ' +
|
|
22
|
-
'Used-by ↑ [ExerciseCard](?path=/docs/workout-exercisecard--docs) (expanded), which ' +
|
|
21
|
+
'[SetRow](?path=/docs/custom-workout-setrow--docs) so headers align over the rows. ' +
|
|
22
|
+
'Used-by ↑ [ExerciseCard](?path=/docs/custom-workout-exercisecard--docs) (expanded), which ' +
|
|
23
23
|
'renders it with `showPrevious={false}`. ' +
|
|
24
24
|
'Column set / widths provisional pending TD-03.56 responsive unification.',
|
|
25
25
|
},
|
|
@@ -31,9 +31,9 @@ const meta: Meta<typeof SetTableHeader> = {
|
|
|
31
31
|
},
|
|
32
32
|
decorators: [
|
|
33
33
|
(Story) => (
|
|
34
|
-
<
|
|
34
|
+
<Surface level="base" style={{ width: 320 }}>
|
|
35
35
|
<Story />
|
|
36
|
-
</
|
|
36
|
+
</Surface>
|
|
37
37
|
),
|
|
38
38
|
],
|
|
39
39
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
|
|
2
|
-
import { View
|
|
2
|
+
import { View } from 'react-native'
|
|
3
|
+
import { Typography } from '../Typography'
|
|
3
4
|
|
|
4
5
|
// One header column: its label and its fixed width (PREV is the lone flex column,
|
|
5
6
|
// `undefined`). Order + widths mirror SetRow's cells so headers align over rows.
|
|
@@ -19,6 +20,11 @@ export interface SetTableHeaderProps {
|
|
|
19
20
|
// distribute across the full strip width via `justifyContent: space-between`.
|
|
20
21
|
const PREV_COLUMN: Column = { label: 'PREV', width: undefined }
|
|
21
22
|
|
|
23
|
+
// `microLabel` is the 10px step of the scale (TOKENS.md §4), in the same Inter face as
|
|
24
|
+
// the table's value cells. Only the row's line box is pinned: the raw <Text> this
|
|
25
|
+
// replaced carried no lineHeight, and the variant's `leading-normal` would grow the row.
|
|
26
|
+
const COLUMN_LABEL = 'leading-[normal]'
|
|
27
|
+
|
|
22
28
|
/** Columns in order, with the weight column reflecting `unit`; PREV dropped when hidden. */
|
|
23
29
|
function buildColumns(unit: 'lbs' | 'kg', showPrevious: boolean): Column[] {
|
|
24
30
|
return [
|
|
@@ -68,18 +74,9 @@ export function SetTableHeader({
|
|
|
68
74
|
}}
|
|
69
75
|
{...(isFlex ? { className: 'flex-1' } : {})}
|
|
70
76
|
>
|
|
71
|
-
<
|
|
72
|
-
className="text-text-tertiary"
|
|
73
|
-
style={{
|
|
74
|
-
fontSize: 10,
|
|
75
|
-
fontWeight: '600',
|
|
76
|
-
fontFamily: 'Inter, sans-serif',
|
|
77
|
-
textTransform: 'uppercase',
|
|
78
|
-
letterSpacing: 0.8,
|
|
79
|
-
}}
|
|
80
|
-
>
|
|
77
|
+
<Typography variant="microLabel" color="tertiary" className={COLUMN_LABEL}>
|
|
81
78
|
{label}
|
|
82
|
-
</
|
|
79
|
+
</Typography>
|
|
83
80
|
</View>
|
|
84
81
|
)
|
|
85
82
|
})}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
-
import { View } from 'react-native'
|
|
3
2
|
import { SetsRepsLoad } from './SetsRepsLoad'
|
|
3
|
+
import { Surface } from '../../ui/surface'
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* `SetsRepsLoad` — the `sets × reps @ load` prescription line in the TempoDisplay
|
|
@@ -8,7 +8,7 @@ import { SetsRepsLoad } from './SetsRepsLoad'
|
|
|
8
8
|
* separators).
|
|
9
9
|
*/
|
|
10
10
|
const meta: Meta<typeof SetsRepsLoad> = {
|
|
11
|
-
title: 'Workout/SetsRepsLoad',
|
|
11
|
+
title: 'Custom/Workout/SetsRepsLoad',
|
|
12
12
|
component: SetsRepsLoad,
|
|
13
13
|
tags: ['autodocs'],
|
|
14
14
|
parameters: {
|
|
@@ -16,17 +16,17 @@ const meta: Meta<typeof SetsRepsLoad> = {
|
|
|
16
16
|
description: {
|
|
17
17
|
component:
|
|
18
18
|
'**Atom.** The `sets × reps @ load` prescription line in the TempoDisplay visual ' +
|
|
19
|
-
'language. Composes [MetricCell](?path=/docs/workout-metriccell--docs) ' +
|
|
20
|
-
'(shared with [TempoDisplay](?path=/docs/workout-tempodisplay--docs)). ' +
|
|
21
|
-
'Used-by ↑ [ExerciseHeading](?path=/docs/workout-exerciseheading--docs).',
|
|
19
|
+
'language. Composes [MetricCell](?path=/docs/custom-workout-metriccell--docs) ' +
|
|
20
|
+
'(shared with [TempoDisplay](?path=/docs/custom-workout-tempodisplay--docs)). ' +
|
|
21
|
+
'Used-by ↑ [ExerciseHeading](?path=/docs/custom-workout-exerciseheading--docs).',
|
|
22
22
|
},
|
|
23
23
|
},
|
|
24
24
|
},
|
|
25
25
|
decorators: [
|
|
26
26
|
(Story) => (
|
|
27
|
-
<
|
|
27
|
+
<Surface level="base" style={{ padding: 16 }}>
|
|
28
28
|
<Story />
|
|
29
|
-
</
|
|
29
|
+
</Surface>
|
|
30
30
|
),
|
|
31
31
|
],
|
|
32
32
|
}
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
// Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
|
|
2
2
|
import { View, type ViewProps } from 'react-native'
|
|
3
|
-
import {
|
|
3
|
+
import { useOnSurfaceColor } from '../../ui/surface/SurfaceContext'
|
|
4
4
|
import { MetricCell } from './metricText'
|
|
5
5
|
|
|
6
|
-
const t = getSemanticColors('dark')
|
|
7
|
-
|
|
8
6
|
const TIMES = '×' // × multiplication sign, padded by muted separators
|
|
9
7
|
const AT = '@'
|
|
10
8
|
|
|
@@ -34,8 +32,8 @@ export function SetsRepsLoad({
|
|
|
34
32
|
className,
|
|
35
33
|
...props
|
|
36
34
|
}: SetsRepsLoadProps) {
|
|
37
|
-
const value =
|
|
38
|
-
const sep =
|
|
35
|
+
const value = useOnSurfaceColor('primary')
|
|
36
|
+
const sep = useOnSurfaceColor('tertiary')
|
|
39
37
|
|
|
40
38
|
return (
|
|
41
39
|
<View
|
|
@@ -2,6 +2,7 @@ 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 { Sparkline } from './Sparkline'
|
|
5
|
+
import { resolveColor } from '../../../theme/resolve-color'
|
|
5
6
|
|
|
6
7
|
const sampleData = [10, 25, 15, 30, 20]
|
|
7
8
|
|
|
@@ -19,9 +20,7 @@ describe('Sparkline', () => {
|
|
|
19
20
|
it('renders line segments between points', () => {
|
|
20
21
|
render(<Sparkline data={sampleData} />)
|
|
21
22
|
for (let i = 1; i < sampleData.length; i++) {
|
|
22
|
-
expect(
|
|
23
|
-
screen.getByTestId(`sparkline-segment-${i}`),
|
|
24
|
-
).toBeInTheDocument()
|
|
23
|
+
expect(screen.getByTestId(`sparkline-segment-${i}`)).toBeInTheDocument()
|
|
25
24
|
}
|
|
26
25
|
})
|
|
27
26
|
|
|
@@ -40,9 +39,7 @@ describe('Sparkline', () => {
|
|
|
40
39
|
it('highlights last dot when highlightLast is true', () => {
|
|
41
40
|
render(<Sparkline data={sampleData} highlightLast />)
|
|
42
41
|
const lastIndex = sampleData.length - 1
|
|
43
|
-
expect(
|
|
44
|
-
screen.getByTestId(`sparkline-dot-${lastIndex}`),
|
|
45
|
-
).toBeInTheDocument()
|
|
42
|
+
expect(screen.getByTestId(`sparkline-dot-${lastIndex}`)).toBeInTheDocument()
|
|
46
43
|
// Other dots should not be present (showDots is false)
|
|
47
44
|
expect(screen.queryByTestId('sparkline-dot-0')).not.toBeInTheDocument()
|
|
48
45
|
})
|
|
@@ -62,7 +59,7 @@ describe('Sparkline', () => {
|
|
|
62
59
|
{ value: 20, color: '#ff0000' },
|
|
63
60
|
{ value: 25, color: '#00ff00', dashed: true },
|
|
64
61
|
]}
|
|
65
|
-
|
|
62
|
+
/>
|
|
66
63
|
)
|
|
67
64
|
expect(screen.getByTestId('sparkline-reference-0')).toBeInTheDocument()
|
|
68
65
|
expect(screen.getByTestId('sparkline-reference-1')).toBeInTheDocument()
|
|
@@ -70,9 +67,36 @@ describe('Sparkline', () => {
|
|
|
70
67
|
|
|
71
68
|
it('does not render reference lines when not provided', () => {
|
|
72
69
|
render(<Sparkline data={sampleData} />)
|
|
73
|
-
expect(
|
|
74
|
-
|
|
75
|
-
|
|
70
|
+
expect(screen.queryByTestId('sparkline-reference-0')).not.toBeInTheDocument()
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it('defaults the trace to the brand-primary token', () => {
|
|
74
|
+
render(<Sparkline data={sampleData} highlightLast />)
|
|
75
|
+
expect(screen.getByTestId('sparkline-segment-1')).toHaveStyle({
|
|
76
|
+
backgroundColor: resolveColor('brand-primary'),
|
|
77
|
+
})
|
|
78
|
+
expect(screen.getByTestId('sparkline-dot-4')).toHaveStyle({
|
|
79
|
+
backgroundColor: resolveColor('brand-primary'),
|
|
80
|
+
})
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('lets a caller override the trace colour', () => {
|
|
84
|
+
render(<Sparkline data={sampleData} color="#00ff00" />)
|
|
85
|
+
expect(screen.getByTestId('sparkline-segment-1')).toHaveStyle({
|
|
86
|
+
backgroundColor: '#00ff00',
|
|
87
|
+
})
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
it('renders a reference-line label in the caller colour', () => {
|
|
91
|
+
render(
|
|
92
|
+
<Sparkline
|
|
93
|
+
data={sampleData}
|
|
94
|
+
referenceLines={[{ value: 20, color: '#ff0000', label: 'MAV' }]}
|
|
95
|
+
/>
|
|
96
|
+
)
|
|
97
|
+
const label = screen.getByTestId('sparkline-reference-label-0')
|
|
98
|
+
expect(label).toHaveTextContent('MAV')
|
|
99
|
+
expect(label).toHaveStyle({ color: '#ff0000' })
|
|
76
100
|
})
|
|
77
101
|
|
|
78
102
|
it('uses default dimensions', () => {
|
|
@@ -89,23 +113,17 @@ describe('Sparkline', () => {
|
|
|
89
113
|
|
|
90
114
|
it('sets accessibility label', () => {
|
|
91
115
|
render(<Sparkline data={sampleData} />)
|
|
92
|
-
expect(
|
|
93
|
-
screen.getByLabelText('Sparkline chart with 5 data points'),
|
|
94
|
-
).toBeInTheDocument()
|
|
116
|
+
expect(screen.getByLabelText('Sparkline chart with 5 data points')).toBeInTheDocument()
|
|
95
117
|
})
|
|
96
118
|
|
|
97
119
|
it('sets empty accessibility label for no data', () => {
|
|
98
120
|
render(<Sparkline data={[]} />)
|
|
99
|
-
expect(
|
|
100
|
-
screen.getByLabelText('Sparkline chart, no data'),
|
|
101
|
-
).toBeInTheDocument()
|
|
121
|
+
expect(screen.getByLabelText('Sparkline chart, no data')).toBeInTheDocument()
|
|
102
122
|
})
|
|
103
123
|
|
|
104
124
|
describe('accessibility', () => {
|
|
105
125
|
it('has no accessibility violations', async () => {
|
|
106
|
-
const { container } = render(
|
|
107
|
-
<Sparkline data={sampleData} showDots highlightLast />,
|
|
108
|
-
)
|
|
126
|
+
const { container } = render(<Sparkline data={sampleData} showDots highlightLast />)
|
|
109
127
|
const results = await axe(container)
|
|
110
128
|
expect(results).toHaveNoViolations()
|
|
111
129
|
})
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
// Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
|
|
2
|
-
import
|
|
3
|
-
import { View, Text, type ViewProps } from 'react-native'
|
|
2
|
+
import { View, type ViewProps } from 'react-native'
|
|
4
3
|
import { cn } from '../../../utils/cn'
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
const BRAND_PRIMARY = getSemanticColors('dark')['brand-primary']
|
|
4
|
+
import { Typography } from '../Typography'
|
|
5
|
+
import { resolveColor } from '../../../theme/resolve-color'
|
|
8
6
|
|
|
9
7
|
export interface SparklineProps extends ViewProps {
|
|
10
8
|
data: number[]
|
|
@@ -22,10 +20,7 @@ export interface SparklineProps extends ViewProps {
|
|
|
22
20
|
className?: string
|
|
23
21
|
}
|
|
24
22
|
|
|
25
|
-
function normalizeData(
|
|
26
|
-
data: number[],
|
|
27
|
-
height: number,
|
|
28
|
-
): number[] {
|
|
23
|
+
function normalizeData(data: number[], height: number): number[] {
|
|
29
24
|
if (data.length === 0) return []
|
|
30
25
|
const min = Math.min(...data)
|
|
31
26
|
const max = Math.max(...data)
|
|
@@ -33,11 +28,7 @@ function normalizeData(
|
|
|
33
28
|
return data.map((v) => height - ((v - min) / range) * height)
|
|
34
29
|
}
|
|
35
30
|
|
|
36
|
-
function normalizeValue(
|
|
37
|
-
value: number,
|
|
38
|
-
data: number[],
|
|
39
|
-
height: number,
|
|
40
|
-
): number {
|
|
31
|
+
function normalizeValue(value: number, data: number[], height: number): number {
|
|
41
32
|
const min = Math.min(...data)
|
|
42
33
|
const max = Math.max(...data)
|
|
43
34
|
const range = max - min || 1
|
|
@@ -67,7 +58,7 @@ export function Sparkline({
|
|
|
67
58
|
)
|
|
68
59
|
}
|
|
69
60
|
|
|
70
|
-
const resolvedColor = color ??
|
|
61
|
+
const resolvedColor = color ?? resolveColor('brand-primary')
|
|
71
62
|
const normalized = normalizeData(data, height)
|
|
72
63
|
const stepX = data.length > 1 ? width / (data.length - 1) : 0
|
|
73
64
|
|
|
@@ -112,19 +103,24 @@ export function Sparkline({
|
|
|
112
103
|
testID={`sparkline-reference-${i}`}
|
|
113
104
|
>
|
|
114
105
|
{line.label && (
|
|
115
|
-
|
|
106
|
+
// `3xs` (9px) is the scale floor; the label was 7px, which is off it
|
|
107
|
+
// entirely (TOKENS.md §4). The line box stays unpinned, as the raw
|
|
108
|
+
// <Text> this replaced was, so the absolute offset still lands.
|
|
109
|
+
<Typography
|
|
110
|
+
variant="caption"
|
|
111
|
+
color="inherit"
|
|
112
|
+
className="text-3xs leading-[normal]"
|
|
116
113
|
style={{
|
|
117
114
|
position: 'absolute',
|
|
118
115
|
right: 0,
|
|
119
116
|
top: -10,
|
|
120
|
-
fontSize: 7,
|
|
121
117
|
color: line.color,
|
|
122
118
|
opacity: 1,
|
|
123
119
|
}}
|
|
124
120
|
testID={`sparkline-reference-label-${i}`}
|
|
125
121
|
>
|
|
126
122
|
{line.label}
|
|
127
|
-
</
|
|
123
|
+
</Typography>
|
|
128
124
|
)}
|
|
129
125
|
</View>
|
|
130
126
|
)
|
|
@@ -2,6 +2,16 @@ 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 { StatusDot } from './StatusDot'
|
|
5
|
+
import { getSemanticColors } from '../../../theme/tokens/semantic'
|
|
6
|
+
import { greyRamp } from '../../../theme/tokens/primitives'
|
|
7
|
+
import { hexToRgb } from '../../../theme/color-utils'
|
|
8
|
+
import { resolveColor } from '../../../theme/resolve-color'
|
|
9
|
+
|
|
10
|
+
/** The `rgba(r, g, b` prefix getGlowShadow emits for a token colour. */
|
|
11
|
+
function glowRgb(hex: string): string {
|
|
12
|
+
const { r, g, b } = hexToRgb(hex)!
|
|
13
|
+
return `rgba(${r}, ${g}, ${b}`
|
|
14
|
+
}
|
|
5
15
|
|
|
6
16
|
describe('StatusDot', () => {
|
|
7
17
|
it('renders a dot', () => {
|
|
@@ -50,6 +60,23 @@ describe('StatusDot', () => {
|
|
|
50
60
|
})
|
|
51
61
|
|
|
52
62
|
describe('solid variants', () => {
|
|
63
|
+
it.each([
|
|
64
|
+
['success', 'status-success'],
|
|
65
|
+
['warning', 'status-warning'],
|
|
66
|
+
['error', 'status-error'],
|
|
67
|
+
['neutral', 'text-tertiary'],
|
|
68
|
+
] as const)('fills the %s dot from the %s token', (variant, token) => {
|
|
69
|
+
render(<StatusDot variant={variant} />)
|
|
70
|
+
expect(screen.getByTestId('status-dot')).toHaveStyle({
|
|
71
|
+
backgroundColor: resolveColor(token),
|
|
72
|
+
})
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('draws the glyph on a solid fill in text-inverse', () => {
|
|
76
|
+
render(<StatusDot variant="success" size="md" icon="check" />)
|
|
77
|
+
expect(screen.getByText('✓')).toHaveStyle({ color: resolveColor('text-inverse') })
|
|
78
|
+
})
|
|
79
|
+
|
|
53
80
|
it('renders all solid variant types without error', () => {
|
|
54
81
|
const { rerender } = render(<StatusDot variant="success" />)
|
|
55
82
|
expect(screen.getByTestId('status-dot')).toBeInTheDocument()
|
|
@@ -84,6 +111,18 @@ describe('StatusDot', () => {
|
|
|
84
111
|
expect(screen.getByLabelText('future status')).toBeInTheDocument()
|
|
85
112
|
})
|
|
86
113
|
|
|
114
|
+
// The rim is a whole rung above the wash. Asserting both rules out the
|
|
115
|
+
// flattening that collapses a two-step ring onto one `-subtle`.
|
|
116
|
+
it.each([
|
|
117
|
+
['on-track', 'status-success'],
|
|
118
|
+
['deviation', 'status-warning'],
|
|
119
|
+
] as const)('washes the %s ring with %s -subtle inside a -muted rim', (variant, role) => {
|
|
120
|
+
render(<StatusDot variant={variant} />)
|
|
121
|
+
const dot = screen.getByTestId('status-dot')
|
|
122
|
+
expect(dot).toHaveStyle({ backgroundColor: resolveColor(`${role}-subtle`) })
|
|
123
|
+
expect(dot.style.borderTopColor).toBe(resolveColor(`${role}-muted`))
|
|
124
|
+
})
|
|
125
|
+
|
|
87
126
|
it('renders the future variant with a 1px dashed border', () => {
|
|
88
127
|
render(<StatusDot variant="future" />)
|
|
89
128
|
const dot = screen.getByTestId('status-dot')
|
|
@@ -103,19 +142,19 @@ describe('StatusDot', () => {
|
|
|
103
142
|
it('applies a box-shadow when glow is set on a solid variant', () => {
|
|
104
143
|
render(<StatusDot variant="success" glow />)
|
|
105
144
|
const dot = screen.getByTestId('status-dot')
|
|
106
|
-
expect(dot.style.boxShadow).toContain('
|
|
145
|
+
expect(dot.style.boxShadow).toContain(glowRgb(getSemanticColors('dark')['status-success']))
|
|
107
146
|
})
|
|
108
147
|
|
|
109
148
|
it('applies a box-shadow when glow is set on a ring variant', () => {
|
|
110
149
|
render(<StatusDot variant="on-track" glow />)
|
|
111
150
|
const dot = screen.getByTestId('status-dot')
|
|
112
|
-
expect(dot.style.boxShadow).toContain('
|
|
151
|
+
expect(dot.style.boxShadow).toContain(glowRgb(getSemanticColors('dark')['status-success']))
|
|
113
152
|
})
|
|
114
153
|
|
|
115
154
|
it('applies a gray box-shadow when glow is set on the future variant', () => {
|
|
116
155
|
render(<StatusDot variant="future" glow />)
|
|
117
156
|
const dot = screen.getByTestId('status-dot')
|
|
118
|
-
expect(dot.style.boxShadow).toContain(
|
|
157
|
+
expect(dot.style.boxShadow).toContain(glowRgb(greyRamp[500]))
|
|
119
158
|
})
|
|
120
159
|
|
|
121
160
|
it('does not apply a box-shadow when glow is not set', () => {
|
|
@@ -128,7 +167,7 @@ describe('StatusDot', () => {
|
|
|
128
167
|
describe('accessibility', () => {
|
|
129
168
|
it('has no accessibility violations', async () => {
|
|
130
169
|
const { container } = render(
|
|
131
|
-
<StatusDot variant="success" size="md" icon="check" label="On track"
|
|
170
|
+
<StatusDot variant="success" size="md" icon="check" label="On track" />
|
|
132
171
|
)
|
|
133
172
|
const results = await axe(container)
|
|
134
173
|
expect(results).toHaveNoViolations()
|
|
@@ -136,7 +175,7 @@ describe('StatusDot', () => {
|
|
|
136
175
|
|
|
137
176
|
it('has no accessibility violations for ring variants', async () => {
|
|
138
177
|
const { container } = render(
|
|
139
|
-
<StatusDot variant="on-track" size="md" icon="check" label="On track"
|
|
178
|
+
<StatusDot variant="on-track" size="md" icon="check" label="On track" />
|
|
140
179
|
)
|
|
141
180
|
const results = await axe(container)
|
|
142
181
|
expect(results).toHaveNoViolations()
|