playground: move pages under /components to match docs

This commit is contained in:
Benjamin Canac
2024-07-09 17:08:16 +02:00
parent eeec9676cd
commit 9d0db51ccc
41 changed files with 2 additions and 2 deletions

View File

@@ -0,0 +1,39 @@
<script setup lang="ts">
const items = [{
label: 'Getting Started',
icon: 'i-heroicons-information-circle',
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed neque elit, tristique placerat feugiat ac, facilisis vitae arcu. Proin eget egestas augue. Praesent ut sem nec arcu pellentesque aliquet. Duis dapibus diam vel metus tempus vulputate.'
}, {
label: 'Installation',
icon: 'i-heroicons-arrow-down-tray',
disabled: true,
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed neque elit, tristique placerat feugiat ac, facilisis vitae arcu. Proin eget egestas augue. Praesent ut sem nec arcu pellentesque aliquet. Duis dapibus diam vel metus tempus vulputate.'
}, {
label: 'Theming',
icon: 'i-heroicons-eye-dropper',
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed neque elit, tristique placerat feugiat ac, facilisis vitae arcu. Proin eget egestas augue. Praesent ut sem nec arcu pellentesque aliquet. Duis dapibus diam vel metus tempus vulputate.'
}, {
label: 'Layouts',
icon: 'i-heroicons-rectangle-group',
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed neque elit, tristique placerat feugiat ac, facilisis vitae arcu. Proin eget egestas augue. Praesent ut sem nec arcu pellentesque aliquet. Duis dapibus diam vel metus tempus vulputate.'
}, {
label: 'Components',
icon: 'i-heroicons-square-3-stack-3d',
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed neque elit, tristique placerat feugiat ac, facilisis vitae arcu. Proin eget egestas augue. Praesent ut sem nec arcu pellentesque aliquet. Duis dapibus diam vel metus tempus vulputate.'
}, {
label: 'Utilities',
slot: 'custom' as const,
icon: 'i-heroicons-wrench-screwdriver',
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed neque elit, tristique placerat feugiat ac, facilisis vitae arcu. Proin eget egestas augue. Praesent ut sem nec arcu pellentesque aliquet. Duis dapibus diam vel metus tempus vulputate.'
}]
</script>
<template>
<UCard :ui="{ body: 'p-0 sm:p-0' }">
<UAccordion :items="items" class="w-96" :ui="{ trigger: 'px-3.5', content: 'px-3.5' }">
<template #custom="{ item }">
<span class="text-gray-500 dark:text-gray-400">Custom: {{ item.content }}</span>
</template>
</UAccordion>
</UCard>
</template>

View File

@@ -0,0 +1,53 @@
<script setup lang="ts">
import theme from '#build/ui/alert'
const variants = Object.keys(theme.variants.variant) as Array<keyof typeof theme.variants.variant>
const actions = (color: string) => [{
label: 'Action',
color: color as any,
click() {
console.log('Action clicked')
}
}]
const data = {
title: 'Heads up!',
description: 'You can add components to your app using the cli.',
icon: 'i-heroicons-command-line',
close: true
}
</script>
<template>
<div class="flex flex-col gap-4 flex-1 items-center">
<div class="flex flex-col gap-2 w-96">
<UAlert :title="data.title" />
<UAlert :title="data.title" :icon="data.icon" />
<UAlert :title="data.title" :icon="data.icon" :close="data.close" />
<UAlert :title="data.title" :icon="data.icon" :close="data.close" :actions="actions('primary')" />
<UAlert :title="data.title" :icon="data.icon" :close="data.close" :description="data.description" />
<UAlert :title="data.title" :avatar="{ src: 'https://avatars.githubusercontent.com/u/739984?v=4' }" :close="data.close" :description="data.description" />
</div>
<div class="flex items-center gap-2">
<UAlert
v-for="variant in variants"
:key="variant"
v-bind="data"
:actions="actions('gray')"
:variant="variant"
/>
</div>
<div class="flex items-center gap-2">
<UAlert
v-for="variant in variants"
:key="variant"
v-bind="data"
:actions="actions('primary')"
color="gray"
:variant="variant"
/>
</div>
</div>
</template>

View File

@@ -0,0 +1,39 @@
<script setup lang="ts">
import theme from '#build/ui/avatar'
const sizes = Object.keys(theme.variants.size) as Array<keyof typeof theme.variants.size>
</script>
<template>
<div class="flex flex-col gap-2 items-center">
<div class="flex items-center gap-1.5">
<UAvatar v-for="size in sizes" :key="size" src="https://avatars.githubusercontent.com/u/739984?v=4" alt="Benjamin Canac" :size="size" />
</div>
<div class="flex items-center gap-1.5">
<UAvatar v-for="size in sizes" :key="size" icon="i-heroicons-photo" :size="size" />
</div>
<div class="flex items-center gap-1.5">
<UAvatar v-for="size in sizes" :key="size" alt="Benjamin Canac" :size="size" />
</div>
<div class="flex items-center gap-1.5">
<UAvatar v-for="size in sizes" :key="size" :text="size" :size="size" />
</div>
<div class="flex items-center gap-1.5">
<UAvatarGroup v-for="size in sizes" :key="size" :text="size" :size="size" :max="2">
<UChip inset text="1">
<UAvatar src="https://avatars.githubusercontent.com/u/739984?v=4" alt="benjamincanac" />
</UChip>
<UAvatar src="https://avatars.githubusercontent.com/u/904724?v=4" alt="Atinux" />
<UAvatar src="https://avatars.githubusercontent.com/u/7547335?v=4" alt="smarroufin" />
</UAvatarGroup>
</div>
<div class="flex items-center gap-1.5">
<UAvatarGroup v-for="size in sizes" :key="size" :text="size" :size="size" :max="4">
<UAvatar src="https://avatars.githubusercontent.com/u/739984?v=4" alt="benjamincanac" />
<UAvatar src="https://avatars.githubusercontent.com/u/904724?v=4" alt="Atinux" />
<UAvatar src="https://avatars.githubusercontent.com/u/7547335?v=4" alt="smarroufin" />
</UAvatarGroup>
</div>
</div>
</template>

View File

@@ -0,0 +1,26 @@
<script setup lang="ts">
import { upperFirst } from 'scule'
import theme from '#build/ui/badge'
const sizes = Object.keys(theme.variants.size) as Array<keyof typeof theme.variants.size>
const variants = Object.keys(theme.variants.variant) as Array<keyof typeof theme.variants.variant>
</script>
<template>
<div class="flex flex-col gap-2">
<div class="flex items-center gap-2">
<UBadge class="font-bold">
Badge
</UBadge>
</div>
<div class="flex items-center gap-2">
<UBadge v-for="variant in variants" :key="variant" :label="upperFirst(variant)" :variant="variant" />
</div>
<div class="flex items-center gap-2">
<UBadge v-for="variant in variants" :key="variant" :label="upperFirst(variant)" :variant="variant" color="gray" />
</div>
<div class="flex items-center gap-2 ml-[-56px]">
<UBadge v-for="size in sizes" :key="size" label="Badge" :size="size" />
</div>
</div>
</template>

View File

@@ -0,0 +1,32 @@
<script setup lang="ts">
const items = [{
label: 'Home',
to: '/'
}, {
slot: 'dropdown' as const,
icon: 'i-heroicons-ellipsis-horizontal',
children: [{
label: 'Documentation'
}, {
label: 'Themes'
}, {
label: 'GitHub'
}]
}, {
label: 'Components',
disabled: true
}, {
label: 'Breadcrumb',
to: '/breadcrumb'
}]
</script>
<template>
<UBreadcrumb :items="items">
<template #dropdown="{ item }">
<UDropdownMenu :items="item.children">
<UButton :icon="item.icon" color="gray" variant="link" class="p-0.5" />
</UDropdownMenu>
</template>
</UBreadcrumb>
</template>

View File

@@ -0,0 +1,61 @@
<script setup lang="ts">
import theme from '#build/ui/button'
const sizes = Object.keys(theme.variants.size) as Array<keyof typeof theme.variants.size>
</script>
<template>
<div class="flex flex-col gap-4 items-center">
<div class="flex flex-col gap-4 -ml-[110px]">
<UButtonGroup>
<UButton>Button</UButton>
</UButtonGroup>
<UButtonGroup>
<UInput placeholder="Search..." />
</UButtonGroup>
<UButtonGroup>
<UButton color="gray" variant="outline">
Button
</UButton>
<UButton color="gray" variant="subtle">
Button
</UButton>
<UButton color="gray" variant="outline">
Button
</UButton>
</UButtonGroup>
<UButtonGroup orientation="vertical">
<UButton color="gray" variant="outline">
Button
</UButton>
<UInput placeholder="Search..." />
</UButtonGroup>
<UButtonGroup>
<UButton color="gray" variant="outline">
Button
</UButton>
<UInput placeholder="Search..." />
</UButtonGroup>
<UButtonGroup>
<UInput placeholder="Search..." />
<UButton color="gray" variant="outline">
Button
</UButton>
</UButtonGroup>
</div>
<div class="flex gap-4 items-center justify-center">
<UButtonGroup v-for="size in sizes" :key="size" :size="size">
<UInput placeholder="Search..." />
<UButton color="gray" variant="outline">
Button
</UButton>
</UButtonGroup>
</div>
</div>
</template>

View File

