@lovince/ui-vue 0.1.1 → 0.2.0

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 (78) hide show
  1. package/README.md +246 -28
  2. package/dist/components/{combobox.d.ts → choice/combobox.d.ts} +10 -27
  3. package/dist/components/{command.d.ts → choice/command.d.ts} +7 -19
  4. package/dist/components/{emoji-picker.d.ts → choice/emoji-picker.d.ts} +9 -9
  5. package/dist/components/{radio-group.d.ts → choice/radio-group.d.ts} +3 -3
  6. package/dist/components/choice/rating.d.ts +143 -0
  7. package/dist/components/choice/segmented-control.d.ts +26 -0
  8. package/dist/components/{select.d.ts → choice/select.d.ts} +11 -20
  9. package/dist/components/{toggle-group.d.ts → choice/toggle-group.d.ts} +3 -9
  10. package/dist/components/{toggles.d.ts → choice/toggles.d.ts} +4 -4
  11. package/dist/components/{accordion.d.ts → disclosure/accordion.d.ts} +5 -13
  12. package/dist/components/{disclosure.d.ts → disclosure/disclosure.d.ts} +3 -10
  13. package/dist/components/{tabs.d.ts → disclosure/tabs.d.ts} +4 -6
  14. package/dist/components/display/avatar-group.d.ts +61 -0
  15. package/dist/components/{avatar.d.ts → display/avatar.d.ts} +3 -6
  16. package/dist/components/display/feedback.d.ts +623 -0
  17. package/dist/components/display/image.d.ts +95 -0
  18. package/dist/components/display/meter.d.ts +165 -0
  19. package/dist/components/{progress.d.ts → display/progress.d.ts} +2 -6
  20. package/dist/components/display/stat.d.ts +102 -0
  21. package/dist/components/display/table.d.ts +130 -0
  22. package/dist/components/display/timeline.d.ts +181 -0
  23. package/dist/components/forms/color-picker.d.ts +187 -0
  24. package/dist/components/forms/date-picker.d.ts +257 -0
  25. package/dist/components/forms/editable.d.ts +243 -0
  26. package/dist/components/forms/file-upload.d.ts +203 -0
  27. package/dist/components/forms/forms.d.ts +290 -0
  28. package/dist/components/forms/phone-input.d.ts +189 -0
  29. package/dist/components/forms/signature-pad.d.ts +122 -0
  30. package/dist/components/forms/tags-input.d.ts +209 -0
  31. package/dist/components/forms/text-inputs.d.ts +270 -0
  32. package/dist/components/forms/time-field.d.ts +165 -0
  33. package/dist/components/{calendar.d.ts → inputs/calendar.d.ts} +8 -19
  34. package/dist/components/{number-field.d.ts → inputs/number-field.d.ts} +4 -15
  35. package/dist/components/{pin-input.d.ts → inputs/pin-input.d.ts} +2 -12
  36. package/dist/components/{slider.d.ts → inputs/slider.d.ts} +4 -14
  37. package/dist/components/{carousel.d.ts → layout/carousel.d.ts} +50 -20
  38. package/dist/components/layout/marquee.d.ts +104 -0
  39. package/dist/components/layout/primitives.d.ts +242 -0
  40. package/dist/components/layout/scroll-area.d.ts +160 -0
  41. package/dist/components/{sortable.d.ts → layout/sortable.d.ts} +3 -12
  42. package/dist/components/{splitter.d.ts → layout/splitter.d.ts} +3 -11
  43. package/dist/components/{tree.d.ts → layout/tree.d.ts} +3 -9
  44. package/dist/components/layout/virtual-list.d.ts +68 -0
  45. package/dist/components/navigation/breadcrumb.d.ts +114 -0
  46. package/dist/components/navigation/pagination.d.ts +221 -0
  47. package/dist/components/navigation/sidebar.d.ts +258 -0
  48. package/dist/components/{dialog.d.ts → navigation/stepper.d.ts} +142 -114
  49. package/dist/components/navigation/table-of-contents.d.ts +60 -0
  50. package/dist/components/navigation/toolbar.d.ts +179 -0
  51. package/dist/components/{menu.d.ts → overlays/dialog.d.ts} +222 -224
  52. package/dist/components/overlays/menu.d.ts +901 -0
  53. package/dist/components/overlays/menubar.d.ts +106 -0
  54. package/dist/components/{navigation-menu.d.ts → overlays/navigation-menu.d.ts} +6 -19
  55. package/dist/components/{popover.d.ts → overlays/popover.d.ts} +6 -9
  56. package/dist/components/overlays/sheet.d.ts +77 -0
  57. package/dist/components/{toast.d.ts → overlays/toast.d.ts} +6 -14
  58. package/dist/components/{tooltip.d.ts → overlays/tooltip.d.ts} +8 -8
  59. package/dist/components/primitives/button.d.ts +102 -0
  60. package/dist/components/primitives/icon.d.ts +79 -0
  61. package/dist/components/primitives/primitives.d.ts +300 -0
  62. package/dist/composables/data-table.d.ts +0 -2
  63. package/dist/composables/observers.d.ts +0 -9
  64. package/dist/composables/presence.d.ts +0 -5
  65. package/dist/icon.d.ts +2 -0
  66. package/dist/icon.js +76 -0
  67. package/dist/index.d.ts +61 -28
  68. package/dist/index.js +4935 -990
  69. package/dist/shared/dismiss.d.ts +0 -8
  70. package/dist/shared/element.d.ts +0 -2
  71. package/dist/shared/floating-props.d.ts +0 -1
  72. package/dist/shared/list.d.ts +0 -7
  73. package/dist/shared/machine.d.ts +0 -18
  74. package/dist/shared/parts.d.ts +1 -2
  75. package/dist/shared/portal.d.ts +1 -10
  76. package/dist/shared/primitive.d.ts +2 -10
  77. package/dist/shared/roving.d.ts +14 -0
  78. package/package.json +16 -3
