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