@titan-design/react-ui 0.1.0 → 0.2.5

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 (236) hide show
  1. package/README.md +47 -1
  2. package/dist/{chunk-MGW37MPO.mjs → chunk-UXVRPOME.mjs} +173 -3
  3. package/dist/chunk-UXVRPOME.mjs.map +1 -0
  4. package/dist/index.d.mts +101 -12
  5. package/dist/index.d.ts +101 -12
  6. package/dist/index.js +1267 -468
  7. package/dist/index.js.map +1 -1
  8. package/dist/index.mjs +734 -112
  9. package/dist/index.mjs.map +1 -1
  10. package/dist/theme/index.d.mts +86 -1
  11. package/dist/theme/index.d.ts +86 -1
  12. package/dist/theme/index.js +166 -0
  13. package/dist/theme/index.js.map +1 -1
  14. package/dist/theme/index.mjs +1 -1
  15. package/package.json +22 -6
  16. package/src/components/custom/DateTime/DateTime.stories.tsx +180 -0
  17. package/src/components/custom/DateTime/DateTime.test.tsx +152 -0
  18. package/src/components/custom/DateTime/DateTime.tsx +235 -0
  19. package/src/components/custom/DateTime/index.ts +2 -0
  20. package/src/components/custom/EmptyState/EmptyState.stories.tsx +99 -0
  21. package/src/components/custom/EmptyState/EmptyState.test.tsx +106 -0
  22. package/src/components/custom/EmptyState/EmptyState.tsx +68 -0
  23. package/src/components/custom/EmptyState/index.ts +2 -0
  24. package/src/components/custom/Metric/Metric.stories.tsx +133 -0
  25. package/src/components/custom/Metric/Metric.test.tsx +120 -0
  26. package/src/components/custom/Metric/Metric.tsx +92 -0
  27. package/src/components/custom/Metric/index.ts +2 -0
  28. package/src/components/custom/Sidebar/Sidebar.stories.tsx +227 -0
  29. package/src/components/custom/Sidebar/Sidebar.test.tsx +185 -0
  30. package/src/components/custom/Sidebar/Sidebar.tsx +257 -0
  31. package/src/components/custom/Sidebar/index.ts +18 -0
  32. package/src/components/custom/Table/Table.stories.tsx +481 -0
  33. package/src/components/custom/Table/Table.test.tsx +531 -0
  34. package/src/components/custom/Table/Table.tsx +696 -0
  35. package/src/components/custom/Table/index.ts +22 -0
  36. package/src/components/custom/Typography/Typography.stories.tsx +153 -0
  37. package/src/components/custom/Typography/Typography.test.tsx +107 -0
  38. package/src/components/custom/Typography/Typography.tsx +218 -0
  39. package/src/components/custom/Typography/index.ts +19 -0
  40. package/src/components/custom/Workout/RestTimer.stories.tsx +71 -0
  41. package/src/components/custom/Workout/RestTimer.test.tsx +145 -0
  42. package/src/components/custom/Workout/RestTimer.tsx +172 -0
  43. package/src/components/custom/Workout/SupersetWrapper.stories.tsx +144 -0
  44. package/src/components/custom/Workout/SupersetWrapper.test.tsx +130 -0
  45. package/src/components/custom/Workout/SupersetWrapper.tsx +63 -0
  46. package/src/components/custom/Workout/index.ts +2 -0
  47. package/src/components/custom/index.ts +9 -0
  48. package/src/components/custom/stepper/Stepper.stories.tsx +245 -0
  49. package/src/components/custom/stepper/Stepper.test.tsx +230 -0
  50. package/src/components/custom/stepper/Stepper.tsx +250 -0
  51. package/src/components/custom/stepper/index.ts +10 -0
  52. package/src/components/index.ts +5 -0
  53. package/src/components/ui/alert/Alert.stories.tsx +154 -0
  54. package/src/components/ui/alert/Alert.test.tsx +202 -0
  55. package/src/components/ui/alert/Alert.tsx +170 -0
  56. package/src/components/ui/alert/index.ts +8 -0
  57. package/src/components/ui/autocomplete/Autocomplete.stories.tsx +299 -0
  58. package/src/components/ui/autocomplete/Autocomplete.test.tsx +247 -0
  59. package/src/components/ui/autocomplete/Autocomplete.tsx +332 -0
  60. package/src/components/ui/autocomplete/index.ts +2 -0
  61. package/src/components/ui/avatar/Avatar.stories.tsx +105 -0
  62. package/src/components/ui/avatar/Avatar.test.tsx +152 -0
  63. package/src/components/ui/avatar/Avatar.tsx +176 -0
  64. package/src/components/ui/avatar/index.ts +2 -0
  65. package/src/components/ui/badge/Badge.stories.tsx +100 -0
  66. package/src/components/ui/badge/Badge.test.tsx +103 -0
  67. package/src/components/ui/badge/Badge.tsx +119 -0
  68. package/src/components/ui/badge/index.ts +3 -0
  69. package/src/components/ui/breadcrumbs/Breadcrumbs.stories.tsx +70 -0
  70. package/src/components/ui/breadcrumbs/Breadcrumbs.test.tsx +156 -0
  71. package/src/components/ui/breadcrumbs/Breadcrumbs.tsx +127 -0
  72. package/src/components/ui/breadcrumbs/index.ts +2 -0
  73. package/src/components/ui/button/Button.stories.tsx +216 -0
  74. package/src/components/ui/button/Button.test.tsx +132 -0
  75. package/src/components/ui/button/Button.tsx +297 -0
  76. package/src/components/ui/button/index.ts +10 -0
  77. package/src/components/ui/card/Card.stories.tsx +443 -0
  78. package/src/components/ui/card/Card.test.tsx +294 -0
  79. package/src/components/ui/card/Card.tsx +401 -0
  80. package/src/components/ui/card/index.ts +20 -0
  81. package/src/components/ui/checkbox/Checkbox.stories.tsx +126 -0
  82. package/src/components/ui/checkbox/Checkbox.test.tsx +152 -0
  83. package/src/components/ui/checkbox/Checkbox.tsx +163 -0
  84. package/src/components/ui/checkbox/index.ts +2 -0
  85. package/src/components/ui/chip/Chip.stories.tsx +112 -0
  86. package/src/components/ui/chip/Chip.test.tsx +119 -0
  87. package/src/components/ui/chip/Chip.tsx +151 -0
  88. package/src/components/ui/chip/index.ts +2 -0
  89. package/src/components/ui/collapse/Collapse.stories.tsx +313 -0
  90. package/src/components/ui/collapse/Collapse.test.tsx +298 -0
  91. package/src/components/ui/collapse/Collapse.tsx +275 -0
  92. package/src/components/ui/collapse/index.ts +18 -0
  93. package/src/components/ui/data-row/DataRow.stories.tsx +78 -0
  94. package/src/components/ui/data-row/DataRow.test.tsx +78 -0
  95. package/src/components/ui/data-row/DataRow.tsx +29 -0
  96. package/src/components/ui/data-row/index.ts +2 -0
  97. package/src/components/ui/divider/Divider.stories.tsx +71 -0
  98. package/src/components/ui/divider/Divider.test.tsx +55 -0
  99. package/src/components/ui/divider/Divider.tsx +37 -0
  100. package/src/components/ui/divider/index.ts +2 -0
  101. package/src/components/ui/drawer/Drawer.stories.tsx +228 -0
  102. package/src/components/ui/drawer/Drawer.test.tsx +142 -0
  103. package/src/components/ui/drawer/Drawer.tsx +228 -0
  104. package/src/components/ui/drawer/index.ts +9 -0
  105. package/src/components/ui/form-field/FormField.stories.tsx +260 -0
  106. package/src/components/ui/form-field/FormField.test.tsx +280 -0
  107. package/src/components/ui/form-field/FormField.tsx +308 -0
  108. package/src/components/ui/form-field/index.ts +2 -0
  109. package/src/components/ui/help-tip/HelpTip.stories.tsx +250 -0
  110. package/src/components/ui/help-tip/HelpTip.test.tsx +198 -0
  111. package/src/components/ui/help-tip/HelpTip.tsx +229 -0
  112. package/src/components/ui/help-tip/index.ts +2 -0
  113. package/src/components/ui/icon-box/IconBox.stories.tsx +139 -0
  114. package/src/components/ui/icon-box/IconBox.test.tsx +120 -0
  115. package/src/components/ui/icon-box/IconBox.tsx +57 -0
  116. package/src/components/ui/icon-box/index.ts +1 -0
  117. package/src/components/ui/index.ts +38 -0
  118. package/src/components/ui/indicator/Indicator.stories.tsx +74 -0
  119. package/src/components/ui/indicator/Indicator.test.tsx +55 -0
  120. package/src/components/ui/indicator/Indicator.tsx +73 -0
  121. package/src/components/ui/indicator/index.ts +2 -0
  122. package/src/components/ui/input/Input.stories.tsx +192 -0
  123. package/src/components/ui/input/Input.test.tsx +86 -0
  124. package/src/components/ui/input/Input.tsx +273 -0
  125. package/src/components/ui/input/index.ts +2 -0
  126. package/src/components/ui/link/Link.stories.tsx +78 -0
  127. package/src/components/ui/link/Link.test.tsx +94 -0
  128. package/src/components/ui/link/Link.tsx +86 -0
  129. package/src/components/ui/link/index.ts +2 -0
  130. package/src/components/ui/list-item/ListItem.stories.tsx +146 -0
  131. package/src/components/ui/list-item/ListItem.test.tsx +128 -0
  132. package/src/components/ui/list-item/ListItem.tsx +89 -0
  133. package/src/components/ui/list-item/index.ts +14 -0
  134. package/src/components/ui/menu/Menu.stories.tsx +180 -0
  135. package/src/components/ui/menu/Menu.test.tsx +353 -0
  136. package/src/components/ui/menu/Menu.tsx +222 -0
  137. package/src/components/ui/menu/index.ts +2 -0
  138. package/src/components/ui/modal/Modal.stories.tsx +350 -0
  139. package/src/components/ui/modal/Modal.test.tsx +159 -0
  140. package/src/components/ui/modal/Modal.tsx +254 -0
  141. package/src/components/ui/modal/index.ts +19 -0
  142. package/src/components/ui/pill/Pill.stories.tsx +71 -0
  143. package/src/components/ui/pill/Pill.test.tsx +69 -0
  144. package/src/components/ui/pill/Pill.tsx +104 -0
  145. package/src/components/ui/pill/index.ts +2 -0
  146. package/src/components/ui/popover/Popover.stories.tsx +190 -0
  147. package/src/components/ui/popover/Popover.test.tsx +419 -0
  148. package/src/components/ui/popover/Popover.tsx +237 -0
  149. package/src/components/ui/popover/index.ts +2 -0
  150. package/src/components/ui/progress/Progress.stories.tsx +190 -0
  151. package/src/components/ui/progress/Progress.test.tsx +210 -0
  152. package/src/components/ui/progress/Progress.tsx +339 -0
  153. package/src/components/ui/progress/index.ts +9 -0
  154. package/src/components/ui/radio/Radio.stories.tsx +199 -0
  155. package/src/components/ui/radio/Radio.test.tsx +166 -0
  156. package/src/components/ui/radio/Radio.tsx +200 -0
  157. package/src/components/ui/radio/index.ts +2 -0
  158. package/src/components/ui/section/Section.stories.tsx +111 -0
  159. package/src/components/ui/section/Section.test.tsx +121 -0
  160. package/src/components/ui/section/Section.tsx +52 -0
  161. package/src/components/ui/section/index.ts +6 -0
  162. package/src/components/ui/select/Select.stories.tsx +286 -0
  163. package/src/components/ui/select/Select.test.tsx +200 -0
  164. package/src/components/ui/select/Select.tsx +243 -0
  165. package/src/components/ui/select/index.ts +2 -0
  166. package/src/components/ui/skeleton/Skeleton.stories.tsx +153 -0
  167. package/src/components/ui/skeleton/Skeleton.test.tsx +162 -0
  168. package/src/components/ui/skeleton/Skeleton.tsx +229 -0
  169. package/src/components/ui/skeleton/index.ts +16 -0
  170. package/src/components/ui/spinner/Spinner.stories.tsx +67 -0
  171. package/src/components/ui/spinner/Spinner.test.tsx +67 -0
  172. package/src/components/ui/spinner/Spinner.tsx +72 -0
  173. package/src/components/ui/spinner/index.ts +2 -0
  174. package/src/components/ui/stack/Stack.stories.tsx +127 -0
  175. package/src/components/ui/stack/Stack.test.tsx +184 -0
  176. package/src/components/ui/stack/Stack.tsx +76 -0
  177. package/src/components/ui/stack/index.ts +2 -0
  178. package/src/components/ui/surface/Surface.stories.tsx +91 -0
  179. package/src/components/ui/surface/Surface.test.tsx +63 -0
  180. package/src/components/ui/surface/Surface.tsx +51 -0
  181. package/src/components/ui/surface/index.ts +1 -0
  182. package/src/components/ui/switch/Switch.stories.tsx +111 -0
  183. package/src/components/ui/switch/Switch.test.tsx +112 -0
  184. package/src/components/ui/switch/Switch.tsx +103 -0
  185. package/src/components/ui/switch/index.ts +2 -0
  186. package/src/components/ui/tabs/Tabs.stories.tsx +213 -0
  187. package/src/components/ui/tabs/Tabs.test.tsx +192 -0
  188. package/src/components/ui/tabs/Tabs.tsx +253 -0
  189. package/src/components/ui/tabs/index.ts +16 -0
  190. package/src/components/ui/toast/Toast.stories.tsx +255 -0
  191. package/src/components/ui/toast/Toast.test.tsx +207 -0
  192. package/src/components/ui/toast/Toast.tsx +327 -0
  193. package/src/components/ui/toast/index.ts +9 -0
  194. package/src/components/ui/toolbar-button/ToolbarButton.stories.tsx +415 -0
  195. package/src/components/ui/toolbar-button/ToolbarButton.test.tsx +224 -0
  196. package/src/components/ui/toolbar-button/ToolbarButton.tsx +307 -0
  197. package/src/components/ui/toolbar-button/index.ts +7 -0
  198. package/src/components/ui/tooltip/Tooltip.stories.tsx +126 -0
  199. package/src/components/ui/tooltip/Tooltip.test.tsx +262 -0
  200. package/src/components/ui/tooltip/Tooltip.tsx +234 -0
  201. package/src/components/ui/tooltip/index.ts +2 -0
  202. package/src/index.ts +10 -0
  203. package/src/stories/PresetShowcase.stories.tsx +462 -0
  204. package/src/stories/ThemePresets.stories.tsx +85 -0
  205. package/src/test/setup.ts +19 -0
  206. package/src/theme/Colors.stories.tsx +258 -0
  207. package/src/theme/config.ts +193 -0
  208. package/src/theme/elevation.stories.tsx +346 -0
  209. package/src/theme/elevation.test.ts +27 -0
  210. package/src/theme/elevation.ts +567 -0
  211. package/src/theme/global.css +129 -1
  212. package/src/theme/index.ts +40 -0
  213. package/src/theme/presets/apply.ts +97 -0
  214. package/src/theme/presets/audiobook.ts +93 -0
  215. package/src/theme/presets/default.ts +6 -0
  216. package/src/theme/presets/index.ts +4 -0
  217. package/src/theme/presets/presets.test.ts +51 -0
  218. package/src/theme/presets/types.ts +76 -0
  219. package/src/theme/shadows.stories.tsx +523 -0
  220. package/src/theme/shadows.ts +439 -0
  221. package/src/theme/tokens/index.ts +2 -0
  222. package/src/theme/tokens/primitives.ts +354 -0
  223. package/src/theme/tokens/semantic.ts +381 -0
  224. package/src/types/jest-axe.d.ts +16 -0
  225. package/src/types/nativewind.d.ts +54 -0
  226. package/src/utils/avatar-color.test.ts +30 -0
  227. package/src/utils/avatar-color.ts +20 -0
  228. package/src/utils/cn.ts +14 -0
  229. package/src/utils/colors.ts +140 -0
  230. package/src/utils/form.ts +188 -0
  231. package/src/utils/index.ts +29 -0
  232. package/src/utils/useTheme.ts +72 -0
  233. package/src/web-jsx/jsx-dev-runtime.ts +28 -0
  234. package/src/web-jsx/jsx-runtime.ts +41 -0
  235. package/tailwind.config.js +53 -3
  236. package/dist/chunk-MGW37MPO.mjs.map +0 -1