@@ -0,0 +1,70 @@
<script setup lang="ts">
import { upperFirst } from 'scule'
import theme from '#build/ui/button'
const sizes = Object.keys(theme.variants.size) as Array<keyof typeof theme.variants.size>
const variants = Object.keys(theme.variants.variant) as Array<keyof typeof theme.variants.variant>
</script>
<template>
<div class="flex flex-col gap-2">
<div class="flex items-center gap-2">
<UButton class="font-bold">
Button
</UButton>
</div>
<div class="flex items-center gap-2">
<UButton disabled>
Disabled
</UButton>
</div>
<div class="flex items-center gap-2">
<UButton loading>
Loading
</UButton>
</div>
<div class="flex items-center gap-2">
<UButton loading trailing>
Loading
</UButton>
</div>
<div class="flex items-center gap-2">
<UButton v-for="variant in variants" :key="variant" icon="i-heroicons-rocket-launch" :label="upperFirst(variant)" :variant="variant" />
</div>
<div class="flex items-center gap-2">
<UButton
v-for="variant in variants"
:key="variant"
icon="i-heroicons-rocket-launch"
:label="upperFirst(variant)"
:variant="variant"
color="gray"
/>
</div>
<div class="flex items-center gap-2 ml-[-129px]">
<UButton v-for="size in sizes" :key="size" label="Button" :size="size" />
</div>
<div class="flex items-center gap-2 ml-[-171px]">
<UButton v-for="size in sizes" :key="size" icon="i-heroicons-rocket-launch" label="Button" :size="size" />
</div>
<div class="flex items-center gap-2 ml-[-159px]">
<UButton
v-for="size in sizes"
:key="size"
icon="i-heroicons-rocket-launch"
label="Square"
square
:size="size"
/>
</div>
<div class="flex items-center gap-2 ml-[-68px]">
<UButton v-for="size in sizes" :key="size" icon="i-heroicons-rocket-launch" :size="size" />
</div>
<div class="flex items-center gap-2">
<UButton icon="i-heroicons-rocket-launch" trailing-icon="i-heroicons-chevron-down-20-solid" label="Block" block />
</div>
<div class="flex items-center gap-2">
<UButton icon="i-heroicons-cloud-arrow-down" label="Button" class="group" :ui="{ leadingIcon: 'group-hover:animate-pulse' }" />
</div>
</div>
</template>

View File

@@ -0,0 +1,15 @@
<template>
<div class="flex flex-col gap-4">
<UCard class="w-96">
<template #header>
<Placeholder class="h-8" />
</template>
<Placeholder class="h-32" />
<template #footer>
<Placeholder class="h-8" />
</template>
</UCard>
</div>
</template>

View File

@@ -0,0 +1,28 @@
<script setup lang="ts">
import theme from '#build/ui/checkbox'
const sizes = Object.keys(theme.variants.size) as Array<keyof typeof theme.variants.size>
const checked = ref(true)
</script>
<template>
<div class="flex flex-col items-center gap-4">
<div class="flex flex-col gap-4">
<UCheckbox v-model="checked" label="Primary" />
<UCheckbox label="Gray" color="gray" :default-value="true" />
<UCheckbox label="Error" color="error" :model-value="true" />
<UCheckbox label="Icon" icon="i-heroicons-heart-solid" :model-value="true" />
<UCheckbox label="Default value" :default-value="true" />
<UCheckbox label="Indeterminate" indeterminate />
<UCheckbox label="Required" required />
<UCheckbox label="Disabled" disabled />
</div>
<div class="flex items-center gap-4 mr-[-11px]">
<UCheckbox v-for="size in sizes" :key="size" label="Check me" :size="size" />
</div>
<div class="flex items-center gap-4 mr-[-96px]">
<UCheckbox v-for="size in sizes" :key="size" label="Check me" description="This is a description" :size="size" />
</div>
</div>
</template>

View File

@@ -0,0 +1,38 @@
<script setup lang="ts">
import theme from '#build/ui/chip'
const sizes = Object.keys(theme.variants.size) as Array<keyof typeof theme.variants.size>
const positions = Object.keys(theme.variants.position) as Array<keyof typeof theme.variants.position>
const items = [{
name: 'messages',
icon: 'i-heroicons-chat-bubble-oval-left',
count: 3
}, {
name: 'notifications',
icon: 'i-heroicons-bell',
count: 0
}]
</script>
<template>
<div class="flex flex-col gap-4">
<div class="flex items-center gap-2">
<UChip v-for="position in positions" :key="position" :position="position">
<UButton icon="i-heroicons-inbox" color="gray" variant="subtle" />
</UChip>
</div>
<div class="flex items-center gap-2">
<UChip v-for="{ name, icon, count } in items" :key="name" :text="count" :show="count > 0" size="lg">
<UButton :icon="icon" color="gray" variant="subtle" />
</UChip>
</div>
<div class="flex items-center gap-2">
<UChip v-for="size in sizes" :key="size" :size="size" inset text="1">
<UAvatar src="https://avatars.githubusercontent.com/u/739984?v=4" />
</UChip>
</div>
</div>
</template>

View File

@@ -0,0 +1,24 @@
<script setup lang="ts">
const appConfig = useAppConfig()
</script>
<template>
<div class="flex-1">
<UCollapsible class="space-y-2 w-48">
<UButton
class="group"
icon="i-heroicons-light-bulb"
:trailing-icon="appConfig.ui.icons.chevronDown"
color="gray"
variant="outline"
label="Open"
block
:ui="{ trailingIcon: 'group-data-[state=open]:rotate-180 transition-transform duration-200' }"
/>
<template #content>
<Placeholder class="h-96 w-full" />
</template>
</UCollapsible>
</div>
</template>

View File

