@cdx-ui/components 0.2.3 → 0.2.4

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 (248) hide show
  1. package/AGENTS.md +24 -0
  2. package/README.md +2 -0
  3. package/coverage.svg +1 -1
  4. package/docs/components/accordion/content.md +538 -0
  5. package/docs/components/alert/content.md +203 -0
  6. package/docs/components/alert-dialog/content.md +319 -0
  7. package/docs/components/avatar/content.md +368 -0
  8. package/docs/components/avatar/design.md +202 -0
  9. package/docs/components/badge/content.md +235 -0
  10. package/docs/components/bottom-sheet/content.md +351 -0
  11. package/docs/components/bottom-sheet/design.md +249 -0
  12. package/docs/components/breadcrumbs/content.md +169 -0
  13. package/docs/components/button/content.md +398 -0
  14. package/docs/components/button/design.md +258 -0
  15. package/docs/components/calendar/content.md +137 -0
  16. package/docs/components/card/content.md +283 -0
  17. package/docs/components/carousel/content.md +416 -0
  18. package/docs/components/checkbox/content.md +236 -0
  19. package/docs/components/checkbox/design.md +214 -0
  20. package/docs/components/chip/content.md +439 -0
  21. package/docs/components/chip/design.md +418 -0
  22. package/docs/components/color-area/content.md +86 -0
  23. package/docs/components/color-field/content.md +134 -0
  24. package/docs/components/color-picker/content.md +208 -0
  25. package/docs/components/color-slider/content.md +120 -0
  26. package/docs/components/color-swatch/content.md +64 -0
  27. package/docs/components/color-swatch-picker/content.md +126 -0
  28. package/docs/components/content.md +97 -0
  29. package/docs/components/currency-input/content.md +205 -0
  30. package/docs/components/data-grid/content.md +522 -0
  31. package/docs/components/date-field/content.md +147 -0
  32. package/docs/components/date-picker/content.md +360 -0
  33. package/docs/components/date-range-picker/content.md +301 -0
  34. package/docs/components/date-time-field/content.md +157 -0
  35. package/docs/components/date-time-list/content.md +105 -0
  36. package/docs/components/date-time-picker/content.md +272 -0
  37. package/docs/components/dialog/content.md +436 -0
  38. package/docs/components/dialog/design.md +192 -0
  39. package/docs/components/divider/content.md +112 -0
  40. package/docs/components/drawer/content.md +436 -0
  41. package/docs/components/field/content.md +277 -0
  42. package/docs/components/form/content.md +62 -0
  43. package/docs/components/full-screen-dialog/content.md +881 -0
  44. package/docs/components/heading/content.md +55 -0
  45. package/docs/components/icon/content.md +190 -0
  46. package/docs/components/icon-button/content.md +200 -0
  47. package/docs/components/icon-button/design.md +165 -0
  48. package/docs/components/image/content.md +104 -0
  49. package/docs/components/input/content.md +281 -0
  50. package/docs/components/input/design.md +389 -0
  51. package/docs/components/insurance-disclosure/content.md +103 -0
  52. package/docs/components/link/content.md +180 -0
  53. package/docs/components/link/design.md +160 -0
  54. package/docs/components/list-item/content.md +695 -0
  55. package/docs/components/menu/content.md +1247 -0
  56. package/docs/components/month-calendar/content.md +84 -0
  57. package/docs/components/month-picker/content.md +273 -0
  58. package/docs/components/otp-input/content.md +327 -0
  59. package/docs/components/progress-bar/content.md +87 -0
  60. package/docs/components/progress-circle/content.md +176 -0
  61. package/docs/components/progress-segmented/content.md +119 -0
  62. package/docs/components/quick-action-button/content.md +154 -0
  63. package/docs/components/quick-action-button/design.md +170 -0
  64. package/docs/components/radio/content.md +224 -0
  65. package/docs/components/range-calendar/content.md +123 -0
  66. package/docs/components/reorderable-list/content.md +309 -0
  67. package/docs/components/scoped-theme/content.md +47 -0
  68. package/docs/components/scroll-shadow/content.md +686 -0
  69. package/docs/components/select/content.md +400 -0
  70. package/docs/components/select/design.md +301 -0
  71. package/docs/components/skeleton/content.md +103 -0
  72. package/docs/components/slider/content.md +423 -0
  73. package/docs/components/stack/content.md +161 -0
  74. package/docs/components/switch/content.md +150 -0
  75. package/docs/components/switch/design.md +208 -0
  76. package/docs/components/table/content.md +548 -0
  77. package/docs/components/table-pagination/content.md +177 -0
  78. package/docs/components/tabs/content.md +292 -0
  79. package/docs/components/text/content.md +142 -0
  80. package/docs/components/textarea/content.md +222 -0
  81. package/docs/components/tile/content.md +703 -0
  82. package/docs/components/toast/content.md +456 -0
  83. package/docs/components/toggle-button/content.md +505 -0
  84. package/docs/components/tooltip/content.md +350 -0
  85. package/docs/components/virtualized-list/content.md +250 -0
  86. package/docs/foundations/content.md +9 -0
  87. package/docs/foundations/expressions/icons/content.md +19 -0
  88. package/docs/foundations/styles/tokens/all-tokens/content.md +192 -0
  89. package/docs/foundations/styles/tokens/what-are-tokens/content.md +33 -0
  90. package/docs/foundations/styles/tokens/what-are-tokens/design.md +33 -0
  91. package/docs/getting-started/content.md +4 -0
  92. package/docs/getting-started/designers/content.md +13 -0
  93. package/docs/getting-started/designers/design.md +13 -0
  94. package/docs/getting-started/developers/accessibility/content.md +31 -0
  95. package/docs/getting-started/developers/content.md +612 -0
  96. package/docs/getting-started/developers/styling/content.md +193 -0
  97. package/docs/llms.txt +193 -0
  98. package/docs/patterns/account-management/content.md +75 -0
  99. package/docs/patterns/authentication/content.md +52 -0
  100. package/docs/patterns/content.md +10 -0
  101. package/docs/patterns/dashboard/content.md +39 -0
  102. package/docs/patterns/empty-states/content.md +31 -0
  103. package/docs/patterns/forms/content.md +109 -0
  104. package/docs/patterns/onboarding/content.md +69 -0
  105. package/docs/patterns/payments/content.md +35 -0
  106. package/docs/patterns/settings/content.md +72 -0
  107. package/docs/resources/content.md +9 -0
  108. package/docs/resources/figma-libraries/content.md +42 -0
  109. package/docs/resources/release-notes/0.0.1-beta.118/content.md +27 -0
  110. package/docs/resources/release-notes/0.0.1-beta.122/content.md +21 -0
  111. package/docs/resources/release-notes/0.0.1-beta.124/content.md +16 -0
  112. package/docs/resources/release-notes/0.0.1-beta.126/content.md +10 -0
  113. package/docs/resources/release-notes/0.0.1-beta.127/content.md +14 -0
  114. package/docs/resources/release-notes/0.0.1-beta.128/content.md +9 -0
  115. package/docs/resources/release-notes/0.0.1-beta.129/content.md +18 -0
  116. package/docs/resources/release-notes/0.0.1-beta.130/content.md +18 -0
  117. package/docs/resources/release-notes/0.0.1-beta.133/content.md +7 -0
  118. package/docs/resources/release-notes/0.0.1-beta.134/content.md +17 -0
  119. package/docs/resources/release-notes/0.0.1-beta.136/content.md +7 -0
  120. package/docs/resources/release-notes/0.0.1-beta.137/content.md +10 -0
  121. package/docs/resources/release-notes/0.0.1-beta.138/content.md +7 -0
  122. package/docs/resources/release-notes/0.0.1-beta.139/content.md +13 -0
  123. package/docs/resources/release-notes/0.0.1-beta.140/content.md +26 -0
  124. package/docs/resources/release-notes/0.0.1-beta.141/content.md +63 -0
  125. package/docs/resources/release-notes/0.0.1-beta.142/content.md +7 -0
  126. package/docs/resources/release-notes/0.0.1-beta.143/content.md +12 -0
  127. package/docs/resources/release-notes/0.0.1-beta.144/content.md +7 -0
  128. package/docs/resources/release-notes/0.0.1-beta.145/content.md +7 -0
  129. package/docs/resources/release-notes/0.0.1-beta.147/content.md +10 -0
  130. package/docs/resources/release-notes/0.0.1-beta.149/content.md +10 -0
  131. package/docs/resources/release-notes/0.0.1-beta.150/content.md +13 -0
  132. package/docs/resources/release-notes/0.0.1-beta.152/content.md +7 -0
  133. package/docs/resources/release-notes/0.0.1-beta.154/content.md +15 -0
  134. package/docs/resources/release-notes/0.0.1-beta.156/content.md +7 -0
  135. package/docs/resources/release-notes/0.0.1-beta.158/content.md +9 -0
  136. package/docs/resources/release-notes/0.0.1-beta.159/content.md +11 -0
  137. package/docs/resources/release-notes/0.0.1-beta.160/content.md +7 -0
  138. package/docs/resources/release-notes/0.0.1-beta.161/content.md +7 -0
  139. package/docs/resources/release-notes/0.0.1-beta.163/content.md +7 -0
  140. package/docs/resources/release-notes/0.0.1-beta.164/content.md +9 -0
  141. package/docs/resources/release-notes/0.0.1-beta.166/content.md +17 -0
  142. package/docs/resources/release-notes/0.0.1-beta.167/content.md +7 -0
  143. package/docs/resources/release-notes/0.0.1-beta.168/content.md +7 -0
  144. package/docs/resources/release-notes/0.0.1-beta.169/content.md +7 -0
  145. package/docs/resources/release-notes/0.0.1-beta.171/content.md +16 -0
  146. package/docs/resources/release-notes/0.0.1-beta.172/content.md +24 -0
  147. package/docs/resources/release-notes/0.0.1-beta.173/content.md +15 -0
  148. package/docs/resources/release-notes/0.0.1-beta.174/content.md +7 -0
  149. package/docs/resources/release-notes/0.0.1-beta.176/content.md +32 -0
  150. package/docs/resources/release-notes/0.0.1-beta.179/content.md +11 -0
  151. package/docs/resources/release-notes/0.0.1-beta.182/content.md +13 -0
  152. package/docs/resources/release-notes/0.0.1-beta.185/content.md +7 -0
  153. package/docs/resources/release-notes/0.0.1-beta.187/content.md +27 -0
  154. package/docs/resources/release-notes/0.1.1/content.md +23 -0
  155. package/docs/resources/release-notes/0.1.2/content.md +7 -0
  156. package/docs/resources/release-notes/0.1.4/content.md +19 -0
  157. package/docs/resources/release-notes/0.1.5/content.md +7 -0
  158. package/docs/resources/release-notes/0.1.6/content.md +7 -0
  159. package/docs/resources/release-notes/0.2.0/content.md +31 -0
  160. package/docs/resources/release-notes/0.2.1/content.md +17 -0
  161. package/docs/resources/release-notes/0.2.2/content.md +11 -0
  162. package/docs/resources/release-notes/0.2.3/content.md +15 -0
  163. package/docs/resources/release-notes/content.md +64 -0
  164. package/lib/commonjs/components/DateTimeList/index.js +11 -26
  165. package/lib/commonjs/components/DateTimeList/index.js.map +1 -1
  166. package/lib/commonjs/components/DateTimeList/styles.js +2 -12
  167. package/lib/commonjs/components/DateTimeList/styles.js.map +1 -1
  168. package/lib/commonjs/components/ListItem/index.js.map +1 -1
  169. package/lib/commonjs/components/ListItem/styles.js +2 -1
  170. package/lib/commonjs/components/ListItem/styles.js.map +1 -1
  171. package/lib/commonjs/components/Menu/index.js +11 -5
  172. package/lib/commonjs/components/Menu/index.js.map +1 -1
  173. package/lib/commonjs/components/ScrollShadow/gradient.js +29 -0
  174. package/lib/commonjs/components/ScrollShadow/gradient.js.map +1 -0
  175. package/lib/commonjs/components/ScrollShadow/index.js +171 -44
  176. package/lib/commonjs/components/ScrollShadow/index.js.map +1 -1
  177. package/lib/commonjs/components/ScrollShadow/styles.js +58 -14
  178. package/lib/commonjs/components/ScrollShadow/styles.js.map +1 -1
  179. package/lib/commonjs/components/ScrollShadow/useComposedScrollHandler.js +63 -0
  180. package/lib/commonjs/components/ScrollShadow/useComposedScrollHandler.js.map +1 -0
  181. package/lib/commonjs/components/ScrollShadow/useScrollShadow.js +48 -8
  182. package/lib/commonjs/components/ScrollShadow/useScrollShadow.js.map +1 -1
  183. package/lib/commonjs/components/Tabs/index.js +16 -23
  184. package/lib/commonjs/components/Tabs/index.js.map +1 -1
  185. package/lib/commonjs/components/Tabs/styles.js +1 -10
  186. package/lib/commonjs/components/Tabs/styles.js.map +1 -1
  187. package/lib/commonjs/figma/ListItem.SectionHeader.figma.js +20 -5
  188. package/lib/commonjs/figma/ListItem.SectionHeader.figma.js.map +1 -1
  189. package/lib/module/components/DateTimeList/index.js +12 -27
  190. package/lib/module/components/DateTimeList/index.js.map +1 -1
  191. package/lib/module/components/DateTimeList/styles.js +1 -11
  192. package/lib/module/components/DateTimeList/styles.js.map +1 -1
  193. package/lib/module/components/ListItem/index.js.map +1 -1
  194. package/lib/module/components/ListItem/styles.js +2 -1
  195. package/lib/module/components/ListItem/styles.js.map +1 -1
  196. package/lib/module/components/Menu/index.js +11 -5
  197. package/lib/module/components/Menu/index.js.map +1 -1
  198. package/lib/module/components/ScrollShadow/gradient.js +24 -0
  199. package/lib/module/components/ScrollShadow/gradient.js.map +1 -0
  200. package/lib/module/components/ScrollShadow/index.js +170 -48
  201. package/lib/module/components/ScrollShadow/index.js.map +1 -1
  202. package/lib/module/components/ScrollShadow/styles.js +57 -13
  203. package/lib/module/components/ScrollShadow/styles.js.map +1 -1
  204. package/lib/module/components/ScrollShadow/useComposedScrollHandler.js +57 -0
  205. package/lib/module/components/ScrollShadow/useComposedScrollHandler.js.map +1 -0
  206. package/lib/module/components/ScrollShadow/useScrollShadow.js +46 -8
  207. package/lib/module/components/ScrollShadow/useScrollShadow.js.map +1 -1
  208. package/lib/module/components/Tabs/index.js +17 -24
  209. package/lib/module/components/Tabs/index.js.map +1 -1
  210. package/lib/module/components/Tabs/styles.js +0 -9
  211. package/lib/module/components/Tabs/styles.js.map +1 -1
  212. package/lib/module/figma/ListItem.SectionHeader.figma.js +20 -5
  213. package/lib/module/figma/ListItem.SectionHeader.figma.js.map +1 -1
  214. package/lib/typescript/components/DateTimeList/index.d.ts.map +1 -1
  215. package/lib/typescript/components/DateTimeList/styles.d.ts +1 -4
  216. package/lib/typescript/components/DateTimeList/styles.d.ts.map +1 -1
  217. package/lib/typescript/components/ListItem/index.d.ts +5 -4
  218. package/lib/typescript/components/ListItem/index.d.ts.map +1 -1
  219. package/lib/typescript/components/ListItem/styles.d.ts +1 -1
  220. package/lib/typescript/components/ListItem/styles.d.ts.map +1 -1
  221. package/lib/typescript/components/Menu/index.d.ts.map +1 -1
  222. package/lib/typescript/components/ScrollShadow/gradient.d.ts +7 -0
  223. package/lib/typescript/components/ScrollShadow/gradient.d.ts.map +1 -0
  224. package/lib/typescript/components/ScrollShadow/index.d.ts +57 -15
  225. package/lib/typescript/components/ScrollShadow/index.d.ts.map +1 -1
  226. package/lib/typescript/components/ScrollShadow/styles.d.ts +8 -2
  227. package/lib/typescript/components/ScrollShadow/styles.d.ts.map +1 -1
  228. package/lib/typescript/components/ScrollShadow/useComposedScrollHandler.d.ts +13 -0
  229. package/lib/typescript/components/ScrollShadow/useComposedScrollHandler.d.ts.map +1 -0
  230. package/lib/typescript/components/ScrollShadow/useScrollShadow.d.ts +15 -2
  231. package/lib/typescript/components/ScrollShadow/useScrollShadow.d.ts.map +1 -1
  232. package/lib/typescript/components/Tabs/index.d.ts.map +1 -1
  233. package/lib/typescript/components/Tabs/styles.d.ts +0 -7
  234. package/lib/typescript/components/Tabs/styles.d.ts.map +1 -1
  235. package/lib/typescript/figma/ListItem.SectionHeader.figma.d.ts.map +1 -1
  236. package/package.json +8 -6
  237. package/src/components/DateTimeList/index.tsx +13 -26
  238. package/src/components/DateTimeList/styles.ts +1 -10
  239. package/src/components/ListItem/index.tsx +5 -4
  240. package/src/components/ListItem/styles.ts +1 -0
  241. package/src/components/Menu/index.tsx +11 -8
  242. package/src/components/ScrollShadow/gradient.ts +48 -0
  243. package/src/components/ScrollShadow/index.tsx +300 -69
  244. package/src/components/ScrollShadow/styles.ts +45 -28
  245. package/src/components/ScrollShadow/useComposedScrollHandler.ts +58 -0
  246. package/src/components/ScrollShadow/useScrollShadow.ts +72 -10
  247. package/src/components/Tabs/index.tsx +25 -35
  248. package/src/components/Tabs/styles.ts +0 -9
