@titan-design/react-ui 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bodymap.js +12 -7
- package/dist/bodymap.js.map +1 -1
- package/dist/bodymap.mjs +12 -7
- package/dist/bodymap.mjs.map +1 -1
- package/dist/index.d.mts +855 -124
- package/dist/index.d.ts +855 -124
- package/dist/index.js +2276 -801
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2230 -802
- package/dist/index.mjs.map +1 -1
- package/dist/{pages-MO0JCySL.d.mts → pages-8u_4WbL-.d.mts} +280 -57
- package/dist/{pages-D7Jlssvp.d.ts → pages-DaWiBOGa.d.ts} +280 -57
- package/dist/pages.d.mts +1 -1
- package/dist/pages.d.ts +1 -1
- package/dist/pages.js +1255 -798
- package/dist/pages.js.map +1 -1
- package/dist/pages.mjs +1255 -798
- package/dist/pages.mjs.map +1 -1
- package/dist/theme/index.d.mts +16 -82
- package/dist/theme/index.d.ts +16 -82
- package/dist/theme/index.js +13 -115
- package/dist/theme/index.js.map +1 -1
- package/dist/theme/index.mjs +13 -115
- package/dist/theme/index.mjs.map +1 -1
- package/dist/theme/tokens-css.js +14 -6
- package/dist/theme/tokens-css.js.map +1 -1
- package/dist/theme/tokens-css.mjs +14 -6
- package/dist/theme/tokens-css.mjs.map +1 -1
- package/package.json +1 -1
- package/src/arch/Architecture.stories.tsx +870 -0
- package/src/arch/annotations.json +48 -0
- package/src/arch/arch-graph.json +4301 -0
- package/src/components/custom/DateTime/DateTime.stories.tsx +1 -1
- package/src/components/custom/EmptyState/EmptyState.stories.tsx +1 -1
- package/src/components/custom/Gauge/Gauge.stories.tsx +1 -1
- package/src/components/custom/Metric/Metric.stories.tsx +1 -1
- package/src/components/custom/Scatter/Scatter.stories.tsx +1 -1
- package/src/components/custom/Sidebar/Sidebar.stories.tsx +1 -1
- package/src/components/custom/Table/Table.stories.tsx +1 -1
- package/src/components/custom/TimerReadout/TimerReadout.stories.tsx +62 -0
- package/src/components/custom/TimerReadout/TimerReadout.test.tsx +72 -0
- package/src/components/custom/TimerReadout/TimerReadout.tsx +75 -0
- package/src/components/custom/TimerReadout/index.ts +1 -0
- package/src/components/custom/Treemap/Treemap.stories.tsx +1 -1
- package/src/components/custom/Typography/Typography.stories.tsx +1 -1
- package/src/components/custom/Workout/ActiveWorkoutPage.stories.tsx +21 -13
- package/src/components/custom/Workout/ActiveWorkoutPage.test.tsx +16 -13
- package/src/components/custom/Workout/ActiveWorkoutPage.tsx +9 -15
- package/src/components/custom/Workout/BaseBadge.stories.tsx +1 -1
- package/src/components/custom/Workout/BodyMap.stories.tsx +1 -1
- package/src/components/custom/Workout/BodyMapDetailPanel.stories.tsx +1 -1
- package/src/components/custom/Workout/CapacityBandChart.stories.tsx +1 -1
- package/src/components/custom/Workout/DeviationBar.stories.tsx +1 -1
- package/src/components/custom/Workout/ExerciseCard.stories.tsx +51 -66
- package/src/components/custom/Workout/ExerciseCard.test.tsx +146 -143
- package/src/components/custom/Workout/ExerciseCard.tsx +176 -198
- package/src/components/custom/Workout/ExerciseCardHeading.stories.tsx +99 -0
- package/src/components/custom/Workout/ExerciseCardHeading.test.tsx +72 -0
- package/src/components/custom/Workout/ExerciseCardHeading.tsx +43 -0
- package/src/components/custom/Workout/ExerciseCardUnified.stories.tsx +151 -0
- package/src/components/custom/Workout/ExerciseDetailPage.stories.tsx +5 -3
- package/src/components/custom/Workout/ExerciseDetailPage.test.tsx +4 -4
- package/src/components/custom/Workout/ExerciseDetailPage.tsx +4 -5
- package/src/components/custom/Workout/ExerciseHeading.stories.tsx +81 -0
- package/src/components/custom/Workout/ExerciseHeading.test.tsx +77 -0
- package/src/components/custom/Workout/ExerciseHeading.tsx +85 -0
- package/src/components/custom/Workout/ExerciseIndicator.stories.tsx +110 -0
- package/src/components/custom/Workout/ExerciseIndicator.test.tsx +93 -0
- package/src/components/custom/Workout/ExerciseIndicator.tsx +120 -0
- package/src/components/custom/Workout/FatigueMeter.stories.tsx +51 -0
- package/src/components/custom/Workout/FatigueMeter.test.tsx +73 -0
- package/src/components/custom/Workout/FatigueMeter.tsx +82 -0
- package/src/components/custom/Workout/InputBar.stories.tsx +1 -1
- package/src/components/custom/Workout/IntensityBar.stories.tsx +1 -1
- package/src/components/custom/Workout/LiveAuraFrame.stories.tsx +153 -0
- package/src/components/custom/Workout/LiveAuraFrame.test.tsx +74 -0
- package/src/components/custom/Workout/LiveAuraFrame.tsx +105 -0
- package/src/components/custom/Workout/MesoCard.stories.tsx +1 -1
- package/src/components/custom/Workout/MesoProgressBar.stories.tsx +1 -1
- package/src/components/custom/Workout/MesoStatusCard.stories.tsx +1 -1
- package/src/components/custom/Workout/MetricTiles.stories.tsx +60 -0
- package/src/components/custom/Workout/MetricTiles.test.tsx +58 -0
- package/src/components/custom/Workout/MetricTiles.tsx +48 -0
- package/src/components/custom/Workout/MuscleGroupChip.stories.tsx +1 -1
- package/src/components/custom/Workout/PlaceholderStrip.stories.tsx +1 -1
- package/src/components/custom/Workout/PrBadge.stories.tsx +1 -1
- package/src/components/custom/Workout/PrHistoryModal.stories.tsx +1 -1
- package/src/components/custom/Workout/ProgramPlanningPage.stories.tsx +4 -16
- package/src/components/custom/Workout/ProgramPlanningPage.tsx +8 -6
- package/src/components/custom/Workout/README.md +146 -4
- package/src/components/custom/Workout/ReadinessCheck.stories.tsx +1 -1
- package/src/components/custom/Workout/RestTimer.stories.tsx +1 -1
- package/src/components/custom/Workout/RestTimer.tsx +15 -10
- package/src/components/custom/Workout/RowInventory.stories.tsx +405 -0
- package/src/components/custom/Workout/S3FullRail.stories.tsx +34 -0
- package/src/components/custom/Workout/S3SessionPace.stories.tsx +95 -0
- package/src/components/custom/Workout/S3SessionRailHeading.stories.tsx +61 -0
- package/src/components/custom/Workout/S3SetTypes.stories.tsx +176 -0
- package/src/components/custom/Workout/S3WorkoutExpansion.stories.tsx +240 -0
- package/src/components/custom/Workout/ScheduleTiles.stories.tsx +40 -0
- package/src/components/custom/Workout/ScheduleTiles.test.tsx +34 -0
- package/src/components/custom/Workout/ScheduleTiles.tsx +70 -0
- package/src/components/custom/Workout/SegmentedBar.stories.tsx +99 -0
- package/src/components/custom/Workout/SegmentedBar.test.tsx +77 -0
- package/src/components/custom/Workout/SegmentedBar.tsx +172 -0
- package/src/components/custom/Workout/SegmentedProgressBar.stories.tsx +50 -0
- package/src/components/custom/Workout/SegmentedProgressBar.test.tsx +80 -0
- package/src/components/custom/Workout/SegmentedProgressBar.tsx +67 -0
- package/src/components/custom/Workout/SessionHeader.stories.tsx +121 -0
- package/src/components/custom/Workout/SessionHeader.test.tsx +122 -0
- package/src/components/custom/Workout/SessionHeader.tsx +152 -0
- package/src/components/custom/Workout/SessionRail.stories.tsx +153 -0
- package/src/components/custom/Workout/SessionRail.test.tsx +86 -0
- package/src/components/custom/Workout/SessionRail.tsx +126 -0
- package/src/components/custom/Workout/SetBar.stories.tsx +124 -0
- package/src/components/custom/Workout/SetBar.test.tsx +123 -0
- package/src/components/custom/Workout/SetBar.tsx +171 -0
- package/src/components/custom/Workout/SetRow.stories.tsx +50 -100
- package/src/components/custom/Workout/SetRow.test.tsx +94 -166
- package/src/components/custom/Workout/SetRow.tsx +169 -232
- package/src/components/custom/Workout/SetStrip.stories.tsx +154 -0
- package/src/components/custom/Workout/SetStrip.test.tsx +109 -0
- package/src/components/custom/Workout/SetStrip.tsx +71 -0
- package/src/components/custom/Workout/SetTableHeader.stories.tsx +66 -0
- package/src/components/custom/Workout/SetTableHeader.test.tsx +47 -0
- package/src/components/custom/Workout/SetTableHeader.tsx +88 -0
- package/src/components/custom/Workout/SetsRepsLoad.stories.tsx +47 -0
- package/src/components/custom/Workout/SetsRepsLoad.test.tsx +44 -0
- package/src/components/custom/Workout/SetsRepsLoad.tsx +52 -0
- package/src/components/custom/Workout/Sparkline.stories.tsx +1 -1
- package/src/components/custom/Workout/StatusDot.stories.tsx +1 -1
- package/src/components/custom/Workout/StatusPill.stories.tsx +59 -0
- package/src/components/custom/Workout/StatusPill.test.tsx +63 -0
- package/src/components/custom/Workout/StatusPill.tsx +88 -0
- package/src/components/custom/Workout/StrengthTrendChart.stories.tsx +1 -1
- package/src/components/custom/Workout/SupersetWrapper.stories.tsx +1 -1
- package/src/components/custom/Workout/TempoBar.stories.tsx +1 -1
- package/src/components/custom/Workout/TempoDisplay.stories.tsx +12 -1
- package/src/components/custom/Workout/TempoDisplay.tsx +23 -33
- package/src/components/custom/Workout/TrainingStatusPage.stories.tsx +1 -1
- package/src/components/custom/Workout/VelocityStrip.modalities.stories.tsx +254 -0
- package/src/components/custom/Workout/VelocityStrip.responsive.stories.tsx +145 -0
- package/src/components/custom/Workout/VelocityStrip.stories.tsx +129 -66
- package/src/components/custom/Workout/VelocityStrip.test.tsx +376 -141
- package/src/components/custom/Workout/VelocityStrip.tsx +508 -120
- package/src/components/custom/Workout/VolumeLandmarkBar.stories.tsx +73 -0
- package/src/components/custom/Workout/VolumeLandmarkBar.test.tsx +119 -0
- package/src/components/custom/Workout/VolumeLandmarkBar.tsx +154 -0
- package/src/components/custom/Workout/WeekRow.stories.tsx +1 -1
- package/src/components/custom/Workout/WeightBadge.stories.tsx +1 -1
- package/src/components/custom/Workout/WorkoutCard.stories.tsx +2 -6
- package/src/components/custom/Workout/WorkoutCard.test.tsx +13 -46
- package/src/components/custom/Workout/WorkoutPill.stories.tsx +1 -1
- package/src/components/custom/Workout/ZoneTrack.stories.tsx +140 -0
- package/src/components/custom/Workout/ZoneTrack.test.tsx +200 -0
- package/src/components/custom/Workout/ZoneTrack.tsx +315 -0
- package/src/components/custom/Workout/index.ts +55 -2
- package/src/components/custom/Workout/metricText.stories.tsx +69 -0
- package/src/components/custom/Workout/metricText.tsx +34 -0
- package/src/components/custom/Workout/paceTone.test.ts +29 -0
- package/src/components/custom/Workout/paceTone.ts +27 -0
- package/src/components/custom/Workout/setHeadingKit.tsx +177 -0
- package/src/components/custom/index.ts +1 -0
- package/src/components/custom/stepper/Stepper.stories.tsx +1 -1
- package/src/components/icons/icons.stories.tsx +67 -2
- package/src/components/icons/icons.test.tsx +32 -2
- package/src/components/icons/icons.tsx +108 -0
- package/src/components/shell/BrandLockup.stories.tsx +3 -3
- package/src/components/shell/DashboardShell.stories.tsx +65 -0
- package/src/components/shell/DashboardShell.test.tsx +42 -0
- package/src/components/shell/DashboardShell.tsx +74 -0
- package/src/components/shell/DeviceIndicator.stories.tsx +2 -2
- package/src/components/shell/DeviceMenu.stories.tsx +5 -5
- package/src/components/shell/DeviceRow.stories.tsx +3 -3
- package/src/components/shell/NavItem.stories.tsx +52 -0
- package/src/components/shell/NavItem.test.tsx +35 -0
- package/src/components/shell/NavItem.tsx +75 -0
- package/src/components/shell/README.md +30 -0
- package/src/components/shell/SessionRailSpecimen.stories.tsx +753 -0
- package/src/components/shell/SessionStatePill.stories.tsx +3 -3
- package/src/components/shell/SessionStatePill.tsx +1 -1
- package/src/components/shell/SideNav.stories.tsx +81 -0
- package/src/components/shell/SideNav.test.tsx +51 -0
- package/src/components/shell/SideNav.tsx +74 -0
- package/src/components/shell/TopBar.stories.tsx +5 -5
- package/src/components/shell/index.ts +5 -0
- package/src/components/ui/alert/Alert.stories.tsx +1 -1
- package/src/components/ui/autocomplete/Autocomplete.stories.tsx +1 -1
- package/src/components/ui/avatar/Avatar.stories.tsx +1 -1
- package/src/components/ui/badge/Badge.stories.tsx +1 -1
- package/src/components/ui/breadcrumbs/Breadcrumbs.stories.tsx +1 -1
- package/src/components/ui/button/Button.stories.tsx +1 -1
- package/src/components/ui/card/Card.stories.tsx +1 -1
- package/src/components/ui/checkbox/Checkbox.stories.tsx +1 -1
- package/src/components/ui/chip/Chip.stories.tsx +1 -1
- package/src/components/ui/collapse/Collapse.stories.tsx +1 -1
- package/src/components/ui/data-row/DataRow.stories.tsx +1 -1
- package/src/components/ui/divider/Divider.stories.tsx +1 -1
- package/src/components/ui/drawer/Drawer.stories.tsx +1 -1
- package/src/components/ui/form-field/FormField.stories.tsx +1 -1
- package/src/components/ui/help-tip/HelpTip.stories.tsx +1 -1
- package/src/components/ui/icon-box/IconBox.stories.tsx +1 -1
- package/src/components/ui/index.ts +1 -0
- package/src/components/ui/indicator/Indicator.stories.tsx +2 -2
- package/src/components/ui/indicator/Indicator.test.tsx +1 -0
- package/src/components/ui/indicator/Indicator.tsx +6 -1
- package/src/components/ui/input/Input.stories.tsx +1 -1
- package/src/components/ui/link/Link.stories.tsx +1 -1
- package/src/components/ui/list-item/ListItem.stories.tsx +1 -1
- package/src/components/ui/menu/Menu.stories.tsx +1 -1
- package/src/components/ui/modal/Modal.stories.tsx +1 -1
- package/src/components/ui/pill/Pill.stories.tsx +1 -1
- package/src/components/ui/popover/Popover.stories.tsx +1 -1
- package/src/components/ui/progress/Progress.stories.tsx +1 -1
- package/src/components/ui/radio/Radio.stories.tsx +1 -1
- package/src/components/ui/section/Section.stories.tsx +1 -1
- package/src/components/ui/select/Select.stories.tsx +1 -1
- package/src/components/ui/skeleton/Skeleton.stories.tsx +1 -1
- package/src/components/ui/spinner/Spinner.stories.tsx +1 -1
- package/src/components/ui/stack/Stack.stories.tsx +1 -1
- package/src/components/ui/surface/Surface.stories.tsx +1 -1
- package/src/components/ui/switch/Switch.stories.tsx +1 -1
- package/src/components/ui/tabs/Tabs.stories.tsx +1 -1
- package/src/components/ui/tile/Tile.stories.tsx +79 -0
- package/src/components/ui/tile/Tile.test.tsx +48 -0
- package/src/components/ui/tile/Tile.tsx +64 -0
- package/src/components/ui/tile/index.ts +2 -0
- package/src/components/ui/toast/Toast.stories.tsx +1 -1
- package/src/components/ui/toolbar-button/ToolbarButton.stories.tsx +1 -1
- package/src/components/ui/tooltip/Tooltip.stories.tsx +1 -1
- package/src/examples/react-hook-form/ReactHookForm.stories.tsx +1 -1
- package/src/hooks/index.ts +2 -0
- package/src/hooks/useTimer.test.ts +150 -0
- package/src/hooks/useTimer.ts +77 -0
- package/src/index.ts +3 -0
- package/src/pages.ts +1 -1
- package/src/stories/PresetShowcase.stories.tsx +1 -1
- package/src/stories/ThemePresets.stories.tsx +1 -1
- package/src/theme/ColorSystem.stories.tsx +1 -1
- package/src/theme/Colors.stories.tsx +5 -8
- package/src/theme/config.ts +4 -0
- package/src/theme/elevation.stories.tsx +1 -1
- package/src/theme/global.css +5 -1
- package/src/theme/shadows.stories.tsx +1 -1
- package/src/theme/tokens/primitives.ts +11 -107
- package/src/theme/tokens/semantic.ts +8 -0
- package/src/theme/workout-tokens.ts +11 -7
- package/tailwind.config.js +5 -0
|
@@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
|
2
2
|
import { SessionStatePill } from './SessionStatePill'
|
|
3
3
|
|
|
4
4
|
const meta: Meta<typeof SessionStatePill> = {
|
|
5
|
-
title: 'Shell/
|
|
5
|
+
title: 'Shell/SessionStatePill',
|
|
6
6
|
component: SessionStatePill,
|
|
7
7
|
tags: ['autodocs'],
|
|
8
8
|
args: { state: 'live' },
|
|
@@ -15,8 +15,8 @@ const meta: Meta<typeof SessionStatePill> = {
|
|
|
15
15
|
description: {
|
|
16
16
|
component:
|
|
17
17
|
'**Molecule** (= the ledger’s reusable StatusPill — also used by the Live-view header). Composes ' +
|
|
18
|
-
'[Indicator](?path=/docs/components-indicator--docs) (pulse `ping` + vivid color for live) + ' +
|
|
19
|
-
'[Typography](?path=/docs/
|
|
18
|
+
'[Indicator](?path=/docs/components-atoms-indicator--docs) (pulse `ping` + vivid color for live) + ' +
|
|
19
|
+
'[Typography](?path=/docs/foundations-typography--docs) (`monoLabel`). Use the `state` control to switch.',
|
|
20
20
|
},
|
|
21
21
|
},
|
|
22
22
|
},
|
|
@@ -23,7 +23,7 @@ const stateConfig: Record<
|
|
|
23
23
|
}
|
|
24
24
|
> = {
|
|
25
25
|
// live = vivid green with an expanding ring; rest = solid amber (no pulse — operator); idle is dim.
|
|
26
|
-
live: { label: 'LIVE', color: '
|
|
26
|
+
live: { label: 'LIVE', color: 'live', pulse: 'ping', textColor: 'primary' },
|
|
27
27
|
rest: { label: 'REST', color: 'warning', pulse: false, textColor: 'primary' },
|
|
28
28
|
idle: { label: 'IDLE', color: 'default', pulse: false, textColor: 'secondary' },
|
|
29
29
|
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import { useArgs } from 'storybook/preview-api'
|
|
3
|
+
import { View } from 'react-native'
|
|
4
|
+
import { SideNav } from './SideNav'
|
|
5
|
+
import { Typography } from '../custom/Typography'
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof SideNav> = {
|
|
8
|
+
title: 'Shell/SideNav',
|
|
9
|
+
component: SideNav,
|
|
10
|
+
tags: ['autodocs'],
|
|
11
|
+
args: { activeKey: 'live', liveKey: null },
|
|
12
|
+
argTypes: {
|
|
13
|
+
activeKey: { control: 'select', options: ['live', 'review', 'program', 'body'] },
|
|
14
|
+
liveKey: { control: 'select', options: [null, 'live', 'review', 'program', 'body'] },
|
|
15
|
+
onNavigate: { action: 'navigate' },
|
|
16
|
+
},
|
|
17
|
+
// Interactive AND Controls-synced: clicking a category writes `activeKey` back to the
|
|
18
|
+
// story args (canvas + Controls stay in lock-step) and still fires the `onNavigate`
|
|
19
|
+
// action. `useArgs` must be called directly in the story/render fn (not a child
|
|
20
|
+
// component), so the rules-of-hooks lint is disabled here — the idiomatic SB pattern.
|
|
21
|
+
render: (args) => {
|
|
22
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
23
|
+
const [, updateArgs] = useArgs()
|
|
24
|
+
return (
|
|
25
|
+
<SideNav
|
|
26
|
+
{...args}
|
|
27
|
+
onNavigate={(key) => {
|
|
28
|
+
updateArgs({ activeKey: key })
|
|
29
|
+
args.onNavigate?.(key)
|
|
30
|
+
}}
|
|
31
|
+
/>
|
|
32
|
+
)
|
|
33
|
+
},
|
|
34
|
+
// Render in-context: full-height shell frame so the rail snaps to the left edge and
|
|
35
|
+
// stretches with the viewport (resize the canvas height to see it hold).
|
|
36
|
+
decorators: [
|
|
37
|
+
(Story) => (
|
|
38
|
+
<View className="min-h-screen flex-row bg-background-default">
|
|
39
|
+
<Story />
|
|
40
|
+
<View className="flex-1 items-center justify-center">
|
|
41
|
+
<Typography variant="body2" color="tertiary">
|
|
42
|
+
main viewport
|
|
43
|
+
</Typography>
|
|
44
|
+
</View>
|
|
45
|
+
</View>
|
|
46
|
+
),
|
|
47
|
+
],
|
|
48
|
+
parameters: {
|
|
49
|
+
layout: 'fullscreen',
|
|
50
|
+
docs: {
|
|
51
|
+
description: {
|
|
52
|
+
component:
|
|
53
|
+
'**Organism** (shell region). The persistent 60px left rail switching Live · Review · Plan · ' +
|
|
54
|
+
'Body. Composes [NavItem](?path=/docs/shell-navitem--docs) × the four categories + the ' +
|
|
55
|
+
'shared [icon set](?path=/docs/foundations-icons--docs). Presentational — drive it with `activeKey` / ' +
|
|
56
|
+
'`onNavigate` / `liveKey`. Active = left accent bar; `liveKey` (when not active) tints that ' +
|
|
57
|
+
'label a muted green.\n\n' +
|
|
58
|
+
'**Try it:** stories render in a full-height shell frame — the rail pins to the left edge and ' +
|
|
59
|
+
'stretches to the viewport, items staying top-aligned. **Resize the canvas** to see it hold at ' +
|
|
60
|
+
'any height. Use the **Controls** to move `activeKey` / `liveKey`.',
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
}
|
|
65
|
+
export default meta
|
|
66
|
+
type Story = StoryObj<typeof SideNav>
|
|
67
|
+
|
|
68
|
+
export const Default: Story = {}
|
|
69
|
+
|
|
70
|
+
export const LiveElsewhere: Story = {
|
|
71
|
+
args: { activeKey: 'program', liveKey: 'live' },
|
|
72
|
+
parameters: {
|
|
73
|
+
docs: {
|
|
74
|
+
description: {
|
|
75
|
+
story:
|
|
76
|
+
'A set runs on Live while the operator is on Plan → the Live item carries a quiet green label cue. ' +
|
|
77
|
+
'(Still clickable — try switching away and back.)',
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest'
|
|
2
|
+
import { render, screen, fireEvent, within } from '@testing-library/react'
|
|
3
|
+
import { axe } from 'jest-axe'
|
|
4
|
+
import { SideNav } from './SideNav'
|
|
5
|
+
|
|
6
|
+
describe('SideNav', () => {
|
|
7
|
+
it('renders the four default categories as tabs in a tablist', () => {
|
|
8
|
+
render(<SideNav activeKey="live" />)
|
|
9
|
+
expect(screen.getByRole('tablist')).toBeInTheDocument()
|
|
10
|
+
;['Live', 'Review', 'Plan', 'Body'].forEach((name) => {
|
|
11
|
+
expect(screen.getByRole('tab', { name })).toBeInTheDocument()
|
|
12
|
+
})
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('shows the accent bar only on the active category', () => {
|
|
16
|
+
render(<SideNav activeKey="program" />)
|
|
17
|
+
expect(screen.getAllByTestId('nav-item-accent')).toHaveLength(1)
|
|
18
|
+
expect(
|
|
19
|
+
within(screen.getByRole('tab', { name: 'Plan' })).getByTestId('nav-item-accent')
|
|
20
|
+
).toBeInTheDocument()
|
|
21
|
+
expect(
|
|
22
|
+
within(screen.getByRole('tab', { name: 'Live' })).queryByTestId('nav-item-accent')
|
|
23
|
+
).toBeNull()
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('reports the tapped key via onNavigate', () => {
|
|
27
|
+
const onNavigate = vi.fn()
|
|
28
|
+
render(<SideNav activeKey="live" onNavigate={onNavigate} />)
|
|
29
|
+
fireEvent.click(screen.getByRole('tab', { name: 'Body' }))
|
|
30
|
+
expect(onNavigate).toHaveBeenCalledWith('body')
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('keeps the active item as active even when it is also the liveKey (no double-state)', () => {
|
|
34
|
+
render(<SideNav activeKey="live" liveKey="live" />)
|
|
35
|
+
// active wins: exactly one accent bar, on Live (the live cue never applies to the active view)
|
|
36
|
+
expect(screen.getAllByTestId('nav-item-accent')).toHaveLength(1)
|
|
37
|
+
expect(
|
|
38
|
+
within(screen.getByRole('tab', { name: 'Live' })).getByTestId('nav-item-accent')
|
|
39
|
+
).toBeInTheDocument()
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('accepts custom items', () => {
|
|
43
|
+
render(<SideNav activeKey="a" items={[{ key: 'a', label: 'Alpha', icon: null }]} />)
|
|
44
|
+
expect(screen.getByRole('tab', { name: 'Alpha' })).toBeInTheDocument()
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('has no accessibility violations', async () => {
|
|
48
|
+
const { container } = render(<SideNav activeKey="live" />)
|
|
49
|
+
expect(await axe(container)).toHaveNoViolations()
|
|
50
|
+
})
|
|
51
|
+
})
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
import { View } from 'react-native'
|
|
3
|
+
import { cn } from '../../utils/cn'
|
|
4
|
+
import { ActivityIcon, HistoryIcon, LayersIcon, PersonStandingIcon } from '../icons'
|
|
5
|
+
import { NavItem } from './NavItem'
|
|
6
|
+
|
|
7
|
+
export interface SideNavItem {
|
|
8
|
+
/** Stable category key — matches `activeKey` / `liveKey`. */
|
|
9
|
+
key: string
|
|
10
|
+
/** Micro-label shown under the glyph. */
|
|
11
|
+
label: string
|
|
12
|
+
/** The glyph node (rendered ~20px via `currentColor`). */
|
|
13
|
+
icon: ReactNode
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface SideNavProps {
|
|
17
|
+
/** Category items, top → bottom. Defaults to the four dashboard categories. */
|
|
18
|
+
items?: SideNavItem[]
|
|
19
|
+
/** Key of the active category. */
|
|
20
|
+
activeKey: string
|
|
21
|
+
/** Called with the tapped item's key. */
|
|
22
|
+
onNavigate?: (key: string) => void
|
|
23
|
+
/**
|
|
24
|
+
* Key of a category with live activity while it is NOT active (e.g. a set
|
|
25
|
+
* running off its view) → a quiet green cue on that item's label.
|
|
26
|
+
*/
|
|
27
|
+
liveKey?: string | null
|
|
28
|
+
className?: string
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** The four dashboard categories with their locked S2 glyphs (activity · history · layers · figure). */
|
|
32
|
+
export const defaultNavItems: SideNavItem[] = [
|
|
33
|
+
{ key: 'live', label: 'Live', icon: <ActivityIcon size={20} color="currentColor" /> },
|
|
34
|
+
{ key: 'review', label: 'Review', icon: <HistoryIcon size={20} color="currentColor" /> },
|
|
35
|
+
{ key: 'program', label: 'Plan', icon: <LayersIcon size={20} color="currentColor" /> },
|
|
36
|
+
{ key: 'body', label: 'Body', icon: <PersonStandingIcon size={20} color="currentColor" /> },
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Shell S2 · SideNav — the persistent 60px left rail that switches the main
|
|
41
|
+
* viewport between categories (Live · Review · Program · Body). Presentational:
|
|
42
|
+
* it renders `items` and reports taps via `onNavigate`; the app owns routing and
|
|
43
|
+
* which key is `live`. Active item shows a left accent bar; `liveKey` (when not
|
|
44
|
+
* the active view) tints that item's label a muted green. Fixed 60px at every
|
|
45
|
+
* width — labels sit under the glyph, so they never change the rail width.
|
|
46
|
+
*/
|
|
47
|
+
export function SideNav({
|
|
48
|
+
items = defaultNavItems,
|
|
49
|
+
activeKey,
|
|
50
|
+
onNavigate,
|
|
51
|
+
liveKey = null,
|
|
52
|
+
className,
|
|
53
|
+
}: SideNavProps) {
|
|
54
|
+
return (
|
|
55
|
+
<View
|
|
56
|
+
accessibilityRole="tablist"
|
|
57
|
+
className={cn(
|
|
58
|
+
'w-[60px] items-center gap-[6px] border-r border-border bg-background-base py-3',
|
|
59
|
+
className
|
|
60
|
+
)}
|
|
61
|
+
>
|
|
62
|
+
{items.map((item) => (
|
|
63
|
+
<NavItem
|
|
64
|
+
key={item.key}
|
|
65
|
+
icon={item.icon}
|
|
66
|
+
label={item.label}
|
|
67
|
+
active={item.key === activeKey}
|
|
68
|
+
live={item.key === liveKey && item.key !== activeKey}
|
|
69
|
+
onPress={() => onNavigate?.(item.key)}
|
|
70
|
+
/>
|
|
71
|
+
))}
|
|
72
|
+
</View>
|
|
73
|
+
)
|
|
74
|
+
}
|
|
@@ -29,11 +29,11 @@ const meta: Meta<typeof TopBar> = {
|
|
|
29
29
|
component:
|
|
30
30
|
'**Organism** — the persistent shell chrome band, and the root of the top-bar family ' +
|
|
31
31
|
'(see `shell/README.md` for the full dependency map). Composes ' +
|
|
32
|
-
'[BrandLockup](?path=/docs/shell-
|
|
33
|
-
'[SessionStatePill](?path=/docs/shell-
|
|
34
|
-
'[Divider](?path=/docs/components-divider--docs) (`bg-border-prominent`) + ' +
|
|
35
|
-
'[DeviceMenu](?path=/docs/shell-
|
|
36
|
-
'[DateTime](?path=/docs/
|
|
32
|
+
'[BrandLockup](?path=/docs/shell-brandlockup--docs) + ' +
|
|
33
|
+
'[SessionStatePill](?path=/docs/shell-sessionstatepill--docs) + ' +
|
|
34
|
+
'[Divider](?path=/docs/components-atoms-divider--docs) (`bg-border-prominent`) + ' +
|
|
35
|
+
'[DeviceMenu](?path=/docs/shell-devicemenu--docs) + ' +
|
|
36
|
+
'[DateTime](?path=/docs/components-molecules-datetime--docs) (`variant="mono"` live clock). ' +
|
|
37
37
|
'Background = the shared `surfaceGradient.chrome` primitive.\n\n' +
|
|
38
38
|
'**Try it:** use the **Controls** to change `state`, edit `devices` (set one to `lost` to see the ' +
|
|
39
39
|
'fault), or toggle `showSubtitle` / `showClock`. **Resize the canvas** to watch the ' +
|
|
@@ -6,3 +6,8 @@ export * from './DeviceIndicator'
|
|
|
6
6
|
export * from './DeviceRow'
|
|
7
7
|
export * from './DeviceMenu'
|
|
8
8
|
export * from './TopBar'
|
|
9
|
+
// S2 · Side nav family.
|
|
10
|
+
export * from './NavItem'
|
|
11
|
+
export * from './SideNav'
|
|
12
|
+
// S3 · Dashboard shell — composes the nav + top-bar chrome over a content slot.
|
|
13
|
+
export * from './DashboardShell'
|
|
@@ -4,7 +4,7 @@ import { View, Text } from 'react-native'
|
|
|
4
4
|
import { Autocomplete, AutocompleteOption } from './Autocomplete'
|
|
5
5
|
|
|
6
6
|
const meta: Meta<typeof Autocomplete> = {
|
|
7
|
-
title: 'Components/Autocomplete',
|
|
7
|
+
title: 'Components/Molecules/Autocomplete',
|
|
8
8
|
component: Autocomplete,
|
|
9
9
|
tags: ['autodocs'],
|
|
10
10
|
argTypes: {
|
|
@@ -3,7 +3,7 @@ import { Text } from 'react-native'
|
|
|
3
3
|
import { Breadcrumbs, BreadcrumbItem } from './Breadcrumbs'
|
|
4
4
|
|
|
5
5
|
const meta: Meta<typeof Breadcrumbs> = {
|
|
6
|
-
title: 'Components/Breadcrumbs',
|
|
6
|
+
title: 'Components/Molecules/Breadcrumbs',
|
|
7
7
|
component: Breadcrumbs,
|
|
8
8
|
tags: ['autodocs'],
|
|
9
9
|
argTypes: {
|
|
@@ -5,7 +5,7 @@ import { Drawer, DrawerBody, DrawerFooter } from './Drawer'
|
|
|
5
5
|
import { Button, ButtonText } from '../button'
|
|
6
6
|
|
|
7
7
|
const meta: Meta<typeof Drawer> = {
|
|
8
|
-
title: 'Components/Drawer',
|
|
8
|
+
title: 'Components/Organisms/Drawer',
|
|
9
9
|
component: Drawer,
|
|
10
10
|
tags: ['autodocs'],
|
|
11
11
|
argTypes: {
|
|
@@ -3,14 +3,14 @@ import { View } from 'react-native'
|
|
|
3
3
|
import { Indicator } from './Indicator'
|
|
4
4
|
|
|
5
5
|
const meta: Meta<typeof Indicator> = {
|
|
6
|
-
title: 'Components/Indicator',
|
|
6
|
+
title: 'Components/Atoms/Indicator',
|
|
7
7
|
component: Indicator,
|
|
8
8
|
tags: ['autodocs'],
|
|
9
9
|
argTypes: {
|
|
10
10
|
size: { control: 'select', options: ['xs', 'sm', 'md', 'lg'] },
|
|
11
11
|
color: {
|
|
12
12
|
control: 'select',
|
|
13
|
-
options: ['default', 'primary', 'success', 'error', 'warning', 'info'],
|
|
13
|
+
options: ['default', 'primary', 'success', 'live', 'error', 'warning', 'info'],
|
|
14
14
|
},
|
|
15
15
|
glow: { control: 'boolean' },
|
|
16
16
|
ring: { control: 'boolean' },
|
|
@@ -6,6 +6,7 @@ export type IndicatorColor =
|
|
|
6
6
|
| 'default'
|
|
7
7
|
| 'primary'
|
|
8
8
|
| 'success'
|
|
9
|
+
| 'live'
|
|
9
10
|
| 'error'
|
|
10
11
|
| 'warning'
|
|
11
12
|
| 'info'
|
|
@@ -42,6 +43,7 @@ const colorStyles: Record<IndicatorColor, string> = {
|
|
|
42
43
|
default: 'bg-text-tertiary',
|
|
43
44
|
primary: 'bg-brand-primary',
|
|
44
45
|
success: 'bg-status-success',
|
|
46
|
+
live: 'bg-status-live',
|
|
45
47
|
error: 'bg-status-error',
|
|
46
48
|
warning: 'bg-status-warning',
|
|
47
49
|
info: 'bg-status-info',
|
|
@@ -98,7 +100,10 @@ export function Indicator({
|
|
|
98
100
|
colorClass,
|
|
99
101
|
pulseMode === 'opacity' && 'animate-pulse',
|
|
100
102
|
ring && 'border-2 border-background-base',
|
|
101
|
-
glow &&
|
|
103
|
+
glow &&
|
|
104
|
+
!customColor &&
|
|
105
|
+
(color === 'success' || color === 'live') &&
|
|
106
|
+
'shadow-[0_0_6px_rgba(46,213,115,0.6)]',
|
|
102
107
|
glow && !customColor && color === 'error' && 'shadow-[0_0_6px_rgba(239,68,68,0.6)]',
|
|
103
108
|
glow && !customColor && color === 'warning' && 'shadow-[0_0_6px_rgba(245,158,11,0.6)]',
|
|
104
109
|
glow && !customColor && color === 'primary' && 'shadow-[0_0_6px_rgba(255,121,0,0.6)]',
|
|
@@ -4,7 +4,7 @@ import { Menu, MenuTrigger, MenuList, MenuItem, MenuDivider, MenuGroup } from '.
|
|
|
4
4
|
import { Button, ButtonText } from '../button/Button'
|
|
5
5
|
|
|
6
6
|
const meta: Meta<typeof Menu> = {
|
|
7
|
-
title: 'Components/Menu',
|
|
7
|
+
title: 'Components/Molecules/Menu',
|
|
8
8
|
component: Menu,
|
|
9
9
|
tags: ['autodocs'],
|
|
10
10
|
argTypes: {
|
|
@@ -4,7 +4,7 @@ import { Popover, PopoverTrigger, PopoverContent, PopoverCloseButton } from './P
|
|
|
4
4
|
import { Button, ButtonText } from '../button/Button'
|
|
5
5
|
|
|
6
6
|
const meta: Meta<typeof Popover> = {
|
|
7
|
-
title: 'Components/Popover',
|
|
7
|
+
title: 'Components/Molecules/Popover',
|
|
8
8
|
component: Popover,
|
|
9
9
|
tags: ['autodocs'],
|
|
10
10
|
argTypes: {
|
|
@@ -3,7 +3,7 @@ import { View, Text } from 'react-native'
|
|
|
3
3
|
import { Progress, CircularProgress, ProgressSteps } from './Progress'
|
|
4
4
|
|
|
5
5
|
const meta: Meta<typeof Progress> = {
|
|
6
|
-
title: 'Components/Progress',
|
|
6
|
+
title: 'Components/Molecules/Progress',
|
|
7
7
|
component: Progress,
|
|
8
8
|
tags: ['autodocs'],
|
|
9
9
|
argTypes: {
|
|
@@ -4,7 +4,7 @@ import { Surface } from './Surface'
|
|
|
4
4
|
import type { ElevationLevel } from '../../../theme/elevation'
|
|
5
5
|
|
|
6
6
|
const meta: Meta<typeof Surface> = {
|
|
7
|
-
title: 'Components/Surface',
|
|
7
|
+
title: 'Components/Atoms/Surface',
|
|
8
8
|
component: Surface,
|
|
9
9
|
tags: ['autodocs'],
|
|
10
10
|
argTypes: {
|