@titan-design/react-ui 0.1.0 → 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.
Files changed (192) hide show
  1. package/package.json +10 -5
  2. package/src/components/custom/DateTime/DateTime.stories.tsx +180 -0
  3. package/src/components/custom/DateTime/DateTime.test.tsx +152 -0
  4. package/src/components/custom/DateTime/DateTime.tsx +235 -0
  5. package/src/components/custom/DateTime/index.ts +2 -0
  6. package/src/components/custom/EmptyState/EmptyState.stories.tsx +99 -0
  7. package/src/components/custom/EmptyState/EmptyState.test.tsx +106 -0
  8. package/src/components/custom/EmptyState/EmptyState.tsx +68 -0
  9. package/src/components/custom/EmptyState/index.ts +2 -0
  10. package/src/components/custom/Metric/Metric.stories.tsx +133 -0
  11. package/src/components/custom/Metric/Metric.test.tsx +120 -0
  12. package/src/components/custom/Metric/Metric.tsx +92 -0
  13. package/src/components/custom/Metric/index.ts +2 -0
  14. package/src/components/custom/Sidebar/Sidebar.stories.tsx +227 -0
  15. package/src/components/custom/Sidebar/Sidebar.test.tsx +185 -0
  16. package/src/components/custom/Sidebar/Sidebar.tsx +257 -0
  17. package/src/components/custom/Sidebar/index.ts +18 -0
  18. package/src/components/custom/Table/Table.stories.tsx +481 -0
  19. package/src/components/custom/Table/Table.test.tsx +531 -0
  20. package/src/components/custom/Table/Table.tsx +696 -0
  21. package/src/components/custom/Table/index.ts +22 -0
  22. package/src/components/custom/Typography/Typography.stories.tsx +153 -0
  23. package/src/components/custom/Typography/Typography.test.tsx +107 -0
  24. package/src/components/custom/Typography/Typography.tsx +218 -0
  25. package/src/components/custom/Typography/index.ts +19 -0
  26. package/src/components/custom/index.ts +8 -0
  27. package/src/components/custom/stepper/Stepper.stories.tsx +245 -0
  28. package/src/components/custom/stepper/Stepper.test.tsx +230 -0
  29. package/src/components/custom/stepper/Stepper.tsx +250 -0
  30. package/src/components/custom/stepper/index.ts +10 -0
  31. package/src/components/index.ts +5 -0
  32. package/src/components/ui/alert/Alert.stories.tsx +154 -0
  33. package/src/components/ui/alert/Alert.test.tsx +202 -0
  34. package/src/components/ui/alert/Alert.tsx +170 -0
  35. package/src/components/ui/alert/index.ts +8 -0
  36. package/src/components/ui/autocomplete/Autocomplete.stories.tsx +299 -0
  37. package/src/components/ui/autocomplete/Autocomplete.test.tsx +247 -0
  38. package/src/components/ui/autocomplete/Autocomplete.tsx +332 -0
  39. package/src/components/ui/autocomplete/index.ts +2 -0
  40. package/src/components/ui/avatar/Avatar.stories.tsx +105 -0
  41. package/src/components/ui/avatar/Avatar.test.tsx +135 -0
  42. package/src/components/ui/avatar/Avatar.tsx +168 -0
  43. package/src/components/ui/avatar/index.ts +2 -0
  44. package/src/components/ui/badge/Badge.stories.tsx +100 -0
  45. package/src/components/ui/badge/Badge.test.tsx +86 -0
  46. package/src/components/ui/badge/Badge.tsx +105 -0
  47. package/src/components/ui/badge/index.ts +2 -0
  48. package/src/components/ui/breadcrumbs/Breadcrumbs.stories.tsx +70 -0
  49. package/src/components/ui/breadcrumbs/Breadcrumbs.test.tsx +156 -0
  50. package/src/components/ui/breadcrumbs/Breadcrumbs.tsx +127 -0
  51. package/src/components/ui/breadcrumbs/index.ts +2 -0
  52. package/src/components/ui/button/Button.stories.tsx +216 -0
  53. package/src/components/ui/button/Button.test.tsx +132 -0
  54. package/src/components/ui/button/Button.tsx +242 -0
  55. package/src/components/ui/button/index.ts +10 -0
  56. package/src/components/ui/card/Card.stories.tsx +443 -0
  57. package/src/components/ui/card/Card.test.tsx +265 -0
  58. package/src/components/ui/card/Card.tsx +380 -0
  59. package/src/components/ui/card/index.ts +20 -0
  60. package/src/components/ui/checkbox/Checkbox.stories.tsx +126 -0
  61. package/src/components/ui/checkbox/Checkbox.test.tsx +152 -0
  62. package/src/components/ui/checkbox/Checkbox.tsx +163 -0
  63. package/src/components/ui/checkbox/index.ts +2 -0
  64. package/src/components/ui/chip/Chip.stories.tsx +112 -0
  65. package/src/components/ui/chip/Chip.test.tsx +119 -0
  66. package/src/components/ui/chip/Chip.tsx +151 -0
  67. package/src/components/ui/chip/index.ts +2 -0
  68. package/src/components/ui/collapse/Collapse.stories.tsx +313 -0
  69. package/src/components/ui/collapse/Collapse.test.tsx +298 -0
  70. package/src/components/ui/collapse/Collapse.tsx +275 -0
  71. package/src/components/ui/collapse/index.ts +18 -0
  72. package/src/components/ui/data-row/DataRow.stories.tsx +78 -0
  73. package/src/components/ui/data-row/DataRow.test.tsx +78 -0
  74. package/src/components/ui/data-row/DataRow.tsx +29 -0
  75. package/src/components/ui/data-row/index.ts +2 -0
  76. package/src/components/ui/divider/Divider.stories.tsx +71 -0
  77. package/src/components/ui/divider/Divider.test.tsx +55 -0
  78. package/src/components/ui/divider/Divider.tsx +37 -0
  79. package/src/components/ui/divider/index.ts +2 -0
  80. package/src/components/ui/drawer/Drawer.stories.tsx +228 -0
  81. package/src/components/ui/drawer/Drawer.test.tsx +142 -0
  82. package/src/components/ui/drawer/Drawer.tsx +228 -0
  83. package/src/components/ui/drawer/index.ts +9 -0
  84. package/src/components/ui/form-field/FormField.stories.tsx +260 -0
  85. package/src/components/ui/form-field/FormField.test.tsx +280 -0
  86. package/src/components/ui/form-field/FormField.tsx +308 -0
  87. package/src/components/ui/form-field/index.ts +2 -0
  88. package/src/components/ui/help-tip/HelpTip.stories.tsx +250 -0
  89. package/src/components/ui/help-tip/HelpTip.test.tsx +198 -0
  90. package/src/components/ui/help-tip/HelpTip.tsx +229 -0
  91. package/src/components/ui/help-tip/index.ts +2 -0
  92. package/src/components/ui/icon-box/IconBox.stories.tsx +139 -0
  93. package/src/components/ui/icon-box/IconBox.test.tsx +120 -0
  94. package/src/components/ui/icon-box/IconBox.tsx +57 -0
  95. package/src/components/ui/icon-box/index.ts +1 -0
  96. package/src/components/ui/index.ts +36 -0
  97. package/src/components/ui/input/Input.stories.tsx +192 -0
  98. package/src/components/ui/input/Input.test.tsx +86 -0
  99. package/src/components/ui/input/Input.tsx +273 -0
  100. package/src/components/ui/input/index.ts +2 -0
  101. package/src/components/ui/link/Link.stories.tsx +78 -0
  102. package/src/components/ui/link/Link.test.tsx +94 -0
  103. package/src/components/ui/link/Link.tsx +86 -0
  104. package/src/components/ui/link/index.ts +2 -0
  105. package/src/components/ui/list-item/ListItem.stories.tsx +146 -0
  106. package/src/components/ui/list-item/ListItem.test.tsx +128 -0
  107. package/src/components/ui/list-item/ListItem.tsx +89 -0
  108. package/src/components/ui/list-item/index.ts +14 -0
  109. package/src/components/ui/menu/Menu.stories.tsx +180 -0
  110. package/src/components/ui/menu/Menu.test.tsx +353 -0
  111. package/src/components/ui/menu/Menu.tsx +222 -0
  112. package/src/components/ui/menu/index.ts +2 -0
  113. package/src/components/ui/modal/Modal.stories.tsx +350 -0
  114. package/src/components/ui/modal/Modal.test.tsx +159 -0
  115. package/src/components/ui/modal/Modal.tsx +254 -0
  116. package/src/components/ui/modal/index.ts +19 -0
  117. package/src/components/ui/popover/Popover.stories.tsx +190 -0
  118. package/src/components/ui/popover/Popover.test.tsx +277 -0
  119. package/src/components/ui/popover/Popover.tsx +162 -0
  120. package/src/components/ui/popover/index.ts +2 -0
  121. package/src/components/ui/progress/Progress.stories.tsx +190 -0
  122. package/src/components/ui/progress/Progress.test.tsx +181 -0
  123. package/src/components/ui/progress/Progress.tsx +320 -0
  124. package/src/components/ui/progress/index.ts +9 -0
  125. package/src/components/ui/radio/Radio.stories.tsx +199 -0
  126. package/src/components/ui/radio/Radio.test.tsx +166 -0
  127. package/src/components/ui/radio/Radio.tsx +200 -0
  128. package/src/components/ui/radio/index.ts +2 -0
  129. package/src/components/ui/section/Section.stories.tsx +111 -0
  130. package/src/components/ui/section/Section.test.tsx +121 -0
  131. package/src/components/ui/section/Section.tsx +52 -0
  132. package/src/components/ui/section/index.ts +6 -0
  133. package/src/components/ui/select/Select.stories.tsx +286 -0
  134. package/src/components/ui/select/Select.test.tsx +180 -0
  135. package/src/components/ui/select/Select.tsx +236 -0
  136. package/src/components/ui/select/index.ts +2 -0
  137. package/src/components/ui/skeleton/Skeleton.stories.tsx +153 -0
  138. package/src/components/ui/skeleton/Skeleton.test.tsx +162 -0
  139. package/src/components/ui/skeleton/Skeleton.tsx +229 -0
  140. package/src/components/ui/skeleton/index.ts +16 -0
  141. package/src/components/ui/spinner/Spinner.stories.tsx +67 -0
  142. package/src/components/ui/spinner/Spinner.test.tsx +67 -0
  143. package/src/components/ui/spinner/Spinner.tsx +72 -0
  144. package/src/components/ui/spinner/index.ts +2 -0
  145. package/src/components/ui/stack/Stack.stories.tsx +127 -0
  146. package/src/components/ui/stack/Stack.test.tsx +184 -0
  147. package/src/components/ui/stack/Stack.tsx +76 -0
  148. package/src/components/ui/stack/index.ts +2 -0
  149. package/src/components/ui/surface/Surface.stories.tsx +91 -0
  150. package/src/components/ui/surface/Surface.test.tsx +63 -0
  151. package/src/components/ui/surface/Surface.tsx +51 -0
  152. package/src/components/ui/surface/index.ts +1 -0
  153. package/src/components/ui/switch/Switch.stories.tsx +111 -0
  154. package/src/components/ui/switch/Switch.test.tsx +112 -0
  155. package/src/components/ui/switch/Switch.tsx +103 -0
  156. package/src/components/ui/switch/index.ts +2 -0
  157. package/src/components/ui/tabs/Tabs.stories.tsx +213 -0
  158. package/src/components/ui/tabs/Tabs.test.tsx +192 -0
  159. package/src/components/ui/tabs/Tabs.tsx +253 -0
  160. package/src/components/ui/tabs/index.ts +16 -0
  161. package/src/components/ui/toast/Toast.stories.tsx +255 -0
  162. package/src/components/ui/toast/Toast.test.tsx +207 -0
  163. package/src/components/ui/toast/Toast.tsx +327 -0
  164. package/src/components/ui/toast/index.ts +8 -0
  165. package/src/components/ui/toolbar-button/ToolbarButton.stories.tsx +415 -0
  166. package/src/components/ui/toolbar-button/ToolbarButton.test.tsx +224 -0
  167. package/src/components/ui/toolbar-button/ToolbarButton.tsx +307 -0
  168. package/src/components/ui/toolbar-button/index.ts +7 -0
  169. package/src/components/ui/tooltip/Tooltip.stories.tsx +126 -0
  170. package/src/components/ui/tooltip/Tooltip.test.tsx +179 -0
  171. package/src/components/ui/tooltip/Tooltip.tsx +143 -0
  172. package/src/components/ui/tooltip/index.ts +2 -0
  173. package/src/index.ts +10 -0
  174. package/src/test/setup.ts +19 -0
  175. package/src/theme/Colors.stories.tsx +258 -0
  176. package/src/theme/config.ts +193 -0
  177. package/src/theme/elevation.stories.tsx +346 -0
  178. package/src/theme/elevation.test.ts +27 -0
  179. package/src/theme/elevation.ts +567 -0
  180. package/src/theme/index.ts +39 -0
  181. package/src/theme/shadows.stories.tsx +523 -0
  182. package/src/theme/shadows.ts +439 -0
  183. package/src/theme/tokens/index.ts +2 -0
  184. package/src/theme/tokens/primitives.ts +354 -0
  185. package/src/theme/tokens/semantic.ts +381 -0
  186. package/src/types/jest-axe.d.ts +16 -0
  187. package/src/types/nativewind.d.ts +54 -0
  188. package/src/utils/cn.ts +14 -0
  189. package/src/utils/colors.ts +140 -0
  190. package/src/utils/form.ts +188 -0
  191. package/src/utils/index.ts +29 -0
  192. package/src/utils/useTheme.ts +72 -0
@@ -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,16 @@
1
+ export {
2
+ Tabs,
3
+ TabList,
4
+ Tab,
5
+ TabPanels,
6
+ TabPanel,
7
+ } from './Tabs'
8
+ export type {
9
+ TabsProps,
10
+ TabListProps,
11
+ TabProps,
12
+ TabPanelsProps,
13
+ TabPanelProps,
14
+ TabsVariant,
15
+ TabsOrientation,
16
+ } from './Tabs'
@@ -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
+ }