@@ -0,0 +1,145 @@
<script setup lang="ts">
// import { createReusableTemplate, refDebounced } from '@vueuse/core'
import { createReusableTemplate } from '@vueuse/core'
import type { User } from '~/types'
const [DefineTemplate, ReuseTemplate] = createReusableTemplate()
const toast = useToast()
const open = ref(false)
const searchTerm = ref('')
// const searchTermDebounced = refDebounced(searchTerm, 200)
const selected = ref([])
const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
// params: { q: searchTermDebounced },
transform: (data: User[]) => {
return data?.map(user => ({ id: user.id, label: user.name, suffix: user.email, avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` } })) || []
},
lazy: true
})
const groups = computed(() => [{
id: 'users',
label: searchTerm.value ? `Users matching “${searchTerm.value}”...` : 'Users',
items: users.value || []
}, {
id: 'actions',
items: [{
label: 'Add new file',
suffix: 'Create a new file in the current directory or workspace.',
icon: 'i-heroicons-document-plus',
select: (e: Event) => {
e?.preventDefault()
toast.add({ title: 'New file added!' })
},
kbds: ['meta', 'N']
}, {
label: 'Add new folder',
suffix: 'Create a new folder in the current directory or workspace.',
icon: 'i-heroicons-folder-plus',
select: (e: Event) => {
e?.preventDefault()
toast.add({ title: 'New folder added!' })
},
kbds: ['meta', 'F']
}, {
label: 'Add hashtag',
suffix: 'Add a hashtag to the current item.',
icon: 'i-heroicons-hashtag',
select: (e: Event) => {
e?.preventDefault()
toast.add({ title: 'Hashtag added!' })
},
kbds: ['meta', 'H']
}, {
label: 'Add label',
suffix: 'Add a label to the current item.',
icon: 'i-heroicons-tag',
select: (e: Event) => {
e?.preventDefault()
toast.add({ title: 'Label added!' })
},
kbds: ['meta', 'L']
}]
}])
const labels = [{
label: 'bug',
chip: {
color: 'red' as const
}
}, {
label: 'feature',
chip: {
color: 'green' as const
}
}, {
label: 'enhancement',
chip: {
color: 'blue' as const
}
}]
const label = ref()
// function onSelect(item: typeof groups.value[number]['items'][number]) {
function onSelect(item: any) {
console.log('Selected', item)
}
defineShortcuts({
meta_k: () => open.value = !open.value,
...extractShortcuts(groups.value)
})
</script>
<template>
<DefineTemplate>
<UCommandPalette
v-model="selected"
v-model:search-term="searchTerm"
:loading="status === 'pending'"
:groups="groups"
:fuse="{
fuseOptions: {
includeMatches: true
}
}"
multiple
class="sm:max-h-80"
@update:model-value="onSelect"
/>
</DefineTemplate>
<div class="flex-1 flex flex-col gap-12 w-full max-w-lg">
<div class="flex items-center justify-between gap-2 mt-[58px]">
<UModal v-model:open="open">
<UButton label="Open modal" color="gray" variant="outline" />
<template #content>
<ReuseTemplate :close="true" @close="open = false" />
</template>
</UModal>
<UDrawer should-scale-background>
<UButton label="Open drawer" color="gray" variant="outline" />
<template #content>
<ReuseTemplate class="border-t border-gray-200 dark:border-gray-800" />
</template>
</UDrawer>
<UPopover :content="{ side: 'right', align: 'start' }">
<UButton label="Select label (popover)" color="gray" variant="outline" />
<template #content>
<UCommandPalette v-model="label" placeholder="Search labels..." :groups="[{ id: 'labels', items: labels }]" :ui="{ input: '[&>input]:h-9' }" />
</template>
</UPopover>
</div>
<UCard :ui="{ body: '!p-0' }">
<ReuseTemplate />
</UCard>
</div>
</template>

View File

@@ -0,0 +1,94 @@
<script setup lang="ts">
import theme from '#build/ui/context-menu'
const items = [
[{
label: 'My account',
avatar: {
src: 'https://avatars.githubusercontent.com/u/739984?v=4'
}
}],
[{
label: 'Appearance',
children: [{
label: 'System',
icon: 'i-heroicons-computer-desktop'
}, {
label: 'Light',
icon: 'i-heroicons-sun'
}, {
label: 'Dark',
icon: 'i-heroicons-moon'
}]
}],
[{
label: 'Show Sidebar',
kbds: ['meta', 'S'],
select() {
console.log('Show Sidebar clicked')
}
}, {
label: 'Show Toolbar',
kbds: ['shift', 'meta', 'D'],
select() {
console.log('Show Toolbar clicked')
}
}, {
label: 'Collapse Pinned Tabs',
disabled: true
}], [{
label: 'Refresh the Page'
}, {
label: 'Clear Cookies and Refresh'
}, {
label: 'Clear Cache and Refresh'
}, {
type: 'separator' as const
}, {
label: 'Developer',
children: [[{
label: 'View Source',
kbds: ['option', 'meta', 'U'],
select() {
console.log('View Source clicked')
}
}, {
label: 'Developer Tools',
kbds: ['option', 'meta', 'I'],
select() {
console.log('Developer Tools clicked')
}
}], [{
label: 'Inspect Elements',
kbds: ['option', 'meta', 'C'],
select() {
console.log('Inspect Elements clicked')
}
}], [{
label: 'JavaScript Console',
kbds: ['option', 'meta', 'J'],
select() {
console.log('JavaScript Console clicked')
}
}]]
}]
]
const sizes = Object.keys(theme.variants.size)
const size = ref('md' as const)
defineShortcuts(extractShortcuts(items))
</script>
<template>
<div class="flex flex-col items-center gap-8">
<USelectMenu v-model="size" :items="sizes" placeholder="Size" />
<UContextMenu :items="items" class="min-w-48" :size="size">
<div class="flex items-center justify-center rounded-md border border-dashed border-gray-300 dark:border-gray-700 text-sm aspect-video w-72">
Right click here
</div>
</UContextMenu>
</div>
</template>

View File

@@ -0,0 +1,28 @@
<script setup lang="ts">
const open = ref(false)
</script>
<template>
<div class="flex flex-col gap-2">
<UDrawer v-model:open="open" title="Drawer with v-model" description="This is useful to control the state yourself.">
<UButton color="gray" variant="outline" label="Open with v-model" />
<template #body>
<Placeholder class="h-48 w-full" />
</template>
<template #footer>
<UButton label="Submit" color="gray" class="justify-center" />
<UButton label="Cancel" color="gray" variant="outline" class="justify-center" @click="open = false" />
</template>
</UDrawer>
<UDrawer should-scale-background title="Drawer with `should-scale-background`" description="You need to add the `vaul-drawer-wrapper` directive to your content to make it work.">
<UButton color="gray" variant="outline" label="Open with scale" />
<template #body>
<Placeholder class="h-screen w-full" />
</template>
</UDrawer>
</div>
</template>

View File

@@ -0,0 +1,138 @@
<script setup lang="ts">
import theme from '#build/ui/dropdown-menu'
const items = [
[{
label: 'My account',
avatar: {
src: 'https://avatars.githubusercontent.com/u/739984?v=4'
},
type: 'label' as const
}],
[{
label: 'Profile',
icon: 'i-heroicons-user',
slot: 'custom' as const,
select(e: Event) {
e.preventDefault()
console.log('Profile clicked')
}
}, {
label: 'Billing',
icon: 'i-heroicons-credit-card',
kbds: ['meta', 'b'],
select() {
console.log('Billing clicked')
}
}, {
label: 'Settings',
icon: 'i-heroicons-cog',
kbds: ['?'],
select() {
console.log('Settings clicked')
}
}], [{
label: 'Team',
icon: 'i-heroicons-users'
}, {
label: 'Invite users',
icon: 'i-heroicons-user-plus',
children: [[{
label: 'Invite by email',
icon: 'i-heroicons-paper-airplane'
}, {
label: 'Invite by link',
icon: 'i-heroicons-link',
kbds: ['meta', 'i'],
select(e: Event) {
e?.preventDefault()
console.log('Invite by link clicked')
}
}], [{
label: 'More',
icon: 'i-heroicons-plus-circle',
children: [{
label: 'Import from Slack',
icon: 'i-simple-icons-slack',
to: 'https://slack.com',
target: '_blank',
select(e: Event) {
e.preventDefault()
console.log('Import from Slack clicked')
}
}, {
label: 'Import from Trello',
icon: 'i-simple-icons-trello',
select(e: Event) {
e.preventDefault()
console.log('Import from Trello clicked')
}
}, {
label: 'Import from Asana',
icon: 'i-simple-icons-asana',
select(e: Event) {
e.preventDefault()
console.log('Import from Asana clicked')
}
}]
}]]
}, {
label: 'New team',
icon: 'i-heroicons-plus',
kbds: ['meta', 'n'],
select() {
console.log('New team clicked')
}
}], [{
label: 'GitHub',
icon: 'i-simple-icons-github',
to: 'https://github.com/nuxt/ui',
target: '_blank',
select(e: Event) {
e.preventDefault()
}
}, {
label: 'Support',
icon: 'i-heroicons-lifebuoy',
to: '/dropdown-menu'
}, {
type: 'separator' as const
}, {
label: 'Keyboard Shortcuts',
icon: 'i-heroicons-key'
}, {
label: 'API',
icon: 'i-heroicons-cube',
disabled: true
}], [{
label: 'Logout',
icon: 'i-heroicons-arrow-right-start-on-rectangle',
kbds: ['shift', 'meta', 'q'],
select() {
console.log('Logout clicked')
}
}]
]
const sizes = Object.keys(theme.variants.size)
const size = ref('md' as const)
defineShortcuts(extractShortcuts(items))
</script>
<template>
<div class="flex-1">
<div class="flex flex-col items-center gap-8">
<USelectMenu v-model="size" :items="sizes" placeholder="Size" />
<UDropdownMenu :items="items" :size="size" arrow :content="{ side: 'bottom', align: 'start' }" class="min-w-48">
<UButton label="Open" color="gray" variant="outline" icon="i-heroicons-user" />
<template #custom-trailing>
<UIcon name="i-heroicons-check-badge" class="shrink-0 size-5 text-primary-500 dark:text-primary-400" />
</template>
</UDropdownMenu>
</div>
</div>
</template>

View File

@@ -0,0 +1,50 @@
<script setup lang="ts">
import theme from '#build/ui/form-field'
const sizes = Object.keys(theme.variants.size) as Array<keyof typeof theme.variants.size>
const feedbacks = [
{ description: 'This is a description' },
{ error: 'This is an error' },
{ hint: 'This is a hint' },
{ help: 'Help! I need somebody!' },
{ required: true }
]
</script>
<template>
<div class="flex flex-col items-center gap-4">
<div class="flex flex-col gap-4 ml-[-38px]">
<div v-for="(feedback, count) in feedbacks" :key="count" class="flex items-center">
<UFormField v-bind="feedback" label="Email" name="email">
<UInput placeholder="john@lennon.com" />
</UFormField>
</div>
</div>
<div class="flex items-center gap-4">
<UFormField
v-for="size in sizes"
:key="size"
:size="size"
label="Email"
name="email"
>
<UInput placeholder="john@lennon.com" />
</UFormField>
</div>
<div class="flex items-center gap-4">
<UFormField
v-for="size in sizes"
:key="size"
:size="size"
label="Email"
description="This is a description"
name="email"
>
<UInput placeholder="john@lennon.com" />
</UFormField>
</div>
</div>
</template>

View File

@@ -0,0 +1,114 @@
<script setup lang="ts">
import { z } from 'zod'
import type { FormSubmitEvent } from '@nuxt/ui'
const schema = z.object({
email: z.string().email(),
password: z.string().min(8),
tos: z.literal(true)
})
type Schema = z.output<typeof schema>
const state = reactive<Partial<Schema>>({})
const state2 = reactive<Partial<Schema>>({})
function onSubmit(event: FormSubmitEvent<Schema>) {
console.log(event.data)
}
</script>
<template>
<div class="flex flex-col gap-4">
<div class="flex gap-4">
<UForm
:state="state"
:schema="schema"
class="gap-4 flex flex-col w-60"
@submit="(event) => onSubmit(event)"
>
<UFormField label="Email" name="email">
<UInput v-model="state.email" placeholder="john@lennon.com" />
</UFormField>
<UFormField label="Password" name="password">
<UInput v-model="state.password" type="password" />
</UFormField>
<UFormField name="tos">
<UCheckbox v-model="state.tos" label="I accept the terms and conditions" />
</UFormField>
<div>
<UButton color="gray" type="submit">
Submit
</UButton>
</div>
</UForm>
<UForm
:state="state2"
:schema="schema"
class="gap-4 flex flex-col w-60"
:validate-on-input-delay="2000"
@submit="(event) => onSubmit(event)"
>
<UFormField label="Email" name="email">
<UInput v-model="state2.email" placeholder="john@lennon.com" />
</UFormField>
<UFormField
label="Password"
name="password"
:validate-on-input-delay="50"
eager-validation
>
<UInput v-model="state2.password" type="password" />
</UFormField>
<div>
<UButton color="gray" type="submit">
Submit
</UButton>
</div>
</UForm>
<FormNestedExample />
<FormNestedListExample />
</div>
<USeparator class="my-8" />
<div class="flex gap-4 flex-wrap">
<div>
<p class="text-lg font-bold underline mb-4">
Validate on input
</p>
<FormElementsExample :validate-on="['input']" />
</div>
<div>
<p class="text-lg font-bold underline mb-4">
Validate on change
</p>
<FormElementsExample :validate-on="['change']" />
</div>
<div>
<p class="text-lg font-bold underline mb-4">
Validate on blur
</p>
<FormElementsExample :validate-on="['blur']" />
</div>
<div>
<p class="text-lg font-bold underline mb-4">
Default
</p>
<FormElementsExample />
</div>
<div>
<p class="text-lg font-bold underline mb-4">
Disabled
</p>
<FormElementsExample disabled />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,147 @@
<script setup lang="ts">
import { upperFirst } from 'scule'
import { refDebounced } from '@vueuse/core'
import type { User } from '~/types'
import theme from '#build/ui/input-menu'
const sizes = Object.keys(theme.variants.size) as Array<keyof typeof theme.variants.size>
const variants = Object.keys(theme.variants.variant) as Array<keyof typeof theme.variants.variant>
const fruits = ['Apple', 'Banana', 'Blueberry', 'Grapes', 'Pineapple']
const vegetables = ['Aubergine', 'Broccoli', 'Carrot', 'Courgette', 'Leek']
const items = [[{ label: 'Fruits', type: 'label' }, ...fruits], [{ label: 'Vegetables', type: 'label' }, ...vegetables]]
const selectedItems = ref([fruits[0], vegetables[0]])
const statuses = [{
label: 'Backlog',
icon: 'i-heroicons-question-mark-circle'
}, {
label: 'Todo',
icon: 'i-heroicons-plus-circle'
}, {
label: 'In Progress',
icon: 'i-heroicons-arrow-up-circle'
}, {
label: 'Done',
icon: 'i-heroicons-check-circle'
}, {
label: 'Canceled',
icon: 'i-heroicons-x-circle'
}]
const searchTerm = ref('')
const searchTermDebounced = refDebounced(searchTerm, 200)
const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
params: { q: searchTermDebounced },
transform: (data: User[]) => {
return data?.map(user => ({ id: user.id, label: user.name, avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` } })) || []
},
lazy: true
})
</script>
<template>
<div class="flex flex-col items-center gap-4">
<div class="flex flex-col gap-4 w-48">
<UInputMenu :items="items" autofocus placeholder="Search..." />
</div>
<div class="flex items-center gap-2">
<UInputMenu
v-for="variant in variants"
:key="variant"
:items="items"
:placeholder="upperFirst(variant)"
:variant="variant"
class="w-48"
/>
</div>
<div class="flex items-center gap-2">
<UInputMenu
v-for="variant in variants"
:key="variant"
:items="items"
:placeholder="upperFirst(variant)"
:variant="variant"
color="gray"
class="w-48"
/>
</div>
<div class="flex items-center gap-2">
<UInputMenu
v-for="variant in variants"
:key="variant"
:items="items"
:placeholder="upperFirst(variant)"
:variant="variant"
color="error"
highlight
class="w-48"
/>
</div>
<div class="flex flex-col gap-4 w-48">
<UInputMenu :items="items" placeholder="Disabled" disabled />
<UInputMenu :items="items" placeholder="Required" required />
<UInputMenu v-model="selectedItems" :items="items" placeholder="Multiple" multiple />
<UInputMenu :items="items" loading placeholder="Search..." />
</div>
<div class="flex items-center gap-4">
<UInputMenu
v-for="size in sizes"
:key="size"
:items="items"
placeholder="Search..."
:size="size"
class="w-48"
/>
</div>
<div class="flex items-center gap-4">
<UInputMenu
v-for="size in sizes"
:key="size"
:items="statuses"
placeholder="Search status..."
icon="i-heroicons-magnifying-glass"
trailing-icon="i-heroicons-chevron-up-down-20-solid"
:size="size"
class="w-48"
>
<template #leading="{ modelValue, ui }">
<UIcon v-if="modelValue?.icon" :name="modelValue.icon" :class="ui.leadingIcon()" />
</template>
</UInputMenu>
</div>
<div class="flex items-center gap-4">
<UInputMenu
v-for="size in sizes"
:key="size"
v-model:search-term="searchTerm"
:items="users || []"
:loading="status === 'pending'"
:filter="false"
icon="i-heroicons-user"
placeholder="Search users..."
:size="size"
class="w-48"
>
<template #leading="{ modelValue, ui }">
<UAvatar v-if="modelValue?.avatar" :size="ui.itemLeadingAvatarSize()" v-bind="modelValue.avatar" />
</template>
</UInputMenu>
</div>
<div class="flex items-center gap-4">
<UInputMenu
v-for="size in sizes"
:key="size"
:items="items"
:model-value="[fruits[0]]"
multiple
icon="i-heroicons-magnifying-glass"
placeholder="Search..."
:size="size"
class="w-48"
/>
</div>
</div>
</template>

