@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
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
import { View, Text } from 'react-native'
|
|
3
|
+
import { cn } from '../../utils/cn'
|
|
4
|
+
import { SideNav, type SideNavItem } from './SideNav'
|
|
5
|
+
import { TopBar } from './TopBar'
|
|
6
|
+
import { type Device } from './DeviceRow'
|
|
7
|
+
import { type SessionState } from './SessionStatePill'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* `DashboardShell` — the wall-dashboard chrome: a persistent {@link SideNav} rail +
|
|
11
|
+
* {@link TopBar} over a main content region. A thin frame that wires the shell-nav
|
|
12
|
+
* organisms together and exposes a `children` content slot, so the dashboard app
|
|
13
|
+
* mounts one component instead of re-assembling the chrome. Driven entirely by props.
|
|
14
|
+
*/
|
|
15
|
+
export interface DashboardShellProps {
|
|
16
|
+
/** Active nav category key. */
|
|
17
|
+
activeKey?: string
|
|
18
|
+
/** Nav categories, top → bottom. Defaults to SideNav's four dashboard categories. */
|
|
19
|
+
navItems?: SideNavItem[]
|
|
20
|
+
/** A category with off-view live activity → a quiet cue on that item. */
|
|
21
|
+
liveKey?: string | null
|
|
22
|
+
/** Global session state → the TopBar status pill. */
|
|
23
|
+
state?: SessionState
|
|
24
|
+
/** Connected devices → the TopBar connection glyph + dropdown. */
|
|
25
|
+
devices?: Device[]
|
|
26
|
+
/** TopBar brand subtitle. */
|
|
27
|
+
subtitle?: string
|
|
28
|
+
onNavigate?: (key: string) => void
|
|
29
|
+
onSelectDevice?: (device: Device) => void
|
|
30
|
+
/** Main content region. A placeholder renders when omitted. */
|
|
31
|
+
children?: ReactNode
|
|
32
|
+
className?: string
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const DEMO_DEVICES: Device[] = [
|
|
36
|
+
{ id: 'Voltra-A3F2', nickname: 'Left Cable', slot: 'L', state: 'connected' },
|
|
37
|
+
{ id: 'Voltra-9B1C', nickname: 'Right Cable', slot: 'R', state: 'connected' },
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
function ContentPlaceholder() {
|
|
41
|
+
return (
|
|
42
|
+
<View className="flex-1 items-center justify-center">
|
|
43
|
+
<Text className="font-heading text-sm text-text-tertiary">main content region</Text>
|
|
44
|
+
</View>
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function DashboardShell({
|
|
49
|
+
activeKey = 'live',
|
|
50
|
+
navItems,
|
|
51
|
+
liveKey = null,
|
|
52
|
+
state = 'live',
|
|
53
|
+
devices = DEMO_DEVICES,
|
|
54
|
+
subtitle = 'wall dashboard',
|
|
55
|
+
onNavigate,
|
|
56
|
+
onSelectDevice,
|
|
57
|
+
children,
|
|
58
|
+
className,
|
|
59
|
+
}: DashboardShellProps) {
|
|
60
|
+
return (
|
|
61
|
+
<View className={cn('flex-1 flex-row bg-surface-base', className)}>
|
|
62
|
+
<SideNav activeKey={activeKey} items={navItems} liveKey={liveKey} onNavigate={onNavigate} />
|
|
63
|
+
<View className="flex-1">
|
|
64
|
+
<TopBar
|
|
65
|
+
state={state}
|
|
66
|
+
devices={devices}
|
|
67
|
+
subtitle={subtitle}
|
|
68
|
+
onSelectDevice={onSelectDevice}
|
|
69
|
+
/>
|
|
70
|
+
<View className="flex-1">{children ?? <ContentPlaceholder />}</View>
|
|
71
|
+
</View>
|
|
72
|
+
</View>
|
|
73
|
+
)
|
|
74
|
+
}
|
|
@@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
|
2
2
|
import { DeviceIndicator } from './DeviceIndicator'
|
|
3
3
|
|
|
4
4
|
const meta: Meta<typeof DeviceIndicator> = {
|
|
5
|
-
title: 'Shell/
|
|
5
|
+
title: 'Shell/DeviceIndicator',
|
|
6
6
|
component: DeviceIndicator,
|
|
7
7
|
tags: ['autodocs'],
|
|
8
8
|
args: { status: 'connected' },
|
|
@@ -15,7 +15,7 @@ const meta: Meta<typeof DeviceIndicator> = {
|
|
|
15
15
|
docs: {
|
|
16
16
|
description: {
|
|
17
17
|
component:
|
|
18
|
-
'**Molecule.** Composes [BluetoothIcon](?path=/docs/icons--docs), color-coded by ' +
|
|
18
|
+
'**Molecule.** Composes [BluetoothIcon](?path=/docs/foundations-icons--docs), color-coded by ' +
|
|
19
19
|
'connection state via a `text-status-*-vivid` token — the glyph color alone carries the fault ' +
|
|
20
20
|
'(no separate badge). Switch the `status` control to see connected / degraded / lost.',
|
|
21
21
|
},
|
|
@@ -10,7 +10,7 @@ const DEVICES: Device[] = [
|
|
|
10
10
|
]
|
|
11
11
|
|
|
12
12
|
const meta: Meta<typeof DeviceMenu> = {
|
|
13
|
-
title: 'Shell/
|
|
13
|
+
title: 'Shell/DeviceMenu',
|
|
14
14
|
component: DeviceMenu,
|
|
15
15
|
tags: ['autodocs'],
|
|
16
16
|
args: { devices: DEVICES, isOpen: false },
|
|
@@ -32,10 +32,10 @@ const meta: Meta<typeof DeviceMenu> = {
|
|
|
32
32
|
description: {
|
|
33
33
|
component:
|
|
34
34
|
'**Organism.** The device glyph + its dropdown. Composes ' +
|
|
35
|
-
'[Popover](?path=/docs/components-popover--docs) + ' +
|
|
36
|
-
'[DeviceIndicator](?path=/docs/shell-
|
|
37
|
-
'[DeviceRow](?path=/docs/shell-
|
|
38
|
-
'[Typography](?path=/docs/
|
|
35
|
+
'[Popover](?path=/docs/components-molecules-popover--docs) + ' +
|
|
36
|
+
'[DeviceIndicator](?path=/docs/shell-deviceindicator--docs) (trigger) + ' +
|
|
37
|
+
'[DeviceRow](?path=/docs/shell-devicerow--docs) (list) + ' +
|
|
38
|
+
'[Typography](?path=/docs/foundations-typography--docs) (header). Aggregates the bound devices ' +
|
|
39
39
|
'(worst-of) for the glyph state. Click the glyph, or toggle the `isOpen` control.',
|
|
40
40
|
},
|
|
41
41
|
},
|
|
@@ -3,7 +3,7 @@ import { View } from 'react-native'
|
|
|
3
3
|
import { DeviceRow } from './DeviceRow'
|
|
4
4
|
|
|
5
5
|
const meta: Meta<typeof DeviceRow> = {
|
|
6
|
-
title: 'Shell/
|
|
6
|
+
title: 'Shell/DeviceRow',
|
|
7
7
|
component: DeviceRow,
|
|
8
8
|
tags: ['autodocs'],
|
|
9
9
|
args: {
|
|
@@ -25,8 +25,8 @@ const meta: Meta<typeof DeviceRow> = {
|
|
|
25
25
|
description: {
|
|
26
26
|
component:
|
|
27
27
|
'**Molecule.** One device row: status dot · name · Bluetooth id. Composes ' +
|
|
28
|
-
'[Indicator](?path=/docs/components-indicator--docs) (status dot) + ' +
|
|
29
|
-
'[Typography](?path=/docs/
|
|
28
|
+
'[Indicator](?path=/docs/components-atoms-indicator--docs) (status dot) + ' +
|
|
29
|
+
'[Typography](?path=/docs/foundations-typography--docs) (`body2` name, `mono` id). ' +
|
|
30
30
|
'Edit the `device` control (set `state` to `lost` / `available`, `slot` to `null`) to explore.',
|
|
31
31
|
},
|
|
32
32
|
},
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import { View } from 'react-native'
|
|
3
|
+
import { NavItem } from './NavItem'
|
|
4
|
+
import { ActivityIcon } from '../icons'
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof NavItem> = {
|
|
7
|
+
title: 'Shell/NavItem',
|
|
8
|
+
component: NavItem,
|
|
9
|
+
tags: ['autodocs'],
|
|
10
|
+
args: { label: 'Live', active: false, live: false },
|
|
11
|
+
argTypes: {
|
|
12
|
+
label: { control: 'text' },
|
|
13
|
+
active: { control: 'boolean' },
|
|
14
|
+
live: { control: 'boolean' },
|
|
15
|
+
icon: { control: false },
|
|
16
|
+
onPress: { action: 'press' },
|
|
17
|
+
},
|
|
18
|
+
decorators: [
|
|
19
|
+
(Story) => (
|
|
20
|
+
<View className="w-[60px] items-center bg-background-base py-2">
|
|
21
|
+
<Story />
|
|
22
|
+
</View>
|
|
23
|
+
),
|
|
24
|
+
],
|
|
25
|
+
parameters: {
|
|
26
|
+
docs: {
|
|
27
|
+
description: {
|
|
28
|
+
component:
|
|
29
|
+
'**Molecule.** One category button — a glyph over an uppercase micro-label in a 46×46 target, ' +
|
|
30
|
+
'spanning the full 60px rail so the active accent bar sits flush to the edge. Composes an ' +
|
|
31
|
+
'[icon](?path=/docs/foundations-icons--docs) + [Typography](?path=/docs/foundations-typography--docs). Toggle ' +
|
|
32
|
+
'`active` / `live` to see the states.',
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
render: (args) => <NavItem {...args} icon={<ActivityIcon size={20} color="currentColor" />} />,
|
|
37
|
+
}
|
|
38
|
+
export default meta
|
|
39
|
+
type Story = StoryObj<typeof NavItem>
|
|
40
|
+
|
|
41
|
+
export const Default: Story = {}
|
|
42
|
+
export const Active: Story = { args: { active: true } }
|
|
43
|
+
export const LiveElsewhere: Story = {
|
|
44
|
+
args: { live: true },
|
|
45
|
+
parameters: {
|
|
46
|
+
docs: {
|
|
47
|
+
description: {
|
|
48
|
+
story: 'Not the active view, but a set is running for this category → muted-green label.',
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest'
|
|
2
|
+
import { render, screen, fireEvent } from '@testing-library/react'
|
|
3
|
+
import { NavItem } from './NavItem'
|
|
4
|
+
import { ActivityIcon } from '../icons'
|
|
5
|
+
|
|
6
|
+
const icon = <ActivityIcon size={20} color="currentColor" />
|
|
7
|
+
|
|
8
|
+
describe('NavItem', () => {
|
|
9
|
+
it('renders the label and is exposed as a tab', () => {
|
|
10
|
+
render(<NavItem icon={icon} label="Live" />)
|
|
11
|
+
expect(screen.getByRole('tab', { name: 'Live' })).toBeInTheDocument()
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
it('shows the accent bar when active', () => {
|
|
15
|
+
render(<NavItem icon={icon} label="Live" active />)
|
|
16
|
+
expect(screen.getByTestId('nav-item-accent')).toBeInTheDocument()
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
it('hides the accent bar when inactive', () => {
|
|
20
|
+
render(<NavItem icon={icon} label="Live" />)
|
|
21
|
+
expect(screen.queryByTestId('nav-item-accent')).toBeNull()
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('fires onPress when tapped', () => {
|
|
25
|
+
const onPress = vi.fn()
|
|
26
|
+
render(<NavItem icon={icon} label="Live" onPress={onPress} />)
|
|
27
|
+
fireEvent.click(screen.getByRole('tab', { name: 'Live' }))
|
|
28
|
+
expect(onPress).toHaveBeenCalledTimes(1)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('renders the live-elsewhere state', () => {
|
|
32
|
+
const { container } = render(<NavItem icon={icon} label="Live" live />)
|
|
33
|
+
expect(container.firstChild).toBeInTheDocument()
|
|
34
|
+
})
|
|
35
|
+
})
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
import { Pressable, View } from 'react-native'
|
|
3
|
+
import { cn } from '../../utils/cn'
|
|
4
|
+
import { Typography } from '../custom/Typography'
|
|
5
|
+
|
|
6
|
+
export interface NavItemProps {
|
|
7
|
+
/** The nav glyph (an icon from `components/icons`, rendered ~20px via `currentColor`). */
|
|
8
|
+
icon: ReactNode
|
|
9
|
+
/** Short label shown under the glyph (uppercased for display; used as the accessible name). */
|
|
10
|
+
label: string
|
|
11
|
+
/** Active category → left accent bar + brand-colored glyph & label. */
|
|
12
|
+
active?: boolean
|
|
13
|
+
/**
|
|
14
|
+
* A set is running for this category while it is NOT the active view
|
|
15
|
+
* (live-elsewhere) → a quiet muted-green label tint. Ignored when `active`.
|
|
16
|
+
*/
|
|
17
|
+
live?: boolean
|
|
18
|
+
onPress?: () => void
|
|
19
|
+
className?: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Shell S2 · NavItem — one category button in the {@link SideNav}: a 20px glyph
|
|
24
|
+
* over an uppercase micro-label in a 46×46 target. The button spans the full 60px
|
|
25
|
+
* rail so the active **left accent bar** sits flush to the rail's edge. States:
|
|
26
|
+
* active = accent bar + `brand-primary`; `live` (while not active) tints only the
|
|
27
|
+
* label `status-success-dark` (the glyph stays dim); otherwise dim `text-tertiary`.
|
|
28
|
+
*/
|
|
29
|
+
export function NavItem({
|
|
30
|
+
icon,
|
|
31
|
+
label,
|
|
32
|
+
active = false,
|
|
33
|
+
live = false,
|
|
34
|
+
onPress,
|
|
35
|
+
className,
|
|
36
|
+
}: NavItemProps) {
|
|
37
|
+
const glyphColor = active ? 'text-brand-primary' : 'text-text-tertiary'
|
|
38
|
+
const labelColor = active
|
|
39
|
+
? 'text-brand-primary'
|
|
40
|
+
: live
|
|
41
|
+
? 'text-status-live-muted'
|
|
42
|
+
: 'text-text-tertiary'
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<Pressable
|
|
46
|
+
accessibilityRole="tab"
|
|
47
|
+
accessibilityState={{ selected: active }}
|
|
48
|
+
accessibilityLabel={label}
|
|
49
|
+
onPress={onPress}
|
|
50
|
+
className={cn('relative h-[46px] w-[60px] items-center justify-center', className)}
|
|
51
|
+
>
|
|
52
|
+
{active ? (
|
|
53
|
+
<View
|
|
54
|
+
testID="nav-item-accent"
|
|
55
|
+
className="absolute left-0 top-[13px] bottom-[13px] w-[3px] rounded-r-[3px] bg-brand-primary"
|
|
56
|
+
/>
|
|
57
|
+
) : null}
|
|
58
|
+
<View
|
|
59
|
+
className={cn(
|
|
60
|
+
'h-[46px] w-[46px] items-center justify-center gap-[3px] rounded-[11px]',
|
|
61
|
+
glyphColor
|
|
62
|
+
)}
|
|
63
|
+
>
|
|
64
|
+
{icon}
|
|
65
|
+
<Typography
|
|
66
|
+
variant="button"
|
|
67
|
+
color="inherit"
|
|
68
|
+
className={cn('text-[8.5px] font-bold uppercase tracking-[0.4px]', labelColor)}
|
|
69
|
+
>
|
|
70
|
+
{label}
|
|
71
|
+
</Typography>
|
|
72
|
+
</View>
|
|
73
|
+
</Pressable>
|
|
74
|
+
)
|
|
75
|
+
}
|
|
@@ -9,6 +9,23 @@ This README is the **index**: it maps each component's dependencies (**composes
|
|
|
9
9
|
should be reaching for an existing primitive instead. Storybook mirrors this tree under `Shell/TopBar/…`,
|
|
10
10
|
and every component's autodocs page repeats its "Composes" links.
|
|
11
11
|
|
|
12
|
+
## Specimen: `SideNav` (the second shell unit, S2)
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
SideNav ...................... organism — the 60px left rail (Shell/SideNav)
|
|
16
|
+
├─ NavItem ................... molecule × 4 (Live · Review · Plan · Body)
|
|
17
|
+
│ ├─ icon ................... icons (shared primitive) — Activity/History/Layers/PersonStanding
|
|
18
|
+
│ └─ Typography ............. titan · button variant (uppercase micro-label)
|
|
19
|
+
└─ (accent bar) .............. bg-brand-primary edge bar on the active item
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Decisions locked 2026-07-08 (specimen `coordination/design-explorations/shell/S2-sidenav/`): lucide glyphs
|
|
23
|
+
(activity · history · layers · figure) · **Plan** label (narrower than "Program") · active = **left accent
|
|
24
|
+
bar** (short, centered on the icon+label) · **60px** icon+micro-label · live cue = **muted-green label**
|
|
25
|
+
(`status-live-muted`) on the Live item while a set runs off-Live · four items, no footer, no top-nav. Fixed
|
|
26
|
+
60px at every width (labels sit under the glyph). `SideNav` is presentational — `activeKey` / `onNavigate` /
|
|
27
|
+
`liveKey`; the app owns routing + which key is live.
|
|
28
|
+
|
|
12
29
|
## Specimen: `TopBar` (the first shell unit, S1)
|
|
13
30
|
|
|
14
31
|
```
|
|
@@ -33,6 +50,8 @@ TopBar ....................... organism — the chrome band (Shell/TopBar)
|
|
|
33
50
|
|
|
34
51
|
| Component | Tier | Composes ↓ | Used-by ↑ |
|
|
35
52
|
|---|---|---|---|
|
|
53
|
+
| `SideNav` | organism | NavItem ×4, `components/icons` (Activity/History/Layers/PersonStanding) | dashboard app root *(planned)* |
|
|
54
|
+
| `NavItem` | molecule | icon, Typography | SideNav |
|
|
36
55
|
| `TopBar` | organism | BrandLockup, SessionStatePill, Divider, DeviceMenu, DateTime, `surfaceGradient.chrome` | dashboard app root *(planned)* |
|
|
37
56
|
| `BrandLockup` | molecule | VoltrasMark, Typography | TopBar |
|
|
38
57
|
| `SessionStatePill` | molecule | Indicator, Typography | TopBar, **Live-view header** *(planned reuse)* |
|
|
@@ -79,8 +98,19 @@ Every leaf now composes a primitive rather than hand-rolling it:
|
|
|
79
98
|
- **Lint guardrails** — components may not inline `linear-gradient` (use `surfaceGradient`); shell + icons may
|
|
80
99
|
not use raw hex (use tokens).
|
|
81
100
|
|
|
101
|
+
**S2 shared substrate:** four nav glyphs added to `components/icons` (`ActivityIcon`, `HistoryIcon`,
|
|
102
|
+
`LayersIcon`, `PersonStandingIcon` — lucide-mirrored, like Dumbbell/Star), available system-wide.
|
|
103
|
+
|
|
82
104
|
**Watch-list (known gaps to close as we go):**
|
|
83
105
|
- **Dot primitive overlap** — titan has both `StatusDot` (Workout, semantic) and `Indicator` (ui, generic).
|
|
84
106
|
The shell standardizes on `Indicator`; a future pass could consolidate.
|
|
85
107
|
- **Other hand-rolled gradients** — `MesoCard`, `DeviationBar`, `MesoStatusCard`, `BodyMapDetailPanel` still
|
|
86
108
|
inline `linear-gradient` strings; they should adopt `surfaceGradient` / `linearGradient`.
|
|
109
|
+
- **`status-live` token family (new, decoupled from success)** — introduced `status-live` (green-300, the
|
|
110
|
+
vivid LIVE-pill green) + `status-live-muted` (green-500 `#22A444`, the quiet nav cue) so "live" has its own
|
|
111
|
+
role: changing `status-success` no longer affects live, and vice-versa. Wired the full chain (semantic →
|
|
112
|
+
config → global.css → tailwind); `Indicator` gained a `live` color; the S1 LIVE pill was repointed
|
|
113
|
+
`success`→`live` (value-preserving). This realized a slice of TD-05.09 Fork 1b (wiring ramp steps as tokens).
|
|
114
|
+
- **`aria-selected` on `NavItem`** — RNW does not emit `aria-selected` from `accessibilityState={{selected}}`
|
|
115
|
+
in the jsdom test env, so active-state is asserted via the accent-bar testID. Confirm the on-device/RNW
|
|
116
|
+
build exposes selection to AT (may need an explicit `aria-selected` for full tab semantics).
|