mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-21 15:31:46 +01:00
Compare commits
52 Commits
v3.2.0
...
refactor/t
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f14314387a | ||
|
|
8a20b7118d | ||
|
|
08da06a78c | ||
|
|
20fc6bfaad | ||
|
|
c1427a3264 | ||
|
|
2afce33038 | ||
|
|
8e82780df0 | ||
|
|
f81687724e | ||
|
|
6519a74de4 | ||
|
|
dbf19913a2 | ||
|
|
da05c37ffe | ||
|
|
ec569e427b | ||
|
|
1d052ec565 | ||
|
|
faae76e796 | ||
|
|
6ea33aba68 | ||
|
|
1ba8a55bcb | ||
|
|
63730d684b | ||
|
|
9ab184cc24 | ||
|
|
ad0e4ddbf4 | ||
|
|
6a93556aed | ||
|
|
9debce737c | ||
|
|
772631cde9 | ||
|
|
d7aefa53b2 | ||
|
|
8922c7388e | ||
|
|
c355cacd43 | ||
|
|
a0e71d9e29 | ||
|
|
127e06ae83 | ||
|
|
09c1ed8bf4 | ||
|
|
a05102fab3 | ||
|
|
09caf44d0d | ||
|
|
15482aae76 | ||
|
|
f903ec396f | ||
|
|
b00e07f13d | ||
|
|
5c573b37b6 | ||
|
|
f62c5ec20c | ||
|
|
b96a1ccbab | ||
|
|
4ce654076c | ||
|
|
fb9e7bb856 | ||
|
|
69a7b957d5 | ||
|
|
3b67d54833 | ||
|
|
df8f20232f | ||
|
|
347694b4b5 | ||
|
|
021880328b | ||
|
|
9c1f423555 | ||
|
|
6cb737e038 | ||
|
|
231b82fe4c | ||
|
|
57a5037b13 | ||
|
|
752e2b69bd | ||
|
|
6237663a01 | ||
|
|
44cfa00e4d | ||
|
|
8cbbab9a6b | ||
|
|
2d51e20939 |
@@ -147,7 +147,8 @@ const test = ({ name, prose, content }) => {
|
|||||||
? undefined
|
? undefined
|
||||||
: `
|
: `
|
||||||
import { describe, it, expect } from 'vitest'
|
import { describe, it, expect } from 'vitest'
|
||||||
import ${upperName}, { type ${upperName}Props, type ${upperName}Slots } from '../../${content ? '../' : ''}src/runtime/components/${content ? 'content/' : ''}${upperName}.vue'
|
import ${upperName} from '../../${content ? '../' : ''}src/runtime/components/${content ? 'content/' : ''}${upperName}.vue'
|
||||||
|
import type { ${upperName}Props, ${upperName}Slots } from '../../${content ? '../' : ''}src/runtime/components/${content ? 'content/' : ''}${upperName}.vue'
|
||||||
import ComponentRender from '../${content ? '../' : ''}component-render'
|
import ComponentRender from '../${content ? '../' : ''}component-render'
|
||||||
|
|
||||||
describe('${upperName}', () => {
|
describe('${upperName}', () => {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<UBanner
|
<UBanner
|
||||||
id="ui3-launch"
|
id="ui3-launch"
|
||||||
|
title="Nuxt UI v3 is officially released!"
|
||||||
icon="i-lucide-rocket"
|
icon="i-lucide-rocket"
|
||||||
:actions="[
|
:actions="[
|
||||||
{
|
{
|
||||||
@@ -10,9 +11,5 @@
|
|||||||
}
|
}
|
||||||
]"
|
]"
|
||||||
close
|
close
|
||||||
>
|
/>
|
||||||
<template #title>
|
|
||||||
<span class="font-semibold">Nuxt UI v3</span> is officially released.
|
|
||||||
</template>
|
|
||||||
</UBanner>
|
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ const meta = await fetchComponentMeta(name as any)
|
|||||||
</ProseCode>
|
</ProseCode>
|
||||||
</ProseTd>
|
</ProseTd>
|
||||||
<ProseTd>
|
<ProseTd>
|
||||||
<HighlightInlineType v-if="slot.type" :type="slot.type" />
|
<HighlightInlineType v-if="slot.type" :type="slot.type.replace(/ui:\s*\{[^}]*\}/g, 'ui: {}')" />
|
||||||
|
|
||||||
<MDC v-if="slot.description" :value="slot.description" class="text-toned mt-1" :cache-key="`${kebabCase(route.path)}-${slot.name}-description`" />
|
<MDC v-if="slot.description" :value="slot.description" class="text-toned mt-1" :cache-key="`${kebabCase(route.path)}-${slot.name}-description`" />
|
||||||
</ProseTd>
|
</ProseTd>
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const groups = [
|
||||||
|
{
|
||||||
|
id: 'actions',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
label: 'Add new file',
|
||||||
|
suffix: 'Create a new file in the current directory',
|
||||||
|
icon: 'i-lucide-file-plus',
|
||||||
|
kbds: ['meta', 'N']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Add new folder',
|
||||||
|
suffix: 'Create a new folder in the current directory',
|
||||||
|
icon: 'i-lucide-folder-plus',
|
||||||
|
kbds: ['meta', 'F']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Search files',
|
||||||
|
suffix: 'Search across all files in the project',
|
||||||
|
icon: 'i-lucide-search',
|
||||||
|
kbds: ['meta', 'P']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Settings',
|
||||||
|
suffix: 'Open application settings',
|
||||||
|
icon: 'i-lucide-settings',
|
||||||
|
kbds: ['meta', ',']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'recent',
|
||||||
|
label: 'Recent',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
label: 'project.vue',
|
||||||
|
suffix: 'components/',
|
||||||
|
icon: 'i-vscode-icons-file-type-vue'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'readme.md',
|
||||||
|
suffix: 'docs/',
|
||||||
|
icon: 'i-vscode-icons-file-type-markdown'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'package.json',
|
||||||
|
suffix: 'root/',
|
||||||
|
icon: 'i-vscode-icons-file-type-node'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UCommandPalette :groups="groups" class="flex-1 h-80">
|
||||||
|
<template #footer>
|
||||||
|
<div class="flex items-center justify-between gap-2">
|
||||||
|
<UIcon name="i-simple-icons-nuxtdotjs" class="size-5 text-dimmed ml-1" />
|
||||||
|
<div class="flex items-center gap-1">
|
||||||
|
<UButton color="neutral" variant="ghost" label="Open Command" class="text-dimmed" size="xs">
|
||||||
|
<template #trailing>
|
||||||
|
<UKbd value="enter" />
|
||||||
|
</template>
|
||||||
|
</UButton>
|
||||||
|
<USeparator orientation="vertical" class="h-4" />
|
||||||
|
<UButton color="neutral" variant="ghost" label="Actions" class="text-dimmed" size="xs">
|
||||||
|
<template #trailing>
|
||||||
|
<UKbd value="meta" />
|
||||||
|
<UKbd value="k" />
|
||||||
|
</template>
|
||||||
|
</UButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</UCommandPalette>
|
||||||
|
</template>
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { object, string, nonempty, refine, type Infer } from 'superstruct'
|
import { object, string, nonempty, refine } from 'superstruct'
|
||||||
|
import type { Infer } from 'superstruct'
|
||||||
import type { FormSubmitEvent } from '@nuxt/ui'
|
import type { FormSubmitEvent } from '@nuxt/ui'
|
||||||
|
|
||||||
const schema = object({
|
const schema = object({
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { object, string, type InferType } from 'yup'
|
import { object, string } from 'yup'
|
||||||
|
import type { InferType } from 'yup'
|
||||||
import type { FormSubmitEvent } from '@nuxt/ui'
|
import type { FormSubmitEvent } from '@nuxt/ui'
|
||||||
|
|
||||||
const schema = object({
|
const schema = object({
|
||||||
|
|||||||
@@ -24,3 +24,10 @@ const password = ref('')
|
|||||||
</template>
|
</template>
|
||||||
</UInput>
|
</UInput>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* Hide the password reveal button in Edge */
|
||||||
|
::-ms-reveal {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const open = ref(false)
|
||||||
|
const anchor = ref({ x: 0, y: 0 })
|
||||||
|
|
||||||
|
const reference = computed(() => ({
|
||||||
|
getBoundingClientRect: () =>
|
||||||
|
({
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
left: anchor.value.x,
|
||||||
|
right: anchor.value.x,
|
||||||
|
top: anchor.value.y,
|
||||||
|
bottom: anchor.value.y,
|
||||||
|
...anchor.value
|
||||||
|
} as DOMRect)
|
||||||
|
}))
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UPopover
|
||||||
|
:open="open"
|
||||||
|
:reference="reference"
|
||||||
|
:content="{ side: 'top', sideOffset: 16, updatePositionStrategy: 'always' }"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="flex items-center justify-center rounded-md border border-dashed border-accented text-sm aspect-video w-72"
|
||||||
|
@pointerenter="open = true"
|
||||||
|
@pointerleave="open = false"
|
||||||
|
@pointermove="(ev) => {
|
||||||
|
anchor.x = ev.clientX
|
||||||
|
anchor.y = ev.clientY
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
Hover me
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template #content>
|
||||||
|
<div class="p-4">
|
||||||
|
{{ anchor.x.toFixed(0) }} - {{ anchor.y.toFixed(0) }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</UPopover>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { h, resolveComponent } from 'vue'
|
||||||
|
import type { TableColumn, TableRow } from '@nuxt/ui'
|
||||||
|
|
||||||
|
const UBadge = resolveComponent('UBadge')
|
||||||
|
|
||||||
|
type Payment = {
|
||||||
|
id: string
|
||||||
|
date: string
|
||||||
|
status: 'paid' | 'failed' | 'refunded'
|
||||||
|
email: string
|
||||||
|
amount: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = ref<Payment[]>([{
|
||||||
|
id: '4600',
|
||||||
|
date: '2024-03-11T15:30:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'james.anderson@example.com',
|
||||||
|
amount: 594
|
||||||
|
}, {
|
||||||
|
id: '4599',
|
||||||
|
date: '2024-03-11T10:10:00',
|
||||||
|
status: 'failed',
|
||||||
|
email: 'mia.white@example.com',
|
||||||
|
amount: 276
|
||||||
|
}, {
|
||||||
|
id: '4598',
|
||||||
|
date: '2024-03-11T08:50:00',
|
||||||
|
status: 'refunded',
|
||||||
|
email: 'william.brown@example.com',
|
||||||
|
amount: 315
|
||||||
|
}, {
|
||||||
|
id: '4597',
|
||||||
|
date: '2024-03-10T19:45:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'emma.davis@example.com',
|
||||||
|
amount: 529
|
||||||
|
}, {
|
||||||
|
id: '4596',
|
||||||
|
date: '2024-03-10T15:55:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'ethan.harris@example.com',
|
||||||
|
amount: 639
|
||||||
|
}])
|
||||||
|
|
||||||
|
const columns: TableColumn<Payment>[] = [{
|
||||||
|
accessorKey: 'id',
|
||||||
|
header: '#',
|
||||||
|
cell: ({ row }) => `#${row.getValue('id')}`
|
||||||
|
}, {
|
||||||
|
accessorKey: 'date',
|
||||||
|
header: 'Date',
|
||||||
|
cell: ({ row }) => {
|
||||||
|
return new Date(row.getValue('date')).toLocaleString('en-US', {
|
||||||
|
day: 'numeric',
|
||||||
|
month: 'short',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
hour12: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
accessorKey: 'status',
|
||||||
|
header: 'Status',
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const color = ({
|
||||||
|
paid: 'success' as const,
|
||||||
|
failed: 'error' as const,
|
||||||
|
refunded: 'neutral' as const
|
||||||
|
})[row.getValue('status') as string]
|
||||||
|
|
||||||
|
return h(UBadge, { class: 'capitalize', variant: 'subtle', color }, () => row.getValue('status'))
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
accessorKey: 'email',
|
||||||
|
header: 'Email'
|
||||||
|
}, {
|
||||||
|
accessorKey: 'amount',
|
||||||
|
header: () => h('div', { class: 'text-right' }, 'Amount'),
|
||||||
|
footer: ({ column }) => {
|
||||||
|
const total = column.getFacetedRowModel().rows.reduce((acc: number, row: TableRow<Payment>) => acc + Number.parseFloat(row.getValue('amount')), 0)
|
||||||
|
|
||||||
|
const formatted = new Intl.NumberFormat('en-US', {
|
||||||
|
style: 'currency',
|
||||||
|
currency: 'EUR'
|
||||||
|
}).format(total)
|
||||||
|
|
||||||
|
return h('div', { class: 'text-right font-medium' }, `Total: ${formatted}`)
|
||||||
|
},
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const amount = Number.parseFloat(row.getValue('amount'))
|
||||||
|
|
||||||
|
const formatted = new Intl.NumberFormat('en-US', {
|
||||||
|
style: 'currency',
|
||||||
|
currency: 'EUR'
|
||||||
|
}).format(amount)
|
||||||
|
|
||||||
|
return h('div', { class: 'text-right font-medium' }, formatted)
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UTable :data="data" :columns="columns" class="flex-1" />
|
||||||
|
</template>
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { h, resolveComponent } from 'vue'
|
import { h, resolveComponent } from 'vue'
|
||||||
import type { TableColumn } from '@nuxt/ui'
|
import type { TableColumn } from '@nuxt/ui'
|
||||||
import { getGroupedRowModel, type GroupingOptions } from '@tanstack/vue-table'
|
import { getGroupedRowModel } from '@tanstack/vue-table'
|
||||||
|
import type { GroupingOptions } from '@tanstack/vue-table'
|
||||||
|
|
||||||
const UBadge = resolveComponent('UBadge')
|
const UBadge = resolveComponent('UBadge')
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,159 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { h, resolveComponent } from 'vue'
|
||||||
|
import type { ContextMenuItem, TableColumn, TableRow } from '@nuxt/ui'
|
||||||
|
import { useClipboard } from '@vueuse/core'
|
||||||
|
|
||||||
|
const UBadge = resolveComponent('UBadge')
|
||||||
|
const UCheckbox = resolveComponent('UCheckbox')
|
||||||
|
|
||||||
|
const toast = useToast()
|
||||||
|
const { copy } = useClipboard()
|
||||||
|
|
||||||
|
type Payment = {
|
||||||
|
id: string
|
||||||
|
date: string
|
||||||
|
status: 'paid' | 'failed' | 'refunded'
|
||||||
|
email: string
|
||||||
|
amount: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = ref<Payment[]>([{
|
||||||
|
id: '4600',
|
||||||
|
date: '2024-03-11T15:30:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'james.anderson@example.com',
|
||||||
|
amount: 594
|
||||||
|
}, {
|
||||||
|
id: '4599',
|
||||||
|
date: '2024-03-11T10:10:00',
|
||||||
|
status: 'failed',
|
||||||
|
email: 'mia.white@example.com',
|
||||||
|
amount: 276
|
||||||
|
}, {
|
||||||
|
id: '4598',
|
||||||
|
date: '2024-03-11T08:50:00',
|
||||||
|
status: 'refunded',
|
||||||
|
email: 'william.brown@example.com',
|
||||||
|
amount: 315
|
||||||
|
}, {
|
||||||
|
id: '4597',
|
||||||
|
date: '2024-03-10T19:45:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'emma.davis@example.com',
|
||||||
|
amount: 529
|
||||||
|
}, {
|
||||||
|
id: '4596',
|
||||||
|
date: '2024-03-10T15:55:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'ethan.harris@example.com',
|
||||||
|
amount: 639
|
||||||
|
}])
|
||||||
|
|
||||||
|
const columns: TableColumn<Payment>[] = [{
|
||||||
|
id: 'select',
|
||||||
|
header: ({ table }) => h(UCheckbox, {
|
||||||
|
'modelValue': table.getIsSomePageRowsSelected() ? 'indeterminate' : table.getIsAllPageRowsSelected(),
|
||||||
|
'onUpdate:modelValue': (value: boolean | 'indeterminate') => table.toggleAllPageRowsSelected(!!value),
|
||||||
|
'aria-label': 'Select all'
|
||||||
|
}),
|
||||||
|
cell: ({ row }) => h(UCheckbox, {
|
||||||
|
'modelValue': row.getIsSelected(),
|
||||||
|
'onUpdate:modelValue': (value: boolean | 'indeterminate') => row.toggleSelected(!!value),
|
||||||
|
'aria-label': 'Select row'
|
||||||
|
})
|
||||||
|
}, {
|
||||||
|
accessorKey: 'id',
|
||||||
|
header: '#',
|
||||||
|
cell: ({ row }) => `#${row.getValue('id')}`
|
||||||
|
}, {
|
||||||
|
accessorKey: 'date',
|
||||||
|
header: 'Date',
|
||||||
|
cell: ({ row }) => {
|
||||||
|
return new Date(row.getValue('date')).toLocaleString('en-US', {
|
||||||
|
day: 'numeric',
|
||||||
|
month: 'short',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
hour12: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
accessorKey: 'status',
|
||||||
|
header: 'Status',
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const color = ({
|
||||||
|
paid: 'success' as const,
|
||||||
|
failed: 'error' as const,
|
||||||
|
refunded: 'neutral' as const
|
||||||
|
})[row.getValue('status') as string]
|
||||||
|
|
||||||
|
return h(UBadge, { class: 'capitalize', variant: 'subtle', color }, () => row.getValue('status'))
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
accessorKey: 'email',
|
||||||
|
header: 'Email'
|
||||||
|
}, {
|
||||||
|
accessorKey: 'amount',
|
||||||
|
header: () => h('div', { class: 'text-right' }, 'Amount'),
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const amount = Number.parseFloat(row.getValue('amount'))
|
||||||
|
|
||||||
|
const formatted = new Intl.NumberFormat('en-US', {
|
||||||
|
style: 'currency',
|
||||||
|
currency: 'EUR'
|
||||||
|
}).format(amount)
|
||||||
|
|
||||||
|
return h('div', { class: 'text-right font-medium' }, formatted)
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
|
||||||
|
const items = ref<ContextMenuItem[]>([])
|
||||||
|
|
||||||
|
function getRowItems(row: TableRow<Payment>) {
|
||||||
|
return [{
|
||||||
|
type: 'label' as const,
|
||||||
|
label: 'Actions'
|
||||||
|
}, {
|
||||||
|
label: 'Copy payment ID',
|
||||||
|
onSelect() {
|
||||||
|
copy(row.original.id)
|
||||||
|
|
||||||
|
toast.add({
|
||||||
|
title: 'Payment ID copied to clipboard!',
|
||||||
|
color: 'success',
|
||||||
|
icon: 'i-lucide-circle-check'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
label: row.getIsExpanded() ? 'Collapse' : 'Expand',
|
||||||
|
onSelect() {
|
||||||
|
row.toggleExpanded()
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
type: 'separator' as const
|
||||||
|
}, {
|
||||||
|
label: 'View customer'
|
||||||
|
}, {
|
||||||
|
label: 'View payment details'
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
function onContextmenu(_e: Event, row: TableRow<Payment>) {
|
||||||
|
items.value = getRowItems(row)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UContextMenu :items="items">
|
||||||
|
<UTable
|
||||||
|
:data="data"
|
||||||
|
:columns="columns"
|
||||||
|
class="flex-1"
|
||||||
|
@contextmenu="onContextmenu"
|
||||||
|
>
|
||||||
|
<template #expanded="{ row }">
|
||||||
|
<pre>{{ row.original }}</pre>
|
||||||
|
</template>
|
||||||
|
</UTable>
|
||||||
|
</UContextMenu>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { h, resolveComponent } from 'vue'
|
||||||
|
import type { TableColumn, TableRow } from '@nuxt/ui'
|
||||||
|
|
||||||
|
const UBadge = resolveComponent('UBadge')
|
||||||
|
const UCheckbox = resolveComponent('UCheckbox')
|
||||||
|
|
||||||
|
type Payment = {
|
||||||
|
id: string
|
||||||
|
date: string
|
||||||
|
status: 'paid' | 'failed' | 'refunded'
|
||||||
|
email: string
|
||||||
|
amount: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = ref<Payment[]>([{
|
||||||
|
id: '4600',
|
||||||
|
date: '2024-03-11T15:30:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'james.anderson@example.com',
|
||||||
|
amount: 594
|
||||||
|
}, {
|
||||||
|
id: '4599',
|
||||||
|
date: '2024-03-11T10:10:00',
|
||||||
|
status: 'failed',
|
||||||
|
email: 'mia.white@example.com',
|
||||||
|
amount: 276
|
||||||
|
}, {
|
||||||
|
id: '4598',
|
||||||
|
date: '2024-03-11T08:50:00',
|
||||||
|
status: 'refunded',
|
||||||
|
email: 'william.brown@example.com',
|
||||||
|
amount: 315
|
||||||
|
}, {
|
||||||
|
id: '4597',
|
||||||
|
date: '2024-03-10T19:45:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'emma.davis@example.com',
|
||||||
|
amount: 529
|
||||||
|
}, {
|
||||||
|
id: '4596',
|
||||||
|
date: '2024-03-10T15:55:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'ethan.harris@example.com',
|
||||||
|
amount: 639
|
||||||
|
}])
|
||||||
|
|
||||||
|
const columns: TableColumn<Payment>[] = [{
|
||||||
|
id: 'select',
|
||||||
|
header: ({ table }) => h(UCheckbox, {
|
||||||
|
'modelValue': table.getIsSomePageRowsSelected() ? 'indeterminate' : table.getIsAllPageRowsSelected(),
|
||||||
|
'onUpdate:modelValue': (value: boolean | 'indeterminate') => table.toggleAllPageRowsSelected(!!value),
|
||||||
|
'aria-label': 'Select all'
|
||||||
|
}),
|
||||||
|
cell: ({ row }) => h(UCheckbox, {
|
||||||
|
'modelValue': row.getIsSelected(),
|
||||||
|
'onUpdate:modelValue': (value: boolean | 'indeterminate') => row.toggleSelected(!!value),
|
||||||
|
'aria-label': 'Select row'
|
||||||
|
})
|
||||||
|
}, {
|
||||||
|
accessorKey: 'id',
|
||||||
|
header: '#',
|
||||||
|
cell: ({ row }) => `#${row.getValue('id')}`
|
||||||
|
}, {
|
||||||
|
accessorKey: 'date',
|
||||||
|
header: 'Date',
|
||||||
|
cell: ({ row }) => {
|
||||||
|
return new Date(row.getValue('date')).toLocaleString('en-US', {
|
||||||
|
day: 'numeric',
|
||||||
|
month: 'short',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
hour12: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
accessorKey: 'status',
|
||||||
|
header: 'Status',
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const color = ({
|
||||||
|
paid: 'success' as const,
|
||||||
|
failed: 'error' as const,
|
||||||
|
refunded: 'neutral' as const
|
||||||
|
})[row.getValue('status') as string]
|
||||||
|
|
||||||
|
return h(UBadge, { class: 'capitalize', variant: 'subtle', color }, () => row.getValue('status'))
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
accessorKey: 'email',
|
||||||
|
header: 'Email'
|
||||||
|
}, {
|
||||||
|
accessorKey: 'amount',
|
||||||
|
header: () => h('div', { class: 'text-right' }, 'Amount'),
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const amount = Number.parseFloat(row.getValue('amount'))
|
||||||
|
|
||||||
|
const formatted = new Intl.NumberFormat('en-US', {
|
||||||
|
style: 'currency',
|
||||||
|
currency: 'EUR'
|
||||||
|
}).format(amount)
|
||||||
|
|
||||||
|
return h('div', { class: 'text-right font-medium' }, formatted)
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
|
||||||
|
const anchor = ref({ x: 0, y: 0 })
|
||||||
|
|
||||||
|
const reference = computed(() => ({
|
||||||
|
getBoundingClientRect: () =>
|
||||||
|
({
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
left: anchor.value.x,
|
||||||
|
right: anchor.value.x,
|
||||||
|
top: anchor.value.y,
|
||||||
|
bottom: anchor.value.y,
|
||||||
|
...anchor.value
|
||||||
|
} as DOMRect)
|
||||||
|
}))
|
||||||
|
|
||||||
|
const open = ref(false)
|
||||||
|
const openDebounced = refDebounced(open, 10)
|
||||||
|
const selectedRow = ref<TableRow<Payment> | null>(null)
|
||||||
|
|
||||||
|
function onHover(_e: Event, row: TableRow<Payment> | null) {
|
||||||
|
selectedRow.value = row
|
||||||
|
|
||||||
|
open.value = !!row
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex w-full flex-1 gap-1">
|
||||||
|
<UTable
|
||||||
|
:data="data"
|
||||||
|
:columns="columns"
|
||||||
|
class="flex-1"
|
||||||
|
@pointermove="(ev: PointerEvent) => {
|
||||||
|
anchor.x = ev.clientX
|
||||||
|
anchor.y = ev.clientY
|
||||||
|
}"
|
||||||
|
@hover="onHover"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<UPopover
|
||||||
|
:content="{ side: 'top', sideOffset: 16, updatePositionStrategy: 'always' }"
|
||||||
|
:open="openDebounced"
|
||||||
|
:reference="reference"
|
||||||
|
>
|
||||||
|
<template #content>
|
||||||
|
<div class="p-4">
|
||||||
|
{{ selectedRow?.original?.id }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</UPopover>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -112,7 +112,7 @@ function onSelect(row: TableRow<Payment>, e?: Event) {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class=" flex w-full flex-1 gap-1">
|
<div class="flex w-full flex-1 gap-1">
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<UTable
|
<UTable
|
||||||
ref="table"
|
ref="table"
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
|
function showToast() {
|
||||||
|
toast.add({
|
||||||
|
title: 'Uh oh! Something went wrong.',
|
||||||
|
description: 'There was a problem with your request.',
|
||||||
|
icon: 'i-lucide-wifi',
|
||||||
|
progress: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UButton label="Show toast" color="neutral" variant="outline" @click="showToast" />
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const open = ref(false)
|
||||||
|
const anchor = ref({ x: 0, y: 0 })
|
||||||
|
|
||||||
|
const reference = computed(() => ({
|
||||||
|
getBoundingClientRect: () =>
|
||||||
|
({
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
left: anchor.value.x,
|
||||||
|
right: anchor.value.x,
|
||||||
|
top: anchor.value.y,
|
||||||
|
bottom: anchor.value.y,
|
||||||
|
...anchor.value
|
||||||
|
} as DOMRect)
|
||||||
|
}))
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UTooltip
|
||||||
|
:open="open"
|
||||||
|
:reference="reference"
|
||||||
|
:content="{ side: 'top', sideOffset: 16, updatePositionStrategy: 'always' }"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="flex items-center justify-center rounded-md border border-dashed border-accented text-sm aspect-video w-72"
|
||||||
|
@pointerenter="open = true"
|
||||||
|
@pointerleave="open = false"
|
||||||
|
@pointermove="(ev) => {
|
||||||
|
anchor.x = ev.clientX
|
||||||
|
anchor.y = ev.clientY
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
Hover me
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template #content>
|
||||||
|
{{ anchor.x.toFixed(0) }} - {{ anchor.y.toFixed(0) }}
|
||||||
|
</template>
|
||||||
|
</UTooltip>
|
||||||
|
</template>
|
||||||
@@ -16,12 +16,12 @@ function handleMessage(message) {
|
|||||||
async function handleFormatMessage(message) {
|
async function handleFormatMessage(message) {
|
||||||
if (!globalThis.prettier) {
|
if (!globalThis.prettier) {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
import('https://cdn.jsdelivr.net/npm/prettier@3.5.2/standalone.js'),
|
import('https://cdn.jsdelivr.net/npm/prettier@3.6.2/standalone.js'),
|
||||||
import('https://cdn.jsdelivr.net/npm/prettier@3.5.2/plugins/babel.js'),
|
import('https://cdn.jsdelivr.net/npm/prettier@3.6.2/plugins/babel.js'),
|
||||||
import('https://cdn.jsdelivr.net/npm/prettier@3.5.2/plugins/estree.js'),
|
import('https://cdn.jsdelivr.net/npm/prettier@3.6.2/plugins/estree.js'),
|
||||||
import('https://cdn.jsdelivr.net/npm/prettier@3.5.2/plugins/html.js'),
|
import('https://cdn.jsdelivr.net/npm/prettier@3.6.2/plugins/html.js'),
|
||||||
import('https://cdn.jsdelivr.net/npm/prettier@3.5.2/plugins/markdown.js'),
|
import('https://cdn.jsdelivr.net/npm/prettier@3.6.2/plugins/markdown.js'),
|
||||||
import('https://cdn.jsdelivr.net/npm/prettier@3.5.2/plugins/typescript.js')
|
import('https://cdn.jsdelivr.net/npm/prettier@3.6.2/plugins/typescript.js')
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,12 @@ props:
|
|||||||
You can use any name from the <https://icones.js.org> collection.
|
You can use any name from the <https://icones.js.org> collection.
|
||||||
::
|
::
|
||||||
|
|
||||||
|
::warning
|
||||||
|
When using collections with a dash (`-`), you need to separate the icon name from the collection name with a colon (`:`) as `@iconify/vue` does not handle this case like `@nuxt/icon`. For example, instead of `i-simple-icons-github` you need to write `i-simple-icons:github` or `simple-icons:github`.
|
||||||
|
|
||||||
|
Learn more about the [Iconify naming convention](https://iconify.design/docs/icon-components/vue/#icon).
|
||||||
|
::
|
||||||
|
|
||||||
### Component Props
|
### Component Props
|
||||||
|
|
||||||
Some components also have an `icon` prop to display an icon, like the [Button](/components/button) for example:
|
Some components also have an `icon` prop to display an icon, like the [Button](/components/button) for example:
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ Look at the `code` parameter, there you need to pass the iso code of the languag
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
### Extend locale :badge{label="Soon" class="align-text-top"}
|
### Extend locale :badge{label="New" class="align-text-top"}
|
||||||
|
|
||||||
You can customize an existing locale by overriding its `messages` or `code` using the `extendLocale` composable:
|
You can customize an existing locale by overriding its `messages` or `code` using the `extendLocale` composable:
|
||||||
|
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ Look at the `code` parameter, there you need to pass the iso code of the languag
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
### Extend locale :badge{label="Soon" class="align-text-top"}
|
### Extend locale :badge{label="New" class="align-text-top"}
|
||||||
|
|
||||||
You can customize an existing locale by overriding its `messages` or `code` using the `extendLocale` composable:
|
You can customize an existing locale by overriding its `messages` or `code` using the `extendLocale` composable:
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ links:
|
|||||||
- label: GitHub
|
- label: GitHub
|
||||||
icon: i-simple-icons-github
|
icon: i-simple-icons-github
|
||||||
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/CheckboxGroup.vue
|
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/CheckboxGroup.vue
|
||||||
navigation.badge: New
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ props:
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
### Variant :badge{label="New" class="align-text-top"}
|
### Variant
|
||||||
|
|
||||||
Use the `variant` prop to change the variant of the Checkbox.
|
Use the `variant` prop to change the variant of the Checkbox.
|
||||||
|
|
||||||
@@ -190,7 +190,7 @@ props:
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
### Indicator :badge{label="New" class="align-text-top"}
|
### Indicator
|
||||||
|
|
||||||
Use the `indicator` prop to change the position or hide the indicator. Defaults to `start`.
|
Use the `indicator` prop to change the position or hide the indicator. Defaults to `start`.
|
||||||
|
|
||||||
|
|||||||
@@ -52,8 +52,8 @@ Each group contains an `items` array of objects that define the commands. Each i
|
|||||||
- `loading?: boolean`{lang="ts-type"}
|
- `loading?: boolean`{lang="ts-type"}
|
||||||
- `disabled?: boolean`{lang="ts-type"}
|
- `disabled?: boolean`{lang="ts-type"}
|
||||||
- [`slot?: string`{lang="ts-type"}](#with-custom-slot)
|
- [`slot?: string`{lang="ts-type"}](#with-custom-slot)
|
||||||
- `placeholder?: string`{lang="ts-type"} :badge{label="Soon"}
|
- `placeholder?: string`{lang="ts-type"}
|
||||||
- `children?: CommandPaletteItem[]`{lang="ts-type"} :badge{label="Soon"}
|
- `children?: CommandPaletteItem[]`{lang="ts-type"}
|
||||||
- `onSelect?(e?: Event): void`{lang="ts-type"}
|
- `onSelect?(e?: Event): void`{lang="ts-type"}
|
||||||
- `class?: any`{lang="ts-type"}
|
- `class?: any`{lang="ts-type"}
|
||||||
- `ui?: { item?: ClassNameValue, itemLeadingIcon?: ClassNameValue, itemLeadingAvatarSize?: ClassNameValue, itemLeadingAvatar?: ClassNameValue, itemLeadingChipSize?: ClassNameValue, itemLeadingChip?: ClassNameValue, itemLabel?: ClassNameValue, itemLabelPrefix?: ClassNameValue, itemLabelBase?: ClassNameValue, itemLabelSuffix?: ClassNameValue, itemTrailing?: ClassNameValue, itemTrailingKbds?: ClassNameValue, itemTrailingKbdsSize?: ClassNameValue, itemTrailingHighlightedIcon?: ClassNameValue, itemTrailingIcon?: ClassNameValue }`{lang="ts-type"}
|
- `ui?: { item?: ClassNameValue, itemLeadingIcon?: ClassNameValue, itemLeadingAvatarSize?: ClassNameValue, itemLeadingAvatar?: ClassNameValue, itemLeadingChipSize?: ClassNameValue, itemLeadingChip?: ClassNameValue, itemLabel?: ClassNameValue, itemLabelPrefix?: ClassNameValue, itemLabelBase?: ClassNameValue, itemLabelSuffix?: ClassNameValue, itemTrailing?: ClassNameValue, itemTrailingKbds?: ClassNameValue, itemTrailingKbdsSize?: ClassNameValue, itemTrailingHighlightedIcon?: ClassNameValue, itemTrailingIcon?: ClassNameValue }`{lang="ts-type"}
|
||||||
@@ -327,7 +327,7 @@ You can customize this icon globally in your `vite.config.ts` under `ui.icons.ch
|
|||||||
:::
|
:::
|
||||||
::
|
::
|
||||||
|
|
||||||
### Trailing Icon :badge{label="Soon" class="align-text-top"}
|
### Trailing Icon :badge{label="New" class="align-text-top"}
|
||||||
|
|
||||||
Use the `trailing-icon` prop to customize the trailing [Icon](/components/icon) when an item has children. Defaults to `i-lucide-chevron-right`.
|
Use the `trailing-icon` prop to customize the trailing [Icon](/components/icon) when an item has children. Defaults to `i-lucide-chevron-right`.
|
||||||
|
|
||||||
@@ -565,7 +565,7 @@ You can customize this icon globally in your `vite.config.ts` under `ui.icons.cl
|
|||||||
:::
|
:::
|
||||||
::
|
::
|
||||||
|
|
||||||
### Back :badge{label="Soon" class="align-text-top"}
|
### Back :badge{label="New" class="align-text-top"}
|
||||||
|
|
||||||
Use the `back` prop to customize or hide the back button (with `false` value) displayed when navigating into a submenu.
|
Use the `back` prop to customize or hide the back button (with `false` value) displayed when navigating into a submenu.
|
||||||
|
|
||||||
@@ -604,7 +604,7 @@ props:
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
### Back Icon :badge{label="Soon" class="align-text-top"}
|
### Back Icon :badge{label="New" class="align-text-top"}
|
||||||
|
|
||||||
Use the `back-icon` prop to customize the back button [Icon](/components/icon). Defaults to `i-lucide-arrow-left`.
|
Use the `back-icon` prop to customize the back button [Icon](/components/icon). Defaults to `i-lucide-arrow-left`.
|
||||||
|
|
||||||
@@ -717,7 +717,7 @@ props:
|
|||||||
This example uses the `@update:model-value` event to reset the search term when an item is selected.
|
This example uses the `@update:model-value` event to reset the search term when an item is selected.
|
||||||
::
|
::
|
||||||
|
|
||||||
### With children in items :badge{label="Soon" class="align-text-top"}
|
### With children in items :badge{label="New" class="align-text-top"}
|
||||||
|
|
||||||
You can create hierarchical menus by using the `children` property in items. When an item has children, it will automatically display a chevron icon and enable navigation into a submenu.
|
You can create hierarchical menus by using the `children` property in items. When an item has children, it will automatically display a chevron icon and enable navigation into a submenu.
|
||||||
|
|
||||||
@@ -877,6 +877,20 @@ props:
|
|||||||
This can be useful when using the CommandPalette inside a [`Modal`](/components/modal) for example.
|
This can be useful when using the CommandPalette inside a [`Modal`](/components/modal) for example.
|
||||||
::
|
::
|
||||||
|
|
||||||
|
### With footer slot :badge{label="Soon" class="align-text-top"}
|
||||||
|
|
||||||
|
Use the `#footer` slot to add custom content at the bottom of the CommandPalette, such as keyboard shortcuts help or additional actions.
|
||||||
|
|
||||||
|
::component-example
|
||||||
|
---
|
||||||
|
collapse: true
|
||||||
|
name: 'command-palette-footer-slot-example'
|
||||||
|
class: '!p-0'
|
||||||
|
props:
|
||||||
|
autofocus: false
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
### With custom slot
|
### With custom slot
|
||||||
|
|
||||||
Use the `slot` property to customize a specific item or group.
|
Use the `slot` property to customize a specific item or group.
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ links:
|
|||||||
- label: GitHub
|
- label: GitHub
|
||||||
icon: i-simple-icons-github
|
icon: i-simple-icons-github
|
||||||
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/InputTags.vue
|
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/InputTags.vue
|
||||||
navigation.badge: Soon
|
navigation.badge: New
|
||||||
---
|
---
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
@@ -51,6 +51,17 @@ props:
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
|
### Max Length :badge{label="Soon" class="align-text-top"}
|
||||||
|
|
||||||
|
Use the `max-length` prop to set the maximum number of characters allowed in a tag.
|
||||||
|
|
||||||
|
::component-code
|
||||||
|
---
|
||||||
|
props:
|
||||||
|
maxLength: 4
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
### Color
|
### Color
|
||||||
|
|
||||||
Use the `color` prop to change the ring color when the InputTags is focused.
|
Use the `color` prop to change the ring color when the InputTags is focused.
|
||||||
|
|||||||
@@ -889,7 +889,7 @@ You can inspect the DOM to see each item's content being rendered.
|
|||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
### With tooltip in items :badge{label="New" class="align-text-top"}
|
### With tooltip in items
|
||||||
|
|
||||||
When orientation is `vertical` and the menu is `collapsed`, you can set the `tooltip` prop to `true` to display a [Tooltip](/components/tooltip) around items with their label but you can also use the `tooltip` property on each item to override the default tooltip.
|
When orientation is `vertical` and the menu is `collapsed`, you can set the `tooltip` prop to `true` to display a [Tooltip](/components/tooltip) around items with their label but you can also use the `tooltip` property on each item to override the default tooltip.
|
||||||
|
|
||||||
@@ -994,7 +994,7 @@ props:
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
### With popover in items :badge{label="New" class="align-text-top"}
|
### With popover in items
|
||||||
|
|
||||||
When orientation is `vertical` and the menu is `collapsed`, you can set the `popover` prop to `true` to display a [Popover](/components/popover) around items with their children but you can also use the `popover` property on each item to override the default popover.
|
When orientation is `vertical` and the menu is `collapsed`, you can set the `popover` prop to `true` to display a [Popover](/components/popover) around items with their children but you can also use the `popover` property on each item to override the default popover.
|
||||||
|
|
||||||
|
|||||||
@@ -202,7 +202,17 @@ name: 'popover-command-palette-example'
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
### With anchor slot :badge{label="New" class="align-text-top"}
|
### With following cursor :badge{label="Soon" class="align-text-top"}
|
||||||
|
|
||||||
|
You can make the Popover follow the cursor when hovering over an element using the [`reference`](https://reka-ui.com/docs/components/tooltip#trigger) prop:
|
||||||
|
|
||||||
|
::component-example
|
||||||
|
---
|
||||||
|
name: 'popover-cursor-example'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
### With anchor slot
|
||||||
|
|
||||||
You can use the `#anchor` slot to position the Popover against a custom element.
|
You can use the `#anchor` slot to position the Popover against a custom element.
|
||||||
|
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ props:
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
### Variant :badge{label="New" class="align-text-top"}
|
### Variant
|
||||||
|
|
||||||
Use the `variant` prop to change the variant of the RadioGroup.
|
Use the `variant` prop to change the variant of the RadioGroup.
|
||||||
|
|
||||||
@@ -240,7 +240,7 @@ props:
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
### Indicator :badge{label="New" class="align-text-top"}
|
### Indicator
|
||||||
|
|
||||||
Use the `indicator` prop to change the position or hide the indicator. Defaults to `start`.
|
Use the `indicator` prop to change the position or hide the indicator. Defaults to `start`.
|
||||||
|
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ props:
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
### Tooltip :badge{label="New" class="align-text-top"}
|
### Tooltip
|
||||||
|
|
||||||
Use the `tooltip` prop to display a [Tooltip](/components/tooltip) around the Slider thumbs with the current value. You can set it to `true` for default behavior or pass an object to customize it with any property from the [Tooltip](/components/tooltip#props) component.
|
Use the `tooltip` prop to display a [Tooltip](/components/tooltip) around the Slider thumbs with the current value. You can set it to `true` for default behavior or pass an object to customize it with any property from the [Tooltip](/components/tooltip#props) component.
|
||||||
|
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ Use the `columns` prop as an array of [ColumnDef](https://tanstack.com/table/lat
|
|||||||
|
|
||||||
- `accessorKey`: [The key of the row object to use when extracting the value for the column.]{class="text-muted"}
|
- `accessorKey`: [The key of the row object to use when extracting the value for the column.]{class="text-muted"}
|
||||||
- `header`: [The header to display for the column. If a string is passed, it can be used as a default for the column ID. If a function is passed, it will be passed a props object for the header and should return the rendered header value (the exact type depends on the adapter being used).]{class="text-muted"}
|
- `header`: [The header to display for the column. If a string is passed, it can be used as a default for the column ID. If a function is passed, it will be passed a props object for the header and should return the rendered header value (the exact type depends on the adapter being used).]{class="text-muted"}
|
||||||
|
- `footer`: [The footer to display for the column. Works exactly like header, but is displayed under the table.]{class="text-muted"}
|
||||||
- `cell`: [The cell to display each row for the column. If a function is passed, it will be passed a props object for the cell and should return the rendered cell value (the exact type depends on the adapter being used).]{class="text-muted"}
|
- `cell`: [The cell to display each row for the column. If a function is passed, it will be passed a props object for the cell and should return the rendered cell value (the exact type depends on the adapter being used).]{class="text-muted"}
|
||||||
- `meta`: [Extra properties for the column.]{class="text-muted"}
|
- `meta`: [Extra properties for the column.]{class="text-muted"}
|
||||||
- `class`:
|
- `class`:
|
||||||
@@ -161,7 +162,7 @@ props:
|
|||||||
|
|
||||||
### Sticky
|
### Sticky
|
||||||
|
|
||||||
Use the `sticky` prop to make the header sticky.
|
Use the `sticky` prop to make the header or footer sticky.
|
||||||
|
|
||||||
::component-code
|
::component-code
|
||||||
---
|
---
|
||||||
@@ -172,6 +173,10 @@ ignore:
|
|||||||
- class
|
- class
|
||||||
external:
|
external:
|
||||||
- data
|
- data
|
||||||
|
items:
|
||||||
|
sticky:
|
||||||
|
- true
|
||||||
|
- false
|
||||||
props:
|
props:
|
||||||
sticky: true
|
sticky: true
|
||||||
data:
|
data:
|
||||||
@@ -266,8 +271,8 @@ You can group rows based on a given column value and show/hide sub rows via some
|
|||||||
|
|
||||||
#### Important parts:
|
#### Important parts:
|
||||||
|
|
||||||
* Add prop `grouping` to `UTable` component with an array of column ids you want to group by.
|
* Add `grouping` prop with an array of column ids you want to group by.
|
||||||
* Add prop `grouping-options` to `UTable`. It must include `getGroupedRowModel`, you can import it from `@tanstack/vue-table` or implement your own.
|
* Add `grouping-options` prop. It must include `getGroupedRowModel`, you can import it from `@tanstack/vue-table` or implement your own.
|
||||||
* Expand rows via `row.toggleExpanded()` method on any cell of the row. Keep in mind, it also toggles `#expanded` slot.
|
* Expand rows via `row.toggleExpanded()` method on any cell of the row. Keep in mind, it also toggles `#expanded` slot.
|
||||||
* Use `aggregateFn` on column definition to define how to aggregate the rows.
|
* Use `aggregateFn` on column definition to define how to aggregate the rows.
|
||||||
* `agregatedCell` renderer on column definition only works if there is no `cell` renderer.
|
* `agregatedCell` renderer on column definition only works if there is no `cell` renderer.
|
||||||
@@ -304,19 +309,19 @@ class: '!p-0'
|
|||||||
You can use the `row-selection` prop to control the selection state of the rows (can be binded with `v-model`).
|
You can use the `row-selection` prop to control the selection state of the rows (can be binded with `v-model`).
|
||||||
::
|
::
|
||||||
|
|
||||||
### With `@select` event
|
### With row select event
|
||||||
|
|
||||||
You can add a `@select` listener to make rows clickable. The handler function receives the `TableRow` instance as the first argument and an optional `Event` as the second argument.
|
You can add a `@select` listener to make rows clickable with or without a checkbox column.
|
||||||
|
|
||||||
::note
|
::note
|
||||||
You can use this to navigate to a page, open a modal or even to select the row manually.
|
The handler function receives the `TableRow` instance as the first argument and an optional `Event` as the second argument.
|
||||||
::
|
::
|
||||||
|
|
||||||
::component-example
|
::component-example
|
||||||
---
|
---
|
||||||
prettier: true
|
prettier: true
|
||||||
collapse: true
|
collapse: true
|
||||||
name: 'table-row-selection-event-example'
|
name: 'table-row-select-event-example'
|
||||||
highlights:
|
highlights:
|
||||||
- 123
|
- 123
|
||||||
- 130
|
- 130
|
||||||
@@ -324,6 +329,70 @@ class: '!p-0'
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
|
::tip
|
||||||
|
You can use this to navigate to a page, open a modal or even to select the row manually.
|
||||||
|
::
|
||||||
|
|
||||||
|
### With row context menu event :badge{label="Soon" class="align-text-top"}
|
||||||
|
|
||||||
|
You can add a `@contextmenu` listener to make rows right clickable and wrap the Table in a [ContextMenu](/components/context-menu) component to display row actions for example.
|
||||||
|
|
||||||
|
::note
|
||||||
|
The handler function receives the `Event` and `TableRow` instance as the first and second arguments respectively.
|
||||||
|
::
|
||||||
|
|
||||||
|
::component-example
|
||||||
|
---
|
||||||
|
prettier: true
|
||||||
|
collapse: true
|
||||||
|
name: 'table-row-context-menu-event-example'
|
||||||
|
highlights:
|
||||||
|
- 130
|
||||||
|
- 170
|
||||||
|
class: '!p-0'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
### With row hover event :badge{label="Soon" class="align-text-top"}
|
||||||
|
|
||||||
|
You can add a `@hover` listener to make rows hoverable and use a [Popover](/components/popover) or a [Tooltip](/components/tooltip) component to display row details for example.
|
||||||
|
|
||||||
|
::note
|
||||||
|
The handler function receives the `Event` and `TableRow` instance as the first and second arguments respectively.
|
||||||
|
::
|
||||||
|
|
||||||
|
::component-example
|
||||||
|
---
|
||||||
|
prettier: true
|
||||||
|
collapse: true
|
||||||
|
name: 'table-row-hover-event-example'
|
||||||
|
highlights:
|
||||||
|
- 126
|
||||||
|
- 149
|
||||||
|
class: '!p-0'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
::note
|
||||||
|
This example is similar as the Popover [with following cursor example](/components/popover#with-following-cursor) and uses a [`refDebounced`](https://vueuse.org/shared/refDebounced/#refdebounced) to prevent the Popover from opening and closing too quickly when moving the cursor from one row to another.
|
||||||
|
::
|
||||||
|
|
||||||
|
### With column footer :badge{label="Soon" class="align-text-top"}
|
||||||
|
|
||||||
|
You can add a `footer` property to the column definition to render a footer for the column.
|
||||||
|
|
||||||
|
::component-example
|
||||||
|
---
|
||||||
|
prettier: true
|
||||||
|
collapse: true
|
||||||
|
name: 'table-column-footer-example'
|
||||||
|
highlights:
|
||||||
|
- 94
|
||||||
|
- 108
|
||||||
|
class: '!p-0'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
### With column sorting
|
### With column sorting
|
||||||
|
|
||||||
You can update a column `header` to render a [Button](/components/button) component inside the `header` to toggle the sorting state using the TanStack Table [Sorting APIs](https://tanstack.com/table/latest/docs/api/features/sorting).
|
You can update a column `header` to render a [Button](/components/button) component inside the `header` to toggle the sorting state using the TanStack Table [Sorting APIs](https://tanstack.com/table/latest/docs/api/features/sorting).
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ props:
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
### Icon :badge{label="New" class="align-text-top"}
|
### Icon
|
||||||
|
|
||||||
Use the `icon` prop to show an [Icon](/components/icon) inside the Textarea.
|
Use the `icon` prop to show an [Icon](/components/icon) inside the Textarea.
|
||||||
|
|
||||||
@@ -157,7 +157,7 @@ props:
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
### Avatar :badge{label="New" class="align-text-top"}
|
### Avatar
|
||||||
|
|
||||||
Use the `avatar` prop to show an [Avatar](/components/avatar) inside the Textarea.
|
Use the `avatar` prop to show an [Avatar](/components/avatar) inside the Textarea.
|
||||||
|
|
||||||
@@ -176,7 +176,7 @@ props:
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
### Loading :badge{label="New" class="align-text-top"}
|
### Loading
|
||||||
|
|
||||||
Use the `loading` prop to show a loading icon on the Textarea.
|
Use the `loading` prop to show a loading icon on the Textarea.
|
||||||
|
|
||||||
@@ -192,7 +192,7 @@ props:
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
### Loading Icon :badge{label="New" class="align-text-top"}
|
### Loading Icon
|
||||||
|
|
||||||
Use the `loading-icon` prop to customize the loading icon. Defaults to `i-lucide-loader-circle`.
|
Use the `loading-icon` prop to customize the loading icon. Defaults to `i-lucide-loader-circle`.
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ links:
|
|||||||
- label: GitHub
|
- label: GitHub
|
||||||
icon: i-simple-icons-github
|
icon: i-simple-icons-github
|
||||||
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Timeline.vue
|
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Timeline.vue
|
||||||
navigation.badge: Soon
|
navigation.badge: New
|
||||||
---
|
---
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ name: 'toast-color-example'
|
|||||||
|
|
||||||
### Close
|
### Close
|
||||||
|
|
||||||
Pass a `close` field to customize or hide the close button (with `false` value).
|
Pass a `close` field to customize or hide the close [Button](/components/button) (with `false` value).
|
||||||
|
|
||||||
::component-example
|
::component-example
|
||||||
---
|
---
|
||||||
@@ -143,7 +143,7 @@ You can customize this icon globally in your `vite.config.ts` under `ui.icons.cl
|
|||||||
|
|
||||||
### Actions
|
### Actions
|
||||||
|
|
||||||
Pass an `actions` field to add some [Button](/components/button) actions to the Alert.
|
Pass an `actions` field to add some [Button](/components/button) actions to the Toast.
|
||||||
|
|
||||||
::component-example
|
::component-example
|
||||||
---
|
---
|
||||||
@@ -155,9 +155,23 @@ name: 'toast-actions-example'
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
|
### Progress :badge{label="Soon" class="align-text-top"}
|
||||||
|
|
||||||
|
Pass a `progress` field to customize or hide the [Progress](/components/progress) bar (with `false` value).
|
||||||
|
|
||||||
|
::tip
|
||||||
|
The Progress bar inherits the Toast color by default, but you can override it using the `progress.color` field.
|
||||||
|
::
|
||||||
|
|
||||||
|
::component-example
|
||||||
|
---
|
||||||
|
name: 'toast-progress-example'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
### Orientation
|
### Orientation
|
||||||
|
|
||||||
Use the `orientation` prop to change the orientation of the Toast.
|
Pass an `orientation` field to the `toast.add` method to change the orientation of the Toast.
|
||||||
|
|
||||||
::component-example
|
::component-example
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -186,6 +186,16 @@ name: 'tooltip-open-example'
|
|||||||
In this example, leveraging [`defineShortcuts`](/composables/define-shortcuts), you can toggle the Tooltip by pressing :kbd{value="O"}.
|
In this example, leveraging [`defineShortcuts`](/composables/define-shortcuts), you can toggle the Tooltip by pressing :kbd{value="O"}.
|
||||||
::
|
::
|
||||||
|
|
||||||
|
### With following cursor :badge{label="Soon" class="align-text-top"}
|
||||||
|
|
||||||
|
You can make the Tooltip follow the cursor when hovering over an element using the [`reference`](https://reka-ui.com/docs/components/tooltip#trigger) prop:
|
||||||
|
|
||||||
|
::component-example
|
||||||
|
---
|
||||||
|
name: 'tooltip-cursor-example'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
||||||
### Props
|
### Props
|
||||||
|
|||||||
@@ -11,40 +11,40 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ai-sdk/vue": "^1.2.12",
|
"@ai-sdk/vue": "^1.2.12",
|
||||||
"@iconify-json/logos": "^1.2.4",
|
"@iconify-json/logos": "^1.2.4",
|
||||||
"@iconify-json/lucide": "^1.2.51",
|
"@iconify-json/lucide": "^1.2.56",
|
||||||
"@iconify-json/simple-icons": "^1.2.39",
|
"@iconify-json/simple-icons": "^1.2.42",
|
||||||
"@iconify-json/vscode-icons": "^1.2.23",
|
"@iconify-json/vscode-icons": "^1.2.23",
|
||||||
"@nuxt/content": "^3.6.1",
|
"@nuxt/content": "^3.6.3",
|
||||||
"@nuxt/image": "^1.10.0",
|
"@nuxt/image": "^1.10.0",
|
||||||
"@nuxt/ui": "workspace:*",
|
"@nuxt/ui": "workspace:*",
|
||||||
"@nuxt/ui-pro": "https://pkg.pr.new/@nuxt/ui-pro@55e248c",
|
"@nuxt/ui-pro": "https://pkg.pr.new/@nuxt/ui-pro@22fdc5e",
|
||||||
"@nuxthub/core": "^0.9.0",
|
"@nuxthub/core": "^0.9.0",
|
||||||
"@nuxtjs/plausible": "^1.2.0",
|
"@nuxtjs/plausible": "^1.2.0",
|
||||||
"@octokit/rest": "^22.0.0",
|
"@octokit/rest": "^22.0.0",
|
||||||
"@rollup/plugin-yaml": "^4.1.2",
|
"@rollup/plugin-yaml": "^4.1.2",
|
||||||
"@vueuse/integrations": "^13.4.0",
|
"@vueuse/integrations": "^13.5.0",
|
||||||
"@vueuse/nuxt": "^13.4.0",
|
"@vueuse/nuxt": "^13.5.0",
|
||||||
"ai": "^4.3.16",
|
"ai": "^4.3.16",
|
||||||
"better-sqlite3": "^12.0.0",
|
"better-sqlite3": "^12.2.0",
|
||||||
"capture-website": "^4.2.0",
|
"capture-website": "^4.2.0",
|
||||||
"joi": "^17.13.3",
|
"joi": "^17.13.3",
|
||||||
"maska": "^3.1.1",
|
"maska": "^3.2.0",
|
||||||
"motion-v": "^1.3.0",
|
"motion-v": "^1.5.0",
|
||||||
"nuxt": "^3.17.5",
|
"nuxt": "^3.17.6",
|
||||||
"nuxt-component-meta": "^0.11.0",
|
"nuxt-component-meta": "^0.12.1",
|
||||||
"nuxt-llms": "^0.1.3",
|
"nuxt-llms": "^0.1.3",
|
||||||
"nuxt-og-image": "^5.1.7",
|
"nuxt-og-image": "^5.1.9",
|
||||||
"prettier": "^3.6.0",
|
"prettier": "^3.6.2",
|
||||||
"shiki-transformer-color-highlight": "^1.0.0",
|
"shiki-transformer-color-highlight": "^1.0.0",
|
||||||
"sortablejs": "^1.15.6",
|
"sortablejs": "^1.15.6",
|
||||||
"superstruct": "^2.0.2",
|
"superstruct": "^2.0.2",
|
||||||
"ufo": "^1.6.1",
|
"ufo": "^1.6.1",
|
||||||
"valibot": "^1.1.0",
|
"valibot": "^1.1.0",
|
||||||
"workers-ai-provider": "^0.7.0",
|
"workers-ai-provider": "^0.7.1",
|
||||||
"yup": "^1.6.1",
|
"yup": "^1.6.1",
|
||||||
"zod": "^3.25.67"
|
"zod": "^3.25.75"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"wrangler": "^4.20.5"
|
"wrangler": "^4.23.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
42
package.json
42
package.json
@@ -2,7 +2,7 @@
|
|||||||
"name": "@nuxt/ui",
|
"name": "@nuxt/ui",
|
||||||
"description": "A UI Library for Modern Web Apps, powered by Vue & Tailwind CSS.",
|
"description": "A UI Library for Modern Web Apps, powered by Vue & Tailwind CSS.",
|
||||||
"version": "3.2.0",
|
"version": "3.2.0",
|
||||||
"packageManager": "pnpm@10.12.2",
|
"packageManager": "pnpm@10.12.4",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/nuxt/ui.git"
|
"url": "git+https://github.com/nuxt/ui.git"
|
||||||
@@ -98,9 +98,9 @@
|
|||||||
"prepack": "pnpm build",
|
"prepack": "pnpm build",
|
||||||
"dev": "nuxt dev playground --uiDev",
|
"dev": "nuxt dev playground --uiDev",
|
||||||
"dev:build": "nuxt build playground",
|
"dev:build": "nuxt build playground",
|
||||||
"dev:vue": "vite playground-vue -- --uiDev",
|
"dev:vue": "pnpm --filter playground-vue dev -- --uiDev",
|
||||||
"dev:vue:build": "vite build playground-vue",
|
"dev:vue:build": "pnpm --filter playground-vue build",
|
||||||
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxt prepare playground && nuxt prepare docs && vite build playground-vue",
|
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxt prepare playground && nuxt prepare docs && pnpm dev:vue:build",
|
||||||
"docs": "nuxt dev docs --uiDev",
|
"docs": "nuxt dev docs --uiDev",
|
||||||
"docs:build": "nuxt build docs",
|
"docs:build": "nuxt build docs",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
@@ -115,17 +115,17 @@
|
|||||||
"@internationalized/date": "^3.8.2",
|
"@internationalized/date": "^3.8.2",
|
||||||
"@internationalized/number": "^3.6.3",
|
"@internationalized/number": "^3.6.3",
|
||||||
"@nuxt/fonts": "^0.11.4",
|
"@nuxt/fonts": "^0.11.4",
|
||||||
"@nuxt/icon": "^1.14.0",
|
"@nuxt/icon": "^1.15.0",
|
||||||
"@nuxt/kit": "^3.17.5",
|
"@nuxt/kit": "^3.17.6",
|
||||||
"@nuxt/schema": "^3.17.5",
|
"@nuxt/schema": "^3.17.6",
|
||||||
"@nuxtjs/color-mode": "^3.5.2",
|
"@nuxtjs/color-mode": "^3.5.2",
|
||||||
"@standard-schema/spec": "^1.0.0",
|
"@standard-schema/spec": "^1.0.0",
|
||||||
"@tailwindcss/postcss": "^4.1.10",
|
"@tailwindcss/postcss": "^4.1.11",
|
||||||
"@tailwindcss/vite": "^4.1.10",
|
"@tailwindcss/vite": "^4.1.11",
|
||||||
"@tanstack/vue-table": "^8.21.3",
|
"@tanstack/vue-table": "^8.21.3",
|
||||||
"@unhead/vue": "^2.0.10",
|
"@unhead/vue": "^2.0.12",
|
||||||
"@vueuse/core": "^13.4.0",
|
"@vueuse/core": "^13.5.0",
|
||||||
"@vueuse/integrations": "^13.4.0",
|
"@vueuse/integrations": "^13.5.0",
|
||||||
"colortranslator": "^5.0.0",
|
"colortranslator": "^5.0.0",
|
||||||
"consola": "^3.4.2",
|
"consola": "^3.4.2",
|
||||||
"defu": "^6.1.4",
|
"defu": "^6.1.4",
|
||||||
@@ -143,31 +143,31 @@
|
|||||||
"mlly": "^1.7.4",
|
"mlly": "^1.7.4",
|
||||||
"ohash": "^2.0.11",
|
"ohash": "^2.0.11",
|
||||||
"pathe": "^2.0.3",
|
"pathe": "^2.0.3",
|
||||||
"reka-ui": "2.3.1",
|
"reka-ui": "2.3.2",
|
||||||
"scule": "^1.3.0",
|
"scule": "^1.3.0",
|
||||||
"tailwind-variants": "^1.0.0",
|
"tailwind-variants": "^1.0.0",
|
||||||
"tailwindcss": "^4.1.10",
|
"tailwindcss": "^4.1.11",
|
||||||
"tinyglobby": "^0.2.14",
|
"tinyglobby": "^0.2.14",
|
||||||
"unplugin": "^2.3.5",
|
"unplugin": "^2.3.5",
|
||||||
"unplugin-auto-import": "^19.3.0",
|
"unplugin-auto-import": "^19.3.0",
|
||||||
"unplugin-vue-components": "^28.7.0",
|
"unplugin-vue-components": "^28.8.0",
|
||||||
"vaul-vue": "0.4.1",
|
"vaul-vue": "0.4.1",
|
||||||
"vue-component-type-helpers": "^2.2.10"
|
"vue-component-type-helpers": "^3.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nuxt/eslint-config": "^1.4.1",
|
"@nuxt/eslint-config": "^1.5.2",
|
||||||
"@nuxt/module-builder": "^1.0.1",
|
"@nuxt/module-builder": "^1.0.1",
|
||||||
"@nuxt/test-utils": "^3.19.1",
|
"@nuxt/test-utils": "^3.19.2",
|
||||||
"@release-it/conventional-changelog": "^10.0.1",
|
"@release-it/conventional-changelog": "^10.0.1",
|
||||||
"@vue/test-utils": "^2.4.6",
|
"@vue/test-utils": "^2.4.6",
|
||||||
"embla-carousel": "^8.6.0",
|
"embla-carousel": "^8.6.0",
|
||||||
"eslint": "^9.29.0",
|
"eslint": "^9.30.1",
|
||||||
"happy-dom": "^18.0.1",
|
"happy-dom": "^18.0.1",
|
||||||
"nuxt": "^3.17.5",
|
"nuxt": "^3.17.6",
|
||||||
"release-it": "^19.0.3",
|
"release-it": "^19.0.3",
|
||||||
"vitest": "^3.2.4",
|
"vitest": "^3.2.4",
|
||||||
"vitest-environment-nuxt": "^1.0.1",
|
"vitest-environment-nuxt": "^1.0.1",
|
||||||
"vue-tsc": "^2.2.10"
|
"vue-tsc": "^3.0.1"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@inertiajs/vue3": "^2.0.7",
|
"@inertiajs/vue3": "^2.0.7",
|
||||||
|
|||||||
@@ -13,12 +13,12 @@
|
|||||||
"@nuxt/ui": "workspace:*",
|
"@nuxt/ui": "workspace:*",
|
||||||
"vue": "^3.5.17",
|
"vue": "^3.5.17",
|
||||||
"vue-router": "^4.5.1",
|
"vue-router": "^4.5.1",
|
||||||
"zod": "^3.25.67"
|
"zod": "^3.25.75"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "^5.2.4",
|
"@vitejs/plugin-vue": "^5.2.4",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
"vite": "^6.3.5",
|
"vite": "^6.3.5",
|
||||||
"vue-tsc": "^2.2.10"
|
"vue-tsc": "^3.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -166,7 +166,27 @@ defineShortcuts({
|
|||||||
multiple
|
multiple
|
||||||
class="sm:max-h-80"
|
class="sm:max-h-80"
|
||||||
@update:model-value="onSelect"
|
@update:model-value="onSelect"
|
||||||
/>
|
>
|
||||||
|
<template #footer>
|
||||||
|
<div class="flex items-center justify-between gap-2">
|
||||||
|
<UIcon name="i-simple-icons-nuxtdotjs" class="size-5 text-dimmed ml-1" />
|
||||||
|
<div class="flex items-center gap-1">
|
||||||
|
<UButton color="neutral" variant="ghost" label="Open Command" class="text-dimmed" size="xs">
|
||||||
|
<template #trailing>
|
||||||
|
<UKbd value="enter" />
|
||||||
|
</template>
|
||||||
|
</UButton>
|
||||||
|
<USeparator orientation="vertical" class="h-4" />
|
||||||
|
<UButton color="neutral" variant="ghost" label="Actions" class="text-dimmed" size="xs">
|
||||||
|
<template #trailing>
|
||||||
|
<UKbd value="meta" />
|
||||||
|
<UKbd value="k" />
|
||||||
|
</template>
|
||||||
|
</UButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</UCommandPalette>
|
||||||
</DefineTemplate>
|
</DefineTemplate>
|
||||||
|
|
||||||
<div class="flex-1 flex flex-col gap-12 w-full max-w-lg">
|
<div class="flex-1 flex flex-col gap-12 w-full max-w-lg">
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { h, resolveComponent } from 'vue'
|
|||||||
import { upperFirst } from 'scule'
|
import { upperFirst } from 'scule'
|
||||||
import type { TableColumn, TableRow } from '@nuxt/ui'
|
import type { TableColumn, TableRow } from '@nuxt/ui'
|
||||||
import { getPaginationRowModel } from '@tanstack/vue-table'
|
import { getPaginationRowModel } from '@tanstack/vue-table'
|
||||||
import { useClipboard } from '@vueuse/core'
|
import { useClipboard, refDebounced } from '@vueuse/core'
|
||||||
|
|
||||||
const UButton = resolveComponent('UButton')
|
const UButton = resolveComponent('UButton')
|
||||||
const UCheckbox = resolveComponent('UCheckbox')
|
const UCheckbox = resolveComponent('UCheckbox')
|
||||||
@@ -147,6 +147,35 @@ const data = ref<Payment[]>([{
|
|||||||
|
|
||||||
const currentID = ref(4601)
|
const currentID = ref(4601)
|
||||||
|
|
||||||
|
function getRowItems(row: TableRow<Payment>) {
|
||||||
|
return [{
|
||||||
|
type: 'label' as const,
|
||||||
|
label: 'Actions'
|
||||||
|
}, {
|
||||||
|
label: 'Copy payment ID',
|
||||||
|
onSelect() {
|
||||||
|
copy(row.original.id)
|
||||||
|
|
||||||
|
toast.add({
|
||||||
|
title: 'Payment ID copied to clipboard!',
|
||||||
|
color: 'success',
|
||||||
|
icon: 'i-lucide-circle-check'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
label: row.getIsExpanded() ? 'Collapse' : 'Expand',
|
||||||
|
onSelect() {
|
||||||
|
row.toggleExpanded()
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
type: 'separator' as const
|
||||||
|
}, {
|
||||||
|
label: 'View customer'
|
||||||
|
}, {
|
||||||
|
label: 'View payment details'
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
const columns: TableColumn<Payment>[] = [{
|
const columns: TableColumn<Payment>[] = [{
|
||||||
id: 'select',
|
id: 'select',
|
||||||
header: ({ table }) => h(UCheckbox, {
|
header: ({ table }) => h(UCheckbox, {
|
||||||
@@ -213,6 +242,16 @@ const columns: TableColumn<Payment>[] = [{
|
|||||||
}, {
|
}, {
|
||||||
accessorKey: 'amount',
|
accessorKey: 'amount',
|
||||||
header: () => h('div', { class: 'text-right' }, 'Amount'),
|
header: () => h('div', { class: 'text-right' }, 'Amount'),
|
||||||
|
footer: ({ column }) => {
|
||||||
|
const total = column.getFacetedRowModel().rows.reduce((acc: number, row: TableRow<Payment>) => acc + Number.parseFloat(row.getValue('amount')), 0)
|
||||||
|
|
||||||
|
const formatted = new Intl.NumberFormat('en-US', {
|
||||||
|
style: 'currency',
|
||||||
|
currency: 'EUR'
|
||||||
|
}).format(total)
|
||||||
|
|
||||||
|
return h('div', { class: 'text-right font-medium' }, `Total: ${formatted}`)
|
||||||
|
},
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const amount = Number.parseFloat(row.getValue('amount'))
|
const amount = Number.parseFloat(row.getValue('amount'))
|
||||||
|
|
||||||
@@ -227,38 +266,11 @@ const columns: TableColumn<Payment>[] = [{
|
|||||||
id: 'actions',
|
id: 'actions',
|
||||||
enableHiding: false,
|
enableHiding: false,
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const items = [{
|
|
||||||
type: 'label',
|
|
||||||
label: 'Actions'
|
|
||||||
}, {
|
|
||||||
label: 'Copy payment ID',
|
|
||||||
onSelect() {
|
|
||||||
copy(row.original.id)
|
|
||||||
|
|
||||||
toast.add({
|
|
||||||
title: 'Payment ID copied to clipboard!',
|
|
||||||
color: 'success',
|
|
||||||
icon: 'i-lucide-circle-check'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
label: row.getIsExpanded() ? 'Collapse' : 'Expand',
|
|
||||||
onSelect() {
|
|
||||||
row.toggleExpanded()
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
type: 'separator'
|
|
||||||
}, {
|
|
||||||
label: 'View customer'
|
|
||||||
}, {
|
|
||||||
label: 'View payment details'
|
|
||||||
}]
|
|
||||||
|
|
||||||
return h('div', { class: 'text-right' }, h(UDropdownMenu, {
|
return h('div', { class: 'text-right' }, h(UDropdownMenu, {
|
||||||
'content': {
|
'content': {
|
||||||
align: 'end'
|
align: 'end'
|
||||||
},
|
},
|
||||||
items,
|
'items': getRowItems(row),
|
||||||
'aria-label': 'Actions dropdown'
|
'aria-label': 'Actions dropdown'
|
||||||
}, () => h(UButton, {
|
}, () => h(UButton, {
|
||||||
'icon': 'i-lucide-ellipsis-vertical',
|
'icon': 'i-lucide-ellipsis-vertical',
|
||||||
@@ -296,8 +308,41 @@ function randomize() {
|
|||||||
data.value = data.value.sort(() => Math.random() - 0.5)
|
data.value = data.value.sort(() => Math.random() - 0.5)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const rowSelection = ref<Record<string, boolean>>({})
|
||||||
|
|
||||||
function onSelect(row: TableRow<Payment>) {
|
function onSelect(row: TableRow<Payment>) {
|
||||||
console.log(row)
|
row.toggleSelected(!row.getIsSelected())
|
||||||
|
}
|
||||||
|
|
||||||
|
const contextmenuRow = ref<TableRow<Payment> | null>(null)
|
||||||
|
const contextmenuItems = computed(() => contextmenuRow.value ? getRowItems(contextmenuRow.value) : [])
|
||||||
|
|
||||||
|
function onContextmenu(e: Event, row: TableRow<Payment>) {
|
||||||
|
contextmenuRow.value = row
|
||||||
|
}
|
||||||
|
|
||||||
|
const popoverOpen = ref(false)
|
||||||
|
const popoverOpenDebounced = refDebounced(popoverOpen, 1)
|
||||||
|
const popoverAnchor = ref({ x: 0, y: 0 })
|
||||||
|
const popoverRow = ref<TableRow<Payment> | null>(null)
|
||||||
|
|
||||||
|
const reference = computed(() => ({
|
||||||
|
getBoundingClientRect: () =>
|
||||||
|
({
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
left: popoverAnchor.value.x,
|
||||||
|
right: popoverAnchor.value.x,
|
||||||
|
top: popoverAnchor.value.y,
|
||||||
|
bottom: popoverAnchor.value.y,
|
||||||
|
...popoverAnchor.value
|
||||||
|
} as DOMRect)
|
||||||
|
}))
|
||||||
|
|
||||||
|
function onHover(_e: Event, row: TableRow<Payment> | null) {
|
||||||
|
popoverRow.value = row
|
||||||
|
|
||||||
|
popoverOpen.value = !!row
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@@ -344,27 +389,44 @@ onMounted(() => {
|
|||||||
</UDropdownMenu>
|
</UDropdownMenu>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<UTable
|
<UContextMenu :items="contextmenuItems">
|
||||||
ref="table"
|
<UTable
|
||||||
:data="data"
|
ref="table"
|
||||||
:columns="columns"
|
:data="data"
|
||||||
:column-pinning="columnPinning"
|
:columns="columns"
|
||||||
:loading="loading"
|
:column-pinning="columnPinning"
|
||||||
:pagination="pagination"
|
:row-selection="rowSelection"
|
||||||
:pagination-options="{
|
:loading="loading"
|
||||||
getPaginationRowModel: getPaginationRowModel()
|
:pagination="pagination"
|
||||||
}"
|
:pagination-options="{
|
||||||
:ui="{
|
getPaginationRowModel: getPaginationRowModel()
|
||||||
tr: 'divide-x divide-default'
|
}"
|
||||||
}"
|
:ui="{
|
||||||
sticky
|
tr: 'divide-x divide-default'
|
||||||
class="border border-accented rounded-sm"
|
}"
|
||||||
@select="onSelect"
|
sticky
|
||||||
>
|
class="border border-accented rounded-sm"
|
||||||
<template #expanded="{ row }">
|
@select="onSelect"
|
||||||
<pre>{{ row.original }}</pre>
|
@contextmenu="onContextmenu"
|
||||||
|
@pointermove="(ev: PointerEvent) => {
|
||||||
|
popoverAnchor.x = ev.clientX
|
||||||
|
popoverAnchor.y = ev.clientY
|
||||||
|
}"
|
||||||
|
@hover="onHover"
|
||||||
|
>
|
||||||
|
<template #expanded="{ row }">
|
||||||
|
<pre>{{ row.original }}</pre>
|
||||||
|
</template>
|
||||||
|
</UTable>
|
||||||
|
</UContextMenu>
|
||||||
|
|
||||||
|
<UPopover :content="{ side: 'top', sideOffset: 16, updatePositionStrategy: 'always' }" :open="popoverOpenDebounced" :reference="reference">
|
||||||
|
<template #content>
|
||||||
|
<div class="p-4">
|
||||||
|
{{ popoverRow?.original?.id }}
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</UTable>
|
</UPopover>
|
||||||
|
|
||||||
<div class="flex items-center justify-between gap-3">
|
<div class="flex items-center justify-between gap-3">
|
||||||
<div class="text-sm text-muted">
|
<div class="text-sm text-muted">
|
||||||
|
|||||||
@@ -9,17 +9,17 @@
|
|||||||
"typecheck": "nuxt typecheck"
|
"typecheck": "nuxt typecheck"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@iconify-json/lucide": "^1.2.51",
|
"@iconify-json/lucide": "^1.2.56",
|
||||||
"@iconify-json/simple-icons": "^1.2.39",
|
"@iconify-json/simple-icons": "^1.2.42",
|
||||||
"@internationalized/date": "^3.8.2",
|
"@internationalized/date": "^3.8.2",
|
||||||
"@nuxt/ui": "workspace:*",
|
"@nuxt/ui": "workspace:*",
|
||||||
"@nuxthub/core": "^0.9.0",
|
"@nuxthub/core": "^0.9.0",
|
||||||
"nuxt": "^3.17.5",
|
"nuxt": "^3.17.6",
|
||||||
"zod": "^3.25.67"
|
"zod": "^3.25.75"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
"vue-tsc": "^2.2.10"
|
"vue-tsc": "^3.0.1"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"unimport": "4.1.1"
|
"unimport": "4.1.1"
|
||||||
|
|||||||
3331
pnpm-lock.yaml
generated
3331
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -22,6 +22,12 @@
|
|||||||
"reka-ui",
|
"reka-ui",
|
||||||
"vaul-vue"
|
"vaul-vue"
|
||||||
]
|
]
|
||||||
|
}, {
|
||||||
|
"groupName": "vue-tsc",
|
||||||
|
"matchPackageNames": [
|
||||||
|
"vue-tsc",
|
||||||
|
"vue-component-type-helpers"
|
||||||
|
]
|
||||||
}, {
|
}, {
|
||||||
"matchDepTypes": ["peerDependencies"],
|
"matchDepTypes": ["peerDependencies"],
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { defu } from 'defu'
|
|||||||
/**
|
/**
|
||||||
* This plugin adds all the Nuxt UI components as auto-imports.
|
* This plugin adds all the Nuxt UI components as auto-imports.
|
||||||
*/
|
*/
|
||||||
export default function ComponentImportPlugin(options: NuxtUIOptions & { prefix: NonNullable<NuxtUIOptions['prefix']> }, meta: UnpluginContextMeta) {
|
export default function ComponentImportPlugin(options: NuxtUIOptions & { prefix: NonNullable<NuxtUIOptions['prefix']>, extraRuntimeDir?: string }, meta: UnpluginContextMeta) {
|
||||||
const components = globSync('**/*.vue', { cwd: join(runtimeDir, 'components') })
|
const components = globSync('**/*.vue', { cwd: join(runtimeDir, 'components') })
|
||||||
const componentNames = new Set(components.map(c => `${options.prefix}${c.replace(/\.vue$/, '')}`))
|
const componentNames = new Set(components.map(c => `${options.prefix}${c.replace(/\.vue$/, '')}`))
|
||||||
|
|
||||||
@@ -50,13 +50,15 @@ export default function ComponentImportPlugin(options: NuxtUIOptions & { prefix:
|
|||||||
name: 'nuxt:ui:components',
|
name: 'nuxt:ui:components',
|
||||||
enforce: 'pre',
|
enforce: 'pre',
|
||||||
resolveId(id, importer) {
|
resolveId(id, importer) {
|
||||||
// only apply to runtime nuxt ui components
|
if (!importer) {
|
||||||
if (!importer || !normalize(importer).includes(runtimeDir)) {
|
return
|
||||||
|
}
|
||||||
|
if (!normalize(importer).includes(runtimeDir) && (!options.extraRuntimeDir || !normalize(importer).includes(options.extraRuntimeDir))) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// only apply to relative imports
|
// only apply to relative imports or nuxt ui runtime components
|
||||||
if (!RELATIVE_IMPORT_RE.test(id)) {
|
if (!RELATIVE_IMPORT_RE.test(id) && !id.startsWith('@nuxt/ui/components/')) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ import { normalize } from 'pathe'
|
|||||||
import { resolvePathSync } from 'mlly'
|
import { resolvePathSync } from 'mlly'
|
||||||
import MagicString from 'magic-string'
|
import MagicString from 'magic-string'
|
||||||
|
|
||||||
import { runtimeDir, type NuxtUIOptions } from '../unplugin'
|
import { runtimeDir } from '../unplugin'
|
||||||
|
import type { NuxtUIOptions } from '../unplugin'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This plugin normalises Nuxt environment (#imports) and `import.meta.client` within the Nuxt UI components.
|
* This plugin normalises Nuxt environment (#imports) and `import.meta.client` within the Nuxt UI components.
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ import { genSafeVariableName } from 'knitwork'
|
|||||||
import MagicString from 'magic-string'
|
import MagicString from 'magic-string'
|
||||||
import { resolvePathSync } from 'mlly'
|
import { resolvePathSync } from 'mlly'
|
||||||
|
|
||||||
import { runtimeDir, type NuxtUIOptions } from '../unplugin'
|
import { runtimeDir } from '../unplugin'
|
||||||
|
import type { NuxtUIOptions } from '../unplugin'
|
||||||
|
|
||||||
import type { UnpluginOptions } from 'unplugin'
|
import type { UnpluginOptions } from 'unplugin'
|
||||||
|
|
||||||
|
|||||||
@@ -42,14 +42,15 @@ export interface ButtonSlots {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { type Ref, computed, ref, inject } from 'vue'
|
import { computed, ref, inject } from 'vue'
|
||||||
|
import type { Ref } from 'vue'
|
||||||
import { defu } from 'defu'
|
import { defu } from 'defu'
|
||||||
import { useForwardProps } from 'reka-ui'
|
import { useForwardProps } from 'reka-ui'
|
||||||
import { useAppConfig } from '#imports'
|
import { useAppConfig } from '#imports'
|
||||||
import { useComponentIcons } from '../composables/useComponentIcons'
|
import { useComponentIcons } from '../composables/useComponentIcons'
|
||||||
import { useButtonGroup } from '../composables/useButtonGroup'
|
import { useButtonGroup } from '../composables/useButtonGroup'
|
||||||
import { formLoadingInjectionKey } from '../composables/useFormField'
|
import { formLoadingInjectionKey } from '../composables/useFormField'
|
||||||
import { omit } from '../utils'
|
import { omit, mergeClasses } from '../utils'
|
||||||
import { tv } from '../utils/tv'
|
import { tv } from '../utils/tv'
|
||||||
import { pickLinkProps } from '../utils/link'
|
import { pickLinkProps } from '../utils/link'
|
||||||
import UIcon from './Icon.vue'
|
import UIcon from './Icon.vue'
|
||||||
@@ -57,11 +58,7 @@ import UAvatar from './Avatar.vue'
|
|||||||
import ULink from './Link.vue'
|
import ULink from './Link.vue'
|
||||||
import ULinkBase from './LinkBase.vue'
|
import ULinkBase from './LinkBase.vue'
|
||||||
|
|
||||||
const props = withDefaults(defineProps<ButtonProps>(), {
|
const props = defineProps<ButtonProps>()
|
||||||
active: undefined,
|
|
||||||
activeClass: '',
|
|
||||||
inactiveClass: ''
|
|
||||||
})
|
|
||||||
const slots = defineSlots<ButtonSlots>()
|
const slots = defineSlots<ButtonSlots>()
|
||||||
|
|
||||||
const appConfig = useAppConfig() as Button['AppConfig']
|
const appConfig = useAppConfig() as Button['AppConfig']
|
||||||
@@ -96,10 +93,10 @@ const ui = computed(() => tv({
|
|||||||
variants: {
|
variants: {
|
||||||
active: {
|
active: {
|
||||||
true: {
|
true: {
|
||||||
base: props.activeClass
|
base: mergeClasses(appConfig.ui?.button?.variants?.active?.true?.base, props.activeClass)
|
||||||
},
|
},
|
||||||
false: {
|
false: {
|
||||||
base: props.inactiveClass
|
base: mergeClasses(appConfig.ui?.button?.variants?.active?.false?.base, props.inactiveClass)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ export interface CarouselEmits {
|
|||||||
import { computed, ref, watch, onMounted } from 'vue'
|
import { computed, ref, watch, onMounted } from 'vue'
|
||||||
import useEmblaCarousel from 'embla-carousel-vue'
|
import useEmblaCarousel from 'embla-carousel-vue'
|
||||||
import { Primitive, useForwardProps } from 'reka-ui'
|
import { Primitive, useForwardProps } from 'reka-ui'
|
||||||
import { reactivePick, computedAsync } from '@vueuse/core'
|
import { reactivePick } from '@vueuse/core'
|
||||||
import { useAppConfig } from '#imports'
|
import { useAppConfig } from '#imports'
|
||||||
import { useLocale } from '../composables/useLocale'
|
import { useLocale } from '../composables/useLocale'
|
||||||
import { tv } from '../utils/tv'
|
import { tv } from '../utils/tv'
|
||||||
@@ -175,41 +175,45 @@ const options = computed<EmblaOptionsType>(() => ({
|
|||||||
direction: dir.value === 'rtl' ? 'rtl' : 'ltr'
|
direction: dir.value === 'rtl' ? 'rtl' : 'ltr'
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const plugins = computedAsync<EmblaPluginType[]>(async () => {
|
const plugins = ref<EmblaPluginType[]>([])
|
||||||
const plugins = []
|
|
||||||
|
async function loadPlugins() {
|
||||||
|
const emblaPlugins: EmblaPluginType[] = []
|
||||||
|
|
||||||
if (props.autoplay) {
|
if (props.autoplay) {
|
||||||
const AutoplayPlugin = await import('embla-carousel-autoplay').then(r => r.default)
|
const AutoplayPlugin = await import('embla-carousel-autoplay').then(r => r.default)
|
||||||
plugins.push(AutoplayPlugin(typeof props.autoplay === 'boolean' ? {} : props.autoplay))
|
emblaPlugins.push(AutoplayPlugin(typeof props.autoplay === 'boolean' ? {} : props.autoplay))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.autoScroll) {
|
if (props.autoScroll) {
|
||||||
const AutoScrollPlugin = await import('embla-carousel-auto-scroll').then(r => r.default)
|
const AutoScrollPlugin = await import('embla-carousel-auto-scroll').then(r => r.default)
|
||||||
plugins.push(AutoScrollPlugin(typeof props.autoScroll === 'boolean' ? {} : props.autoScroll))
|
emblaPlugins.push(AutoScrollPlugin(typeof props.autoScroll === 'boolean' ? {} : props.autoScroll))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.autoHeight) {
|
if (props.autoHeight) {
|
||||||
const AutoHeightPlugin = await import('embla-carousel-auto-height').then(r => r.default)
|
const AutoHeightPlugin = await import('embla-carousel-auto-height').then(r => r.default)
|
||||||
plugins.push(AutoHeightPlugin(typeof props.autoHeight === 'boolean' ? {} : props.autoHeight))
|
emblaPlugins.push(AutoHeightPlugin(typeof props.autoHeight === 'boolean' ? {} : props.autoHeight))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.classNames) {
|
if (props.classNames) {
|
||||||
const ClassNamesPlugin = await import('embla-carousel-class-names').then(r => r.default)
|
const ClassNamesPlugin = await import('embla-carousel-class-names').then(r => r.default)
|
||||||
plugins.push(ClassNamesPlugin(typeof props.classNames === 'boolean' ? {} : props.classNames))
|
emblaPlugins.push(ClassNamesPlugin(typeof props.classNames === 'boolean' ? {} : props.classNames))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.fade) {
|
if (props.fade) {
|
||||||
const FadePlugin = await import('embla-carousel-fade').then(r => r.default)
|
const FadePlugin = await import('embla-carousel-fade').then(r => r.default)
|
||||||
plugins.push(FadePlugin(typeof props.fade === 'boolean' ? {} : props.fade))
|
emblaPlugins.push(FadePlugin(typeof props.fade === 'boolean' ? {} : props.fade))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.wheelGestures) {
|
if (props.wheelGestures) {
|
||||||
const { WheelGesturesPlugin } = await import('embla-carousel-wheel-gestures')
|
const { WheelGesturesPlugin } = await import('embla-carousel-wheel-gestures')
|
||||||
plugins.push(WheelGesturesPlugin(typeof props.wheelGestures === 'boolean' ? {} : props.wheelGestures))
|
emblaPlugins.push(WheelGesturesPlugin(typeof props.wheelGestures === 'boolean' ? {} : props.wheelGestures))
|
||||||
}
|
}
|
||||||
|
|
||||||
return plugins
|
plugins.value = emblaPlugins
|
||||||
})
|
}
|
||||||
|
|
||||||
|
watch(() => [props.autoplay, props.autoScroll, props.autoHeight, props.classNames, props.fade, props.wheelGestures], loadPlugins, { immediate: true })
|
||||||
|
|
||||||
const [emblaRef, emblaApi] = useEmblaCarousel(options.value, plugins.value)
|
const [emblaRef, emblaApi] = useEmblaCarousel(options.value, plugins.value)
|
||||||
|
|
||||||
@@ -335,6 +339,7 @@ defineExpose({
|
|||||||
:aria-label="t('carousel.goto', { slide: index + 1 })"
|
:aria-label="t('carousel.goto', { slide: index + 1 })"
|
||||||
:class="ui.dot({ class: props.ui?.dot, active: selectedIndex === index })"
|
:class="ui.dot({ class: props.ui?.dot, active: selectedIndex === index })"
|
||||||
:data-state="selectedIndex === index ? 'active' : undefined"
|
:data-state="selectedIndex === index ? 'active' : undefined"
|
||||||
|
:aria-current="selectedIndex === index ? true : undefined"
|
||||||
@click="scrollTo(index)"
|
@click="scrollTo(index)"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -147,6 +147,7 @@ type SlotProps<T> = (props: { item: T, index: number }) => any
|
|||||||
|
|
||||||
export type CommandPaletteSlots<G extends CommandPaletteGroup<T> = CommandPaletteGroup<any>, T extends CommandPaletteItem = CommandPaletteItem> = {
|
export type CommandPaletteSlots<G extends CommandPaletteGroup<T> = CommandPaletteGroup<any>, T extends CommandPaletteItem = CommandPaletteItem> = {
|
||||||
'empty'(props: { searchTerm?: string }): any
|
'empty'(props: { searchTerm?: string }): any
|
||||||
|
'footer'(props: { ui: { [K in keyof Required<CommandPalette['slots']>]: (props?: Record<string, any>) => string } }): any
|
||||||
'back'(props: { ui: { [K in keyof Required<CommandPalette['slots']>]: (props?: Record<string, any>) => string } }): any
|
'back'(props: { ui: { [K in keyof Required<CommandPalette['slots']>]: (props?: Record<string, any>) => string } }): any
|
||||||
'close'(props: { ui: { [K in keyof Required<CommandPalette['slots']>]: (props?: Record<string, any>) => string } }): any
|
'close'(props: { ui: { [K in keyof Required<CommandPalette['slots']>]: (props?: Record<string, any>) => string } }): any
|
||||||
'item': SlotProps<T>
|
'item': SlotProps<T>
|
||||||
@@ -444,5 +445,9 @@ function onSelect(e: Event, item: T) {
|
|||||||
</slot>
|
</slot>
|
||||||
</div>
|
</div>
|
||||||
</ListboxContent>
|
</ListboxContent>
|
||||||
|
|
||||||
|
<div v-if="!!slots.footer" :class="ui.footer({ class: props.ui?.footer })">
|
||||||
|
<slot name="footer" :ui="ui" />
|
||||||
|
</div>
|
||||||
</ListboxRoot>
|
</ListboxRoot>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -47,7 +47,6 @@ import ULink from './Link.vue'
|
|||||||
import UAvatar from './Avatar.vue'
|
import UAvatar from './Avatar.vue'
|
||||||
import UIcon from './Icon.vue'
|
import UIcon from './Icon.vue'
|
||||||
import UKbd from './Kbd.vue'
|
import UKbd from './Kbd.vue'
|
||||||
// eslint-disable-next-line import/no-self-import
|
|
||||||
import UContextMenuContent from './ContextMenuContent.vue'
|
import UContextMenuContent from './ContextMenuContent.vue'
|
||||||
|
|
||||||
const props = defineProps<ContextMenuContentProps<T>>()
|
const props = defineProps<ContextMenuContentProps<T>>()
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ import ULink from './Link.vue'
|
|||||||
import UAvatar from './Avatar.vue'
|
import UAvatar from './Avatar.vue'
|
||||||
import UIcon from './Icon.vue'
|
import UIcon from './Icon.vue'
|
||||||
import UKbd from './Kbd.vue'
|
import UKbd from './Kbd.vue'
|
||||||
// eslint-disable-next-line import/no-self-import
|
|
||||||
import UDropdownMenuContent from './DropdownMenuContent.vue'
|
import UDropdownMenuContent from './DropdownMenuContent.vue'
|
||||||
|
|
||||||
const props = defineProps<DropdownMenuContentProps<T>>()
|
const props = defineProps<DropdownMenuContentProps<T>>()
|
||||||
|
|||||||
@@ -47,7 +47,8 @@ export interface FormFieldSlots {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref, inject, provide, type Ref, useId } from 'vue'
|
import { computed, ref, inject, provide, useId } from 'vue'
|
||||||
|
import type { Ref } from 'vue'
|
||||||
import { Primitive, Label } from 'reka-ui'
|
import { Primitive, Label } from 'reka-ui'
|
||||||
import { useAppConfig } from '#imports'
|
import { useAppConfig } from '#imports'
|
||||||
import { formFieldInjectionKey, inputIdInjectionKey } from '../composables/useFormField'
|
import { formFieldInjectionKey, inputIdInjectionKey } from '../composables/useFormField'
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import type { AcceptableValue, ComponentConfig } from '../types/utils'
|
|||||||
|
|
||||||
type Input = ComponentConfig<typeof theme, AppConfig, 'input'>
|
type Input = ComponentConfig<typeof theme, AppConfig, 'input'>
|
||||||
|
|
||||||
export interface InputProps extends UseComponentIconsProps {
|
export interface InputProps<T extends AcceptableValue = AcceptableValue> extends UseComponentIconsProps {
|
||||||
/**
|
/**
|
||||||
* The element or component this component should render as.
|
* The element or component this component should render as.
|
||||||
* @defaultValue 'div'
|
* @defaultValue 'div'
|
||||||
@@ -38,6 +38,8 @@ export interface InputProps extends UseComponentIconsProps {
|
|||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
/** Highlight the ring color like a focus state. */
|
/** Highlight the ring color like a focus state. */
|
||||||
highlight?: boolean
|
highlight?: boolean
|
||||||
|
modelValue?: T
|
||||||
|
defaultValue?: T
|
||||||
modelModifiers?: {
|
modelModifiers?: {
|
||||||
string?: boolean
|
string?: boolean
|
||||||
number?: boolean
|
number?: boolean
|
||||||
@@ -65,6 +67,7 @@ export interface InputSlots {
|
|||||||
<script setup lang="ts" generic="T extends AcceptableValue">
|
<script setup lang="ts" generic="T extends AcceptableValue">
|
||||||
import { ref, computed, onMounted } from 'vue'
|
import { ref, computed, onMounted } from 'vue'
|
||||||
import { Primitive } from 'reka-ui'
|
import { Primitive } from 'reka-ui'
|
||||||
|
import { useVModel } from '@vueuse/core'
|
||||||
import { useAppConfig } from '#imports'
|
import { useAppConfig } from '#imports'
|
||||||
import { useButtonGroup } from '../composables/useButtonGroup'
|
import { useButtonGroup } from '../composables/useButtonGroup'
|
||||||
import { useComponentIcons } from '../composables/useComponentIcons'
|
import { useComponentIcons } from '../composables/useComponentIcons'
|
||||||
@@ -76,7 +79,7 @@ import UAvatar from './Avatar.vue'
|
|||||||
|
|
||||||
defineOptions({ inheritAttrs: false })
|
defineOptions({ inheritAttrs: false })
|
||||||
|
|
||||||
const props = withDefaults(defineProps<InputProps>(), {
|
const props = withDefaults(defineProps<InputProps<T>>(), {
|
||||||
type: 'text',
|
type: 'text',
|
||||||
autocomplete: 'off',
|
autocomplete: 'off',
|
||||||
autofocusDelay: 0
|
autofocusDelay: 0
|
||||||
@@ -84,13 +87,12 @@ const props = withDefaults(defineProps<InputProps>(), {
|
|||||||
const emits = defineEmits<InputEmits<T>>()
|
const emits = defineEmits<InputEmits<T>>()
|
||||||
const slots = defineSlots<InputSlots>()
|
const slots = defineSlots<InputSlots>()
|
||||||
|
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
const modelValue = useVModel<InputProps<T>, 'modelValue', 'update:modelValue'>(props, 'modelValue', emits, { defaultValue: props.defaultValue })
|
||||||
const [modelValue, modelModifiers] = defineModel<T>()
|
|
||||||
|
|
||||||
const appConfig = useAppConfig() as Input['AppConfig']
|
const appConfig = useAppConfig() as Input['AppConfig']
|
||||||
|
|
||||||
const { emitFormBlur, emitFormInput, emitFormChange, size: formGroupSize, color, id, name, highlight, disabled, emitFormFocus, ariaAttrs } = useFormField<InputProps>(props, { deferInputValidation: true })
|
const { emitFormBlur, emitFormInput, emitFormChange, size: formGroupSize, color, id, name, highlight, disabled, emitFormFocus, ariaAttrs } = useFormField<InputProps<T>>(props, { deferInputValidation: true })
|
||||||
const { orientation, size: buttonGroupSize } = useButtonGroup<InputProps>(props)
|
const { orientation, size: buttonGroupSize } = useButtonGroup<InputProps<T>>(props)
|
||||||
const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponentIcons(props)
|
const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponentIcons(props)
|
||||||
|
|
||||||
const inputSize = computed(() => buttonGroupSize.value || formGroupSize.value)
|
const inputSize = computed(() => buttonGroupSize.value || formGroupSize.value)
|
||||||
@@ -111,15 +113,15 @@ const inputRef = ref<HTMLInputElement | null>(null)
|
|||||||
|
|
||||||
// Custom function to handle the v-model properties
|
// Custom function to handle the v-model properties
|
||||||
function updateInput(value: string | null) {
|
function updateInput(value: string | null) {
|
||||||
if (modelModifiers.trim) {
|
if (props.modelModifiers?.trim) {
|
||||||
value = value?.trim() ?? null
|
value = value?.trim() ?? null
|
||||||
}
|
}
|
||||||
|
|
||||||
if (modelModifiers.number || props.type === 'number') {
|
if (props.modelModifiers?.number || props.type === 'number') {
|
||||||
value = looseToNumber(value)
|
value = looseToNumber(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (modelModifiers.nullify) {
|
if (props.modelModifiers?.nullify) {
|
||||||
value ||= null
|
value ||= null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,7 +130,7 @@ function updateInput(value: string | null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onInput(event: Event) {
|
function onInput(event: Event) {
|
||||||
if (!modelModifiers.lazy) {
|
if (!props.modelModifiers?.lazy) {
|
||||||
updateInput((event.target as HTMLInputElement).value)
|
updateInput((event.target as HTMLInputElement).value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,12 +138,12 @@ function onInput(event: Event) {
|
|||||||
function onChange(event: Event) {
|
function onChange(event: Event) {
|
||||||
const value = (event.target as HTMLInputElement).value
|
const value = (event.target as HTMLInputElement).value
|
||||||
|
|
||||||
if (modelModifiers.lazy) {
|
if (props.modelModifiers?.lazy) {
|
||||||
updateInput(value)
|
updateInput(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update trimmed input so that it has same behavior as native input https://github.com/vuejs/core/blob/5ea8a8a4fab4e19a71e123e4d27d051f5e927172/packages/runtime-dom/src/directives/vModel.ts#L63
|
// Update trimmed input so that it has same behavior as native input https://github.com/vuejs/core/blob/5ea8a8a4fab4e19a71e123e4d27d051f5e927172/packages/runtime-dom/src/directives/vModel.ts#L63
|
||||||
if (modelModifiers.trim) {
|
if (props.modelModifiers?.trim) {
|
||||||
(event.target as HTMLInputElement).value = value.trim()
|
(event.target as HTMLInputElement).value = value.trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ export interface InputTagsProps<T extends InputTagItem = InputTagItem> extends P
|
|||||||
as?: any
|
as?: any
|
||||||
/** The placeholder text when the input is empty. */
|
/** The placeholder text when the input is empty. */
|
||||||
placeholder?: string
|
placeholder?: string
|
||||||
|
/** The maximum number of character allowed. */
|
||||||
|
maxLength?: number
|
||||||
/**
|
/**
|
||||||
* @defaultValue 'primary'
|
* @defaultValue 'primary'
|
||||||
*/
|
*/
|
||||||
@@ -182,6 +184,7 @@ defineExpose({
|
|||||||
ref="inputRef"
|
ref="inputRef"
|
||||||
v-bind="{ ...$attrs, ...ariaAttrs }"
|
v-bind="{ ...$attrs, ...ariaAttrs }"
|
||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
|
:max-length="maxLength"
|
||||||
:class="ui.input({ class: props.ui?.input })"
|
:class="ui.input({ class: props.ui?.input })"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
@@ -88,11 +88,12 @@ export interface LinkSlots {
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { defu } from 'defu'
|
|
||||||
import { isEqual } from 'ohash/utils'
|
import { isEqual } from 'ohash/utils'
|
||||||
import { useForwardProps } from 'reka-ui'
|
import { useForwardProps } from 'reka-ui'
|
||||||
|
import { defu } from 'defu'
|
||||||
import { reactiveOmit } from '@vueuse/core'
|
import { reactiveOmit } from '@vueuse/core'
|
||||||
import { useRoute, useAppConfig } from '#imports'
|
import { useRoute, useAppConfig } from '#imports'
|
||||||
|
import { mergeClasses } from '../utils'
|
||||||
import { tv } from '../utils/tv'
|
import { tv } from '../utils/tv'
|
||||||
import { isPartiallyEqual } from '../utils/link'
|
import { isPartiallyEqual } from '../utils/link'
|
||||||
import ULinkBase from './LinkBase.vue'
|
import ULinkBase from './LinkBase.vue'
|
||||||
@@ -103,9 +104,7 @@ const props = withDefaults(defineProps<LinkProps>(), {
|
|||||||
as: 'button',
|
as: 'button',
|
||||||
type: 'button',
|
type: 'button',
|
||||||
ariaCurrentValue: 'page',
|
ariaCurrentValue: 'page',
|
||||||
active: undefined,
|
active: undefined
|
||||||
activeClass: '',
|
|
||||||
inactiveClass: ''
|
|
||||||
})
|
})
|
||||||
defineSlots<LinkSlots>()
|
defineSlots<LinkSlots>()
|
||||||
|
|
||||||
@@ -119,8 +118,8 @@ const ui = computed(() => tv({
|
|||||||
...defu({
|
...defu({
|
||||||
variants: {
|
variants: {
|
||||||
active: {
|
active: {
|
||||||
true: props.activeClass,
|
true: mergeClasses(appConfig.ui?.link?.variants?.active?.true, props.activeClass),
|
||||||
false: props.inactiveClass
|
false: mergeClasses(appConfig.ui?.link?.variants?.active?.false, props.inactiveClass)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, appConfig.ui?.link || {})
|
}, appConfig.ui?.link || {})
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { useOverlay, type Overlay } from '../composables/useOverlay'
|
import { useOverlay } from '../composables/useOverlay'
|
||||||
|
import type { Overlay } from '../composables/useOverlay'
|
||||||
|
|
||||||
const { overlays, unmount, close } = useOverlay()
|
const { overlays, unmount, close } = useOverlay()
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { PopoverRootProps, HoverCardRootProps, PopoverRootEmits, PopoverContentProps, PopoverContentEmits, PopoverArrowProps } from 'reka-ui'
|
import type { PopoverRootProps, HoverCardRootProps, PopoverRootEmits, PopoverContentProps, PopoverContentEmits, PopoverArrowProps, HoverCardTriggerProps } from 'reka-ui'
|
||||||
import type { AppConfig } from '@nuxt/schema'
|
import type { AppConfig } from '@nuxt/schema'
|
||||||
import theme from '#build/ui/popover'
|
import theme from '#build/ui/popover'
|
||||||
import type { EmitsToProps, ComponentConfig } from '../types/utils'
|
import type { EmitsToProps, ComponentConfig } from '../types/utils'
|
||||||
@@ -27,6 +27,12 @@ export interface PopoverProps extends PopoverRootProps, Pick<HoverCardRootProps,
|
|||||||
* @defaultValue true
|
* @defaultValue true
|
||||||
*/
|
*/
|
||||||
portal?: boolean | string | HTMLElement
|
portal?: boolean | string | HTMLElement
|
||||||
|
/**
|
||||||
|
* The reference (or anchor) element that is being referred to for positioning.
|
||||||
|
*
|
||||||
|
* If not provided will use the current component as anchor.
|
||||||
|
*/
|
||||||
|
reference?: HoverCardTriggerProps['reference']
|
||||||
/**
|
/**
|
||||||
* When `false`, the popover will not close when clicking outside or pressing escape.
|
* When `false`, the popover will not close when clicking outside or pressing escape.
|
||||||
* @defaultValue true
|
* @defaultValue true
|
||||||
@@ -100,7 +106,7 @@ const Component = computed(() => props.mode === 'hover' ? HoverCard : Popover)
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Component.Root v-slot="{ open }" v-bind="rootProps">
|
<Component.Root v-slot="{ open }" v-bind="rootProps">
|
||||||
<Component.Trigger v-if="!!slots.default" as-child :class="props.class">
|
<Component.Trigger v-if="!!slots.default || !!reference" as-child :reference="reference" :class="props.class">
|
||||||
<slot :open="open" />
|
<slot :open="open" />
|
||||||
</Component.Trigger>
|
</Component.Trigger>
|
||||||
|
|
||||||
|
|||||||
@@ -83,10 +83,10 @@ export interface TableProps<T extends TableData = TableData> extends TableOption
|
|||||||
*/
|
*/
|
||||||
empty?: string
|
empty?: string
|
||||||
/**
|
/**
|
||||||
* Whether the table should have a sticky header.
|
* Whether the table should have a sticky header or footer. True for both, 'header' for header only, 'footer' for footer only.
|
||||||
* @defaultValue false
|
* @defaultValue false
|
||||||
*/
|
*/
|
||||||
sticky?: boolean
|
sticky?: boolean | 'header' | 'footer'
|
||||||
/** Whether the table should be in loading state. */
|
/** Whether the table should be in loading state. */
|
||||||
loading?: boolean
|
loading?: boolean
|
||||||
/**
|
/**
|
||||||
@@ -165,11 +165,14 @@ export interface TableProps<T extends TableData = TableData> extends TableOption
|
|||||||
*/
|
*/
|
||||||
facetedOptions?: FacetedOptions<T>
|
facetedOptions?: FacetedOptions<T>
|
||||||
onSelect?: (row: TableRow<T>, e?: Event) => void
|
onSelect?: (row: TableRow<T>, e?: Event) => void
|
||||||
|
onHover?: (e: Event, row: TableRow<T> | null) => void
|
||||||
|
onContextmenu?: ((e: Event, row: TableRow<T>) => void) | Array<((e: Event, row: TableRow<T>) => void)>
|
||||||
class?: any
|
class?: any
|
||||||
ui?: Table['slots']
|
ui?: Table['slots']
|
||||||
}
|
}
|
||||||
|
|
||||||
type DynamicHeaderSlots<T, K = keyof T> = Record<string, (props: HeaderContext<T, unknown>) => any> & Record<`${K extends string ? K : never}-header`, (props: HeaderContext<T, unknown>) => any>
|
type DynamicHeaderSlots<T, K = keyof T> = Record<string, (props: HeaderContext<T, unknown>) => any> & Record<`${K extends string ? K : never}-header`, (props: HeaderContext<T, unknown>) => any>
|
||||||
|
type DynamicFooterSlots<T, K = keyof T> = Record<string, (props: HeaderContext<T, unknown>) => any> & Record<`${K extends string ? K : never}-footer`, (props: HeaderContext<T, unknown>) => any>
|
||||||
type DynamicCellSlots<T, K = keyof T> = Record<string, (props: CellContext<T, unknown>) => any> & Record<`${K extends string ? K : never}-cell`, (props: CellContext<T, unknown>) => any>
|
type DynamicCellSlots<T, K = keyof T> = Record<string, (props: CellContext<T, unknown>) => any> & Record<`${K extends string ? K : never}-cell`, (props: CellContext<T, unknown>) => any>
|
||||||
|
|
||||||
export type TableSlots<T extends TableData = TableData> = {
|
export type TableSlots<T extends TableData = TableData> = {
|
||||||
@@ -179,7 +182,7 @@ export type TableSlots<T extends TableData = TableData> = {
|
|||||||
'caption': (props?: {}) => any
|
'caption': (props?: {}) => any
|
||||||
'body-top': (props?: {}) => any
|
'body-top': (props?: {}) => any
|
||||||
'body-bottom': (props?: {}) => any
|
'body-bottom': (props?: {}) => any
|
||||||
} & DynamicHeaderSlots<T> & DynamicCellSlots<T>
|
} & DynamicHeaderSlots<T> & DynamicFooterSlots<T> & DynamicCellSlots<T>
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -214,6 +217,22 @@ const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.table || {})
|
|||||||
loadingAnimation: props.loadingAnimation
|
loadingAnimation: props.loadingAnimation
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
const hasFooter = computed(() => {
|
||||||
|
function hasFooterRecursive(columns: TableColumn<T>[]): boolean {
|
||||||
|
for (const column of columns) {
|
||||||
|
if ('footer' in column) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if ('columns' in column && hasFooterRecursive(column.columns as TableColumn<T>[])) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return hasFooterRecursive(columns.value)
|
||||||
|
})
|
||||||
|
|
||||||
const globalFilterState = defineModel<string>('globalFilter', { default: undefined })
|
const globalFilterState = defineModel<string>('globalFilter', { default: undefined })
|
||||||
const columnFiltersState = defineModel<ColumnFiltersState>('columnFilters', { default: [] })
|
const columnFiltersState = defineModel<ColumnFiltersState>('columnFilters', { default: [] })
|
||||||
const columnOrderState = defineModel<ColumnOrderState>('columnOrder', { default: [] })
|
const columnOrderState = defineModel<ColumnOrderState>('columnOrder', { default: [] })
|
||||||
@@ -233,7 +252,9 @@ const tableRef = ref<HTMLTableElement | null>(null)
|
|||||||
const tableApi = useVueTable({
|
const tableApi = useVueTable({
|
||||||
...reactiveOmit(props, 'as', 'data', 'columns', 'caption', 'sticky', 'loading', 'loadingColor', 'loadingAnimation', 'class', 'ui'),
|
...reactiveOmit(props, 'as', 'data', 'columns', 'caption', 'sticky', 'loading', 'loadingColor', 'loadingAnimation', 'class', 'ui'),
|
||||||
data,
|
data,
|
||||||
columns: columns.value,
|
get columns() {
|
||||||
|
return columns.value
|
||||||
|
},
|
||||||
meta: meta.value,
|
meta: meta.value,
|
||||||
getCoreRowModel: getCoreRowModel(),
|
getCoreRowModel: getCoreRowModel(),
|
||||||
...(props.globalFilterOptions || {}),
|
...(props.globalFilterOptions || {}),
|
||||||
@@ -311,7 +332,7 @@ function valueUpdater<T extends Updater<any>>(updaterOrValue: T, ref: Ref) {
|
|||||||
ref.value = typeof updaterOrValue === 'function' ? updaterOrValue(ref.value) : updaterOrValue
|
ref.value = typeof updaterOrValue === 'function' ? updaterOrValue(ref.value) : updaterOrValue
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleRowSelect(row: TableRow<T>, e: Event) {
|
function onRowSelect(e: Event, row: TableRow<T>) {
|
||||||
if (!props.onSelect) {
|
if (!props.onSelect) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -324,9 +345,30 @@ function handleRowSelect(row: TableRow<T>, e: Event) {
|
|||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
|
|
||||||
|
// FIXME: `e` should be the first argument for consistency
|
||||||
props.onSelect(row, e)
|
props.onSelect(row, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onRowHover(e: Event, row: TableRow<T> | null) {
|
||||||
|
if (!props.onHover) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
props.onHover(e, row)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onRowContextmenu(e: Event, row: TableRow<T>) {
|
||||||
|
if (!props.onContextmenu) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(props.onContextmenu)) {
|
||||||
|
props.onContextmenu.forEach(fn => fn(e, row))
|
||||||
|
} else {
|
||||||
|
props.onContextmenu(e, row)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.data, () => {
|
() => props.data, () => {
|
||||||
data.value = props.data ? [...props.data] : []
|
data.value = props.data ? [...props.data] : []
|
||||||
@@ -354,6 +396,7 @@ defineExpose({
|
|||||||
v-for="header in headerGroup.headers"
|
v-for="header in headerGroup.headers"
|
||||||
:key="header.id"
|
:key="header.id"
|
||||||
:data-pinned="header.column.getIsPinned()"
|
:data-pinned="header.column.getIsPinned()"
|
||||||
|
:scope="header.colSpan > 1 ? 'colgroup' : 'col'"
|
||||||
:colspan="header.colSpan > 1 ? header.colSpan : undefined"
|
:colspan="header.colSpan > 1 ? header.colSpan : undefined"
|
||||||
:class="ui.th({
|
:class="ui.th({
|
||||||
class: [
|
class: [
|
||||||
@@ -379,7 +422,7 @@ defineExpose({
|
|||||||
<template v-for="row in tableApi.getRowModel().rows" :key="row.id">
|
<template v-for="row in tableApi.getRowModel().rows" :key="row.id">
|
||||||
<tr
|
<tr
|
||||||
:data-selected="row.getIsSelected()"
|
:data-selected="row.getIsSelected()"
|
||||||
:data-selectable="!!props.onSelect"
|
:data-selectable="!!props.onSelect || !!props.onHover || !!props.onContextmenu"
|
||||||
:data-expanded="row.getIsExpanded()"
|
:data-expanded="row.getIsExpanded()"
|
||||||
:role="props.onSelect ? 'button' : undefined"
|
:role="props.onSelect ? 'button' : undefined"
|
||||||
:tabindex="props.onSelect ? 0 : undefined"
|
:tabindex="props.onSelect ? 0 : undefined"
|
||||||
@@ -389,7 +432,10 @@ defineExpose({
|
|||||||
typeof tableApi.options.meta?.class?.tr === 'function' ? tableApi.options.meta.class.tr(row) : tableApi.options.meta?.class?.tr
|
typeof tableApi.options.meta?.class?.tr === 'function' ? tableApi.options.meta.class.tr(row) : tableApi.options.meta?.class?.tr
|
||||||
]
|
]
|
||||||
})"
|
})"
|
||||||
@click="handleRowSelect(row, $event)"
|
@click="onRowSelect($event, row)"
|
||||||
|
@pointerenter="onRowHover($event, row)"
|
||||||
|
@pointerleave="onRowHover($event, null)"
|
||||||
|
@contextmenu="onRowContextmenu($event, row)"
|
||||||
>
|
>
|
||||||
<td
|
<td
|
||||||
v-for="cell in row.getVisibleCells()"
|
v-for="cell in row.getVisibleCells()"
|
||||||
@@ -432,6 +478,30 @@ defineExpose({
|
|||||||
|
|
||||||
<slot name="body-bottom" />
|
<slot name="body-bottom" />
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
||||||
|
<tfoot v-if="hasFooter" :class="ui.tfoot({ class: [props.ui?.tfoot] })">
|
||||||
|
<tr :class="ui.separator({ class: [props.ui?.separator] })" />
|
||||||
|
|
||||||
|
<tr v-for="footerGroup in tableApi.getFooterGroups()" :key="footerGroup.id" :class="ui.tr({ class: [props.ui?.tr] })">
|
||||||
|
<th
|
||||||
|
v-for="header in footerGroup.headers"
|
||||||
|
:key="header.id"
|
||||||
|
:data-pinned="header.column.getIsPinned()"
|
||||||
|
:colspan="header.colSpan > 1 ? header.colSpan : undefined"
|
||||||
|
:class="ui.th({
|
||||||
|
class: [
|
||||||
|
props.ui?.th,
|
||||||
|
typeof header.column.columnDef.meta?.class?.th === 'function' ? header.column.columnDef.meta.class.th(header) : header.column.columnDef.meta?.class?.th
|
||||||
|
],
|
||||||
|
pinned: !!header.column.getIsPinned()
|
||||||
|
})"
|
||||||
|
>
|
||||||
|
<slot :name="`${header.id}-footer`" v-bind="header.getContext()">
|
||||||
|
<FlexRender v-if="!header.isPlaceholder" :render="header.column.columnDef.footer" :props="header.getContext()" />
|
||||||
|
</slot>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
</table>
|
</table>
|
||||||
</Primitive>
|
</Primitive>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ type Textarea = ComponentConfig<typeof theme, AppConfig, 'textarea'>
|
|||||||
|
|
||||||
type TextareaValue = string | number | null
|
type TextareaValue = string | number | null
|
||||||
|
|
||||||
export interface TextareaProps extends UseComponentIconsProps {
|
export interface TextareaProps<T extends TextareaValue = TextareaValue> extends UseComponentIconsProps {
|
||||||
/**
|
/**
|
||||||
* The element or component this component should render as.
|
* The element or component this component should render as.
|
||||||
* @defaultValue 'div'
|
* @defaultValue 'div'
|
||||||
@@ -41,8 +41,11 @@ export interface TextareaProps extends UseComponentIconsProps {
|
|||||||
maxrows?: number
|
maxrows?: number
|
||||||
/** Highlight the ring color like a focus state. */
|
/** Highlight the ring color like a focus state. */
|
||||||
highlight?: boolean
|
highlight?: boolean
|
||||||
|
modelValue?: T
|
||||||
|
defaultValue?: T
|
||||||
modelModifiers?: {
|
modelModifiers?: {
|
||||||
string?: boolean
|
string?: boolean
|
||||||
|
number?: boolean
|
||||||
trim?: boolean
|
trim?: boolean
|
||||||
lazy?: boolean
|
lazy?: boolean
|
||||||
nullify?: boolean
|
nullify?: boolean
|
||||||
@@ -67,6 +70,7 @@ export interface TextareaSlots {
|
|||||||
<script setup lang="ts" generic="T extends TextareaValue">
|
<script setup lang="ts" generic="T extends TextareaValue">
|
||||||
import { ref, computed, onMounted, nextTick, watch } from 'vue'
|
import { ref, computed, onMounted, nextTick, watch } from 'vue'
|
||||||
import { Primitive } from 'reka-ui'
|
import { Primitive } from 'reka-ui'
|
||||||
|
import { useVModel } from '@vueuse/core'
|
||||||
import { useAppConfig } from '#imports'
|
import { useAppConfig } from '#imports'
|
||||||
import { useComponentIcons } from '../composables/useComponentIcons'
|
import { useComponentIcons } from '../composables/useComponentIcons'
|
||||||
import { useFormField } from '../composables/useFormField'
|
import { useFormField } from '../composables/useFormField'
|
||||||
@@ -77,7 +81,7 @@ import UAvatar from './Avatar.vue'
|
|||||||
|
|
||||||
defineOptions({ inheritAttrs: false })
|
defineOptions({ inheritAttrs: false })
|
||||||
|
|
||||||
const props = withDefaults(defineProps<TextareaProps>(), {
|
const props = withDefaults(defineProps<TextareaProps<T>>(), {
|
||||||
rows: 3,
|
rows: 3,
|
||||||
maxrows: 0,
|
maxrows: 0,
|
||||||
autofocusDelay: 0,
|
autofocusDelay: 0,
|
||||||
@@ -86,12 +90,11 @@ const props = withDefaults(defineProps<TextareaProps>(), {
|
|||||||
const emits = defineEmits<TextareaEmits<T>>()
|
const emits = defineEmits<TextareaEmits<T>>()
|
||||||
const slots = defineSlots<TextareaSlots>()
|
const slots = defineSlots<TextareaSlots>()
|
||||||
|
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
const modelValue = useVModel<TextareaProps<T>, 'modelValue', 'update:modelValue'>(props, 'modelValue', emits, { defaultValue: props.defaultValue })
|
||||||
const [modelValue, modelModifiers] = defineModel<T>()
|
|
||||||
|
|
||||||
const appConfig = useAppConfig() as Textarea['AppConfig']
|
const appConfig = useAppConfig() as Textarea['AppConfig']
|
||||||
|
|
||||||
const { emitFormFocus, emitFormBlur, emitFormInput, emitFormChange, size, color, id, name, highlight, disabled, ariaAttrs } = useFormField<TextareaProps>(props, { deferInputValidation: true })
|
const { emitFormFocus, emitFormBlur, emitFormInput, emitFormChange, size, color, id, name, highlight, disabled, ariaAttrs } = useFormField<TextareaProps<T>>(props, { deferInputValidation: true })
|
||||||
const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponentIcons(props)
|
const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponentIcons(props)
|
||||||
|
|
||||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.textarea || {}) })({
|
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.textarea || {}) })({
|
||||||
@@ -109,15 +112,15 @@ const textareaRef = ref<HTMLTextAreaElement | null>(null)
|
|||||||
|
|
||||||
// Custom function to handle the v-model properties
|
// Custom function to handle the v-model properties
|
||||||
function updateInput(value: string | null) {
|
function updateInput(value: string | null) {
|
||||||
if (modelModifiers.trim) {
|
if (props.modelModifiers?.trim) {
|
||||||
value = value?.trim() ?? null
|
value = value?.trim() ?? null
|
||||||
}
|
}
|
||||||
|
|
||||||
if (modelModifiers.number) {
|
if (props.modelModifiers?.number) {
|
||||||
value = looseToNumber(value)
|
value = looseToNumber(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (modelModifiers.nullify) {
|
if (props.modelModifiers?.nullify) {
|
||||||
value ||= null
|
value ||= null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,7 +131,7 @@ function updateInput(value: string | null) {
|
|||||||
function onInput(event: Event) {
|
function onInput(event: Event) {
|
||||||
autoResize()
|
autoResize()
|
||||||
|
|
||||||
if (!modelModifiers.lazy) {
|
if (!props.modelModifiers?.lazy) {
|
||||||
updateInput((event.target as HTMLInputElement).value)
|
updateInput((event.target as HTMLInputElement).value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,12 +139,12 @@ function onInput(event: Event) {
|
|||||||
function onChange(event: Event) {
|
function onChange(event: Event) {
|
||||||
const value = (event.target as HTMLInputElement).value
|
const value = (event.target as HTMLInputElement).value
|
||||||
|
|
||||||
if (modelModifiers.lazy) {
|
if (props.modelModifiers?.lazy) {
|
||||||
updateInput(value)
|
updateInput(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update trimmed textarea so that it has same behavior as native textarea https://github.com/vuejs/core/blob/5ea8a8a4fab4e19a71e123e4d27d051f5e927172/packages/runtime-dom/src/directives/vModel.ts#L63
|
// Update trimmed textarea so that it has same behavior as native textarea https://github.com/vuejs/core/blob/5ea8a8a4fab4e19a71e123e4d27d051f5e927172/packages/runtime-dom/src/directives/vModel.ts#L63
|
||||||
if (modelModifiers.trim) {
|
if (props.modelModifiers?.trim) {
|
||||||
(event.target as HTMLInputElement).value = value.trim()
|
(event.target as HTMLInputElement).value = value.trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import type { ToastRootProps, ToastRootEmits } from 'reka-ui'
|
import type { ToastRootProps, ToastRootEmits } from 'reka-ui'
|
||||||
import type { AppConfig } from '@nuxt/schema'
|
import type { AppConfig } from '@nuxt/schema'
|
||||||
import theme from '#build/ui/toast'
|
import theme from '#build/ui/toast'
|
||||||
import type { AvatarProps, ButtonProps } from '../types'
|
import type { AvatarProps, ButtonProps, ProgressProps } from '../types'
|
||||||
import type { StringOrVNode, ComponentConfig } from '../types/utils'
|
import type { StringOrVNode, ComponentConfig } from '../types/utils'
|
||||||
|
|
||||||
type Toast = ComponentConfig<typeof theme, AppConfig, 'toast'>
|
type Toast = ComponentConfig<typeof theme, AppConfig, 'toast'>
|
||||||
@@ -29,18 +29,6 @@ export interface ToastProps extends Pick<ToastRootProps, 'defaultOpen' | 'open'
|
|||||||
* @defaultValue 'vertical'
|
* @defaultValue 'vertical'
|
||||||
*/
|
*/
|
||||||
orientation?: Toast['variants']['orientation']
|
orientation?: Toast['variants']['orientation']
|
||||||
/**
|
|
||||||
* Whether to show the progress bar.
|
|
||||||
* @defaultValue true
|
|
||||||
*/
|
|
||||||
progress?: boolean
|
|
||||||
/**
|
|
||||||
* Display a list of actions:
|
|
||||||
* - under the title and description when orientation is `vertical`
|
|
||||||
* - next to the close button when orientation is `horizontal`
|
|
||||||
* `{ size: 'xs' }`{lang="ts-type"}
|
|
||||||
*/
|
|
||||||
actions?: ButtonProps[]
|
|
||||||
/**
|
/**
|
||||||
* Display a close button to dismiss the toast.
|
* Display a close button to dismiss the toast.
|
||||||
* `{ size: 'md', color: 'neutral', variant: 'link' }`{lang="ts-type"}
|
* `{ size: 'md', color: 'neutral', variant: 'link' }`{lang="ts-type"}
|
||||||
@@ -53,6 +41,19 @@ export interface ToastProps extends Pick<ToastRootProps, 'defaultOpen' | 'open'
|
|||||||
* @IconifyIcon
|
* @IconifyIcon
|
||||||
*/
|
*/
|
||||||
closeIcon?: string
|
closeIcon?: string
|
||||||
|
/**
|
||||||
|
* Display a list of actions:
|
||||||
|
* - under the title and description when orientation is `vertical`
|
||||||
|
* - next to the close button when orientation is `horizontal`
|
||||||
|
* `{ size: 'xs' }`{lang="ts-type"}
|
||||||
|
*/
|
||||||
|
actions?: ButtonProps[]
|
||||||
|
/**
|
||||||
|
* Display a progress bar showing the toast's remaining duration.
|
||||||
|
* `{ size: 'sm' }`{lang="ts-type"}
|
||||||
|
* @defaultValue true
|
||||||
|
*/
|
||||||
|
progress?: boolean | Pick<ProgressProps, 'color'>
|
||||||
class?: any
|
class?: any
|
||||||
ui?: Toast['slots']
|
ui?: Toast['slots']
|
||||||
}
|
}
|
||||||
@@ -78,10 +79,11 @@ import { tv } from '../utils/tv'
|
|||||||
import UIcon from './Icon.vue'
|
import UIcon from './Icon.vue'
|
||||||
import UAvatar from './Avatar.vue'
|
import UAvatar from './Avatar.vue'
|
||||||
import UButton from './Button.vue'
|
import UButton from './Button.vue'
|
||||||
|
import UProgress from './Progress.vue'
|
||||||
|
|
||||||
const props = withDefaults(defineProps<ToastProps>(), {
|
const props = withDefaults(defineProps<ToastProps>(), {
|
||||||
close: true,
|
|
||||||
orientation: 'vertical',
|
orientation: 'vertical',
|
||||||
|
close: true,
|
||||||
progress: true
|
progress: true
|
||||||
})
|
})
|
||||||
const emits = defineEmits<ToastEmits>()
|
const emits = defineEmits<ToastEmits>()
|
||||||
@@ -119,7 +121,7 @@ defineExpose({
|
|||||||
<template>
|
<template>
|
||||||
<ToastRoot
|
<ToastRoot
|
||||||
ref="el"
|
ref="el"
|
||||||
v-slot="{ remaining, duration }"
|
v-slot="{ remaining, duration, open }"
|
||||||
v-bind="rootProps"
|
v-bind="rootProps"
|
||||||
:data-orientation="orientation"
|
:data-orientation="orientation"
|
||||||
:class="ui.root({ class: [props.ui?.root, props.class] })"
|
:class="ui.root({ class: [props.ui?.root, props.class] })"
|
||||||
@@ -184,6 +186,13 @@ defineExpose({
|
|||||||
</ToastClose>
|
</ToastClose>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="progress && remaining > 0 && duration" :class="ui.progress({ class: props.ui?.progress })" :style="{ width: `${remaining / duration * 100}%` }" />
|
<UProgress
|
||||||
|
v-if="progress && open && remaining > 0 && duration"
|
||||||
|
:model-value="remaining / duration * 100"
|
||||||
|
:color="color"
|
||||||
|
v-bind="(typeof progress === 'object' ? progress as Partial<ProgressProps> : {})"
|
||||||
|
size="sm"
|
||||||
|
:class="ui.progress({ class: props.ui?.progress })"
|
||||||
|
/>
|
||||||
</ToastRoot>
|
</ToastRoot>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -120,6 +120,8 @@ function getOffset(index: number) {
|
|||||||
:close="(toast.close as boolean)"
|
:close="(toast.close as boolean)"
|
||||||
:data-expanded="expanded"
|
:data-expanded="expanded"
|
||||||
:data-front="!expanded && index === toasts.length - 1"
|
:data-front="!expanded && index === toasts.length - 1"
|
||||||
|
:data-second="!expanded && index === toasts.length - 2"
|
||||||
|
:data-third="!expanded && index === toasts.length - 3"
|
||||||
:style="{
|
:style="{
|
||||||
'--index': (index - toasts.length) + toasts.length,
|
'--index': (index - toasts.length) + toasts.length,
|
||||||
'--before': toasts.length - 1 - index,
|
'--before': toasts.length - 1 - index,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { TooltipRootProps, TooltipRootEmits, TooltipContentProps, TooltipContentEmits, TooltipArrowProps } from 'reka-ui'
|
import type { TooltipRootProps, TooltipRootEmits, TooltipContentProps, TooltipContentEmits, TooltipArrowProps, TooltipTriggerProps } from 'reka-ui'
|
||||||
import type { AppConfig } from '@nuxt/schema'
|
import type { AppConfig } from '@nuxt/schema'
|
||||||
import theme from '#build/ui/tooltip'
|
import theme from '#build/ui/tooltip'
|
||||||
import type { KbdProps } from '../types'
|
import type { KbdProps } from '../types'
|
||||||
@@ -27,6 +27,12 @@ export interface TooltipProps extends TooltipRootProps {
|
|||||||
* @defaultValue true
|
* @defaultValue true
|
||||||
*/
|
*/
|
||||||
portal?: boolean | string | HTMLElement
|
portal?: boolean | string | HTMLElement
|
||||||
|
/**
|
||||||
|
* The reference (or anchor) element that is being referred to for positioning.
|
||||||
|
*
|
||||||
|
* If not provided will use the current component as anchor.
|
||||||
|
*/
|
||||||
|
reference?: TooltipTriggerProps['reference']
|
||||||
class?: any
|
class?: any
|
||||||
ui?: Tooltip['slots']
|
ui?: Tooltip['slots']
|
||||||
}
|
}
|
||||||
@@ -70,7 +76,7 @@ const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.tooltip || {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<TooltipRoot v-slot="{ open }" v-bind="rootProps">
|
<TooltipRoot v-slot="{ open }" v-bind="rootProps">
|
||||||
<TooltipTrigger v-if="!!slots.default" v-bind="$attrs" as-child :class="props.class">
|
<TooltipTrigger v-if="!!slots.default || !!reference" v-bind="$attrs" as-child :reference="reference" :class="props.class">
|
||||||
<slot :open="open" />
|
<slot :open="open" />
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ interface Shortcut {
|
|||||||
|
|
||||||
const chainedShortcutRegex = /^[^-]+.*-.*[^-]+$/
|
const chainedShortcutRegex = /^[^-]+.*-.*[^-]+$/
|
||||||
const combinedShortcutRegex = /^[^_]+.*_.*[^_]+$/
|
const combinedShortcutRegex = /^[^_]+.*_.*[^_]+$/
|
||||||
|
// keyboard keys which can be combined with Shift modifier (in addition to alphabet keys)
|
||||||
|
const shiftableKeys = ['arrowleft', 'arrowright', 'arrowup', 'arrowright', 'tab', 'escape', 'enter', 'backspace']
|
||||||
|
|
||||||
export function extractShortcuts(items: any[] | any[][]) {
|
export function extractShortcuts(items: any[] | any[][]) {
|
||||||
const shortcuts: Record<string, Handler> = {}
|
const shortcuts: Record<string, Handler> = {}
|
||||||
@@ -76,7 +78,8 @@ export function defineShortcuts(config: MaybeRef<ShortcutsConfig>, options: Shor
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const alphabeticalKey = /^[a-z]{1}$/i.test(e.key)
|
const alphabetKey = /^[a-z]{1}$/i.test(e.key)
|
||||||
|
const shiftableKey = shiftableKeys.includes(e.key.toLowerCase())
|
||||||
|
|
||||||
let chainedKey
|
let chainedKey
|
||||||
chainedInputs.value.push(e.key)
|
chainedInputs.value.push(e.key)
|
||||||
@@ -109,9 +112,9 @@ export function defineShortcuts(config: MaybeRef<ShortcutsConfig>, options: Shor
|
|||||||
if (e.ctrlKey !== shortcut.ctrlKey) {
|
if (e.ctrlKey !== shortcut.ctrlKey) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// shift modifier is only checked in combination with alphabetical keys
|
// shift modifier is only checked in combination with alphabet keys and some extra keys
|
||||||
// (shift with non-alphabetical keys would change the key)
|
// (shift with special characters would change the key)
|
||||||
if (alphabeticalKey && e.shiftKey !== shortcut.shiftKey) {
|
if ((alphabetKey || shiftableKey) && e.shiftKey !== shortcut.shiftKey) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// alt modifier changes the combined key anyways
|
// alt modifier changes the combined key anyways
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { inject, provide, computed, type ComputedRef, type InjectionKey } from 'vue'
|
import { inject, provide, computed } from 'vue'
|
||||||
|
import type { ComputedRef, InjectionKey } from 'vue'
|
||||||
import type { AvatarGroupProps } from '../types'
|
import type { AvatarGroupProps } from '../types'
|
||||||
|
|
||||||
export const avatarGroupInjectionKey: InjectionKey<ComputedRef<{ size: AvatarGroupProps['size'] }>> = Symbol('nuxt-ui.avatar-group')
|
export const avatarGroupInjectionKey: InjectionKey<ComputedRef<{ size: AvatarGroupProps['size'] }>> = Symbol('nuxt-ui.avatar-group')
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { computed, toValue, type MaybeRefOrGetter } from 'vue'
|
import { computed, toValue } from 'vue'
|
||||||
|
import type { MaybeRefOrGetter } from 'vue'
|
||||||
import { useAppConfig } from '#imports'
|
import { useAppConfig } from '#imports'
|
||||||
import type { AvatarProps } from '../types'
|
import type { AvatarProps } from '../types'
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { inject, computed, type InjectionKey, type Ref, type ComputedRef, provide } from 'vue'
|
import { inject, computed, provide } from 'vue'
|
||||||
import { type UseEventBusReturn, useDebounceFn } from '@vueuse/core'
|
import type { InjectionKey, Ref, ComputedRef } from 'vue'
|
||||||
|
import { useDebounceFn } from '@vueuse/core'
|
||||||
|
import type { UseEventBusReturn } from '@vueuse/core'
|
||||||
import type { FormFieldProps } from '../types'
|
import type { FormFieldProps } from '../types'
|
||||||
import type { FormEvent, FormInputEvents, FormFieldInjectedOptions, FormInjectedOptions } from '../types/form'
|
import type { FormEvent, FormInputEvents, FormFieldInjectedOptions, FormInjectedOptions } from '../types/form'
|
||||||
import type { GetObjectField } from '../types/utils'
|
import type { GetObjectField } from '../types/utils'
|
||||||
|
|||||||
@@ -3,9 +3,34 @@ import { reactive, markRaw, shallowReactive } from 'vue'
|
|||||||
import { createSharedComposable } from '@vueuse/core'
|
import { createSharedComposable } from '@vueuse/core'
|
||||||
import type { ComponentProps, ComponentEmit } from 'vue-component-type-helpers'
|
import type { ComponentProps, ComponentEmit } from 'vue-component-type-helpers'
|
||||||
|
|
||||||
// Extracts the first argument of the close event
|
/**
|
||||||
type CloseEventArgType<T> = T extends (event: 'close', args_0: infer R) => void ? R : never
|
* This is a workaround for a design limitation in TypeScript.
|
||||||
|
*
|
||||||
|
* Conditional types only match the last function overload, not a union of all possible
|
||||||
|
* parameter types. This workaround forces TypeScript to properly extract the 'close'
|
||||||
|
* event argument type from component emits with multiple event signatures.
|
||||||
|
*
|
||||||
|
* @see https://github.com/microsoft/TypeScript/issues/32164
|
||||||
|
*/
|
||||||
|
type CloseEventArgType<T> = T extends {
|
||||||
|
(event: 'close', arg_0: infer Arg, ...args: any[]): void
|
||||||
|
(...args: any[]): void
|
||||||
|
(...args: any[]): void
|
||||||
|
(...args: any[]): void
|
||||||
|
(...args: any[]): void
|
||||||
|
(...args: any[]): void
|
||||||
|
(...args: any[]): void
|
||||||
|
(...args: any[]): void
|
||||||
|
(...args: any[]): void
|
||||||
|
(...args: any[]): void
|
||||||
|
(...args: any[]): void
|
||||||
|
(...args: any[]): void
|
||||||
|
(...args: any[]): void
|
||||||
|
(...args: any[]): void
|
||||||
|
(...args: any[]): void
|
||||||
|
(...args: any[]): void
|
||||||
|
(...args: any[]): void
|
||||||
|
} ? Arg : never
|
||||||
export type OverlayOptions<OverlayAttrs = Record<string, any>> = {
|
export type OverlayOptions<OverlayAttrs = Record<string, any>> = {
|
||||||
defaultOpen?: boolean
|
defaultOpen?: boolean
|
||||||
props?: OverlayAttrs
|
props?: OverlayAttrs
|
||||||
@@ -46,7 +71,7 @@ function _useOverlay() {
|
|||||||
isMounted: !!defaultOpen,
|
isMounted: !!defaultOpen,
|
||||||
destroyOnClose: !!destroyOnClose,
|
destroyOnClose: !!destroyOnClose,
|
||||||
originalProps: props || {},
|
originalProps: props || {},
|
||||||
props: { ...(props || {}) }
|
props: { ...props }
|
||||||
})
|
})
|
||||||
|
|
||||||
overlays.push(options)
|
overlays.push(options)
|
||||||
@@ -110,7 +135,7 @@ function _useOverlay() {
|
|||||||
const patch = <T extends Component>(id: symbol, props: Partial<ComponentProps<T>>): void => {
|
const patch = <T extends Component>(id: symbol, props: Partial<ComponentProps<T>>): void => {
|
||||||
const overlay = getOverlay(id)
|
const overlay = getOverlay(id)
|
||||||
|
|
||||||
overlay.props = { ...props }
|
overlay.props = { ...overlay.props, ...props }
|
||||||
}
|
}
|
||||||
|
|
||||||
const getOverlay = (id: symbol): Overlay => {
|
const getOverlay = (id: symbol): Overlay => {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { inject, provide, computed, type Ref, type InjectionKey } from 'vue'
|
import { inject, provide, computed } from 'vue'
|
||||||
|
import type { Ref, InjectionKey } from 'vue'
|
||||||
|
|
||||||
export const portalTargetInjectionKey: InjectionKey<Ref<string | HTMLElement>> = Symbol('nuxt-ui.portal-target')
|
export const portalTargetInjectionKey: InjectionKey<Ref<string | HTMLElement>> = Symbol('nuxt-ui.portal-target')
|
||||||
|
|
||||||
|
|||||||
@@ -21,11 +21,11 @@ export interface Form<S extends FormSchema> {
|
|||||||
blurredFields: ReadonlySet<DeepReadonly<keyof FormData<S, false>>>
|
blurredFields: ReadonlySet<DeepReadonly<keyof FormData<S, false>>>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type FormSchema<I extends object = object, O extends object = I> =
|
export type FormSchema<I extends object = object, O extends object = I>
|
||||||
| YupObjectSchema<I>
|
= | YupObjectSchema<I>
|
||||||
| JoiSchema<I>
|
| JoiSchema<I>
|
||||||
| SuperstructSchema<any, any>
|
| SuperstructSchema<any, any>
|
||||||
| StandardSchemaV1<I, O>
|
| StandardSchemaV1<I, O>
|
||||||
|
|
||||||
// Define a utility type to infer the input type based on the schema type
|
// Define a utility type to infer the input type based on the schema type
|
||||||
export type InferInput<Schema> = Schema extends StandardSchemaV1 ? StandardSchemaV1.InferInput<Schema>
|
export type InferInput<Schema> = Schema extends StandardSchemaV1 ? StandardSchemaV1.InferInput<Schema>
|
||||||
@@ -83,10 +83,10 @@ export type FormInputEvent<T extends object> = {
|
|||||||
eager?: boolean
|
eager?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type FormEvent<T extends object> =
|
export type FormEvent<T extends object>
|
||||||
| FormInputEvent<T>
|
= | FormInputEvent<T>
|
||||||
| FormChildAttachEvent
|
| FormChildAttachEvent
|
||||||
| FormChildDetachEvent
|
| FormChildDetachEvent
|
||||||
|
|
||||||
export interface FormInjectedOptions {
|
export interface FormInjectedOptions {
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
|
|||||||
@@ -30,8 +30,8 @@ type ComponentSlots<T extends { slots?: Record<string, any> }> = Id<{
|
|||||||
[K in keyof T['slots']]?: ClassValue
|
[K in keyof T['slots']]?: ClassValue
|
||||||
}>
|
}>
|
||||||
|
|
||||||
type GetComponentAppConfig<A, U extends string, K extends string> =
|
type GetComponentAppConfig<A, U extends string, K extends string>
|
||||||
A extends Record<U, Record<K, any>> ? A[U][K] : {}
|
= A extends Record<U, Record<K, any>> ? A[U][K] : {}
|
||||||
|
|
||||||
type ComponentAppConfig<
|
type ComponentAppConfig<
|
||||||
T,
|
T,
|
||||||
|
|||||||
@@ -44,8 +44,8 @@ export type MergeTypes<T extends object> = {
|
|||||||
|
|
||||||
export type GetItemKeys<I> = keyof Extract<NestedItem<I>, object>
|
export type GetItemKeys<I> = keyof Extract<NestedItem<I>, object>
|
||||||
|
|
||||||
export type GetItemValue<I, VK extends GetItemKeys<I> | undefined, T extends NestedItem<I> = NestedItem<I>> =
|
export type GetItemValue<I, VK extends GetItemKeys<I> | undefined, T extends NestedItem<I> = NestedItem<I>>
|
||||||
T extends object
|
= T extends object
|
||||||
? VK extends undefined
|
? VK extends undefined
|
||||||
? T
|
? T
|
||||||
: VK extends keyof T
|
: VK extends keyof T
|
||||||
@@ -70,10 +70,10 @@ export type GetModelValueEmits<
|
|||||||
'update:modelValue': [payload: GetModelValue<T, VK, M>]
|
'update:modelValue': [payload: GetModelValue<T, VK, M>]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type StringOrVNode =
|
export type StringOrVNode
|
||||||
| string
|
= | string
|
||||||
| VNode
|
| VNode
|
||||||
| (() => VNode)
|
| (() => VNode)
|
||||||
|
|
||||||
export type EmitsToProps<T> = {
|
export type EmitsToProps<T> = {
|
||||||
[K in keyof T as `on${Capitalize<string & K>}`]: T[K] extends [...args: infer Args]
|
[K in keyof T as `on${Capitalize<string & K>}`]: T[K] extends [...args: infer Args]
|
||||||
|
|||||||
@@ -85,3 +85,14 @@ export function compare<T>(value?: T, currentValue?: T, comparator?: string | ((
|
|||||||
export function isArrayOfArray<A>(item: A[] | A[][]): item is A[][] {
|
export function isArrayOfArray<A>(item: A[] | A[][]): item is A[][] {
|
||||||
return Array.isArray(item[0])
|
return Array.isArray(item[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function mergeClasses(appConfigClass?: string | string[], propClass?: string) {
|
||||||
|
if (!appConfigClass && !propClass) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
...(Array.isArray(appConfigClass) ? appConfigClass : [appConfigClass]),
|
||||||
|
propClass
|
||||||
|
].filter(Boolean)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { createTV, type defaultConfig } from 'tailwind-variants'
|
import { createTV } from 'tailwind-variants'
|
||||||
|
import type { defaultConfig } from 'tailwind-variants'
|
||||||
import type { AppConfig } from '@nuxt/schema'
|
import type { AppConfig } from '@nuxt/schema'
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
|
|
||||||
|
|||||||
@@ -158,13 +158,13 @@ import colors from 'tailwindcss/colors'
|
|||||||
|
|
||||||
const icons = ${JSON.stringify(uiConfig.icons)};
|
const icons = ${JSON.stringify(uiConfig.icons)};
|
||||||
|
|
||||||
type NeutralColor = 'slate' | 'gray' | 'zinc' | 'neutral' | 'stone' | (string & {})
|
type NeutralColor = 'slate' | 'gray' | 'zinc' | 'neutral' | 'stone'
|
||||||
type Color = Exclude<keyof typeof colors, 'inherit' | 'current' | 'transparent' | 'black' | 'white' | NeutralColor> | (string & {})
|
type Color = Exclude<keyof typeof colors, 'inherit' | 'current' | 'transparent' | 'black' | 'white' | NeutralColor> | (string & {})
|
||||||
|
|
||||||
type AppConfigUI = {
|
type AppConfigUI = {
|
||||||
colors?: {
|
colors?: {
|
||||||
${options.theme?.colors?.map(color => `'${color}'?: Color`).join('\n\t\t')}
|
${options.theme?.colors?.map(color => `'${color}'?: Color`).join('\n\t\t')}
|
||||||
neutral?: NeutralColor
|
neutral?: NeutralColor | (string & {})
|
||||||
}
|
}
|
||||||
icons?: Partial<typeof icons>
|
icons?: Partial<typeof icons>
|
||||||
tv?: typeof defaultConfig
|
tv?: typeof defaultConfig
|
||||||
|
|||||||
@@ -86,51 +86,51 @@ export default (options: Required<ModuleOptions>) => ({
|
|||||||
compoundVariants: [...(options.theme.colors || []).map((color: string) => ({
|
compoundVariants: [...(options.theme.colors || []).map((color: string) => ({
|
||||||
color,
|
color,
|
||||||
variant: 'solid',
|
variant: 'solid',
|
||||||
class: `text-inverted bg-${color} hover:bg-${color}/75 disabled:bg-${color} aria-disabled:bg-${color} focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-${color}`
|
class: `text-inverted bg-${color} hover:bg-${color}/75 active:bg-${color}/75 disabled:bg-${color} aria-disabled:bg-${color} focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-${color}`
|
||||||
})), ...(options.theme.colors || []).map((color: string) => ({
|
})), ...(options.theme.colors || []).map((color: string) => ({
|
||||||
color,
|
color,
|
||||||
variant: 'outline',
|
variant: 'outline',
|
||||||
class: `ring ring-inset ring-${color}/50 text-${color} hover:bg-${color}/10 disabled:bg-transparent aria-disabled:bg-transparent dark:disabled:bg-transparent dark:aria-disabled:bg-transparent focus:outline-none focus-visible:ring-2 focus-visible:ring-${color}`
|
class: `ring ring-inset ring-${color}/50 text-${color} hover:bg-${color}/10 active:bg-${color}/10 disabled:bg-transparent aria-disabled:bg-transparent dark:disabled:bg-transparent dark:aria-disabled:bg-transparent focus:outline-none focus-visible:ring-2 focus-visible:ring-${color}`
|
||||||
})), ...(options.theme.colors || []).map((color: string) => ({
|
})), ...(options.theme.colors || []).map((color: string) => ({
|
||||||
color,
|
color,
|
||||||
variant: 'soft',
|
variant: 'soft',
|
||||||
class: `text-${color} bg-${color}/10 hover:bg-${color}/15 focus:outline-none focus-visible:bg-${color}/15 disabled:bg-${color}/10 aria-disabled:bg-${color}/10`
|
class: `text-${color} bg-${color}/10 hover:bg-${color}/15 active:bg-${color}/15 focus:outline-none focus-visible:bg-${color}/15 disabled:bg-${color}/10 aria-disabled:bg-${color}/10`
|
||||||
})), ...(options.theme.colors || []).map((color: string) => ({
|
})), ...(options.theme.colors || []).map((color: string) => ({
|
||||||
color,
|
color,
|
||||||
variant: 'subtle',
|
variant: 'subtle',
|
||||||
class: `text-${color} ring ring-inset ring-${color}/25 bg-${color}/10 hover:bg-${color}/15 disabled:bg-${color}/10 aria-disabled:bg-${color}/10 focus:outline-none focus-visible:ring-2 focus-visible:ring-${color}`
|
class: `text-${color} ring ring-inset ring-${color}/25 bg-${color}/10 hover:bg-${color}/15 active:bg-${color}/15 disabled:bg-${color}/10 aria-disabled:bg-${color}/10 focus:outline-none focus-visible:ring-2 focus-visible:ring-${color}`
|
||||||
})), ...(options.theme.colors || []).map((color: string) => ({
|
})), ...(options.theme.colors || []).map((color: string) => ({
|
||||||
color,
|
color,
|
||||||
variant: 'ghost',
|
variant: 'ghost',
|
||||||
class: `text-${color} hover:bg-${color}/10 focus:outline-none focus-visible:bg-${color}/10 disabled:bg-transparent aria-disabled:bg-transparent dark:disabled:bg-transparent dark:aria-disabled:bg-transparent`
|
class: `text-${color} hover:bg-${color}/10 active:bg-${color}/10 focus:outline-none focus-visible:bg-${color}/10 disabled:bg-transparent aria-disabled:bg-transparent dark:disabled:bg-transparent dark:aria-disabled:bg-transparent`
|
||||||
})), ...(options.theme.colors || []).map((color: string) => ({
|
})), ...(options.theme.colors || []).map((color: string) => ({
|
||||||
color,
|
color,
|
||||||
variant: 'link',
|
variant: 'link',
|
||||||
class: `text-${color} hover:text-${color}/75 disabled:text-${color} aria-disabled:text-${color} focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-${color}`
|
class: `text-${color} hover:text-${color}/75 active:text-${color}/75 disabled:text-${color} aria-disabled:text-${color} focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-${color}`
|
||||||
})), {
|
})), {
|
||||||
color: 'neutral',
|
color: 'neutral',
|
||||||
variant: 'solid',
|
variant: 'solid',
|
||||||
class: 'text-inverted bg-inverted hover:bg-inverted/90 disabled:bg-inverted aria-disabled:bg-inverted focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-inverted'
|
class: 'text-inverted bg-inverted hover:bg-inverted/90 active:bg-inverted/90 disabled:bg-inverted aria-disabled:bg-inverted focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-inverted'
|
||||||
}, {
|
}, {
|
||||||
color: 'neutral',
|
color: 'neutral',
|
||||||
variant: 'outline',
|
variant: 'outline',
|
||||||
class: 'ring ring-inset ring-accented text-default bg-default hover:bg-elevated disabled:bg-default aria-disabled:bg-default focus:outline-none focus-visible:ring-2 focus-visible:ring-inverted'
|
class: 'ring ring-inset ring-accented text-default bg-default hover:bg-elevated active:bg-elevated disabled:bg-default aria-disabled:bg-default focus:outline-none focus-visible:ring-2 focus-visible:ring-inverted'
|
||||||
}, {
|
}, {
|
||||||
color: 'neutral',
|
color: 'neutral',
|
||||||
variant: 'soft',
|
variant: 'soft',
|
||||||
class: 'text-default bg-elevated hover:bg-accented/75 focus:outline-none focus-visible:bg-accented/75 disabled:bg-elevated aria-disabled:bg-elevated'
|
class: 'text-default bg-elevated hover:bg-accented/75 active:bg-accented/75 focus:outline-none focus-visible:bg-accented/75 disabled:bg-elevated aria-disabled:bg-elevated'
|
||||||
}, {
|
}, {
|
||||||
color: 'neutral',
|
color: 'neutral',
|
||||||
variant: 'subtle',
|
variant: 'subtle',
|
||||||
class: 'ring ring-inset ring-accented text-default bg-elevated hover:bg-accented/75 disabled:bg-elevated aria-disabled:bg-elevated focus:outline-none focus-visible:ring-2 focus-visible:ring-inverted'
|
class: 'ring ring-inset ring-accented text-default bg-elevated hover:bg-accented/75 active:bg-accented/75 disabled:bg-elevated aria-disabled:bg-elevated focus:outline-none focus-visible:ring-2 focus-visible:ring-inverted'
|
||||||
}, {
|
}, {
|
||||||
color: 'neutral',
|
color: 'neutral',
|
||||||
variant: 'ghost',
|
variant: 'ghost',
|
||||||
class: 'text-default hover:bg-elevated focus:outline-none focus-visible:bg-elevated hover:disabled:bg-transparent dark:hover:disabled:bg-transparent hover:aria-disabled:bg-transparent dark:hover:aria-disabled:bg-transparent'
|
class: 'text-default hover:bg-elevated active:bg-elevated focus:outline-none focus-visible:bg-elevated hover:disabled:bg-transparent dark:hover:disabled:bg-transparent hover:aria-disabled:bg-transparent dark:hover:aria-disabled:bg-transparent'
|
||||||
}, {
|
}, {
|
||||||
color: 'neutral',
|
color: 'neutral',
|
||||||
variant: 'link',
|
variant: 'link',
|
||||||
class: 'text-muted hover:text-default disabled:text-muted aria-disabled:text-muted focus:outline-none focus-visible:ring-inset focus-visible:ring-2 focus-visible:ring-inverted'
|
class: 'text-muted hover:text-default active:text-default disabled:text-muted aria-disabled:text-muted focus:outline-none focus-visible:ring-inset focus-visible:ring-2 focus-visible:ring-inverted'
|
||||||
}, {
|
}, {
|
||||||
size: 'xs',
|
size: 'xs',
|
||||||
square: true,
|
square: true,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ export default (options: Required<ModuleOptions>) => ({
|
|||||||
close: '',
|
close: '',
|
||||||
back: 'p-0',
|
back: 'p-0',
|
||||||
content: 'relative overflow-hidden flex flex-col',
|
content: 'relative overflow-hidden flex flex-col',
|
||||||
|
footer: 'p-1',
|
||||||
viewport: 'relative divide-y divide-default scroll-py-1 overflow-y-auto flex-1 focus:outline-none',
|
viewport: 'relative divide-y divide-default scroll-py-1 overflow-y-auto flex-1 focus:outline-none',
|
||||||
group: 'p-1 isolate',
|
group: 'p-1 isolate',
|
||||||
empty: 'py-6 text-center text-sm text-muted',
|
empty: 'py-6 text-center text-sm text-muted',
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ export default (options: Required<ModuleOptions>) => ({
|
|||||||
caption: 'sr-only',
|
caption: 'sr-only',
|
||||||
thead: 'relative',
|
thead: 'relative',
|
||||||
tbody: 'divide-y divide-default [&>tr]:data-[selectable=true]:hover:bg-elevated/50 [&>tr]:data-[selectable=true]:focus-visible:outline-primary',
|
tbody: 'divide-y divide-default [&>tr]:data-[selectable=true]:hover:bg-elevated/50 [&>tr]:data-[selectable=true]:focus-visible:outline-primary',
|
||||||
|
tfoot: 'relative',
|
||||||
tr: 'data-[selected=true]:bg-elevated/50',
|
tr: 'data-[selected=true]:bg-elevated/50',
|
||||||
th: 'px-4 py-3.5 text-sm text-highlighted text-left rtl:text-right font-semibold [&:has([role=checkbox])]:pe-0',
|
th: 'px-4 py-3.5 text-sm text-highlighted text-left rtl:text-right font-semibold [&:has([role=checkbox])]:pe-0',
|
||||||
td: 'p-4 text-sm text-muted whitespace-nowrap [&:has([role=checkbox])]:pe-0',
|
td: 'p-4 text-sm text-muted whitespace-nowrap [&:has([role=checkbox])]:pe-0',
|
||||||
@@ -23,7 +24,14 @@ export default (options: Required<ModuleOptions>) => ({
|
|||||||
},
|
},
|
||||||
sticky: {
|
sticky: {
|
||||||
true: {
|
true: {
|
||||||
|
thead: 'sticky top-0 inset-x-0 bg-default/75 z-[1] backdrop-blur',
|
||||||
|
tfoot: 'sticky bottom-0 inset-x-0 bg-default/75 z-[1] backdrop-blur'
|
||||||
|
},
|
||||||
|
header: {
|
||||||
thead: 'sticky top-0 inset-x-0 bg-default/75 z-[1] backdrop-blur'
|
thead: 'sticky top-0 inset-x-0 bg-default/75 z-[1] backdrop-blur'
|
||||||
|
},
|
||||||
|
footer: {
|
||||||
|
tfoot: 'sticky bottom-0 inset-x-0 bg-default/75 z-[1] backdrop-blur'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
loading: {
|
loading: {
|
||||||
|
|||||||
@@ -10,20 +10,18 @@ export default (options: Required<ModuleOptions>) => ({
|
|||||||
avatar: 'shrink-0',
|
avatar: 'shrink-0',
|
||||||
avatarSize: '2xl',
|
avatarSize: '2xl',
|
||||||
actions: 'flex gap-1.5 shrink-0',
|
actions: 'flex gap-1.5 shrink-0',
|
||||||
progress: 'absolute inset-x-0 bottom-0 h-1 z-[-1]',
|
progress: 'absolute inset-x-0 bottom-0',
|
||||||
close: 'p-0'
|
close: 'p-0'
|
||||||
},
|
},
|
||||||
variants: {
|
variants: {
|
||||||
color: {
|
color: {
|
||||||
...Object.fromEntries((options.theme.colors || []).map((color: string) => [color, {
|
...Object.fromEntries((options.theme.colors || []).map((color: string) => [color, {
|
||||||
root: `focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-${color}`,
|
root: `focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-${color}`,
|
||||||
icon: `text-${color}`,
|
icon: `text-${color}`
|
||||||
progress: `bg-${color}`
|
|
||||||
}])),
|
}])),
|
||||||
neutral: {
|
neutral: {
|
||||||
root: 'focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-inverted',
|
root: 'focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-inverted',
|
||||||
icon: 'text-highlighted',
|
icon: 'text-highlighted'
|
||||||
progress: 'bg-inverted'
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
orientation: {
|
orientation: {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
export default {
|
export default {
|
||||||
slots: {
|
slots: {
|
||||||
viewport: 'fixed flex flex-col w-[calc(100%-2rem)] sm:w-96 z-[100] data-[expanded=true]:h-(--height) focus:outline-none',
|
viewport: 'fixed flex flex-col w-[calc(100%-2rem)] sm:w-96 z-[100] data-[expanded=true]:h-(--height) focus:outline-none transition-all duration-500 ease-in-out',
|
||||||
base: 'pointer-events-auto absolute inset-x-0 z-(--index) transform-(--transform) data-[expanded=false]:data-[front=false]:h-(--front-height) data-[expanded=false]:data-[front=false]:*:opacity-0 data-[front=false]:*:transition-opacity data-[front=false]:*:duration-100 data-[state=closed]:animate-[toast-closed_200ms_ease-in-out] data-[state=closed]:data-[expanded=false]:data-[front=false]:animate-[toast-collapsed-closed_200ms_ease-in-out] data-[swipe=move]:transition-none transition-[transform,translate,height] duration-200 ease-out'
|
base: 'pointer-events-auto absolute inset-x-0 z-(--index) transform-(--transform) data-[expanded=false]:data-[front=false]:max-h-[calc(var(--front-height)+0.5rem)] data-[expanded=false]:data-[front=false]:min-h-[3rem] data-[expanded=false]:data-[front=false]:overflow-hidden data-[expanded=false]:data-[front=false]:data-[second=false]:data-[third=false]:opacity-0 data-[expanded=false]:data-[second=true]:opacity-60 data-[expanded=false]:data-[third=true]:opacity-30 data-[expanded=false]:data-[front=false]:*:opacity-0 data-[front=false]:*:transition-opacity data-[front=false]:*:duration-200 transition-[transform,translate,max-height,opacity] duration-200 ease-out data-[state=closed]:animate-[toast-closed_200ms_ease-out] data-[state=closed]:data-[expanded=false]:data-[front=false]:animate-[toast-collapsed-closed_200ms_ease-out] data-[swipe=move]:transition-none'
|
||||||
},
|
},
|
||||||
variants: {
|
variants: {
|
||||||
position: {
|
position: {
|
||||||
@@ -35,13 +35,13 @@ export default {
|
|||||||
position: ['top-left', 'top-center', 'top-right'],
|
position: ['top-left', 'top-center', 'top-right'],
|
||||||
class: {
|
class: {
|
||||||
viewport: 'top-4',
|
viewport: 'top-4',
|
||||||
base: 'top-0 data-[state=open]:animate-[slide-in-from-top_200ms_ease-in-out]'
|
base: 'top-0 data-[state=open]:animate-[slide-in-from-top_280ms_cubic-bezier(0.4,0,0.6,1)]'
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
position: ['bottom-left', 'bottom-center', 'bottom-right'],
|
position: ['bottom-left', 'bottom-center', 'bottom-right'],
|
||||||
class: {
|
class: {
|
||||||
viewport: 'bottom-4',
|
viewport: 'bottom-4',
|
||||||
base: 'bottom-0 data-[state=open]:animate-[slide-in-from-bottom_200ms_ease-in-out]'
|
base: 'bottom-0 data-[state=open]:animate-[slide-in-from-bottom_280ms_cubic-bezier(0.4,0,0.6,1)]'
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
swipeDirection: ['left', 'right'],
|
swipeDirection: ['left', 'right'],
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { describe, it, expect } from 'vitest'
|
import { describe, it, expect } from 'vitest'
|
||||||
import Accordion, { type AccordionProps, type AccordionSlots } from '../../src/runtime/components/Accordion.vue'
|
import Accordion from '../../src/runtime/components/Accordion.vue'
|
||||||
|
import type { AccordionProps, AccordionSlots } from '../../src/runtime/components/Accordion.vue'
|
||||||
import ComponentRender from '../component-render'
|
import ComponentRender from '../component-render'
|
||||||
|
|
||||||
describe('Accordion', () => {
|
describe('Accordion', () => {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { describe, it, expect } from 'vitest'
|
import { describe, it, expect } from 'vitest'
|
||||||
import Alert, { type AlertProps, type AlertSlots } from '../../src/runtime/components/Alert.vue'
|
import Alert from '../../src/runtime/components/Alert.vue'
|
||||||
|
import type { AlertProps, AlertSlots } from '../../src/runtime/components/Alert.vue'
|
||||||
import ComponentRender from '../component-render'
|
import ComponentRender from '../component-render'
|
||||||
import theme from '#build/ui/alert'
|
import theme from '#build/ui/alert'
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { describe, it, expect } from 'vitest'
|
import { describe, it, expect } from 'vitest'
|
||||||
import Avatar, { type AvatarProps, type AvatarSlots } from '../../src/runtime/components/Avatar.vue'
|
import Avatar from '../../src/runtime/components/Avatar.vue'
|
||||||
|
import type { AvatarProps, AvatarSlots } from '../../src/runtime/components/Avatar.vue'
|
||||||
import ComponentRender from '../component-render'
|
import ComponentRender from '../component-render'
|
||||||
import theme from '#build/ui/avatar'
|
import theme from '#build/ui/avatar'
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { defineComponent } from 'vue'
|
import { defineComponent } from 'vue'
|
||||||
import { describe, it, expect } from 'vitest'
|
import { describe, it, expect } from 'vitest'
|
||||||
import Avatar from '../../src/runtime/components/Avatar.vue'
|
import Avatar from '../../src/runtime/components/Avatar.vue'
|
||||||
import AvatarGroup, { type AvatarGroupProps, type AvatarGroupSlots } from '../../src/runtime/components/AvatarGroup.vue'
|
import AvatarGroup from '../../src/runtime/components/AvatarGroup.vue'
|
||||||
|
import type { AvatarGroupProps, AvatarGroupSlots } from '../../src/runtime/components/AvatarGroup.vue'
|
||||||
import ComponentRender from '../component-render'
|
import ComponentRender from '../component-render'
|
||||||
import theme from '#build/ui/avatar-group'
|
import theme from '#build/ui/avatar-group'
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { describe, it, expect } from 'vitest'
|
import { describe, it, expect } from 'vitest'
|
||||||
import Badge, { type BadgeProps, type BadgeSlots } from '../../src/runtime/components/Badge.vue'
|
import Badge from '../../src/runtime/components/Badge.vue'
|
||||||
|
import type { BadgeProps, BadgeSlots } from '../../src/runtime/components/Badge.vue'
|
||||||
import ComponentRender from '../component-render'
|
import ComponentRender from '../component-render'
|
||||||
import theme from '#build/ui/badge'
|
import theme from '#build/ui/badge'
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { describe, it, expect } from 'vitest'
|
import { describe, it, expect } from 'vitest'
|
||||||
import Breadcrumb, { type BreadcrumbProps, type BreadcrumbSlots } from '../../src/runtime/components/Breadcrumb.vue'
|
import Breadcrumb from '../../src/runtime/components/Breadcrumb.vue'
|
||||||
|
import type { BreadcrumbProps, BreadcrumbSlots } from '../../src/runtime/components/Breadcrumb.vue'
|
||||||
import ComponentRender from '../component-render'
|
import ComponentRender from '../component-render'
|
||||||
|
|
||||||
describe('Breadcrumb', () => {
|
describe('Breadcrumb', () => {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { describe, it, expect, test } from 'vitest'
|
import { describe, it, expect, test } from 'vitest'
|
||||||
import Button, { type ButtonProps, type ButtonSlots } from '../../src/runtime/components/Button.vue'
|
import Button from '../../src/runtime/components/Button.vue'
|
||||||
|
import type { ButtonProps, ButtonSlots } from '../../src/runtime/components/Button.vue'
|
||||||
import ComponentRender from '../component-render'
|
import ComponentRender from '../component-render'
|
||||||
import theme from '#build/ui/button'
|
import theme from '#build/ui/button'
|
||||||
import { mountSuspended } from '@nuxt/test-utils/runtime'
|
import { mountSuspended } from '@nuxt/test-utils/runtime'
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { describe, it, expect } from 'vitest'
|
import { describe, it, expect } from 'vitest'
|
||||||
import ButtonGroup, { type ButtonGroupProps, type ButtonGroupSlots } from '../../src/runtime/components/ButtonGroup.vue'
|
import ButtonGroup from '../../src/runtime/components/ButtonGroup.vue'
|
||||||
|
import type { ButtonGroupProps, ButtonGroupSlots } from '../../src/runtime/components/ButtonGroup.vue'
|
||||||
import ComponentRender from '../component-render'
|
import ComponentRender from '../component-render'
|
||||||
import { UInput, UButton } from '#components'
|
import { UInput, UButton } from '#components'
|
||||||
import buttonTheme from '#build/ui/button'
|
import buttonTheme from '#build/ui/button'
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { describe, it, expect, vi, afterAll, test } from 'vitest'
|
import { describe, it, expect, vi, afterAll, test } from 'vitest'
|
||||||
import { mountSuspended } from '@nuxt/test-utils/runtime'
|
import { mountSuspended } from '@nuxt/test-utils/runtime'
|
||||||
import Calendar, { type CalendarProps, type CalendarSlots } from '../../src/runtime/components/Calendar.vue'
|
import Calendar from '../../src/runtime/components/Calendar.vue'
|
||||||
|
import type { CalendarProps, CalendarSlots } from '../../src/runtime/components/Calendar.vue'
|
||||||
import ComponentRender from '../component-render'
|
import ComponentRender from '../component-render'
|
||||||
import theme from '#build/ui/calendar'
|
import theme from '#build/ui/calendar'
|
||||||
import { CalendarDate } from '@internationalized/date'
|
import { CalendarDate } from '@internationalized/date'
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { describe, it, expect } from 'vitest'
|
import { describe, it, expect } from 'vitest'
|
||||||
import Card, { type CardProps, type CardSlots } from '../../src/runtime/components/Card.vue'
|
import Card from '../../src/runtime/components/Card.vue'
|
||||||
|
import type { CardProps, CardSlots } from '../../src/runtime/components/Card.vue'
|
||||||
import ComponentRender from '../component-render'
|
import ComponentRender from '../component-render'
|
||||||
import theme from '#build/ui/card'
|
import theme from '#build/ui/card'
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { defineComponent } from 'vue'
|
import { defineComponent } from 'vue'
|
||||||
import { describe, it, expect } from 'vitest'
|
import { describe, it, expect } from 'vitest'
|
||||||
import Carousel, { type CarouselProps, type CarouselSlots } from '../../src/runtime/components/Carousel.vue'
|
import Carousel from '../../src/runtime/components/Carousel.vue'
|
||||||
|
import type { CarouselProps, CarouselSlots } from '../../src/runtime/components/Carousel.vue'
|
||||||
import ComponentRender from '../component-render'
|
import ComponentRender from '../component-render'
|
||||||
|
|
||||||
const CarouselWrapper = defineComponent({
|
const CarouselWrapper = defineComponent({
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { describe, it, expect, test } from 'vitest'
|
import { describe, it, expect, test } from 'vitest'
|
||||||
import Checkbox, { type CheckboxProps, type CheckboxSlots } from '../../src/runtime/components/Checkbox.vue'
|
import Checkbox from '../../src/runtime/components/Checkbox.vue'
|
||||||
|
import type { CheckboxProps, CheckboxSlots } from '../../src/runtime/components/Checkbox.vue'
|
||||||
import ComponentRender from '../component-render'
|
import ComponentRender from '../component-render'
|
||||||
import theme from '#build/ui/checkbox'
|
import theme from '#build/ui/checkbox'
|
||||||
import { renderForm } from '../utils/form'
|
import { renderForm } from '../utils/form'
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { describe, it, expect, test } from 'vitest'
|
import { describe, it, expect, test } from 'vitest'
|
||||||
import CheckboxGroup, { type CheckboxGroupProps, type CheckboxGroupSlots } from '../../src/runtime/components/CheckboxGroup.vue'
|
import CheckboxGroup from '../../src/runtime/components/CheckboxGroup.vue'
|
||||||
|
import type { CheckboxGroupProps, CheckboxGroupSlots } from '../../src/runtime/components/CheckboxGroup.vue'
|
||||||
import ComponentRender from '../component-render'
|
import ComponentRender from '../component-render'
|
||||||
import theme from '#build/ui/checkbox-group'
|
import theme from '#build/ui/checkbox-group'
|
||||||
import themeCheckbox from '#build/ui/checkbox'
|
import themeCheckbox from '#build/ui/checkbox'
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { describe, it, expect } from 'vitest'
|
import { describe, it, expect } from 'vitest'
|
||||||
import Chip, { type ChipProps, type ChipSlots } from '../../src/runtime/components/Chip.vue'
|
import Chip from '../../src/runtime/components/Chip.vue'
|
||||||
|
import type { ChipProps, ChipSlots } from '../../src/runtime/components/Chip.vue'
|
||||||
import ComponentRender from '../component-render'
|
import ComponentRender from '../component-render'
|
||||||
import theme from '#build/ui/chip'
|
import theme from '#build/ui/chip'
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { describe, it, expect } from 'vitest'
|
import { describe, it, expect } from 'vitest'
|
||||||
import Collapsible, { type CollapsibleProps, type CollapsibleSlots } from '../../src/runtime/components/Collapsible.vue'
|
import Collapsible from '../../src/runtime/components/Collapsible.vue'
|
||||||
|
import type { CollapsibleProps, CollapsibleSlots } from '../../src/runtime/components/Collapsible.vue'
|
||||||
import ComponentRender from '../component-render'
|
import ComponentRender from '../component-render'
|
||||||
|
|
||||||
describe('Collapsible', () => {
|
describe('Collapsible', () => {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { describe, it, expect, test } from 'vitest'
|
import { describe, it, expect, test } from 'vitest'
|
||||||
import { mountSuspended } from '@nuxt/test-utils/runtime'
|
import { mountSuspended } from '@nuxt/test-utils/runtime'
|
||||||
import ColorPicker, { type ColorPickerProps } from '../../src/runtime/components/ColorPicker.vue'
|
import ColorPicker from '../../src/runtime/components/ColorPicker.vue'
|
||||||
|
import type { ColorPickerProps } from '../../src/runtime/components/ColorPicker.vue'
|
||||||
import ComponentRender from '../component-render'
|
import ComponentRender from '../component-render'
|
||||||
import theme from '#build/ui/color-picker'
|
import theme from '#build/ui/color-picker'
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { describe, it, expect } from 'vitest'
|
import { describe, it, expect } from 'vitest'
|
||||||
import CommandPalette, { type CommandPaletteProps, type CommandPaletteSlots } from '../../src/runtime/components/CommandPalette.vue'
|
import CommandPalette from '../../src/runtime/components/CommandPalette.vue'
|
||||||
|
import type { CommandPaletteProps, CommandPaletteSlots } from '../../src/runtime/components/CommandPalette.vue'
|
||||||
import ComponentRender from '../component-render'
|
import ComponentRender from '../component-render'
|
||||||
|
|
||||||
describe('CommandPalette', () => {
|
describe('CommandPalette', () => {
|
||||||
@@ -88,7 +89,8 @@ describe('CommandPalette', () => {
|
|||||||
['with item-label slot', { props, slots: { 'item-label': () => 'Item label slot' } }],
|
['with item-label slot', { props, slots: { 'item-label': () => 'Item label slot' } }],
|
||||||
['with item-trailing slot', { props, slots: { 'item-trailing': () => 'Item trailing slot' } }],
|
['with item-trailing slot', { props, slots: { 'item-trailing': () => 'Item trailing slot' } }],
|
||||||
['with custom slot', { props, slots: { custom: () => 'Custom slot' } }],
|
['with custom slot', { props, slots: { custom: () => 'Custom slot' } }],
|
||||||
['with close slot', { props: { ...props, close: true }, slots: { close: () => 'Close slot' } }]
|
['with close slot', { props: { ...props, close: true }, slots: { close: () => 'Close slot' } }],
|
||||||
|
['with footer slot', { props, slots: { footer: () => 'Footer slot' } }]
|
||||||
])('renders %s correctly', async (nameOrHtml: string, options: { props?: CommandPaletteProps, slots?: Partial<CommandPaletteSlots> }) => {
|
])('renders %s correctly', async (nameOrHtml: string, options: { props?: CommandPaletteProps, slots?: Partial<CommandPaletteSlots> }) => {
|
||||||
const html = await ComponentRender(nameOrHtml, options, CommandPalette)
|
const html = await ComponentRender(nameOrHtml, options, CommandPalette)
|
||||||
expect(html).toMatchSnapshot()
|
expect(html).toMatchSnapshot()
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { describe, it, expect } from 'vitest'
|
import { describe, it, expect } from 'vitest'
|
||||||
import Container, { type ContainerProps, type ContainerSlots } from '../../src/runtime/components/Container.vue'
|
import Container from '../../src/runtime/components/Container.vue'
|
||||||
|
import type { ContainerProps, ContainerSlots } from '../../src/runtime/components/Container.vue'
|
||||||
import ComponentRender from '../component-render'
|
import ComponentRender from '../component-render'
|
||||||
|
|
||||||
describe('Container', () => {
|
describe('Container', () => {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { h, defineComponent } from 'vue'
|
import { h, defineComponent } from 'vue'
|
||||||
import { describe, it, expect, test } from 'vitest'
|
import { describe, it, expect, test } from 'vitest'
|
||||||
import ContextMenu, { type ContextMenuProps, type ContextMenuSlots } from '../../src/runtime/components/ContextMenu.vue'
|
import ContextMenu from '../../src/runtime/components/ContextMenu.vue'
|
||||||
|
import type { ContextMenuProps, ContextMenuSlots } from '../../src/runtime/components/ContextMenu.vue'
|
||||||
import theme from '#build/ui/context-menu'
|
import theme from '#build/ui/context-menu'
|
||||||
import { mountSuspended } from '@nuxt/test-utils/runtime'
|
import { mountSuspended } from '@nuxt/test-utils/runtime'
|
||||||
import { expectSlotProps } from '../utils/types'
|
import { expectSlotProps } from '../utils/types'
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { describe, it, expect } from 'vitest'
|
import { describe, it, expect } from 'vitest'
|
||||||
import Drawer, { type DrawerProps, type DrawerSlots } from '../../src/runtime/components/Drawer.vue'
|
import Drawer from '../../src/runtime/components/Drawer.vue'
|
||||||
|
import type { DrawerProps, DrawerSlots } from '../../src/runtime/components/Drawer.vue'
|
||||||
import ComponentRender from '../component-render'
|
import ComponentRender from '../component-render'
|
||||||
import theme from '#build/ui/drawer'
|
import theme from '#build/ui/drawer'
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { describe, it, expect, test } from 'vitest'
|
import { describe, it, expect, test } from 'vitest'
|
||||||
import DropdownMenu, { type DropdownMenuProps, type DropdownMenuSlots } from '../../src/runtime/components/DropdownMenu.vue'
|
import DropdownMenu from '../../src/runtime/components/DropdownMenu.vue'
|
||||||
|
import type { DropdownMenuProps, DropdownMenuSlots } from '../../src/runtime/components/DropdownMenu.vue'
|
||||||
import ComponentRender from '../component-render'
|
import ComponentRender from '../component-render'
|
||||||
import theme from '#build/ui/dropdown-menu'
|
import theme from '#build/ui/dropdown-menu'
|
||||||
import { expectSlotProps } from '../utils/types'
|
import { expectSlotProps } from '../utils/types'
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { describe, it, expect, test } from 'vitest'
|
import { describe, it, expect, test } from 'vitest'
|
||||||
import { mount } from '@vue/test-utils'
|
import { mount } from '@vue/test-utils'
|
||||||
import Input, { type InputProps, type InputSlots } from '../../src/runtime/components/Input.vue'
|
import Input from '../../src/runtime/components/Input.vue'
|
||||||
|
import type { InputProps, InputSlots } from '../../src/runtime/components/Input.vue'
|
||||||
import ComponentRender from '../component-render'
|
import ComponentRender from '../component-render'
|
||||||
import theme from '#build/ui/input'
|
import theme from '#build/ui/input'
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { describe, it, expect, test } from 'vitest'
|
import { describe, it, expect, test } from 'vitest'
|
||||||
import InputMenu, { type InputMenuProps, type InputMenuSlots } from '../../src/runtime/components/InputMenu.vue'
|
import InputMenu from '../../src/runtime/components/InputMenu.vue'
|
||||||
|
import type { InputMenuProps, InputMenuSlots } from '../../src/runtime/components/InputMenu.vue'
|
||||||
import ComponentRender from '../component-render'
|
import ComponentRender from '../component-render'
|
||||||
import theme from '#build/ui/input'
|
import theme from '#build/ui/input'
|
||||||
import { renderForm } from '../utils/form'
|
import { renderForm } from '../utils/form'
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ import { describe, it, expect, test } from 'vitest'
|
|||||||
import { flushPromises } from '@vue/test-utils'
|
import { flushPromises } from '@vue/test-utils'
|
||||||
import { mountSuspended } from '@nuxt/test-utils/runtime'
|
import { mountSuspended } from '@nuxt/test-utils/runtime'
|
||||||
import { reactive } from 'vue'
|
import { reactive } from 'vue'
|
||||||
import InputNumber, { type InputNumberProps, type InputNumberSlots } from '../../src/runtime/components/InputNumber.vue'
|
import InputNumber from '../../src/runtime/components/InputNumber.vue'
|
||||||
|
import type { InputNumberProps, InputNumberSlots } from '../../src/runtime/components/InputNumber.vue'
|
||||||
import ComponentRender from '../component-render'
|
import ComponentRender from '../component-render'
|
||||||
import theme from '#build/ui/input-number'
|
import theme from '#build/ui/input-number'
|
||||||
import type { FormInputEvents } from '~/src/module'
|
import type { FormInputEvents } from '~/src/module'
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user