package/README.md CHANGED
@@ -6,7 +6,7 @@ effects run through [`@lovince/ui-dom`](../ui-dom). This package subscribes to
6
6
  the machine, feeds props in, and renders what `connect()` returns. It holds no
7
7
  behaviour of its own.
8
8
 
9
- > **Status: `0.1.0`, experimental.** The API may change before `1.0`.
9
+ > **Status: `0.2.0`, experimental.** The API may change before `1.0`.
10
10
 
11
11
  ```bash
12
12
  bun add @lovince/ui-vue
@@ -16,41 +16,259 @@ Requires Vue 3.5+ (for SSR-stable `useId`). No CSS ships with the package. Style
16
16
  it with classes and the `data-*` state attributes, or use
17
17
  [`@lovince/themes`](../themes) for tokens.
18
18
 
19
+ Every component export carries the `Lv` prefix (`LvButton`, `LvDialogRoot`), so
20
+ names never clash with your own components or native elements.
21
+
19
22
  ## Components
20
23
 
21
24
  | Group | Component | Parts |
22
25
  |---|---|---|
23
- | Disclosure | Disclosure | `DisclosureRoot`, `DisclosureTrigger`, `DisclosureContent` |
24
- | | Accordion | `AccordionRoot`, `AccordionItem`, `AccordionHeader`, `AccordionTrigger`, `AccordionContent` |
25
- | | Tabs | `TabsRoot`, `TabsList`, `TabsTrigger`, `TabsContent` |
26
- | Overlays | Dialog | `DialogRoot`, `DialogTrigger`, `DialogPortal`, `DialogOverlay`, `DialogContent`, `DialogTitle`, `DialogDescription`, `DialogClose` |
27
- | | Popover | `PopoverRoot`, `PopoverTrigger`, `PopoverAnchor`, `PopoverPortal`, `PopoverContent`, `PopoverClose` |
28
- | | Tooltip | `TooltipRoot`, `TooltipTrigger`, `TooltipPortal`, `TooltipContent` |
29
- | | Hover card | `HoverCardRoot`, `HoverCardTrigger`, `HoverCardPortal`, `HoverCardContent` |
30
- | | Menu | `DropdownMenuRoot`, `DropdownMenuTrigger`, `DropdownMenuContent`, `DropdownMenuItem`, `DropdownMenuCheckboxItem`, `DropdownMenuRadioGroup`, `DropdownMenuRadioItem`, `DropdownMenuItemIndicator`, `DropdownMenuLabel`, `DropdownMenuGroup`, `DropdownMenuSeparator`, `ContextMenuTrigger` |
31
- | | Navigation menu | `NavigationMenuRoot`, `NavigationMenuList`, `NavigationMenuItem`, `NavigationMenuTrigger`, `NavigationMenuContent`, `NavigationMenuLink` |
32
- | | Toast | `ToastViewport`, `ToastRoot`, `ToastTitle`, `ToastDescription`, `ToastAction`, `ToastClose`, plus `toast()` |
33
- | Choice | Checkbox, Switch | `CheckboxRoot`, `CheckboxIndicator`, `SwitchRoot`, `SwitchThumb` |
34
- | | Radio group | `RadioGroupRoot`, `RadioGroupItem`, `RadioGroupIndicator` |
35
- | | Toggle, toggle group | `ToggleRoot`, `ToggleGroupRoot`, `ToggleGroupItem` |
36
- | | Select | `SelectRoot`, `SelectTrigger`, `SelectValue`, `SelectContent`, `SelectItem`, `SelectItemText`, `SelectItemIndicator`, `SelectGroup`, `SelectLabel`, `SelectSeparator` |
37
- | | Combobox | `ComboboxRoot`, `ComboboxAnchor`, `ComboboxInput`, `ComboboxTrigger`, `ComboboxClear`, `ComboboxContent`, `ComboboxItem`, `ComboboxItemIndicator`, `ComboboxEmpty` |
38
- | | Command palette | `CommandRoot`, `CommandInput`, `CommandList`, `CommandGroup`, `CommandItem`, `CommandEmpty`, `CommandSeparator` |
39
- | | Emoji picker | `EmojiPickerRoot`, `EmojiPickerSearch`, `EmojiPickerList`, `EmojiPickerGroup`, `EmojiPickerItem`, `EmojiPickerNav`… |
40
- | Input | Slider | `SliderRoot`, `SliderTrack`, `SliderRange`, `SliderThumb` |
41
- | | Number field | `NumberFieldRoot`, `NumberFieldInput`, `NumberFieldIncrement`, `NumberFieldDecrement` |
42
- | | PIN input | `PinInputRoot`, `PinInputInput` |
43
- | | Calendar | `CalendarRoot`, `CalendarHeading`, `CalendarPrev`, `CalendarNext`, `CalendarGrid`, `CalendarHeadCell`, `CalendarCell`, `CalendarDay` |
44
- | Layout | Splitter | `SplitterGroup`, `SplitterPanel`, `SplitterHandle` |
45
- | | Tree | `TreeRoot`, `TreeItem`, `TreeGroup` |
46
- | | Carousel | `CarouselRoot`, `CarouselViewport`, `CarouselSlide`, `CarouselPrevious`, `CarouselNext`, `CarouselIndicators`, `CarouselIndicator` |
47
- | | Sortable | `SortableRoot`, `SortableItem`, `SortableHandle` |
48
- | Display | Avatar, Progress | `AvatarRoot`, `AvatarImage`, `AvatarFallback`, `ProgressRoot`, `ProgressIndicator` |
26
+ | Primitives | Text, Heading, Label, Link, Code, Kbd, Box, AspectRatio, VisuallyHidden, Separator | |
27
+ | | Icon | `LvIcon`, from the separate `@lovince/ui-vue/icon` entry |
28
+ | Disclosure | Disclosure | `LvDisclosureRoot`, `LvDisclosureTrigger`, `LvDisclosureContent` |
29
+ | | Accordion | `LvAccordionRoot`, `LvAccordionItem`, `LvAccordionHeader`, `LvAccordionTrigger`, `LvAccordionContent` |
30
+ | | Tabs | `LvTabsRoot`, `LvTabsList`, `LvTabsTrigger`, `LvTabsContent` |
31
+ | Overlays | Dialog | `LvDialogRoot`, `LvDialogTrigger`, `LvDialogPortal`, `LvDialogOverlay`, `LvDialogContent`, `LvDialogTitle`, `LvDialogDescription`, `LvDialogClose` |
32
+ | | Popover | `LvPopoverRoot`, `LvPopoverTrigger`, `LvPopoverAnchor`, `LvPopoverPortal`, `LvPopoverContent`, `LvPopoverClose` |
33
+ | | Tooltip | `LvTooltipRoot`, `LvTooltipTrigger`, `LvTooltipPortal`, `LvTooltipContent` |
34
+ | | Hover card | `LvHoverCardRoot`, `LvHoverCardTrigger`, `LvHoverCardPortal`, `LvHoverCardContent` |
35
+ | | Menu | `LvDropdownMenuRoot`, `LvDropdownMenuTrigger`, `LvDropdownMenuContent`, `LvDropdownMenuItem`, `LvDropdownMenuCheckboxItem`, `LvDropdownMenuRadioGroup`, `LvDropdownMenuRadioItem`, `LvDropdownMenuItemIndicator`, `LvDropdownMenuLabel`, `LvDropdownMenuGroup`, `LvDropdownMenuSeparator`, `LvContextMenuTrigger` |
36
+ | | Navigation menu | `LvNavigationMenuRoot`, `LvNavigationMenuList`, `LvNavigationMenuItem`, `LvNavigationMenuTrigger`, `LvNavigationMenuContent`, `LvNavigationMenuLink` |
37
+ | | Toast | `LvToastViewport`, `LvToastRoot`, `LvToastTitle`, `LvToastDescription`, `LvToastAction`, `LvToastClose`, plus `toast()` |
38
+ | Choice | Checkbox, Switch | `LvCheckboxRoot`, `LvCheckboxIndicator`, `LvSwitchRoot`, `LvSwitchThumb` |
39
+ | | Radio group | `LvRadioGroupRoot`, `LvRadioGroupItem`, `LvRadioGroupIndicator` |
40
+ | | Toggle, toggle group | `LvToggleRoot`, `LvToggleGroupRoot`, `LvToggleGroupItem` |
41
+ | | Select | `LvSelectRoot`, `LvSelectTrigger`, `LvSelectValue`, `LvSelectContent`, `LvSelectItem`, `LvSelectItemText`, `LvSelectItemIndicator`, `LvSelectGroup`, `LvSelectLabel`, `LvSelectSeparator` |
42
+ | | Combobox | `LvComboboxRoot`, `LvComboboxAnchor`, `LvComboboxInput`, `LvComboboxTrigger`, `LvComboboxClear`, `LvComboboxContent`, `LvComboboxItem`, `LvComboboxItemIndicator`, `LvComboboxEmpty` |
43
+ | | Command palette | `LvCommandRoot`, `LvCommandInput`, `LvCommandList`, `LvCommandGroup`, `LvCommandItem`, `LvCommandEmpty`, `LvCommandSeparator` |
44
+ | | Emoji picker | `LvEmojiPickerRoot`, `LvEmojiPickerSearch`, `LvEmojiPickerList`, `LvEmojiPickerGroup`, `LvEmojiPickerItem`, `LvEmojiPickerNav`… |
45
+ | Input | Slider | `LvSliderRoot`, `LvSliderTrack`, `LvSliderRange`, `LvSliderThumb` |
46
+ | | Number field | `LvNumberFieldRoot`, `LvNumberFieldInput`, `LvNumberFieldIncrement`, `LvNumberFieldDecrement` |
47
+ | | PIN input | `LvPinInputRoot`, `LvPinInputInput` |
48
+ | | Calendar | `LvCalendarRoot`, `LvCalendarHeading`, `LvCalendarPrev`, `LvCalendarNext`, `LvCalendarGrid`, `LvCalendarHeadCell`, `LvCalendarCell`, `LvCalendarDay` |
49
+ | Forms | Field, Input, Textarea, Form, TagsInput, FileUpload, TimeField, PhoneInput | `LvFieldRoot`, `LvFieldLabel`, `LvTagsInputRoot`, `LvTagsInputInput`, `LvTagsInputItem`, `LvTagsInputRemove`, `LvFileUploadRoot`, `LvFileUploadInput`, `LvFileUploadItem`, `LvFileUploadRemove`, `LvTimeFieldRoot`, `LvTimeFieldInput`, `LvPhoneInputRoot`, `LvPhoneInputInput`, `LvPhoneInputCountrySelect` |
50
+ | Layout | ScrollArea | `LvScrollAreaRoot`, `LvScrollAreaViewport`, `LvScrollAreaScrollbar`, `LvScrollAreaThumb` |
51
+ | | Splitter | `LvSplitterGroup`, `LvSplitterPanel`, `LvSplitterHandle` |
52
+ | | Tree | `LvTreeRoot`, `LvTreeItem`, `LvTreeGroup` |
53
+ | | Carousel | `LvCarouselRoot`, `LvCarouselViewport`, `LvCarouselSlide`, `LvCarouselPrevious`, `LvCarouselNext`, `LvCarouselIndicators`, `LvCarouselIndicator` |
54
+ | | Sortable | `LvSortableRoot`, `LvSortableItem`, `LvSortableHandle` |
55
+ | Display | Avatar, Progress | `LvAvatarRoot`, `LvAvatarImage`, `LvAvatarFallback`, `LvProgressRoot`, `LvProgressIndicator` |
56
+
57
+ ### Primitives
58
+
59
+ The primitives provide semantic HTML and stable styling hooks without imposing
60
+ CSS. `LvText` and `LvHeading` expose a `data-text-role` matching the typography
61
+ roles in `@lovince/tokens`; heading level controls semantics independently from
62
+ visual size.
63
+
64
+ ```vue
65
+ <script setup lang="ts">
66
+ import { LvAspectRatio, LvCode, LvHeading, LvKbd, LvLabel, LvLink, LvSeparator, LvText, LvVisuallyHidden } from '@lovince/ui-vue'
67
+ </script>
68
+
69
+ <template>
70
+ <LvHeading :level="1" size="display-md">Workspace</LvHeading>
71
+ <LvText variant="body-md">Manage projects and members.</LvText>
72
+ <LvLabel for="query">Search</LvLabel>
73
+ <LvLink href="/guide">Read the guide</LvLink>
74
+ <LvCode>bun test</LvCode>
75
+ <LvKbd>Ctrl+K</LvKbd>
76
+ <LvAspectRatio :ratio="16 / 9">
77
+ <img src="/product.webp" alt="Product detail" />
78
+ </LvAspectRatio>
79
+ <LvVisuallyHidden>Search projects</LvVisuallyHidden>
80
+ <LvSeparator :decorative="false" />
81
+ </template>
82
+ ```
83
+
84
+ `LvBox`, `LvAspectRatio`, `LvText`, `LvHeading`, `LvLabel`, `LvLink`, `LvCode`, `LvKbd`, and
85
+ `LvVisuallyHidden` support `as` and `asChild`. `LvAspectRatio` defaults to a 1:1
86
+ frame; set `ratio` to a positive finite number such as `16 / 9` to size its
87
+ content region. `LvLink` defaults to an anchor and leaves navigation attributes
88
+ such as `href`, `target`, and `rel` under the caller's control. `LvCode` and
89
+ `LvKbd` default to their corresponding semantic elements. When `LvHeading`
90
+ renders a non-heading element, it keeps its semantic level with
91
+ `role="heading"` and `aria-level`. `LvVisuallyHidden` applies the minimal inline
92
+ clipping styles needed to remain available to assistive technology. A
93
+ non-decorative `LvSeparator` exposes separator semantics and its orientation;
94
+ decorative separators are hidden from the accessibility tree.
95
+
96
+ ### Button
97
+
98
+ `LvButton` is the one part that names a design decision: `variant`
99
+ (`solid`, `subtle`, `outline`, `ghost`, `danger`) and `size` (`sm`, `md`, `lg`,
100
+ `icon`) become `data-variant` and `data-size`, and your stylesheet decides what
101
+ each of them looks like. What the component owns is the markup a hand-written
102
+ button gets wrong.
103
+
104
+ ```vue
105
+ <script setup lang="ts">
106
+ import { LvButton } from '@lovince/ui-vue'
107
+ </script>
108
+
109
+ <template>
110
+ <LvButton @click="save">Save</LvButton>
111
+ <LvButton type="submit" variant="solid" size="lg" :loading="saving">Save</LvButton>
112
+ <LvButton as="a" href="/settings" variant="ghost" size="sm">Settings</LvButton>
113
+ <LvButton :disabled="!valid" variant="outline">Publish</LvButton>
114
+ </template>
115
+ ```
116
+
117
+ - `type` defaults to `button`, so a button inside a form never submits by
118
+ accident; ask for `submit` or `reset` explicitly.
119
+ - `disabled` sets the native attribute when the element is a real `button`, and
120
+ `aria-disabled` when it is not, since an anchor cannot be disabled.
121
+ - `loading` sets `aria-busy` and keeps the control in the tab order — a request
122
+ started from the keyboard stays visible to the keyboard — while swallowing
123
+ clicks so the handler cannot fire twice.
124
+ - Both states also appear as `data-disabled` and `data-loading`, and neither
125
+ runs the `onClick` it was given. With `asChild` the element is yours, click
126
+ handling included; the state attributes still arrive.
127
+
128
+ ### Icon
129
+
130
+ `LvIcon` draws a glyph from `@lovince/icons` on the registry's shared 24-unit
131
+ canvas. It is its own entry because the icon registry is an optional peer:
132
+ importing `@lovince/ui-vue` loads no icon data, and an app that brings its own
133
+ icons needs nothing extra.
134
+
135
+ ```vue
136
+ <script setup lang="ts">
137
+ import { LvIcon } from '@lovince/ui-vue/icon'
138
+ </script>
139
+
140
+ <template>
141
+ <LvIcon name="arrowRight" />
142
+ <LvIcon name="success" :size="16" :stroke-width="1.5" label="Completed" />
143
+ </template>
144
+ ```
145
+
146
+ An icon is decorative — `aria-hidden="true"` — unless it is given a `label`,
147
+ which makes it `role="img"` with that accessible name. `absoluteStrokeWidth`
148
+ keeps the rendered stroke thickness constant as `size` grows; `fill` takes 0 to
149
+ 1 for a partial wash; `rotate` and `flip` turn about the canvas centre. Size and
150
+ stroke fall back to the registry's own defaults, and an unknown `name` renders an
151
+ empty box rather than breaking layout.
152
+
153
+ ### Forms
154
+
155
+ `LvFieldRoot` provides a stable input id and wires the label, description, and
156
+ error into `LvInput` or `LvTextarea`. Description and error copy can be passed as
157
+ props or named slots. Set `invalid` to expose an error slot, or pass `error` to
158
+ mark the field invalid automatically. `required` and `disabled` are propagated
159
+ to the field control; each control can override them.
160
+
161
+ ```vue
162
+ <script setup lang="ts">
163
+ import { LvFieldLabel, LvFieldRoot, LvForm, LvInput, LvTextarea } from '@lovince/ui-vue'
164
+ </script>
165
+
166
+ <template>
167
+ <LvForm @submit.prevent="save">
168
+ <LvFieldRoot required description="Use your work email.">
169
+ <LvFieldLabel>Email</LvFieldLabel>
170
+ <LvInput type="email" name="email" autocomplete="email" />
171
+ </LvFieldRoot>
172
+
173
+ <LvFieldRoot :invalid="Boolean(message)" :error="message">
174
+ <LvFieldLabel>Project summary</LvFieldLabel>
175
+ <LvTextarea name="summary" />
176
+ </LvFieldRoot>
177
+
178
+ <button type="submit">Save</button>
179
+ </LvForm>
180
+ </template>
181
+ ```
182
+
183
+ `LvInput` and `LvTextarea` support `modelValue` / `defaultValue` and emit
184
+ `update:modelValue`. `LvTextarea` uses content-based field sizing where the
185
+ browser supports it and retains a native `rows` fallback. `LvForm` is a native
186
+ form wrapper: browser constraint validation and submit events remain in
187
+ control. Set `submitting` to expose `aria-busy` and `data-submitting`, or
188
+ `noValidate` when validation is handled elsewhere.
189
+
190
+ `TagsInput` and `FileUpload` expose their current values and rejected entries
191
+ through the root's default slot. Their pure rules and rejection reasons live in
192
+ `@lovince/ui-headless`.
193
+
194
+ ```vue
195
+ <script setup lang="ts">
196
+ import {
197
+ LvFileUploadInput,
198
+ LvFileUploadItem,
199
+ LvFileUploadRemove,
200
+ LvFileUploadRoot,
201
+ LvTagsInputInput,
202
+ LvTagsInputItem,
203
+ LvTagsInputRemove,
204
+ LvTagsInputRoot,
205
+ } from '@lovince/ui-vue'
206
+ </script>
207
+
208
+ <LvTagsInputRoot v-model="emails" :max="5" :validate="isEmail" name="emails">
209
+ <LvTagsInputItem v-for="(email, index) in emails" :key="email" :index="index">
210
+ {{ email }} <LvTagsInputRemove />
211
+ </LvTagsInputItem>
212
+ <LvTagsInputInput label="Add email address" />
213
+ </LvTagsInputRoot>
214
+
215
+ <LvFileUploadRoot v-model="files" accept="image/*,.pdf" :max-files="4" :max-size="5000000">
216
+ <LvFileUploadInput />
217
+ <LvFileUploadItem v-for="(file, index) in files" :key="file.name" :index="index">
218
+ <template #default="{ file, size }">
219
+ {{ file.name }} ({{ size }}) <LvFileUploadRemove />
220
+ </template>
221
+ </LvFileUploadItem>
222
+ </LvFileUploadRoot>
223
+ ```
224
+
225
+ Tags are created with Enter or a configured separator; pasted separator-delimited
226
+ text is split, and duplicate, invalid, and over-limit values emit `rejected`.
227
+ File selection and dropping share the same type, per-file size, count, and total
228
+ size checks. `LvFileUploadRoot` emits accepted `File[]` values and rejected files
229
+ with their reason; `LvFileUploadItem` also exposes kind, extension, and localized
230
+ size to its slot.
231
+
232
+ `TimeField` accepts human-entered times and stores normalized `HH:mm` values.
233
+ It validates optional `min` and `max` bounds on blur or Enter, and exposes
234
+ `invalid` when parsing or range validation fails. `PhoneInput` formats a
235
+ national number on blur, recognizes supported international prefixes, and
236
+ emits E.164 values. It includes a country selector and accepts an optional
237
+ custom country list.
238
+
239
+ ```vue
240
+ <LvTimeFieldRoot v-model="appointment" min="09:00" max="17:00" required name="appointment">
241
+ <LvTimeFieldInput label="Appointment time" />
242
+ </LvTimeFieldRoot>
243
+
244
+ <LvPhoneInputRoot v-model="phone" country="KE" name="phone">
245
+ <LvPhoneInputCountrySelect />
246
+ <LvPhoneInputInput label="Mobile number" />
247
+ </LvPhoneInputRoot>
248
+ ```
249
+
250
+ `ScrollArea` keeps scrolling native while exposing custom, keyboard-operable
251
+ scrollbar parts. Size the root from its container, then render the viewport and
252
+ the scrollbar/ thumb parts inside it. The root slot reports current overflow
253
+ metrics; style the track and thumb with their `data-scroll-area-*` attributes.
254
+ The viewport keeps its native scroll behavior and uses `scrollbar-width: none`
255
+ where that property is supported.
256
+
257
+ ```vue
258
+ <LvScrollAreaRoot class="results">
259
+ <LvScrollAreaViewport>
260
+ <div v-for="result in results" :key="result.id">{{ result.title }}</div>
261
+ </LvScrollAreaViewport>
262
+ <LvScrollAreaScrollbar>
263
+ <LvScrollAreaThumb />
264
+ </LvScrollAreaScrollbar>
265
+ </LvScrollAreaRoot>
266
+ ```
49
267
 
50
268
  Composables: `useDataTable`, `useVirtualList`, `useInView`, `useScrollEdges`,
51
269
  `useActiveSection`, `useClipboard`, plus the building blocks below.
52
270
 
53
- The package is `sideEffects: false`. An app importing only `DialogRoot` ships
271
+ The package is `sideEffects: false`. An app importing only `LvDialogRoot` ships
54
272
  no menu, select or table code, and a test enforces this.
55
273
 
56
274
  ## Shared conventions
@@ -1,10 +1,5 @@
1
1
  import { PropType } from 'vue';
2
- /**
3
- * A text input that filters a list of options, per the WAI-ARIA combobox
4
- * pattern with list autocomplete. Focus stays in the input throughout; the
5
- * highlighted option is announced through `aria-activedescendant`.
6
- */
7
- export declare const ComboboxRoot: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
2
+ export declare const LvComboboxRoot: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
8
3
  modelValue: {
9
4
  type: PropType<string | string[]>;
10
5
  default: undefined;
@@ -13,7 +8,6 @@ export declare const ComboboxRoot: import('vue').DefineComponent<import('vue').E
13
8
  type: PropType<string | string[]>;
14
9
  default: undefined;
15
10
  };
16
- /** Pick several: `v-model` is an array, picking toggles, and the list stays open. */
17
11
  multiple: {
18
12
  type: BooleanConstructor;
19
13
  default: boolean;
@@ -38,12 +32,10 @@ export declare const ComboboxRoot: import('vue').DefineComponent<import('vue').E
38
32
  type: BooleanConstructor;
39
33
  default: boolean;
40
34
  };
41
- /** Filter options by the typed text. Turn off when the server filters and you render the results. */
42
35
  filter: {
43
36
  type: BooleanConstructor;
44
37
  default: boolean;
45
38
  };
46
- /** Adds a hidden input so the value submits with a native form. */
47
39
  name: {
48
40
  type: StringConstructor;
49
41
  default: undefined;
@@ -63,7 +55,6 @@ export declare const ComboboxRoot: import('vue').DefineComponent<import('vue').E
63
55
  type: PropType<string | string[]>;
64
56
  default: undefined;
65
57
  };
66
- /** Pick several: `v-model` is an array, picking toggles, and the list stays open. */
67
58
  multiple: {
68
59
  type: BooleanConstructor;
69
60
  default: boolean;
@@ -88,12 +79,10 @@ export declare const ComboboxRoot: import('vue').DefineComponent<import('vue').E
88
79
  type: BooleanConstructor;
89
80
  default: boolean;
90
81
  };
91
- /** Filter options by the typed text. Turn off when the server filters and you render the results. */
92
82
  filter: {
93
83
  type: BooleanConstructor;
94
84
  default: boolean;
95
85
  };
96
- /** Adds a hidden input so the value submits with a native form. */
97
86
  name: {
98
87
  type: StringConstructor;
99
88
  default: undefined;
@@ -114,8 +103,7 @@ export declare const ComboboxRoot: import('vue').DefineComponent<import('vue').E
114
103
  defaultValue: string | string[];
115
104
  searchTerm: string;
116
105
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
117
- /** What the list is positioned against: usually a wrapper round the input and its buttons. */
118
- export declare const ComboboxAnchor: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
106
+ export declare const LvComboboxAnchor: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
119
107
  readonly as: {
120
108
  readonly type: PropType<string | import('vue').Component>;
121
109
  readonly default: "div";
@@ -139,7 +127,7 @@ export declare const ComboboxAnchor: import('vue').DefineComponent<import('vue')
139
127
  readonly as: string | import('vue').Component;
140
128
  readonly asChild: boolean;
141
129
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
142
- export declare const ComboboxInput: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
130
+ export declare const LvComboboxInput: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
143
131
  as: {
144
132
  default: string;
145
133
  type: PropType<string | import('vue').Component>;
@@ -163,8 +151,7 @@ export declare const ComboboxInput: import('vue').DefineComponent<import('vue').
163
151
  as: string | import('vue').Component;
164
152
  asChild: boolean;
165
153
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
166
- /** Toggles the list. Out of the tab order: the input is the one control. */
167
- export declare const ComboboxTrigger: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
154
+ export declare const LvComboboxTrigger: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
168
155
  label: {
169
156
  type: StringConstructor;
170
157
  default: string;
@@ -197,8 +184,7 @@ export declare const ComboboxTrigger: import('vue').DefineComponent<import('vue'
197
184
  asChild: boolean;
198
185
  label: string;
199
186
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
200
- /** Clears the value and the text. Renders nothing while there is nothing to clear. */
201
- export declare const ComboboxClear: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
187
+ export declare const LvComboboxClear: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
202
188
  label: {
203
189
  type: StringConstructor;
204
190
  default: string;
@@ -231,7 +217,7 @@ export declare const ComboboxClear: import('vue').DefineComponent<import('vue').
231
217
  asChild: boolean;
232
218
  label: string;
233
219
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
234
- export declare const ComboboxPortal: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
220
+ export declare const LvComboboxPortal: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
235
221
  to: {
236
222
  type: PropType<string | HTMLElement>;
237
223
  default: string;
@@ -255,7 +241,7 @@ export declare const ComboboxPortal: import('vue').DefineComponent<import('vue')
255
241
  to: string | HTMLElement;
256
242
  disabled: boolean;
257
243
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
258
- export declare const ComboboxContent: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
244
+ export declare const LvComboboxContent: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
259
245
  align: {
260
246
  default: string;
261
247
  type: PropType<import('@lovince/ui-headless').Align>;
@@ -328,7 +314,7 @@ export declare const ComboboxContent: import('vue').DefineComponent<import('vue'
328
314
  collisionPadding: number;
329
315
  avoidCollisions: boolean;
330
316
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
331
- export declare const ComboboxItem: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
317
+ export declare const LvComboboxItem: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
332
318
  value: {
333
319
  type: StringConstructor;
334
320
  required: true;
@@ -337,7 +323,6 @@ export declare const ComboboxItem: import('vue').DefineComponent<import('vue').E
337
323
  type: BooleanConstructor;
338
324
  default: boolean;
339
325
  };
340
- /** Text used for matching and shown in the input; defaults to the item's text. */
341
326
  textValue: {
342
327
  type: StringConstructor;
343
328
  default: undefined;
@@ -361,7 +346,6 @@ export declare const ComboboxItem: import('vue').DefineComponent<import('vue').E
361
346
  type: BooleanConstructor;
362
347
  default: boolean;
363
348
  };
364
- /** Text used for matching and shown in the input; defaults to the item's text. */
365
349
  textValue: {
366
350
  type: StringConstructor;
367
351
  default: undefined;
@@ -380,7 +364,7 @@ export declare const ComboboxItem: import('vue').DefineComponent<import('vue').E
380
364
  disabled: boolean;
381
365
  textValue: string;
382
366
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
383
- export declare const ComboboxItemIndicator: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
367
+ export declare const LvComboboxItemIndicator: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
384
368
  as: {
385
369
  default: string;
386
370
  type: PropType<string | import('vue').Component>;
@@ -404,8 +388,7 @@ export declare const ComboboxItemIndicator: import('vue').DefineComponent<import
404
388
  as: string | import('vue').Component;
405
389
  asChild: boolean;
406
390
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
407
- /** Shown when no option matches. A live region, so the result is announced. */
408
- export declare const ComboboxEmpty: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
391
+ export declare const LvComboboxEmpty: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
409
392
  readonly as: {
410
393
  readonly type: PropType<string | import('vue').Component>;
411
394
  readonly default: "div";
@@ -1,14 +1,9 @@
1
1
  import { PropType } from 'vue';
2
- /**
3
- * A command palette: a search field over a list of actions, filtered as you
4
- * type. Focus stays in the field; Enter runs the highlighted action.
5
- */
6
- export declare const CommandRoot: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
2
+ export declare const LvCommandRoot: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
7
3
  search: {
8
4
  type: StringConstructor;
9
5
  default: undefined;
10
6
  };
11
- /** Filter items by the search. Turn off when you render server results yourself. */
12
7
  filter: {
13
8
  type: BooleanConstructor;
14
9
  default: boolean;
@@ -30,7 +25,6 @@ export declare const CommandRoot: import('vue').DefineComponent<import('vue').Ex
30
25
  type: StringConstructor;
31
26
  default: undefined;
32
27
  };
33
- /** Filter items by the search. Turn off when you render server results yourself. */
34
28
  filter: {
35
29
  type: BooleanConstructor;
36
30
  default: boolean;
@@ -51,7 +45,7 @@ export declare const CommandRoot: import('vue').DefineComponent<import('vue').Ex
51
45
  asChild: boolean;
52
46
  search: string;
53
47
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
54
- export declare const CommandInput: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
48
+ export declare const LvCommandInput: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
55
49
  as: {
56
50
  default: string;
57
51
  type: PropType<string | import('vue').Component>;
@@ -75,7 +69,7 @@ export declare const CommandInput: import('vue').DefineComponent<import('vue').E
75
69
  as: string | import('vue').Component;
76
70
  asChild: boolean;
77
71
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
78
- export declare const CommandList: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
72
+ export declare const LvCommandList: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
79
73
  label: {
80
74
  type: StringConstructor;
81
75
  default: string;
@@ -108,8 +102,7 @@ export declare const CommandList: import('vue').DefineComponent<import('vue').Ex
108
102
  asChild: boolean;
109
103
  label: string;
110
104
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
111
- /** A labelled set of items. Hidden while none of its items match. */
112
- export declare const CommandGroup: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
105
+ export declare const LvCommandGroup: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
113
106
  heading: {
114
107
  type: StringConstructor;
115
108
  default: undefined;
@@ -142,13 +135,11 @@ export declare const CommandGroup: import('vue').DefineComponent<import('vue').E
142
135
  asChild: boolean;
143
136
  heading: string;
144
137
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
145
- export declare const CommandItem: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
146
- /** Passed to `@select`; defaults to the item's text. */
138
+ export declare const LvCommandItem: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
147
139
  value: {
148
140
  type: StringConstructor;
149
141
  default: undefined;
150
142
  };
151
- /** Extra words that should find this item: synonyms, codes, old names. */
152
143
  keywords: {
153
144
  type: PropType<string[]>;
154
145
  default: () => never[];
@@ -170,12 +161,10 @@ export declare const CommandItem: import('vue').DefineComponent<import('vue').Ex
170
161
  }>, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
171
162
  select: (value: string) => boolean;
172
163
  }, string, import('vue').PublicProps, Readonly<import('vue').ExtractPropTypes<{
173
- /** Passed to `@select`; defaults to the item's text. */
174
164
  value: {
175
165
  type: StringConstructor;
176
166
  default: undefined;
177
167
  };
178
- /** Extra words that should find this item: synonyms, codes, old names. */
179
168
  keywords: {
180
169
  type: PropType<string[]>;
181
170
  default: () => never[];
@@ -201,7 +190,7 @@ export declare const CommandItem: import('vue').DefineComponent<import('vue').Ex
201
190
  disabled: boolean;
202
191
  keywords: string[];
203
192
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
204
- export declare const CommandEmpty: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
193
+ export declare const LvCommandEmpty: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
205
194
  readonly as: {
206
195
  readonly type: PropType<string | import('vue').Component>;
207
196
  readonly default: "div";
@@ -225,8 +214,7 @@ export declare const CommandEmpty: import('vue').DefineComponent<import('vue').E
225
214
  readonly as: string | import('vue').Component;
226
215
  readonly asChild: boolean;
227
216
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
228
- /** Hidden while searching, when groups no longer sit in their usual order. */
229
- export declare const CommandSeparator: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
217
+ export declare const LvCommandSeparator: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
230
218
  readonly as: {
231
219
  readonly type: PropType<string | import('vue').Component>;
232
220
  readonly default: "div";
@@ -1,7 +1,7 @@
1
1
  import { PropType } from 'vue';
2
2
  import { Direction, EmojiGroup, EmojiPickerSection, EmojiRecord, SkinTone } from '@lovince/ui-headless';
3
3
  export type { EmojiPickerSection };
4
- export declare const EmojiPickerRoot: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
4
+ export declare const LvEmojiPickerRoot: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
5
5
  emojis: {
6
6
  type: PropType<readonly EmojiRecord[]>;
7
7
  default: () => never[];
@@ -107,7 +107,7 @@ export declare const EmojiPickerRoot: import('vue').DefineComponent<import('vue'
107
107
  maxRecent: number;
108
108
  columns: number;
109
109
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
110
- export declare const EmojiPickerSearch: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
110
+ export declare const LvEmojiPickerSearch: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
111
111
  as: {
112
112
  default: string;
113
113
  type: PropType<string | import('vue').Component>;
@@ -131,7 +131,7 @@ export declare const EmojiPickerSearch: import('vue').DefineComponent<import('vu
131
131
  as: string | import('vue').Component;
132
132
  asChild: boolean;
133
133
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
134
- export declare const EmojiPickerList: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
134
+ export declare const LvEmojiPickerList: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
135
135
  label: {
136
136
  type: StringConstructor;
137
137
  default: string;
@@ -164,7 +164,7 @@ export declare const EmojiPickerList: import('vue').DefineComponent<import('vue'
164
164
  asChild: boolean;
165
165
  label: string;
166
166
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
167
- export declare const EmojiPickerGroup: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
167
+ export declare const LvEmojiPickerGroup: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
168
168
  section: {
169
169
  type: PropType<EmojiPickerSection>;
170
170
  required: true;
@@ -196,7 +196,7 @@ export declare const EmojiPickerGroup: import('vue').DefineComponent<import('vue
196
196
  as: string | import('vue').Component;
197
197
  asChild: boolean;
198
198
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
199
- export declare const EmojiPickerGroupHeading: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
199
+ export declare const LvEmojiPickerGroupHeading: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
200
200
  as: {
201
201
  default: string;
202
202
  type: PropType<string | import('vue').Component>;
@@ -220,7 +220,7 @@ export declare const EmojiPickerGroupHeading: import('vue').DefineComponent<impo
220
220
  as: string | import('vue').Component;
221
221
  asChild: boolean;
222
222
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
223
- export declare const EmojiPickerItem: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
223
+ export declare const LvEmojiPickerItem: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
224
224
  as: {
225
225
  default: string;
226
226
  type: PropType<string | import('vue').Component>;
@@ -260,7 +260,7 @@ export declare const EmojiPickerItem: import('vue').DefineComponent<import('vue'
260
260
  as: string | import('vue').Component;
261
261
  asChild: boolean;
262
262
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
263
- export declare const EmojiPickerNav: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
263
+ export declare const LvEmojiPickerNav: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
264
264
  label: {
265
265
  type: StringConstructor;
266
266
  default: string;
@@ -293,7 +293,7 @@ export declare const EmojiPickerNav: import('vue').DefineComponent<import('vue')
293
293
  asChild: boolean;
294
294
  label: string;
295
295
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
296
- export declare const EmojiPickerNavItem: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
296
+ export declare const LvEmojiPickerNavItem: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
297
297
  section: {
298
298
  type: StringConstructor;
299
299
  required: true;
@@ -333,7 +333,7 @@ export declare const EmojiPickerNavItem: import('vue').DefineComponent<import('v
333
333
  as: string | import('vue').Component;
334
334
  asChild: boolean;
335
335
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
336
- export declare const EmojiPickerEmpty: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
336
+ export declare const LvEmojiPickerEmpty: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
337
337
  readonly as: {
338
338
  readonly type: PropType<string | import('vue').Component>;
339
339
  readonly default: "div";