View File

@@ -0,0 +1,80 @@
<script setup lang="ts">
import { upperFirst } from 'scule'
import theme from '#build/ui/input'
const sizes = Object.keys(theme.variants.size) as Array<keyof typeof theme.variants.size>
const variants = Object.keys(theme.variants.variant) as Array<keyof typeof theme.variants.variant>
</script>
<template>
<div class="flex flex-col items-center gap-4">
<div class="flex flex-col gap-4 w-48">
<UInput autofocus placeholder="Search..." />
</div>
<div class="flex items-center gap-2">
<UInput v-for="variant in variants" :key="variant" :placeholder="upperFirst(variant)" :variant="variant" class="w-48" />
</div>
<div class="flex items-center gap-2">
<UInput
v-for="variant in variants"
:key="variant"
:placeholder="upperFirst(variant)"
:variant="variant"
color="gray"
class="w-48"
/>
</div>
<div class="flex items-center gap-2">
<UInput
v-for="variant in variants"
:key="variant"
:placeholder="upperFirst(variant)"
:variant="variant"
color="error"
highlight
class="w-48"
/>
</div>
<div class="flex flex-col gap-4 w-48">
<UInput placeholder="Disabled" disabled />
<UInput placeholder="Required" required />
<UInput file="i-heroicons-calculator" type="number" :model-value="10" />
<UInput icon="i-heroicons-folder" type="file" />
<UInput icon="i-heroicons-calendar" type="date" :model-value="new Date().toISOString().substring(0, 10)" />
<UInput icon="i-heroicons-lock-closed" type="password" model-value="password" />
<UInput loading placeholder="Search..." />
<UInput loading trailing placeholder="Search..." />
<UInput loading icon="i-heroicons-magnifying-glass" trailing-icon="i-heroicons-chevron-down" placeholder="Search..." />
</div>
<div class="flex items-center gap-4">
<UInput
v-for="size in sizes"
:key="size"
placeholder="Search..."
:size="size"
class="w-48"
/>
</div>
<div class="flex items-center gap-4">
<UInput
v-for="size in sizes"
:key="size"
icon="i-heroicons-magnifying-glass"
placeholder="Search..."
:size="size"
class="w-48"
/>
</div>
<div class="flex items-center gap-4">
<UInput
v-for="size in sizes"
:key="size"
icon="i-heroicons-magnifying-glass"
trailing
placeholder="Search..."
:size="size"
class="w-48"
/>
</div>
</div>
</template>

View File

@@ -0,0 +1,28 @@
<script setup lang="ts">
import theme from '#build/ui/kbd'
import { kbdKeysMap } from '@nuxt/ui/runtime/composables/useKbd.js'
const sizes = Object.keys(theme.variants.size) as Array<keyof typeof theme.variants.size>
const kbdKeys = Object.keys(kbdKeysMap)
</script>
<template>
<div class="flex flex-col gap-2">
<div class="flex items-center gap-1">
<UKbd value="meta" />
</div>
<div class="flex items-center gap-1">
<UKbd value="meta" variant="subtle" />
</div>
<div class="flex items-center gap-1">
<UKbd value="meta" variant="solid" />
</div>
<div class="flex items-center gap-1 ml-[-216px]">
<UKbd v-for="(kdbKey, index) in kbdKeys" :key="index" :value="kdbKey" />
</div>
<div class="flex items-center gap-1 ml-[-22px]">
<UKbd v-for="size in sizes" :key="size" value="meta" :size="size" />
</div>
</div>
</template>

View File

@@ -0,0 +1,55 @@
<template>
<div class="flex items-center gap-4">
<div class="flex flex-col items-start gap-2 text-sm">
<ULink raw>
Button raw
</ULink>
<ULink active>
Button active
</ULink>
<ULink active class="font-medium" active-class="text-gray-900 dark:text-white">
Button active with class
</ULink>
<ULink active disabled>
Button active disabled
</ULink>
<ULink>
Button inactive
</ULink>
<ULink class="font-medium" inactive-class="hover:text-primary-500 dark:hover:text-primary-400">
Button inactive with class
</ULink>
<ULink disabled>
Button inactive disabled
</ULink>
</div>
<div class="flex flex-col items-start gap-2 text-sm">
<ULink to="/link" raw>
Link raw
</ULink>
<ULink to="/link">
Link active
</ULink>
<ULink to="/link" class="font-medium" active-class="text-gray-900 dark:text-white">
Link active with class
</ULink>
<ULink to="/link" disabled>
Link active disabled
</ULink>
<ULink to="/button">
Link inactive
</ULink>
<ULink to="/button" class="font-medium" inactive-class="hover:text-primary-500 dark:hover:text-primary-400">
Link inactive with class
</ULink>
<ULink to="/button" disabled>
Link inactive disabled
</ULink>
</div>
</div>
</template>

