@ui-sdk/vue 0.0.0-stage → 0.1.0-beta.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 (262) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +10 -2
  3. package/dist/accordion.d.ts +561 -0
  4. package/dist/accordion.js +24 -0
  5. package/dist/agent.d.ts +2 -0
  6. package/dist/agent.js +7 -0
  7. package/dist/alert-dialog.d.ts +231 -0
  8. package/dist/alert-dialog.js +33 -0
  9. package/dist/async-options.d.ts +18 -0
  10. package/dist/async-options.js +40 -0
  11. package/dist/autocomplete.d.ts +384 -0
  12. package/dist/autocomplete.js +344 -0
  13. package/dist/avatar.d.ts +40 -0
  14. package/dist/avatar.js +28 -0
  15. package/dist/button.d.ts +52 -0
  16. package/dist/button.js +43 -0
  17. package/dist/calendar-context.d.ts +10 -0
  18. package/dist/calendar-context.js +7 -0
  19. package/dist/calendar.d.ts +311 -0
  20. package/dist/calendar.js +349 -0
  21. package/dist/carousel.d.ts +236 -0
  22. package/dist/carousel.js +204 -0
  23. package/dist/checkbox.d.ts +211 -0
  24. package/dist/checkbox.js +151 -0
  25. package/dist/children.d.ts +41 -0
  26. package/dist/children.js +135 -0
  27. package/dist/collapsible.d.ts +54 -0
  28. package/dist/collapsible.js +18 -0
  29. package/dist/color-picker.d.ts +187 -0
  30. package/dist/color-picker.js +158 -0
  31. package/dist/color.d.ts +2 -0
  32. package/dist/color.js +21 -0
  33. package/dist/combobox.d.ts +517 -0
  34. package/dist/combobox.js +531 -0
  35. package/dist/command.d.ts +322 -0
  36. package/dist/command.js +345 -0
  37. package/dist/controller-target.d.ts +49 -0
  38. package/dist/controller-target.js +112 -0
  39. package/dist/create-parts.d.ts +126 -0
  40. package/dist/create-parts.js +206 -0
  41. package/dist/date-input.d.ts +133 -0
  42. package/dist/date-input.js +31 -0
  43. package/dist/date-picker.d.ts +328 -0
  44. package/dist/date-picker.js +354 -0
  45. package/dist/date.d.ts +2 -0
  46. package/dist/date.js +59 -0
  47. package/dist/dialog-parts.d.ts +259 -0
  48. package/dist/dialog-parts.js +300 -0
  49. package/dist/dialog.d.ts +251 -0
  50. package/dist/dialog.js +25 -0
  51. package/dist/drawer.d.ts +304 -0
  52. package/dist/drawer.js +373 -0
  53. package/dist/editor-context.d.ts +4 -0
  54. package/dist/editor-context.js +5 -0
  55. package/dist/field-context.d.ts +26 -0
  56. package/dist/field-context.js +44 -0
  57. package/dist/field.d.ts +154 -0
  58. package/dist/field.js +240 -0
  59. package/dist/file-input.d.ts +212 -0
  60. package/dist/file-input.js +255 -0
  61. package/dist/form-inputs.d.ts +16 -0
  62. package/dist/form-inputs.js +25 -0
  63. package/dist/gesture.d.ts +1 -0
  64. package/dist/gesture.js +15 -0
  65. package/dist/history.d.ts +2 -0
  66. package/dist/history.js +11 -0
  67. package/dist/hover-card.d.ts +137 -0
  68. package/dist/hover-card.js +153 -0
  69. package/dist/image-cropper.d.ts +74 -0
  70. package/dist/image-cropper.js +36 -0
  71. package/dist/image.d.ts +2 -0
  72. package/dist/image.js +5 -0
  73. package/dist/index.d.ts +59 -0
  74. package/dist/index.js +60 -0
  75. package/dist/input.d.ts +120 -0
  76. package/dist/input.js +73 -0
  77. package/dist/item-icon.d.ts +34 -0
  78. package/dist/item-icon.js +63 -0
  79. package/dist/label.d.ts +16 -0
  80. package/dist/label.js +8 -0
  81. package/dist/lifecycle.d.ts +11 -0
  82. package/dist/lifecycle.js +91 -0
  83. package/dist/list-items.d.ts +38 -0
  84. package/dist/list-items.js +59 -0
  85. package/dist/list-text.d.ts +7 -0
  86. package/dist/list-text.js +15 -0
  87. package/dist/listbox.d.ts +273 -0
  88. package/dist/listbox.js +126 -0
  89. package/dist/menu.d.ts +678 -0
  90. package/dist/menu.js +607 -0
  91. package/dist/menubar-context.d.ts +5 -0
  92. package/dist/menubar-context.js +5 -0
  93. package/dist/menubar.d.ts +61 -0
  94. package/dist/menubar.js +48 -0
  95. package/dist/meter.d.ts +41 -0
  96. package/dist/meter.js +37 -0
  97. package/dist/navigation-menu.d.ts +228 -0
  98. package/dist/navigation-menu.js +195 -0
  99. package/dist/number-input.d.ts +176 -0
  100. package/dist/number-input.js +231 -0
  101. package/dist/number.d.ts +2 -0
  102. package/dist/number.js +39 -0
  103. package/dist/nuxt/entries.json +977 -0
  104. package/dist/nuxt/module.d.ts +18 -0
  105. package/dist/nuxt/module.js +31 -0
  106. package/dist/nuxt/registrations.d.ts +22 -0
  107. package/dist/nuxt/registrations.js +22 -0
  108. package/dist/observe.d.ts +2 -0
  109. package/dist/observe.js +7 -0
  110. package/dist/otp-input.d.ts +142 -0
  111. package/dist/otp-input.js +130 -0
  112. package/dist/overlays.d.ts +40 -0
  113. package/dist/overlays.js +118 -0
  114. package/dist/pagination.d.ts +201 -0
  115. package/dist/pagination.js +146 -0
  116. package/dist/password-input.d.ts +144 -0
  117. package/dist/password-input.js +134 -0
  118. package/dist/popover.d.ts +211 -0
  119. package/dist/popover.js +258 -0
  120. package/dist/popup.d.ts +60 -0
  121. package/dist/popup.js +89 -0
  122. package/dist/preferences.d.ts +44 -0
  123. package/dist/preferences.js +102 -0
  124. package/dist/primitive.d.ts +2 -0
  125. package/dist/primitive.js +11 -0
  126. package/dist/progress.d.ts +41 -0
  127. package/dist/progress.js +37 -0
  128. package/dist/radio.d.ts +164 -0
  129. package/dist/radio.js +145 -0
  130. package/dist/rating.d.ts +149 -0
  131. package/dist/rating.js +146 -0
  132. package/dist/renderless.d.ts +4 -0
  133. package/dist/renderless.js +28 -0
  134. package/dist/resizable.d.ts +54 -0
  135. package/dist/resizable.js +22 -0
  136. package/dist/scroll-area.d.ts +72 -0
  137. package/dist/scroll-area.js +77 -0
  138. package/dist/segmented-control.d.ts +151 -0
  139. package/dist/segmented-control.js +110 -0
  140. package/dist/segmented-input.d.ts +76 -0
  141. package/dist/segmented-input.js +115 -0
  142. package/dist/select.d.ts +458 -0
  143. package/dist/select.js +468 -0
  144. package/dist/separator.d.ts +14 -0
  145. package/dist/separator.js +8 -0
  146. package/dist/shortcuts.d.ts +32 -0
  147. package/dist/shortcuts.js +56 -0
  148. package/dist/slider.d.ts +65 -0
  149. package/dist/slider.js +194 -0
  150. package/dist/switch.d.ts +103 -0
  151. package/dist/switch.js +71 -0
  152. package/dist/tabs.d.ts +338 -0
  153. package/dist/tabs.js +27 -0
  154. package/dist/text-control.d.ts +89 -0
  155. package/dist/text-control.js +51 -0
  156. package/dist/text-match.d.ts +2 -0
  157. package/dist/text-match.js +7 -0
  158. package/dist/textarea.d.ts +106 -0
  159. package/dist/textarea.js +68 -0
  160. package/dist/time-input.d.ts +131 -0
  161. package/dist/time-input.js +32 -0
  162. package/dist/timeline.d.ts +81 -0
  163. package/dist/timeline.js +30 -0
  164. package/dist/toast-parts.d.ts +203 -0
  165. package/dist/toast-parts.js +270 -0
  166. package/dist/toast.d.ts +2 -0
  167. package/dist/toast.js +21 -0
  168. package/dist/toggle-props.d.ts +22 -0
  169. package/dist/toggle-props.js +14 -0
  170. package/dist/toggle.d.ts +199 -0
  171. package/dist/toggle.js +129 -0
  172. package/dist/token-input.d.ts +136 -0
  173. package/dist/token-input.js +214 -0
  174. package/dist/toolbar-context.d.ts +7 -0
  175. package/dist/toolbar-context.js +20 -0
  176. package/dist/toolbar.d.ts +120 -0
  177. package/dist/toolbar.js +77 -0
  178. package/dist/tooltip.d.ts +160 -0
  179. package/dist/tooltip.js +224 -0
  180. package/dist/tree.d.ts +300 -0
  181. package/dist/tree.js +209 -0
  182. package/dist/trigger-menu.d.ts +452 -0
  183. package/dist/trigger-menu.js +254 -0
  184. package/dist/use-primitive.d.ts +132 -0
  185. package/dist/use-primitive.js +462 -0
  186. package/dist/waterfall.d.ts +201 -0
  187. package/dist/waterfall.js +171 -0
  188. package/docs/README.md +89 -0
  189. package/docs/agents.md +1523 -0
  190. package/docs/components/accordion.md +1369 -0
  191. package/docs/components/alert-dialog.md +799 -0
  192. package/docs/components/autocomplete.md +1857 -0
  193. package/docs/components/avatar.md +272 -0
  194. package/docs/components/button.md +435 -0
  195. package/docs/components/calendar.md +1589 -0
  196. package/docs/components/carousel.md +1171 -0
  197. package/docs/components/checkbox.md +1008 -0
  198. package/docs/components/collapsible.md +536 -0
  199. package/docs/components/color-picker.md +706 -0
  200. package/docs/components/combobox.md +3504 -0
  201. package/docs/components/command.md +1481 -0
  202. package/docs/components/date-input.md +536 -0
  203. package/docs/components/date-picker.md +1244 -0
  204. package/docs/components/dialog.md +1397 -0
  205. package/docs/components/drawer.md +1166 -0
  206. package/docs/components/field.md +538 -0
  207. package/docs/components/file-input.md +611 -0
  208. package/docs/components/hover-card.md +505 -0
  209. package/docs/components/image-cropper.md +556 -0
  210. package/docs/components/input.md +306 -0
  211. package/docs/components/label.md +203 -0
  212. package/docs/components/listbox.md +1004 -0
  213. package/docs/components/menu.md +1229 -0
  214. package/docs/components/menubar.md +443 -0
  215. package/docs/components/meter.md +315 -0
  216. package/docs/components/navigation-menu.md +1523 -0
  217. package/docs/components/number-input.md +1029 -0
  218. package/docs/components/otp-input.md +512 -0
  219. package/docs/components/pagination.md +613 -0
  220. package/docs/components/password-input.md +649 -0
  221. package/docs/components/popover.md +1448 -0
  222. package/docs/components/preferences.md +208 -0
  223. package/docs/components/progress.md +423 -0
  224. package/docs/components/radio.md +785 -0
  225. package/docs/components/rating.md +416 -0
  226. package/docs/components/resizable.md +910 -0
  227. package/docs/components/scroll-area.md +915 -0
  228. package/docs/components/segmented-control.md +435 -0
  229. package/docs/components/select.md +2015 -0
  230. package/docs/components/separator.md +229 -0
  231. package/docs/components/slider.md +1084 -0
  232. package/docs/components/switch.md +565 -0
  233. package/docs/components/tabs.md +944 -0
  234. package/docs/components/textarea.md +299 -0
  235. package/docs/components/time-input.md +224 -0
  236. package/docs/components/timeline.md +654 -0
  237. package/docs/components/toast.md +1224 -0
  238. package/docs/components/toggle.md +500 -0
  239. package/docs/components/token-input.md +953 -0
  240. package/docs/components/toolbar.md +739 -0
  241. package/docs/components/tooltip.md +818 -0
  242. package/docs/components/tree.md +1306 -0
  243. package/docs/components/trigger-menu.md +1223 -0
  244. package/docs/components/waterfall.md +717 -0
  245. package/docs/controllers.md +71 -0
  246. package/docs/core.md +8027 -0
  247. package/docs/custom-parts.md +89 -0
  248. package/docs/first-component.md +83 -0
  249. package/docs/forms.md +122 -0
  250. package/docs/installation.md +72 -0
  251. package/docs/introduction.md +32 -0
  252. package/docs/lists-and-menus.md +122 -0
  253. package/docs/llms.txt +1502 -0
  254. package/docs/parts-and-composition.md +85 -0
  255. package/docs/popups.md +108 -0
  256. package/docs/rendering.md +56 -0
  257. package/docs/server-rendering.md +180 -0
  258. package/docs/state-and-events.md +100 -0
  259. package/docs/styling-and-animation.md +148 -0
  260. package/docs/utilities/async-options.md +348 -0
  261. package/docs/utilities/shortcuts.md +159 -0
  262. package/package.json +352 -3
