@titan-design/react-ui 0.1.0 → 0.2.5
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 +47 -1
- package/dist/{chunk-MGW37MPO.mjs → chunk-UXVRPOME.mjs} +173 -3
- package/dist/chunk-UXVRPOME.mjs.map +1 -0
- package/dist/index.d.mts +101 -12
- package/dist/index.d.ts +101 -12
- package/dist/index.js +1267 -468
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +734 -112
- package/dist/index.mjs.map +1 -1
- package/dist/theme/index.d.mts +86 -1
- package/dist/theme/index.d.ts +86 -1
- package/dist/theme/index.js +166 -0
- package/dist/theme/index.js.map +1 -1
- package/dist/theme/index.mjs +1 -1
- package/package.json +22 -6
- package/src/components/custom/DateTime/DateTime.stories.tsx +180 -0
- package/src/components/custom/DateTime/DateTime.test.tsx +152 -0
- package/src/components/custom/DateTime/DateTime.tsx +235 -0
- package/src/components/custom/DateTime/index.ts +2 -0
- package/src/components/custom/EmptyState/EmptyState.stories.tsx +99 -0
- package/src/components/custom/EmptyState/EmptyState.test.tsx +106 -0
- package/src/components/custom/EmptyState/EmptyState.tsx +68 -0
- package/src/components/custom/EmptyState/index.ts +2 -0
- package/src/components/custom/Metric/Metric.stories.tsx +133 -0
- package/src/components/custom/Metric/Metric.test.tsx +120 -0
- package/src/components/custom/Metric/Metric.tsx +92 -0
- package/src/components/custom/Metric/index.ts +2 -0
- package/src/components/custom/Sidebar/Sidebar.stories.tsx +227 -0
- package/src/components/custom/Sidebar/Sidebar.test.tsx +185 -0
- package/src/components/custom/Sidebar/Sidebar.tsx +257 -0
- package/src/components/custom/Sidebar/index.ts +18 -0
- package/src/components/custom/Table/Table.stories.tsx +481 -0
- package/src/components/custom/Table/Table.test.tsx +531 -0
- package/src/components/custom/Table/Table.tsx +696 -0
- package/src/components/custom/Table/index.ts +22 -0
- package/src/components/custom/Typography/Typography.stories.tsx +153 -0
- package/src/components/custom/Typography/Typography.test.tsx +107 -0
- package/src/components/custom/Typography/Typography.tsx +218 -0
- package/src/components/custom/Typography/index.ts +19 -0
- package/src/components/custom/Workout/RestTimer.stories.tsx +71 -0
- package/src/components/custom/Workout/RestTimer.test.tsx +145 -0
- package/src/components/custom/Workout/RestTimer.tsx +172 -0
- package/src/components/custom/Workout/SupersetWrapper.stories.tsx +144 -0
- package/src/components/custom/Workout/SupersetWrapper.test.tsx +130 -0
- package/src/components/custom/Workout/SupersetWrapper.tsx +63 -0
- package/src/components/custom/Workout/index.ts +2 -0
- package/src/components/custom/index.ts +9 -0
- package/src/components/custom/stepper/Stepper.stories.tsx +245 -0
- package/src/components/custom/stepper/Stepper.test.tsx +230 -0
- package/src/components/custom/stepper/Stepper.tsx +250 -0
- package/src/components/custom/stepper/index.ts +10 -0
- package/src/components/index.ts +5 -0
- package/src/components/ui/alert/Alert.stories.tsx +154 -0
- package/src/components/ui/alert/Alert.test.tsx +202 -0
- package/src/components/ui/alert/Alert.tsx +170 -0
- package/src/components/ui/alert/index.ts +8 -0
- package/src/components/ui/autocomplete/Autocomplete.stories.tsx +299 -0
- package/src/components/ui/autocomplete/Autocomplete.test.tsx +247 -0
- package/src/components/ui/autocomplete/Autocomplete.tsx +332 -0
- package/src/components/ui/autocomplete/index.ts +2 -0
- package/src/components/ui/avatar/Avatar.stories.tsx +105 -0
- package/src/components/ui/avatar/Avatar.test.tsx +152 -0
- package/src/components/ui/avatar/Avatar.tsx +176 -0
- package/src/components/ui/avatar/index.ts +2 -0
- package/src/components/ui/badge/Badge.stories.tsx +100 -0
- package/src/components/ui/badge/Badge.test.tsx +103 -0
- package/src/components/ui/badge/Badge.tsx +119 -0
- package/src/components/ui/badge/index.ts +3 -0
- package/src/components/ui/breadcrumbs/Breadcrumbs.stories.tsx +70 -0
- package/src/components/ui/breadcrumbs/Breadcrumbs.test.tsx +156 -0
- package/src/components/ui/breadcrumbs/Breadcrumbs.tsx +127 -0
- package/src/components/ui/breadcrumbs/index.ts +2 -0
- package/src/components/ui/button/Button.stories.tsx +216 -0
- package/src/components/ui/button/Button.test.tsx +132 -0
- package/src/components/ui/button/Button.tsx +297 -0
- package/src/components/ui/button/index.ts +10 -0
- package/src/components/ui/card/Card.stories.tsx +443 -0
- package/src/components/ui/card/Card.test.tsx +294 -0
- package/src/components/ui/card/Card.tsx +401 -0
- package/src/components/ui/card/index.ts +20 -0
- package/src/components/ui/checkbox/Checkbox.stories.tsx +126 -0
- package/src/components/ui/checkbox/Checkbox.test.tsx +152 -0
- package/src/components/ui/checkbox/Checkbox.tsx +163 -0
- package/src/components/ui/checkbox/index.ts +2 -0
- package/src/components/ui/chip/Chip.stories.tsx +112 -0
- package/src/components/ui/chip/Chip.test.tsx +119 -0
- package/src/components/ui/chip/Chip.tsx +151 -0
- package/src/components/ui/chip/index.ts +2 -0
- package/src/components/ui/collapse/Collapse.stories.tsx +313 -0
- package/src/components/ui/collapse/Collapse.test.tsx +298 -0
- package/src/components/ui/collapse/Collapse.tsx +275 -0
- package/src/components/ui/collapse/index.ts +18 -0
- package/src/components/ui/data-row/DataRow.stories.tsx +78 -0
- package/src/components/ui/data-row/DataRow.test.tsx +78 -0
- package/src/components/ui/data-row/DataRow.tsx +29 -0
- package/src/components/ui/data-row/index.ts +2 -0
- package/src/components/ui/divider/Divider.stories.tsx +71 -0
- package/src/components/ui/divider/Divider.test.tsx +55 -0
- package/src/components/ui/divider/Divider.tsx +37 -0
- package/src/components/ui/divider/index.ts +2 -0
- package/src/components/ui/drawer/Drawer.stories.tsx +228 -0
- package/src/components/ui/drawer/Drawer.test.tsx +142 -0
- package/src/components/ui/drawer/Drawer.tsx +228 -0
- package/src/components/ui/drawer/index.ts +9 -0
- package/src/components/ui/form-field/FormField.stories.tsx +260 -0
- package/src/components/ui/form-field/FormField.test.tsx +280 -0
- package/src/components/ui/form-field/FormField.tsx +308 -0
- package/src/components/ui/form-field/index.ts +2 -0
- package/src/components/ui/help-tip/HelpTip.stories.tsx +250 -0
- package/src/components/ui/help-tip/HelpTip.test.tsx +198 -0
- package/src/components/ui/help-tip/HelpTip.tsx +229 -0
- package/src/components/ui/help-tip/index.ts +2 -0
- package/src/components/ui/icon-box/IconBox.stories.tsx +139 -0
- package/src/components/ui/icon-box/IconBox.test.tsx +120 -0
- package/src/components/ui/icon-box/IconBox.tsx +57 -0
- package/src/components/ui/icon-box/index.ts +1 -0
- package/src/components/ui/index.ts +38 -0
- package/src/components/ui/indicator/Indicator.stories.tsx +74 -0
- package/src/components/ui/indicator/Indicator.test.tsx +55 -0
- package/src/components/ui/indicator/Indicator.tsx +73 -0
- package/src/components/ui/indicator/index.ts +2 -0
- package/src/components/ui/input/Input.stories.tsx +192 -0
- package/src/components/ui/input/Input.test.tsx +86 -0
- package/src/components/ui/input/Input.tsx +273 -0
- package/src/components/ui/input/index.ts +2 -0
- package/src/components/ui/link/Link.stories.tsx +78 -0
- package/src/components/ui/link/Link.test.tsx +94 -0
- package/src/components/ui/link/Link.tsx +86 -0
- package/src/components/ui/link/index.ts +2 -0
- package/src/components/ui/list-item/ListItem.stories.tsx +146 -0
- package/src/components/ui/list-item/ListItem.test.tsx +128 -0
- package/src/components/ui/list-item/ListItem.tsx +89 -0
- package/src/components/ui/list-item/index.ts +14 -0
- package/src/components/ui/menu/Menu.stories.tsx +180 -0
- package/src/components/ui/menu/Menu.test.tsx +353 -0
- package/src/components/ui/menu/Menu.tsx +222 -0
- package/src/components/ui/menu/index.ts +2 -0
- package/src/components/ui/modal/Modal.stories.tsx +350 -0
- package/src/components/ui/modal/Modal.test.tsx +159 -0
- package/src/components/ui/modal/Modal.tsx +254 -0
- package/src/components/ui/modal/index.ts +19 -0
- package/src/components/ui/pill/Pill.stories.tsx +71 -0
- package/src/components/ui/pill/Pill.test.tsx +69 -0
- package/src/components/ui/pill/Pill.tsx +104 -0
- package/src/components/ui/pill/index.ts +2 -0
- package/src/components/ui/popover/Popover.stories.tsx +190 -0
- package/src/components/ui/popover/Popover.test.tsx +419 -0
- package/src/components/ui/popover/Popover.tsx +237 -0
- package/src/components/ui/popover/index.ts +2 -0
- package/src/components/ui/progress/Progress.stories.tsx +190 -0
- package/src/components/ui/progress/Progress.test.tsx +210 -0
- package/src/components/ui/progress/Progress.tsx +339 -0
- package/src/components/ui/progress/index.ts +9 -0
- package/src/components/ui/radio/Radio.stories.tsx +199 -0
- package/src/components/ui/radio/Radio.test.tsx +166 -0
- package/src/components/ui/radio/Radio.tsx +200 -0
- package/src/components/ui/radio/index.ts +2 -0
- package/src/components/ui/section/Section.stories.tsx +111 -0
- package/src/components/ui/section/Section.test.tsx +121 -0
- package/src/components/ui/section/Section.tsx +52 -0
- package/src/components/ui/section/index.ts +6 -0
- package/src/components/ui/select/Select.stories.tsx +286 -0
- package/src/components/ui/select/Select.test.tsx +200 -0
- package/src/components/ui/select/Select.tsx +243 -0
- package/src/components/ui/select/index.ts +2 -0
- package/src/components/ui/skeleton/Skeleton.stories.tsx +153 -0
- package/src/components/ui/skeleton/Skeleton.test.tsx +162 -0
- package/src/components/ui/skeleton/Skeleton.tsx +229 -0
- package/src/components/ui/skeleton/index.ts +16 -0
- package/src/components/ui/spinner/Spinner.stories.tsx +67 -0
- package/src/components/ui/spinner/Spinner.test.tsx +67 -0
- package/src/components/ui/spinner/Spinner.tsx +72 -0
- package/src/components/ui/spinner/index.ts +2 -0
- package/src/components/ui/stack/Stack.stories.tsx +127 -0
- package/src/components/ui/stack/Stack.test.tsx +184 -0
- package/src/components/ui/stack/Stack.tsx +76 -0
- package/src/components/ui/stack/index.ts +2 -0
- package/src/components/ui/surface/Surface.stories.tsx +91 -0
- package/src/components/ui/surface/Surface.test.tsx +63 -0
- package/src/components/ui/surface/Surface.tsx +51 -0
- package/src/components/ui/surface/index.ts +1 -0
- package/src/components/ui/switch/Switch.stories.tsx +111 -0
- package/src/components/ui/switch/Switch.test.tsx +112 -0
- package/src/components/ui/switch/Switch.tsx +103 -0
- package/src/components/ui/switch/index.ts +2 -0
- package/src/components/ui/tabs/Tabs.stories.tsx +213 -0
- package/src/components/ui/tabs/Tabs.test.tsx +192 -0
- package/src/components/ui/tabs/Tabs.tsx +253 -0
- package/src/components/ui/tabs/index.ts +16 -0
- package/src/components/ui/toast/Toast.stories.tsx +255 -0
- package/src/components/ui/toast/Toast.test.tsx +207 -0
- package/src/components/ui/toast/Toast.tsx +327 -0
- package/src/components/ui/toast/index.ts +9 -0
- package/src/components/ui/toolbar-button/ToolbarButton.stories.tsx +415 -0
- package/src/components/ui/toolbar-button/ToolbarButton.test.tsx +224 -0
- package/src/components/ui/toolbar-button/ToolbarButton.tsx +307 -0
- package/src/components/ui/toolbar-button/index.ts +7 -0
- package/src/components/ui/tooltip/Tooltip.stories.tsx +126 -0
- package/src/components/ui/tooltip/Tooltip.test.tsx +262 -0
- package/src/components/ui/tooltip/Tooltip.tsx +234 -0
- package/src/components/ui/tooltip/index.ts +2 -0
- package/src/index.ts +10 -0
- package/src/stories/PresetShowcase.stories.tsx +462 -0
- package/src/stories/ThemePresets.stories.tsx +85 -0
- package/src/test/setup.ts +19 -0
- package/src/theme/Colors.stories.tsx +258 -0
- package/src/theme/config.ts +193 -0
- package/src/theme/elevation.stories.tsx +346 -0
- package/src/theme/elevation.test.ts +27 -0
- package/src/theme/elevation.ts +567 -0
- package/src/theme/global.css +129 -1
- package/src/theme/index.ts +40 -0
- package/src/theme/presets/apply.ts +97 -0
- package/src/theme/presets/audiobook.ts +93 -0
- package/src/theme/presets/default.ts +6 -0
- package/src/theme/presets/index.ts +4 -0
- package/src/theme/presets/presets.test.ts +51 -0
- package/src/theme/presets/types.ts +76 -0
- package/src/theme/shadows.stories.tsx +523 -0
- package/src/theme/shadows.ts +439 -0
- package/src/theme/tokens/index.ts +2 -0
- package/src/theme/tokens/primitives.ts +354 -0
- package/src/theme/tokens/semantic.ts +381 -0
- package/src/types/jest-axe.d.ts +16 -0
- package/src/types/nativewind.d.ts +54 -0
- package/src/utils/avatar-color.test.ts +30 -0
- package/src/utils/avatar-color.ts +20 -0
- package/src/utils/cn.ts +14 -0
- package/src/utils/colors.ts +140 -0
- package/src/utils/form.ts +188 -0
- package/src/utils/index.ts +29 -0
- package/src/utils/useTheme.ts +72 -0
- package/src/web-jsx/jsx-dev-runtime.ts +28 -0
- package/src/web-jsx/jsx-runtime.ts +41 -0
- package/tailwind.config.js +53 -3
- package/dist/chunk-MGW37MPO.mjs.map +0 -1
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react'
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
3
|
+
import { View, Text, Pressable } from 'react-native'
|
|
4
|
+
import { Button, ButtonText } from '../components/ui/button'
|
|
5
|
+
import { Card } from '../components/ui/card'
|
|
6
|
+
import { Input } from '../components/ui/input'
|
|
7
|
+
import { Progress, CircularProgress } from '../components/ui/progress'
|
|
8
|
+
import { Badge } from '../components/ui/badge'
|
|
9
|
+
import { Toast } from '../components/ui/toast'
|
|
10
|
+
import { Switch } from '../components/ui/switch'
|
|
11
|
+
import { applyThemePreset, audiobookPreset, defaultPreset } from '../theme/presets'
|
|
12
|
+
import type { ThemePreset } from '../theme/presets'
|
|
13
|
+
|
|
14
|
+
const presets: Record<string, ThemePreset> = {
|
|
15
|
+
default: defaultPreset,
|
|
16
|
+
audiobook: audiobookPreset,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function PresetSwitcher({
|
|
20
|
+
activePreset,
|
|
21
|
+
onSwitch,
|
|
22
|
+
}: {
|
|
23
|
+
activePreset: string
|
|
24
|
+
onSwitch: (name: string) => void
|
|
25
|
+
}) {
|
|
26
|
+
return (
|
|
27
|
+
<View className="flex-row items-center gap-3 p-4 bg-surface-elevated rounded-lg border border-border-default">
|
|
28
|
+
<Text className="text-text-secondary text-sm font-medium">Active Preset:</Text>
|
|
29
|
+
{Object.keys(presets).map((name) => (
|
|
30
|
+
<Pressable
|
|
31
|
+
key={name}
|
|
32
|
+
onPress={() => onSwitch(name)}
|
|
33
|
+
className={`px-4 py-2 rounded-md border ${
|
|
34
|
+
activePreset === name
|
|
35
|
+
? 'bg-brand-primary border-brand-primary'
|
|
36
|
+
: 'bg-surface-base border-border-default web:hover:bg-interactive-hover'
|
|
37
|
+
}`}
|
|
38
|
+
>
|
|
39
|
+
<Text
|
|
40
|
+
className={`text-sm font-semibold capitalize ${
|
|
41
|
+
activePreset === name ? 'text-on-brand-primary' : 'text-text-primary'
|
|
42
|
+
}`}
|
|
43
|
+
>
|
|
44
|
+
{name}
|
|
45
|
+
</Text>
|
|
46
|
+
</Pressable>
|
|
47
|
+
))}
|
|
48
|
+
</View>
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function usePresetToggle(initial = 'default') {
|
|
53
|
+
const [activePreset, setActivePreset] = useState(initial)
|
|
54
|
+
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
const cleanup = applyThemePreset(presets[activePreset])
|
|
57
|
+
return cleanup
|
|
58
|
+
}, [activePreset])
|
|
59
|
+
|
|
60
|
+
return { activePreset, setActivePreset }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
// SideBySide Story
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
|
|
67
|
+
function SideBySideShowcase() {
|
|
68
|
+
const { activePreset, setActivePreset } = usePresetToggle()
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<View className="gap-6 p-6 bg-background-default min-h-screen">
|
|
72
|
+
<View className="gap-2">
|
|
73
|
+
<Text className="text-3xl font-bold text-text-primary font-heading">
|
|
74
|
+
Preset Showcase
|
|
75
|
+
</Text>
|
|
76
|
+
<Text className="text-text-secondary text-base">
|
|
77
|
+
Toggle between presets to see how the entire design system adapts.
|
|
78
|
+
</Text>
|
|
79
|
+
</View>
|
|
80
|
+
|
|
81
|
+
<PresetSwitcher activePreset={activePreset} onSwitch={setActivePreset} />
|
|
82
|
+
|
|
83
|
+
{/* Typography */}
|
|
84
|
+
<Section title="Typography">
|
|
85
|
+
<Text className="text-3xl font-bold text-text-primary font-heading">
|
|
86
|
+
Heading (font-heading)
|
|
87
|
+
</Text>
|
|
88
|
+
<Text className="text-xl font-semibold text-text-primary font-heading">
|
|
89
|
+
Subheading (font-heading)
|
|
90
|
+
</Text>
|
|
91
|
+
<Text className="text-base text-text-primary font-body">
|
|
92
|
+
Body text using font-body. The quick brown fox jumps over the lazy dog. This
|
|
93
|
+
demonstrates how paragraph text renders under the current preset.
|
|
94
|
+
</Text>
|
|
95
|
+
<Text className="text-sm text-text-secondary font-body">
|
|
96
|
+
Secondary body text for captions and supporting content.
|
|
97
|
+
</Text>
|
|
98
|
+
<Text className="text-sm text-text-primary font-mono">
|
|
99
|
+
{'const monospace = \'font-mono rendering\''}
|
|
100
|
+
</Text>
|
|
101
|
+
</Section>
|
|
102
|
+
|
|
103
|
+
{/* Buttons */}
|
|
104
|
+
<Section title="Buttons">
|
|
105
|
+
<View className="gap-3">
|
|
106
|
+
<Text className="text-xs font-semibold text-text-tertiary uppercase tracking-wider">
|
|
107
|
+
Primary
|
|
108
|
+
</Text>
|
|
109
|
+
<View className="flex-row gap-3 flex-wrap">
|
|
110
|
+
<Button variant="solid" color="primary">
|
|
111
|
+
<ButtonText>Solid</ButtonText>
|
|
112
|
+
</Button>
|
|
113
|
+
<Button variant="outline" color="primary">
|
|
114
|
+
<ButtonText>Outline</ButtonText>
|
|
115
|
+
</Button>
|
|
116
|
+
<Button variant="ghost" color="primary">
|
|
117
|
+
<ButtonText>Ghost</ButtonText>
|
|
118
|
+
</Button>
|
|
119
|
+
</View>
|
|
120
|
+
<Text className="text-xs font-semibold text-text-tertiary uppercase tracking-wider">
|
|
121
|
+
Secondary
|
|
122
|
+
</Text>
|
|
123
|
+
<View className="flex-row gap-3 flex-wrap">
|
|
124
|
+
<Button variant="solid" color="secondary">
|
|
125
|
+
<ButtonText>Solid</ButtonText>
|
|
126
|
+
</Button>
|
|
127
|
+
<Button variant="outline" color="secondary">
|
|
128
|
+
<ButtonText>Outline</ButtonText>
|
|
129
|
+
</Button>
|
|
130
|
+
<Button variant="ghost" color="secondary">
|
|
131
|
+
<ButtonText>Ghost</ButtonText>
|
|
132
|
+
</Button>
|
|
133
|
+
</View>
|
|
134
|
+
</View>
|
|
135
|
+
</Section>
|
|
136
|
+
|
|
137
|
+
{/* Cards with Badges */}
|
|
138
|
+
<Section title="Cards">
|
|
139
|
+
<View className="flex-row gap-4 flex-wrap">
|
|
140
|
+
<Card className="p-4 gap-3 flex-1 min-w-[260px]">
|
|
141
|
+
<Text className="text-lg font-semibold text-text-primary font-heading">
|
|
142
|
+
Elevated Card
|
|
143
|
+
</Text>
|
|
144
|
+
<Text className="text-text-secondary text-sm">
|
|
145
|
+
Surface elevation and border colors adapt to the active preset.
|
|
146
|
+
</Text>
|
|
147
|
+
<View className="flex-row gap-2 flex-wrap">
|
|
148
|
+
<Badge color="success">Complete</Badge>
|
|
149
|
+
<Badge color="warning">Pending</Badge>
|
|
150
|
+
<Badge color="error">Failed</Badge>
|
|
151
|
+
</View>
|
|
152
|
+
</Card>
|
|
153
|
+
<Card className="p-4 gap-3 flex-1 min-w-[260px]">
|
|
154
|
+
<Text className="text-lg font-semibold text-text-primary font-heading">
|
|
155
|
+
Content Card
|
|
156
|
+
</Text>
|
|
157
|
+
<Text className="text-text-secondary text-sm">
|
|
158
|
+
Brand secondary and info badges shown below for comparison.
|
|
159
|
+
</Text>
|
|
160
|
+
<View className="flex-row gap-2 flex-wrap">
|
|
161
|
+
<Badge color="info">Info</Badge>
|
|
162
|
+
<Badge color="secondary">Secondary</Badge>
|
|
163
|
+
</View>
|
|
164
|
+
</Card>
|
|
165
|
+
</View>
|
|
166
|
+
</Section>
|
|
167
|
+
|
|
168
|
+
{/* Input + Switch */}
|
|
169
|
+
<Section title="Inputs">
|
|
170
|
+
<View className="gap-3 max-w-[400px]">
|
|
171
|
+
<Input placeholder="Default input..." />
|
|
172
|
+
<Input placeholder="Disabled input..." isDisabled />
|
|
173
|
+
<View className="flex-row items-center gap-3">
|
|
174
|
+
<Switch />
|
|
175
|
+
<Text className="text-text-primary text-sm">Toggle switch</Text>
|
|
176
|
+
</View>
|
|
177
|
+
</View>
|
|
178
|
+
</Section>
|
|
179
|
+
|
|
180
|
+
{/* Progress */}
|
|
181
|
+
<Section title="Progress">
|
|
182
|
+
<View className="gap-4">
|
|
183
|
+
<Progress value={65} label="Primary" showValue color="primary" />
|
|
184
|
+
<Progress value={40} label="Secondary" showValue color="secondary" />
|
|
185
|
+
<Progress value={100} label="Success" showValue color="success" />
|
|
186
|
+
<View className="flex-row gap-4 items-center flex-wrap">
|
|
187
|
+
<CircularProgress value={25} size={48} showValue color="primary" />
|
|
188
|
+
<CircularProgress value={50} size={48} showValue color="secondary" />
|
|
189
|
+
<CircularProgress value={75} size={48} showValue color="success" />
|
|
190
|
+
<CircularProgress value={100} size={48} showValue color="error" />
|
|
191
|
+
</View>
|
|
192
|
+
</View>
|
|
193
|
+
</Section>
|
|
194
|
+
|
|
195
|
+
{/* Toasts */}
|
|
196
|
+
<Section title="Toasts">
|
|
197
|
+
<View className="gap-3">
|
|
198
|
+
<Toast title="Operation successful" description="Your changes have been saved." status="success" />
|
|
199
|
+
<Toast title="Something went wrong" description="Please try again later." status="error" />
|
|
200
|
+
<Toast title="Heads up" description="This action cannot be undone." status="warning" />
|
|
201
|
+
<Toast title="New update available" description="Version 2.0 is ready to install." status="info" />
|
|
202
|
+
</View>
|
|
203
|
+
</Section>
|
|
204
|
+
|
|
205
|
+
{/* Status Colors */}
|
|
206
|
+
<Section title="Status Colors">
|
|
207
|
+
<View className="flex-row gap-3 flex-wrap">
|
|
208
|
+
<Badge color="success" size="lg">Success</Badge>
|
|
209
|
+
<Badge color="error" size="lg">Error</Badge>
|
|
210
|
+
<Badge color="warning" size="lg">Warning</Badge>
|
|
211
|
+
<Badge color="info" size="lg">Info</Badge>
|
|
212
|
+
</View>
|
|
213
|
+
</Section>
|
|
214
|
+
</View>
|
|
215
|
+
)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// ---------------------------------------------------------------------------
|
|
219
|
+
// Typography Story
|
|
220
|
+
// ---------------------------------------------------------------------------
|
|
221
|
+
|
|
222
|
+
function TypographyShowcase() {
|
|
223
|
+
const { activePreset, setActivePreset } = usePresetToggle()
|
|
224
|
+
|
|
225
|
+
return (
|
|
226
|
+
<View className="gap-6 p-6 bg-background-default min-h-screen">
|
|
227
|
+
<View className="gap-2">
|
|
228
|
+
<Text className="text-3xl font-bold text-text-primary font-heading">
|
|
229
|
+
Typography
|
|
230
|
+
</Text>
|
|
231
|
+
<Text className="text-text-secondary text-base">
|
|
232
|
+
How fonts and text styles change across presets.
|
|
233
|
+
</Text>
|
|
234
|
+
</View>
|
|
235
|
+
|
|
236
|
+
<PresetSwitcher activePreset={activePreset} onSwitch={setActivePreset} />
|
|
237
|
+
|
|
238
|
+
<Section title="Heading Font (font-heading)">
|
|
239
|
+
<Text className="text-4xl font-bold text-text-primary font-heading">
|
|
240
|
+
H1 - The Art of Design Systems
|
|
241
|
+
</Text>
|
|
242
|
+
<Text className="text-3xl font-bold text-text-primary font-heading">
|
|
243
|
+
H2 - Component Architecture
|
|
244
|
+
</Text>
|
|
245
|
+
<Text className="text-2xl font-semibold text-text-primary font-heading">
|
|
246
|
+
H3 - Token-Driven Theming
|
|
247
|
+
</Text>
|
|
248
|
+
<Text className="text-xl font-semibold text-text-primary font-heading">
|
|
249
|
+
H4 - Cross-Platform Consistency
|
|
250
|
+
</Text>
|
|
251
|
+
</Section>
|
|
252
|
+
|
|
253
|
+
<Section title="Body Font (font-body)">
|
|
254
|
+
<Text className="text-base text-text-primary font-body">
|
|
255
|
+
Regular body text. Design systems provide a shared language for product teams.
|
|
256
|
+
By codifying decisions into reusable components and tokens, teams move faster
|
|
257
|
+
while maintaining visual consistency across every surface.
|
|
258
|
+
</Text>
|
|
259
|
+
<Text className="text-base font-semibold text-text-primary font-body">
|
|
260
|
+
Bold body text. Emphasis and hierarchy within paragraph content.
|
|
261
|
+
</Text>
|
|
262
|
+
<Text className="text-base italic text-text-primary font-body">
|
|
263
|
+
Italic body text. Used for quotations, citations, and emphasis.
|
|
264
|
+
</Text>
|
|
265
|
+
<Text className="text-sm text-text-secondary font-body">
|
|
266
|
+
Small / secondary text. Captions, labels, and supporting information
|
|
267
|
+
that supplements the main content without competing for attention.
|
|
268
|
+
</Text>
|
|
269
|
+
<Text className="text-xs text-text-tertiary font-body">
|
|
270
|
+
Extra-small / tertiary text. Timestamps, metadata, fine print.
|
|
271
|
+
</Text>
|
|
272
|
+
</Section>
|
|
273
|
+
|
|
274
|
+
<Section title="Monospace Font (font-mono)">
|
|
275
|
+
<View className="bg-surface-elevated rounded-lg p-4 border border-border-default">
|
|
276
|
+
<Text className="text-sm text-text-primary font-mono">
|
|
277
|
+
{'const theme = applyThemePreset(audiobookPreset)'}
|
|
278
|
+
</Text>
|
|
279
|
+
<Text className="text-sm text-text-secondary font-mono">
|
|
280
|
+
{'// Overrides CSS custom properties on :root'}
|
|
281
|
+
</Text>
|
|
282
|
+
<Text className="text-sm text-text-primary font-mono">
|
|
283
|
+
{'export type ThemePreset = { name: string }'}
|
|
284
|
+
</Text>
|
|
285
|
+
</View>
|
|
286
|
+
</Section>
|
|
287
|
+
|
|
288
|
+
<Section title="Mixed Typography">
|
|
289
|
+
<Card className="p-5 gap-3">
|
|
290
|
+
<Text className="text-2xl font-bold text-text-primary font-heading">
|
|
291
|
+
Chapter One
|
|
292
|
+
</Text>
|
|
293
|
+
<Text className="text-base text-text-secondary font-body">
|
|
294
|
+
It was a bright cold day in April, and the clocks were striking thirteen.
|
|
295
|
+
Winston Smith, his chin nuzzled into his breast in an effort to escape the
|
|
296
|
+
vile wind, slipped quickly through the glass doors of Victory Mansions,
|
|
297
|
+
though not quickly enough to prevent a swirl of gritty dust from entering
|
|
298
|
+
along with him.
|
|
299
|
+
</Text>
|
|
300
|
+
<Text className="text-xs text-text-tertiary font-mono">
|
|
301
|
+
George Orwell, 1984
|
|
302
|
+
</Text>
|
|
303
|
+
</Card>
|
|
304
|
+
</Section>
|
|
305
|
+
</View>
|
|
306
|
+
)
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// ---------------------------------------------------------------------------
|
|
310
|
+
// ColorPalette Story
|
|
311
|
+
// ---------------------------------------------------------------------------
|
|
312
|
+
|
|
313
|
+
function ColorSwatch({
|
|
314
|
+
label,
|
|
315
|
+
className,
|
|
316
|
+
textClass = 'text-white',
|
|
317
|
+
}: {
|
|
318
|
+
label: string
|
|
319
|
+
className: string
|
|
320
|
+
textClass?: string
|
|
321
|
+
}) {
|
|
322
|
+
return (
|
|
323
|
+
<View className={`rounded-lg p-3 min-w-[120px] min-h-[60px] justify-end ${className}`}>
|
|
324
|
+
<Text className={`text-xs font-mono font-medium ${textClass}`}>{label}</Text>
|
|
325
|
+
</View>
|
|
326
|
+
)
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function ColorPaletteShowcase() {
|
|
330
|
+
const { activePreset, setActivePreset } = usePresetToggle()
|
|
331
|
+
|
|
332
|
+
return (
|
|
333
|
+
<View className="gap-6 p-6 bg-background-default min-h-screen">
|
|
334
|
+
<View className="gap-2">
|
|
335
|
+
<Text className="text-3xl font-bold text-text-primary font-heading">
|
|
336
|
+
Color Palette
|
|
337
|
+
</Text>
|
|
338
|
+
<Text className="text-text-secondary text-base">
|
|
339
|
+
Semantic color tokens rendered as swatches. Toggle presets to compare palettes.
|
|
340
|
+
</Text>
|
|
341
|
+
</View>
|
|
342
|
+
|
|
343
|
+
<PresetSwitcher activePreset={activePreset} onSwitch={setActivePreset} />
|
|
344
|
+
|
|
345
|
+
<Section title="Brand Colors">
|
|
346
|
+
<View className="flex-row gap-3 flex-wrap">
|
|
347
|
+
<ColorSwatch label="brand-primary" className="bg-brand-primary" />
|
|
348
|
+
<ColorSwatch label="brand-primary-light" className="bg-brand-primary-light" />
|
|
349
|
+
<ColorSwatch label="brand-primary-dark" className="bg-brand-primary-dark" />
|
|
350
|
+
<ColorSwatch label="brand-primary-subtle" className="bg-brand-primary-subtle" textClass="text-text-primary" />
|
|
351
|
+
</View>
|
|
352
|
+
<View className="flex-row gap-3 flex-wrap mt-2">
|
|
353
|
+
<ColorSwatch label="brand-secondary" className="bg-brand-secondary" />
|
|
354
|
+
<ColorSwatch label="brand-secondary-light" className="bg-brand-secondary-light" />
|
|
355
|
+
<ColorSwatch label="brand-secondary-dark" className="bg-brand-secondary-dark" />
|
|
356
|
+
<ColorSwatch label="brand-secondary-subtle" className="bg-brand-secondary-subtle" textClass="text-text-primary" />
|
|
357
|
+
</View>
|
|
358
|
+
</Section>
|
|
359
|
+
|
|
360
|
+
<Section title="Status Colors">
|
|
361
|
+
<View className="flex-row gap-3 flex-wrap">
|
|
362
|
+
<ColorSwatch label="status-success" className="bg-status-success" />
|
|
363
|
+
<ColorSwatch label="status-error" className="bg-status-error" />
|
|
364
|
+
<ColorSwatch label="status-warning" className="bg-status-warning" />
|
|
365
|
+
<ColorSwatch label="status-info" className="bg-status-info" />
|
|
366
|
+
</View>
|
|
367
|
+
</Section>
|
|
368
|
+
|
|
369
|
+
<Section title="Text Colors">
|
|
370
|
+
<View className="flex-row gap-3 flex-wrap">
|
|
371
|
+
<View className="rounded-lg p-3 min-w-[120px] min-h-[60px] justify-end bg-surface-elevated border border-border-default">
|
|
372
|
+
<Text className="text-xs font-mono font-medium text-text-primary">text-primary</Text>
|
|
373
|
+
</View>
|
|
374
|
+
<View className="rounded-lg p-3 min-w-[120px] min-h-[60px] justify-end bg-surface-elevated border border-border-default">
|
|
375
|
+
<Text className="text-xs font-mono font-medium text-text-secondary">text-secondary</Text>
|
|
376
|
+
</View>
|
|
377
|
+
<View className="rounded-lg p-3 min-w-[120px] min-h-[60px] justify-end bg-surface-elevated border border-border-default">
|
|
378
|
+
<Text className="text-xs font-mono font-medium text-text-tertiary">text-tertiary</Text>
|
|
379
|
+
</View>
|
|
380
|
+
</View>
|
|
381
|
+
</Section>
|
|
382
|
+
|
|
383
|
+
<Section title="Surface Colors">
|
|
384
|
+
<View className="flex-row gap-3 flex-wrap">
|
|
385
|
+
<ColorSwatch label="surface-base" className="bg-surface-base border border-border-default" textClass="text-text-primary" />
|
|
386
|
+
<ColorSwatch label="surface-elevated" className="bg-surface-elevated border border-border-default" textClass="text-text-primary" />
|
|
387
|
+
<ColorSwatch label="surface-raised" className="bg-surface-raised border border-border-default" textClass="text-text-primary" />
|
|
388
|
+
<ColorSwatch label="surface-overlay" className="bg-surface-overlay border border-border-default" textClass="text-text-primary" />
|
|
389
|
+
<ColorSwatch label="surface-input" className="bg-surface-input border border-border-default" textClass="text-text-primary" />
|
|
390
|
+
</View>
|
|
391
|
+
</Section>
|
|
392
|
+
|
|
393
|
+
<Section title="Background Colors">
|
|
394
|
+
<View className="flex-row gap-3 flex-wrap">
|
|
395
|
+
<ColorSwatch label="background-base" className="bg-background-base border border-border-default" textClass="text-text-primary" />
|
|
396
|
+
<ColorSwatch label="background-default" className="bg-background-default border border-border-default" textClass="text-text-primary" />
|
|
397
|
+
<ColorSwatch label="background-subtle" className="bg-background-subtle border border-border-default" textClass="text-text-primary" />
|
|
398
|
+
</View>
|
|
399
|
+
</Section>
|
|
400
|
+
|
|
401
|
+
<Section title="Border Colors">
|
|
402
|
+
<View className="flex-row gap-3 flex-wrap">
|
|
403
|
+
<View className="rounded-lg p-3 min-w-[120px] min-h-[60px] justify-end bg-surface-base border-2 border-border-default">
|
|
404
|
+
<Text className="text-xs font-mono font-medium text-text-primary">border-default</Text>
|
|
405
|
+
</View>
|
|
406
|
+
<View className="rounded-lg p-3 min-w-[120px] min-h-[60px] justify-end bg-surface-base border-2 border-border-subtle">
|
|
407
|
+
<Text className="text-xs font-mono font-medium text-text-primary">border-subtle</Text>
|
|
408
|
+
</View>
|
|
409
|
+
<View className="rounded-lg p-3 min-w-[120px] min-h-[60px] justify-end bg-surface-base border-2 border-border-strong">
|
|
410
|
+
<Text className="text-xs font-mono font-medium text-text-primary">border-strong</Text>
|
|
411
|
+
</View>
|
|
412
|
+
<View className="rounded-lg p-3 min-w-[120px] min-h-[60px] justify-end bg-surface-base border-2 border-border-focus">
|
|
413
|
+
<Text className="text-xs font-mono font-medium text-text-primary">border-focus</Text>
|
|
414
|
+
</View>
|
|
415
|
+
</View>
|
|
416
|
+
</Section>
|
|
417
|
+
</View>
|
|
418
|
+
)
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// ---------------------------------------------------------------------------
|
|
422
|
+
// Shared Section Component
|
|
423
|
+
// ---------------------------------------------------------------------------
|
|
424
|
+
|
|
425
|
+
function Section({ title, children }: { title: string; children: React.ReactNode }) {
|
|
426
|
+
return (
|
|
427
|
+
<View className="gap-3">
|
|
428
|
+
<Text className="text-lg font-semibold text-text-primary font-heading border-b border-border-subtle pb-2">
|
|
429
|
+
{title}
|
|
430
|
+
</Text>
|
|
431
|
+
{children}
|
|
432
|
+
</View>
|
|
433
|
+
)
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// ---------------------------------------------------------------------------
|
|
437
|
+
// Storybook Meta & Exports
|
|
438
|
+
// ---------------------------------------------------------------------------
|
|
439
|
+
|
|
440
|
+
const meta: Meta = {
|
|
441
|
+
title: 'Theme/Preset Showcase',
|
|
442
|
+
parameters: {
|
|
443
|
+
layout: 'fullscreen',
|
|
444
|
+
},
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
export default meta
|
|
448
|
+
|
|
449
|
+
export const SideBySide: StoryObj = {
|
|
450
|
+
render: () => <SideBySideShowcase />,
|
|
451
|
+
name: 'Component Showcase',
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
export const Typography: StoryObj = {
|
|
455
|
+
render: () => <TypographyShowcase />,
|
|
456
|
+
name: 'Typography',
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
export const ColorPalette: StoryObj = {
|
|
460
|
+
render: () => <ColorPaletteShowcase />,
|
|
461
|
+
name: 'Color Palette',
|
|
462
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import React, { useEffect } from 'react'
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
3
|
+
import { View, Text } from 'react-native'
|
|
4
|
+
import { Button, ButtonText } from '../components/ui/button'
|
|
5
|
+
import { Card } from '../components/ui/card'
|
|
6
|
+
import { Input } from '../components/ui/input'
|
|
7
|
+
import { Progress, CircularProgress } from '../components/ui/progress'
|
|
8
|
+
import { Badge } from '../components/ui/badge'
|
|
9
|
+
import { applyThemePreset, audiobookPreset, defaultPreset } from '../theme/presets'
|
|
10
|
+
|
|
11
|
+
function PresetShowcase({ preset }: { preset: typeof audiobookPreset }) {
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
const cleanup = applyThemePreset(preset)
|
|
14
|
+
return cleanup
|
|
15
|
+
}, [preset])
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<View className="gap-6 p-6 bg-background-default min-h-screen">
|
|
19
|
+
<Text className="text-2xl font-bold text-text-primary font-heading">
|
|
20
|
+
{preset.name} preset
|
|
21
|
+
</Text>
|
|
22
|
+
|
|
23
|
+
{/* Buttons */}
|
|
24
|
+
<View className="flex-row gap-3 flex-wrap">
|
|
25
|
+
<Button variant="solid" color="primary">
|
|
26
|
+
<ButtonText>Primary</ButtonText>
|
|
27
|
+
</Button>
|
|
28
|
+
<Button variant="solid" color="secondary">
|
|
29
|
+
<ButtonText>Secondary</ButtonText>
|
|
30
|
+
</Button>
|
|
31
|
+
<Button variant="outline" color="primary">
|
|
32
|
+
<ButtonText>Outline</ButtonText>
|
|
33
|
+
</Button>
|
|
34
|
+
<Button variant="ghost" color="primary">
|
|
35
|
+
<ButtonText>Ghost</ButtonText>
|
|
36
|
+
</Button>
|
|
37
|
+
</View>
|
|
38
|
+
|
|
39
|
+
{/* Card */}
|
|
40
|
+
<Card className="p-4 gap-3">
|
|
41
|
+
<Text className="text-lg font-semibold text-text-primary">Sample Card</Text>
|
|
42
|
+
<Text className="text-text-secondary">
|
|
43
|
+
This card demonstrates surface elevation and border colors with the current preset.
|
|
44
|
+
</Text>
|
|
45
|
+
<View className="flex-row gap-2">
|
|
46
|
+
<Badge color="success">Complete</Badge>
|
|
47
|
+
<Badge color="warning">Pending</Badge>
|
|
48
|
+
<Badge color="error">Failed</Badge>
|
|
49
|
+
</View>
|
|
50
|
+
</Card>
|
|
51
|
+
|
|
52
|
+
{/* Input */}
|
|
53
|
+
<Input placeholder="Type something..." />
|
|
54
|
+
|
|
55
|
+
{/* Progress */}
|
|
56
|
+
<View className="gap-3">
|
|
57
|
+
<Progress value={65} label="Processing" showValue color="primary" />
|
|
58
|
+
<View className="flex-row gap-4 items-center">
|
|
59
|
+
<CircularProgress value={25} size={48} showValue color="primary" />
|
|
60
|
+
<CircularProgress value={75} size={48} showValue color="secondary" />
|
|
61
|
+
<CircularProgress value={100} size={48} showValue color="success" />
|
|
62
|
+
</View>
|
|
63
|
+
</View>
|
|
64
|
+
</View>
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const meta: Meta = {
|
|
69
|
+
title: 'Theme/Presets',
|
|
70
|
+
parameters: {
|
|
71
|
+
layout: 'fullscreen',
|
|
72
|
+
},
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export default meta
|
|
76
|
+
|
|
77
|
+
export const Default: StoryObj = {
|
|
78
|
+
render: () => <PresetShowcase preset={defaultPreset} />,
|
|
79
|
+
name: 'Default Preset',
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export const Audiobook: StoryObj = {
|
|
83
|
+
render: () => <PresetShowcase preset={audiobookPreset} />,
|
|
84
|
+
name: 'Audiobook Preset',
|
|
85
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/// <reference types="vitest/globals" />
|
|
2
|
+
import '@testing-library/jest-dom'
|
|
3
|
+
import { expect, afterEach } from 'vitest'
|
|
4
|
+
import { cleanup } from '@testing-library/react'
|
|
5
|
+
import { toHaveNoViolations } from 'jest-axe'
|
|
6
|
+
|
|
7
|
+
// Extend Vitest's expect with jest-axe matchers
|
|
8
|
+
expect.extend(toHaveNoViolations)
|
|
9
|
+
|
|
10
|
+
// Cleanup after each test
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
cleanup()
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
// Mock react-native components for web testing
|
|
16
|
+
vi.mock('react-native', async () => {
|
|
17
|
+
const RNW = await vi.importActual('react-native-web')
|
|
18
|
+
return RNW
|
|
19
|
+
})
|