View File

@@ -0,0 +1,69 @@
<script setup lang="ts">
import { LazyModalProgrammaticExample } from '#components'
const open = ref(false)
const count = ref(0)
const modal = useModal()
function openModal() {
count.value++
modal.open(LazyModalProgrammaticExample, {
description: 'And you can even provide a description !',
count: count.value
})
}
</script>
<template>
<div class="flex flex-col gap-2">
<UModal title="First modal">
<UButton color="gray" variant="outline" label="Open with nested" />
<template #footer>
<UModal title="Second modal">
<UButton label="Open second" />
</UModal>
</template>
</UModal>
<UModal v-model:open="open" title="Modal with v-model" description="This can be useful to control the state of the modal yourself." />
<UButton label="Open with v-model" color="gray" variant="subtle" @click="open = true" />
<UModal title="Modal without overlay" description="This modal has `overlay: false` prop." :overlay="false">
<UButton label="Open without overlay" color="gray" variant="outline" />
</UModal>
<UModal title="Modal without modal & overlay" description="This modal has `modal: false` and `overlay: false` to interact with outside content." :overlay="false" :modal="false">
<UButton label="Open without modal" color="gray" variant="subtle" />
</UModal>
<UModal title="Modal without transition" description="This modal has `transition: false` prop." :transition="false">
<UButton label="Open without transition" color="gray" variant="outline" />
</UModal>
<UModal title="Modal without portal" description="This modal has `portal: false` prop." :portal="false">
<UButton label="Open without portal" color="gray" variant="subtle" />
</UModal>
<UModal title="Modal fullscreen" description="This modal has `fullscreen: true` prop." fullscreen>
<UButton label="Open fullscreen" color="gray" variant="outline" />
</UModal>
<UModal title="Modal prevent close" description="This modal has `prevent-close: true` prop so it won't close when clicking outside." prevent-close>
<UButton label="Open unclosable" color="gray" variant="subtle" />
</UModal>
<UModal title="Modal without close button" description="This modal has `close: false` prop." :close="false">
<UButton label="Open without close button" color="gray" variant="outline" />
</UModal>
<UModal title="Modal with custom close button" description="The `close` prop inherits from the Button props." :close="{ color: 'primary', variant: 'solid', size: 'xs' }" :ui="{ close: 'top-3.5 rounded-full' }">
<UButton label="Open with custom close button" color="gray" variant="subtle" />
</UModal>
<UButton label="Open programmatically" color="gray" variant="outline" @click="openModal" />
</div>
</template>

View File

@@ -0,0 +1,108 @@
<script setup lang="ts">
import theme from '#build/ui/navigation-menu'
const colors = Object.keys(theme.variants.color)
const variants = Object.keys(theme.variants.variant)
const orientations = Object.keys(theme.variants.orientation)
const color = ref(theme.defaultVariants.color)
const highlightColor = ref()
const variant = ref(theme.defaultVariants.variant)
const orientation = ref('horizontal' as const)
const highlight = ref(true)
const items = [
[{
label: 'Documentation',
icon: 'i-heroicons-book-open',
badge: 10,
children: [{
label: 'Introduction',
description: 'Fully styled and customizable components for Nuxt.',
icon: 'i-heroicons-home'
}, {
label: 'Installation',
description: 'Learn how to install and configure Nuxt UI in your application.',
icon: 'i-heroicons-cloud-arrow-down'
}, {
label: 'Theming',
description: 'Learn how to customize the look and feel of the components.',
icon: 'i-heroicons-swatch'
}, {
label: 'Shortcuts',
description: 'Learn how to display and define keyboard shortcuts in your app.',
icon: 'i-heroicons-computer-desktop'
}]
}, {
label: 'Components',
icon: 'i-heroicons-cube-transparent',
active: true,
children: [{
label: 'Link',
icon: 'i-heroicons-document',
description: 'Use NuxtLink with superpowers.',
to: '/link'
}, {
label: 'Modal',
icon: 'i-heroicons-document',
description: 'Display a modal within your application.',
to: '/modal'
}, {
label: 'NavigationMenu',
icon: 'i-heroicons-document',
description: 'Display a list of links.',
to: '/navigation-menu'
}, {
label: 'Pagination',
icon: 'i-heroicons-document',
description: 'Display a list of pages.',
to: '/pagination'
}, {
label: 'Popover',
icon: 'i-heroicons-document',
description: 'Display a non-modal dialog that floats around a trigger element.',
to: '/popover'
}, {
label: 'Progress',
icon: 'i-heroicons-document',
description: 'Show a horizontal bar to indicate task progression.',
to: '/progress'
}]
}, {
label: 'GitHub',
icon: 'i-simple-icons-github',
to: 'https://github.com/nuxt/ui',
target: '_blank'
}, {
label: 'Help',
icon: 'i-heroicons-question-mark-circle',
disabled: true
}]
]
</script>
<template>
<div class="flex flex-col items-center gap-12">
<div class="flex items-center gap-2">
<USelect v-model="color" :items="colors" placeholder="Color" />
<USelect v-model="variant" :items="variants" placeholder="Variant" />
<USelect v-model="orientation" :items="orientations" placeholder="Orientation" />
<USwitch v-model="highlight" label="Highlight" />
<USelect v-model="highlightColor" :items="colors" placeholder="Highlight color" />
</div>
<UNavigationMenu
:items="items"
:color="color"
:variant="variant"
:orientation="orientation"
:highlight="highlight"
:highlight-color="highlightColor"
arrow
:class="highlight ? [
'border-gray-200 dark:border-gray-800',
orientation === 'vertical' as any ? 'border-l' : 'border-b'
] : undefined"
/>
</div>
</template>

View File

@@ -0,0 +1,17 @@
<script setup lang="ts">
const page = ref(5)
</script>
<template>
<div class="flex flex-col gap-4">
<div class="flex flex-col gap-2">
<p>With buttons (default)</p>
<UPagination v-model:page="page" :total="100" :sibling-count="1" show-edges />
</div>
<div class="flex flex-col gap-2">
<p>With links</p>
<UPagination v-model:page="page" :total="100" :to="page => ({ query: { page } })" :sibling-count="1" show-edges />
</div>
</div>
</template>

View File

@@ -0,0 +1,92 @@
<script setup lang="ts">
const open = ref(false)
const loading = ref(false)
function send() {
loading.value = true
setTimeout(() => {
loading.value = false
open.value = false
}, 1000)
}
</script>
<template>
<div class="text-center">
<div>
<UPopover arrow :content="{ side: 'top' }">
<UButton label="Click me top" color="gray" variant="outline" />
<template #content>
<div class="w-48 h-16" />
</template>
</UPopover>
<div class="flex items-center gap-2 my-2">
<UPopover arrow :content="{ side: 'left' }">
<UButton label="Click me top" color="gray" variant="outline" />
<template #content>
<div class="w-48 h-16" />
</template>
</UPopover>
<UPopover arrow :content="{ side: 'right' }">
<UButton label="Click me top" color="gray" variant="outline" />
<template #content>
<div class="w-48 h-16" />
</template>
</UPopover>
</div>
<UPopover v-model:open="open" arrow>
<UButton label="Click me bottom" color="gray" variant="outline" />
<template #content>
<div class="flex justify-center gap-2 p-4 w-48">
<UButton label="Close" color="gray" @click="open = false" />
<UButton label="Send" color="gray" trailing-icon="i-heroicons-paper-airplane" :loading="loading" @click="send" />
</div>
</template>
</UPopover>
</div>
<div class="mt-24">
<UPopover mode="hover" arrow :content="{ side: 'top' }">
<UButton label="Hover me top" color="gray" variant="outline" />
<template #content>
<div class="w-48 h-16" />
</template>
</UPopover>
<div class="flex items-center gap-2 my-2">
<UPopover mode="hover" arrow :content="{ side: 'left' }">
<UButton label="Hover me left" color="gray" variant="outline" />
<template #content>
<div class="w-48 h-16" />
</template>
</UPopover>
<UPopover mode="hover" arrow :content="{ side: 'right' }">
<UButton label="Hover me right" color="gray" variant="outline" />
<template #content>
<div class="w-48 h-16" />
</template>
</UPopover>
</div>
<UPopover mode="hover" arrow>
<UButton label="Hover me bottom" color="gray" variant="outline" />
<template #content>
<div class="w-48 h-16" />
</template>
</UPopover>
</div>
</div>
</template>

View File

@@ -0,0 +1,69 @@
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import theme from '#build/ui/progress'
const sizes = Object.keys(theme.variants.size) as Array<keyof typeof theme.variants.size>
const value1 = ref(0)
const value2 = ref(0)
const max = ['Waiting...', 'Cloning...', 'Migrating...', 'Deploying...', 'Done!']
onMounted(() => {
setInterval(() => {
if (value1.value === 100) {
value1.value = 0
return
}
value1.value += 25
}, 1000)
setInterval(() => {
if (value2.value === 4) {
value2.value = 0
return
}
value2.value += 1
}, 1000)
})
</script>
<template>
<div class="flex flex-col gap-8 items-center">
<div class="flex flex-col gap-4 w-48">
<UProgress />
<UProgress color="gray" />
<UProgress color="error" />
<UProgress animation="carousel-inverse" />
<UProgress animation="swing" />
<UProgress animation="elastic" />
<UProgress v-model="value2" :max="max" status />
<UProgress v-model="value2" :max="max" status inverted />
</div>
<div class="flex items-center gap-4">
<UProgress v-for="size in sizes" :key="size" v-model="value1" :size="size" class="w-48" />
</div>
<div class="h-48 flex items-center gap-8">
<UProgress orientation="vertical" />
<UProgress orientation="vertical" animation="carousel-inverse" />
<UProgress orientation="vertical" animation="swing" />
<UProgress orientation="vertical" animation="elastic" />
<UProgress v-model="value2" orientation="vertical" :max="max" status class="w-48 justify-start" />
<UProgress
v-model="value2"
orientation="vertical"
:max="max"
status
inverted
class="w-48 justify-start"
/>
</div>
<div class="h-48 flex items-center gap-8">
<UProgress v-for="size in sizes" :key="size" v-model="value1" orientation="vertical" :size="size" />
</div>
</div>
</template>

