@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.
- package/package.json +10 -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
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { View, Text, Pressable, Modal, ScrollView, type ViewProps } from 'react-native'
|
|
3
|
+
import { cn } from '../../../utils/cn'
|
|
4
|
+
|
|
5
|
+
export type DrawerPlacement = 'left' | 'right' | 'top' | 'bottom'
|
|
6
|
+
export type DrawerSize = 'sm' | 'md' | 'lg' | 'xl' | 'full'
|
|
7
|
+
|
|
8
|
+
export interface DrawerProps extends ViewProps {
|
|
9
|
+
/** Whether the drawer is open */
|
|
10
|
+
isOpen: boolean
|
|
11
|
+
/** Callback when drawer should close */
|
|
12
|
+
onClose: () => void
|
|
13
|
+
/** Placement of the drawer */
|
|
14
|
+
placement?: DrawerPlacement
|
|
15
|
+
/** Size of the drawer */
|
|
16
|
+
size?: DrawerSize
|
|
17
|
+
/** Whether clicking the backdrop closes the drawer */
|
|
18
|
+
closeOnOverlayClick?: boolean
|
|
19
|
+
/** Whether to show the close button */
|
|
20
|
+
showCloseButton?: boolean
|
|
21
|
+
/** Title for the drawer header */
|
|
22
|
+
title?: string
|
|
23
|
+
/** Additional className for the drawer content */
|
|
24
|
+
className?: string
|
|
25
|
+
children?: React.ReactNode
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const sizeStyles: Record<DrawerPlacement, Record<DrawerSize, string>> = {
|
|
29
|
+
left: {
|
|
30
|
+
sm: 'w-64',
|
|
31
|
+
md: 'w-80',
|
|
32
|
+
lg: 'w-96',
|
|
33
|
+
xl: 'w-[480px]',
|
|
34
|
+
full: 'w-full',
|
|
35
|
+
},
|
|
36
|
+
right: {
|
|
37
|
+
sm: 'w-64',
|
|
38
|
+
md: 'w-80',
|
|
39
|
+
lg: 'w-96',
|
|
40
|
+
xl: 'w-[480px]',
|
|
41
|
+
full: 'w-full',
|
|
42
|
+
},
|
|
43
|
+
top: {
|
|
44
|
+
sm: 'h-32',
|
|
45
|
+
md: 'h-48',
|
|
46
|
+
lg: 'h-64',
|
|
47
|
+
xl: 'h-96',
|
|
48
|
+
full: 'h-full',
|
|
49
|
+
},
|
|
50
|
+
bottom: {
|
|
51
|
+
sm: 'h-32',
|
|
52
|
+
md: 'h-48',
|
|
53
|
+
lg: 'h-64',
|
|
54
|
+
xl: 'h-96',
|
|
55
|
+
full: 'h-full',
|
|
56
|
+
},
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const placementStyles: Record<DrawerPlacement, string> = {
|
|
60
|
+
left: 'left-0 top-0 bottom-0 h-full',
|
|
61
|
+
right: 'right-0 top-0 bottom-0 h-full',
|
|
62
|
+
top: 'top-0 left-0 right-0 w-full',
|
|
63
|
+
bottom: 'bottom-0 left-0 right-0 w-full',
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Drawer component for side panel overlays.
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* <Drawer
|
|
71
|
+
* isOpen={isOpen}
|
|
72
|
+
* onClose={() => setIsOpen(false)}
|
|
73
|
+
* title="Settings"
|
|
74
|
+
* placement="right"
|
|
75
|
+
* >
|
|
76
|
+
* <DrawerBody>
|
|
77
|
+
* <Text>Drawer content</Text>
|
|
78
|
+
* </DrawerBody>
|
|
79
|
+
* </Drawer>
|
|
80
|
+
*/
|
|
81
|
+
export function Drawer({
|
|
82
|
+
isOpen,
|
|
83
|
+
onClose,
|
|
84
|
+
placement = 'right',
|
|
85
|
+
size = 'md',
|
|
86
|
+
closeOnOverlayClick = true,
|
|
87
|
+
showCloseButton = true,
|
|
88
|
+
title,
|
|
89
|
+
className,
|
|
90
|
+
children,
|
|
91
|
+
...props
|
|
92
|
+
}: DrawerProps) {
|
|
93
|
+
if (!isOpen) return null
|
|
94
|
+
|
|
95
|
+
const handleOverlayPress = () => {
|
|
96
|
+
if (closeOnOverlayClick) {
|
|
97
|
+
onClose()
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<Modal
|
|
103
|
+
visible={isOpen}
|
|
104
|
+
transparent
|
|
105
|
+
animationType="fade"
|
|
106
|
+
onRequestClose={onClose}
|
|
107
|
+
>
|
|
108
|
+
<View className="flex-1">
|
|
109
|
+
{/* Backdrop */}
|
|
110
|
+
<Pressable
|
|
111
|
+
onPress={handleOverlayPress}
|
|
112
|
+
className="absolute inset-0 bg-black/50"
|
|
113
|
+
/>
|
|
114
|
+
|
|
115
|
+
{/* Drawer Panel */}
|
|
116
|
+
<View
|
|
117
|
+
className={cn(
|
|
118
|
+
'absolute bg-surface-elevated shadow-2xl',
|
|
119
|
+
placementStyles[placement],
|
|
120
|
+
sizeStyles[placement][size],
|
|
121
|
+
className
|
|
122
|
+
)}
|
|
123
|
+
accessibilityRole={'dialog' as any}
|
|
124
|
+
{...props}
|
|
125
|
+
>
|
|
126
|
+
{/* Header */}
|
|
127
|
+
{(title || showCloseButton) && (
|
|
128
|
+
<View className="flex-row items-center justify-between px-4 py-3 border-b border-border-default">
|
|
129
|
+
{title && (
|
|
130
|
+
<Text className="text-lg font-semibold text-text-primary">
|
|
131
|
+
{title}
|
|
132
|
+
</Text>
|
|
133
|
+
)}
|
|
134
|
+
{showCloseButton && (
|
|
135
|
+
<Pressable
|
|
136
|
+
onPress={onClose}
|
|
137
|
+
className="p-2 -m-2 rounded-full web:hover:bg-interactive-hover"
|
|
138
|
+
accessibilityLabel="Close drawer"
|
|
139
|
+
>
|
|
140
|
+
<Text className="text-text-secondary text-xl">×</Text>
|
|
141
|
+
</Pressable>
|
|
142
|
+
)}
|
|
143
|
+
</View>
|
|
144
|
+
)}
|
|
145
|
+
|
|
146
|
+
{/* Content */}
|
|
147
|
+
<View className="flex-1">
|
|
148
|
+
{children}
|
|
149
|
+
</View>
|
|
150
|
+
</View>
|
|
151
|
+
</View>
|
|
152
|
+
</Modal>
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export interface DrawerHeaderProps extends ViewProps {
|
|
157
|
+
/** Additional className */
|
|
158
|
+
className?: string
|
|
159
|
+
children?: React.ReactNode
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Header section for the drawer.
|
|
164
|
+
*/
|
|
165
|
+
export function DrawerHeader({ className, children, ...props }: DrawerHeaderProps) {
|
|
166
|
+
return (
|
|
167
|
+
<View
|
|
168
|
+
className={cn('px-4 py-3 border-b border-border-default', className)}
|
|
169
|
+
{...props}
|
|
170
|
+
>
|
|
171
|
+
{children}
|
|
172
|
+
</View>
|
|
173
|
+
)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export interface DrawerBodyProps extends ViewProps {
|
|
177
|
+
/** Whether the body should scroll */
|
|
178
|
+
scrollable?: boolean
|
|
179
|
+
/** Additional className */
|
|
180
|
+
className?: string
|
|
181
|
+
children?: React.ReactNode
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Body/content section for the drawer.
|
|
186
|
+
*/
|
|
187
|
+
export function DrawerBody({ scrollable = true, className, children, ...props }: DrawerBodyProps) {
|
|
188
|
+
if (scrollable) {
|
|
189
|
+
return (
|
|
190
|
+
<ScrollView
|
|
191
|
+
className={cn('flex-1 p-4', className)}
|
|
192
|
+
{...props}
|
|
193
|
+
>
|
|
194
|
+
{children}
|
|
195
|
+
</ScrollView>
|
|
196
|
+
)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return (
|
|
200
|
+
<View className={cn('flex-1 p-4', className)} {...props}>
|
|
201
|
+
{children}
|
|
202
|
+
</View>
|
|
203
|
+
)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export interface DrawerFooterProps extends ViewProps {
|
|
207
|
+
/** Additional className */
|
|
208
|
+
className?: string
|
|
209
|
+
children?: React.ReactNode
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Footer section for the drawer, typically for action buttons.
|
|
214
|
+
*/
|
|
215
|
+
export function DrawerFooter({ className, children, ...props }: DrawerFooterProps) {
|
|
216
|
+
return (
|
|
217
|
+
<View
|
|
218
|
+
className={cn(
|
|
219
|
+
'flex-row items-center justify-end gap-3',
|
|
220
|
+
'px-4 py-3 border-t border-border-default',
|
|
221
|
+
className
|
|
222
|
+
)}
|
|
223
|
+
{...props}
|
|
224
|
+
>
|
|
225
|
+
{children}
|
|
226
|
+
</View>
|
|
227
|
+
)
|
|
228
|
+
}
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import { View, Text, TextInput } from 'react-native'
|
|
3
|
+
import { FormField, FormSection, FormActions, FormRow } from './FormField'
|
|
4
|
+
import { Button, ButtonText } from '../button'
|
|
5
|
+
import { Input } from '../input'
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof FormField> = {
|
|
8
|
+
title: 'Components/FormField',
|
|
9
|
+
component: FormField,
|
|
10
|
+
tags: ['autodocs'],
|
|
11
|
+
argTypes: {
|
|
12
|
+
labelSize: {
|
|
13
|
+
control: 'select',
|
|
14
|
+
options: ['sm', 'md', 'lg'],
|
|
15
|
+
},
|
|
16
|
+
orientation: {
|
|
17
|
+
control: 'select',
|
|
18
|
+
options: ['vertical', 'horizontal'],
|
|
19
|
+
},
|
|
20
|
+
isRequired: {
|
|
21
|
+
control: 'boolean',
|
|
22
|
+
},
|
|
23
|
+
isDisabled: {
|
|
24
|
+
control: 'boolean',
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export default meta
|
|
30
|
+
type Story = StoryObj<typeof FormField>
|
|
31
|
+
|
|
32
|
+
// Simple input for stories
|
|
33
|
+
function SimpleInput({ isInvalid, isDisabled, ...props }: any) {
|
|
34
|
+
return (
|
|
35
|
+
<View
|
|
36
|
+
className={`border rounded-md px-3 py-2 bg-surface-input ${
|
|
37
|
+
isInvalid ? 'border-status-error' : 'border-border-input'
|
|
38
|
+
} ${isDisabled ? 'opacity-50' : ''}`}
|
|
39
|
+
>
|
|
40
|
+
<TextInput
|
|
41
|
+
className="text-text-primary"
|
|
42
|
+
placeholderTextColor="var(--color-text-tertiary)"
|
|
43
|
+
editable={!isDisabled}
|
|
44
|
+
{...props}
|
|
45
|
+
/>
|
|
46
|
+
</View>
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export const Default: Story = {
|
|
51
|
+
render: () => (
|
|
52
|
+
<View className="w-80">
|
|
53
|
+
<FormField label="Email Address">
|
|
54
|
+
<SimpleInput placeholder="Enter your email" />
|
|
55
|
+
</FormField>
|
|
56
|
+
</View>
|
|
57
|
+
),
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export const Required: Story = {
|
|
61
|
+
render: () => (
|
|
62
|
+
<View className="w-80">
|
|
63
|
+
<FormField label="Email Address" isRequired>
|
|
64
|
+
<SimpleInput placeholder="Enter your email" />
|
|
65
|
+
</FormField>
|
|
66
|
+
</View>
|
|
67
|
+
),
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export const WithHelperText: Story = {
|
|
71
|
+
render: () => (
|
|
72
|
+
<View className="w-80">
|
|
73
|
+
<FormField
|
|
74
|
+
label="Username"
|
|
75
|
+
helperText="Choose a unique username between 3-20 characters"
|
|
76
|
+
>
|
|
77
|
+
<SimpleInput placeholder="Enter username" />
|
|
78
|
+
</FormField>
|
|
79
|
+
</View>
|
|
80
|
+
),
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export const WithError: Story = {
|
|
84
|
+
render: () => (
|
|
85
|
+
<View className="w-80">
|
|
86
|
+
<FormField
|
|
87
|
+
label="Password"
|
|
88
|
+
isRequired
|
|
89
|
+
errorMessage="Password must be at least 8 characters"
|
|
90
|
+
>
|
|
91
|
+
<SimpleInput placeholder="Enter password" isInvalid />
|
|
92
|
+
</FormField>
|
|
93
|
+
</View>
|
|
94
|
+
),
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export const Disabled: Story = {
|
|
98
|
+
render: () => (
|
|
99
|
+
<View className="w-80">
|
|
100
|
+
<FormField label="Email Address" isDisabled>
|
|
101
|
+
<SimpleInput placeholder="Enter your email" isDisabled />
|
|
102
|
+
</FormField>
|
|
103
|
+
</View>
|
|
104
|
+
),
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export const LabelSizes: Story = {
|
|
108
|
+
render: () => (
|
|
109
|
+
<View className="gap-6 w-80">
|
|
110
|
+
<FormField label="Small Label" labelSize="sm">
|
|
111
|
+
<SimpleInput placeholder="Small" />
|
|
112
|
+
</FormField>
|
|
113
|
+
<FormField label="Medium Label" labelSize="md">
|
|
114
|
+
<SimpleInput placeholder="Medium" />
|
|
115
|
+
</FormField>
|
|
116
|
+
<FormField label="Large Label" labelSize="lg">
|
|
117
|
+
<SimpleInput placeholder="Large" />
|
|
118
|
+
</FormField>
|
|
119
|
+
</View>
|
|
120
|
+
),
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export const HorizontalLayout: Story = {
|
|
124
|
+
render: () => (
|
|
125
|
+
<View className="gap-4 w-96">
|
|
126
|
+
<FormField label="Name" orientation="horizontal" labelWidth={100}>
|
|
127
|
+
<SimpleInput placeholder="Enter name" />
|
|
128
|
+
</FormField>
|
|
129
|
+
<FormField label="Email" orientation="horizontal" labelWidth={100} isRequired>
|
|
130
|
+
<SimpleInput placeholder="Enter email" />
|
|
131
|
+
</FormField>
|
|
132
|
+
<FormField
|
|
133
|
+
label="Bio"
|
|
134
|
+
orientation="horizontal"
|
|
135
|
+
labelWidth={100}
|
|
136
|
+
helperText="Brief description about yourself"
|
|
137
|
+
>
|
|
138
|
+
<SimpleInput placeholder="Enter bio" />
|
|
139
|
+
</FormField>
|
|
140
|
+
</View>
|
|
141
|
+
),
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export const FormRowExample: Story = {
|
|
145
|
+
render: () => (
|
|
146
|
+
<View className="w-full max-w-lg">
|
|
147
|
+
<FormRow>
|
|
148
|
+
<FormField label="First Name" isRequired className="flex-1">
|
|
149
|
+
<SimpleInput placeholder="John" />
|
|
150
|
+
</FormField>
|
|
151
|
+
<FormField label="Last Name" isRequired className="flex-1">
|
|
152
|
+
<SimpleInput placeholder="Doe" />
|
|
153
|
+
</FormField>
|
|
154
|
+
</FormRow>
|
|
155
|
+
</View>
|
|
156
|
+
),
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export const FormSectionExample: Story = {
|
|
160
|
+
render: () => (
|
|
161
|
+
<View className="w-full max-w-lg">
|
|
162
|
+
<FormSection
|
|
163
|
+
title="Personal Information"
|
|
164
|
+
description="Please provide your personal details"
|
|
165
|
+
>
|
|
166
|
+
<FormRow>
|
|
167
|
+
<FormField label="First Name" isRequired className="flex-1">
|
|
168
|
+
<SimpleInput placeholder="John" />
|
|
169
|
+
</FormField>
|
|
170
|
+
<FormField label="Last Name" isRequired className="flex-1">
|
|
171
|
+
<SimpleInput placeholder="Doe" />
|
|
172
|
+
</FormField>
|
|
173
|
+
</FormRow>
|
|
174
|
+
<FormField label="Email" isRequired helperText="We'll use this for notifications">
|
|
175
|
+
<SimpleInput placeholder="john@example.com" />
|
|
176
|
+
</FormField>
|
|
177
|
+
</FormSection>
|
|
178
|
+
</View>
|
|
179
|
+
),
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export const CompleteFormExample: Story = {
|
|
183
|
+
render: () => (
|
|
184
|
+
<View className="w-full max-w-lg p-6 bg-surface-base rounded-lg">
|
|
185
|
+
<FormSection title="Create Account" description="Fill in the details to create your account">
|
|
186
|
+
<FormRow>
|
|
187
|
+
<FormField label="First Name" isRequired className="flex-1">
|
|
188
|
+
<SimpleInput placeholder="John" />
|
|
189
|
+
</FormField>
|
|
190
|
+
<FormField label="Last Name" isRequired className="flex-1">
|
|
191
|
+
<SimpleInput placeholder="Doe" />
|
|
192
|
+
</FormField>
|
|
193
|
+
</FormRow>
|
|
194
|
+
|
|
195
|
+
<FormField label="Email" isRequired>
|
|
196
|
+
<SimpleInput placeholder="john@example.com" />
|
|
197
|
+
</FormField>
|
|
198
|
+
|
|
199
|
+
<FormField
|
|
200
|
+
label="Password"
|
|
201
|
+
isRequired
|
|
202
|
+
helperText="At least 8 characters with one uppercase and number"
|
|
203
|
+
>
|
|
204
|
+
<SimpleInput placeholder="••••••••" secureTextEntry />
|
|
205
|
+
</FormField>
|
|
206
|
+
|
|
207
|
+
<FormField label="Confirm Password" isRequired>
|
|
208
|
+
<SimpleInput placeholder="••••••••" secureTextEntry />
|
|
209
|
+
</FormField>
|
|
210
|
+
</FormSection>
|
|
211
|
+
|
|
212
|
+
<FormSection title="Preferences" description="Customize your experience">
|
|
213
|
+
<FormField label="Company (optional)">
|
|
214
|
+
<SimpleInput placeholder="Acme Inc." />
|
|
215
|
+
</FormField>
|
|
216
|
+
|
|
217
|
+
<FormField label="Timezone" helperText="Select your local timezone">
|
|
218
|
+
<SimpleInput placeholder="America/New_York" />
|
|
219
|
+
</FormField>
|
|
220
|
+
</FormSection>
|
|
221
|
+
|
|
222
|
+
<FormActions align="right">
|
|
223
|
+
<Button variant="ghost">
|
|
224
|
+
<ButtonText>Cancel</ButtonText>
|
|
225
|
+
</Button>
|
|
226
|
+
<Button>
|
|
227
|
+
<ButtonText>Create Account</ButtonText>
|
|
228
|
+
</Button>
|
|
229
|
+
</FormActions>
|
|
230
|
+
</View>
|
|
231
|
+
),
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export const ValidationStates: Story = {
|
|
235
|
+
render: () => (
|
|
236
|
+
<View className="gap-6 w-80">
|
|
237
|
+
<FormField label="Valid Input" helperText="This field is valid">
|
|
238
|
+
<View className="border border-status-success rounded-md px-3 py-2 bg-surface-input">
|
|
239
|
+
<Text className="text-text-primary">valid@email.com</Text>
|
|
240
|
+
</View>
|
|
241
|
+
</FormField>
|
|
242
|
+
|
|
243
|
+
<FormField
|
|
244
|
+
label="Invalid Input"
|
|
245
|
+
isRequired
|
|
246
|
+
errorMessage="Please enter a valid email address"
|
|
247
|
+
>
|
|
248
|
+
<View className="border border-status-error rounded-md px-3 py-2 bg-surface-input">
|
|
249
|
+
<Text className="text-text-primary">not-an-email</Text>
|
|
250
|
+
</View>
|
|
251
|
+
</FormField>
|
|
252
|
+
|
|
253
|
+
<FormField label="Disabled Input" isDisabled>
|
|
254
|
+
<View className="border border-border-input rounded-md px-3 py-2 bg-surface-input opacity-50">
|
|
255
|
+
<Text className="text-text-tertiary">Disabled value</Text>
|
|
256
|
+
</View>
|
|
257
|
+
</FormField>
|
|
258
|
+
</View>
|
|
259
|
+
),
|
|
260
|
+
}
|