@@ -0,0 +1,435 @@
1
+ # Segmented Control
2
+
3
+ Pick one of a few options from a row of buttons, with an indicator that slides to the choice.
4
+
5
+ Use a segmented control to switch between a few views or settings, like List and Grid. It works like a radio group, with one value and a single tab stop, and its indicator slides to the checked item.
6
+
7
+ ```vue title="basic.vue"
8
+ <script setup lang="ts">
9
+ import { Field, FieldLabel } from "@ui-sdk/vue/field"
10
+ import {
11
+ SegmentedControl,
12
+ SegmentedControlIndicator,
13
+ SegmentedControlItem,
14
+ } from "@ui-sdk/vue/segmented-control"
15
+ </script>
16
+
17
+ <template>
18
+ <Field>
19
+ <FieldLabel>View</FieldLabel>
20
+ <SegmentedControl default-value="list">
21
+ <SegmentedControlIndicator />
22
+ <SegmentedControlItem value="list">List</SegmentedControlItem>
23
+ <SegmentedControlItem value="grid">Grid</SegmentedControlItem>
24
+ <SegmentedControlItem value="board">Board</SegmentedControlItem>
25
+ </SegmentedControl>
26
+ </Field>
27
+ </template>
28
+ ```
29
+
30
+ Choose Grid, then use the arrow keys to move between views. The label above names the control, as in any field.
31
+
32
+ ## Installation
33
+
34
+ ```bash
35
+ npm install @ui-sdk/vue @ui-sdk/core
36
+ ```
37
+
38
+ ## Composition
39
+
40
+ Use the following composition to build a `SegmentedControl`:
41
+
42
+ ```text
43
+ SegmentedControl
44
+ ├── SegmentedControlIndicator
45
+ ├── SegmentedControlItem
46
+ └── SegmentedControlItem
47
+ ```
48
+
49
+ Give each item a `value`. The control's value is the checked item's value, and the indicator sits behind that item.
50
+
51
+ ## Usage
52
+
53
+ Import the parts and put them together:
54
+
55
+ ```vue
56
+ <script setup lang="ts">
57
+ import {
58
+ SegmentedControl,
59
+ SegmentedControlIndicator,
60
+ SegmentedControlItem,
61
+ } from "@ui-sdk/vue/segmented-control"
62
+ </script>
63
+
64
+ <template>
65
+ <SegmentedControl aria-label="View" default-value="list">
66
+ <SegmentedControlIndicator />
67
+ <SegmentedControlItem value="list">List</SegmentedControlItem>
68
+ <SegmentedControlItem value="grid">Grid</SegmentedControlItem>
69
+ </SegmentedControl>
70
+ </template>
71
+ ```
72
+
73
+ Name the control with `aria-label`, or put it in a [Field](./field.md) whose label names it. Without `default-value`, nothing starts checked and Tab enters on the first enabled item.
74
+
75
+ ## Indicator
76
+
77
+ `SegmentedControlIndicator` measures the checked item. Position it inside the control and use its [CSS variables](#styling-indicator) to draw the background that slides to the choice. It measures again when an item or the control changes size, and has no box while nothing is checked.
78
+
79
+ ```css
80
+ [data-slot="segmented-control"] {
81
+ position: relative;
82
+ display: inline-flex;
83
+ isolation: isolate;
84
+ }
85
+
86
+ [data-slot="segmented-control-indicator"] {
87
+ position: absolute;
88
+ z-index: -1;
89
+ top: var(--segmented-control-indicator-top);
90
+ left: var(--segmented-control-indicator-left);
91
+ width: var(--segmented-control-indicator-width);
92
+ height: var(--segmented-control-indicator-height);
93
+ transition-property: top, left, width, height;
94
+ transition-duration: 200ms;
95
+ }
96
+ ```
97
+
98
+ ## Vertical
99
+
100
+ Set `orientation="vertical"` to stack the items. Arrow keys on both axes still move between them, as in any radio group, and `data-orientation` on every part lets one stylesheet lay out both directions.
101
+
102
+ ```vue title="vertical.vue"
103
+ <script setup lang="ts">
104
+ import { Field, FieldLabel } from "@ui-sdk/vue/field"
105
+ import {
106
+ SegmentedControl,
107
+ SegmentedControlIndicator,
108
+ SegmentedControlItem,
109
+ } from "@ui-sdk/vue/segmented-control"
110
+ </script>
111
+
112
+ <template>
113
+ <Field>
114
+ <FieldLabel>Vertical alignment</FieldLabel>
115
+ <SegmentedControl orientation="vertical" default-value="middle">
116
+ <SegmentedControlIndicator />
117
+ <SegmentedControlItem value="top">Top</SegmentedControlItem>
118
+ <SegmentedControlItem value="middle">Middle</SegmentedControlItem>
119
+ <SegmentedControlItem value="bottom">Bottom</SegmentedControlItem>
120
+ </SegmentedControl>
121
+ </Field>
122
+ </template>
123
+ ```
124
+
125
+ Choose Bottom, then press the Up arrow. The indicator follows down the column.
126
+
127
+ ## Disabled
128
+
129
+ Set `disabled` on the control or on one item. Disabled items can't be focused or checked, and arrow keys skip them. A disabled control submits nothing.
130
+
131
+ ```vue title="disabled.vue"
132
+ <script setup lang="ts">
133
+ import {
134
+ SegmentedControl,
135
+ SegmentedControlIndicator,
136
+ SegmentedControlItem,
137
+ } from "@ui-sdk/vue/segmented-control"
138
+ </script>
139
+
140
+ <template>
141
+ <div data-slot="stack">
142
+ <SegmentedControl aria-label="Theme" default-value="system" disabled>
143
+ <SegmentedControlIndicator />
144
+ <SegmentedControlItem value="light">Light</SegmentedControlItem>
145
+ <SegmentedControlItem value="dark">Dark</SegmentedControlItem>
146
+ <SegmentedControlItem value="system">System</SegmentedControlItem>
147
+ </SegmentedControl>
148
+ <SegmentedControl aria-label="Billing period" default-value="monthly">
149
+ <SegmentedControlIndicator />
150
+ <SegmentedControlItem value="monthly">Monthly</SegmentedControlItem>
151
+ <SegmentedControlItem value="yearly">Yearly</SegmentedControlItem>
152
+ <SegmentedControlItem value="lifetime" disabled
153
+ >Lifetime</SegmentedControlItem
154
+ >
155
+ </SegmentedControl>
156
+ </div>
157
+ </template>
158
+ ```
159
+
160
+ The Theme control is disabled. In Billing period, Lifetime is disabled, so the arrow keys move between Monthly and Yearly.
161
+
162
+ ## Controlled
163
+
164
+ Bind `v-model:value`, and listen to `@value-change` for the reason. Call `details.cancel()` in the listener to keep the current value. A [controller](#controller) from `createSegmentedControlController()` checks an item from anywhere, like the Show Month button below. Create one controller per mounted control.
165
+
166
+ ```vue title="controlled.vue"
167
+ <script setup lang="ts">
168
+ import {
169
+ createSegmentedControlController,
170
+ SegmentedControl,
171
+ SegmentedControlIndicator,
172
+ SegmentedControlItem,
173
+ } from "@ui-sdk/vue/segmented-control"
174
+ import { ref } from "vue"
175
+ import { useLog } from "./log"
176
+
177
+ const log = useLog()
178
+ const range = createSegmentedControlController()
179
+ const value = ref<string | null>("week")
180
+ </script>
181
+
182
+ <template>
183
+ <div>
184
+ <div data-slot="controls">
185
+ <button type="button" @click="range.setValue('month')">Show Month</button>
186
+ </div>
187
+ <SegmentedControl
188
+ v-model:value="value"
189
+ aria-label="Range"
190
+ :controller="range"
191
+ @value-change="
192
+ (next, details) => log(`value: ${next} (${details.reason})`)
193
+ "
194
+ >
195
+ <SegmentedControlIndicator />
196
+ <SegmentedControlItem value="day">Day</SegmentedControlItem>
197
+ <SegmentedControlItem value="week">Week</SegmentedControlItem>
198
+ <SegmentedControlItem value="month">Month</SegmentedControlItem>
199
+ </SegmentedControl>
200
+ </div>
201
+ </template>
202
+ ```
203
+
204
+ Choose Day, then press Show Month. The console logs each change and its reason.
205
+
206
+ See [State and Events](../state-and-events.md) for controlled state, cancellation and debugging.
207
+
208
+ ## Form
209
+
210
+ Each item renders a hidden native radio, so the control takes part in forms. Set `name` to submit the checked value; nothing submits without a name or a checked item. `required` blocks submission until there's a choice, `form` joins a form by its id from elsewhere on the page, and a reset returns to `default-value`.
211
+
212
+ ```vue title="form.vue"
213
+ <script setup lang="ts">
214
+ import { Field, FieldDescription, FieldLabel } from "@ui-sdk/vue/field"
215
+ import {
216
+ SegmentedControl,
217
+ SegmentedControlIndicator,
218
+ SegmentedControlItem,
219
+ } from "@ui-sdk/vue/segmented-control"
220
+ import { useLog } from "./log"
221
+
222
+ const log = useLog()
223
+
224
+ function submit(event: Event) {
225
+ const data = new FormData(event.currentTarget as HTMLFormElement)
226
+ log(`Saved theme=${data.get("theme")}, density=${data.get("density")}`)
227
+ }
228
+ </script>
229
+
230
+ <template>
231
+ <form data-slot="stack" @submit.prevent="submit">
232
+ <Field>
233
+ <FieldLabel>Theme</FieldLabel>
234
+ <SegmentedControl name="theme" required default-value="system">
235
+ <SegmentedControlIndicator />
236
+ <SegmentedControlItem value="light">Light</SegmentedControlItem>
237
+ <SegmentedControlItem value="dark">Dark</SegmentedControlItem>
238
+ <SegmentedControlItem value="system">System</SegmentedControlItem>
239
+ </SegmentedControl>
240
+ <FieldDescription>System follows your device's setting.</FieldDescription>
241
+ </Field>
242
+ <Field>
243
+ <FieldLabel>Density</FieldLabel>
244
+ <SegmentedControl name="density" default-value="comfortable">
245
+ <SegmentedControlIndicator />
246
+ <SegmentedControlItem value="compact">Compact</SegmentedControlItem>
247
+ <SegmentedControlItem value="comfortable"
248
+ >Comfortable</SegmentedControlItem
249
+ >
250
+ </SegmentedControl>
251
+ </Field>
252
+ <div data-slot="row">
253
+ <button type="submit" data-variant="solid">Save</button>
254
+ <button type="reset">Reset</button>
255
+ </div>
256
+ </form>
257
+ </template>
258
+ ```
259
+
260
+ Choose Dark and Compact, then press Save. The console logs both values, and Reset returns each control to its default.
261
+
262
+ ## Read-Only
263
+
264
+ Set `readOnly` to keep the value while still letting people focus the items. Arrow keys move focus without checking anything, and the value still submits with a form.
265
+
266
+ ## Loop
267
+
268
+ Arrow keys wrap from the last item to the first. Set `loop` to `false` to stop at the ends.
269
+
270
+ ```vue
271
+ <SegmentedControl aria-label="View" default-value="list" :loop="false">
272
+ ...
273
+ </SegmentedControl>
274
+ ```
275
+
276
+ ## Styling
277
+
278
+ Parts are unstyled. Use `data-checked` and `data-unchecked` for the checked item, and `data-disabled`, `data-readonly` and `data-required` for the rest. `data-orientation` is on the control and the indicator.
279
+
280
+ Each part has a `data-slot`, such as `segmented-control-item`, and its state attributes are listed under [Data Attributes](#data-attributes).
281
+
282
+ ### SegmentedControlIndicator
283
+ - `--segmented-control-indicator-left`: Distance from the control's left edge to the checked item.
284
+ - `--segmented-control-indicator-right`: Distance from the control's right edge to the checked item.
285
+ - `--segmented-control-indicator-top`: Distance from the control's top edge to the checked item.
286
+ - `--segmented-control-indicator-bottom`: Distance from the control's bottom edge to the checked item.
287
+ - `--segmented-control-indicator-width`: Width of the checked item.
288
+ - `--segmented-control-indicator-height`: Height of the checked item.
289
+
290
+ ## Data Attributes
291
+
292
+ ### SegmentedControl
293
+ `[data-slot="segmented-control"]`
294
+
295
+ - `data-orientation` (`horizontal`, `vertical`): Which way the items run.
296
+ - `data-readonly`: Present when the control is read-only.
297
+ - `data-required`: Present when the control is required.
298
+ - `data-disabled`: Present when disabled, by its own prop, the control or a `<fieldset>`.
299
+
300
+ ### SegmentedControlItem
301
+ `[data-slot="segmented-control-item"]`
302
+
303
+ - `data-checked`: Present on the checked item.
304
+ - `data-readonly`: Present when the control is read-only.
305
+ - `data-required`: Present when the control is required.
306
+ - `data-unchecked`: Present on items that aren't checked.
307
+ - `data-disabled`: Present when disabled, by its own prop, the control or a `<fieldset>`.
308
+
309
+ ### SegmentedControlIndicator
310
+ `[data-slot="segmented-control-indicator"]`
311
+
312
+ - `data-orientation` (`horizontal`, `vertical`): Which way the items run.
313
+
314
+ ## API Reference
315
+
316
+ Props are the same as below. `onValueChange` is `v-model:value`, or `@value-change` for the reason.
317
+
318
+ ### SegmentedControl
319
+
320
+ - `value?` (`T | null`): The checked item's value. Pass it to control the segmented control. `null` checks nothing.
321
+ - `defaultValue?` (`T | null`): The item checked at first, when uncontrolled. A form reset returns to it.
322
+ - `@value-change` (`((value: T | null, details: ChangeDetails<SegmentedControlValueChangeReason>) => void)`): Runs when a user or a form reset wants to change the value. `details.reason` says why, and `details.cancel()` stops it.
323
+ - `disabled?` (`boolean`): Disables every item: they can't be focused or changed, and the value isn't submitted.
324
+ - `readOnly?` (`boolean`): Keeps the items focusable and the value submitted, but stops changes.
325
+ - `required?` (`boolean`): Blocks form submission until an item is checked, with the browser's own message.
326
+ - `name?` (`string`): The name the value submits under. Without one, the segmented control submits nothing.
327
+ - `form?` (`string`): The id of a form to belong to, when the segmented control isn't inside that form.
328
+ - `loop?` (`boolean`): Whether arrow keys wrap from the last item to the first.
329
+ - `orientation?` (`SegmentedControlOrientation`): Which way the items run, for styling and for screen readers. Arrow keys on both axes move between items either way, as in any radio group.
330
+ - `controller?` (`{ getValue: () => T | null; setValue: (value: T | null, details?: SegmentedControlChangeOptions) => boolean; focus: () => void; }`): Checks items from anywhere. Create one with `createSegmentedControlController()`.
331
+ - `debug?` (`boolean`): Logs every state change with its reason to the console, in development builds.
332
+
333
+ ### SegmentedControlItem
334
+
335
+ - `value` (`string`): What the value becomes when this item is checked.
336
+ - `disabled?` (`boolean`): Skips the item, like a disabled native radio: arrow keys pass it and it can't be focused or checked.
337
+
338
+ ### State
339
+
340
+ What render functions, state-based class names and slots receive.
341
+
342
+ - `value` (`string | null`): The checked item's value.
343
+ - `disabled` (`boolean`): Whether the segmented control is disabled, by itself or by a `<fieldset>`.
344
+ - `readOnly` (`boolean`): Whether it's read-only: arrow keys still move focus, but nothing changes the checked item.
345
+ - `required` (`boolean`): Whether a form won't submit until an item is checked.
346
+ - `orientation` (`SegmentedControlOrientation`): Which way the items run.
347
+
348
+ - `checked` (`boolean`): Whether this item is the control's value, which the indicator sits behind.
349
+ - `disabled` (`boolean`): Whether this item is disabled, by itself, the segmented control or a `<fieldset>`.
350
+ - `readOnly` (`boolean`): Whether the control is read-only, so pressing this item changes nothing.
351
+ - `required` (`boolean`): Whether a form won't submit until an item is checked.
352
+
353
+ - `value` (`string | null`): The checked item's value, which the indicator sits behind.
354
+ - `orientation` (`SegmentedControlOrientation`): Which way the items run.
355
+
356
+ ### Reasons
357
+
358
+ `details.reason` says why the checked value changed.
359
+
360
+ - `item-press` (`MouseEvent | KeyboardEvent`): A member of a collection was chosen: a menu item, a tab, a radio or a checkbox item.
361
+ - `list-navigation` (`KeyboardEvent`): Arrow, Home or End keys moved the selection, typing picked an item, or Ctrl or ⌘ with A selected every item of a listbox.
362
+ - `reset` (`none`): A form reset.
363
+ - `restore` (`none`): The browser restored state, like after going back, or a saved value was read back from storage.
364
+ - `imperative` (`none`): A controller or `api` call, or a property set on a web component.
365
+
366
+ ### Controller
367
+
368
+ Create one with `createSegmentedControlController()` and pass it as `controller`.
369
+
370
+ Every write method takes an optional `{ reason, event }`, runs the change callback like a press, and returns `false` when the change is canceled.
371
+
372
+ - `getValue()` (`() => T | null`): The checked item's value.
373
+ - `setValue(value, details?)` (`(value: T | null, details?: SegmentedControlChangeOptions | undefined) => boolean`): Checks an item, as a user would. Disabled items and read-only controls ignore it.
374
+ - `focus()` (`() => void`): Moves focus to the checked item, or the first one.
375
+
376
+ ### Events
377
+
378
+ Events get the new value first, then the details. `v-model` only updates when the change event wasn't canceled.
379
+
380
+ | Binding | On | Receives |
381
+ | --------------- | ------------------ | ----------------------------------------------------------------- |
382
+ | `v-model:value` | `SegmentedControl` | The checked value |
383
+ | `@value-change` | `SegmentedControl` | `(value, details)`: the checked value, and the [reason](#reasons) |
384
+
385
+ ## Accessibility
386
+
387
+ A segmented control is a radio group, so it follows the [WAI-ARIA radio group pattern](https://www.w3.org/WAI/ARIA/apg/patterns/radio/) and behaves like native radios that share a name. Each `SegmentedControlItem` renders a button with the `radio` role, and a hidden native radio that takes part in forms. The indicator is presentational, so screen readers skip it.
388
+
389
+ ### Keyboard
390
+
391
+ | Key | Does |
392
+ | ---------- | ---------------------------------------------------------------------- |
393
+ | Tab | Moves into the control, onto the checked item or the first enabled one |
394
+ | Arrow keys | Move to the next or previous item and check it, skipping disabled ones |
395
+ | Space | Checks the focused item |
396
+ | Enter | Submits its form |
397
+
398
+ ### Tested Behavior
399
+
400
+ Every line below is an automated test.
401
+
402
+ **Selection**
403
+
404
+ - The default item is checked, and only that one.
405
+ - Clicking an item checks it and says why.
406
+ - Clicking a disabled item does nothing.
407
+ - A change can be cancelled.
408
+ - A controller checks an item.
409
+
410
+ **Keyboard**
411
+
412
+ - Tab lands on the checked item, and the next Tab leaves.
413
+ - Arrow keys on both axes check the next item, skip disabled ones and wrap.
414
+
415
+ **Orientation**
416
+
417
+ - A segmented control is horizontal unless it says otherwise.
418
+ - A vertical segmented control reports it.
419
+
420
+ **Indicator**
421
+
422
+ - The indicator covers the checked item.
423
+ - The indicator follows the checked item.
424
+ - The indicator covers the checked item in a vertical control.
425
+ - The indicator matches items with fractional widths exactly.
426
+ - A scaled ancestor doesn't move the indicator off fractional items.
427
+ - With nothing checked, the indicator has no box.
428
+
429
+ **Forms**
430
+
431
+ - The checked value submits under the control's name.
432
+ - A form reset checks the default item again.
433
+ - A disabled control submits nothing, and its items can't be focused or checked.
434
+
435
+ Runs in React, Vue and web components.