View File

@@ -0,0 +1,61 @@
<script setup lang="ts">
import theme from '#build/ui/radio-group'
const sizes = Object.keys(theme.variants.size) as Array<keyof typeof theme.variants.size>
const literalOptions = [
'Option 1',
'Option 2',
'Option 3'
]
const items = [
{ value: '1', label: 'Option 1' },
{ value: '2', label: 'Option 2' },
{ value: '3', label: 'Option 3' }
]
const itemsWithDescription = [
{ value: '1', label: 'Option 1', description: 'Description 1' },
{ value: '2', label: 'Option 2', description: 'Description 2' },
{ value: '3', label: 'Option 3', description: 'Description 3' }
]
</script>
<template>
<div class="flex flex-col items-center gap-4">
<div class="flex flex-col gap-4 ml-[100px]">
<URadioGroup :items="items" default-value="1" />
<URadioGroup :items="items" color="gray" default-value="1" />
<URadioGroup :items="items" color="error" default-value="2" />
<URadioGroup :items="literalOptions" />
<URadioGroup :items="items" label="Disabled" disabled />
<URadioGroup :items="items" orientation="horizontal" class="ml-[-91px]" />
</div>
<div class="flex items-center gap-4 ml-[34px]">
<URadioGroup v-for="size in sizes" :key="size" :size="size" :items="items" />
</div>
<div class="flex items-center gap-4 ml-[74px]">
<URadioGroup v-for="size in sizes" :key="size" :size="size" :items="itemsWithDescription" />
</div>
<div class="flex gap-4">
<URadioGroup :items="items" legend="Legend" />
<URadioGroup :items="items" legend="Legend" required />
<URadioGroup :items="items">
<template #legend>
<span class="italic font-bold">
With slots
</span>
</template>
<template #label="{ item }">
<span class="italic">
{{ item.label }}
</span>
</template>
</URadioGroup>
</div>
<URadioGroup :items="items" legend="Legend" orientation="horizontal" required />
</div>
</template>

View File

@@ -0,0 +1,140 @@
<script setup lang="ts">
import { upperFirst } from 'scule'
import { refDebounced } from '@vueuse/core'
import theme from '#build/ui/select-menu'
import type { User } from '~/types'
const sizes = Object.keys(theme.variants.size) as Array<keyof typeof theme.variants.size>
const variants = Object.keys(theme.variants.variant) as Array<keyof typeof theme.variants.variant>
const fruits = ['Apple', 'Banana', 'Blueberry', 'Grapes', 'Pineapple']
const vegetables = ['Aubergine', 'Broccoli', 'Carrot', 'Courgette', 'Leek']
const items = [[{ label: 'Fruits', type: 'label' }, ...fruits], [{ label: 'Vegetables', type: 'label' }, ...vegetables]]
const selectedItems = ref([fruits[0], vegetables[0]])
const statuses = [{
label: 'Backlog',
value: 'backlog',
icon: 'i-heroicons-question-mark-circle'
}, {
label: 'Todo',
value: 'todo',
icon: 'i-heroicons-plus-circle'
}, {
label: 'In Progress',
value: 'in_progress',
icon: 'i-heroicons-arrow-up-circle'
}, {
label: 'Done',
value: 'done',
icon: 'i-heroicons-check-circle'
}, {
label: 'Canceled',
value: 'canceled',
icon: 'i-heroicons-x-circle'
}]
const searchTerm = ref('')
const searchTermDebounced = refDebounced(searchTerm, 200)
const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
params: { q: searchTermDebounced },
transform: (data: User[]) => {
return data?.map(user => ({ id: user.id, label: user.name, avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` } })) || []
},
lazy: true
})
</script>
<template>
<div class="flex flex-col items-center gap-4">
<div class="flex flex-col gap-4 w-48">
<USelectMenu :items="items" />
</div>
<div class="flex items-center gap-2">
<USelectMenu
v-for="variant in variants"
:key="variant"
:items="items"
:placeholder="upperFirst(variant)"
:variant="variant"
class="w-48"
/>
</div>
<div class="flex items-center gap-2">
<USelectMenu
v-for="variant in variants"
:key="variant"
:items="items"
:placeholder="upperFirst(variant)"
:variant="variant"
color="gray"
class="w-48"
/>
</div>
<div class="flex items-center gap-2">
<USelectMenu
v-for="variant in variants"
:key="variant"
:items="items"
:placeholder="upperFirst(variant)"
:variant="variant"
color="error"
highlight
class="w-48"
/>
</div>
<div class="flex flex-col gap-4 w-48">
<USelectMenu :items="items" placeholder="Disabled" disabled />
<USelectMenu :items="items" placeholder="Required" required />
<USelectMenu v-model="selectedItems" :items="items" placeholder="Multiple" multiple />
<USelectMenu :items="items" loading placeholder="Search..." />
</div>
<div class="flex items-center gap-4">
<USelectMenu
v-for="size in sizes"
:key="size"
:items="items"
placeholder="Search..."
:size="size"
class="w-48"
/>
</div>
<div class="flex items-center gap-4">
<USelectMenu
v-for="size in sizes"
:key="size"
:items="statuses"
placeholder="Search status..."
icon="i-heroicons-magnifying-glass"
trailing-icon="i-heroicons-chevron-up-down-20-solid"
:size="size"
class="w-48"
>
<template #leading="{ modelValue, ui }">
<UIcon v-if="modelValue" :name="modelValue.icon" :class="ui.leadingIcon()" />
</template>
</USelectMenu>
</div>
<div class="flex items-center gap-4">
<USelectMenu
v-for="size in sizes"
:key="size"
v-model:search-term="searchTerm"
:items="users || []"
:loading="status === 'pending'"
:filter="false"
icon="i-heroicons-user"
placeholder="Search users..."
:size="size"
class="w-48"
@update:open="searchTerm = ''"
>
<template #leading="{ modelValue, ui }">
<UAvatar v-if="modelValue?.avatar" :size="ui.itemLeadingAvatarSize()" v-bind="modelValue.avatar" />
</template>
</USelectMenu>
</div>
</div>
</template>

View File

@@ -0,0 +1,138 @@
<script setup lang="ts">
import { upperFirst } from 'scule'
import theme from '#build/ui/select'
import type { User } from '~/types'
const sizes = Object.keys(theme.variants.size) as Array<keyof typeof theme.variants.size>
const variants = Object.keys(theme.variants.variant) as Array<keyof typeof theme.variants.variant>
const fruits = ['Apple', 'Banana', 'Blueberry', 'Grapes', 'Pineapple']
const vegetables = ['Aubergine', 'Broccoli', 'Carrot', 'Courgette', 'Leek']
const items = [[{ label: 'Fruits', type: 'label' }, ...fruits], [{ label: 'Vegetables', type: 'label' }, ...vegetables]]
const statuses = [{
label: 'Backlog',
value: 'backlog',
icon: 'i-heroicons-question-mark-circle'
}, {
label: 'Todo',
value: 'todo',
icon: 'i-heroicons-plus-circle'
}, {
label: 'In Progress',
value: 'in_progress',
icon: 'i-heroicons-arrow-up-circle'
}, {
label: 'Done',
value: 'done',
icon: 'i-heroicons-check-circle'
}, {
label: 'Canceled',
value: 'canceled',
icon: 'i-heroicons-x-circle'
}]
const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
transform: (data: User[]) => {
return data?.map(user => ({ label: user.name, value: String(user.id), avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` } })) || []
},
lazy: true
})
function getStatusIcon(value: string): string {
return statuses.find(status => status.value === value)?.icon || 'i-heroicons-user'
}
function getUserAvatar(value: string) {
return users.value?.find(user => user.value === value)?.avatar || {}
}
</script>
<template>
<div class="flex flex-col items-center gap-4">
<div class="flex flex-col gap-4 w-48">
<USelect :items="items" placeholder="Search..." />
</div>
<div class="flex items-center gap-2">
<USelect
v-for="variant in variants"
:key="variant"
:items="items"
:placeholder="upperFirst(variant)"
:variant="variant"
class="w-48"
/>
</div>
<div class="flex items-center gap-2">
<USelect
v-for="variant in variants"
:key="variant"
:items="items"
:placeholder="upperFirst(variant)"
:variant="variant"
color="gray"
class="w-48"
/>
</div>
<div class="flex items-center gap-2">
<USelect
v-for="variant in variants"
:key="variant"
:items="items"
:placeholder="upperFirst(variant)"
:variant="variant"
color="error"
highlight
class="w-48"
/>
</div>
<div class="flex flex-col gap-4 w-48">
<USelect :items="items" placeholder="Disabled" disabled />
<USelect :items="items" placeholder="Required" required />
<USelect :items="items" loading placeholder="Search..." />
</div>
<div class="flex items-center gap-4">
<USelect
v-for="size in sizes"
:key="size"
:items="items"
placeholder="Search..."
:size="size"
class="w-48"
/>
</div>
<div class="flex items-center gap-4">
<USelect
v-for="size in sizes"
:key="size"
:items="statuses"
placeholder="Search status..."
icon="i-heroicons-magnifying-glass"
trailing-icon="i-heroicons-chevron-up-down-20-solid"
:size="size"
class="w-48"
>
<template #leading="{ modelValue, ui }">
<UIcon v-if="modelValue" :name="getStatusIcon(modelValue)" :class="ui.leadingIcon()" />
</template>
</USelect>
</div>
<div class="flex items-center gap-4">
<USelect
v-for="size in sizes"
:key="size"
:items="users || []"
:loading="status === 'pending'"
icon="i-heroicons-user"
placeholder="Search users..."
:size="size"
class="w-48"
>
<template #leading="{ modelValue, ui }">
<UAvatar v-if="modelValue" :size="ui.itemLeadingAvatarSize()" v-bind="getUserAvatar(modelValue)" />
</template>
</USelect>
</div>
</div>
</template>

