@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,306 @@
1
+ # Input
2
+
3
+ Enter text, add a clear button, and connect the value to your form or state.
4
+
5
+ An input is a native text field for short values like a name, an email address or a search. It works inside a field and a form, and adds a clear button, text around the input, and change reasons.
6
+
7
+ ```vue title="basic.vue"
8
+ <script setup lang="ts">
9
+ import { XIcon } from "@lucide/vue"
10
+ import { Field, FieldDescription, FieldLabel } from "@ui-sdk/vue/field"
11
+ import { Input, InputClear, InputGroup } from "@ui-sdk/vue/input"
12
+ import { useLog } from "./log"
13
+
14
+ const log = useLog()
15
+ </script>
16
+
17
+ <template>
18
+ <Field name="website">
19
+ <FieldLabel>Website</FieldLabel>
20
+ <InputGroup>
21
+ <span>https://</span>
22
+ <Input
23
+ placeholder="example.com"
24
+ default-value="hollisgrant.com"
25
+ @value-change="
26
+ (value: string, details: { reason: string }) =>
27
+ log(`${value ? `“${value}”` : 'Empty'} (${details.reason})`)
28
+ "
29
+ />
30
+ <InputClear aria-label="Clear website">
31
+ <XIcon aria-hidden="true" />
32
+ </InputClear>
33
+ </InputGroup>
34
+ <FieldDescription>Shown on your public profile.</FieldDescription>
35
+ </Field>
36
+ </template>
37
+ ```
38
+
39
+ Edit the website, then press Clear website. The text clears, focus returns to the input and the console logs the reason. Pressing https:// focuses the input too.
40
+
41
+ ## Installation
42
+
43
+ ```bash
44
+ npm install @ui-sdk/vue @ui-sdk/core
45
+ ```
46
+
47
+ ## Composition
48
+
49
+ Use the following composition to build an `Input` or an `InputGroup`:
50
+
51
+ ```text
52
+ Input
53
+
54
+ InputGroup
55
+ ├── Input
56
+ └── InputClear
57
+ ```
58
+
59
+ `Input` renders a native `<input>`. Pass its type, placeholder, autocomplete and other native attributes directly. `InputGroup` holds text or buttons around it, and `InputClear` empties it.
60
+
61
+ ## Usage
62
+
63
+ Import the parts and put them together:
64
+
65
+ ```vue
66
+ <script setup lang="ts">
67
+ import { Input, InputClear, InputGroup } from "@ui-sdk/vue/input"
68
+ </script>
69
+
70
+ <template>
71
+ <InputGroup>
72
+ <Input type="search" aria-label="Search" />
73
+ <InputClear aria-label="Clear search" />
74
+ </InputGroup>
75
+ </template>
76
+ ```
77
+
78
+ ## Fields
79
+
80
+ Wrap the input in a [Field](./field.md) to give it a visible label, a description and errors. The field also supplies its `name` when the input has none.
81
+
82
+ ```vue
83
+ <Field name="username">
84
+ <FieldLabel>Username</FieldLabel>
85
+ <Input autocomplete="username" />
86
+ <FieldDescription>Choose a unique username for your account.</FieldDescription>
87
+ </Field>
88
+ ```
89
+
90
+ ## Disabled and Read Only
91
+
92
+ Set `disabled` on the input or its field to prevent editing and leave it out of the tab order and the submitted values. A `read-only` input can still be focused, selected and submitted.
93
+
94
+ ```vue
95
+ <Field disabled>
96
+ <FieldLabel>Email</FieldLabel>
97
+ <Input type="email" default-value="hollis@example.com" />
98
+ </Field>
99
+
100
+ <Field>
101
+ <FieldLabel>Profile URL</FieldLabel>
102
+ <Input read-only value="example.com/hollis" />
103
+ </Field>
104
+ ```
105
+
106
+ ## Validation
107
+
108
+ Use native constraints such as `required`, `type="email"` and `pattern`. The field shows its error after a submit attempt and updates it as the value changes. Set `invalid` on the field for an error your app decides; the input gets `aria-invalid`. [Field](./field.md#validation) covers the error parts and their matches.
109
+
110
+ ```vue
111
+ <Field name="email" :invalid="emailTaken ? true : undefined">
112
+ <FieldLabel>Email</FieldLabel>
113
+ <Input type="email" required />
114
+ <FieldError match="valueMissing">Enter your email address.</FieldError>
115
+ <FieldError match>That email is already in use.</FieldError>
116
+ </Field>
117
+ ```
118
+
119
+ ## Input Group
120
+
121
+ Place text or buttons around an input with `InputGroup`. Pressing the group's text focuses the input. The text is display content, not part of the submitted value.
122
+
123
+ ```vue
124
+ <InputGroup>
125
+ <span>https://</span>
126
+ <Input aria-label="Website" placeholder="example" />
127
+ <span>.com</span>
128
+ </InputGroup>
129
+ ```
130
+
131
+ ## Clear Button
132
+
133
+ `InputClear` shows while the input has text it can clear. Pressing it empties the value, reports `clear-press` and returns focus to the input. Controlled state and native input listeners receive the change too.
134
+
135
+ ```vue
136
+ <InputGroup>
137
+ <Input type="search" aria-label="Search people" />
138
+ <InputClear aria-label="Clear search">
139
+ <XIcon aria-hidden="true" />
140
+ </InputClear>
141
+ </InputGroup>
142
+ ```
143
+
144
+ ## Controlled
145
+
146
+ Bind the value with `v-model` to control the input. `@value-change` also receives the change details, and native `@input` still works.
147
+
148
+ ```vue
149
+ <script setup lang="ts">
150
+ const name = ref("Hollis Grant")
151
+ </script>
152
+
153
+ <template>
154
+ <Input v-model="name" />
155
+ </template>
156
+ ```
157
+
158
+ See [State and Events](../state-and-events.md) for the shared controlled-state conventions. Input's [reasons](#reasons) tell typing, clearing, resetting and controller changes apart.
159
+
160
+ ## Filtering What's Typed
161
+
162
+ Call `details.cancel()` in `@value-change` to reject a change. The previous text and caret position are restored.
163
+
164
+ ```vue
165
+ <Input
166
+ inputmode="numeric"
167
+ :maxlength="6"
168
+ @value-change="
169
+ (value, details) => {
170
+ if (!/^\d*$/.test(value)) details.cancel()
171
+ }
172
+ "
173
+ />
174
+ ```
175
+
176
+ ## Controller
177
+
178
+ Use `createInputController()` for actions outside the input. Its methods update the input through the same change events as typing.
179
+
180
+ ```vue
181
+ <script setup lang="ts">
182
+ const message = createInputController()
183
+ </script>
184
+
185
+ <template>
186
+ <Input :controller="message" aria-label="Message" />
187
+ <button @click="message.setValue('On my way!')">Quick Reply</button>
188
+ <button @click="message.clear()">Clear</button>
189
+ <button @click="message.focus()">Focus</button>
190
+ </template>
191
+ ```
192
+
193
+ [Controllers](../controllers.md) covers creating and sharing a controller.
194
+
195
+ ## Passwords and Files
196
+
197
+ Use [Password Input](./password-input.md) to show and hide a password. A native file input works with `type="file"`; use [File Input](./file-input.md) for dropping, previews and validation.
198
+
199
+ ## Styling
200
+
201
+ Each part has a `data-slot`, such as `input-group`, and shows its state through the attributes listed under [Data Attributes](#data-attributes). Use `data-filled`, `data-disabled`, `data-readonly` and `data-required` on the input, and the field's validation and focus attributes inside a field. For a group's focus ring, style `:has(input:focus-visible)`, so a focused button inside it keeps its own ring.
202
+
203
+ ## Data Attributes
204
+
205
+ ### Input
206
+ `[data-slot="input"]`
207
+
208
+ - `data-filled`: Present while the field holds any text.
209
+ - `data-disabled`: Present when the field is disabled, by its own prop or its Field.
210
+ - `data-readonly`: Present when the field is read-only.
211
+ - `data-required`: Present when the field is required.
212
+
213
+ ### InputGroup
214
+ `[data-slot="input-group"]`
215
+
216
+ - `data-filled`: Present while the field in the group holds any text.
217
+ - `data-disabled`: Present while the field in the group is disabled, by itself or a `<fieldset>`.
218
+ - `data-readonly`: Present while the field in the group is read-only.
219
+
220
+ ### InputClear
221
+ `[data-slot="input-clear"]`
222
+
223
+ - `data-filled`: Present while the field in the group holds any text.
224
+ - `data-disabled`: Present while the field in the group is disabled, by itself or a `<fieldset>`.
225
+ - `data-readonly`: Present while the field in the group is read-only.
226
+
227
+ ## API Reference
228
+
229
+ Props are the same as below, plus every native input attribute. `onValueChange` is `v-model`, or `@value-change` for the reason.
230
+
231
+ ### Input
232
+
233
+ - `value?` (`string`): The value, for a controlled field.
234
+ - `defaultValue?` (`string`): The value it starts with. A form reset returns here.
235
+ - `@value-change` (`((value: string, details: ChangeDetails<InputValueChangeReason>) => void)`): Called when the user changes the value: typing (`"input-change"`), a Clear part (`"clear-press"`), a form reset (`"reset"`) or a controller (`"imperative"`). `details.cancel()` keeps the value as it was.
236
+ - `controller?` (`object`): Reads and writes the field from anywhere. Create one with `createInputController()`.
237
+ - `debug?` (`boolean`): Logs every state change with its reason to the console, in development builds.
238
+
239
+ - `disabled?` (`boolean`): Disables the field.
240
+ - `readOnly?` (`boolean`): Makes the field read-only.
241
+ - `required?` (`boolean`): Makes the field required.
242
+
243
+ ### InputGroup
244
+
245
+ - `debug?` (`boolean`): Logs every state change with its reason to the console, in development builds.
246
+
247
+ ### InputClear
248
+
249
+ A `<button>` that empties the input in its group and focuses it. It renders only while there's text to clear.
250
+
251
+ ### State
252
+
253
+ - `filled` (`boolean`): Whether the field holds any text.
254
+ - `disabled` (`boolean`): Whether the field is disabled.
255
+ - `readOnly` (`boolean`): Whether the field is read-only.
256
+ - `required` (`boolean`): Whether the field is required.
257
+
258
+ `InputGroup` and `InputClear` receive the state of the input inside the group.
259
+
260
+ - `filled` (`boolean`): Whether the field in the group holds any text.
261
+ - `disabled` (`boolean`): Whether the field in the group is disabled, by itself or by a `<fieldset>`.
262
+ - `readOnly` (`boolean`): Whether the field in the group is read-only.
263
+
264
+ ### Reasons
265
+
266
+ - `input-change` (`Event`): Typing, pasting or dropping changed a text field's value; in a combobox it can also open or close it, or Enter keeps the typed text as the value.
267
+ - `clear-press` (`MouseEvent | KeyboardEvent`): A Clear part was pressed, like on an input, a select or a combobox.
268
+ - `reset` (`none`): A form reset.
269
+ - `imperative` (`none`): A controller or `api` call, or a property set on a web component.
270
+
271
+ ### Controller
272
+
273
+ Create one with `createInputController()` and pass it as `controller`.
274
+
275
+ - `getValue()` (`() => string`): The current value.
276
+ - `setValue(value, options?)` (`(value: string, options?: ChangeOptions<InputValueChangeReason> | undefined) => boolean`): Sets the value as if the user typed it, so `input` fires. Returns false when the change was canceled or nothing changed.
277
+ - `clear(options?)` (`(options?: ChangeOptions<InputValueChangeReason> | undefined) => boolean`): Empties the field, as if the user deleted the text, so `input` fires. Returns false when the change was canceled or nothing changed.
278
+ - `focus()` (`() => void`): Moves focus to the field.
279
+
280
+ ## Accessibility
281
+
282
+ Give the input a visible field label or an `aria-label`, and give the clear button an accessible name. The native input keeps standard text editing, selection, autocomplete and form behavior.
283
+
284
+ - The label names the input and the description describes it.
285
+ - Clicking the label focuses the input.
286
+ - An invalid input is marked once the form tries to submit.
287
+ - Data-filled follows the text.
288
+ - An input group's clear button shows only while there's text.
289
+ - <ui-input>'s value property writes the field quietly, and its api reports. Web components only.
290
+
291
+ **Enhancements**
292
+
293
+ - Changes report their reason: typing, then a reset.
294
+ - A canceled change keeps the text as it was.
295
+ - A canceled change keeps a controlled input's text as it was.
296
+ - A canceled change waits for an IME to finish before the text goes back.
297
+ - A controlled input keeps every keystroke.
298
+ - A required input marks its label required.
299
+
300
+ **Input group**
301
+
302
+ - The clear button empties the field, reports why, and returns focus.
303
+ - Pressing the group's text focuses its field.
304
+ - The clear button hides while its field is disabled.
305
+
306
+ Runs in React, Vue and web components.
@@ -0,0 +1,203 @@
1
+ # Label
2
+
3
+ Name any control with a label, and let a press on the label reach it.
4
+
5
+ A plain `<label>` only reaches inputs, buttons and selects. Label names any control by its `for`, including a radio group or a slider, and a press on the label moves focus into it.
6
+
7
+ ```vue title="basic.vue"
8
+ <script setup lang="ts">
9
+ import { Label } from "@ui-sdk/vue/label"
10
+ import { Radio, RadioGroup, RadioIndicator } from "@ui-sdk/vue/radio"
11
+
12
+ const speeds = [
13
+ { value: "standard", label: "Standard" },
14
+ { value: "express", label: "Express" },
15
+ { value: "overnight", label: "Overnight" },
16
+ ]
17
+ </script>
18
+
19
+ <template>
20
+ <div data-slot="stack">
21
+ <Label for="shipping-speed">Shipping speed</Label>
22
+ <RadioGroup id="shipping-speed" default-value="standard">
23
+ <Label v-for="speed in speeds" :key="speed.value">
24
+ <Radio :value="speed.value">
25
+ <RadioIndicator />
26
+ </Radio>
27
+ {{ speed.label }}
28
+ </Label>
29
+ </RadioGroup>
30
+ </div>
31
+ </template>
32
+ ```
33
+
34
+ Press Shipping speed. Focus moves to the checked option, and a screen reader names the group Shipping speed.
35
+
36
+ ## Installation
37
+
38
+ ```bash
39
+ npm install @ui-sdk/vue @ui-sdk/core
40
+ ```
41
+
42
+ ## Usage
43
+
44
+ Import the label and point its `for` at the control's `id`:
45
+
46
+ ```vue
47
+ <script setup lang="ts">
48
+ import { Label } from "@ui-sdk/vue/label"
49
+ import { Radio, RadioGroup } from "@ui-sdk/vue/radio"
50
+ </script>
51
+
52
+ <template>
53
+ <Label for="plan">Plan</Label>
54
+ <RadioGroup id="plan" default-value="pro">
55
+ <Radio value="free">Free</Radio>
56
+ <Radio value="pro">Pro</Radio>
57
+ </RadioGroup>
58
+ </template>
59
+ ```
60
+
61
+ Label renders a native `<label>`. When the browser can reach the control on its own, like an input, it does. For anything else, the control takes the label as its name, and a press on the label focuses it, checks it or clicks it, as a native label would.
62
+
63
+ Inside a [Field](./field.md), use `FieldLabel`, which also ties the field's description and errors to the control.
64
+
65
+ ## Build Your Own
66
+
67
+ `@ui-sdk/vue/label` comes with a ready-made Label. If you'd like to build your own, with your own name, start from the `label` primitive in `@ui-sdk/core`:
68
+
69
+ ```ts title="caption.ts"
70
+ import { label } from "@ui-sdk/core/label"
71
+ import { createParts } from "@ui-sdk/vue/primitive"
72
+
73
+ const { Root: Caption } = createParts(label, { root: "label" })
74
+
75
+ export { Caption }
76
+ ```
77
+
78
+ ```vue
79
+ <template>
80
+ <Caption for="plan">Plan</Caption>
81
+ </template>
82
+ ```
83
+
84
+ `Caption` keeps the behavior of the ready-made Label: it names the control and reaches it on a press. See [Rendering](../rendering.md) and [Custom Parts](../custom-parts.md) for more.
85
+
86
+ ## Slider
87
+
88
+ A label for a slider names its thumb, so the thumb reads as **Volume**, and a press on the label focuses it.
89
+
90
+ ```vue title="slider.vue"
91
+ <script setup lang="ts">
92
+ import { Label } from "@ui-sdk/vue/label"
93
+ import {
94
+ Slider,
95
+ SliderControl,
96
+ SliderIndicator,
97
+ SliderThumb,
98
+ SliderTrack,
99
+ SliderValue,
100
+ } from "@ui-sdk/vue/slider"
101
+ </script>
102
+
103
+ <template>
104
+ <Slider id="volume" :default-value="75">
105
+ <div data-slot="row">
106
+ <Label for="volume">Volume</Label>
107
+ <SliderValue />
108
+ </div>
109
+ <SliderControl>
110
+ <SliderTrack>
111
+ <SliderIndicator />
112
+ <SliderThumb />
113
+ </SliderTrack>
114
+ </SliderControl>
115
+ </Slider>
116
+ </template>
117
+ ```
118
+
119
+ Press Volume, then use the arrow keys. The thumb moves, and a screen reader reads it as Volume.
120
+
121
+ ## Switch
122
+
123
+ Point the label at a switch beside it. A press on the label turns the switch on and off.
124
+
125
+ ```vue title="switch.vue"
126
+ <script setup lang="ts">
127
+ import { Label } from "@ui-sdk/vue/label"
128
+ import { Switch, SwitchThumb } from "@ui-sdk/vue/switch"
129
+ </script>
130
+
131
+ <template>
132
+ <div data-slot="row">
133
+ <Switch id="updates">
134
+ <SwitchThumb />
135
+ </Switch>
136
+ <Label for="updates">Email me product updates</Label>
137
+ </div>
138
+ </template>
139
+ ```
140
+
141
+ Press Email me product updates. The switch turns on.
142
+
143
+ ## Wrapping a Control
144
+
145
+ Put the control inside the label instead of pointing at it, and you need no `id`.
146
+
147
+ ```vue title="wrap.vue"
148
+ <script setup lang="ts">
149
+ import { CheckIcon } from "@lucide/vue"
150
+ import { Checkbox, CheckboxIndicator } from "@ui-sdk/vue/checkbox"
151
+ import { Label } from "@ui-sdk/vue/label"
152
+ </script>
153
+
154
+ <template>
155
+ <Label>
156
+ <Checkbox>
157
+ <CheckboxIndicator>
158
+ <CheckIcon aria-hidden="true" />
159
+ </CheckboxIndicator>
160
+ </Checkbox>
161
+ Accept terms and conditions
162
+ </Label>
163
+ </template>
164
+ ```
165
+
166
+ Press Accept terms and conditions. The checkbox checks, and a double click doesn't select the text.
167
+
168
+ ## Custom Rendering
169
+
170
+ Use the `#render` slot to use a different element, such as a `<span>`, and bind the supplied `props` to it. A label that isn't a `<label>` still names its control and reaches it on a press. See [Parts and Composition](../parts-and-composition.md) for more.
171
+
172
+ ## Styling
173
+
174
+ The label is unstyled. It has a `data-slot` of `label`.
175
+
176
+ A double click on the label doesn't select its text, so you don't need `user-select: none` for that.
177
+
178
+ ## API Reference
179
+
180
+ Props are the same as below, along with a native label's `for`.
181
+
182
+ ### Label
183
+
184
+ - `debug?` (`boolean`): Logs every state change with its reason to the console, in development builds.
185
+
186
+ ## Accessibility
187
+
188
+ A native `<label>` names the element its `for` points at only when that element is labelable, like an input or a button. Label names any other control through `aria-labelledby`, unless the control has an `aria-label` or an `aria-labelledby` of its own, which win. Its press does what a native label's does: it checks a checkbox, a switch or a radio, focuses a group's checked radio, and focuses anything else. A press on a disabled control does nothing.
189
+
190
+ ### Tested Behavior
191
+
192
+ Every line below is an automated test.
193
+
194
+ - A label names a native input, and a press focuses it.
195
+ - A label names a radio group, and a press focuses its checked radio.
196
+ - A label for a slider names its thumb, and a press focuses it.
197
+ - A label around a checkbox names it, and a press toggles it.
198
+ - A press on the label of a disabled group moves no focus.
199
+ - A control its author names keeps that name.
200
+ - A label names a control that renders after it.
201
+ - A double click on a label doesn't select its text.
202
+
203
+ Runs in React, Vue and web components.