@urbint/cl 1.0.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/.cursor/rules +313 -0
  2. package/.rnstorybook/index.ts +11 -0
  3. package/.rnstorybook/main.ts +8 -0
  4. package/.rnstorybook/preview.tsx +14 -0
  5. package/.rnstorybook/storybook.requires.ts +49 -0
  6. package/.storybook/main.ts +16 -0
  7. package/.storybook/preview.ts +32 -0
  8. package/.storybook/vitest.setup.ts +7 -0
  9. package/App.tsx +422 -0
  10. package/README.md +229 -0
  11. package/app.json +33 -0
  12. package/assets/adaptive-icon.png +0 -0
  13. package/assets/favicon.png +0 -0
  14. package/assets/icon.png +0 -0
  15. package/assets/splash-icon.png +0 -0
  16. package/babel.config.js +16 -0
  17. package/docs/components/CodeBlock.tsx +80 -0
  18. package/docs/components/PropTable.tsx +93 -0
  19. package/docs/components/Sidebar.tsx +199 -0
  20. package/docs/components/index.ts +8 -0
  21. package/docs/data/colorTokens.ts +70 -0
  22. package/docs/data/componentData.tsx +1685 -0
  23. package/docs/data/index.ts +7 -0
  24. package/docs/index.ts +19 -0
  25. package/docs/navigation.ts +94 -0
  26. package/docs/pages/ColorsPage.tsx +226 -0
  27. package/docs/pages/ComponentPage.tsx +235 -0
  28. package/docs/pages/InstallationPage.tsx +232 -0
  29. package/docs/pages/IntroductionPage.tsx +163 -0
  30. package/docs/pages/ThemingPage.tsx +251 -0
  31. package/docs/pages/index.ts +10 -0
  32. package/docs/theme.ts +64 -0
  33. package/docs/types.ts +54 -0
  34. package/index.ts +8 -0
  35. package/llms.txt +1893 -0
  36. package/mcp-config.example.json +10 -0
  37. package/mcp-server/README.md +192 -0
  38. package/mcp-server/package-lock.json +1707 -0
  39. package/mcp-server/package.json +38 -0
  40. package/mcp-server/src/index.ts +1136 -0
  41. package/mcp-server/src/registry/components.ts +1446 -0
  42. package/mcp-server/src/registry/index.ts +3 -0
  43. package/mcp-server/src/registry/tokens.ts +256 -0
  44. package/mcp-server/tsconfig.json +19 -0
  45. package/package.json +92 -0
  46. package/src/components/Accordion/Accordion.stories.tsx +226 -0
  47. package/src/components/Accordion/Accordion.tsx +255 -0
  48. package/src/components/Accordion/index.ts +12 -0
  49. package/src/components/ActionSheet/ActionSheet.stories.tsx +393 -0
  50. package/src/components/ActionSheet/ActionSheet.tsx +258 -0
  51. package/src/components/ActionSheet/index.ts +2 -0
  52. package/src/components/Alert/Alert.stories.tsx +165 -0
  53. package/src/components/Alert/Alert.tsx +164 -0
  54. package/src/components/Alert/index.ts +2 -0
  55. package/src/components/AlertDialog/AlertDialog.stories.tsx +330 -0
  56. package/src/components/AlertDialog/AlertDialog.tsx +234 -0
  57. package/src/components/AlertDialog/index.ts +2 -0
  58. package/src/components/Avatar/Avatar.stories.tsx +154 -0
  59. package/src/components/Avatar/Avatar.tsx +219 -0
  60. package/src/components/Avatar/index.ts +2 -0
  61. package/src/components/Badge/Badge.stories.tsx +146 -0
  62. package/src/components/Badge/Badge.tsx +125 -0
  63. package/src/components/Badge/index.ts +2 -0
  64. package/src/components/Box/Box.stories.tsx +192 -0
  65. package/src/components/Box/Box.tsx +184 -0
  66. package/src/components/Box/index.ts +2 -0
  67. package/src/components/Button/Button.stories.tsx +157 -0
  68. package/src/components/Button/Button.tsx +180 -0
  69. package/src/components/Button/index.ts +2 -0
  70. package/src/components/Card/Card.stories.tsx +145 -0
  71. package/src/components/Card/Card.tsx +169 -0
  72. package/src/components/Card/index.ts +11 -0
  73. package/src/components/Center/Center.stories.tsx +215 -0
  74. package/src/components/Center/Center.tsx +29 -0
  75. package/src/components/Center/index.ts +2 -0
  76. package/src/components/Checkbox/Checkbox.stories.tsx +94 -0
  77. package/src/components/Checkbox/Checkbox.tsx +242 -0
  78. package/src/components/Checkbox/index.ts +2 -0
  79. package/src/components/DatePicker/DatePicker.stories.tsx +623 -0
  80. package/src/components/DatePicker/DatePicker.tsx +1228 -0
  81. package/src/components/DatePicker/index.ts +8 -0
  82. package/src/components/Divider/Divider.stories.tsx +224 -0
  83. package/src/components/Divider/Divider.tsx +73 -0
  84. package/src/components/Divider/index.ts +2 -0
  85. package/src/components/Drawer/Drawer.stories.tsx +414 -0
  86. package/src/components/Drawer/Drawer.tsx +342 -0
  87. package/src/components/Drawer/index.ts +11 -0
  88. package/src/components/Fab/Fab.stories.tsx +360 -0
  89. package/src/components/Fab/Fab.tsx +185 -0
  90. package/src/components/Fab/index.ts +2 -0
  91. package/src/components/FormControl/FormControl.stories.tsx +276 -0
  92. package/src/components/FormControl/FormControl.tsx +185 -0
  93. package/src/components/FormControl/index.ts +12 -0
  94. package/src/components/Grid/Grid.stories.tsx +244 -0
  95. package/src/components/Grid/Grid.tsx +93 -0
  96. package/src/components/Grid/index.ts +2 -0
  97. package/src/components/HStack/HStack.stories.tsx +230 -0
  98. package/src/components/HStack/HStack.tsx +80 -0
  99. package/src/components/HStack/index.ts +2 -0
  100. package/src/components/Heading/Heading.stories.tsx +111 -0
  101. package/src/components/Heading/Heading.tsx +85 -0
  102. package/src/components/Heading/index.ts +2 -0
  103. package/src/components/Icon/Icon.stories.tsx +320 -0
  104. package/src/components/Icon/Icon.tsx +117 -0
  105. package/src/components/Icon/index.ts +2 -0
  106. package/src/components/Image/Image.stories.tsx +357 -0
  107. package/src/components/Image/Image.tsx +168 -0
  108. package/src/components/Image/index.ts +2 -0
  109. package/src/components/Input/Input.stories.tsx +164 -0
  110. package/src/components/Input/Input.tsx +274 -0
  111. package/src/components/Input/index.ts +2 -0
  112. package/src/components/Link/Link.stories.tsx +187 -0
  113. package/src/components/Link/Link.tsx +104 -0
  114. package/src/components/Link/index.ts +2 -0
  115. package/src/components/Menu/Menu.stories.tsx +363 -0
  116. package/src/components/Menu/Menu.tsx +238 -0
  117. package/src/components/Menu/index.ts +2 -0
  118. package/src/components/Modal/Modal.stories.tsx +156 -0
  119. package/src/components/Modal/Modal.tsx +280 -0
  120. package/src/components/Modal/index.ts +11 -0
  121. package/src/components/Popover/Popover.stories.tsx +330 -0
  122. package/src/components/Popover/Popover.tsx +315 -0
  123. package/src/components/Popover/index.ts +11 -0
  124. package/src/components/Portal/Portal.stories.tsx +376 -0
  125. package/src/components/Portal/Portal.tsx +100 -0
  126. package/src/components/Portal/index.ts +2 -0
  127. package/src/components/Pressable/Pressable.stories.tsx +338 -0
  128. package/src/components/Pressable/Pressable.tsx +71 -0
  129. package/src/components/Pressable/index.ts +2 -0
  130. package/src/components/Progress/Progress.stories.tsx +131 -0
  131. package/src/components/Progress/Progress.tsx +219 -0
  132. package/src/components/Progress/index.ts +2 -0
  133. package/src/components/Radio/Radio.stories.tsx +101 -0
  134. package/src/components/Radio/Radio.tsx +234 -0
  135. package/src/components/Radio/index.ts +2 -0
  136. package/src/components/Select/Select.stories.tsx +908 -0
  137. package/src/components/Select/Select.tsx +659 -0
  138. package/src/components/Select/index.ts +8 -0
  139. package/src/components/Skeleton/Skeleton.stories.tsx +154 -0
  140. package/src/components/Skeleton/Skeleton.tsx +192 -0
  141. package/src/components/Skeleton/index.ts +8 -0
  142. package/src/components/Slider/Slider.stories.tsx +363 -0
  143. package/src/components/Slider/Slider.tsx +209 -0
  144. package/src/components/Slider/index.ts +2 -0
  145. package/src/components/Spinner/Spinner.stories.tsx +108 -0
  146. package/src/components/Spinner/Spinner.tsx +121 -0
  147. package/src/components/Spinner/index.ts +2 -0
  148. package/src/components/Switch/Switch.stories.tsx +116 -0
  149. package/src/components/Switch/Switch.tsx +172 -0
  150. package/src/components/Switch/index.ts +2 -0
  151. package/src/components/Table/Table.stories.tsx +417 -0
  152. package/src/components/Table/Table.tsx +233 -0
  153. package/src/components/Table/index.ts +2 -0
  154. package/src/components/Text/Text.stories.tsx +93 -0
  155. package/src/components/Text/Text.tsx +119 -0
  156. package/src/components/Text/index.ts +2 -0
  157. package/src/components/Textarea/Textarea.stories.tsx +280 -0
  158. package/src/components/Textarea/Textarea.tsx +212 -0
  159. package/src/components/Textarea/index.ts +2 -0
  160. package/src/components/Toast/Toast.stories.tsx +446 -0
  161. package/src/components/Toast/Toast.tsx +221 -0
  162. package/src/components/Toast/index.ts +2 -0
  163. package/src/components/Tooltip/Tooltip.stories.tsx +354 -0
  164. package/src/components/Tooltip/Tooltip.tsx +261 -0
  165. package/src/components/Tooltip/index.ts +2 -0
  166. package/src/components/VStack/VStack.stories.tsx +183 -0
  167. package/src/components/VStack/VStack.tsx +76 -0
  168. package/src/components/VStack/index.ts +2 -0
  169. package/src/components/index.ts +62 -0
  170. package/src/hooks/index.ts +7 -0
  171. package/src/hooks/useControllableState.ts +41 -0
  172. package/src/hooks/useDisclosure.ts +51 -0
  173. package/src/index.ts +22 -0
  174. package/src/stories/Button.stories.tsx +53 -0
  175. package/src/stories/Button.tsx +101 -0
  176. package/src/stories/Configure.mdx +364 -0
  177. package/src/stories/Header.stories.tsx +33 -0
  178. package/src/stories/Header.tsx +75 -0
  179. package/src/stories/Page.stories.tsx +25 -0
  180. package/src/stories/Page.tsx +154 -0
  181. package/src/stories/assets/accessibility.png +0 -0
  182. package/src/stories/assets/accessibility.svg +1 -0
  183. package/src/stories/assets/addon-library.png +0 -0
  184. package/src/stories/assets/assets.png +0 -0
  185. package/src/stories/assets/avif-test-image.avif +0 -0
  186. package/src/stories/assets/context.png +0 -0
  187. package/src/stories/assets/discord.svg +1 -0
  188. package/src/stories/assets/docs.png +0 -0
  189. package/src/stories/assets/figma-plugin.png +0 -0
  190. package/src/stories/assets/github.svg +1 -0
  191. package/src/stories/assets/share.png +0 -0
  192. package/src/stories/assets/styling.png +0 -0
  193. package/src/stories/assets/testing.png +0 -0
  194. package/src/stories/assets/theming.png +0 -0
  195. package/src/stories/assets/tutorials.svg +1 -0
  196. package/src/stories/assets/youtube.svg +1 -0
  197. package/src/styles/index.ts +7 -0
  198. package/src/styles/tokens.ts +318 -0
  199. package/src/styles/unistyles.ts +254 -0
  200. package/src/utils/createContext.tsx +25 -0
  201. package/src/utils/index.ts +7 -0
  202. package/src/utils/mergeRefs.ts +21 -0
  203. package/tsconfig.json +26 -0
  204. package/urbint-cl-1.0.0.tgz +0 -0
  205. package/vitest.config.ts +37 -0
  206. package/vitest.shims.d.ts +1 -0