View File

@@ -0,0 +1,38 @@
<template>
<div class="flex flex-col gap-4">
<div>
<p class="font-semibold text-gray-900 dark:text-white">
Nuxt UI
</p>
<p>An open-source UI component library.</p>
</div>
<USeparator icon="i-simple-icons-nuxtdotjs" type="dashed" />
<div class="h-24 flex gap-4 items-center">
<div class="flex-1 text-center">
Blog
</div>
<USeparator
:avatar="{ src: 'https://avatars.githubusercontent.com/u/739984?v=4' }"
decorative
orientation="vertical"
/>
<div class="flex-1 text-center">
Docs
</div>
<USeparator decorative orientation="vertical">
<UAvatar size="2xs" src="https://avatars.githubusercontent.com/u/13056429?v=4" />
</USeparator>
<div class="flex-1 text-center">
Source
</div>
</div>
<USeparator label="As simple as it gets" type="dotted" size="lg" color="primary" />
</div>
</template>

View File

@@ -0,0 +1,80 @@
<template>
<div class="w-full flex flex-col gap-4">
<UCard class="flex-1">
<template #header>
<h3 class="font-bold">
Shortcuts
</h3>
</template>
<div class="space-y-2">
<div>
<span>{{ shortcutsState.a.label }} shortcut</span>
<UCheckbox v-model="shortcutsState.a.disabled" :label="`Disable ${shortcutsState.a.label}`" />
<UCheckbox v-model="shortcutsState.a.usingInput" :label="`Using in inputs ${shortcutsState.a.label}`" />
</div>
<div>
<span>{{ shortcutsState.shift_i.label }} shortcut</span>
<UCheckbox v-model="shortcutsState.shift_i.disabled" :label="`Disable ${shortcutsState.shift_i.label}`" />
<UCheckbox v-model="shortcutsState.shift_i.usingInput" :label="`Using in inputs ${shortcutsState.shift_i.label}`" />
</div>
<div>
<span>{{ shortcutsState['g-i'].label }} shortcut</span>
<UCheckbox v-model="shortcutsState['g-i'].disabled" :label="`Disable ${shortcutsState['g-i'].label}`" />
<UCheckbox v-model="shortcutsState['g-i'].usingInput" :label="`Using in inputs ${shortcutsState['g-i'].label}`" />
</div>
<UInput placeholder="Input to focus" />
</div>
</UCard>
<UCard :ui="{ body: 'h-[200px] overflow-y-auto' }" class="flex-1">
<template #header>
<div class="flex items-center justify-between gap-4">
<h3 class="font-bold">
Logs
</h3>
<UButton icon="i-heroicons-trash" size="sm" color="gray" class="-my-1" @click="logs = []" />
</div>
</template>
<p v-for="(log, index) of logs" :key="index">
{{ log }}
</p>
</UCard>
</div>
</template>
<script setup lang="ts">
const logs = ref<string[]>([])
const shortcutsState = ref({
'a': {
label: 'A',
disabled: false,
usingInput: false
},
'shift_i': {
label: 'Shift+I',
disabled: false,
usingInput: false
},
'g-i': {
label: 'G->I',
disabled: false,
usingInput: false
}
})
const shortcuts = computed(() => {
return Object.entries(shortcutsState.value).reduce<ShortcutsConfig>((acc, [key, { label, disabled, usingInput }]) => {
if (disabled) {
return acc
}
acc[key] = {
handler: () => { logs.value.unshift(`"${label}" triggered`) },
usingInput
}
return acc
}, {})
})
defineShortcuts(shortcuts)
</script>

View File

@@ -0,0 +1,10 @@
<template>
<div class="flex items-center gap-4">
<USkeleton class="h-12 w-12 rounded-full" />
<div class="space-y-2">
<USkeleton class="h-4 w-[250px]" />
<USkeleton class="h-4 w-[200px]" />
</div>
</div>
</template>

View File

@@ -0,0 +1,125 @@
<script setup lang="ts">
import { LazySlideoverProgrammaticExample } from '#components'
const open = ref(false)
const count = ref(0)
const slideover = useSlideover()
function openSlideover() {
count.value++
slideover.open(LazySlideoverProgrammaticExample, {
description: 'And you can even provide a description!',
count: count.value
})
}
</script>
<template>
<div class="flex flex-col gap-2">
<USlideover title="First slideover">
<UButton color="gray" variant="outline" label="Open with nested" />
<template #body>
<Placeholder class="h-full w-full" />
</template>
<template #footer>
<USlideover title="Second slideover">
<UButton label="Open second" />
</USlideover>
</template>
</USlideover>
<USlideover title="Slideover on left side" description="This slideover has `side: 'left'` prop." side="left">
<UButton label="Open on left" color="gray" variant="subtle" />
<template #body>
<Placeholder class="h-full w-full" />
</template>
</USlideover>
<USlideover title="Slideover on top side" description="This slideover has `side: 'top'` prop." side="top">
<UButton label="Open on top" color="gray" variant="outline" />
<template #body>
<Placeholder class="h-48 w-full" />
</template>
</USlideover>
<USlideover title="Slideover on bottom side" description="This slideover has `side: 'bottom'` prop." side="bottom">
<UButton label="Open on bottom" color="gray" variant="subtle" />
<template #body>
<Placeholder class="h-48 w-full" />
</template>
</USlideover>
<USlideover v-model:open="open" title="Slideover with v-model" description="This is useful to control the state yourself.">
<template #body>
<Placeholder class="h-full w-full" />
</template>
</USlideover>
<UButton label="Open with v-model" color="gray" variant="outline" @click="open = true" />
<USlideover title="Slideover without overlay" description="This slideover has `overlay: false` prop." :overlay="false">
<UButton label="Open without overlay" color="gray" variant="subtle" />
<template #body>
<Placeholder class="h-full w-full" />
</template>
</USlideover>
<USlideover title="Slideover without modal & overlay" description="This slideover has `modal: false` and `overlay: false` to interact with outside content." :overlay="false" :modal="false">
<UButton label="Open without modal" color="gray" variant="outline" />
<template #body>
<Placeholder class="h-full w-full" />
</template>
</USlideover>
<USlideover title="Slideover without transition" description="This slideover has `transition: false` prop." :transition="false">
<UButton label="Open without transition" color="gray" variant="subtle" />
<template #body>
<Placeholder class="h-full w-full" />
</template>
</USlideover>
<USlideover title="Slideover without portal" description="This slideover has `portal: false` prop." :portal="false">
<UButton label="Open without portal" color="gray" variant="outline" />
<template #body>
<Placeholder class="h-full w-full" />
</template>
</USlideover>
<USlideover title="Slideover prevent close" description="This slideover has `prevent-close: true` prop so it won't close when clicking outside." prevent-close>
<UButton label="Open unclosable" color="gray" variant="subtle" />
<template #body>
<Placeholder class="h-full w-full" />
</template>
</USlideover>
<USlideover title="Slideover without close button" description="This slideover has `close: false` prop." :close="false">
<UButton label="Open without close button" color="gray" variant="outline" />
<template #body>
<Placeholder class="h-full w-full" />
</template>
</USlideover>
<USlideover title="Slideover with custom close button" description="The `close` prop inherits from the Button props." :close="{ color: 'primary', variant: 'solid', size: 'xs' }" :ui="{ close: 'top-3.5 rounded-full' }">
<UButton label="Open with custom close button" color="gray" variant="subtle" />
<template #body>
<Placeholder class="h-full w-full" />
</template>
</USlideover>
<UButton label="Open programmatically" color="gray" variant="outline" @click="openSlideover" />
</div>
</template>

View File

@@ -0,0 +1,38 @@
<script setup lang="ts">
import theme from '#build/ui/slider'
const sizes = Object.keys(theme.variants.size) as Array<keyof typeof theme.variants.size>
const value = ref(50)
</script>
<template>
<div class="flex flex-col gap-6 items-center">
<div class="flex flex-col gap-6 w-48">
<USlider v-model="value" />
<USlider color="gray" :model-value="50" />
<USlider color="error" :model-value="50" />
<USlider :default-value="100" />
<USlider inverted />
<USlider disabled />
<USlider :min="4" :max="12" :step="2" :model-value="6" />
</div>
<div class="flex flex-col gap-6 w-48">
<USlider :default-value="[0, 20]" />
<USlider :model-value="[0, 20]" />
<USlider :model-value="[0, 20, 80]" />
<USlider :model-value="[0, 80]" :min-steps-between-thumbs="20" />
</div>
<div class="flex items-center gap-6">
<USlider v-for="size in sizes" :key="size" v-model="value" :size="size" class="w-48" />
</div>
<div class="h-48 flex items-center gap-6">
<USlider :model-value="[0, 20, 80]" orientation="vertical" />
<USlider v-model="value" orientation="vertical" />
<USlider :model-value="[0, 80]" :min-steps-between-thumbs="20" orientation="vertical" />
</div>
</div>
</template>