@@ -0,0 +1,400 @@
1
+ # Select (https://design.candescent.com/docs/components/select)
2
+
3
+ Resources: [Figma](https://www.figma.com/design/0lEVMVlLj8ZS4fgItr3pmd/%E2%AD%90-Forge-DS--React-Native-Components?node-id=427-2296), [Storybook](https://victorious-wave-0daadf00f.6.azurestaticapps.net/?path=/story/inputs-select--default)
4
+
5
+ `Select` provides a compound interface for a trigger, selected value, dropdown content, and options.
6
+
7
+ ```tsx
8
+ import { Button, Icon, Select } from '@cdx-ui/components';
9
+ import { CheckCircle, KeyboardArrowDown } from '@cdx-ui/icons';
10
+ ```
11
+
12
+ ## Basic
13
+
14
+ Provide the same options to `items` and render them as `Select.Item` children so value labels and keyboard navigation stay in sync.
15
+
16
+ ```tsx
17
+ import { Select } from '@cdx-ui/components';
18
+ import { accounts } from './_data';
19
+
20
+ export default function SelectBasicDemo() {
21
+ return (
22
+ <Select items={accounts} className="w-full" accessibilityLabel="Account type">
23
+ <Select.Trigger>
24
+ <Select.Value placeholder="Select an account…" />
25
+ </Select.Trigger>
26
+ <Select.Content>
27
+ {accounts.map((account) => (
28
+ <Select.Item key={account.value} value={account.value}>
29
+ <Select.ItemLabel>{account.label}</Select.ItemLabel>
30
+ </Select.Item>
31
+ ))}
32
+ </Select.Content>
33
+ </Select>
34
+ );
35
+ }
36
+ ```
37
+
38
+ ## Sizes
39
+
40
+ Use the default size for most forms and `small` for compact layouts.
41
+
42
+ ```tsx
43
+ import { Select, Text, View, type SelectProps } from '@cdx-ui/components';
44
+ import { accounts } from './_data';
45
+
46
+ const sizes = ['default', 'small'] as const satisfies readonly NonNullable<SelectProps['size']>[];
47
+
48
+ export default function SelectSizesDemo() {
49
+ return (
50
+ <View className="w-full gap-4">
51
+ {sizes.map((size) => (
52
+ <View key={size} className="gap-2">
53
+ <Text size="sm" color="secondary">
54
+ {size}
55
+ </Text>
56
+ <Select
57
+ items={accounts}
58
+ size={size}
59
+ defaultValue="savings"
60
+ accessibilityLabel={`${size} account type`}
61
+ >
62
+ <Select.Trigger>
63
+ <Select.Value placeholder="Select an account…" />
64
+ </Select.Trigger>
65
+ <Select.Content>
66
+ {accounts.map((account) => (
67
+ <Select.Item key={account.value} value={account.value}>
68
+ <Select.ItemLabel>{account.label}</Select.ItemLabel>
69
+ </Select.Item>
70
+ ))}
71
+ </Select.Content>
72
+ </Select>
73
+ </View>
74
+ ))}
75
+ </View>
76
+ );
77
+ }
78
+ ```
79
+
80
+ ## With field
81
+
82
+ Compose with `Field` to provide a visible label, helper text, and shared required or validation state.
83
+
84
+ ```tsx
85
+ import { Field, Select } from '@cdx-ui/components';
86
+ import { accounts } from './_data';
87
+
88
+ export default function SelectWithFieldDemo() {
89
+ return (
90
+ <Field className="w-full" isRequired>
91
+ <Field.Label>Account type</Field.Label>
92
+ <Select items={accounts} isRequired>
93
+ <Select.Trigger>
94
+ <Select.Value placeholder="Select an account…" />
95
+ </Select.Trigger>
96
+ <Select.Content>
97
+ {accounts.map((account) => (
98
+ <Select.Item key={account.value} value={account.value}>
99
+ <Select.ItemLabel>{account.label}</Select.ItemLabel>
100
+ </Select.Item>
101
+ ))}
102
+ </Select.Content>
103
+ </Select>
104
+ <Field.Helper>Choose the account type to open.</Field.Helper>
105
+ </Field>
106
+ );
107
+ }
108
+ ```
109
+
110
+ ## Custom trigger
111
+
112
+ Set `asChild` on `Select.Trigger` to use a Forge pressable such as `Button`, `IconButton`, or `Chip`. Use the trigger and item `indicator` props to replace their default icons, or pass `null` to suppress an indicator.
113
+
114
+ ```tsx
115
+ import { Button, Icon, Select } from '@cdx-ui/components';
116
+ import { CheckCircle, KeyboardArrowDown } from '@cdx-ui/icons';
117
+ import { accounts } from './_data';
118
+
119
+ export default function SelectCustomTriggerDemo() {
120
+ return (
121
+ <Select items={accounts} accessibilityLabel="Filter accounts">
122
+ <Select.Trigger
123
+ asChild
124
+ indicator={<Button.Icon as={KeyboardArrowDown} position="trailing" />}
125
+ >
126
+ <Button variant="outline" color="action">
127
+ <Button.Label>Filter accounts</Button.Label>
128
+ </Button>
129
+ </Select.Trigger>
130
+ <Select.Content className="min-w-64">
131
+ {accounts.map((account) => (
132
+ <Select.Item
133
+ key={account.value}
134
+ value={account.value}
135
+ indicator={<Icon as={CheckCircle} color="action" className="size-5" />}
136
+ >
137
+ <Select.ItemLabel>{account.label}</Select.ItemLabel>
138
+ </Select.Item>
139
+ ))}
140
+ </Select.Content>
141
+ </Select>
142
+ );
143
+ }
144
+ ```
145
+
146
+ ## Placement
147
+
148
+ The default placement is `bottom-start`. Choose a side alone to center the overlay, or append `-start` or `-end` to align it with the corresponding edge of the trigger. The requested side is preferred rather than guaranteed: when the overlay would overflow the viewport it still flips to the opposite side and shifts along the cross axis.
149
+
150
+ ```tsx
151
+ import { Box, Select, Text, type SelectContentProps } from '@cdx-ui/components';
152
+ import { accounts } from './_data';
153
+
154
+ interface PlacementSelectProps {
155
+ placement: NonNullable<SelectContentProps['placement']>;
156
+ }
157
+
158
+ const PLACEMENT_GROUPS = [
159
+ {
160
+ label: 'Top',
161
+ placements: ['top-start', 'top', 'top-end'],
162
+ },
163
+ {
164
+ label: 'Right',
165
+ placements: ['right-start', 'right', 'right-end'],
166
+ },
167
+ {
168
+ label: 'Bottom',
169
+ placements: ['bottom-start', 'bottom', 'bottom-end'],
170
+ },
171
+ {
172
+ label: 'Left',
173
+ placements: ['left-start', 'left', 'left-end'],
174
+ },
175
+ ] as const satisfies readonly {
176
+ label: string;
177
+ placements: readonly PlacementSelectProps['placement'][];
178
+ }[];
179
+
180
+ function PlacementSelect({ placement }: PlacementSelectProps) {
181
+ return (
182
+ <Select
183
+ items={accounts}
184
+ className="min-w-0 flex-1"
185
+ accessibilityLabel={`${placement} account type`}
186
+ >
187
+ <Select.Trigger>
188
+ <Select.Value placeholder={placement} />
189
+ </Select.Trigger>
190
+ <Select.Content placement={placement}>
191
+ {accounts.map((account) => (
192
+ <Select.Item key={account.value} value={account.value}>
193
+ <Select.ItemLabel>{account.label}</Select.ItemLabel>
194
+ </Select.Item>
195
+ ))}
196
+ </Select.Content>
197
+ </Select>
198
+ );
199
+ }
200
+
201
+ export default function SelectPlacementDemo() {
202
+ return (
203
+ <Box className="w-full max-w-140 gap-6">
204
+ {PLACEMENT_GROUPS.map(({ label, placements }) => (
205
+ <Box key={label} className="w-full items-center gap-2">
206
+ <Text size="sm" color="secondary">
207
+ {label}
208
+ </Text>
209
+ <Box className="w-full flex-row gap-2">
210
+ {placements.map((placement) => (
211
+ <PlacementSelect key={placement} placement={placement} />
212
+ ))}
213
+ </Box>
214
+ </Box>
215
+ ))}
216
+ </Box>
217
+ );
218
+ }
219
+ ```
220
+
221
+ ## API
222
+
223
+ ### Select
224
+
225
+ The root manages the selected value, open state, options, and form-control state.
226
+
227
+ | Prop | Type | Default | Description |
228
+ | --- | --- | --- | --- |
229
+ | `className?` | `string` | — | — |
230
+ | `children?` | `ReactNode` | — | — |
231
+ | `variant?` | `'outline' \| null` | — | — |
232
+ | `size?` | `'default' \| 'small' \| null` | — | — |
233
+ | `items` | `SelectItemData[]` | — | Options used to resolve `Select.Value` labels and drive keyboard navigation. Render matching `Select.Item` elements inside `Select.Content`. |
234
+ | `value?` | `string` | — | — |
235
+ | `defaultValue?` | `string` | — | — |
236
+ | `onValueChange?` | `((value: string) => void)` | — | — |
237
+ | `isOpen?` | `boolean` | — | Controlled open state. Use `defaultOpen` for the uncontrolled variant. |
238
+ | `defaultOpen?` | `boolean` | — | — |
239
+ | `onOpenChange?` | `((open: boolean) => void)` | — | — |
240
+ | `disabled?` | `boolean` | — | — |
241
+ | `isRequired?` | `boolean` | — | — |
242
+ | `isInvalid?` | `boolean` | — | — |
243
+ | `isReadOnly?` | `boolean` | — | — |
244
+ | `native?` | `boolean` | — | Prevents `Select.Content` from rendering. Reserved for native picker integration. |
245
+
246
+ Inherits all [`ViewProps`](https://reactnative.dev/docs/view#props).
247
+
248
+ | Attribute | Values | Description |
249
+ | --------------- | ----------------- | --------------- |
250
+ | `data-slot` | `select` | Root container |
251
+ | `data-disabled` | `true` \| `false` | Disabled state |
252
+ | `data-required` | `true` \| `false` | Required state |
253
+ | `data-invalid` | `true` \| `false` | Invalid state |
254
+ | `data-readonly` | `true` \| `false` | Read-only state |
255
+
256
+ ## Sub-components
257
+
258
+ ### Select.Trigger
259
+
260
+ Pressable that opens and closes the dropdown. By default it appends a chevron that reflects the open state. The `indicator` prop replaces that chevron, and `indicator={null}` suppresses it. An `asChild` trigger keeps its existing children unless an indicator is explicitly provided.
261
+
262
+ | Prop | Type | Default | Description |
263
+ | --- | --- | --- | --- |
264
+ | `className?` | `string` | — | — |
265
+ | `children?` | `ReactNode` | — | Either children or a render prop that receives a boolean reflecting whether the component is currently pressed. |
266
+ | `asChild?` | `boolean` | — | Substitute the rendered trigger element with the single child. The child must be another Forge primitive (IconButton, Chip, Button) — anything whose underlying Pressable accepts Pressable-shaped handlers. When set, the auto-injected arrow indicator is skipped and `selectTriggerVariants` is not applied so the substituted component's own appearance wins. On web, `<label htmlFor>` association is preserved via a `focus()` bridge registered by the primitive. NOTE — `data-slot` overlap: the substituted component's own `data-slot` (e.g. `button`, `icon-button`, `chip`) may win on the merged element. Selectors that need guaranteed `select-trigger` access should wrap the trigger instead of relying on `data-slot`. |
267
+ | `indicator?` | `ReactNode` | — | Replaces the default open/closed chevron. Pass `null` to suppress the indicator. When `asChild` is set, only an explicitly provided indicator is appended to the substituted child. |
268
+ | `isHovered?` | `boolean` | — | — |
269
+ | `isActive?` | `boolean` | — | — |
270
+ | `isFocused?` | `boolean` | — | — |
271
+ | `isFocusVisible?` | `boolean` | — | — |
272
+ | `isDisabled?` | `boolean` | — | — |
273
+ | `aria-describedby?` | `string` | — | — |
274
+
275
+ Inherits all [`PressableProps`](https://reactnative.dev/docs/pressable#props).
276
+
277
+ | Attribute | Values | Description |
278
+ | ------------------ | ----------------------------------------------------- | ---------------------------------------------------------------------------------------- |
279
+ | `data-slot` | `select-trigger` | Trigger element; a substituted child's own slot may take precedence when using `asChild` |
280
+ | `data-state` | `open` \| `closed` | Dropdown state |
281
+ | `data-focus` | `true` \| `false` | Focus state |
282
+ | `data-active` | `true` \| `false` | Pressed state |
283
+ | `data-disabled` | `true` \| `false` | Disabled state |
284
+ | `data-required` | `true` \| `false` | Required state |
285
+ | `data-invalid` | `true` \| `false` | Invalid state |
286
+ | `data-readonly` | `true` \| `false` | Read-only state |
287
+ | `data-field-state` | `idle` \| `invalid` \| `focused` \| `focused-invalid` | Combined focus and validation state |
288
+
289
+ ### Select.Value
290
+
291
+ Displays the label from `items` that matches the selected value, or the placeholder when no value is selected.
292
+
293
+ | Prop | Type | Default | Description |
294
+ | --- | --- | --- | --- |
295
+ | `className?` | `string` | — | — |
296
+ | `children?` | `ReactNode` | — | — |
297
+ | `asChild?` | `boolean` | — | — |
298
+ | `placeholder?` | `string` | — | — |
299
+
300
+ Inherits all [`TextProps`](https://reactnative.dev/docs/text#props).
301
+
302
+ On web viewports below 600px, value text renders at 16px (`text-base`) for both sizes so it matches
303
+ `Input` and `Textarea` and iOS Safari does not auto-zoom the page when the trigger is focused. At
304
+ 600px and above, web matches native sizes (14px default, 12px small).
305
+
306
+ ### Select.Icon
307
+
308
+ Renders a Forge icon sized to the select. Use it as the `indicator` value when a replacement should retain Select icon sizing and state styling.
309
+
310
+ | Prop | Type | Default | Description |
311
+ | --- | --- | --- | --- |
312
+ | `className?` | `string` | — | Utility classes for size (`size-*`) and placement, merged after the defaults. |
313
+ | `filled?` | `boolean` | — | When `true`, renders the filled variant of the icon (if available). Defaults to `false` (outlined). |
314
+ | `size?` | `string \| number` | — | Width and height of the icon, in pixels or any SVG length. The bare icon renders at 24 when omitted. |
315
+ | `data-testid?` | `string` | — | Test identifier forwarded to the rendered SVG. |
316
+ | `as?` | `ForgeIcon` | — | Forge icon component to render. Required unless `asChild` is set, in which case the consumer's substituted element is rendered instead. |
317
+ | `asChild?` | `boolean` | — | — |
318
+ | `position?` | `'leading' \| 'trailing'` | — | Position hint that emits `data-icon="inline-start"` or `data-icon="inline-end"`. CVA uses this attribute to apply leading/trailing padding logically (RTL-aware). Omit to apply the symmetric baseline gap. |
319
+
320
+ Inherits all `ForgeIconProps` (react-native-svg `SvgProps` plus `filled` and `size`).
321
+
322
+ ### Select.Content
323
+
324
+ Dropdown panel that positions itself around the trigger, scrolls long option lists, and closes when dismissed.
325
+
326
+ | Prop | Type | Default | Description |
327
+ | --- | --- | --- | --- |
328
+ | `className?` | `string` | — | — |
329
+ | `children?` | `ReactNode` | — | — |
330
+ | `asChild?` | `boolean` | — | — |
331
+ | `placement?` | `'auto' \| 'auto-start' \| 'auto-end' \| 'top' \| 'top-start' \| 'top-end' \| 'right' \| 'right-start' \| 'right-end' \| 'bottom' \| 'bottom-start' \| 'bottom-end' \| 'left' \| 'left-start' \| 'left-end'` | — | Preferred dropdown placement relative to the trigger. Defaults to `bottom-start`. |
332
+ | `offset?` | `number` | — | Distance between the trigger and dropdown in pixels. Defaults to 4. |
333
+ | `crossOffset?` | `number` | — | Cross-axis adjustment from the aligned dropdown position in pixels. Defaults to 0. |
334
+ | `enteringAnimation?` | `EntryOrExitLayoutType` | — | Enter animation for the dropdown. Defaults to a 150ms fade. |
335
+ | `exitingAnimation?` | `EntryOrExitLayoutType` | — | Exit animation for the dropdown. Defaults to a 100ms fade. |
336
+
337
+ Inherits all [`ViewProps`](https://reactnative.dev/docs/view#props).
338
+
339
+ ### Select.Item
340
+
341
+ An individual option. Its `value` must match an entry in the root `items` array. The `indicator` prop replaces the default selected-item checkmark, and `indicator={null}` suppresses it.
342
+
343
+ | Prop | Type | Default | Description |
344
+ | --- | --- | --- | --- |
345
+ | `className?` | `string` | — | — |
346
+ | `children?` | `ReactNode` | — | Either children or a render prop that receives a boolean reflecting whether the component is currently pressed. |
347
+ | `disabled?` | `boolean` | — | Whether the press behavior is disabled. |
348
+ | `asChild?` | `boolean` | — | — |
349
+ | `indicator?` | `ReactNode` | — | Replaces the default selected-item checkmark. The replacement is rendered only while the item is selected. Pass `null` to suppress the indicator. |
350
+ | `value` | `string` | — | — |
351
+ | `isHovered?` | `boolean` | — | — |
352
+ | `isActive?` | `boolean` | — | — |
353
+ | `isFocused?` | `boolean` | — | — |
354
+ | `isFocusVisible?` | `boolean` | — | — |
355
+ | `isDisabled?` | `boolean` | — | — |
356
+
357
+ Inherits all [`PressableProps`](https://reactnative.dev/docs/pressable#props).
358
+
359
+ | Attribute | Values | Description |
360
+ | -------------------- | ------------------------ | ---------------------------------- |
361
+ | `data-slot` | `select-item` | Option element |
362
+ | `data-state` | `checked` \| `unchecked` | Selection state |
363
+ | `data-highlighted` | `true` \| `false` | Current keyboard-navigation target |
364
+ | `data-hover` | `true` \| `false` | Hover state |
365
+ | `data-focus` | `true` \| `false` | Focus state |
366
+ | `data-active` | `true` \| `false` | Pressed state |
367
+ | `data-disabled` | `true` \| `false` | Disabled state |
368
+ | `data-focus-visible` | `true` \| `false` | Keyboard focus ring |
369
+
370
+ ### Select.ItemLabel
371
+
372
+ Text label rendered inside a `Select.Item`.
373
+
374
+ | Prop | Type | Default | Description |
375
+ | --- | --- | --- | --- |
376
+ | `className?` | `string` | — | — |
377
+ | `children?` | `ReactNode` | — | — |
378
+ | `asChild?` | `boolean` | — | — |
379
+
380
+ Inherits all [`TextProps`](https://reactnative.dev/docs/text#props).
381
+
382
+ ## Anatomy
383
+
384
+ | `data-slot` | Element |
385
+ | ------------------- | ----------------------------- |
386
+ | `select` | Root container |
387
+ | `select-trigger` | Dropdown trigger |
388
+ | `select-value` | Selected label or placeholder |
389
+ | `select-icon` | Dropdown indicator |
390
+ | `select-content` | Dropdown panel |
391
+ | `select-item` | Option |
392
+ | `select-item-label` | Option label |
393
+
394
+ ## Accessibility
395
+
396
+ The trigger uses `role="combobox"` with `aria-haspopup="listbox"`, reflects open, required, invalid, and read-only state, and points to the dropdown content. The content uses `role="listbox"` and each item uses `role="option"` with selected and disabled state.
397
+
398
+ Compose with `Field.Label` for a visible associated label. When no visible label is present, provide `accessibilityLabel` on `Select`.
399
+
400
+ On web, Enter, Space, Arrow Down, or Arrow Up opens the dropdown. Arrow keys, Home, and End move through enabled options; Enter or Space selects; Escape closes; and Tab closes while continuing focus navigation.