@titan-design/react-ui 0.0.2 → 0.1.1
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/{chunk-NA3EES23.mjs → chunk-MGW37MPO.mjs} +26 -4
- package/dist/chunk-MGW37MPO.mjs.map +1 -0
- package/dist/index.d.mts +115 -2
- package/dist/index.d.ts +115 -2
- package/dist/index.js +262 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +225 -3
- package/dist/index.mjs.map +1 -1
- package/dist/theme/index.d.mts +124 -109
- package/dist/theme/index.d.ts +124 -109
- package/dist/theme/index.js +24 -1
- package/dist/theme/index.js.map +1 -1
- package/dist/theme/index.mjs +1 -1
- package/package.json +12 -5
- 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/index.ts +8 -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 +135 -0
- package/src/components/ui/avatar/Avatar.tsx +168 -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 +86 -0
- package/src/components/ui/badge/Badge.tsx +105 -0
- package/src/components/ui/badge/index.ts +2 -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 +242 -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 +265 -0
- package/src/components/ui/card/Card.tsx +380 -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 +36 -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/popover/Popover.stories.tsx +190 -0
- package/src/components/ui/popover/Popover.test.tsx +277 -0
- package/src/components/ui/popover/Popover.tsx +162 -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 +181 -0
- package/src/components/ui/progress/Progress.tsx +320 -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 +180 -0
- package/src/components/ui/select/Select.tsx +236 -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 +8 -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 +179 -0
- package/src/components/ui/tooltip/Tooltip.tsx +143 -0
- package/src/components/ui/tooltip/index.ts +2 -0
- package/src/index.ts +10 -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/index.ts +39 -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/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/dist/chunk-NA3EES23.mjs.map +0 -1
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import React, { createContext, useContext, useState } from 'react'
|
|
2
|
+
import { View, Text, Pressable, ScrollView, type ViewProps } from 'react-native'
|
|
3
|
+
import { cn } from '../../../utils/cn'
|
|
4
|
+
|
|
5
|
+
export type TabsVariant = 'line' | 'enclosed' | 'soft-rounded'
|
|
6
|
+
export type TabsOrientation = 'horizontal' | 'vertical'
|
|
7
|
+
|
|
8
|
+
interface TabsContextType {
|
|
9
|
+
activeIndex: number
|
|
10
|
+
setActiveIndex: (index: number) => void
|
|
11
|
+
variant: TabsVariant
|
|
12
|
+
orientation: TabsOrientation
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const TabsContext = createContext<TabsContextType>({
|
|
16
|
+
activeIndex: 0,
|
|
17
|
+
setActiveIndex: () => {},
|
|
18
|
+
variant: 'line',
|
|
19
|
+
orientation: 'horizontal',
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
export interface TabsProps extends ViewProps {
|
|
23
|
+
/** Currently active tab index */
|
|
24
|
+
defaultIndex?: number
|
|
25
|
+
/** Controlled active index */
|
|
26
|
+
index?: number
|
|
27
|
+
/** Callback when tab changes */
|
|
28
|
+
onChange?: (index: number) => void
|
|
29
|
+
/** Visual variant */
|
|
30
|
+
variant?: TabsVariant
|
|
31
|
+
/** Tab orientation */
|
|
32
|
+
orientation?: TabsOrientation
|
|
33
|
+
/** Additional className */
|
|
34
|
+
className?: string
|
|
35
|
+
children?: React.ReactNode
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Tabs component for tabbed navigation.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* <Tabs defaultIndex={0} onChange={(i) => console.log(i)}>
|
|
43
|
+
* <TabList>
|
|
44
|
+
* <Tab>Tab 1</Tab>
|
|
45
|
+
* <Tab>Tab 2</Tab>
|
|
46
|
+
* <Tab>Tab 3</Tab>
|
|
47
|
+
* </TabList>
|
|
48
|
+
* <TabPanels>
|
|
49
|
+
* <TabPanel>Content 1</TabPanel>
|
|
50
|
+
* <TabPanel>Content 2</TabPanel>
|
|
51
|
+
* <TabPanel>Content 3</TabPanel>
|
|
52
|
+
* </TabPanels>
|
|
53
|
+
* </Tabs>
|
|
54
|
+
*/
|
|
55
|
+
export function Tabs({
|
|
56
|
+
defaultIndex = 0,
|
|
57
|
+
index,
|
|
58
|
+
onChange,
|
|
59
|
+
variant = 'line',
|
|
60
|
+
orientation = 'horizontal',
|
|
61
|
+
className,
|
|
62
|
+
children,
|
|
63
|
+
...props
|
|
64
|
+
}: TabsProps) {
|
|
65
|
+
const [internalIndex, setInternalIndex] = useState(defaultIndex)
|
|
66
|
+
const activeIndex = index ?? internalIndex
|
|
67
|
+
|
|
68
|
+
const setActiveIndex = (newIndex: number) => {
|
|
69
|
+
if (index === undefined) {
|
|
70
|
+
setInternalIndex(newIndex)
|
|
71
|
+
}
|
|
72
|
+
onChange?.(newIndex)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<TabsContext.Provider value={{ activeIndex, setActiveIndex, variant, orientation }}>
|
|
77
|
+
<View
|
|
78
|
+
className={cn(
|
|
79
|
+
orientation === 'vertical' ? 'flex-row' : 'flex-col',
|
|
80
|
+
className
|
|
81
|
+
)}
|
|
82
|
+
{...props}
|
|
83
|
+
>
|
|
84
|
+
{children}
|
|
85
|
+
</View>
|
|
86
|
+
</TabsContext.Provider>
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface TabListProps {
|
|
91
|
+
children?: React.ReactNode
|
|
92
|
+
className?: string
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Container for Tab components.
|
|
97
|
+
*/
|
|
98
|
+
export function TabList({ children, className }: TabListProps) {
|
|
99
|
+
const { variant, orientation } = useContext(TabsContext)
|
|
100
|
+
|
|
101
|
+
const variantStyles = {
|
|
102
|
+
line: orientation === 'horizontal'
|
|
103
|
+
? 'border-b border-border-default'
|
|
104
|
+
: 'border-r border-border-default',
|
|
105
|
+
enclosed: 'bg-surface-raised rounded-lg p-1',
|
|
106
|
+
'soft-rounded': 'bg-surface-raised rounded-full p-1',
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const content = (
|
|
110
|
+
<View
|
|
111
|
+
className={cn(
|
|
112
|
+
orientation === 'horizontal' ? 'flex-row' : 'flex-col',
|
|
113
|
+
variantStyles[variant],
|
|
114
|
+
className
|
|
115
|
+
)}
|
|
116
|
+
>
|
|
117
|
+
{React.Children.map(children, (child, index) => {
|
|
118
|
+
if (React.isValidElement(child)) {
|
|
119
|
+
return React.cloneElement(child as React.ReactElement<any>, { index })
|
|
120
|
+
}
|
|
121
|
+
return child
|
|
122
|
+
})}
|
|
123
|
+
</View>
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
if (orientation === 'horizontal') {
|
|
127
|
+
return (
|
|
128
|
+
<ScrollView
|
|
129
|
+
horizontal
|
|
130
|
+
showsHorizontalScrollIndicator={false}
|
|
131
|
+
contentContainerStyle={{ flexGrow: 1 }}
|
|
132
|
+
>
|
|
133
|
+
{content}
|
|
134
|
+
</ScrollView>
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return content
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export interface TabProps {
|
|
142
|
+
/** Tab index (injected by TabList) */
|
|
143
|
+
index?: number
|
|
144
|
+
/** Whether the tab is disabled */
|
|
145
|
+
isDisabled?: boolean
|
|
146
|
+
/** Additional className */
|
|
147
|
+
className?: string
|
|
148
|
+
children?: React.ReactNode
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Individual tab button.
|
|
153
|
+
*/
|
|
154
|
+
export function Tab({
|
|
155
|
+
index = 0,
|
|
156
|
+
isDisabled = false,
|
|
157
|
+
className,
|
|
158
|
+
children,
|
|
159
|
+
}: TabProps) {
|
|
160
|
+
const { activeIndex, setActiveIndex, variant, orientation } = useContext(TabsContext)
|
|
161
|
+
const isActive = activeIndex === index
|
|
162
|
+
|
|
163
|
+
const baseStyles = 'font-medium text-sm transition-colors'
|
|
164
|
+
|
|
165
|
+
const variantStyles = {
|
|
166
|
+
line: cn(
|
|
167
|
+
'px-4 py-2',
|
|
168
|
+
orientation === 'horizontal' ? '-mb-px border-b-2' : '-mr-px border-r-2',
|
|
169
|
+
isActive
|
|
170
|
+
? 'border-brand-primary text-brand-primary'
|
|
171
|
+
: 'border-transparent text-text-secondary web:hover:text-text-primary'
|
|
172
|
+
),
|
|
173
|
+
enclosed: cn(
|
|
174
|
+
'px-4 py-2 rounded-md',
|
|
175
|
+
isActive
|
|
176
|
+
? 'bg-surface-elevated text-text-primary shadow-sm'
|
|
177
|
+
: 'text-text-secondary web:hover:text-text-primary'
|
|
178
|
+
),
|
|
179
|
+
'soft-rounded': cn(
|
|
180
|
+
'px-4 py-2 rounded-full',
|
|
181
|
+
isActive
|
|
182
|
+
? 'bg-brand-primary text-white'
|
|
183
|
+
: 'text-text-secondary web:hover:text-text-primary'
|
|
184
|
+
),
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return (
|
|
188
|
+
<Pressable
|
|
189
|
+
accessibilityRole="tab"
|
|
190
|
+
accessibilityState={{ selected: isActive, disabled: isDisabled }}
|
|
191
|
+
disabled={isDisabled}
|
|
192
|
+
onPress={() => setActiveIndex(index)}
|
|
193
|
+
className={cn(
|
|
194
|
+
baseStyles,
|
|
195
|
+
variantStyles[variant],
|
|
196
|
+
isDisabled && 'opacity-50 cursor-not-allowed',
|
|
197
|
+
className
|
|
198
|
+
)}
|
|
199
|
+
>
|
|
200
|
+
<Text
|
|
201
|
+
className={cn(
|
|
202
|
+
'font-medium',
|
|
203
|
+
isActive
|
|
204
|
+
? variant === 'soft-rounded'
|
|
205
|
+
? 'text-white'
|
|
206
|
+
: 'text-text-primary'
|
|
207
|
+
: 'text-text-secondary'
|
|
208
|
+
)}
|
|
209
|
+
>
|
|
210
|
+
{children}
|
|
211
|
+
</Text>
|
|
212
|
+
</Pressable>
|
|
213
|
+
)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export interface TabPanelsProps {
|
|
217
|
+
children?: React.ReactNode
|
|
218
|
+
className?: string
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Container for TabPanel components.
|
|
223
|
+
*/
|
|
224
|
+
export function TabPanels({ children, className }: TabPanelsProps) {
|
|
225
|
+
const { activeIndex } = useContext(TabsContext)
|
|
226
|
+
|
|
227
|
+
return (
|
|
228
|
+
<View className={cn('flex-1', className)}>
|
|
229
|
+
{React.Children.map(children, (child, index) => {
|
|
230
|
+
if (React.isValidElement(child) && index === activeIndex) {
|
|
231
|
+
return child
|
|
232
|
+
}
|
|
233
|
+
return null
|
|
234
|
+
})}
|
|
235
|
+
</View>
|
|
236
|
+
)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export interface TabPanelProps {
|
|
240
|
+
children?: React.ReactNode
|
|
241
|
+
className?: string
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Individual tab panel content.
|
|
246
|
+
*/
|
|
247
|
+
export function TabPanel({ children, className }: TabPanelProps) {
|
|
248
|
+
return (
|
|
249
|
+
<View className={cn('py-4', className)}>
|
|
250
|
+
{children}
|
|
251
|
+
</View>
|
|
252
|
+
)
|
|
253
|
+
}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import { View } from 'react-native'
|
|
3
|
+
import { Toast, ToastProvider, useToast } from './Toast'
|
|
4
|
+
import { Button, ButtonText } from '../button'
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof Toast> = {
|
|
7
|
+
title: 'Components/Toast',
|
|
8
|
+
component: Toast,
|
|
9
|
+
tags: ['autodocs'],
|
|
10
|
+
argTypes: {
|
|
11
|
+
status: {
|
|
12
|
+
control: 'select',
|
|
13
|
+
options: ['success', 'error', 'warning', 'info'],
|
|
14
|
+
},
|
|
15
|
+
showIcon: {
|
|
16
|
+
control: 'boolean',
|
|
17
|
+
},
|
|
18
|
+
isClosable: {
|
|
19
|
+
control: 'boolean',
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export default meta
|
|
25
|
+
type Story = StoryObj<typeof Toast>
|
|
26
|
+
|
|
27
|
+
export const Default: Story = {
|
|
28
|
+
args: {
|
|
29
|
+
title: 'Notification',
|
|
30
|
+
description: 'This is a toast notification.',
|
|
31
|
+
status: 'info',
|
|
32
|
+
},
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const Success: Story = {
|
|
36
|
+
args: {
|
|
37
|
+
title: 'Success!',
|
|
38
|
+
description: 'Your changes have been saved successfully.',
|
|
39
|
+
status: 'success',
|
|
40
|
+
},
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const Error: Story = {
|
|
44
|
+
args: {
|
|
45
|
+
title: 'Error',
|
|
46
|
+
description: 'There was a problem processing your request.',
|
|
47
|
+
status: 'error',
|
|
48
|
+
},
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const Warning: Story = {
|
|
52
|
+
args: {
|
|
53
|
+
title: 'Warning',
|
|
54
|
+
description: 'Your session is about to expire.',
|
|
55
|
+
status: 'warning',
|
|
56
|
+
},
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export const Info: Story = {
|
|
60
|
+
args: {
|
|
61
|
+
title: 'Information',
|
|
62
|
+
description: 'A new version is available.',
|
|
63
|
+
status: 'info',
|
|
64
|
+
},
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export const AllStatuses: Story = {
|
|
68
|
+
render: () => (
|
|
69
|
+
<View className="gap-3">
|
|
70
|
+
<Toast title="Success" description="Operation completed successfully." status="success" />
|
|
71
|
+
<Toast title="Error" description="Something went wrong." status="error" />
|
|
72
|
+
<Toast title="Warning" description="Please review your input." status="warning" />
|
|
73
|
+
<Toast title="Info" description="New features are available." status="info" />
|
|
74
|
+
</View>
|
|
75
|
+
),
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export const TitleOnly: Story = {
|
|
79
|
+
render: () => (
|
|
80
|
+
<View className="gap-3">
|
|
81
|
+
<Toast title="File saved" status="success" />
|
|
82
|
+
<Toast title="Connection lost" status="error" />
|
|
83
|
+
<Toast title="Low storage" status="warning" />
|
|
84
|
+
</View>
|
|
85
|
+
),
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export const WithCloseButton: Story = {
|
|
89
|
+
args: {
|
|
90
|
+
title: 'Closable Toast',
|
|
91
|
+
description: 'Click the X to dismiss this notification.',
|
|
92
|
+
status: 'info',
|
|
93
|
+
isClosable: true,
|
|
94
|
+
onClose: () => console.log('Toast closed'),
|
|
95
|
+
},
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export const WithoutIcon: Story = {
|
|
99
|
+
args: {
|
|
100
|
+
title: 'No Icon',
|
|
101
|
+
description: 'This toast has no status icon.',
|
|
102
|
+
showIcon: false,
|
|
103
|
+
},
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Interactive example with provider
|
|
107
|
+
function ToastDemo() {
|
|
108
|
+
const { addToast, removeAllToasts } = useToast()
|
|
109
|
+
|
|
110
|
+
return (
|
|
111
|
+
<View className="gap-3">
|
|
112
|
+
<View className="flex-row flex-wrap gap-2">
|
|
113
|
+
<Button
|
|
114
|
+
onPress={() =>
|
|
115
|
+
addToast({
|
|
116
|
+
title: 'Success!',
|
|
117
|
+
description: 'Your changes have been saved.',
|
|
118
|
+
status: 'success',
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
color="success"
|
|
122
|
+
>
|
|
123
|
+
<ButtonText>Success Toast</ButtonText>
|
|
124
|
+
</Button>
|
|
125
|
+
|
|
126
|
+
<Button
|
|
127
|
+
onPress={() =>
|
|
128
|
+
addToast({
|
|
129
|
+
title: 'Error',
|
|
130
|
+
description: 'Something went wrong.',
|
|
131
|
+
status: 'error',
|
|
132
|
+
})
|
|
133
|
+
}
|
|
134
|
+
color="error"
|
|
135
|
+
>
|
|
136
|
+
<ButtonText>Error Toast</ButtonText>
|
|
137
|
+
</Button>
|
|
138
|
+
|
|
139
|
+
<Button
|
|
140
|
+
onPress={() =>
|
|
141
|
+
addToast({
|
|
142
|
+
title: 'Warning',
|
|
143
|
+
description: 'Please check your input.',
|
|
144
|
+
status: 'warning',
|
|
145
|
+
})
|
|
146
|
+
}
|
|
147
|
+
color="warning"
|
|
148
|
+
>
|
|
149
|
+
<ButtonText>Warning Toast</ButtonText>
|
|
150
|
+
</Button>
|
|
151
|
+
|
|
152
|
+
<Button
|
|
153
|
+
onPress={() =>
|
|
154
|
+
addToast({
|
|
155
|
+
title: 'Info',
|
|
156
|
+
description: 'New updates available.',
|
|
157
|
+
status: 'info',
|
|
158
|
+
})
|
|
159
|
+
}
|
|
160
|
+
color="info"
|
|
161
|
+
>
|
|
162
|
+
<ButtonText>Info Toast</ButtonText>
|
|
163
|
+
</Button>
|
|
164
|
+
</View>
|
|
165
|
+
|
|
166
|
+
<Button variant="outline" onPress={removeAllToasts}>
|
|
167
|
+
<ButtonText>Clear All</ButtonText>
|
|
168
|
+
</Button>
|
|
169
|
+
</View>
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export const Interactive: Story = {
|
|
174
|
+
render: () => (
|
|
175
|
+
<ToastProvider position="top-right" defaultDuration={3000}>
|
|
176
|
+
<ToastDemo />
|
|
177
|
+
</ToastProvider>
|
|
178
|
+
),
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Different positions
|
|
182
|
+
function PositionDemo() {
|
|
183
|
+
const { addToast } = useToast()
|
|
184
|
+
|
|
185
|
+
return (
|
|
186
|
+
<Button
|
|
187
|
+
onPress={() =>
|
|
188
|
+
addToast({
|
|
189
|
+
title: 'Toast at this position',
|
|
190
|
+
status: 'info',
|
|
191
|
+
})
|
|
192
|
+
}
|
|
193
|
+
>
|
|
194
|
+
<ButtonText>Show Toast</ButtonText>
|
|
195
|
+
</Button>
|
|
196
|
+
)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export const TopPosition: Story = {
|
|
200
|
+
render: () => (
|
|
201
|
+
<ToastProvider position="top">
|
|
202
|
+
<PositionDemo />
|
|
203
|
+
</ToastProvider>
|
|
204
|
+
),
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export const BottomPosition: Story = {
|
|
208
|
+
render: () => (
|
|
209
|
+
<ToastProvider position="bottom">
|
|
210
|
+
<PositionDemo />
|
|
211
|
+
</ToastProvider>
|
|
212
|
+
),
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export const LongDuration: Story = {
|
|
216
|
+
render: () => {
|
|
217
|
+
function Demo() {
|
|
218
|
+
const { addToast } = useToast()
|
|
219
|
+
return (
|
|
220
|
+
<View className="gap-2">
|
|
221
|
+
<Button
|
|
222
|
+
onPress={() =>
|
|
223
|
+
addToast({
|
|
224
|
+
title: 'Persistent Toast',
|
|
225
|
+
description: 'This toast will stay for 10 seconds.',
|
|
226
|
+
duration: 10000,
|
|
227
|
+
})
|
|
228
|
+
}
|
|
229
|
+
>
|
|
230
|
+
<ButtonText>10 Second Toast</ButtonText>
|
|
231
|
+
</Button>
|
|
232
|
+
<Button
|
|
233
|
+
onPress={() =>
|
|
234
|
+
addToast({
|
|
235
|
+
title: 'Manual Close Only',
|
|
236
|
+
description: 'This toast will not auto-dismiss.',
|
|
237
|
+
duration: 0,
|
|
238
|
+
isClosable: true,
|
|
239
|
+
})
|
|
240
|
+
}
|
|
241
|
+
variant="outline"
|
|
242
|
+
>
|
|
243
|
+
<ButtonText>Non-Auto-Dismissing Toast</ButtonText>
|
|
244
|
+
</Button>
|
|
245
|
+
</View>
|
|
246
|
+
)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return (
|
|
250
|
+
<ToastProvider position="top-right">
|
|
251
|
+
<Demo />
|
|
252
|
+
</ToastProvider>
|
|
253
|
+
)
|
|
254
|
+
},
|
|
255
|
+
}
|