View File

@@ -0,0 +1,64 @@
<script setup lang="ts">
import theme from '#build/ui/switch'
const sizes = Object.keys(theme.variants.size) as Array<keyof typeof theme.variants.size>
const checked = ref(true)
</script>
<template>
<div class="flex flex-col items-center gap-4">
<div class="flex flex-col gap-4 ml-[-114px]">
<USwitch v-model="checked" label="Primary" />
<USwitch color="gray" label="Gray" :default-value="true" />
<USwitch color="error" label="Error" :default-value="true" />
<USwitch label="Default value" :default-value="true" />
<USwitch label="Required" required />
<USwitch label="Disabled" disabled />
</div>
<div class="flex items-center gap-4 ml-[-82px]">
<USwitch v-for="size in sizes" :key="size" :size="size" label="Switch me" />
</div>
<div class="flex items-center gap-4 ml-[-82px]">
<USwitch
v-for="size in sizes"
:key="size"
:size="size"
label="Switch me"
unchecked-icon="i-heroicons-x-mark-20-solid"
checked-icon="i-heroicons-check-20-solid"
/>
</div>
<div class="flex items-center gap-4 ml-[-82px]">
<USwitch
v-for="size in sizes"
:key="size"
:size="size"
label="Switch me"
unchecked-icon="i-heroicons-x-mark-20-solid"
checked-icon="i-heroicons-check-20-solid"
loading
/>
</div>
<div class="flex items-center gap-4">
<USwitch
v-for="size in sizes"
:key="size"
:size="size"
label="Switch me"
description="This is a description"
/>
</div>
<div class="flex items-center gap-4">
<USwitch
v-for="size in sizes"
:key="size"
:size="size"
label="Switch me"
description="This is a description"
unchecked-icon="i-heroicons-x-mark-20-solid"
checked-icon="i-heroicons-check-20-solid"
/>
</div>
</div>
</template>

View File

@@ -0,0 +1,65 @@
<script setup lang="ts">
import theme from '#build/ui/tabs'
const colors = Object.keys(theme.variants.color)
const variants = Object.keys(theme.variants.variant)
const orientations = Object.keys(theme.variants.orientation)
const sizes = Object.keys(theme.variants.size)
const color = ref(theme.defaultVariants.color)
const variant = ref(theme.defaultVariants.variant)
const orientation = ref('horizontal' as const)
const size = ref('md' as const)
const items = [{
label: 'Tab1',
avatar: {
src: 'https://avatars.githubusercontent.com/u/739984?v=4'
},
content: 'This is the content shown for Tab1'
}, {
label: 'Tab2',
icon: 'i-heroicons-user',
content: 'And, this is the content for Tab2'
}, {
label: 'Tab3',
icon: 'i-heroicons-bell',
content: 'Finally, this is the content for Tab3',
slot: 'custom' as const
}]
</script>
<template>
<div class="flex flex-col items-center gap-12">
<div class="flex items-center gap-2">
<USelect v-model="color" :items="colors" placeholder="Color" />
<USelect v-model="variant" :items="variants" placeholder="Variant" />
<USelect v-model="orientation" :items="orientations" placeholder="Orientation" />
<USelect v-model="size" :items="sizes" placeholder="Size" />
</div>
<div class="flex gap-4">
<UTabs
:color="color"
:variant="variant"
:orientation="orientation"
:size="size"
:items="[{ label: 'Monthly' }, { label: 'Yearly' }]"
:content="false"
/>
<UTabs
:color="color"
:variant="variant"
:orientation="orientation"
:size="size"
:items="items"
class="w-96"
>
<template #custom="{ item }">
<span class="text-gray-500 dark:text-gray-400">Custom: {{ item.content }}</span>
</template>
</UTabs>
</div>
</div>
</template>

View File

@@ -0,0 +1,46 @@
<script setup lang="ts">
import { upperFirst } from 'scule'
import theme from '#build/ui/textarea'
const sizes = Object.keys(theme.variants.size) as Array<keyof typeof theme.variants.size>
const variants = Object.keys(theme.variants.variant) as Array<keyof typeof theme.variants.variant>
</script>
<template>
<div class="flex flex-col items-center gap-4">
<div class="flex flex-col gap-4 w-48">
<UTextarea autofocus />
</div>
<div class="flex items-center gap-2">
<UTextarea v-for="variant in variants" :key="variant" :placeholder="upperFirst(variant)" :variant="variant" class="w-48" />
</div>
<div class="flex items-center gap-2">
<UTextarea
v-for="variant in variants"
:key="variant"
:placeholder="upperFirst(variant)"
:variant="variant"
color="gray"
class="w-48"
/>
</div>
<div class="flex items-center gap-2">
<UTextarea
v-for="variant in variants"
:key="variant"
:placeholder="upperFirst(variant)"
:variant="variant"
color="error"
highlight
class="w-48"
/>
</div>
<div class="flex flex-col gap-4 w-48">
<UTextarea placeholder="Search..." disabled />
<UTextarea autoresize :maxrows="5" :rows="1" />
</div>
<div class="flex items-center gap-4">
<UTextarea v-for="size in sizes" :key="size" placeholder="Search..." :size="size" class="w-48" />
</div>
</div>
</template>

View File

@@ -0,0 +1,124 @@
<script setup lang="ts">
import theme from '#build/ui/toaster'
const positions = Object.keys(theme.variants.position)
const { toasts, add, update, remove } = useToast()
const appConfig = useAppConfig()
const count = ref(1)
const last = computed(() => toasts.value[toasts.value.length - 1])
const templates = (id: number) => [{
title: 'Toast',
description: `This is the toast ${id}`
}, {
title: `Toast ${id}`
}, {
description: `This is the toast ${id}`
}, {
title: 'Toast',
description: `This is the toast ${id}`,
icon: 'i-heroicons-rocket-launch'
}, {
title: `Toast ${id}`,
icon: 'i-heroicons-rocket-launch'
}, {
description: `This is the toast ${id}`,
icon: 'i-heroicons-rocket-launch'
}, {
title: 'Toast',
description: `This is the toast ${id}`,
avatar: {
src: 'https://avatars.githubusercontent.com/u/739984?v=4'
}
}, {
title: 'Toast',
description: `This is the toast ${id}`,
avatar: {
src: 'https://avatars.githubusercontent.com/u/739984?v=4'
},
actions: [{
label: 'Action',
click() {
console.log(`Toast ${id} action clicked`)
}
}]
}, {
title: `Toast ${id}`,
icon: 'i-heroicons-rocket-launch',
actions: [{
label: 'Action 1',
color: 'gray' as const,
click() {
console.log(`Toast ${id} action 1 clicked`)
}
}, {
label: 'Action 2',
color: 'gray' as const,
variant: 'outline' as const,
click() {
console.log(`Toast ${id} action 2 clicked`)
}
}]
}, {
description: `This is the toast ${id}`,
icon: 'i-heroicons-rocket-launch',
actions: [{
label: 'Action',
variant: 'outline' as const,
click() {
console.log(`Toast ${id} action clicked`)
}
}]
}]
function addToast() {
const id = count.value++
const template = templates(id)[Math.floor(Math.random() * templates(id).length)]
add({
id,
...template,
click(toast) {
console.log(`Toast ${toast.id} clicked`)
}
})
}
function updateToast() {
if (!last.value) {
return
}
update(last.value.id, {
title: 'Toast updated',
description: `This is the updated toast ${count.value++}`
})
}
function removeToast() {
if (!last.value) {
return
}
remove(last.value.id)
}
</script>
<template>
<div class="flex flex-col items-center gap-8">
<div class="flex flex-col gap-2">
<URadioGroup v-model="appConfig.toaster.position" :items="positions" />
<UCheckbox v-model="appConfig.toaster.expand" label="Expand" class="mt-1" />
<UInput v-model="appConfig.toaster.duration" label="Duration" type="number" class="mt-1" />
</div>
<div class="flex items-center gap-2">
<UButton label="Add new" color="gray" variant="outline" @click="addToast" />
<UButton label="Update last" color="gray" variant="outline" @click="updateToast" />
<UButton label="Remove last" color="gray" variant="outline" @click="removeToast" />
</div>
</div>
</template>

View File

@@ -0,0 +1,21 @@
<template>
<div class="flex flex-col">
<UTooltip text="Top" :kbds="['meta', 'T']" :content="{ side: 'top' }" arrow>
<UAvatar text="T" />
</UTooltip>
<div class="flex items-center gap-2 ml-[-20px]">
<UTooltip text="Left" :kbds="['meta', 'L']" :content="{ side: 'left' }" arrow>
<UAvatar text="L" />
</UTooltip>
<UTooltip text="Right" :kbds="['meta', 'R']" :content="{ side: 'right' }" arrow>
<UAvatar text="R" />
</UTooltip>
</div>
<UTooltip text="Bottom" :kbds="['meta', 'B']" arrow>
<UAvatar text="B" />
</UTooltip>
</div>
</template>