@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.
- package/LICENSE +202 -0
- package/README.md +10 -2
- package/dist/accordion.d.ts +561 -0
- package/dist/accordion.js +24 -0
- package/dist/agent.d.ts +2 -0
- package/dist/agent.js +7 -0
- package/dist/alert-dialog.d.ts +231 -0
- package/dist/alert-dialog.js +33 -0
- package/dist/async-options.d.ts +18 -0
- package/dist/async-options.js +40 -0
- package/dist/autocomplete.d.ts +384 -0
- package/dist/autocomplete.js +344 -0
- package/dist/avatar.d.ts +40 -0
- package/dist/avatar.js +28 -0
- package/dist/button.d.ts +52 -0
- package/dist/button.js +43 -0
- package/dist/calendar-context.d.ts +10 -0
- package/dist/calendar-context.js +7 -0
- package/dist/calendar.d.ts +311 -0
- package/dist/calendar.js +349 -0
- package/dist/carousel.d.ts +236 -0
- package/dist/carousel.js +204 -0
- package/dist/checkbox.d.ts +211 -0
- package/dist/checkbox.js +151 -0
- package/dist/children.d.ts +41 -0
- package/dist/children.js +135 -0
- package/dist/collapsible.d.ts +54 -0
- package/dist/collapsible.js +18 -0
- package/dist/color-picker.d.ts +187 -0
- package/dist/color-picker.js +158 -0
- package/dist/color.d.ts +2 -0
- package/dist/color.js +21 -0
- package/dist/combobox.d.ts +517 -0
- package/dist/combobox.js +531 -0
- package/dist/command.d.ts +322 -0
- package/dist/command.js +345 -0
- package/dist/controller-target.d.ts +49 -0
- package/dist/controller-target.js +112 -0
- package/dist/create-parts.d.ts +126 -0
- package/dist/create-parts.js +206 -0
- package/dist/date-input.d.ts +133 -0
- package/dist/date-input.js +31 -0
- package/dist/date-picker.d.ts +328 -0
- package/dist/date-picker.js +354 -0
- package/dist/date.d.ts +2 -0
- package/dist/date.js +59 -0
- package/dist/dialog-parts.d.ts +259 -0
- package/dist/dialog-parts.js +300 -0
- package/dist/dialog.d.ts +251 -0
- package/dist/dialog.js +25 -0
- package/dist/drawer.d.ts +304 -0
- package/dist/drawer.js +373 -0
- package/dist/editor-context.d.ts +4 -0
- package/dist/editor-context.js +5 -0
- package/dist/field-context.d.ts +26 -0
- package/dist/field-context.js +44 -0
- package/dist/field.d.ts +154 -0
- package/dist/field.js +240 -0
- package/dist/file-input.d.ts +212 -0
- package/dist/file-input.js +255 -0
- package/dist/form-inputs.d.ts +16 -0
- package/dist/form-inputs.js +25 -0
- package/dist/gesture.d.ts +1 -0
- package/dist/gesture.js +15 -0
- package/dist/history.d.ts +2 -0
- package/dist/history.js +11 -0
- package/dist/hover-card.d.ts +137 -0
- package/dist/hover-card.js +153 -0
- package/dist/image-cropper.d.ts +74 -0
- package/dist/image-cropper.js +36 -0
- package/dist/image.d.ts +2 -0
- package/dist/image.js +5 -0
- package/dist/index.d.ts +59 -0
- package/dist/index.js +60 -0
- package/dist/input.d.ts +120 -0
- package/dist/input.js +73 -0
- package/dist/item-icon.d.ts +34 -0
- package/dist/item-icon.js +63 -0
- package/dist/label.d.ts +16 -0
- package/dist/label.js +8 -0
- package/dist/lifecycle.d.ts +11 -0
- package/dist/lifecycle.js +91 -0
- package/dist/list-items.d.ts +38 -0
- package/dist/list-items.js +59 -0
- package/dist/list-text.d.ts +7 -0
- package/dist/list-text.js +15 -0
- package/dist/listbox.d.ts +273 -0
- package/dist/listbox.js +126 -0
- package/dist/menu.d.ts +678 -0
- package/dist/menu.js +607 -0
- package/dist/menubar-context.d.ts +5 -0
- package/dist/menubar-context.js +5 -0
- package/dist/menubar.d.ts +61 -0
- package/dist/menubar.js +48 -0
- package/dist/meter.d.ts +41 -0
- package/dist/meter.js +37 -0
- package/dist/navigation-menu.d.ts +228 -0
- package/dist/navigation-menu.js +195 -0
- package/dist/number-input.d.ts +176 -0
- package/dist/number-input.js +231 -0
- package/dist/number.d.ts +2 -0
- package/dist/number.js +39 -0
- package/dist/nuxt/entries.json +977 -0
- package/dist/nuxt/module.d.ts +18 -0
- package/dist/nuxt/module.js +31 -0
- package/dist/nuxt/registrations.d.ts +22 -0
- package/dist/nuxt/registrations.js +22 -0
- package/dist/observe.d.ts +2 -0
- package/dist/observe.js +7 -0
- package/dist/otp-input.d.ts +142 -0
- package/dist/otp-input.js +130 -0
- package/dist/overlays.d.ts +40 -0
- package/dist/overlays.js +118 -0
- package/dist/pagination.d.ts +201 -0
- package/dist/pagination.js +146 -0
- package/dist/password-input.d.ts +144 -0
- package/dist/password-input.js +134 -0
- package/dist/popover.d.ts +211 -0
- package/dist/popover.js +258 -0
- package/dist/popup.d.ts +60 -0
- package/dist/popup.js +89 -0
- package/dist/preferences.d.ts +44 -0
- package/dist/preferences.js +102 -0
- package/dist/primitive.d.ts +2 -0
- package/dist/primitive.js +11 -0
- package/dist/progress.d.ts +41 -0
- package/dist/progress.js +37 -0
- package/dist/radio.d.ts +164 -0
- package/dist/radio.js +145 -0
- package/dist/rating.d.ts +149 -0
- package/dist/rating.js +146 -0
- package/dist/renderless.d.ts +4 -0
- package/dist/renderless.js +28 -0
- package/dist/resizable.d.ts +54 -0
- package/dist/resizable.js +22 -0
- package/dist/scroll-area.d.ts +72 -0
- package/dist/scroll-area.js +77 -0
- package/dist/segmented-control.d.ts +151 -0
- package/dist/segmented-control.js +110 -0
- package/dist/segmented-input.d.ts +76 -0
- package/dist/segmented-input.js +115 -0
- package/dist/select.d.ts +458 -0
- package/dist/select.js +468 -0
- package/dist/separator.d.ts +14 -0
- package/dist/separator.js +8 -0
- package/dist/shortcuts.d.ts +32 -0
- package/dist/shortcuts.js +56 -0
- package/dist/slider.d.ts +65 -0
- package/dist/slider.js +194 -0
- package/dist/switch.d.ts +103 -0
- package/dist/switch.js +71 -0
- package/dist/tabs.d.ts +338 -0
- package/dist/tabs.js +27 -0
- package/dist/text-control.d.ts +89 -0
- package/dist/text-control.js +51 -0
- package/dist/text-match.d.ts +2 -0
- package/dist/text-match.js +7 -0
- package/dist/textarea.d.ts +106 -0
- package/dist/textarea.js +68 -0
- package/dist/time-input.d.ts +131 -0
- package/dist/time-input.js +32 -0
- package/dist/timeline.d.ts +81 -0
- package/dist/timeline.js +30 -0
- package/dist/toast-parts.d.ts +203 -0
- package/dist/toast-parts.js +270 -0
- package/dist/toast.d.ts +2 -0
- package/dist/toast.js +21 -0
- package/dist/toggle-props.d.ts +22 -0
- package/dist/toggle-props.js +14 -0
- package/dist/toggle.d.ts +199 -0
- package/dist/toggle.js +129 -0
- package/dist/token-input.d.ts +136 -0
- package/dist/token-input.js +214 -0
- package/dist/toolbar-context.d.ts +7 -0
- package/dist/toolbar-context.js +20 -0
- package/dist/toolbar.d.ts +120 -0
- package/dist/toolbar.js +77 -0
- package/dist/tooltip.d.ts +160 -0
- package/dist/tooltip.js +224 -0
- package/dist/tree.d.ts +300 -0
- package/dist/tree.js +209 -0
- package/dist/trigger-menu.d.ts +452 -0
- package/dist/trigger-menu.js +254 -0
- package/dist/use-primitive.d.ts +132 -0
- package/dist/use-primitive.js +462 -0
- package/dist/waterfall.d.ts +201 -0
- package/dist/waterfall.js +171 -0
- package/docs/README.md +89 -0
- package/docs/agents.md +1523 -0
- package/docs/components/accordion.md +1369 -0
- package/docs/components/alert-dialog.md +799 -0
- package/docs/components/autocomplete.md +1857 -0
- package/docs/components/avatar.md +272 -0
- package/docs/components/button.md +435 -0
- package/docs/components/calendar.md +1589 -0
- package/docs/components/carousel.md +1171 -0
- package/docs/components/checkbox.md +1008 -0
- package/docs/components/collapsible.md +536 -0
- package/docs/components/color-picker.md +706 -0
- package/docs/components/combobox.md +3504 -0
- package/docs/components/command.md +1481 -0
- package/docs/components/date-input.md +536 -0
- package/docs/components/date-picker.md +1244 -0
- package/docs/components/dialog.md +1397 -0
- package/docs/components/drawer.md +1166 -0
- package/docs/components/field.md +538 -0
- package/docs/components/file-input.md +611 -0
- package/docs/components/hover-card.md +505 -0
- package/docs/components/image-cropper.md +556 -0
- package/docs/components/input.md +306 -0
- package/docs/components/label.md +203 -0
- package/docs/components/listbox.md +1004 -0
- package/docs/components/menu.md +1229 -0
- package/docs/components/menubar.md +443 -0
- package/docs/components/meter.md +315 -0
- package/docs/components/navigation-menu.md +1523 -0
- package/docs/components/number-input.md +1029 -0
- package/docs/components/otp-input.md +512 -0
- package/docs/components/pagination.md +613 -0
- package/docs/components/password-input.md +649 -0
- package/docs/components/popover.md +1448 -0
- package/docs/components/preferences.md +208 -0
- package/docs/components/progress.md +423 -0
- package/docs/components/radio.md +785 -0
- package/docs/components/rating.md +416 -0
- package/docs/components/resizable.md +910 -0
- package/docs/components/scroll-area.md +915 -0
- package/docs/components/segmented-control.md +435 -0
- package/docs/components/select.md +2015 -0
- package/docs/components/separator.md +229 -0
- package/docs/components/slider.md +1084 -0
- package/docs/components/switch.md +565 -0
- package/docs/components/tabs.md +944 -0
- package/docs/components/textarea.md +299 -0
- package/docs/components/time-input.md +224 -0
- package/docs/components/timeline.md +654 -0
- package/docs/components/toast.md +1224 -0
- package/docs/components/toggle.md +500 -0
- package/docs/components/token-input.md +953 -0
- package/docs/components/toolbar.md +739 -0
- package/docs/components/tooltip.md +818 -0
- package/docs/components/tree.md +1306 -0
- package/docs/components/trigger-menu.md +1223 -0
- package/docs/components/waterfall.md +717 -0
- package/docs/controllers.md +71 -0
- package/docs/core.md +8027 -0
- package/docs/custom-parts.md +89 -0
- package/docs/first-component.md +83 -0
- package/docs/forms.md +122 -0
- package/docs/installation.md +72 -0
- package/docs/introduction.md +32 -0
- package/docs/lists-and-menus.md +122 -0
- package/docs/llms.txt +1502 -0
- package/docs/parts-and-composition.md +85 -0
- package/docs/popups.md +108 -0
- package/docs/rendering.md +56 -0
- package/docs/server-rendering.md +180 -0
- package/docs/state-and-events.md +100 -0
- package/docs/styling-and-animation.md +148 -0
- package/docs/utilities/async-options.md +348 -0
- package/docs/utilities/shortcuts.md +159 -0
- package/package.json +352 -3
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Custom Parts
|
|
2
|
+
|
|
3
|
+
Write a part with its own Vue logic and connect it to a primitive.
|
|
4
|
+
|
|
5
|
+
The parts from [Rendering Primitives](./rendering.md) are enough until one needs its own logic. You can replace just that part and keep the same `rating.ts` definition from [Compose Behaviors](./core.md#complete-primitive).
|
|
6
|
+
|
|
7
|
+
## Write a Root Part
|
|
8
|
+
|
|
9
|
+
Here's a replacement for the generated `RatingRoot`. `usePrimitive` creates its instance, and `usePart` connects the `root` slot:
|
|
10
|
+
|
|
11
|
+
```ts title="rating-root.ts"
|
|
12
|
+
import { usePart, usePrimitive, type PartSlots } from "@ui-sdk/vue/primitive"
|
|
13
|
+
import { defineComponent, type HTMLAttributes } from "vue"
|
|
14
|
+
import { rating } from "./rating"
|
|
15
|
+
|
|
16
|
+
const RatingRoot = defineComponent(
|
|
17
|
+
(
|
|
18
|
+
props: { value?: number; defaultValue?: number } & HTMLAttributes,
|
|
19
|
+
{ emit }
|
|
20
|
+
) => {
|
|
21
|
+
const handle = usePrimitive(rating, () => ({
|
|
22
|
+
value: props.value,
|
|
23
|
+
defaultValue: props.defaultValue,
|
|
24
|
+
onValueChange(value, details) {
|
|
25
|
+
emit("value-change", value, details)
|
|
26
|
+
if (!details.canceled) {
|
|
27
|
+
emit("update:value", value)
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
}))
|
|
31
|
+
return usePart(handle, "root", undefined, undefined, "div")
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
inheritAttrs: false,
|
|
35
|
+
props: ["value", "defaultValue"],
|
|
36
|
+
emits: ["value-change", "update:value"],
|
|
37
|
+
slots: Object as PartSlots<typeof rating, "root">,
|
|
38
|
+
}
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
export { RatingRoot }
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Call both helpers in `setup`. The options getter keeps values current; `usePrimitive` also shares the instance with child parts. `usePart` returns a render function that forwards attrs and slots. `inheritAttrs: false` avoids applying attrs twice.
|
|
45
|
+
|
|
46
|
+
`HTMLAttributes` makes native attrs available to TypeScript callers; only `value` and `defaultValue` are declared component props, so the other attrs still reach `usePart`.
|
|
47
|
+
|
|
48
|
+
This root supports `v-model:value` by emitting `update:value` after the change callback has had a chance to cancel. `PartSlots` types the default and `#render` slots. The adapter manages the instance's lifetime.
|
|
49
|
+
|
|
50
|
+
## Connect a Child Part
|
|
51
|
+
|
|
52
|
+
Pass the primitive itself to `usePart` to find the instance shared by the root above it. This `RatingStar` works inside the manual root above or the generated `RatingRoot`:
|
|
53
|
+
|
|
54
|
+
```ts title="rating-star.ts"
|
|
55
|
+
import { usePart, type PartSlots } from "@ui-sdk/vue/primitive"
|
|
56
|
+
import { defineComponent, type ButtonHTMLAttributes } from "vue"
|
|
57
|
+
import { rating } from "./rating"
|
|
58
|
+
|
|
59
|
+
const RatingStar = defineComponent(
|
|
60
|
+
(props: { value: string } & Omit<ButtonHTMLAttributes, "value">) => {
|
|
61
|
+
return usePart(
|
|
62
|
+
rating,
|
|
63
|
+
"star",
|
|
64
|
+
() => ({ value: props.value }),
|
|
65
|
+
undefined,
|
|
66
|
+
"button"
|
|
67
|
+
)
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
inheritAttrs: false,
|
|
71
|
+
props: ["value"],
|
|
72
|
+
slots: Object as PartSlots<typeof rating, "star">,
|
|
73
|
+
}
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
export { RatingStar }
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Use it in place of the generated star in [Render the Rating](./rendering.md#render-the-rating). `ButtonHTMLAttributes` types its native attrs. The `value` getter identifies the star; the remaining props go to its button. Keep it inside `RatingRoot` and give it an `aria-label`.
|
|
80
|
+
|
|
81
|
+
## Merge Props
|
|
82
|
+
|
|
83
|
+
`usePart` merges the component's attrs with the slot's. Slot attributes win, classes and styles combine, and your listeners run first. An `id`, or a name or typing hint you pass, like `aria-label` or `autocomplete`, replaces the slot's. Call `preventDefault()` in a listener to skip the built-in handler.
|
|
84
|
+
|
|
85
|
+
The last argument can be a tag, or an object with `tag`, `children`, and `extend`. Use `extend` for extra props after the slot's props, such as attributes supplied by a Field. Keep the slot's roles, IDs, and ARIA state intact.
|
|
86
|
+
|
|
87
|
+
## Compose a Popup
|
|
88
|
+
|
|
89
|
+
A custom component can also use an existing Popover or Dialog. [Compose Ours](./core.md#compose-ours) shows that connection with an emoji picker.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# First Component
|
|
2
|
+
|
|
3
|
+
Add a component to your app and style its state.
|
|
4
|
+
|
|
5
|
+
Let's add a Bookmark toggle. Press it once to turn it on, then again to turn it off.
|
|
6
|
+
|
|
7
|
+
If you haven't installed the packages yet, start with [Installation](./installation.md).
|
|
8
|
+
|
|
9
|
+
## Create the Component
|
|
10
|
+
|
|
11
|
+
Create `bookmark-button.vue` next to your app component and render a `Toggle`:
|
|
12
|
+
|
|
13
|
+
```vue title="bookmark-button.vue"
|
|
14
|
+
<script setup lang="ts">
|
|
15
|
+
import { Toggle } from "@ui-sdk/vue/toggle"
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<template>
|
|
19
|
+
<Toggle>Bookmark</Toggle>
|
|
20
|
+
</template>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`Toggle` renders a button. It keeps track of its pressed state, handles Enter and Space, and sets `aria-pressed` for screen readers.
|
|
24
|
+
|
|
25
|
+
## Use It in Your App
|
|
26
|
+
|
|
27
|
+
In your app component (`App.vue` here), import and render `BookmarkButton`:
|
|
28
|
+
|
|
29
|
+
```vue title="App.vue"
|
|
30
|
+
<script setup lang="ts">
|
|
31
|
+
import BookmarkButton from "./bookmark-button.vue"
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<template>
|
|
35
|
+
<BookmarkButton />
|
|
36
|
+
</template>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Open your app. You should see a button labeled Bookmark. Next, add styles so you can tell when it is pressed.
|
|
40
|
+
|
|
41
|
+
## Add Styles
|
|
42
|
+
|
|
43
|
+
When the toggle is pressed, it has a `data-pressed` attribute. Use that attribute to change its appearance.
|
|
44
|
+
|
|
45
|
+
With Tailwind CSS configured in your app, add utility classes to `class`. The `data-pressed:` variant applies while the toggle is pressed:
|
|
46
|
+
|
|
47
|
+
```vue title="bookmark-button.vue"
|
|
48
|
+
<script setup lang="ts">
|
|
49
|
+
import { Toggle } from "@ui-sdk/vue/toggle"
|
|
50
|
+
</script>
|
|
51
|
+
|
|
52
|
+
<template>
|
|
53
|
+
<Toggle
|
|
54
|
+
class="inline-flex h-8 items-center rounded-md border border-neutral-300 px-3 text-sm font-medium hover:bg-neutral-100 focus-visible:outline-2 focus-visible:outline-offset-2 data-pressed:bg-neutral-900 data-pressed:text-white"
|
|
55
|
+
>
|
|
56
|
+
Bookmark
|
|
57
|
+
</Toggle>
|
|
58
|
+
</template>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Try the styled toggle:
|
|
62
|
+
|
|
63
|
+
```vue title="first-component.vue"
|
|
64
|
+
<script setup lang="ts">
|
|
65
|
+
import { Toggle } from "@ui-sdk/vue/toggle"
|
|
66
|
+
</script>
|
|
67
|
+
|
|
68
|
+
<template>
|
|
69
|
+
<Toggle data-variant="outline"> Bookmark </Toggle>
|
|
70
|
+
</template>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Click Bookmark to turn it on or off. A filled background means it is on. You can also focus the button with Tab and press Enter or Space.
|
|
74
|
+
|
|
75
|
+
Keep the label the same in both states. `aria-pressed` tells a screen reader whether the toggle is on.
|
|
76
|
+
|
|
77
|
+
## Keep Going
|
|
78
|
+
|
|
79
|
+
- [Toggle](./components/toggle.md) covers disabled states, controlled state, and groups of toggles.
|
|
80
|
+
- [Components](./README.md) lists the other components you can add with the same install.
|
|
81
|
+
- [Parts and Composition](./parts-and-composition.md) shows how to put a component with several parts together.
|
|
82
|
+
- [State and Events](./state-and-events.md) shows how to read changes and control state from your app.
|
|
83
|
+
- [Styling and Animation](./styling-and-animation.md) covers state attributes, CSS variables, and transitions.
|
package/docs/forms.md
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Forms
|
|
2
|
+
|
|
3
|
+
Submit component values, label controls, and show validation errors.
|
|
4
|
+
|
|
5
|
+
Give a control a `name` to include its value in a form. This works for Select and Combobox as well as inputs and checkboxes.
|
|
6
|
+
|
|
7
|
+
## Submit a Value
|
|
8
|
+
|
|
9
|
+
```vue
|
|
10
|
+
<script setup lang="ts">
|
|
11
|
+
import { Checkbox } from "@ui-sdk/vue/checkbox"
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<template>
|
|
15
|
+
<form action="/preferences" method="post">
|
|
16
|
+
<label
|
|
17
|
+
><Checkbox name="updates" value="yes" default-checked /> Email me
|
|
18
|
+
updates</label
|
|
19
|
+
>
|
|
20
|
+
<button type="submit">Save</button>
|
|
21
|
+
<button type="reset">Reset</button>
|
|
22
|
+
</form>
|
|
23
|
+
</template>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The checkbox submits `updates=yes` when checked. Reset restores its default value.
|
|
27
|
+
|
|
28
|
+
## Reset a Controlled Value
|
|
29
|
+
|
|
30
|
+
A reset also goes through `@checked-change` and `v-model`. Keep the binding in place so your ref follows the reset:
|
|
31
|
+
|
|
32
|
+
```vue
|
|
33
|
+
<script setup lang="ts">
|
|
34
|
+
import { Checkbox, type CheckedState } from "@ui-sdk/vue/checkbox"
|
|
35
|
+
import { ref } from "vue"
|
|
36
|
+
|
|
37
|
+
const checked = ref<CheckedState>(true)
|
|
38
|
+
const reason = ref("No change yet")
|
|
39
|
+
</script>
|
|
40
|
+
|
|
41
|
+
<template>
|
|
42
|
+
<form>
|
|
43
|
+
<label>
|
|
44
|
+
<Checkbox
|
|
45
|
+
v-model:checked="checked"
|
|
46
|
+
name="updates"
|
|
47
|
+
default-checked
|
|
48
|
+
@checked-change="(_next, details) => (reason = details.reason)"
|
|
49
|
+
/>
|
|
50
|
+
Email me updates
|
|
51
|
+
</label>
|
|
52
|
+
<button type="reset">Reset</button>
|
|
53
|
+
<output>{{ reason }}</output>
|
|
54
|
+
</form>
|
|
55
|
+
</template>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Uncheck the box, then press Reset. `default-checked` supplies the reset value. The event receives `true` with reason `"reset"`, and `v-model` updates `checked`. The event runs when reset requests a different value; it does not run if the checkbox is already at its default.
|
|
59
|
+
|
|
60
|
+
## Label and Validate a Field
|
|
61
|
+
|
|
62
|
+
`Field` connects a control to its label, description, and error message:
|
|
63
|
+
|
|
64
|
+
```vue
|
|
65
|
+
<script setup lang="ts">
|
|
66
|
+
import {
|
|
67
|
+
Field,
|
|
68
|
+
FieldDescription,
|
|
69
|
+
FieldError,
|
|
70
|
+
FieldLabel,
|
|
71
|
+
} from "@ui-sdk/vue/field"
|
|
72
|
+
import { Input } from "@ui-sdk/vue/input"
|
|
73
|
+
</script>
|
|
74
|
+
|
|
75
|
+
<template>
|
|
76
|
+
<Field name="email">
|
|
77
|
+
<FieldLabel>Email</FieldLabel>
|
|
78
|
+
<Input type="email" required />
|
|
79
|
+
<FieldDescription>We'll only use it for receipts.</FieldDescription>
|
|
80
|
+
<FieldError />
|
|
81
|
+
</Field>
|
|
82
|
+
</template>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
You can use a Textarea, Checkbox, Switch, Radio Group, or Select in place of the Input. [Field](./components/field.md) shows how they connect.
|
|
86
|
+
|
|
87
|
+
On controls that support them, `required`, `disabled`, `read-only`, and `form` follow native conventions. Disabled fields are left out of submission; read-only fields remain focusable and submitted.
|
|
88
|
+
|
|
89
|
+
## Handle a Submission
|
|
90
|
+
|
|
91
|
+
On a normal submit, the browser checks constraints such as `required` and `type="email"` before running your handler.
|
|
92
|
+
|
|
93
|
+
Your server may still reject a valid-looking value. Pass `invalid` to the field and show the server’s message. When the user edits the field, clear the message and return `invalid` to `undefined` so the field uses native validation again. This example expects a `/api/preferences` endpoint:
|
|
94
|
+
|
|
95
|
+
```vue
|
|
96
|
+
<script setup lang="ts">
|
|
97
|
+
import { Field, FieldError, FieldLabel } from "@ui-sdk/vue/field"
|
|
98
|
+
import { Input } from "@ui-sdk/vue/input"
|
|
99
|
+
import { ref } from "vue"
|
|
100
|
+
|
|
101
|
+
const error = ref("")
|
|
102
|
+
|
|
103
|
+
async function save(event: SubmitEvent) {
|
|
104
|
+
const body = new FormData(event.currentTarget as HTMLFormElement)
|
|
105
|
+
const response = await fetch("/api/preferences", { method: "POST", body })
|
|
106
|
+
error.value = response.ok ? "" : "That email is already taken."
|
|
107
|
+
}
|
|
108
|
+
</script>
|
|
109
|
+
|
|
110
|
+
<template>
|
|
111
|
+
<form @submit.prevent="save">
|
|
112
|
+
<Field name="email" :invalid="error ? true : undefined">
|
|
113
|
+
<FieldLabel>Email</FieldLabel>
|
|
114
|
+
<Input type="email" required @value-change="error = ''" />
|
|
115
|
+
<FieldError v-slot="{ state }">{{ error || state.message }}</FieldError>
|
|
116
|
+
</Field>
|
|
117
|
+
<button type="submit">Save</button>
|
|
118
|
+
</form>
|
|
119
|
+
</template>
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Without a handler, the form submits through its `action` URL.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Installation
|
|
2
|
+
|
|
3
|
+
Install UI SDK in your Vue app.
|
|
4
|
+
|
|
5
|
+
Start with an app using Vue 3.5 or newer.
|
|
6
|
+
|
|
7
|
+
## Install the Packages
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @ui-sdk/vue @ui-sdk/core
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
`@ui-sdk/vue` provides the Vue components. `@ui-sdk/core` provides their behavior and is a peer dependency. Install both so the components and any helpers you use share one copy of the core.
|
|
14
|
+
|
|
15
|
+
## Import a Component
|
|
16
|
+
|
|
17
|
+
Import each component from its own entry:
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { Button } from "@ui-sdk/vue/button"
|
|
21
|
+
import { Toggle } from "@ui-sdk/vue/toggle"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
A Dialog has separate parts for its trigger, content, and title. Import those parts from the same entry:
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import {
|
|
28
|
+
Dialog,
|
|
29
|
+
DialogContent,
|
|
30
|
+
DialogTitle,
|
|
31
|
+
DialogTrigger,
|
|
32
|
+
} from "@ui-sdk/vue/dialog"
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Each [component page](./README.md) lists the parts you can use.
|
|
36
|
+
|
|
37
|
+
## Use with Nuxt
|
|
38
|
+
|
|
39
|
+
Install the same two packages in a Nuxt app, then add `@ui-sdk/vue` to its modules:
|
|
40
|
+
|
|
41
|
+
```ts title="nuxt.config.ts"
|
|
42
|
+
export default defineNuxtConfig({
|
|
43
|
+
modules: ["@ui-sdk/vue"],
|
|
44
|
+
})
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Nuxt finds the module at `@ui-sdk/vue/nuxt`. It registers each part of every entry as an auto-imported component, like `<DialogTrigger>`, and auto-imports their other exports, like `createDialogController`, in `<script>`. Each one is still imported from its own entry, so a page bundles only what it uses. When a name clashes, your own component or `utils/` export wins. `confirm` is left out because it would replace the browser's `confirm()` in your code, so import it from `@ui-sdk/vue/alert-dialog`.
|
|
48
|
+
|
|
49
|
+
The module adds no plugin and no styles. Components render on the server and hydrate in the browser without one, so importing them from their entries, as above, works in Nuxt too.
|
|
50
|
+
|
|
51
|
+
Pass `prefix` to use ui-sdk's components beside your own of the same name:
|
|
52
|
+
|
|
53
|
+
```ts title="nuxt.config.ts"
|
|
54
|
+
export default defineNuxtConfig({
|
|
55
|
+
modules: ["@ui-sdk/vue"],
|
|
56
|
+
uiSdk: { prefix: "U" },
|
|
57
|
+
})
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
With that prefix, `<DialogTrigger>` becomes `<UDialogTrigger>`. Other exports keep their names.
|
|
61
|
+
|
|
62
|
+
## Add Your Styles
|
|
63
|
+
|
|
64
|
+
Components are unstyled, so you style them with your app's CSS or with Tailwind CSS. [Styling and Animation](./styling-and-animation.md) covers both.
|
|
65
|
+
|
|
66
|
+
You can render your first component without setting up a provider, importing a UI SDK stylesheet, or adding a portal root. [Server Rendering](./server-rendering.md) covers rendering the initial HTML and adding interactions during hydration, in Nuxt or your own server. If your Nuxt app sends a Content Security Policy, [Content Security Policy](./styling-and-animation.md#content-security-policy) covers nuxt-security.
|
|
67
|
+
|
|
68
|
+
## Right to Left
|
|
69
|
+
|
|
70
|
+
Set `dir="rtl"` on the page or a container. Arrow keys and popup placement follow that direction without a provider. Use logical CSS properties, such as `padding-inline-start`, for your styles.
|
|
71
|
+
|
|
72
|
+
Continue with [First Component](./first-component.md) to add a working toggle to your app.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Introduction
|
|
2
|
+
|
|
3
|
+
Use unstyled Vue components with keyboard, focus, and screen reader support built in.
|
|
4
|
+
|
|
5
|
+
These docs cover ui-sdk in Vue. Using another framework? See React, Svelte, Solid or Web Components. To build your own components from behaviors, see [Core](./core.md).
|
|
6
|
+
|
|
7
|
+
`@ui-sdk/vue` gives you components such as Select, Dialog, and Tabs, built on ui-sdk's behaviors. They handle interactions, focus, and ARIA attributes. You choose their markup and styles.
|
|
8
|
+
|
|
9
|
+
Import the parts you need, put them together in your app, and style them with your own CSS.
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
Copy this prompt into your agent:
|
|
14
|
+
|
|
15
|
+
```text prompt
|
|
16
|
+
Set up ui-sdk in this Vue project. It needs Vue 3.5 or newer.
|
|
17
|
+
|
|
18
|
+
1. Install @ui-sdk/vue and @ui-sdk/core with the project's package manager.
|
|
19
|
+
2. Add a note to AGENTS.md, creating it if needed: before using ui-sdk, follow the ui-sdk skill if it's installed, and read node_modules/@ui-sdk/vue/docs/llms.txt. To build a new component, also search node_modules/@ui-sdk/vue/docs/core.md by heading, and read rendering.md next to it.
|
|
20
|
+
3. If you support skills, run: npx skills add vercel-labs/ui --skill ui-sdk
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
It installs ui-sdk and points your `AGENTS.md` at the docs that ship with the packages.
|
|
24
|
+
|
|
25
|
+
## Set It Up Yourself
|
|
26
|
+
|
|
27
|
+
- [Installation](./installation.md): Install the packages in your Vue app.
|
|
28
|
+
- [First Component](./first-component.md): Add and style your first component.
|
|
29
|
+
|
|
30
|
+
## Explore the Components
|
|
31
|
+
|
|
32
|
+
The [Using Components guides](./parts-and-composition.md) cover patterns the components share, such as composing parts, handling state, and styling.
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Lists and Menus
|
|
2
|
+
|
|
3
|
+
Supply options as data, load them from a server, and define menu actions.
|
|
4
|
+
|
|
5
|
+
Select, Combobox, Autocomplete, Listbox, and Trigger Menu accept items as data. Use this when your choices already come from an array. You can also compose their item parts yourself.
|
|
6
|
+
|
|
7
|
+
## Start with Strings
|
|
8
|
+
|
|
9
|
+
```vue
|
|
10
|
+
<script setup lang="ts">
|
|
11
|
+
import {
|
|
12
|
+
Select,
|
|
13
|
+
SelectContent,
|
|
14
|
+
SelectTrigger,
|
|
15
|
+
SelectValue,
|
|
16
|
+
} from "@ui-sdk/vue/select"
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<template>
|
|
20
|
+
<Select :items="['Apple', 'Banana', 'Pear']">
|
|
21
|
+
<SelectTrigger aria-label="Fruit"
|
|
22
|
+
><SelectValue placeholder="Choose a fruit"
|
|
23
|
+
/></SelectTrigger>
|
|
24
|
+
<SelectContent />
|
|
25
|
+
</Select>
|
|
26
|
+
</template>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Each string is both the value and its label. Use objects when those need to differ:
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
const items = [
|
|
33
|
+
{ value: "apple", label: "Apple" },
|
|
34
|
+
{ value: "banana", label: "Banana", disabled: true },
|
|
35
|
+
]
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Items can have `keywords` for searching. Group them with `{ type: "group", label, items }`, or separate them with `{ type: "separator" }`.
|
|
39
|
+
|
|
40
|
+
For object values, provide `item-to-value` for the submitted string and `item-to-label` for the displayed text. [Select](./components/select.md) and [Combobox](./components/combobox.md) show complete examples.
|
|
41
|
+
|
|
42
|
+
## Style the Items
|
|
43
|
+
|
|
44
|
+
Items as data render bare parts, each with just its label. To style them, render the parts yourself from your array, with your classes right on them. Type the array with `ListData`, so a mistake in it shows up where you write it:
|
|
45
|
+
|
|
46
|
+
```vue
|
|
47
|
+
<script setup lang="ts">
|
|
48
|
+
import {
|
|
49
|
+
Select,
|
|
50
|
+
SelectContent,
|
|
51
|
+
SelectItem,
|
|
52
|
+
SelectTrigger,
|
|
53
|
+
SelectValue,
|
|
54
|
+
type ListData,
|
|
55
|
+
} from "@ui-sdk/vue/select"
|
|
56
|
+
|
|
57
|
+
const fruits = [
|
|
58
|
+
{ value: "apple", label: "Apple" },
|
|
59
|
+
{ value: "banana", label: "Banana" },
|
|
60
|
+
] satisfies ListData
|
|
61
|
+
</script>
|
|
62
|
+
|
|
63
|
+
<template>
|
|
64
|
+
<Select :items="fruits">
|
|
65
|
+
<SelectTrigger aria-label="Fruit" class="…"
|
|
66
|
+
><SelectValue placeholder="Choose a fruit"
|
|
67
|
+
/></SelectTrigger>
|
|
68
|
+
<SelectContent class="…">
|
|
69
|
+
<SelectItem
|
|
70
|
+
v-for="fruit in fruits"
|
|
71
|
+
:key="fruit.value"
|
|
72
|
+
:value="fruit.value"
|
|
73
|
+
class="…"
|
|
74
|
+
>
|
|
75
|
+
{{ fruit.label }}
|
|
76
|
+
</SelectItem>
|
|
77
|
+
</SelectContent>
|
|
78
|
+
</Select>
|
|
79
|
+
</template>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
A list that searches still filters the items you render, and hides the ones that don't match. When the items come from state, like results from a server, the list part's slot gives you the ones to show. Write `<SelectList v-slot="{ state }">` and render `state.items`.
|
|
83
|
+
|
|
84
|
+
## Search and Load Items
|
|
85
|
+
|
|
86
|
+
Searchable components accept a `filter` to change how typing narrows their items. [Async Options](./utilities/async-options.md) handles server results, debouncing, and stale requests.
|
|
87
|
+
|
|
88
|
+
Data lists can virtualize large collections. You don't need to render every item yourself.
|
|
89
|
+
|
|
90
|
+
## Describe a Menu
|
|
91
|
+
|
|
92
|
+
A `MenuData` array describes actions, separators, groups, and submenus. A plain string is an action whose value and label are the string:
|
|
93
|
+
|
|
94
|
+
```vue
|
|
95
|
+
<script setup lang="ts">
|
|
96
|
+
import { Menu, MenuTrigger, type MenuData } from "@ui-sdk/vue/menu"
|
|
97
|
+
|
|
98
|
+
const actions: MenuData = [
|
|
99
|
+
{ value: "open", label: "Open", shortcut: "mod+o" },
|
|
100
|
+
{ type: "separator" },
|
|
101
|
+
{
|
|
102
|
+
type: "submenu",
|
|
103
|
+
label: "Share",
|
|
104
|
+
items: [{ value: "email", label: "Email" }],
|
|
105
|
+
},
|
|
106
|
+
]
|
|
107
|
+
</script>
|
|
108
|
+
|
|
109
|
+
<template>
|
|
110
|
+
<Menu :items="actions" @action="(value: string) => console.log(value)">
|
|
111
|
+
<MenuTrigger>File</MenuTrigger>
|
|
112
|
+
</Menu>
|
|
113
|
+
</template>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
A data menu renders bare parts too. To style it, place a `MenuContent` of your own and render the items in it, as [Menu](./components/menu.md#menus-as-data) shows. That page also covers checkbox items, radio groups, and nested menus.
|
|
117
|
+
|
|
118
|
+
## Keys and Shortcuts
|
|
119
|
+
|
|
120
|
+
`loop` controls whether arrow keys wrap around a list. Components that support `orientation` use it to choose the navigation axis.
|
|
121
|
+
|
|
122
|
+
A trigger's `shortcut="mod+k"` opens it from elsewhere on the page. A data menu runs item shortcuts while closed; a manually rendered MenuItem's shortcut only displays the keys. [Shortcuts](./utilities/shortcuts.md) covers registering your own.
|