@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,254 @@
|
|
|
1
|
+
import React, { createContext, useContext } from 'react'
|
|
2
|
+
import { Modal as RNModal, View, Text, Pressable, ScrollView, type ModalProps as RNModalProps } from 'react-native'
|
|
3
|
+
import { cn } from '../../../utils/cn'
|
|
4
|
+
|
|
5
|
+
export type ModalSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'full'
|
|
6
|
+
export type ModalScrollBehavior = 'inside' | 'outside'
|
|
7
|
+
|
|
8
|
+
interface ModalContextType {
|
|
9
|
+
onClose?: () => void
|
|
10
|
+
size: ModalSize
|
|
11
|
+
scrollBehavior: ModalScrollBehavior
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const ModalContext = createContext<ModalContextType>({ size: 'md', scrollBehavior: 'outside' })
|
|
15
|
+
|
|
16
|
+
export interface ModalProps extends Omit<RNModalProps, 'visible'> {
|
|
17
|
+
/** Whether the modal is open */
|
|
18
|
+
isOpen: boolean
|
|
19
|
+
/** Callback when modal should close */
|
|
20
|
+
onClose?: () => void
|
|
21
|
+
/** Modal size */
|
|
22
|
+
size?: ModalSize
|
|
23
|
+
/** Where scrolling happens: inside modal body or whole modal */
|
|
24
|
+
scrollBehavior?: ModalScrollBehavior
|
|
25
|
+
/** Whether clicking backdrop closes modal */
|
|
26
|
+
closeOnOverlayClick?: boolean
|
|
27
|
+
/** Whether to blur the backdrop */
|
|
28
|
+
backdropBlur?: boolean
|
|
29
|
+
/** Additional className for backdrop */
|
|
30
|
+
backdropClassName?: string
|
|
31
|
+
/** Animation type */
|
|
32
|
+
animationType?: 'none' | 'fade' | 'slide'
|
|
33
|
+
children?: React.ReactNode
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const sizeStyles: Record<ModalSize, string> = {
|
|
37
|
+
xs: 'max-w-xs',
|
|
38
|
+
sm: 'max-w-sm',
|
|
39
|
+
md: 'max-w-md',
|
|
40
|
+
lg: 'max-w-lg',
|
|
41
|
+
xl: 'max-w-xl',
|
|
42
|
+
full: 'max-w-full m-4',
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Modal component for dialogs and overlays.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* <Modal isOpen={isOpen} onClose={() => setIsOpen(false)}>
|
|
50
|
+
* <ModalContent>
|
|
51
|
+
* <ModalHeader>
|
|
52
|
+
* <ModalTitle>Modal Title</ModalTitle>
|
|
53
|
+
* <ModalCloseButton />
|
|
54
|
+
* </ModalHeader>
|
|
55
|
+
* <ModalBody>
|
|
56
|
+
* <Text>Modal content</Text>
|
|
57
|
+
* </ModalBody>
|
|
58
|
+
* <ModalFooter>
|
|
59
|
+
* <Button onPress={() => setIsOpen(false)}>Close</Button>
|
|
60
|
+
* </ModalFooter>
|
|
61
|
+
* </ModalContent>
|
|
62
|
+
* </Modal>
|
|
63
|
+
*/
|
|
64
|
+
export function Modal({
|
|
65
|
+
isOpen,
|
|
66
|
+
onClose,
|
|
67
|
+
size = 'md',
|
|
68
|
+
scrollBehavior = 'outside',
|
|
69
|
+
closeOnOverlayClick = true,
|
|
70
|
+
backdropBlur = false,
|
|
71
|
+
backdropClassName,
|
|
72
|
+
animationType = 'fade',
|
|
73
|
+
children,
|
|
74
|
+
...props
|
|
75
|
+
}: ModalProps) {
|
|
76
|
+
const handleBackdropPress = () => {
|
|
77
|
+
if (closeOnOverlayClick && onClose) {
|
|
78
|
+
onClose()
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const backdropContent = (
|
|
83
|
+
<Pressable
|
|
84
|
+
onPress={handleBackdropPress}
|
|
85
|
+
className={cn(
|
|
86
|
+
'flex-1 items-center justify-center',
|
|
87
|
+
backdropBlur ? 'bg-black/30 web:backdrop-blur-sm' : 'bg-black/50',
|
|
88
|
+
backdropClassName
|
|
89
|
+
)}
|
|
90
|
+
>
|
|
91
|
+
{children}
|
|
92
|
+
</Pressable>
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
return (
|
|
96
|
+
<ModalContext.Provider value={{ onClose, size, scrollBehavior }}>
|
|
97
|
+
<RNModal
|
|
98
|
+
visible={isOpen}
|
|
99
|
+
transparent
|
|
100
|
+
animationType={animationType}
|
|
101
|
+
onRequestClose={onClose}
|
|
102
|
+
{...props}
|
|
103
|
+
>
|
|
104
|
+
{scrollBehavior === 'outside' ? (
|
|
105
|
+
<ScrollView
|
|
106
|
+
contentContainerStyle={{ flexGrow: 1 }}
|
|
107
|
+
keyboardShouldPersistTaps="handled"
|
|
108
|
+
>
|
|
109
|
+
{backdropContent}
|
|
110
|
+
</ScrollView>
|
|
111
|
+
) : (
|
|
112
|
+
backdropContent
|
|
113
|
+
)}
|
|
114
|
+
</RNModal>
|
|
115
|
+
</ModalContext.Provider>
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export interface ModalContentProps {
|
|
120
|
+
children?: React.ReactNode
|
|
121
|
+
className?: string
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Container for modal content.
|
|
126
|
+
*/
|
|
127
|
+
export function ModalContent({ children, className }: ModalContentProps) {
|
|
128
|
+
const { size } = useContext(ModalContext)
|
|
129
|
+
|
|
130
|
+
return (
|
|
131
|
+
<Pressable
|
|
132
|
+
// Prevent backdrop click from propagating
|
|
133
|
+
onPress={(e) => e.stopPropagation()}
|
|
134
|
+
>
|
|
135
|
+
<View
|
|
136
|
+
className={cn(
|
|
137
|
+
'bg-surface-elevated rounded-lg shadow-xl w-full',
|
|
138
|
+
sizeStyles[size],
|
|
139
|
+
className
|
|
140
|
+
)}
|
|
141
|
+
>
|
|
142
|
+
{children}
|
|
143
|
+
</View>
|
|
144
|
+
</Pressable>
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export interface ModalHeaderProps {
|
|
149
|
+
children?: React.ReactNode
|
|
150
|
+
className?: string
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Header section of modal.
|
|
155
|
+
*/
|
|
156
|
+
export function ModalHeader({ children, className }: ModalHeaderProps) {
|
|
157
|
+
return (
|
|
158
|
+
<View className={cn('flex-row items-center justify-between px-6 py-4 border-b border-divider', className)}>
|
|
159
|
+
{children}
|
|
160
|
+
</View>
|
|
161
|
+
)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export interface ModalTitleProps {
|
|
165
|
+
children?: React.ReactNode
|
|
166
|
+
className?: string
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Title for modal header.
|
|
171
|
+
*/
|
|
172
|
+
export function ModalTitle({ children, className }: ModalTitleProps) {
|
|
173
|
+
return (
|
|
174
|
+
<Text
|
|
175
|
+
accessibilityRole="header"
|
|
176
|
+
className={cn('text-lg font-semibold text-text-primary font-heading', className)}
|
|
177
|
+
>
|
|
178
|
+
{children}
|
|
179
|
+
</Text>
|
|
180
|
+
)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export interface ModalCloseButtonProps {
|
|
184
|
+
className?: string
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Close button for modal header.
|
|
189
|
+
*/
|
|
190
|
+
export function ModalCloseButton({ className }: ModalCloseButtonProps) {
|
|
191
|
+
const { onClose } = useContext(ModalContext)
|
|
192
|
+
|
|
193
|
+
return (
|
|
194
|
+
<Pressable
|
|
195
|
+
onPress={onClose}
|
|
196
|
+
accessibilityRole="button"
|
|
197
|
+
accessibilityLabel="Close modal"
|
|
198
|
+
className={cn(
|
|
199
|
+
'p-1 rounded-md web:hover:bg-interactive-hover active:bg-interactive-active',
|
|
200
|
+
className
|
|
201
|
+
)}
|
|
202
|
+
>
|
|
203
|
+
<Text className="text-text-secondary text-xl leading-none">×</Text>
|
|
204
|
+
</Pressable>
|
|
205
|
+
)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export interface ModalBodyProps {
|
|
209
|
+
children?: React.ReactNode
|
|
210
|
+
/** Maximum height for scrollable content (when scrollBehavior="inside") */
|
|
211
|
+
maxHeight?: number
|
|
212
|
+
className?: string
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Body section of modal.
|
|
217
|
+
*/
|
|
218
|
+
export function ModalBody({ children, maxHeight, className }: ModalBodyProps) {
|
|
219
|
+
const { scrollBehavior } = useContext(ModalContext)
|
|
220
|
+
|
|
221
|
+
if (scrollBehavior === 'inside') {
|
|
222
|
+
return (
|
|
223
|
+
<ScrollView
|
|
224
|
+
className={cn('px-6 py-4', className)}
|
|
225
|
+
style={maxHeight ? { maxHeight } : undefined}
|
|
226
|
+
showsVerticalScrollIndicator
|
|
227
|
+
>
|
|
228
|
+
{children}
|
|
229
|
+
</ScrollView>
|
|
230
|
+
)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return (
|
|
234
|
+
<View className={cn('px-6 py-4', className)}>
|
|
235
|
+
{children}
|
|
236
|
+
</View>
|
|
237
|
+
)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export interface ModalFooterProps {
|
|
241
|
+
children?: React.ReactNode
|
|
242
|
+
className?: string
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Footer section of modal.
|
|
247
|
+
*/
|
|
248
|
+
export function ModalFooter({ children, className }: ModalFooterProps) {
|
|
249
|
+
return (
|
|
250
|
+
<View className={cn('flex-row items-center justify-end gap-2 px-6 py-4 border-t border-divider', className)}>
|
|
251
|
+
{children}
|
|
252
|
+
</View>
|
|
253
|
+
)
|
|
254
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export {
|
|
2
|
+
Modal,
|
|
3
|
+
ModalContent,
|
|
4
|
+
ModalHeader,
|
|
5
|
+
ModalTitle,
|
|
6
|
+
ModalCloseButton,
|
|
7
|
+
ModalBody,
|
|
8
|
+
ModalFooter,
|
|
9
|
+
} from './Modal'
|
|
10
|
+
export type {
|
|
11
|
+
ModalProps,
|
|
12
|
+
ModalContentProps,
|
|
13
|
+
ModalHeaderProps,
|
|
14
|
+
ModalTitleProps,
|
|
15
|
+
ModalCloseButtonProps,
|
|
16
|
+
ModalBodyProps,
|
|
17
|
+
ModalFooterProps,
|
|
18
|
+
ModalSize,
|
|
19
|
+
} from './Modal'
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import { View, Text } from 'react-native'
|
|
3
|
+
import { Popover, PopoverTrigger, PopoverContent, PopoverCloseButton } from './Popover'
|
|
4
|
+
import { Button, ButtonText } from '../button/Button'
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof Popover> = {
|
|
7
|
+
title: 'Components/Popover',
|
|
8
|
+
component: Popover,
|
|
9
|
+
tags: ['autodocs'],
|
|
10
|
+
argTypes: {
|
|
11
|
+
placement: {
|
|
12
|
+
control: 'select',
|
|
13
|
+
options: ['top', 'bottom', 'left', 'right'],
|
|
14
|
+
description: 'Popover placement relative to trigger',
|
|
15
|
+
},
|
|
16
|
+
isOpen: {
|
|
17
|
+
control: 'boolean',
|
|
18
|
+
description: 'Controlled open state',
|
|
19
|
+
},
|
|
20
|
+
closeOnClickOutside: {
|
|
21
|
+
control: 'boolean',
|
|
22
|
+
description: 'Whether clicking outside closes the popover',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
decorators: [
|
|
26
|
+
(Story) => (
|
|
27
|
+
<View style={{ minHeight: 300, padding: 100, alignItems: 'flex-start' }}>
|
|
28
|
+
<Story />
|
|
29
|
+
</View>
|
|
30
|
+
),
|
|
31
|
+
],
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export default meta
|
|
35
|
+
type Story = StoryObj<typeof Popover>
|
|
36
|
+
|
|
37
|
+
export const Default: Story = {
|
|
38
|
+
args: {
|
|
39
|
+
placement: 'bottom',
|
|
40
|
+
},
|
|
41
|
+
render: (args) => (
|
|
42
|
+
<Popover {...args}>
|
|
43
|
+
<PopoverTrigger>
|
|
44
|
+
<Button variant="solid" color="primary">
|
|
45
|
+
<ButtonText>Open Popover</ButtonText>
|
|
46
|
+
</Button>
|
|
47
|
+
</PopoverTrigger>
|
|
48
|
+
<PopoverContent>
|
|
49
|
+
<Text className="text-text-primary text-sm font-semibold">Popover Title</Text>
|
|
50
|
+
<Text className="text-text-secondary text-sm mt-1">
|
|
51
|
+
This is a popover with some basic content.
|
|
52
|
+
</Text>
|
|
53
|
+
</PopoverContent>
|
|
54
|
+
</Popover>
|
|
55
|
+
),
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const PlacementTop: Story = {
|
|
59
|
+
render: () => (
|
|
60
|
+
<Popover placement="top">
|
|
61
|
+
<PopoverTrigger>
|
|
62
|
+
<Button variant="outline" color="primary">
|
|
63
|
+
<ButtonText>Top Placement</ButtonText>
|
|
64
|
+
</Button>
|
|
65
|
+
</PopoverTrigger>
|
|
66
|
+
<PopoverContent>
|
|
67
|
+
<Text className="text-text-primary text-sm">This popover appears above the trigger.</Text>
|
|
68
|
+
</PopoverContent>
|
|
69
|
+
</Popover>
|
|
70
|
+
),
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export const PlacementBottom: Story = {
|
|
74
|
+
render: () => (
|
|
75
|
+
<Popover placement="bottom">
|
|
76
|
+
<PopoverTrigger>
|
|
77
|
+
<Button variant="outline" color="primary">
|
|
78
|
+
<ButtonText>Bottom Placement</ButtonText>
|
|
79
|
+
</Button>
|
|
80
|
+
</PopoverTrigger>
|
|
81
|
+
<PopoverContent>
|
|
82
|
+
<Text className="text-text-primary text-sm">This popover appears below the trigger.</Text>
|
|
83
|
+
</PopoverContent>
|
|
84
|
+
</Popover>
|
|
85
|
+
),
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export const PlacementLeft: Story = {
|
|
89
|
+
render: () => (
|
|
90
|
+
<Popover placement="left">
|
|
91
|
+
<PopoverTrigger>
|
|
92
|
+
<Button variant="outline" color="primary">
|
|
93
|
+
<ButtonText>Left Placement</ButtonText>
|
|
94
|
+
</Button>
|
|
95
|
+
</PopoverTrigger>
|
|
96
|
+
<PopoverContent>
|
|
97
|
+
<Text className="text-text-primary text-sm">This popover appears to the left.</Text>
|
|
98
|
+
</PopoverContent>
|
|
99
|
+
</Popover>
|
|
100
|
+
),
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export const PlacementRight: Story = {
|
|
104
|
+
render: () => (
|
|
105
|
+
<Popover placement="right">
|
|
106
|
+
<PopoverTrigger>
|
|
107
|
+
<Button variant="outline" color="primary">
|
|
108
|
+
<ButtonText>Right Placement</ButtonText>
|
|
109
|
+
</Button>
|
|
110
|
+
</PopoverTrigger>
|
|
111
|
+
<PopoverContent>
|
|
112
|
+
<Text className="text-text-primary text-sm">This popover appears to the right.</Text>
|
|
113
|
+
</PopoverContent>
|
|
114
|
+
</Popover>
|
|
115
|
+
),
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export const WithCloseButton: Story = {
|
|
119
|
+
render: () => (
|
|
120
|
+
<Popover placement="bottom">
|
|
121
|
+
<PopoverTrigger>
|
|
122
|
+
<Button variant="solid" color="primary">
|
|
123
|
+
<ButtonText>With Close</ButtonText>
|
|
124
|
+
</Button>
|
|
125
|
+
</PopoverTrigger>
|
|
126
|
+
<PopoverContent>
|
|
127
|
+
<View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}>
|
|
128
|
+
<Text className="text-text-primary text-sm font-semibold">Settings</Text>
|
|
129
|
+
<PopoverCloseButton>
|
|
130
|
+
<Text className="text-text-secondary text-lg">✕</Text>
|
|
131
|
+
</PopoverCloseButton>
|
|
132
|
+
</View>
|
|
133
|
+
<Text className="text-text-secondary text-sm mt-2">
|
|
134
|
+
Configure your preferences here.
|
|
135
|
+
</Text>
|
|
136
|
+
</PopoverContent>
|
|
137
|
+
</Popover>
|
|
138
|
+
),
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export const WithFormContent: Story = {
|
|
142
|
+
render: () => (
|
|
143
|
+
<Popover placement="bottom">
|
|
144
|
+
<PopoverTrigger>
|
|
145
|
+
<Button variant="solid" color="primary">
|
|
146
|
+
<ButtonText>Edit Name</ButtonText>
|
|
147
|
+
</Button>
|
|
148
|
+
</PopoverTrigger>
|
|
149
|
+
<PopoverContent>
|
|
150
|
+
<View style={{ gap: 12 }}>
|
|
151
|
+
<Text className="text-text-primary text-sm font-semibold">Edit Display Name</Text>
|
|
152
|
+
<Text className="text-text-secondary text-xs">
|
|
153
|
+
Enter your new display name below.
|
|
154
|
+
</Text>
|
|
155
|
+
<View className="bg-surface-inset border border-border-default rounded-md px-3 py-2">
|
|
156
|
+
<Text className="text-text-secondary text-sm">John Doe</Text>
|
|
157
|
+
</View>
|
|
158
|
+
<View style={{ flexDirection: 'row', gap: 8, justifyContent: 'flex-end' }}>
|
|
159
|
+
<PopoverCloseButton>
|
|
160
|
+
<View className="px-3 py-1.5 rounded-md bg-surface-elevated border border-border-default">
|
|
161
|
+
<Text className="text-text-primary text-sm">Cancel</Text>
|
|
162
|
+
</View>
|
|
163
|
+
</PopoverCloseButton>
|
|
164
|
+
<View className="px-3 py-1.5 rounded-md bg-brand-primary">
|
|
165
|
+
<Text className="text-white text-sm font-medium">Save</Text>
|
|
166
|
+
</View>
|
|
167
|
+
</View>
|
|
168
|
+
</View>
|
|
169
|
+
</PopoverContent>
|
|
170
|
+
</Popover>
|
|
171
|
+
),
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export const Controlled: Story = {
|
|
175
|
+
render: () => (
|
|
176
|
+
<Popover isOpen placement="bottom">
|
|
177
|
+
<PopoverTrigger>
|
|
178
|
+
<Button variant="outline" color="primary">
|
|
179
|
+
<ButtonText>Always Open</ButtonText>
|
|
180
|
+
</Button>
|
|
181
|
+
</PopoverTrigger>
|
|
182
|
+
<PopoverContent>
|
|
183
|
+
<Text className="text-text-primary text-sm font-semibold">Controlled Popover</Text>
|
|
184
|
+
<Text className="text-text-secondary text-sm mt-1">
|
|
185
|
+
This popover is controlled and always visible.
|
|
186
|
+
</Text>
|
|
187
|
+
</PopoverContent>
|
|
188
|
+
</Popover>
|
|
189
|
+
),
|
|
190
|
+
}
|