@@ -0,0 +1,151 @@
1
+ import React from 'react'
2
+ import { View, Text, Pressable, type ViewProps } from 'react-native'
3
+ import { cn } from '../../../utils/cn'
4
+
5
+ export type ChipVariant = 'solid' | 'subtle' | 'outline'
6
+ export type ChipColor = 'default' | 'primary' | 'secondary' | 'success' | 'error' | 'warning' | 'info'
7
+ export type ChipSize = 'sm' | 'md' | 'lg'
8
+
9
+ export interface ChipProps extends ViewProps {
10
+ /** Visual variant */
11
+ variant?: ChipVariant
12
+ /** Color scheme */
13
+ color?: ChipColor
14
+ /** Size */
15
+ size?: ChipSize
16
+ /** Left element (icon, avatar) */
17
+ leftElement?: React.ReactNode
18
+ /** Whether the chip is dismissible */
19
+ onDelete?: () => void
20
+ /** Whether the chip is clickable */
21
+ onPress?: () => void
22
+ /** Whether the chip is disabled */
23
+ isDisabled?: boolean
24
+ /** Additional className */
25
+ className?: string
26
+ children?: React.ReactNode
27
+ }
28
+
29
+ const colorStyles: Record<ChipColor, Record<ChipVariant, string>> = {
30
+ default: {
31
+ solid: 'bg-neutral-600 text-white',
32
+ subtle: 'bg-neutral-100 text-neutral-700 dark:bg-neutral-800 dark:text-neutral-200',
33
+ outline: 'border border-neutral-300 dark:border-neutral-600 text-neutral-700 dark:text-neutral-200',
34
+ },
35
+ primary: {
36
+ solid: 'bg-brand-primary text-white',
37
+ subtle: 'bg-brand-primary-subtle text-brand-primary',
38
+ outline: 'border border-brand-primary text-brand-primary',
39
+ },
40
+ secondary: {
41
+ solid: 'bg-brand-secondary text-white',
42
+ subtle: 'bg-brand-secondary-subtle text-brand-secondary',
43
+ outline: 'border border-brand-secondary text-brand-secondary',
44
+ },
45
+ success: {
46
+ solid: 'bg-status-success text-white',
47
+ subtle: 'bg-status-success-subtle text-status-success',
48
+ outline: 'border border-status-success text-status-success',
49
+ },
50
+ error: {
51
+ solid: 'bg-status-error text-white',
52
+ subtle: 'bg-status-error-subtle text-status-error',
53
+ outline: 'border border-status-error text-status-error',
54
+ },
55
+ warning: {
56
+ solid: 'bg-status-warning text-white',
57
+ subtle: 'bg-status-warning-subtle text-status-warning',
58
+ outline: 'border border-status-warning text-status-warning',
59
+ },
60
+ info: {
61
+ solid: 'bg-status-info text-white',
62
+ subtle: 'bg-status-info-subtle text-status-info',
63
+ outline: 'border border-status-info text-status-info',
64
+ },
65
+ }
66
+
67
+ const sizeStyles: Record<ChipSize, { container: string; text: string; deleteButton: string }> = {
68
+ sm: { container: 'px-2 py-0.5 rounded', text: 'text-xs', deleteButton: 'ml-1 -mr-0.5' },
69
+ md: { container: 'px-3 py-1 rounded-md', text: 'text-sm', deleteButton: 'ml-1.5 -mr-1' },
70
+ lg: { container: 'px-4 py-1.5 rounded-md', text: 'text-base', deleteButton: 'ml-2 -mr-1' },
71
+ }
72
+
73
+ /**
74
+ * Chip component for tags, labels, and filters.
75
+ *
76
+ * @example
77
+ * <Chip>Default</Chip>
78
+ * <Chip color="primary" variant="subtle">Primary Subtle</Chip>
79
+ * <Chip onDelete={() => {}}>Dismissible</Chip>
80
+ * <Chip onPress={() => {}}>Clickable</Chip>
81
+ */
82
+ export function Chip({
83
+ variant = 'subtle',
84
+ color = 'default',
85
+ size = 'md',
86
+ leftElement,
87
+ onDelete,
88
+ onPress,
89
+ isDisabled = false,
90
+ className,
91
+ children,
92
+ ...props
93
+ }: ChipProps) {
94
+ const sizes = sizeStyles[size]
95
+ const isClickable = !!onPress
96
+
97
+ const content = (
98
+ <>
99
+ {leftElement && <View className="mr-1.5">{leftElement}</View>}
100
+ <Text className={cn('font-medium', sizes.text)}>
101
+ {children}
102
+ </Text>
103
+ {onDelete && (
104
+ <Pressable
105
+ onPress={(e) => {
106
+ e.stopPropagation?.()
107
+ onDelete()
108
+ }}
109
+ accessibilityRole="button"
110
+ accessibilityLabel="Remove"
111
+ disabled={isDisabled}
112
+ className={cn(
113
+ sizes.deleteButton,
114
+ 'rounded-full p-0.5 web:hover:bg-black/10 active:bg-black/20'
115
+ )}
116
+ >
117
+ <Text className="text-xs leading-none">×</Text>
118
+ </Pressable>
119
+ )}
120
+ </>
121
+ )
122
+
123
+ const baseClassName = cn(
124
+ 'flex-row items-center',
125
+ sizes.container,
126
+ colorStyles[color][variant],
127
+ isClickable && 'web:cursor-pointer web:hover:opacity-80 active:opacity-70',
128
+ isDisabled && 'opacity-50 cursor-not-allowed',
129
+ className
130
+ )
131
+
132
+ if (isClickable) {
133
+ return (
134
+ <Pressable
135
+ onPress={onPress}
136
+ disabled={isDisabled}
137
+ accessibilityRole="button"
138
+ className={baseClassName}
139
+ {...props}
140
+ >
141
+ {content}
142
+ </Pressable>
143
+ )
144
+ }
145
+
146
+ return (
147
+ <View className={baseClassName} {...props}>
148
+ {content}
149
+ </View>
150
+ )
151
+ }
@@ -0,0 +1,2 @@
1
+ export { Chip } from './Chip'
2
+ export type { ChipProps, ChipVariant, ChipColor, ChipSize } from './Chip'
@@ -0,0 +1,313 @@
1
+ import React, { useState } from 'react'
2
+ import type { Meta, StoryObj } from '@storybook/react-vite'
3
+ import { View, Text } from 'react-native'
4
+ import {
5
+ Collapse,
6
+ CollapseButton,
7
+ CollapseContent,
8
+ Accordion,
9
+ AccordionItem,
10
+ AccordionButton,
11
+ AccordionPanel,
12
+ } from './Collapse'
13
+
14
+ const meta: Meta<typeof Accordion> = {
15
+ title: 'Components/Accordion',
16
+ component: Accordion,
17
+ tags: ['autodocs'],
18
+ argTypes: {
19
+ allowMultiple: {
20
+ control: 'boolean',
21
+ description: 'Allow multiple items to be open simultaneously',
22
+ },
23
+ },
24
+ }
25
+
26
+ export default meta
27
+ type Story = StoryObj<typeof Accordion>
28
+
29
+ export const Default: Story = {
30
+ render: () => (
31
+ <View style={{ width: 400 }}>
32
+ <Accordion>
33
+ <AccordionItem>
34
+ <AccordionButton>What is Titan Design?</AccordionButton>
35
+ <AccordionPanel>
36
+ <Text className="text-text-secondary text-sm">
37
+ Titan Design is a cross-platform React and React Native design system built on
38
+ Gluestack UI, NativeWind v4, and Tailwind CSS.
39
+ </Text>
40
+ </AccordionPanel>
41
+ </AccordionItem>
42
+ <AccordionItem>
43
+ <AccordionButton>How do I install it?</AccordionButton>
44
+ <AccordionPanel>
45
+ <Text className="text-text-secondary text-sm">
46
+ Install via pnpm: pnpm add @titan-design/react-ui. Then import the global CSS and
47
+ components as needed.
48
+ </Text>
49
+ </AccordionPanel>
50
+ </AccordionItem>
51
+ <AccordionItem>
52
+ <AccordionButton>Is it accessible?</AccordionButton>
53
+ <AccordionPanel>
54
+ <Text className="text-text-secondary text-sm">
55
+ Yes, all components are built with WCAG 2.1 AA compliance in mind and tested with
56
+ jest-axe for accessibility violations.
57
+ </Text>
58
+ </AccordionPanel>
59
+ </AccordionItem>
60
+ </Accordion>
61
+ </View>
62
+ ),
63
+ }
64
+
65
+ export const AllowMultiple: Story = {
66
+ render: () => (
67
+ <View style={{ width: 400 }}>
68
+ <Accordion allowMultiple>
69
+ <AccordionItem>
70
+ <AccordionButton>Section One</AccordionButton>
71
+ <AccordionPanel>
72
+ <Text className="text-text-secondary text-sm">
73
+ With allowMultiple enabled, you can open multiple accordion items at the same time.
74
+ Try opening this and another section.
75
+ </Text>
76
+ </AccordionPanel>
77
+ </AccordionItem>
78
+ <AccordionItem>
79
+ <AccordionButton>Section Two</AccordionButton>
80
+ <AccordionPanel>
81
+ <Text className="text-text-secondary text-sm">
82
+ This section can be open alongside Section One. In single mode, opening a new
83
+ section would close any previously open one.
84
+ </Text>
85
+ </AccordionPanel>
86
+ </AccordionItem>
87
+ <AccordionItem>
88
+ <AccordionButton>Section Three</AccordionButton>
89
+ <AccordionPanel>
90
+ <Text className="text-text-secondary text-sm">
91
+ All three sections can be expanded simultaneously when allowMultiple is true.
92
+ </Text>
93
+ </AccordionPanel>
94
+ </AccordionItem>
95
+ </Accordion>
96
+ </View>
97
+ ),
98
+ }
99
+
100
+ export const DefaultOpen: Story = {
101
+ render: () => (
102
+ <View style={{ width: 400 }}>
103
+ <Accordion defaultIndex={[0]}>
104
+ <AccordionItem>
105
+ <AccordionButton>Initially Open</AccordionButton>
106
+ <AccordionPanel>
107
+ <Text className="text-text-secondary text-sm">
108
+ This section is open by default using defaultIndex=[0]. The user can close it by
109
+ clicking the header.
110
+ </Text>
111
+ </AccordionPanel>
112
+ </AccordionItem>
113
+ <AccordionItem>
114
+ <AccordionButton>Initially Closed</AccordionButton>
115
+ <AccordionPanel>
116
+ <Text className="text-text-secondary text-sm">
117
+ This section starts closed and opens when the user clicks on it.
118
+ </Text>
119
+ </AccordionPanel>
120
+ </AccordionItem>
121
+ <AccordionItem>
122
+ <AccordionButton>Also Closed</AccordionButton>
123
+ <AccordionPanel>
124
+ <Text className="text-text-secondary text-sm">
125
+ Another closed section.
126
+ </Text>
127
+ </AccordionPanel>
128
+ </AccordionItem>
129
+ </Accordion>
130
+ </View>
131
+ ),
132
+ }
133
+
134
+ export const MultipleDefaultOpen: Story = {
135
+ render: () => (
136
+ <View style={{ width: 400 }}>
137
+ <Accordion allowMultiple defaultIndex={[0, 2]}>
138
+ <AccordionItem>
139
+ <AccordionButton>First (Open)</AccordionButton>
140
+ <AccordionPanel>
141
+ <Text className="text-text-secondary text-sm">
142
+ This section starts open via defaultIndex.
143
+ </Text>
144
+ </AccordionPanel>
145
+ </AccordionItem>
146
+ <AccordionItem>
147
+ <AccordionButton>Second (Closed)</AccordionButton>
148
+ <AccordionPanel>
149
+ <Text className="text-text-secondary text-sm">
150
+ This section starts closed.
151
+ </Text>
152
+ </AccordionPanel>
153
+ </AccordionItem>
154
+ <AccordionItem>
155
+ <AccordionButton>Third (Open)</AccordionButton>
156
+ <AccordionPanel>
157
+ <Text className="text-text-secondary text-sm">
158
+ This section also starts open via defaultIndex.
159
+ </Text>
160
+ </AccordionPanel>
161
+ </AccordionItem>
162
+ </Accordion>
163
+ </View>
164
+ ),
165
+ }
166
+
167
+ export const WithRichContent: Story = {
168
+ render: () => (
169
+ <View style={{ width: 400 }}>
170
+ <Accordion allowMultiple>
171
+ <AccordionItem>
172
+ <AccordionButton>Pricing Details</AccordionButton>
173
+ <AccordionPanel>
174
+ <View style={{ gap: 8 }}>
175
+ <View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
176
+ <Text className="text-text-primary text-sm">Base plan</Text>
177
+ <Text className="text-text-primary text-sm font-semibold">$29/mo</Text>
178
+ </View>
179
+ <View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
180
+ <Text className="text-text-primary text-sm">Pro plan</Text>
181
+ <Text className="text-text-primary text-sm font-semibold">$79/mo</Text>
182
+ </View>
183
+ <View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
184
+ <Text className="text-text-primary text-sm">Enterprise</Text>
185
+ <Text className="text-text-primary text-sm font-semibold">Custom</Text>
186
+ </View>
187
+ </View>
188
+ </AccordionPanel>
189
+ </AccordionItem>
190
+ <AccordionItem>
191
+ <AccordionButton>Features Included</AccordionButton>
192
+ <AccordionPanel>
193
+ <View style={{ gap: 6 }}>
194
+ {['Unlimited projects', 'Custom domains', 'Priority support', 'API access', 'Team collaboration'].map(
195
+ (feature) => (
196
+ <View key={feature} style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
197
+ <Text className="text-status-success text-sm">&#10003;</Text>
198
+ <Text className="text-text-primary text-sm">{feature}</Text>
199
+ </View>
200
+ )
201
+ )}
202
+ </View>
203
+ </AccordionPanel>
204
+ </AccordionItem>
205
+ <AccordionItem>
206
+ <AccordionButton>Frequently Asked Questions</AccordionButton>
207
+ <AccordionPanel>
208
+ <View style={{ gap: 12 }}>
209
+ <View style={{ gap: 2 }}>
210
+ <Text className="text-text-primary text-sm font-medium">Can I cancel anytime?</Text>
211
+ <Text className="text-text-secondary text-sm">
212
+ Yes, you can cancel your subscription at any time with no penalties.
213
+ </Text>
214
+ </View>
215
+ <View style={{ gap: 2 }}>
216
+ <Text className="text-text-primary text-sm font-medium">Is there a free trial?</Text>
217
+ <Text className="text-text-secondary text-sm">
218
+ We offer a 14-day free trial for all plans.
219
+ </Text>
220
+ </View>
221
+ </View>
222
+ </AccordionPanel>
223
+ </AccordionItem>
224
+ </Accordion>
225
+ </View>
226
+ ),
227
+ }
228
+
229
+ export const ManyItems: Story = {
230
+ render: () => (
231
+ <View style={{ width: 400 }}>
232
+ <Accordion>
233
+ {Array.from({ length: 10 }).map((_, i) => (
234
+ <AccordionItem key={i}>
235
+ <AccordionButton>Item {i + 1}</AccordionButton>
236
+ <AccordionPanel>
237
+ <Text className="text-text-secondary text-sm">
238
+ Content for accordion item {i + 1}. Only one item can be expanded at a time in
239
+ single mode.
240
+ </Text>
241
+ </AccordionPanel>
242
+ </AccordionItem>
243
+ ))}
244
+ </Accordion>
245
+ </View>
246
+ ),
247
+ }
248
+
249
+ export const SingleCollapse: Story = {
250
+ render: () => (
251
+ <View style={{ width: 400 }}>
252
+ <Collapse>
253
+ <CollapseButton>
254
+ <Text className="text-text-primary font-medium">Click to expand</Text>
255
+ </CollapseButton>
256
+ <CollapseContent>
257
+ <View className="px-4 pb-3">
258
+ <Text className="text-text-secondary text-sm">
259
+ This is a standalone Collapse component, useful when you only need a single
260
+ collapsible section rather than an accordion group.
261
+ </Text>
262
+ </View>
263
+ </CollapseContent>
264
+ </Collapse>
265
+ </View>
266
+ ),
267
+ }
268
+
269
+ export const CollapseDefaultOpen: Story = {
270
+ render: () => (
271
+ <View style={{ width: 400 }}>
272
+ <Collapse defaultIsOpen>
273
+ <CollapseButton>
274
+ <Text className="text-text-primary font-medium">Details (starts open)</Text>
275
+ </CollapseButton>
276
+ <CollapseContent>
277
+ <View className="px-4 pb-3">
278
+ <Text className="text-text-secondary text-sm">
279
+ This collapse starts in the open state via defaultIsOpen. The user can toggle it
280
+ closed by clicking the header.
281
+ </Text>
282
+ </View>
283
+ </CollapseContent>
284
+ </Collapse>
285
+ </View>
286
+ ),
287
+ }
288
+
289
+ export const CollapseControlled: Story = {
290
+ render: function Render() {
291
+ const [isOpen, setIsOpen] = useState(false)
292
+ return (
293
+ <View style={{ width: 400, gap: 8 }}>
294
+ <Text className="text-sm text-text-secondary">
295
+ State: {isOpen ? 'Open' : 'Closed'}
296
+ </Text>
297
+ <Collapse isOpen={isOpen} onToggle={setIsOpen}>
298
+ <CollapseButton>
299
+ <Text className="text-text-primary font-medium">Controlled Collapse</Text>
300
+ </CollapseButton>
301
+ <CollapseContent>
302
+ <View className="px-4 pb-3">
303
+ <Text className="text-text-secondary text-sm">
304
+ This collapse is controlled externally. The isOpen prop and onToggle callback
305
+ manage state outside the component.
306
+ </Text>
307
+ </View>
308
+ </CollapseContent>
309
+ </Collapse>
310
+ </View>
311
+ )
312
+ },
313
+ }