@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,9 +1,36 @@
|
|
|
1
1
|
import React, { createContext, useContext, useState, useMemo } from 'react'
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
View,
|
|
4
|
+
Text,
|
|
5
|
+
Pressable,
|
|
6
|
+
ScrollView,
|
|
7
|
+
type ViewProps,
|
|
8
|
+
type PressableProps,
|
|
9
|
+
} from 'react-native'
|
|
3
10
|
import { cn } from '../../../utils/cn'
|
|
11
|
+
import { Tooltip } from '../../ui/tooltip'
|
|
4
12
|
|
|
5
13
|
export type SortDirection = 'asc' | 'desc' | null
|
|
6
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Row height / cell padding axis. `comfortable` is the default reading density;
|
|
17
|
+
* `dense` is the scannable-grid density for tables whose job is to fit many rows
|
|
18
|
+
* on screen at once (a backlog, a log, an audit list).
|
|
19
|
+
*/
|
|
20
|
+
export type TableDensity = 'comfortable' | 'dense'
|
|
21
|
+
|
|
22
|
+
/** Cell padding per density, shared by header cells and data cells so a row's two halves cannot drift. */
|
|
23
|
+
const CELL_PADDING: Record<TableDensity, string> = {
|
|
24
|
+
comfortable: 'px-4 py-3',
|
|
25
|
+
dense: 'px-2 py-1',
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// minWidth 0 lets a flexible cell shrink below its nowrap text, so long content truncates instead of pushing the row past the table.
|
|
29
|
+
const FLEX_CELL = { flex: 1, minWidth: 0 } as const
|
|
30
|
+
|
|
31
|
+
// A horizontal ScrollView sizes its content to max-content; a definite width makes the table fill the viewport and flexible cells shrink.
|
|
32
|
+
const SCROLL_CONTENT = { width: '100%' } as const
|
|
33
|
+
|
|
7
34
|
interface TableContextType {
|
|
8
35
|
sortColumn?: string
|
|
9
36
|
sortDirection: SortDirection
|
|
@@ -13,6 +40,7 @@ interface TableContextType {
|
|
|
13
40
|
onSelectAll?: (selected: boolean) => void
|
|
14
41
|
selectable: boolean
|
|
15
42
|
allRowIds: string[]
|
|
43
|
+
density: TableDensity
|
|
16
44
|
}
|
|
17
45
|
|
|
18
46
|
const TableContext = createContext<TableContextType>({
|
|
@@ -20,6 +48,7 @@ const TableContext = createContext<TableContextType>({
|
|
|
20
48
|
selectedRows: new Set(),
|
|
21
49
|
selectable: false,
|
|
22
50
|
allRowIds: [],
|
|
51
|
+
density: 'comfortable',
|
|
23
52
|
})
|
|
24
53
|
|
|
25
54
|
export interface TableProps extends ViewProps {
|
|
@@ -43,6 +72,14 @@ export interface TableProps extends ViewProps {
|
|
|
43
72
|
isLoading?: boolean
|
|
44
73
|
/** Number of skeleton rows to show when loading */
|
|
45
74
|
loadingRowCount?: number
|
|
75
|
+
/** Row height / cell padding. Defaults to `comfortable`. */
|
|
76
|
+
density?: TableDensity
|
|
77
|
+
/**
|
|
78
|
+
* Width the columns need, below which the table scrolls horizontally instead
|
|
79
|
+
* of squeezing them into overlap. Pair with {@link useColumnFit}, which drops
|
|
80
|
+
* columns first and reports what the survivors need.
|
|
81
|
+
*/
|
|
82
|
+
contentMinWidth?: number
|
|
46
83
|
/** Additional className */
|
|
47
84
|
className?: string
|
|
48
85
|
children?: React.ReactNode
|
|
@@ -82,10 +119,19 @@ export function Table({
|
|
|
82
119
|
rowIds = [],
|
|
83
120
|
isLoading = false,
|
|
84
121
|
loadingRowCount = 5,
|
|
122
|
+
density = 'comfortable',
|
|
123
|
+
contentMinWidth,
|
|
85
124
|
className,
|
|
86
125
|
children,
|
|
87
126
|
...props
|
|
88
127
|
}: TableProps) {
|
|
128
|
+
// width 100% + a minWidth floor resolves to max(container, floor): the table fills its
|
|
129
|
+
// container, and once the columns need more than that it overflows and scrolls.
|
|
130
|
+
const contentStyle =
|
|
131
|
+
contentMinWidth === undefined
|
|
132
|
+
? SCROLL_CONTENT
|
|
133
|
+
: { ...SCROLL_CONTENT, minWidth: contentMinWidth }
|
|
134
|
+
|
|
89
135
|
return (
|
|
90
136
|
<TableContext.Provider
|
|
91
137
|
value={{
|
|
@@ -97,16 +143,17 @@ export function Table({
|
|
|
97
143
|
onSelectAll,
|
|
98
144
|
selectable,
|
|
99
145
|
allRowIds: rowIds,
|
|
146
|
+
density,
|
|
100
147
|
}}
|
|
101
148
|
>
|
|
102
149
|
<View className={cn('w-full', className)} {...props}>
|
|
103
|
-
<ScrollView
|
|
150
|
+
<ScrollView
|
|
151
|
+
horizontal
|
|
152
|
+
showsHorizontalScrollIndicator={contentMinWidth !== undefined}
|
|
153
|
+
contentContainerStyle={contentStyle}
|
|
154
|
+
>
|
|
104
155
|
<View role="table" className="w-full min-w-full">
|
|
105
|
-
{isLoading ?
|
|
106
|
-
<TableLoadingSkeleton rowCount={loadingRowCount} />
|
|
107
|
-
) : (
|
|
108
|
-
children
|
|
109
|
-
)}
|
|
156
|
+
{isLoading ? <TableLoadingSkeleton rowCount={loadingRowCount} /> : children}
|
|
110
157
|
</View>
|
|
111
158
|
</ScrollView>
|
|
112
159
|
</View>
|
|
@@ -124,10 +171,7 @@ export interface TableHeaderProps {
|
|
|
124
171
|
*/
|
|
125
172
|
export function TableHeader({ children, className }: TableHeaderProps) {
|
|
126
173
|
return (
|
|
127
|
-
<View
|
|
128
|
-
role="rowgroup"
|
|
129
|
-
className={cn('bg-background-subtle rounded-t-lg', className)}
|
|
130
|
-
>
|
|
174
|
+
<View role="rowgroup" className={cn('bg-background-subtle rounded-t-lg', className)}>
|
|
131
175
|
{children}
|
|
132
176
|
</View>
|
|
133
177
|
)
|
|
@@ -188,6 +232,8 @@ export function TableRow({
|
|
|
188
232
|
export interface TableHeaderCellProps extends Omit<PressableProps, 'children'> {
|
|
189
233
|
/** Sort key for this column */
|
|
190
234
|
sortKey?: string
|
|
235
|
+
/** Full column name behind an abbreviated label: shown on hover and used as the accessible sort name. */
|
|
236
|
+
tooltip?: string
|
|
191
237
|
/** Text alignment */
|
|
192
238
|
align?: 'left' | 'center' | 'right'
|
|
193
239
|
/** Cell width in pixels */
|
|
@@ -202,6 +248,7 @@ export interface TableHeaderCellProps extends Omit<PressableProps, 'children'> {
|
|
|
202
248
|
*/
|
|
203
249
|
export function TableHeaderCell({
|
|
204
250
|
sortKey,
|
|
251
|
+
tooltip,
|
|
205
252
|
align = 'left',
|
|
206
253
|
width,
|
|
207
254
|
className,
|
|
@@ -209,9 +256,11 @@ export function TableHeaderCell({
|
|
|
209
256
|
onPress,
|
|
210
257
|
...props
|
|
211
258
|
}: TableHeaderCellProps) {
|
|
212
|
-
const { sortColumn, sortDirection, onSort } = useContext(TableContext)
|
|
213
|
-
|
|
259
|
+
const { sortColumn, sortDirection, onSort, density } = useContext(TableContext)
|
|
260
|
+
// The sort cycle ends at direction null with the column still set; that is unsorted, not "still descending".
|
|
261
|
+
const isSorted = !!sortKey && sortColumn === sortKey && sortDirection != null
|
|
214
262
|
const isSortable = !!sortKey && !!onSort
|
|
263
|
+
const [hovered, setHovered] = useState(false)
|
|
215
264
|
const ariaSort = isSorted
|
|
216
265
|
? sortDirection === 'asc'
|
|
217
266
|
? 'ascending'
|
|
@@ -233,18 +282,36 @@ export function TableHeaderCell({
|
|
|
233
282
|
right: 'justify-end',
|
|
234
283
|
}
|
|
235
284
|
|
|
285
|
+
const label = (
|
|
286
|
+
<Text
|
|
287
|
+
className={cn(
|
|
288
|
+
'text-xs font-semibold uppercase tracking-wider text-text-secondary',
|
|
289
|
+
isSorted && 'text-text-primary'
|
|
290
|
+
)}
|
|
291
|
+
>
|
|
292
|
+
{children}
|
|
293
|
+
</Text>
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
// The tooltip wraps the sort button rather than sitting inside it: a nested Pressable would take the press.
|
|
297
|
+
const withTooltip = (node: React.ReactNode) =>
|
|
298
|
+
tooltip ? (
|
|
299
|
+
<Tooltip label={tooltip} usePortal>
|
|
300
|
+
{node}
|
|
301
|
+
</Tooltip>
|
|
302
|
+
) : (
|
|
303
|
+
node
|
|
304
|
+
)
|
|
305
|
+
|
|
236
306
|
const content = (
|
|
237
307
|
<>
|
|
238
|
-
|
|
239
|
-
className={cn(
|
|
240
|
-
'text-xs font-semibold uppercase tracking-wider text-text-secondary',
|
|
241
|
-
isSorted && 'text-text-primary'
|
|
242
|
-
)}
|
|
243
|
-
>
|
|
244
|
-
{children}
|
|
245
|
-
</Text>
|
|
308
|
+
{label}
|
|
246
309
|
{isSortable && (
|
|
247
|
-
|
|
310
|
+
// Idle glyphs stay in layout but invisible, so the header reads quiet and nothing shifts on hover.
|
|
311
|
+
<Text
|
|
312
|
+
className={cn('ml-1 text-xs', isSorted ? 'text-text-primary' : 'text-text-tertiary')}
|
|
313
|
+
style={{ opacity: isSorted || hovered ? 1 : 0 }}
|
|
314
|
+
>
|
|
248
315
|
{isSorted ? (sortDirection === 'asc' ? '↑' : '↓') : '↕'}
|
|
249
316
|
</Text>
|
|
250
317
|
)}
|
|
@@ -258,22 +325,29 @@ export function TableHeaderCell({
|
|
|
258
325
|
<View
|
|
259
326
|
role="columnheader"
|
|
260
327
|
aria-sort={ariaSort}
|
|
261
|
-
|
|
328
|
+
// A label and its sort glyph belong to one column: clipped is recoverable, painted over the neighbour is not.
|
|
329
|
+
className="overflow-hidden"
|
|
330
|
+
style={width ? { width } : FLEX_CELL}
|
|
262
331
|
>
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
332
|
+
{withTooltip(
|
|
333
|
+
<Pressable
|
|
334
|
+
accessibilityRole="button"
|
|
335
|
+
accessibilityLabel={`Sort by ${tooltip ?? children}`}
|
|
336
|
+
onPress={handlePress}
|
|
337
|
+
onHoverIn={() => setHovered(true)}
|
|
338
|
+
onHoverOut={() => setHovered(false)}
|
|
339
|
+
className={cn(
|
|
340
|
+
'flex-row items-center',
|
|
341
|
+
CELL_PADDING[density],
|
|
342
|
+
alignStyles[align],
|
|
343
|
+
'web:hover:bg-interactive-hover active:bg-interactive-active',
|
|
344
|
+
className
|
|
345
|
+
)}
|
|
346
|
+
{...props}
|
|
347
|
+
>
|
|
348
|
+
{content}
|
|
349
|
+
</Pressable>
|
|
350
|
+
)}
|
|
277
351
|
</View>
|
|
278
352
|
)
|
|
279
353
|
}
|
|
@@ -281,14 +355,15 @@ export function TableHeaderCell({
|
|
|
281
355
|
return (
|
|
282
356
|
<View
|
|
283
357
|
role="columnheader"
|
|
284
|
-
style={width ? { width } :
|
|
358
|
+
style={width ? { width } : FLEX_CELL}
|
|
285
359
|
className={cn(
|
|
286
|
-
'flex-row items-center
|
|
360
|
+
'flex-row items-center overflow-hidden',
|
|
361
|
+
CELL_PADDING[density],
|
|
287
362
|
alignStyles[align],
|
|
288
363
|
className
|
|
289
364
|
)}
|
|
290
365
|
>
|
|
291
|
-
{content}
|
|
366
|
+
{withTooltip(content)}
|
|
292
367
|
</View>
|
|
293
368
|
)
|
|
294
369
|
}
|
|
@@ -313,6 +388,7 @@ export function TableCell({
|
|
|
313
388
|
children,
|
|
314
389
|
...props
|
|
315
390
|
}: TableCellProps) {
|
|
391
|
+
const { density } = useContext(TableContext)
|
|
316
392
|
const alignStyles = {
|
|
317
393
|
left: 'items-start',
|
|
318
394
|
center: 'items-center',
|
|
@@ -322,16 +398,14 @@ export function TableCell({
|
|
|
322
398
|
return (
|
|
323
399
|
<View
|
|
324
400
|
role="cell"
|
|
325
|
-
style={width ? { width } :
|
|
326
|
-
className={cn(
|
|
327
|
-
'justify-center px-4 py-3.5',
|
|
328
|
-
alignStyles[align],
|
|
329
|
-
className
|
|
330
|
-
)}
|
|
401
|
+
style={width ? { width } : FLEX_CELL}
|
|
402
|
+
className={cn('justify-center', CELL_PADDING[density], alignStyles[align], className)}
|
|
331
403
|
{...props}
|
|
332
404
|
>
|
|
333
405
|
{typeof children === 'string' ? (
|
|
334
|
-
<Text className=
|
|
406
|
+
<Text className={cn(density === 'dense' ? 'text-xs' : 'text-sm', 'text-text-primary')}>
|
|
407
|
+
{children}
|
|
408
|
+
</Text>
|
|
335
409
|
) : (
|
|
336
410
|
children
|
|
337
411
|
)}
|
|
@@ -394,9 +468,7 @@ export function TablePagination({
|
|
|
394
468
|
)}
|
|
395
469
|
>
|
|
396
470
|
<View className="flex-row items-center gap-2">
|
|
397
|
-
<Text className="text-sm text-text-secondary">
|
|
398
|
-
Rows per page:
|
|
399
|
-
</Text>
|
|
471
|
+
<Text className="text-sm text-text-secondary">Rows per page:</Text>
|
|
400
472
|
<View className="flex-row gap-1">
|
|
401
473
|
{pageSizeOptions.map((size) => (
|
|
402
474
|
<Pressable
|
|
@@ -463,11 +535,28 @@ export function TablePagination({
|
|
|
463
535
|
|
|
464
536
|
// Helper hook for managing table state
|
|
465
537
|
|
|
538
|
+
/**
|
|
539
|
+
* An ascending comparator for one column. Returning 0 lets the caller express a
|
|
540
|
+
* tie-break inside the same function; `useTable` inverts the result for `desc`
|
|
541
|
+
* rather than reversing the array, so ties keep their relative order both ways.
|
|
542
|
+
*/
|
|
543
|
+
export type TableComparator<T> = (a: T, b: T) => number
|
|
544
|
+
|
|
545
|
+
const isBlank = (v: unknown): boolean => v === null || v === undefined
|
|
546
|
+
|
|
466
547
|
export interface UseTableOptions<T> {
|
|
467
548
|
data: T[]
|
|
468
549
|
defaultPageSize?: number
|
|
469
550
|
defaultSortColumn?: string
|
|
470
551
|
defaultSortDirection?: SortDirection
|
|
552
|
+
/**
|
|
553
|
+
* Per-column ascending comparators, for columns whose order is not their raw
|
|
554
|
+
* field order — a severity ranked critical→low rather than alphabetically, a
|
|
555
|
+
* numeric field that should sort blanks last, a date read newest-first.
|
|
556
|
+
* Columns absent from the map fall back to the default field compare.
|
|
557
|
+
*/
|
|
558
|
+
comparators?: Partial<Record<keyof T & string, TableComparator<T>>> &
|
|
559
|
+
Record<string, TableComparator<T> | undefined>
|
|
471
560
|
}
|
|
472
561
|
|
|
473
562
|
export interface UseTableReturn<T> {
|
|
@@ -502,6 +591,7 @@ export function useTable<T extends Record<string, any>>({
|
|
|
502
591
|
defaultPageSize = 10,
|
|
503
592
|
defaultSortColumn,
|
|
504
593
|
defaultSortDirection = null,
|
|
594
|
+
comparators,
|
|
505
595
|
}: UseTableOptions<T>): UseTableReturn<T> {
|
|
506
596
|
const [page, setPage] = useState(0)
|
|
507
597
|
const [pageSize, setPageSize] = useState(defaultPageSize)
|
|
@@ -511,18 +601,26 @@ export function useTable<T extends Record<string, any>>({
|
|
|
511
601
|
const sortedData = useMemo(() => {
|
|
512
602
|
if (!sortColumn || !sortDirection) return data
|
|
513
603
|
|
|
604
|
+
const custom = comparators?.[sortColumn]
|
|
605
|
+
// Invert rather than reverse: reversing an already-sorted array also flips
|
|
606
|
+
// tied rows, so equal values would shuffle every time direction changed.
|
|
607
|
+
const sign = sortDirection === 'asc' ? 1 : -1
|
|
608
|
+
|
|
609
|
+
if (custom) return [...data].sort((a, b) => sign * custom(a, b))
|
|
610
|
+
|
|
514
611
|
return [...data].sort((a, b) => {
|
|
515
612
|
const aVal = a[sortColumn]
|
|
516
613
|
const bVal = b[sortColumn]
|
|
517
614
|
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
615
|
+
// Blanks rank last in BOTH directions — outside the sign, so a missing
|
|
616
|
+
// value never masquerades as the smallest one when the column flips.
|
|
617
|
+
const blanks = isBlank(aVal) ? (isBlank(bVal) ? 0 : 1) : isBlank(bVal) ? -1 : 0
|
|
618
|
+
if (blanks !== 0) return blanks
|
|
521
619
|
|
|
522
|
-
|
|
523
|
-
return
|
|
620
|
+
if (aVal === bVal) return 0
|
|
621
|
+
return sign * (aVal < bVal ? -1 : 1)
|
|
524
622
|
})
|
|
525
|
-
}, [data, sortColumn, sortDirection])
|
|
623
|
+
}, [data, sortColumn, sortDirection, comparators])
|
|
526
624
|
|
|
527
625
|
const paginatedData = useMemo(() => {
|
|
528
626
|
const start = page * pageSize
|
|
@@ -532,9 +630,7 @@ export function useTable<T extends Record<string, any>>({
|
|
|
532
630
|
const handleSort = (column: string) => {
|
|
533
631
|
if (sortColumn === column) {
|
|
534
632
|
// Cycle: asc -> desc -> null
|
|
535
|
-
setSortDirection((prev) =>
|
|
536
|
-
prev === 'asc' ? 'desc' : prev === 'desc' ? null : 'asc'
|
|
537
|
-
)
|
|
633
|
+
setSortDirection((prev) => (prev === 'asc' ? 'desc' : prev === 'desc' ? null : 'asc'))
|
|
538
634
|
} else {
|
|
539
635
|
setSortColumn(column)
|
|
540
636
|
setSortDirection('asc')
|
|
@@ -589,14 +685,12 @@ export function TableSelectAllCell({ className }: TableSelectAllCellProps) {
|
|
|
589
685
|
allSelected
|
|
590
686
|
? 'bg-brand-primary border-brand-primary'
|
|
591
687
|
: someSelected
|
|
592
|
-
|
|
593
|
-
|
|
688
|
+
? 'bg-brand-primary-subtle border-brand-primary'
|
|
689
|
+
: 'border-hairline-strong bg-transparent'
|
|
594
690
|
)}
|
|
595
691
|
>
|
|
596
692
|
{(allSelected || someSelected) && (
|
|
597
|
-
<Text className="text-white text-xs font-bold">
|
|
598
|
-
{allSelected ? '✓' : '−'}
|
|
599
|
-
</Text>
|
|
693
|
+
<Text className="text-white text-xs font-bold">{allSelected ? '✓' : '−'}</Text>
|
|
600
694
|
)}
|
|
601
695
|
</Pressable>
|
|
602
696
|
</View>
|
|
@@ -632,9 +726,7 @@ export function TableSelectCell({ rowId, className }: TableSelectCellProps) {
|
|
|
632
726
|
: 'border-hairline-strong bg-transparent web:hover:border-brand-primary'
|
|
633
727
|
)}
|
|
634
728
|
>
|
|
635
|
-
{isSelected &&
|
|
636
|
-
<Text className="text-white text-xs font-bold">✓</Text>
|
|
637
|
-
)}
|
|
729
|
+
{isSelected && <Text className="text-white text-xs font-bold">✓</Text>}
|
|
638
730
|
</Pressable>
|
|
639
731
|
</View>
|
|
640
732
|
)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { fitColumns, type TableColumnFit } from './column-fit'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Five 100px columns: two that must survive, three that drop in a declared
|
|
6
|
+
* order. Every width below is picked to land on one step of that order.
|
|
7
|
+
*/
|
|
8
|
+
const COLUMNS: TableColumnFit[] = [
|
|
9
|
+
{ key: 'id', minWidth: 100 },
|
|
10
|
+
{ key: 'title', minWidth: 100 },
|
|
11
|
+
{ key: 'sev', minWidth: 100, dropPriority: 1 },
|
|
12
|
+
{ key: 'pri', minWidth: 100, dropPriority: 2 },
|
|
13
|
+
{ key: 'tags', minWidth: 100, dropPriority: 3 },
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
const fitAt = (available: number | null) => fitColumns(COLUMNS, available)
|
|
17
|
+
|
|
18
|
+
describe('fitColumns', () => {
|
|
19
|
+
it('keeps every column until one has been measured', () => {
|
|
20
|
+
expect(fitAt(null).visibleKeys).toEqual(['id', 'title', 'sev', 'pri', 'tags'])
|
|
21
|
+
expect(fitAt(null).isScrolling).toBe(false)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('keeps all five columns at a width that fits them', () => {
|
|
25
|
+
expect(fitAt(500).visibleKeys).toEqual(['id', 'title', 'sev', 'pri', 'tags'])
|
|
26
|
+
expect(fitAt(500).contentMinWidth).toBe(500)
|
|
27
|
+
expect(fitAt(500).isScrolling).toBe(false)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('drops the first column in the order when four fit', () => {
|
|
31
|
+
expect(fitAt(450).visibleKeys).toEqual(['id', 'title', 'pri', 'tags'])
|
|
32
|
+
expect(fitAt(450).contentMinWidth).toBe(400)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('drops the second when three fit', () => {
|
|
36
|
+
expect(fitAt(350).visibleKeys).toEqual(['id', 'title', 'tags'])
|
|
37
|
+
expect(fitAt(350).contentMinWidth).toBe(300)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('drops the third when only two fit', () => {
|
|
41
|
+
expect(fitAt(250).visibleKeys).toEqual(['id', 'title'])
|
|
42
|
+
expect(fitAt(250).contentMinWidth).toBe(200)
|
|
43
|
+
expect(fitAt(250).isScrolling).toBe(false)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('stops dropping at the undroppable columns and scrolls instead', () => {
|
|
47
|
+
const fit = fitAt(150)
|
|
48
|
+
expect(fit.visibleKeys).toEqual(['id', 'title'])
|
|
49
|
+
expect(fit.contentMinWidth).toBe(200)
|
|
50
|
+
expect(fit.isScrolling).toBe(true)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('drops one column at a time rather than everything droppable at once', () => {
|
|
54
|
+
// 450 fits four columns; a fit that dropped by category would leave two.
|
|
55
|
+
expect(fitAt(450).visibleKeys).toHaveLength(4)
|
|
56
|
+
expect(fitAt(350).visibleKeys).toHaveLength(3)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('breaks a tied priority by declaration order', () => {
|
|
60
|
+
const tied: TableColumnFit[] = [
|
|
61
|
+
{ key: 'keep', minWidth: 100 },
|
|
62
|
+
{ key: 'left', minWidth: 100, dropPriority: 1 },
|
|
63
|
+
{ key: 'right', minWidth: 100, dropPriority: 1 },
|
|
64
|
+
]
|
|
65
|
+
expect(fitColumns(tied, 200).visibleKeys).toEqual(['keep', 'right'])
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it('reports what the survivors need, which is what the table scrolls to', () => {
|
|
69
|
+
expect(fitColumns([{ key: 'only', minWidth: 320 }], 100)).toEqual({
|
|
70
|
+
visibleKeys: ['only'],
|
|
71
|
+
contentMinWidth: 320,
|
|
72
|
+
isScrolling: true,
|
|
73
|
+
})
|
|
74
|
+
})
|
|
75
|
+
})
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { useCallback, useMemo, useState } from 'react'
|
|
2
|
+
import type { LayoutChangeEvent } from 'react-native'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* One column's terms for the fit calculation: how much room it needs, and where
|
|
6
|
+
* it sits in the drop order.
|
|
7
|
+
*/
|
|
8
|
+
export interface TableColumnFit {
|
|
9
|
+
/** Matches the key the caller renders the column under. */
|
|
10
|
+
key: string
|
|
11
|
+
/**
|
|
12
|
+
* Narrowest width in px at which the column still reads — its header label
|
|
13
|
+
* plus the sort affordance, or its content, whichever is wider. Real numbers
|
|
14
|
+
* matter here: the fit is arithmetic over these, not a guess at a breakpoint.
|
|
15
|
+
*/
|
|
16
|
+
minWidth: number
|
|
17
|
+
/**
|
|
18
|
+
* Drop order: the lowest number leaves first. A column with no `dropPriority`
|
|
19
|
+
* never drops, which is what keeps the identifying columns on screen when the
|
|
20
|
+
* table runs out of room.
|
|
21
|
+
*/
|
|
22
|
+
dropPriority?: number
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface ColumnFitResult {
|
|
26
|
+
/** The keys still rendered, in declaration order. */
|
|
27
|
+
visibleKeys: string[]
|
|
28
|
+
/** What the survivors need. Hand to `Table`'s `contentMinWidth`. */
|
|
29
|
+
contentMinWidth: number
|
|
30
|
+
/** Nothing droppable is left and the survivors still overflow: the table scrolls. */
|
|
31
|
+
isScrolling: boolean
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Ties keep declaration order, so a duplicated priority drops left to right rather than at random.
|
|
35
|
+
function dropOrder(columns: TableColumnFit[]): TableColumnFit[] {
|
|
36
|
+
return columns
|
|
37
|
+
.map((column, index) => ({ column, index }))
|
|
38
|
+
.filter((entry) => entry.column.dropPriority !== undefined)
|
|
39
|
+
.sort((a, b) => a.column.dropPriority! - b.column.dropPriority! || a.index - b.index)
|
|
40
|
+
.map((entry) => entry.column)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Drops columns one at a time in the declared order until the rest fit, then
|
|
45
|
+
* stops. Dropping cannot take an undroppable column, so once those are all that
|
|
46
|
+
* is left the table overflows on purpose and scrolls horizontally — scroll is
|
|
47
|
+
* the floor under dropping, not an alternative to it.
|
|
48
|
+
*
|
|
49
|
+
* `available` of `null` means "not measured yet": everything renders.
|
|
50
|
+
*/
|
|
51
|
+
export function fitColumns(columns: TableColumnFit[], available: number | null): ColumnFitResult {
|
|
52
|
+
const dropped = new Set<string>()
|
|
53
|
+
let needed = columns.reduce((total, column) => total + column.minWidth, 0)
|
|
54
|
+
|
|
55
|
+
if (available !== null) {
|
|
56
|
+
for (const column of dropOrder(columns)) {
|
|
57
|
+
if (needed <= available) break
|
|
58
|
+
dropped.add(column.key)
|
|
59
|
+
needed -= column.minWidth
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
visibleKeys: columns.filter((column) => !dropped.has(column.key)).map((column) => column.key),
|
|
65
|
+
contentMinWidth: needed,
|
|
66
|
+
isScrolling: available !== null && needed > available,
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface UseMeasuredWidthResult {
|
|
71
|
+
/** The measured width, or `null` until the first layout pass. */
|
|
72
|
+
width: number | null
|
|
73
|
+
onLayout: (event: LayoutChangeEvent) => void
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* The width of whatever it is attached to, from the `onLayout` pass every other
|
|
78
|
+
* width-aware table here already uses — no resize observer, and no window
|
|
79
|
+
* width standing in for a column that lives inside a much narrower card.
|
|
80
|
+
*
|
|
81
|
+
* `override` pins the width instead of measuring, so a story or a test can hold
|
|
82
|
+
* one breakpoint still.
|
|
83
|
+
*/
|
|
84
|
+
export function useMeasuredWidth(override?: number): UseMeasuredWidthResult {
|
|
85
|
+
const [measured, setMeasured] = useState<number | null>(null)
|
|
86
|
+
const onLayout = useCallback(
|
|
87
|
+
(event: LayoutChangeEvent) => setMeasured(event.nativeEvent.layout.width),
|
|
88
|
+
[]
|
|
89
|
+
)
|
|
90
|
+
return { width: override ?? measured, onLayout }
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export interface UseColumnFitResult extends ColumnFitResult {
|
|
94
|
+
isVisible: (key: string) => boolean
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* {@link fitColumns} as a hook, with a membership test for the render pass.
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* const { width, onLayout } = useMeasuredWidth()
|
|
102
|
+
* const fit = useColumnFit(COLUMNS, width)
|
|
103
|
+
* <View onLayout={onLayout}>
|
|
104
|
+
* <Table contentMinWidth={fit.contentMinWidth}>
|
|
105
|
+
* {COLUMNS.filter((c) => fit.isVisible(c.key)).map(renderColumn)}
|
|
106
|
+
* </Table>
|
|
107
|
+
* </View>
|
|
108
|
+
*/
|
|
109
|
+
export function useColumnFit(
|
|
110
|
+
columns: TableColumnFit[],
|
|
111
|
+
available: number | null
|
|
112
|
+
): UseColumnFitResult {
|
|
113
|
+
const fit = useMemo(() => fitColumns(columns, available), [columns, available])
|
|
114
|
+
const visible = useMemo(() => new Set(fit.visibleKeys), [fit])
|
|
115
|
+
return { ...fit, isVisible: (key: string) => visible.has(key) }
|
|
116
|
+
}
|
|
@@ -19,4 +19,13 @@ export type {
|
|
|
19
19
|
UseTableOptions,
|
|
20
20
|
UseTableReturn,
|
|
21
21
|
SortDirection,
|
|
22
|
+
TableDensity,
|
|
23
|
+
TableComparator,
|
|
22
24
|
} from './Table'
|
|
25
|
+
export { fitColumns, useColumnFit, useMeasuredWidth } from './column-fit'
|
|
26
|
+
export type {
|
|
27
|
+
TableColumnFit,
|
|
28
|
+
ColumnFitResult,
|
|
29
|
+
UseColumnFitResult,
|
|
30
|
+
UseMeasuredWidthResult,
|
|
31
|
+
} from './column-fit'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
-
import { View } from 'react-native'
|
|
3
2
|
import { TimerReadout } from './TimerReadout'
|
|
3
|
+
import { Surface } from '../../ui/surface'
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* `TimerReadout` — a small textual timer (⏱ + mono, right-justified) built on the
|
|
@@ -8,21 +8,23 @@ import { TimerReadout } from './TimerReadout'
|
|
|
8
8
|
* `running`; the ` / total` suffix stays dim tertiary.
|
|
9
9
|
*/
|
|
10
10
|
const meta: Meta<typeof TimerReadout> = {
|
|
11
|
-
title: '
|
|
11
|
+
title: 'Components/Molecules/TimerReadout',
|
|
12
12
|
component: TimerReadout,
|
|
13
|
-
tags: ['autodocs'],
|
|
13
|
+
tags: ['autodocs', 'status:candidate', '!status:review'],
|
|
14
14
|
parameters: {
|
|
15
15
|
docs: {
|
|
16
16
|
description: {
|
|
17
|
-
component:
|
|
17
|
+
component:
|
|
18
|
+
'**Molecule.** A small textual timer on [useTimer]. Used-by ↑ SessionHeader.' +
|
|
19
|
+
' Composes **Typography**.',
|
|
18
20
|
},
|
|
19
21
|
},
|
|
20
22
|
},
|
|
21
23
|
decorators: [
|
|
22
24
|
(Story) => (
|
|
23
|
-
<
|
|
25
|
+
<Surface level="base" style={{ padding: 16, alignItems: 'flex-end', width: 200 }}>
|
|
24
26
|
<Story />
|
|
25
|
-
</
|
|
27
|
+
</Surface>
|
|
26
28
|
),
|
|
27
29
|
],
|
|
28
30
|
}
|
|
@@ -2,9 +2,9 @@ import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
|
2
2
|
import { Treemap, type TreemapDatum } from './Treemap'
|
|
3
3
|
|
|
4
4
|
const meta: Meta<typeof Treemap> = {
|
|
5
|
-
title: '
|
|
5
|
+
title: 'Custom/Charts/Treemap',
|
|
6
6
|
component: Treemap,
|
|
7
|
-
tags: ['autodocs'],
|
|
7
|
+
tags: ['autodocs', 'status:candidate', '!status:review'],
|
|
8
8
|
argTypes: {
|
|
9
9
|
width: { control: { type: 'range', min: 200, max: 900, step: 20 } },
|
|
10
10
|
height: { control: { type: 'range', min: 120, max: 600, step: 20 } },
|
|
@@ -47,7 +47,7 @@ describe('Treemap', () => {
|
|
|
47
47
|
|
|
48
48
|
it('assigns fallback palette colors when omitted', () => {
|
|
49
49
|
const { container } = render(
|
|
50
|
-
<Treemap data={[{ id: 'x', value: 1 }]} width={100} height={100}
|
|
50
|
+
<Treemap data={[{ id: 'x', value: 1 }]} width={100} height={100} />
|
|
51
51
|
)
|
|
52
52
|
expect(container.firstChild).toBeInTheDocument()
|
|
53
53
|
})
|