@@ -0,0 +1,908 @@
1
+ import React, { useState } from 'react';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+ import { Select, SelectOption } from './Select';
4
+ import { VStack } from '../VStack';
5
+ import { HStack } from '../HStack';
6
+ import { Text } from '../Text';
7
+ import { View, StyleSheet } from 'react-native';
8
+ import { colors, spacing, borderRadius } from '../../styles/tokens';
9
+
10
+ const meta: Meta<typeof Select> = {
11
+ title: 'Forms/Select',
12
+ component: Select,
13
+ argTypes: {
14
+ variant: {
15
+ control: 'select',
16
+ options: ['outline', 'filled'],
17
+ },
18
+ size: {
19
+ control: 'select',
20
+ options: ['sm', 'md', 'lg'],
21
+ },
22
+ isInvalid: { control: 'boolean' },
23
+ isDisabled: { control: 'boolean' },
24
+ isRequired: { control: 'boolean' },
25
+ isMultiple: { control: 'boolean' },
26
+ isSearchable: { control: 'boolean' },
27
+ },
28
+ args: {
29
+ placeholder: 'Select an option',
30
+ variant: 'outline',
31
+ size: 'md',
32
+ options: [
33
+ { label: 'Option 1', value: '1' },
34
+ { label: 'Option 2', value: '2' },
35
+ { label: 'Option 3', value: '3' },
36
+ ],
37
+ },
38
+ };
39
+
40
+ export default meta;
41
+
42
+ type Story = StoryObj<typeof Select>;
43
+
44
+ export const Default: Story = {
45
+ render: (args) => (
46
+ <View style={{ width: 300 }}>
47
+ <Select {...args} />
48
+ </View>
49
+ ),
50
+ };
51
+
52
+ export const WithLabel: Story = {
53
+ render: () => (
54
+ <VStack space={16}>
55
+ <Select
56
+ label="Country"
57
+ placeholder="Select a country"
58
+ options={[
59
+ { label: 'United States', value: 'us' },
60
+ { label: 'Canada', value: 'ca' },
61
+ { label: 'United Kingdom', value: 'uk' },
62
+ { label: 'Australia', value: 'au' },
63
+ ]}
64
+ />
65
+ <Select
66
+ label="Language"
67
+ placeholder="Select a language"
68
+ helperText="Choose your preferred language"
69
+ options={[
70
+ { label: 'English', value: 'en' },
71
+ { label: 'Spanish', value: 'es' },
72
+ { label: 'French', value: 'fr' },
73
+ { label: 'German', value: 'de' },
74
+ ]}
75
+ />
76
+ </VStack>
77
+ ),
78
+ };
79
+
80
+ export const Variants: Story = {
81
+ render: () => (
82
+ <VStack space={16}>
83
+ <Text weight="semiBold">Select Variants</Text>
84
+ <Select
85
+ label="Outline"
86
+ variant="outline"
87
+ placeholder="Select..."
88
+ options={[
89
+ { label: 'Option A', value: 'a' },
90
+ { label: 'Option B', value: 'b' },
91
+ ]}
92
+ />
93
+ <Select
94
+ label="Filled"
95
+ variant="filled"
96
+ placeholder="Select..."
97
+ options={[
98
+ { label: 'Option A', value: 'a' },
99
+ { label: 'Option B', value: 'b' },
100
+ ]}
101
+ />
102
+ </VStack>
103
+ ),
104
+ };
105
+
106
+ export const Sizes: Story = {
107
+ render: () => (
108
+ <VStack space={16}>
109
+ <Text weight="semiBold">Select Sizes</Text>
110
+ <Select
111
+ label="Small"
112
+ size="sm"
113
+ placeholder="Small select..."
114
+ options={[
115
+ { label: 'Small Option', value: 'sm' },
116
+ ]}
117
+ />
118
+ <Select
119
+ label="Medium"
120
+ size="md"
121
+ placeholder="Medium select..."
122
+ options={[
123
+ { label: 'Medium Option', value: 'md' },
124
+ ]}
125
+ />
126
+ <Select
127
+ label="Large"
128
+ size="lg"
129
+ placeholder="Large select..."
130
+ options={[
131
+ { label: 'Large Option', value: 'lg' },
132
+ ]}
133
+ />
134
+ </VStack>
135
+ ),
136
+ };
137
+
138
+ export const States: Story = {
139
+ render: () => (
140
+ <VStack space={16}>
141
+ <Text weight="semiBold">Select States</Text>
142
+ <Select
143
+ label="Normal"
144
+ placeholder="Select..."
145
+ options={[
146
+ { label: 'Option 1', value: '1' },
147
+ { label: 'Option 2', value: '2' },
148
+ ]}
149
+ />
150
+ <Select
151
+ label="With Default Value"
152
+ defaultValue="2"
153
+ options={[
154
+ { label: 'Option 1', value: '1' },
155
+ { label: 'Option 2', value: '2' },
156
+ ]}
157
+ />
158
+ <Select
159
+ label="Disabled"
160
+ placeholder="Select..."
161
+ isDisabled
162
+ options={[
163
+ { label: 'Option 1', value: '1' },
164
+ { label: 'Option 2', value: '2' },
165
+ ]}
166
+ />
167
+ <Select
168
+ label="Required"
169
+ placeholder="Select..."
170
+ isRequired
171
+ options={[
172
+ { label: 'Option 1', value: '1' },
173
+ { label: 'Option 2', value: '2' },
174
+ ]}
175
+ />
176
+ </VStack>
177
+ ),
178
+ };
179
+
180
+ export const Validation: Story = {
181
+ render: () => (
182
+ <VStack space={16}>
183
+ <Text weight="semiBold">Validation States</Text>
184
+ <Select
185
+ label="Valid Selection"
186
+ defaultValue="approved"
187
+ helperText="Your selection has been saved"
188
+ options={[
189
+ { label: 'Approved', value: 'approved' },
190
+ { label: 'Pending', value: 'pending' },
191
+ ]}
192
+ />
193
+ <Select
194
+ label="Invalid Selection"
195
+ placeholder="Select..."
196
+ isInvalid
197
+ errorMessage="Please select an option"
198
+ options={[
199
+ { label: 'Option 1', value: '1' },
200
+ { label: 'Option 2', value: '2' },
201
+ ]}
202
+ />
203
+ </VStack>
204
+ ),
205
+ };
206
+
207
+ export const WithDisabledOptions: Story = {
208
+ render: () => (
209
+ <VStack space={16}>
210
+ <Text weight="semiBold">Disabled Options</Text>
211
+ <Select
212
+ label="Plan"
213
+ placeholder="Select a plan"
214
+ options={[
215
+ { label: 'Free', value: 'free' },
216
+ { label: 'Pro', value: 'pro' },
217
+ { label: 'Enterprise', value: 'enterprise', disabled: true },
218
+ ]}
219
+ helperText="Enterprise plan is currently unavailable"
220
+ />
221
+ </VStack>
222
+ ),
223
+ };
224
+
225
+ export const ManyOptions: Story = {
226
+ render: () => (
227
+ <VStack space={16}>
228
+ <Text weight="semiBold">Many Options</Text>
229
+ <Select
230
+ label="State/Province"
231
+ placeholder="Select a state"
232
+ options={[
233
+ { label: 'Alabama', value: 'AL' },
234
+ { label: 'Alaska', value: 'AK' },
235
+ { label: 'Arizona', value: 'AZ' },
236
+ { label: 'Arkansas', value: 'AR' },
237
+ { label: 'California', value: 'CA' },
238
+ { label: 'Colorado', value: 'CO' },
239
+ { label: 'Connecticut', value: 'CT' },
240
+ { label: 'Delaware', value: 'DE' },
241
+ { label: 'Florida', value: 'FL' },
242
+ { label: 'Georgia', value: 'GA' },
243
+ { label: 'Hawaii', value: 'HI' },
244
+ { label: 'Idaho', value: 'ID' },
245
+ { label: 'Illinois', value: 'IL' },
246
+ { label: 'Indiana', value: 'IN' },
247
+ { label: 'Iowa', value: 'IA' },
248
+ ]}
249
+ />
250
+ </VStack>
251
+ ),
252
+ };
253
+
254
+ // ============================================================================
255
+ // SEARCHABLE SELECT STORIES
256
+ // ============================================================================
257
+
258
+ const countryOptions = [
259
+ { label: 'United States', value: 'us' },
260
+ { label: 'United Kingdom', value: 'uk' },
261
+ { label: 'Canada', value: 'ca' },
262
+ { label: 'Australia', value: 'au' },
263
+ { label: 'Germany', value: 'de' },
264
+ { label: 'France', value: 'fr' },
265
+ { label: 'Spain', value: 'es' },
266
+ { label: 'Italy', value: 'it' },
267
+ { label: 'Japan', value: 'jp' },
268
+ { label: 'China', value: 'cn' },
269
+ { label: 'India', value: 'in' },
270
+ { label: 'Brazil', value: 'br' },
271
+ { label: 'Mexico', value: 'mx' },
272
+ { label: 'Argentina', value: 'ar' },
273
+ { label: 'South Korea', value: 'kr' },
274
+ { label: 'Netherlands', value: 'nl' },
275
+ { label: 'Sweden', value: 'se' },
276
+ { label: 'Norway', value: 'no' },
277
+ { label: 'Denmark', value: 'dk' },
278
+ { label: 'Finland', value: 'fi' },
279
+ ];
280
+
281
+ export const Searchable: Story = {
282
+ render: () => (
283
+ <VStack space={16}>
284
+ <Text weight="semiBold">Searchable Select</Text>
285
+ <Text size="caption" color="secondary">
286
+ Type to filter options. Search is case-insensitive.
287
+ </Text>
288
+ <Select
289
+ label="Country"
290
+ placeholder="Select a country..."
291
+ isSearchable
292
+ searchPlaceholder="Search countries..."
293
+ options={countryOptions}
294
+ helperText="Start typing to filter the list"
295
+ />
296
+ </VStack>
297
+ ),
298
+ };
299
+
300
+ export const SearchableWithDefaultValue: Story = {
301
+ render: () => (
302
+ <VStack space={16}>
303
+ <Text weight="semiBold">Searchable with Default Value</Text>
304
+ <Select
305
+ label="Country"
306
+ placeholder="Select a country..."
307
+ isSearchable
308
+ defaultValue="us"
309
+ options={countryOptions}
310
+ />
311
+ </VStack>
312
+ ),
313
+ };
314
+
315
+ export const SearchableMultiSelect: Story = {
316
+ render: () => (
317
+ <VStack space={16}>
318
+ <Text weight="semiBold">Searchable Multi-Select</Text>
319
+ <Text size="caption" color="secondary">
320
+ Combine search with multi-select for powerful filtering.
321
+ </Text>
322
+ <Select
323
+ label="Countries"
324
+ placeholder="Select countries..."
325
+ isSearchable
326
+ isMultiple
327
+ searchPlaceholder="Search countries..."
328
+ options={countryOptions}
329
+ helperText="Select multiple countries"
330
+ />
331
+ </VStack>
332
+ ),
333
+ };
334
+
335
+ export const SearchableMultiSelectWithDefaults: Story = {
336
+ render: () => (
337
+ <VStack space={16}>
338
+ <Text weight="semiBold">Searchable Multi-Select with Defaults</Text>
339
+ <Select
340
+ label="Countries"
341
+ placeholder="Select countries..."
342
+ isSearchable
343
+ isMultiple
344
+ defaultValue={['us', 'uk', 'ca']}
345
+ options={countryOptions}
346
+ helperText="Pre-selected: United States, United Kingdom, Canada"
347
+ />
348
+ </VStack>
349
+ ),
350
+ };
351
+
352
+ const technologyOptions = [
353
+ { label: 'React', value: 'react', category: 'frontend' },
354
+ { label: 'Vue.js', value: 'vue', category: 'frontend' },
355
+ { label: 'Angular', value: 'angular', category: 'frontend' },
356
+ { label: 'Svelte', value: 'svelte', category: 'frontend' },
357
+ { label: 'Node.js', value: 'nodejs', category: 'backend' },
358
+ { label: 'Python', value: 'python', category: 'backend' },
359
+ { label: 'Go', value: 'go', category: 'backend' },
360
+ { label: 'Rust', value: 'rust', category: 'backend' },
361
+ { label: 'PostgreSQL', value: 'postgres', category: 'database' },
362
+ { label: 'MongoDB', value: 'mongodb', category: 'database' },
363
+ { label: 'Redis', value: 'redis', category: 'database' },
364
+ { label: 'MySQL', value: 'mysql', category: 'database' },
365
+ { label: 'Docker', value: 'docker', category: 'devops' },
366
+ { label: 'Kubernetes', value: 'kubernetes', category: 'devops' },
367
+ { label: 'AWS', value: 'aws', category: 'cloud' },
368
+ { label: 'Google Cloud', value: 'gcp', category: 'cloud' },
369
+ { label: 'Azure', value: 'azure', category: 'cloud' },
370
+ ];
371
+
372
+ export const SearchableWithCustomFilter: Story = {
373
+ render: () => (
374
+ <VStack space={16}>
375
+ <Text weight="semiBold">Custom Filter Function</Text>
376
+ <Text size="caption" color="secondary">
377
+ Search by label or category (e.g., try "frontend", "backend", "database")
378
+ </Text>
379
+ <Select
380
+ label="Technologies"
381
+ placeholder="Search technologies..."
382
+ isSearchable
383
+ isMultiple
384
+ searchPlaceholder="Search by name or category..."
385
+ options={technologyOptions}
386
+ filterOption={(option, query) => {
387
+ const searchLower = query.toLowerCase();
388
+ return (
389
+ option.label.toLowerCase().includes(searchLower) ||
390
+ option.category?.toLowerCase().includes(searchLower)
391
+ );
392
+ }}
393
+ helperText="Try searching 'frontend' or 'database'"
394
+ />
395
+ </VStack>
396
+ ),
397
+ };
398
+
399
+ export const SearchableNoResults: Story = {
400
+ render: () => (
401
+ <VStack space={16}>
402
+ <Text weight="semiBold">Custom No Results Text</Text>
403
+ <Text size="caption" color="secondary">
404
+ Customize the message shown when no options match the search.
405
+ </Text>
406
+ <Select
407
+ label="Country"
408
+ placeholder="Select a country..."
409
+ isSearchable
410
+ noResultsText="No countries match your search"
411
+ options={[
412
+ { label: 'United States', value: 'us' },
413
+ { label: 'Canada', value: 'ca' },
414
+ { label: 'Mexico', value: 'mx' },
415
+ ]}
416
+ helperText='Try typing "xyz" to see the no results message'
417
+ />
418
+ </VStack>
419
+ ),
420
+ };
421
+
422
+ export const SearchableControlled: Story = {
423
+ render: () => {
424
+ const [selected, setSelected] = useState<string>('');
425
+
426
+ return (
427
+ <VStack space={16}>
428
+ <Text weight="semiBold">Controlled Searchable Select</Text>
429
+ <Select
430
+ label="Country"
431
+ placeholder="Select a country..."
432
+ isSearchable
433
+ value={selected}
434
+ onChange={(value) => setSelected(value as string)}
435
+ options={countryOptions}
436
+ />
437
+ <Text size="caption" color="secondary">
438
+ Selected: {selected || 'None'}
439
+ </Text>
440
+ </VStack>
441
+ );
442
+ },
443
+ };
444
+
445
+ export const SearchableSizes: Story = {
446
+ render: () => (
447
+ <VStack space={16}>
448
+ <Text weight="semiBold">Searchable Select Sizes</Text>
449
+ <Select
450
+ label="Small"
451
+ size="sm"
452
+ isSearchable
453
+ placeholder="Search..."
454
+ options={countryOptions.slice(0, 8)}
455
+ />
456
+ <Select
457
+ label="Medium"
458
+ size="md"
459
+ isSearchable
460
+ placeholder="Search..."
461
+ options={countryOptions.slice(0, 8)}
462
+ />
463
+ <Select
464
+ label="Large"
465
+ size="lg"
466
+ isSearchable
467
+ placeholder="Search..."
468
+ options={countryOptions.slice(0, 8)}
469
+ />
470
+ </VStack>
471
+ ),
472
+ };
473
+
474
+ // ============================================================================
475
+ // MULTISELECT STORIES
476
+ // ============================================================================
477
+
478
+ export const MultiSelect: Story = {
479
+ render: () => (
480
+ <VStack space={16}>
481
+ <Text weight="semiBold">Multi-Select</Text>
482
+ <Select
483
+ label="Dropdown Test"
484
+ placeholder="Select options..."
485
+ isMultiple
486
+ options={[
487
+ { label: 'Option 1', value: '1' },
488
+ { label: 'Option 2', value: '2' },
489
+ { label: 'Option 3', value: '3' },
490
+ { label: 'Option 4', value: '4' },
491
+ { label: 'Option 5', value: '5' },
492
+ { label: 'Other', value: 'other' },
493
+ ]}
494
+ helperText="You can select multiple options"
495
+ />
496
+ </VStack>
497
+ ),
498
+ };
499
+
500
+ export const MultiSelectWithDefaultValues: Story = {
501
+ render: () => (
502
+ <VStack space={16}>
503
+ <Text weight="semiBold">Multi-Select with Default Values</Text>
504
+ <Select
505
+ label="Select Skills"
506
+ placeholder="Choose your skills..."
507
+ isMultiple
508
+ defaultValue={['react', 'typescript', 'nodejs']}
509
+ options={[
510
+ { label: 'React', value: 'react' },
511
+ { label: 'TypeScript', value: 'typescript' },
512
+ { label: 'Node.js', value: 'nodejs' },
513
+ { label: 'Python', value: 'python' },
514
+ { label: 'Go', value: 'go' },
515
+ { label: 'Rust', value: 'rust' },
516
+ ]}
517
+ helperText="Pre-selected skills: React, TypeScript, Node.js"
518
+ />
519
+ </VStack>
520
+ ),
521
+ };
522
+
523
+ export const MultiSelectControlled: Story = {
524
+ render: () => {
525
+ const [selectedValues, setSelectedValues] = useState<string[]>(['frontend', 'backend']);
526
+
527
+ return (
528
+ <VStack space={16}>
529
+ <Text weight="semiBold">Controlled Multi-Select</Text>
530
+ <Select
531
+ label="Team Roles"
532
+ placeholder="Assign roles..."
533
+ isMultiple
534
+ value={selectedValues}
535
+ onChange={(value) => setSelectedValues(value as string[])}
536
+ options={[
537
+ { label: 'Frontend', value: 'frontend' },
538
+ { label: 'Backend', value: 'backend' },
539
+ { label: 'DevOps', value: 'devops' },
540
+ { label: 'Design', value: 'design' },
541
+ { label: 'QA', value: 'qa' },
542
+ { label: 'Product', value: 'product' },
543
+ ]}
544
+ />
545
+ <Text size="caption" color="secondary">
546
+ Selected: {selectedValues.join(', ') || 'None'}
547
+ </Text>
548
+ </VStack>
549
+ );
550
+ },
551
+ };
552
+
553
+ export const MultiSelectWithDisabledOptions: Story = {
554
+ render: () => (
555
+ <VStack space={16}>
556
+ <Text weight="semiBold">Multi-Select with Disabled Options</Text>
557
+ <Select
558
+ label="Subscription Features"
559
+ placeholder="Choose features..."
560
+ isMultiple
561
+ defaultValue={['basic']}
562
+ options={[
563
+ { label: 'Basic Analytics', value: 'basic' },
564
+ { label: 'Advanced Analytics', value: 'advanced' },
565
+ { label: 'Custom Reports', value: 'reports', disabled: true },
566
+ { label: 'API Access', value: 'api' },
567
+ { label: 'Priority Support', value: 'support', disabled: true },
568
+ { label: 'Team Collaboration', value: 'team' },
569
+ ]}
570
+ helperText="Some features require a higher subscription tier"
571
+ />
572
+ </VStack>
573
+ ),
574
+ };
575
+
576
+ export const MultiSelectSizes: Story = {
577
+ render: () => (
578
+ <VStack space={16}>
579
+ <Text weight="semiBold">Multi-Select Sizes</Text>
580
+ <Select
581
+ label="Small"
582
+ size="sm"
583
+ isMultiple
584
+ placeholder="Select categories..."
585
+ defaultValue={['tech']}
586
+ options={[
587
+ { label: 'Technology', value: 'tech' },
588
+ { label: 'Business', value: 'business' },
589
+ { label: 'Design', value: 'design' },
590
+ ]}
591
+ />
592
+ <Select
593
+ label="Medium"
594
+ size="md"
595
+ isMultiple
596
+ placeholder="Select categories..."
597
+ defaultValue={['tech', 'design']}
598
+ options={[
599
+ { label: 'Technology', value: 'tech' },
600
+ { label: 'Business', value: 'business' },
601
+ { label: 'Design', value: 'design' },
602
+ ]}
603
+ />
604
+ <Select
605
+ label="Large"
606
+ size="lg"
607
+ isMultiple
608
+ placeholder="Select categories..."
609
+ defaultValue={['tech', 'business', 'design']}
610
+ options={[
611
+ { label: 'Technology', value: 'tech' },
612
+ { label: 'Business', value: 'business' },
613
+ { label: 'Design', value: 'design' },
614
+ ]}
615
+ />
616
+ </VStack>
617
+ ),
618
+ };
619
+
620
+ export const MultiSelectValidation: Story = {
621
+ render: () => (
622
+ <VStack space={16}>
623
+ <Text weight="semiBold">Multi-Select Validation</Text>
624
+ <Select
625
+ label="Required Tags"
626
+ placeholder="Select at least one tag..."
627
+ isMultiple
628
+ isRequired
629
+ isInvalid
630
+ errorMessage="Please select at least one tag"
631
+ options={[
632
+ { label: 'Important', value: 'important' },
633
+ { label: 'Urgent', value: 'urgent' },
634
+ { label: 'Review', value: 'review' },
635
+ { label: 'Follow-up', value: 'followup' },
636
+ ]}
637
+ />
638
+ <Select
639
+ label="Selected Tags"
640
+ placeholder="Select tags..."
641
+ isMultiple
642
+ defaultValue={['important', 'urgent']}
643
+ helperText="Tags applied successfully"
644
+ options={[
645
+ { label: 'Important', value: 'important' },
646
+ { label: 'Urgent', value: 'urgent' },
647
+ { label: 'Review', value: 'review' },
648
+ { label: 'Follow-up', value: 'followup' },
649
+ ]}
650
+ />
651
+ </VStack>
652
+ ),
653
+ };
654
+
655
+ // ============================================================================
656
+ // FORM EXAMPLES
657
+ // ============================================================================
658
+
659
+ export const FormExample: Story = {
660
+ render: () => (
661
+ <VStack space={16}>
662
+ <Text weight="semiBold">Shipping Information</Text>
663
+ <VStack space={12}>
664
+ <Select
665
+ label="Country"
666
+ placeholder="Select country"
667
+ isRequired
668
+ isSearchable
669
+ options={countryOptions}
670
+ />
671
+ <Select
672
+ label="Shipping Method"
673
+ placeholder="Select method"
674
+ isRequired
675
+ options={[
676
+ { label: 'Standard (5-7 days)', value: 'standard' },
677
+ { label: 'Express (2-3 days)', value: 'express' },
678
+ { label: 'Next Day', value: 'next_day' },
679
+ ]}
680
+ helperText="Delivery times are estimates"
681
+ />
682
+ <Select
683
+ label="Package Size"
684
+ placeholder="Select size"
685
+ options={[
686
+ { label: 'Small (up to 1 lb)', value: 'small' },
687
+ { label: 'Medium (1-5 lbs)', value: 'medium' },
688
+ { label: 'Large (5-20 lbs)', value: 'large' },
689
+ { label: 'Extra Large (20+ lbs)', value: 'xl' },
690
+ ]}
691
+ />
692
+ </VStack>
693
+ </VStack>
694
+ ),
695
+ };
696
+
697
+ export const MultiSelectFormExample: Story = {
698
+ render: () => (
699
+ <VStack space={16}>
700
+ <Text weight="semiBold">Project Settings</Text>
701
+ <VStack space={12}>
702
+ <Select
703
+ label="Project Type"
704
+ placeholder="Select type"
705
+ isRequired
706
+ options={[
707
+ { label: 'Web Application', value: 'web' },
708
+ { label: 'Mobile App', value: 'mobile' },
709
+ { label: 'API Service', value: 'api' },
710
+ { label: 'Desktop App', value: 'desktop' },
711
+ ]}
712
+ />
713
+ <Select
714
+ label="Technologies"
715
+ placeholder="Select technologies..."
716
+ isMultiple
717
+ isSearchable
718
+ isRequired
719
+ options={technologyOptions}
720
+ helperText="Select all technologies used in this project"
721
+ />
722
+ <Select
723
+ label="Team Members"
724
+ placeholder="Assign team members..."
725
+ isMultiple
726
+ isSearchable
727
+ options={[
728
+ { label: 'John Doe', value: 'john' },
729
+ { label: 'Jane Smith', value: 'jane' },
730
+ { label: 'Bob Johnson', value: 'bob' },
731
+ { label: 'Alice Williams', value: 'alice' },
732
+ { label: 'Charlie Brown', value: 'charlie' },
733
+ ]}
734
+ helperText="Optional: Assign team members to this project"
735
+ />
736
+ <Select
737
+ label="Priority"
738
+ placeholder="Select priority"
739
+ defaultValue="medium"
740
+ options={[
741
+ { label: 'Low', value: 'low' },
742
+ { label: 'Medium', value: 'medium' },
743
+ { label: 'High', value: 'high' },
744
+ { label: 'Critical', value: 'critical' },
745
+ ]}
746
+ />
747
+ </VStack>
748
+ </VStack>
749
+ ),
750
+ };
751
+
752
+ // ============================================================================
753
+ // CUSTOM RENDERING
754
+ // ============================================================================
755
+
756
+ const colorOptions = [
757
+ { label: 'Red', value: 'red', color: '#EF4444' },
758
+ { label: 'Green', value: 'green', color: '#22C55E' },
759
+ { label: 'Blue', value: 'blue', color: '#3B82F6' },
760
+ { label: 'Yellow', value: 'yellow', color: '#EAB308' },
761
+ { label: 'Purple', value: 'purple', color: '#A855F7' },
762
+ { label: 'Pink', value: 'pink', color: '#EC4899' },
763
+ ];
764
+
765
+ const customStyles = StyleSheet.create({
766
+ colorSwatch: {
767
+ width: 16,
768
+ height: 16,
769
+ borderRadius: 4,
770
+ marginRight: 8,
771
+ },
772
+ optionRow: {
773
+ flexDirection: 'row',
774
+ alignItems: 'center',
775
+ flex: 1,
776
+ },
777
+ });
778
+
779
+ export const CustomRendering: Story = {
780
+ render: () => (
781
+ <VStack space={16}>
782
+ <Text weight="semiBold">Custom Option Rendering</Text>
783
+ <Select
784
+ label="Select Color"
785
+ placeholder="Choose a color..."
786
+ options={colorOptions}
787
+ renderOption={({ option, isSelected }) => (
788
+ <View style={customStyles.optionRow}>
789
+ <View style={[customStyles.colorSwatch, { backgroundColor: option.color }]} />
790
+ <Text style={{ color: isSelected ? colors.brand.blue : colors.text.default }}>
791
+ {option.label}
792
+ </Text>
793
+ </View>
794
+ )}
795
+ renderSelectedValue={({ option, placeholder }) => (
796
+ <View style={customStyles.optionRow}>
797
+ {option ? (
798
+ <>
799
+ <View style={[customStyles.colorSwatch, { backgroundColor: option.color }]} />
800
+ <Text>{option.label}</Text>
801
+ </>
802
+ ) : (
803
+ <Text style={{ color: colors.text.disabled }}>{placeholder}</Text>
804
+ )}
805
+ </View>
806
+ )}
807
+ />
808
+ <Select
809
+ label="Select Colors (Multi)"
810
+ placeholder="Choose colors..."
811
+ isMultiple
812
+ options={colorOptions}
813
+ renderOption={({ option, isSelected, isHovered }) => (
814
+ <View style={customStyles.optionRow}>
815
+ <View style={[customStyles.colorSwatch, { backgroundColor: option.color }]} />
816
+ <Text style={{ color: isSelected ? colors.brand.blue : colors.text.default }}>
817
+ {option.label}
818
+ </Text>
819
+ </View>
820
+ )}
821
+ renderSelectedValue={({ selectedOptions, placeholder, isMultiple }) => (
822
+ <View style={[customStyles.optionRow, { gap: 4 }]}>
823
+ {selectedOptions.length > 0 ? (
824
+ selectedOptions.map((opt) => (
825
+ <View
826
+ key={opt.value}
827
+ style={[
828
+ customStyles.colorSwatch,
829
+ { backgroundColor: opt.color, marginRight: 0 }
830
+ ]}
831
+ />
832
+ ))
833
+ ) : (
834
+ <Text style={{ color: colors.text.disabled }}>{placeholder}</Text>
835
+ )}
836
+ </View>
837
+ )}
838
+ />
839
+ </VStack>
840
+ ),
841
+ };
842
+
843
+ export const CustomRenderingWithSearch: Story = {
844
+ render: () => (
845
+ <VStack space={16}>
846
+ <Text weight="semiBold">Searchable with Custom Rendering</Text>
847
+ <Select
848
+ label="Select Colors"
849
+ placeholder="Search colors..."
850
+ isSearchable
851
+ isMultiple
852
+ options={colorOptions}
853
+ renderOption={({ option, isSelected, searchQuery }) => (
854
+ <View style={customStyles.optionRow}>
855
+ <View style={[customStyles.colorSwatch, { backgroundColor: option.color }]} />
856
+ <Text style={{ color: isSelected ? colors.brand.blue : colors.text.default }}>
857
+ {option.label}
858
+ </Text>
859
+ </View>
860
+ )}
861
+ renderSelectedValue={({ selectedOptions, placeholder }) => (
862
+ <View style={[customStyles.optionRow, { gap: 4 }]}>
863
+ {selectedOptions.length > 0 ? (
864
+ selectedOptions.map((opt) => (
865
+ <View
866
+ key={opt.value}
867
+ style={[
868
+ customStyles.colorSwatch,
869
+ { backgroundColor: opt.color, marginRight: 0 }
870
+ ]}
871
+ />
872
+ ))
873
+ ) : (
874
+ <Text style={{ color: colors.text.disabled }}>{placeholder}</Text>
875
+ )}
876
+ </View>
877
+ )}
878
+ />
879
+ </VStack>
880
+ ),
881
+ };
882
+
883
+ // ============================================================================
884
+ // HOVER STATE DEMO
885
+ // ============================================================================
886
+
887
+ export const HoverStateDemo: Story = {
888
+ render: () => (
889
+ <VStack space={16}>
890
+ <Text weight="semiBold">Hover State Demo</Text>
891
+ <Text size="caption" color="secondary">
892
+ On web, hover over the select trigger and dropdown options to see hover states.
893
+ </Text>
894
+ <Select
895
+ label="Hover over me"
896
+ placeholder="Hover to see border change..."
897
+ options={[
898
+ { label: 'Option 1 - Hover to highlight', value: '1' },
899
+ { label: 'Option 2 - Hover to highlight', value: '2' },
900
+ { label: 'Option 3 - Hover to highlight', value: '3' },
901
+ { label: 'Option 4 - Hover to highlight', value: '4' },
902
+ { label: 'Option 5 - Hover to highlight', value: '5' },
903
+ ]}
904
+ helperText="Hover states provide visual feedback on web"
905
+ />
906
+ </VStack>
907
+ ),
908
+ };