Compare commits
53 Commits
feat/updat
...
fix/form-o
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
385cbeec6c | ||
|
|
4d875c03a2 | ||
|
|
5aea866057 | ||
|
|
d5bcb0da59 | ||
|
|
0a3cc5a25d | ||
|
|
52735fdd40 | ||
|
|
59fd4d52e1 | ||
|
|
8e78eb15c8 | ||
|
|
b7fc69baa7 | ||
|
|
43153c4e91 | ||
|
|
d059efca25 | ||
|
|
eea14155aa | ||
|
|
4ba8503c60 | ||
|
|
fdee2522bb | ||
|
|
39c861a64b | ||
|
|
333b7e4c9b | ||
|
|
f42a79b5ef | ||
|
|
50012d4866 | ||
|
|
b558b8c5aa | ||
|
|
3447a062b6 | ||
|
|
063a23e738 | ||
|
|
da0150a9ec | ||
|
|
981de8b295 | ||
|
|
fb4c210b41 | ||
|
|
8c68af5e3b | ||
|
|
81b46ab880 | ||
|
|
619b6f2a0e | ||
|
|
25913188a7 | ||
|
|
f3098df84a | ||
|
|
3deed4c271 | ||
|
|
e6b1c238b9 | ||
|
|
626b023ddb | ||
|
|
4c667f75f4 | ||
|
|
e04dd53046 | ||
|
|
d25265c8b7 | ||
|
|
7d8353ffdc | ||
|
|
ba534f18b9 | ||
|
|
864083156a | ||
|
|
8435a0fe16 | ||
|
|
d339dcbfb8 | ||
|
|
cd7ab413f6 | ||
|
|
cea881abdc | ||
|
|
d227a105d8 | ||
|
|
f6ff157bc4 | ||
|
|
21fbd07639 | ||
|
|
de234e8aeb | ||
|
|
95a7707963 | ||
|
|
24b54f6d9a | ||
|
|
b3e37688d9 | ||
|
|
eeba3b4049 | ||
|
|
a0c9731f63 | ||
|
|
af1bf1bbde | ||
|
|
54a7d04217 |
2
.github/workflows/docs.yml
vendored
@@ -42,6 +42,8 @@ jobs:
|
||||
|
||||
- name: Build application
|
||||
run: pnpm run docs:build
|
||||
env:
|
||||
NODE_OPTIONS: '--max-old-space-size=8192'
|
||||
|
||||
- name: Deploy to NuxtHub
|
||||
uses: nuxt-hub/action@v1
|
||||
|
||||
3
.github/workflows/module.yml
vendored
@@ -18,7 +18,7 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest] # macos-latest, windows-latest
|
||||
os: [ubuntu-latest, windows-latest] # macos-latest
|
||||
node: [22]
|
||||
|
||||
env:
|
||||
@@ -65,6 +65,7 @@ jobs:
|
||||
run: pnpm run dev:vue:build
|
||||
|
||||
- name: Publish
|
||||
if: matrix.os != 'windows-latest'
|
||||
run: pnpx pkg-pr-new publish --compact --no-template --pnpm
|
||||
|
||||
starter-nuxt:
|
||||
|
||||
1
.npmrc
@@ -1,4 +1,3 @@
|
||||
shamefully-hoist=true
|
||||
auto-install-peers=true
|
||||
ignore-workspace-root-check=true
|
||||
shell-emulator=true
|
||||
|
||||
@@ -7,10 +7,13 @@ export default defineBuildConfig({
|
||||
'./src/vite'
|
||||
],
|
||||
rollup: {
|
||||
emitCJS: true
|
||||
},
|
||||
replace: {
|
||||
'process.env.DEV': 'false'
|
||||
replace: {
|
||||
delimiters: ['', ''],
|
||||
values: {
|
||||
// Used in development to import directly from theme
|
||||
'const isUiDev = true': 'const isUiDev = false'
|
||||
}
|
||||
}
|
||||
},
|
||||
hooks: {
|
||||
'mkdist:entry:options'(ctx, entry, options) {
|
||||
|
||||
@@ -31,13 +31,10 @@ const component = ({ name, primitive, pro, prose, content }) => {
|
||||
? `
|
||||
<script lang="ts">
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import _appConfig from '#build/app.config'
|
||||
import theme from '#build/${path}/${prose ? 'prose/' : ''}${content ? 'content/' : ''}${kebabName}'
|
||||
import { tv } from '../utils/tv'
|
||||
import type { ComponentConfig } from '../types/utils'
|
||||
|
||||
const appConfig${camelName} = _appConfig as AppConfig & { ${key}: { ${prose ? 'prose: { ' : ''}${camelName}: Partial<typeof theme> } }${prose ? ' }' : ''}
|
||||
|
||||
const ${camelName} = tv({ extend: tv(theme), ...(appConfig${camelName}.${key}?.${prose ? 'prose?.' : ''}${camelName} || {}) })
|
||||
type ${upperName} = ComponentConfig<typeof theme, AppConfig, ${upperName}${pro ? `, '${key}'` : ''}>
|
||||
|
||||
export interface ${upperName}Props {
|
||||
/**
|
||||
@@ -46,7 +43,7 @@ export interface ${upperName}Props {
|
||||
*/
|
||||
as?: any
|
||||
class?: any
|
||||
ui?: Partial<typeof ${camelName}.slots>
|
||||
ui?: ${upperName}['slots']
|
||||
}
|
||||
|
||||
export interface ${upperName}Slots {
|
||||
@@ -55,12 +52,17 @@ export interface ${upperName}Slots {
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { Primitive } from 'reka-ui'
|
||||
import { useAppConfig } from '#imports'
|
||||
import { tv } from '../utils/tv'
|
||||
|
||||
const props = defineProps<${upperName}Props>()
|
||||
defineSlots<${upperName}Slots>()
|
||||
|
||||
const ui = ${camelName}()
|
||||
const appConfig = useAppConfig() as ${upperName}['AppConfig']
|
||||
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.${camelName} || {}) })())
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -71,22 +73,16 @@ const ui = ${camelName}()
|
||||
`
|
||||
: `
|
||||
<script lang="ts">
|
||||
import type { VariantProps } from 'tailwind-variants'
|
||||
import type { ${upperName}RootProps, ${upperName}RootEmits } from 'reka-ui'
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import _appConfig from '#build/app.config'
|
||||
import theme from '#build/${path}/${prose ? 'prose/' : ''}${content ? 'content/' : ''}${kebabName}'
|
||||
import { tv } from '../utils/tv'
|
||||
import type { ComponentConfig } from '../types/utils'
|
||||
|
||||
const appConfig${camelName} = _appConfig as AppConfig & { ${key}: { ${prose ? 'prose: { ' : ''}${camelName}: Partial<typeof theme> } }${prose ? ' }' : ''}
|
||||
|
||||
const ${camelName} = tv({ extend: tv(theme), ...(appConfig${camelName}.${key}?.${prose ? 'prose?.' : ''}${camelName} || {}) })
|
||||
|
||||
type ${upperName}Variants = VariantProps<typeof ${camelName}>
|
||||
type ${upperName} = ComponentConfig<typeof theme, AppConfig, ${upperName}${pro ? `, '${key}'` : ''}>
|
||||
|
||||
export interface ${upperName}Props extends Pick<${upperName}RootProps> {
|
||||
class?: any
|
||||
ui?: Partial<typeof ${camelName}.slots>
|
||||
ui?: ${upperName}['slots']
|
||||
}
|
||||
|
||||
export interface ${upperName}Emits extends ${upperName}RootEmits {}
|
||||
@@ -95,16 +91,21 @@ export interface ${upperName}Slots {}
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { ${upperName}Root, useForwardPropsEmits } from 'reka-ui'
|
||||
import { reactivePick } from '@vueuse/core'
|
||||
import { useAppConfig } from '#imports'
|
||||
import { tv } from '../utils/tv'
|
||||
|
||||
const props = defineProps<${upperName}Props>()
|
||||
const emits = defineEmits<${upperName}Emits>()
|
||||
const slots = defineSlots<${upperName}Slots>()
|
||||
|
||||
const appConfig = useAppConfig() as ${upperName}['AppConfig']
|
||||
|
||||
const rootProps = useForwardPropsEmits(reactivePick(props), emits)
|
||||
|
||||
const ui = ${camelName}()
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.${camelName} || {}) })())
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -85,5 +85,5 @@ provide('navigation', mappedNavigation)
|
||||
</template>
|
||||
|
||||
<style>
|
||||
/* Safelist (do not remove): [&>div]:*:my-0 [&>div]:*:w-full h-64 !px-0 !py-0 !pt-0 !pb-0 !p-0 !justify-start !justify-end !min-h-96 h-136 */
|
||||
/* Safelist (do not remove): [&>div]:*:my-0 [&>div]:*:w-full h-64 !px-0 !py-0 !pt-0 !pb-0 !p-0 !justify-start !justify-end !min-h-96 h-136 max-h-[341px] */
|
||||
</style>
|
||||
|
||||
@@ -328,7 +328,7 @@ const { data: ast } = await useAsyncData(`component-code-${name}-${hash({ props:
|
||||
|
||||
<template>
|
||||
<div class="my-5">
|
||||
<div>
|
||||
<div class="relative">
|
||||
<div v-if="options.length" class="flex flex-wrap items-center gap-2.5 border border-(--ui-border-muted) border-b-0 relative rounded-t-[calc(var(--ui-radius)*1.5)] px-4 py-2.5 overflow-x-auto">
|
||||
<template v-for="option in options" :key="option.name">
|
||||
<UFormField
|
||||
|
||||
@@ -22,6 +22,7 @@ function getEmojiFlag(locale: string): string {
|
||||
hi: 'in', // Hindi -> India
|
||||
hy: 'am', // Armenian -> Armenia
|
||||
ja: 'jp', // Japanese -> Japan
|
||||
kk: 'kz', // Kazakh -> Kazakhstan
|
||||
km: 'kh', // Khmer -> Cambodia
|
||||
ko: 'kr', // Korean -> South Korea
|
||||
nb: 'no', // Norwegian Bokmål -> Norway
|
||||
|
||||
@@ -11,7 +11,6 @@ const items = shallowRef<AccordionItem[]>([
|
||||
{
|
||||
label: 'Colors',
|
||||
icon: 'i-lucide-swatch-book',
|
||||
slot: 'colors' as const,
|
||||
content: 'Choose a primary and a neutral color from your Tailwind CSS theme.'
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,26 +1,35 @@
|
||||
<script setup lang="ts">
|
||||
import type { BreadcrumbItem } from '@nuxt/ui'
|
||||
|
||||
const items = [{
|
||||
label: 'Home',
|
||||
to: '/'
|
||||
}, {
|
||||
slot: 'dropdown' as const,
|
||||
icon: 'i-lucide-ellipsis',
|
||||
children: [{
|
||||
label: 'Documentation'
|
||||
}, {
|
||||
label: 'Themes'
|
||||
}, {
|
||||
label: 'GitHub'
|
||||
}]
|
||||
}, {
|
||||
label: 'Components',
|
||||
to: '/components'
|
||||
}, {
|
||||
label: 'Breadcrumb',
|
||||
to: '/components/breadcrumb'
|
||||
}] satisfies BreadcrumbItem[]
|
||||
const items = [
|
||||
{
|
||||
label: 'Home',
|
||||
to: '/'
|
||||
},
|
||||
{
|
||||
slot: 'dropdown' as const,
|
||||
icon: 'i-lucide-ellipsis',
|
||||
children: [
|
||||
{
|
||||
label: 'Documentation'
|
||||
},
|
||||
{
|
||||
label: 'Themes'
|
||||
},
|
||||
{
|
||||
label: 'GitHub'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Components',
|
||||
to: '/components'
|
||||
},
|
||||
{
|
||||
label: 'Breadcrumb',
|
||||
to: '/components/breadcrumb'
|
||||
}
|
||||
] satisfies BreadcrumbItem[]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
<script setup lang="ts">
|
||||
import type { BreadcrumbItem } from '@nuxt/ui'
|
||||
|
||||
const items: BreadcrumbItem[] = [{
|
||||
label: 'Home',
|
||||
to: '/'
|
||||
}, {
|
||||
label: 'Components',
|
||||
to: '/components'
|
||||
}, {
|
||||
label: 'Breadcrumb',
|
||||
to: '/components/breadcrumb'
|
||||
}]
|
||||
const items: BreadcrumbItem[] = [
|
||||
{
|
||||
label: 'Home',
|
||||
to: '/'
|
||||
},
|
||||
{
|
||||
label: 'Components',
|
||||
to: '/components'
|
||||
},
|
||||
{
|
||||
label: 'Breadcrumb',
|
||||
to: '/components/breadcrumb'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,23 +1,30 @@
|
||||
<script setup lang="ts">
|
||||
import type { DropdownMenuItem } from '@nuxt/ui'
|
||||
|
||||
const items: DropdownMenuItem[] = [{
|
||||
label: 'Team',
|
||||
icon: 'i-lucide-users'
|
||||
}, {
|
||||
label: 'Invite users',
|
||||
icon: 'i-lucide-user-plus',
|
||||
children: [{
|
||||
label: 'Invite by email',
|
||||
icon: 'i-lucide-send-horizontal'
|
||||
}, {
|
||||
label: 'Invite by link',
|
||||
icon: 'i-lucide-link'
|
||||
}]
|
||||
}, {
|
||||
label: 'New team',
|
||||
icon: 'i-lucide-plus'
|
||||
}]
|
||||
const items: DropdownMenuItem[] = [
|
||||
{
|
||||
label: 'Team',
|
||||
icon: 'i-lucide-users'
|
||||
},
|
||||
{
|
||||
label: 'Invite users',
|
||||
icon: 'i-lucide-user-plus',
|
||||
children: [
|
||||
{
|
||||
label: 'Invite by email',
|
||||
icon: 'i-lucide-send-horizontal'
|
||||
},
|
||||
{
|
||||
label: 'Invite by link',
|
||||
icon: 'i-lucide-link'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'New team',
|
||||
icon: 'i-lucide-plus'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,76 +1,79 @@
|
||||
<script setup lang="ts">
|
||||
const groups = [{
|
||||
id: 'settings',
|
||||
items: [
|
||||
{
|
||||
label: 'Profile',
|
||||
icon: 'i-lucide-user',
|
||||
kbds: ['meta', 'P']
|
||||
},
|
||||
{
|
||||
label: 'Billing',
|
||||
icon: 'i-lucide-credit-card',
|
||||
kbds: ['meta', 'B'],
|
||||
slot: 'billing' as const
|
||||
},
|
||||
{
|
||||
label: 'Notifications',
|
||||
icon: 'i-lucide-bell'
|
||||
},
|
||||
{
|
||||
label: 'Security',
|
||||
icon: 'i-lucide-lock'
|
||||
}
|
||||
]
|
||||
}, {
|
||||
id: 'users',
|
||||
label: 'Users',
|
||||
slot: 'users' as const,
|
||||
items: [
|
||||
{
|
||||
label: 'Benjamin Canac',
|
||||
suffix: 'benjamincanac',
|
||||
to: 'https://github.com/benjamincanac',
|
||||
target: '_blank'
|
||||
},
|
||||
{
|
||||
label: 'Sylvain Marroufin',
|
||||
suffix: 'smarroufin',
|
||||
to: 'https://github.com/smarroufin',
|
||||
target: '_blank'
|
||||
},
|
||||
{
|
||||
label: 'Sébastien Chopin',
|
||||
suffix: 'atinux',
|
||||
to: 'https://github.com/atinux',
|
||||
target: '_blank'
|
||||
},
|
||||
{
|
||||
label: 'Romain Hamel',
|
||||
suffix: 'romhml',
|
||||
to: 'https://github.com/romhml',
|
||||
target: '_blank'
|
||||
},
|
||||
{
|
||||
label: 'Haytham A. Salama',
|
||||
suffix: 'Haythamasalama',
|
||||
to: 'https://github.com/Haythamasalama',
|
||||
target: '_blank'
|
||||
},
|
||||
{
|
||||
label: 'Daniel Roe',
|
||||
suffix: 'danielroe',
|
||||
to: 'https://github.com/danielroe',
|
||||
target: '_blank'
|
||||
},
|
||||
{
|
||||
label: 'Neil Richter',
|
||||
suffix: 'noook',
|
||||
to: 'https://github.com/noook',
|
||||
target: '_blank'
|
||||
}
|
||||
]
|
||||
}]
|
||||
const groups = [
|
||||
{
|
||||
id: 'settings',
|
||||
items: [
|
||||
{
|
||||
label: 'Profile',
|
||||
icon: 'i-lucide-user',
|
||||
kbds: ['meta', 'P']
|
||||
},
|
||||
{
|
||||
label: 'Billing',
|
||||
icon: 'i-lucide-credit-card',
|
||||
kbds: ['meta', 'B'],
|
||||
slot: 'billing' as const
|
||||
},
|
||||
{
|
||||
label: 'Notifications',
|
||||
icon: 'i-lucide-bell'
|
||||
},
|
||||
{
|
||||
label: 'Security',
|
||||
icon: 'i-lucide-lock'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'users',
|
||||
label: 'Users',
|
||||
slot: 'users' as const,
|
||||
items: [
|
||||
{
|
||||
label: 'Benjamin Canac',
|
||||
suffix: 'benjamincanac',
|
||||
to: 'https://github.com/benjamincanac',
|
||||
target: '_blank'
|
||||
},
|
||||
{
|
||||
label: 'Sylvain Marroufin',
|
||||
suffix: 'smarroufin',
|
||||
to: 'https://github.com/smarroufin',
|
||||
target: '_blank'
|
||||
},
|
||||
{
|
||||
label: 'Sébastien Chopin',
|
||||
suffix: 'atinux',
|
||||
to: 'https://github.com/atinux',
|
||||
target: '_blank'
|
||||
},
|
||||
{
|
||||
label: 'Romain Hamel',
|
||||
suffix: 'romhml',
|
||||
to: 'https://github.com/romhml',
|
||||
target: '_blank'
|
||||
},
|
||||
{
|
||||
label: 'Haytham A. Salama',
|
||||
suffix: 'Haythamasalama',
|
||||
to: 'https://github.com/Haythamasalama',
|
||||
target: '_blank'
|
||||
},
|
||||
{
|
||||
label: 'Daniel Roe',
|
||||
suffix: 'danielroe',
|
||||
to: 'https://github.com/danielroe',
|
||||
target: '_blank'
|
||||
},
|
||||
{
|
||||
label: 'Neil Richter',
|
||||
suffix: 'noook',
|
||||
to: 'https://github.com/noook',
|
||||
target: '_blank'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -3,14 +3,18 @@ import type { ContextMenuItem } from '@nuxt/ui'
|
||||
|
||||
const loading = ref(true)
|
||||
|
||||
const items: ContextMenuItem[] = [{
|
||||
label: 'Refresh the Page',
|
||||
slot: 'refresh'
|
||||
}, {
|
||||
label: 'Clear Cookies and Refresh'
|
||||
}, {
|
||||
label: 'Clear Cache and Refresh'
|
||||
}]
|
||||
const items = [
|
||||
{
|
||||
label: 'Refresh the Page',
|
||||
slot: 'refresh' as const
|
||||
},
|
||||
{
|
||||
label: 'Clear Cookies and Refresh'
|
||||
},
|
||||
{
|
||||
label: 'Clear Cache and Refresh'
|
||||
}
|
||||
] satisfies ContextMenuItem[]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -14,7 +14,7 @@ const validate = (state: any): FormError[] => {
|
||||
}
|
||||
|
||||
const toast = useToast()
|
||||
async function onSubmit(event: FormSubmitEvent<any>) {
|
||||
async function onSubmit(event: FormSubmitEvent<typeof state>) {
|
||||
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
|
||||
console.log(event.data)
|
||||
}
|
||||
|
||||
@@ -34,9 +34,10 @@ const schema = z.object({
|
||||
pin: z.string().regex(/^\d$/).array().length(5)
|
||||
})
|
||||
|
||||
type Schema = z.input<typeof schema>
|
||||
type Input = z.input<typeof schema>
|
||||
type Output = z.output<typeof schema>
|
||||
|
||||
const state = reactive<Partial<Schema>>({})
|
||||
const state = reactive<Partial<Input>>({})
|
||||
|
||||
const form = useTemplateRef('form')
|
||||
|
||||
@@ -47,7 +48,7 @@ const items = [
|
||||
]
|
||||
|
||||
const toast = useToast()
|
||||
async function onSubmit(event: FormSubmitEvent<any>) {
|
||||
async function onSubmit(event: FormSubmitEvent<Output>) {
|
||||
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
|
||||
console.log(event.data)
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ const state = reactive({
|
||||
})
|
||||
|
||||
const toast = useToast()
|
||||
async function onSubmit(event: FormSubmitEvent<any>) {
|
||||
async function onSubmit(event: FormSubmitEvent<typeof state>) {
|
||||
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
|
||||
console.log(event.data)
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ type NestedSchema = z.output<typeof nestedSchema>
|
||||
const state = reactive<Partial<Schema & NestedSchema>>({ })
|
||||
|
||||
const toast = useToast()
|
||||
async function onSubmit(event: FormSubmitEvent<any>) {
|
||||
async function onSubmit(event: FormSubmitEvent<Schema>) {
|
||||
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
|
||||
console.log(event.data)
|
||||
}
|
||||
@@ -39,7 +39,7 @@ async function onSubmit(event: FormSubmitEvent<any>) {
|
||||
<UCheckbox v-model="state.news" name="news" label="Register to our newsletter" @update:model-value="state.email = undefined" />
|
||||
</div>
|
||||
|
||||
<UForm v-if="state.news" :state="state" :schema="nestedSchema">
|
||||
<UForm v-if="state.news" :state="state" :schema="nestedSchema" nested>
|
||||
<UFormField label="Email" name="email">
|
||||
<UInput v-model="state.email" placeholder="john@lennon.com" />
|
||||
</UFormField>
|
||||
|
||||
@@ -34,7 +34,7 @@ function removeItem() {
|
||||
|
||||
const toast = useToast()
|
||||
|
||||
async function onSubmit(event: FormSubmitEvent<any>) {
|
||||
async function onSubmit(event: FormSubmitEvent<Schema>) {
|
||||
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
|
||||
console.log(event.data)
|
||||
}
|
||||
@@ -51,7 +51,14 @@ async function onSubmit(event: FormSubmitEvent<any>) {
|
||||
<UInput v-model="state.customer" placeholder="Wonka Industries" />
|
||||
</UFormField>
|
||||
|
||||
<UForm v-for="item, count in state.items" :key="count" :state="item" :schema="itemSchema" class="flex gap-2">
|
||||
<UForm
|
||||
v-for="item, count in state.items"
|
||||
:key="count"
|
||||
:state="item"
|
||||
:schema="itemSchema"
|
||||
nested
|
||||
class="flex gap-2"
|
||||
>
|
||||
<UFormField :label="!count ? 'Description' : undefined" name="description">
|
||||
<UInput v-model="item.description" />
|
||||
</UFormField>
|
||||
|
||||
@@ -14,7 +14,7 @@ const validate = (state: any): FormError[] => {
|
||||
}
|
||||
|
||||
const toast = useToast()
|
||||
async function onSubmit(event: FormSubmitEvent<any>) {
|
||||
async function onSubmit(event: FormSubmitEvent<typeof state>) {
|
||||
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
|
||||
console.log(event.data)
|
||||
}
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
<script setup lang="ts">
|
||||
import type { NavigationMenuItem } from '@nuxt/ui'
|
||||
|
||||
const items: NavigationMenuItem[] = [
|
||||
const items = [
|
||||
{
|
||||
label: 'Guide',
|
||||
icon: 'i-lucide-book-open'
|
||||
|
||||
},
|
||||
{
|
||||
label: 'Composables',
|
||||
icon: 'i-lucide-database'
|
||||
|
||||
},
|
||||
{
|
||||
label: 'Components',
|
||||
icon: 'i-lucide-box',
|
||||
slot: 'components'
|
||||
slot: 'components' as const
|
||||
}
|
||||
]
|
||||
] satisfies NavigationMenuItem[]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -40,7 +40,7 @@ const label = ref([])
|
||||
multiple
|
||||
placeholder="Search labels..."
|
||||
:groups="[{ id: 'labels', items }]"
|
||||
:ui="{ input: '[&>input]:h-8' }"
|
||||
:ui="{ input: '[&>input]:h-8 [&>input]:text-sm' }"
|
||||
/>
|
||||
</template>
|
||||
</UPopover>
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
<script setup lang="ts">
|
||||
import type { StepperItem } from '@nuxt/ui'
|
||||
|
||||
const items: StepperItem[] = [
|
||||
const items = [
|
||||
{
|
||||
slot: 'address',
|
||||
slot: 'address' as const,
|
||||
title: 'Address',
|
||||
description: 'Add your address here',
|
||||
icon: 'i-lucide-house'
|
||||
}, {
|
||||
slot: 'shipping',
|
||||
slot: 'shipping' as const,
|
||||
title: 'Shipping',
|
||||
description: 'Set your preferred shipping method',
|
||||
icon: 'i-lucide-truck'
|
||||
}, {
|
||||
slot: 'checkout',
|
||||
slot: 'checkout' as const,
|
||||
title: 'Checkout',
|
||||
description: 'Confirm your order'
|
||||
}
|
||||
]
|
||||
] satisfies StepperItem[]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -3,17 +3,14 @@ import type { StepperItem } from '@nuxt/ui'
|
||||
|
||||
const items: StepperItem[] = [
|
||||
{
|
||||
slot: 'address',
|
||||
title: 'Address',
|
||||
description: 'Add your address here',
|
||||
icon: 'i-lucide-house'
|
||||
}, {
|
||||
slot: 'shipping',
|
||||
title: 'Shipping',
|
||||
description: 'Set your preferred shipping method',
|
||||
icon: 'i-lucide-truck'
|
||||
}, {
|
||||
slot: 'checkout',
|
||||
title: 'Checkout',
|
||||
description: 'Confirm your order'
|
||||
}
|
||||
|
||||
@@ -6,21 +6,23 @@ const items = [
|
||||
label: 'app/',
|
||||
slot: 'app' as const,
|
||||
defaultExpanded: true,
|
||||
children: [{
|
||||
label: 'composables/',
|
||||
children: [
|
||||
{ label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
|
||||
{ label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'components/',
|
||||
defaultExpanded: true,
|
||||
children: [
|
||||
{ label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' },
|
||||
{ label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' }
|
||||
]
|
||||
}]
|
||||
children: [
|
||||
{
|
||||
label: 'composables/',
|
||||
children: [
|
||||
{ label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
|
||||
{ label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'components/',
|
||||
defaultExpanded: true,
|
||||
children: [
|
||||
{ label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' },
|
||||
{ label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{ label: 'app.vue', icon: 'i-vscode-icons-file-type-vue' },
|
||||
{ label: 'nuxt.config.ts', icon: 'i-vscode-icons-file-type-nuxt' }
|
||||
|
||||
@@ -5,22 +5,24 @@ const items: TreeItem[] = [
|
||||
{
|
||||
label: 'app/',
|
||||
value: 'app',
|
||||
children: [{
|
||||
label: 'composables/',
|
||||
value: 'composables',
|
||||
children: [
|
||||
{ label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
|
||||
{ label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'components/',
|
||||
value: 'components',
|
||||
children: [
|
||||
{ label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' },
|
||||
{ label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' }
|
||||
]
|
||||
}]
|
||||
children: [
|
||||
{
|
||||
label: 'composables/',
|
||||
value: 'composables',
|
||||
children: [
|
||||
{ label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
|
||||
{ label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'components/',
|
||||
value: 'components',
|
||||
children: [
|
||||
{ label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' },
|
||||
{ label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{ label: 'app.vue', icon: 'i-vscode-icons-file-type-vue' },
|
||||
{ label: 'nuxt.config.ts', icon: 'i-vscode-icons-file-type-nuxt' }
|
||||
|
||||
@@ -5,21 +5,23 @@ const items: TreeItem[] = [
|
||||
{
|
||||
label: 'app/',
|
||||
defaultExpanded: true,
|
||||
children: [{
|
||||
label: 'composables/',
|
||||
children: [
|
||||
{ label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
|
||||
{ label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'components/',
|
||||
defaultExpanded: true,
|
||||
children: [
|
||||
{ label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' },
|
||||
{ label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' }
|
||||
]
|
||||
}]
|
||||
children: [
|
||||
{
|
||||
label: 'composables/',
|
||||
children: [
|
||||
{ label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
|
||||
{ label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'components/',
|
||||
defaultExpanded: true,
|
||||
children: [
|
||||
{ label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' },
|
||||
{ label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{ label: 'app.vue', icon: 'i-vscode-icons-file-type-vue' },
|
||||
{ label: 'nuxt.config.ts', icon: 'i-vscode-icons-file-type-nuxt' }
|
||||
|
||||
@@ -8,21 +8,23 @@ const items: TreeItem[] = [
|
||||
onSelect: (e: Event) => {
|
||||
e.preventDefault()
|
||||
},
|
||||
children: [{
|
||||
label: 'composables/',
|
||||
children: [
|
||||
{ label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
|
||||
{ label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'components/',
|
||||
defaultExpanded: true,
|
||||
children: [
|
||||
{ label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' },
|
||||
{ label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' }
|
||||
]
|
||||
}]
|
||||
children: [
|
||||
{
|
||||
label: 'composables/',
|
||||
children: [
|
||||
{ label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
|
||||
{ label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'components/',
|
||||
defaultExpanded: true,
|
||||
children: [
|
||||
{ label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' },
|
||||
{ label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{ label: 'app.vue', icon: 'i-vscode-icons-file-type-vue' },
|
||||
{ label: 'nuxt.config.ts', icon: 'i-vscode-icons-file-type-nuxt' }
|
||||
|
||||
@@ -8,21 +8,23 @@ const items: TreeItem[] = [
|
||||
onToggle: (e: Event) => {
|
||||
e.preventDefault()
|
||||
},
|
||||
children: [{
|
||||
label: 'composables/',
|
||||
children: [
|
||||
{ label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
|
||||
{ label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'components/',
|
||||
defaultExpanded: true,
|
||||
children: [
|
||||
{ label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' },
|
||||
{ label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' }
|
||||
]
|
||||
}]
|
||||
children: [
|
||||
{
|
||||
label: 'composables/',
|
||||
children: [
|
||||
{ label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
|
||||
{ label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'components/',
|
||||
defaultExpanded: true,
|
||||
children: [
|
||||
{ label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' },
|
||||
{ label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{ label: 'app.vue', icon: 'i-vscode-icons-file-type-vue' },
|
||||
{ label: 'nuxt.config.ts', icon: 'i-vscode-icons-file-type-nuxt' }
|
||||
|
||||
@@ -67,9 +67,9 @@ if (!import.meta.prerender) {
|
||||
|
||||
const type = page.value?.path.includes('components') ? 'Vue Component ' : page.value?.path.includes('composables') ? 'Vue Composable ' : ''
|
||||
useSeoMeta({
|
||||
titleTemplate: `%s ${type}- Nuxt UI ${page.value.module === 'ui-pro' ? 'Pro' : ''} v3${page.value.framework === 'vue' ? ' for Vue' : ''}`,
|
||||
titleTemplate: `%s ${type}- Nuxt UI ${page.value.module === 'ui-pro' ? 'Pro' : ''} ${page.value.framework === 'vue' ? ' for Vue' : ''}`,
|
||||
title: page.value.navigation?.title ? page.value.navigation.title : page.value.title,
|
||||
ogTitle: `${page.value.navigation?.title ? page.value.navigation.title : page.value.title} ${type}- Nuxt UI ${page.value.module === 'ui-pro' ? 'Pro' : ''} v3${page.value.framework === 'vue' ? ' for Vue' : ''}`,
|
||||
ogTitle: `${page.value.navigation?.title ? page.value.navigation.title : page.value.title} ${type}- Nuxt UI ${page.value.module === 'ui-pro' ? 'Pro' : ''} ${page.value.framework === 'vue' ? ' for Vue' : ''}`,
|
||||
description: page.value.description,
|
||||
ogDescription: page.value.description
|
||||
})
|
||||
|
||||
@@ -7,7 +7,7 @@ const title = 'Vue Components'
|
||||
const description = 'Explore 99+ customizable UI components for Vue and Nuxt built with Tailwind CSS and Reka UI.'
|
||||
|
||||
useSeoMeta({
|
||||
titleTemplate: `%s - Nuxt UI`,
|
||||
titleTemplate: '%s - Nuxt UI',
|
||||
title,
|
||||
description,
|
||||
ogTitle: `${title} - Nuxt UI`,
|
||||
|
||||
@@ -5,6 +5,7 @@ import { animate } from 'motion-v'
|
||||
import { joinURL } from 'ufo'
|
||||
|
||||
const { url } = useSiteConfig()
|
||||
|
||||
useSeoMeta({
|
||||
title: page.title,
|
||||
description: page.description,
|
||||
|
||||
@@ -12,7 +12,7 @@ const { url } = useSiteConfig()
|
||||
useSeoMeta({
|
||||
title,
|
||||
description,
|
||||
ogTitle: `${title} - Nuxt UI Pro`,
|
||||
ogTitle: title,
|
||||
ogDescription: description,
|
||||
ogImage: joinURL(url, '/pro/og-image.png')
|
||||
})
|
||||
@@ -33,7 +33,7 @@ const activating = ref(false)
|
||||
const successMessage = ref()
|
||||
const errorMessage = ref('')
|
||||
|
||||
async function submit(event: FormSubmitEvent<any>) {
|
||||
async function submit(event: FormSubmitEvent<Schema>) {
|
||||
activating.value = true
|
||||
errorMessage.value = ''
|
||||
successMessage.value = ''
|
||||
|
||||
@@ -9,10 +9,10 @@ const { url } = useSiteConfig()
|
||||
|
||||
useSeoMeta({
|
||||
title: page.title,
|
||||
ogTitle: page.title,
|
||||
ogImage: joinURL(url, '/pro/og-image.png'),
|
||||
description: page.description,
|
||||
ogDescription: page.description
|
||||
ogTitle: page.title,
|
||||
ogDescription: page.description,
|
||||
ogImage: joinURL(url, '/pro/og-image.png')
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ const { url } = useSiteConfig()
|
||||
|
||||
useSeoMeta({
|
||||
title: page.title,
|
||||
ogTitle: page.title,
|
||||
description: page.description,
|
||||
ogTitle: page.title,
|
||||
ogDescription: page.description,
|
||||
ogImage: joinURL(url, '/pro/og-image.png')
|
||||
})
|
||||
|
||||
@@ -13,7 +13,7 @@ const description = page.value.description
|
||||
useSeoMeta({
|
||||
title,
|
||||
description,
|
||||
ogTitle: `${title} - Nuxt UI Pro`,
|
||||
ogTitle: title,
|
||||
ogDescription: description,
|
||||
ogImage: joinURL(url, '/pro/og-image.png')
|
||||
})
|
||||
|
||||
@@ -5,8 +5,9 @@ const description = 'Discover our Volta board for @nuxt/ui development status.'
|
||||
useSeoMeta({
|
||||
titleTemplate: '%s - Nuxt UI',
|
||||
title,
|
||||
ogTitle: 'Nuxt UI Roadmap',
|
||||
description
|
||||
description,
|
||||
ogTitle: `${title} - Nuxt UI`,
|
||||
ogDescription: description
|
||||
})
|
||||
|
||||
defineOgImageComponent('Docs', {
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
<script setup lang="ts">
|
||||
import { joinURL } from 'ufo'
|
||||
|
||||
const { data: page } = await useAsyncData('showcase', () => queryCollection('showcase').first())
|
||||
if (!page.value) {
|
||||
throw createError({ statusCode: 404, statusMessage: 'Page not found', fatal: true })
|
||||
}
|
||||
|
||||
const { url } = useSiteConfig()
|
||||
|
||||
useSeoMeta({
|
||||
titleTemplate: `%s - Nuxt UI`,
|
||||
title: page.value.title,
|
||||
description: page.value.description,
|
||||
ogTitle: `${page.value.title} - Nuxt UI`,
|
||||
ogDescription: page.value.description,
|
||||
ogImage: joinURL(url, '/og-image.png')
|
||||
ogDescription: page.value.description
|
||||
})
|
||||
|
||||
defineOgImageComponent('Docs', {
|
||||
headline: 'Community'
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -5,8 +5,9 @@ const description = 'The development of Nuxt UI is led by a community of develop
|
||||
useSeoMeta({
|
||||
titleTemplate: '%s - Nuxt UI',
|
||||
title,
|
||||
ogTitle: 'Nuxt UI Team',
|
||||
description
|
||||
description,
|
||||
ogTitle: `${title} - Nuxt UI`,
|
||||
ogDescription: description
|
||||
})
|
||||
|
||||
defineOgImageComponent('Docs', {
|
||||
|
||||
@@ -16,12 +16,12 @@ function handleMessage(message) {
|
||||
async function handleFormatMessage(message) {
|
||||
if (!globalThis.prettier) {
|
||||
await Promise.all([
|
||||
import('https://unpkg.com/prettier@3.5.2/standalone.js'),
|
||||
import('https://unpkg.com/prettier@3.5.2/plugins/babel.js'),
|
||||
import('https://unpkg.com/prettier@3.5.2/plugins/estree.js'),
|
||||
import('https://unpkg.com/prettier@3.5.2/plugins/html.js'),
|
||||
import('https://unpkg.com/prettier@3.5.2/plugins/markdown.js'),
|
||||
import('https://unpkg.com/prettier@3.5.2/plugins/typescript.js')
|
||||
import('https://cdn.jsdelivr.net/npm/prettier@3.5.2/standalone.js'),
|
||||
import('https://cdn.jsdelivr.net/npm/prettier@3.5.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.5.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.5.2/plugins/typescript.js')
|
||||
])
|
||||
}
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@ In Nuxt UI v2, we had a mix between a design system with `primary`, `gray`, `err
|
||||
|
||||
This change introduces several breaking changes that you need to be aware of:
|
||||
|
||||
1. The `gray` color has been renamed to `neutral`
|
||||
- The `gray` color has been renamed to `neutral`
|
||||
|
||||
```diff
|
||||
<template>
|
||||
@@ -203,7 +203,7 @@ You can also use the new [design tokens](/getting-started/theme#neutral-palette)
|
||||
```
|
||||
::
|
||||
|
||||
2. The `DEFAULT` shade that let you write `text-primary` no longer exists, you can use [color shades](/getting-started/theme#color-shades) instead:
|
||||
- The `DEFAULT` shade that let you write `text-primary` no longer exists, you can use [color shades](/getting-started/theme#color-shades) instead:
|
||||
|
||||
```diff
|
||||
<template>
|
||||
@@ -212,7 +212,7 @@ You can also use the new [design tokens](/getting-started/theme#neutral-palette)
|
||||
</template>
|
||||
```
|
||||
|
||||
3. The `gray`, `black` and `white` in the `color` props have been removed in favor of `neutral`:
|
||||
- The `gray`, `black` and `white` in the `color` props have been removed in favor of `neutral`:
|
||||
|
||||
```diff
|
||||
- <UButton color="black" />
|
||||
@@ -225,7 +225,7 @@ You can also use the new [design tokens](/getting-started/theme#neutral-palette)
|
||||
+ <UButton color="neutral" variant="outline" />
|
||||
```
|
||||
|
||||
4. You can no longer use Tailwind CSS colors in the `color` props, use the new aliases instead:
|
||||
- You can no longer use Tailwind CSS colors in the `color` props, use the new aliases instead:
|
||||
|
||||
```diff
|
||||
- <UButton color="red" />
|
||||
@@ -236,7 +236,7 @@ You can also use the new [design tokens](/getting-started/theme#neutral-palette)
|
||||
Learn how to extend the design system to add new color aliases.
|
||||
::
|
||||
|
||||
5. The color configuration in `app.config.ts` has been moved into a `colors` object:
|
||||
- The color configuration in `app.config.ts` has been moved into a `colors` object:
|
||||
|
||||
```diff
|
||||
export default defineAppConfig({
|
||||
@@ -255,7 +255,7 @@ export default defineAppConfig({
|
||||
|
||||
Nuxt UI components are now styled using the [Tailwind Variants API](/getting-started/theme#components-theme), which makes all the overrides you made using the `app.config.ts` and the `ui` prop obsolete.
|
||||
|
||||
1. Update your [`app.config.ts`](/getting-started/theme#config) to override components with their new theme:
|
||||
- Update your [`app.config.ts`](/getting-started/theme#config) to override components with their new theme:
|
||||
|
||||
```diff
|
||||
export default defineAppConfig({
|
||||
@@ -278,7 +278,7 @@ export default defineAppConfig({
|
||||
})
|
||||
```
|
||||
|
||||
2. Update your [`ui` props](/getting-started/theme#props) to override each component's slots using their new theme:
|
||||
- Update your [`ui` props](/getting-started/theme#props) to override each component's slots using their new theme:
|
||||
|
||||
```diff
|
||||
<template>
|
||||
@@ -351,7 +351,7 @@ Here are the Nuxt UI Pro components that have been renamed or removed:
|
||||
|
||||
In addition to the renamed components, there are lots of changes to the components API. Let's detail the most important ones:
|
||||
|
||||
1. The `links` and `options` props have been renamed to `items` for consistency:
|
||||
- The `links` and `options` props have been renamed to `items` for consistency:
|
||||
|
||||
```diff
|
||||
<template>
|
||||
@@ -367,7 +367,25 @@ In addition to the renamed components, there are lots of changes to the componen
|
||||
This change affects the following components: `Breadcrumb`, `HorizontalNavigation`, `InputMenu`, `RadioGroup`, `Select`, `SelectMenu`, `VerticalNavigation`.
|
||||
::
|
||||
|
||||
2. The global `Modals`, `Slideovers` and `Notifications` components have been removed in favor the [App](/components/app) component:
|
||||
- The `click` field in different components has been removed in favor of the native Vue `onClick` event:
|
||||
|
||||
```diff
|
||||
<script setup lang="ts">
|
||||
const items = [{
|
||||
label: 'Edit',
|
||||
- click: () => {
|
||||
+ onClick: () => {
|
||||
console.log('Edit')
|
||||
}
|
||||
}]
|
||||
</script>
|
||||
```
|
||||
|
||||
::note
|
||||
This change affects the `Toast` component as well as all component that have `items` links like `NavigationMenu`, `DropdownMenu`, `CommandPalette`, etc.
|
||||
::
|
||||
|
||||
- The global `Modals`, `Slideovers` and `Notifications` components have been removed in favor the [App](/components/app) component:
|
||||
|
||||
```diff [app.vue]
|
||||
<template>
|
||||
@@ -380,7 +398,7 @@ This change affects the following components: `Breadcrumb`, `HorizontalNavigatio
|
||||
</template>
|
||||
```
|
||||
|
||||
3. The `v-model:open` directive and `default-open` prop are now used to control visibility:
|
||||
- The `v-model:open` directive and `default-open` prop are now used to control visibility:
|
||||
|
||||
```diff
|
||||
<template>
|
||||
@@ -393,7 +411,7 @@ This change affects the following components: `Breadcrumb`, `HorizontalNavigatio
|
||||
This change affects the following components: `ContextMenu`, `Modal` and `Slideover` and enables controlling visibility for `InputMenu`, `Select`, `SelectMenu` and `Tooltip`.
|
||||
::
|
||||
|
||||
4. The default slot is now used for the trigger and the content goes inside the `#content` slot (you don't need to use a `v-model:open` directive with this method):
|
||||
- The default slot is now used for the trigger and the content goes inside the `#content` slot (you don't need to use a `v-model:open` directive with this method):
|
||||
|
||||
```diff
|
||||
<script setup lang="ts">
|
||||
@@ -420,7 +438,7 @@ This change affects the following components: `ContextMenu`, `Modal` and `Slideo
|
||||
This change affects the following components: `Modal`, `Popover`, `Slideover`, `Tooltip`.
|
||||
::
|
||||
|
||||
5. A `#header`, `#body` and `#footer` slots have been added inside the `#content` slot like the `Card` component:
|
||||
- A `#header`, `#body` and `#footer` slots have been added inside the `#content` slot like the `Card` component:
|
||||
|
||||
```diff
|
||||
<template>
|
||||
@@ -439,10 +457,9 @@ This change affects the following components: `Modal`, `Popover`, `Slideover`, `
|
||||
This change affects the following components: `Modal`, `Slideover`.
|
||||
::
|
||||
|
||||
|
||||
### Changed composables
|
||||
|
||||
1. The `useToast()` composable `timeout` prop has been renamed to `duration`:
|
||||
- The `useToast()` composable `timeout` prop has been renamed to `duration`:
|
||||
|
||||
```diff
|
||||
<script setup lang="ts">
|
||||
@@ -453,7 +470,7 @@ const toast = useToast()
|
||||
</script>
|
||||
```
|
||||
|
||||
2. The `useModal` and `useSlideover` composables have been removed in favor of a more generic `useOverlay` composable:
|
||||
- The `useModal` and `useSlideover` composables have been removed in favor of a more generic `useOverlay` composable:
|
||||
|
||||
Some important differences:
|
||||
- The `useOverlay` composable is now used to create overlay instances
|
||||
|
||||
@@ -32,6 +32,7 @@ Use the `items` prop as an array of objects with the following properties:
|
||||
- [`slot?: string`{lang="ts-type"}](#with-custom-slot)
|
||||
- `onSelect?(e: Event): void`{lang="ts-type"}
|
||||
- [`onUpdateChecked?(checked: boolean): void`{lang="ts-type"}](#with-checkbox-items)
|
||||
- `children?: ContextMenuItem[] | ContextMenuItem[][]`{lang="ts-type"}
|
||||
|
||||
You can pass any property from the [Link](/components/link#props) component such as `to`, `target`, etc.
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ Use the `items` prop as an array of objects with the following properties:
|
||||
- [`slot?: string`{lang="ts-type"}](#with-custom-slot)
|
||||
- `onSelect?(e: Event): void`{lang="ts-type"}
|
||||
- [`onUpdateChecked?(checked: boolean): void`{lang="ts-type"}](#with-checkbox-items)
|
||||
- `children?: DropdownMenuItem[] | DropdownMenuItem[][]`{lang="ts-type"}
|
||||
|
||||
You can pass any property from the [Link](/components/link#props) component such as `to`, `target`, etc.
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ It requires two props:
|
||||
class: 'w-60'
|
||||
---
|
||||
::
|
||||
|
||||
|
||||
::component-example{label="Zod"}
|
||||
---
|
||||
name: 'form-example-zod'
|
||||
@@ -206,3 +206,7 @@ This will give you access to the following:
|
||||
| `dirtyFields`{lang="ts-type"} | `DeepReadonly<Set<keyof T>>`{lang="ts-type"} Tracks fields that have been modified by the user. |
|
||||
| `touchedFields`{lang="ts-type"} | `DeepReadonly<Set<keyof T>>`{lang="ts-type"} Tracks fields that the user interacted with. |
|
||||
| `blurredFields`{lang="ts-type"} | `DeepReadonly<Set<keyof T>>`{lang="ts-type"} Tracks fields blurred by the user. |
|
||||
|
||||
## Theme
|
||||
|
||||
:component-theme
|
||||
@@ -28,6 +28,7 @@ Use the `items` prop as an array of objects with the following properties:
|
||||
- `class?: any`{lang="ts-type"}
|
||||
- [`slot?: string`{lang="ts-type"}](#with-custom-slot)
|
||||
- `onSelect?(e: Event): void`{lang="ts-type"}
|
||||
- `children?: NavigationMenuChildItem[]`{lang="ts-type"}
|
||||
|
||||
You can pass any property from the [Link](/components/link#props) component such as `to`, `target`, etc.
|
||||
|
||||
|
||||
@@ -5,27 +5,49 @@ hero:
|
||||
title: Showcase
|
||||
description: Discover our selection of projects built with Nuxt UI.
|
||||
items:
|
||||
- name: Ovatu
|
||||
url: https://ovatu.com/
|
||||
- name: Shelve
|
||||
url: https://shelve.cloud/
|
||||
- name: Uneed
|
||||
url: https://uneed.best/
|
||||
- name: Details
|
||||
url: https://details.team/
|
||||
- name: Espace Asso by Benevolt
|
||||
url: https://asso.benevolt.fr/
|
||||
- name: Directus Docs
|
||||
url: https://docs.directus.io/
|
||||
- name: Super SaaS
|
||||
url: https://supersaas.dev/
|
||||
- name: Espace Asso by Benevolt
|
||||
url: https://asso.benevolt.fr/
|
||||
- name: Juno.one
|
||||
url: https://www.juno.one/
|
||||
- name: Kassebil
|
||||
url: https://kassebil.dk/
|
||||
- name: LearnVue
|
||||
url: https://learnvue.co/
|
||||
- name: Mawrble
|
||||
url: https://mawrble.com/
|
||||
- name: Meet Sponsors
|
||||
url: https://meetsponsors.com/
|
||||
- name: Ovatu
|
||||
url: https://ovatu.com/
|
||||
- name: Pallyy
|
||||
url: https://pallyy.com/
|
||||
- name: Passionate People
|
||||
url: https://passionatepeople.io/
|
||||
- name: Postal
|
||||
url: https://postalserver.io/
|
||||
- name: Prismos
|
||||
url: https://prismos.co/
|
||||
- name: Readyy
|
||||
url: https://readyy.app/
|
||||
- name: Sagematt
|
||||
url: https://siedlung-sagematt.ch/
|
||||
- name: Shelve
|
||||
url: https://shelve.cloud/
|
||||
- name: Speaker Bot
|
||||
url: https://speaker.bot/
|
||||
- name: Super SaaS
|
||||
url: https://supersaas.dev/
|
||||
- name: The Companies API
|
||||
url: https://www.thecompaniesapi.com/
|
||||
- name: Thuprai
|
||||
url: https://thuprai.com/
|
||||
- name: Juno.one
|
||||
url: https://www.juno.one/
|
||||
- name: Pallyy
|
||||
url: https://pallyy.com/
|
||||
- name: Uneed
|
||||
url: https://uneed.best/
|
||||
- name: Wiredash
|
||||
url: https://wiredash.com/
|
||||
- name: Zielgestalt
|
||||
url: https://zielgestalt.de/
|
||||
|
||||
@@ -175,6 +175,10 @@ export default defineNuxtConfig({
|
||||
}
|
||||
},
|
||||
|
||||
hub: {
|
||||
ai: true
|
||||
},
|
||||
|
||||
vite: {
|
||||
plugins: [
|
||||
yaml()
|
||||
|
||||
@@ -3,22 +3,23 @@
|
||||
"name": "@nuxt/ui-docs",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@ai-sdk/vue": "^1.2.8",
|
||||
"@iconify-json/logos": "^1.2.4",
|
||||
"@iconify-json/lucide": "^1.2.34",
|
||||
"@iconify-json/simple-icons": "^1.2.30",
|
||||
"@iconify-json/vscode-icons": "^1.2.18",
|
||||
"@iconify-json/lucide": "^1.2.36",
|
||||
"@iconify-json/simple-icons": "^1.2.32",
|
||||
"@iconify-json/vscode-icons": "^1.2.19",
|
||||
"@nuxt/content": "^3.4.0",
|
||||
"@nuxt/image": "^1.10.0",
|
||||
"@nuxt/ui": "latest",
|
||||
"@nuxt/ui-pro": "https://pkg.pr.new/@nuxt/ui-pro@63da8be",
|
||||
"@nuxthub/core": "^0.8.23",
|
||||
"@nuxt/ui-pro": "https://pkg.pr.new/@nuxt/ui-pro@4757a1e",
|
||||
"@nuxthub/core": "^0.8.24",
|
||||
"@nuxtjs/plausible": "^1.2.0",
|
||||
"@octokit/rest": "^21.1.1",
|
||||
"@rollup/plugin-yaml": "^4.1.2",
|
||||
"@vueuse/nuxt": "^13.0.0",
|
||||
"@vueuse/integrations": "^13.1.0",
|
||||
"@vueuse/nuxt": "^13.1.0",
|
||||
"ai": "^4.3.6",
|
||||
"capture-website": "^4.2.0",
|
||||
"@vueuse/integrations": "^13.0.0",
|
||||
"sortablejs": "^1.15.6",
|
||||
"joi": "^17.13.3",
|
||||
"motion-v": "0.13.1",
|
||||
"nuxt": "^3.16.2",
|
||||
@@ -27,13 +28,15 @@
|
||||
"nuxt-og-image": "^5.1.1",
|
||||
"prettier": "^3.5.3",
|
||||
"shiki-transformer-color-highlight": "^1.0.0",
|
||||
"sortablejs": "^1.15.6",
|
||||
"superstruct": "^2.0.2",
|
||||
"ufo": "^1.5.4",
|
||||
"ufo": "^1.6.1",
|
||||
"valibot": "^1.0.0",
|
||||
"workers-ai-provider": "^0.3.0",
|
||||
"yup": "^1.6.1",
|
||||
"zod": "^3.24.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"wrangler": "^4.7.1"
|
||||
"wrangler": "^4.10.0"
|
||||
}
|
||||
}
|
||||
|
||||
BIN
docs/public/assets/showcase/kassebil.png
Normal file
|
After Width: | Height: | Size: 1.0 MiB |
BIN
docs/public/assets/showcase/learnvue.png
Normal file
|
After Width: | Height: | Size: 558 KiB |
BIN
docs/public/assets/showcase/mawrble.png
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
BIN
docs/public/assets/showcase/meet-sponsors.png
Normal file
|
After Width: | Height: | Size: 367 KiB |
BIN
docs/public/assets/showcase/postal.png
Normal file
|
After Width: | Height: | Size: 2.1 MiB |
BIN
docs/public/assets/showcase/prismos.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
docs/public/assets/showcase/readyy.png
Normal file
|
After Width: | Height: | Size: 282 KiB |
BIN
docs/public/assets/showcase/sagematt.png
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
docs/public/assets/showcase/speaker-bot.png
Normal file
|
After Width: | Height: | Size: 218 KiB |
BIN
docs/public/assets/showcase/wiredash.png
Normal file
|
After Width: | Height: | Size: 645 KiB |
BIN
docs/public/assets/showcase/zielgestalt.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 9.5 KiB After Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 16 KiB |
BIN
docs/public/components/dark/chat-message.png
Normal file
|
After Width: | Height: | Size: 8.5 KiB |
BIN
docs/public/components/dark/chat-messages.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
docs/public/components/dark/chat-palette.png
Normal file
|
After Width: | Height: | Size: 9.1 KiB |
BIN
docs/public/components/dark/chat-prompt-submit.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
docs/public/components/dark/chat-prompt.png
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
docs/public/components/dark/pricing-table.png
Normal file
|
After Width: | Height: | Size: 9.7 KiB |
|
Before Width: | Height: | Size: 9.5 KiB After Width: | Height: | Size: 9.9 KiB |
|
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 9.0 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 16 KiB |
BIN
docs/public/components/light/chat-message.png
Normal file
|
After Width: | Height: | Size: 8.4 KiB |
BIN
docs/public/components/light/chat-messages.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
docs/public/components/light/chat-palette.png
Normal file
|
After Width: | Height: | Size: 8.4 KiB |
BIN
docs/public/components/light/chat-prompt-submit.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
docs/public/components/light/chat-prompt.png
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
docs/public/components/light/pricing-table.png
Normal file
|
After Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 9.8 KiB |
22
docs/server/api/chat.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { streamText } from 'ai'
|
||||
import { createWorkersAI } from 'workers-ai-provider'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const { messages } = await readBody(event)
|
||||
|
||||
// Enable AI Gateway if defined in environment variables
|
||||
const gateway = process.env.CLOUDFLARE_AI_GATEWAY_ID
|
||||
? {
|
||||
id: process.env.CLOUDFLARE_AI_GATEWAY_ID,
|
||||
cacheTtl: 60 * 60 * 24 // 24 hours
|
||||
}
|
||||
: undefined
|
||||
const workersAI = createWorkersAI({ binding: hubAI(), gateway })
|
||||
|
||||
return streamText({
|
||||
model: workersAI('@cf/meta/llama-3.2-3b-instruct'),
|
||||
maxTokens: 10000,
|
||||
system: 'You are a helpful assistant that can answer questions and help.',
|
||||
messages
|
||||
}).toDataStreamResponse()
|
||||
})
|
||||
120
package.json
@@ -2,7 +2,7 @@
|
||||
"name": "@nuxt/ui",
|
||||
"description": "A UI Library for Modern Web Apps, powered by Vue & Tailwind CSS.",
|
||||
"version": "3.0.2",
|
||||
"packageManager": "pnpm@10.7.1",
|
||||
"packageManager": "pnpm@10.8.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/nuxt/ui.git"
|
||||
@@ -12,20 +12,17 @@
|
||||
"license": "MIT",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/module.d.ts",
|
||||
"types": "./dist/module.d.mts",
|
||||
"style": "./dist/runtime/index.css",
|
||||
"import": "./dist/module.mjs",
|
||||
"require": "./dist/module.cjs"
|
||||
"import": "./dist/module.mjs"
|
||||
},
|
||||
"./unplugin": {
|
||||
"types": "./dist/unplugin.d.ts",
|
||||
"import": "./dist/unplugin.mjs",
|
||||
"require": "./dist/unplugin.cjs"
|
||||
"types": "./dist/unplugin.d.mts",
|
||||
"import": "./dist/unplugin.mjs"
|
||||
},
|
||||
"./vite": {
|
||||
"types": "./dist/vite.d.ts",
|
||||
"import": "./dist/vite.mjs",
|
||||
"require": "./dist/vite.cjs"
|
||||
"types": "./dist/vite.d.mts",
|
||||
"import": "./dist/vite.mjs"
|
||||
},
|
||||
"./vue-plugin": {
|
||||
"types": "./vue-plugin.d.ts"
|
||||
@@ -33,6 +30,10 @@
|
||||
"./runtime/*": "./dist/runtime/*",
|
||||
"./components/*": "./dist/runtime/components/*",
|
||||
"./composables/*": "./dist/runtime/composables/*",
|
||||
"./utils": {
|
||||
"types": "./dist/runtime/utils/index.d.ts",
|
||||
"import": "./dist/runtime/utils/index.js"
|
||||
},
|
||||
"./utils/*": {
|
||||
"types": "./dist/runtime/utils/*.d.ts",
|
||||
"import": "./dist/runtime/utils/*.js"
|
||||
@@ -42,6 +43,40 @@
|
||||
"import": "./dist/runtime/locale/index.js"
|
||||
}
|
||||
},
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
".": [
|
||||
"./dist/module.d.mts"
|
||||
],
|
||||
"unplugin": [
|
||||
"./dist/unplugin.d.mts"
|
||||
],
|
||||
"vite": [
|
||||
"./dist/vite.d.mts"
|
||||
],
|
||||
"vue-plugin": [
|
||||
"./vue-plugin.d.ts"
|
||||
],
|
||||
"runtime/*": [
|
||||
"./dist/runtime/*"
|
||||
],
|
||||
"components/*": [
|
||||
"./dist/runtime/components/*"
|
||||
],
|
||||
"composables/*": [
|
||||
"./dist/runtime/composables/*"
|
||||
],
|
||||
"utils": [
|
||||
"./dist/runtime/utils/index.d.ts"
|
||||
],
|
||||
"utils/*": [
|
||||
"./dist/runtime/utils/*.d.ts"
|
||||
],
|
||||
"locale": [
|
||||
"./dist/runtime/locale/index.d.ts"
|
||||
]
|
||||
}
|
||||
},
|
||||
"imports": {
|
||||
"#build/ui/*": "./.nuxt/ui/*.ts",
|
||||
"#build/ui.css": "./.nuxt/ui.css"
|
||||
@@ -50,8 +85,7 @@
|
||||
"nuxt-ui": "./cli/index.mjs"
|
||||
},
|
||||
"style": "./dist/runtime/index.css",
|
||||
"main": "./dist/module.cjs",
|
||||
"types": "./dist/types.d.ts",
|
||||
"main": "./dist/module.mjs",
|
||||
"files": [
|
||||
".nuxt/ui",
|
||||
".nuxt/ui.css",
|
||||
@@ -62,13 +96,13 @@
|
||||
"scripts": {
|
||||
"build": "nuxt-module-build build",
|
||||
"prepack": "pnpm build",
|
||||
"dev": "DEV=true nuxi dev playground",
|
||||
"dev": "nuxi dev playground",
|
||||
"dev:build": "nuxi build playground",
|
||||
"dev:vue": "DEV=true vite playground-vue",
|
||||
"dev:vue": "vite playground-vue",
|
||||
"dev:vue:build": "vite build playground-vue",
|
||||
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground && nuxi prepare docs && vite build playground-vue",
|
||||
"docs": "DEV=true nuxi dev docs",
|
||||
"docs:build": "NODE_OPTIONS='--max-old-space-size=8192' nuxi build docs",
|
||||
"docs": "nuxi dev docs",
|
||||
"docs:build": "nuxi build docs",
|
||||
"docs:prepare": "nuxt-component-meta docs",
|
||||
"lint": "eslint .",
|
||||
"lint:fix": "eslint . --fix",
|
||||
@@ -79,29 +113,29 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@iconify/vue": "^4.3.0",
|
||||
"@internationalized/date": "^3.7.0",
|
||||
"@internationalized/number": "^3.6.0",
|
||||
"@internationalized/date": "^3.8.0",
|
||||
"@internationalized/number": "^3.6.1",
|
||||
"@nuxt/fonts": "^0.11.1",
|
||||
"@nuxt/icon": "^1.11.0",
|
||||
"@nuxt/icon": "^1.12.0",
|
||||
"@nuxt/kit": "^3.16.2",
|
||||
"@nuxt/schema": "^3.16.2",
|
||||
"@nuxtjs/color-mode": "^3.5.2",
|
||||
"@standard-schema/spec": "^1.0.0",
|
||||
"@tailwindcss/postcss": "^4.1.2",
|
||||
"@tailwindcss/vite": "^4.1.2",
|
||||
"@tailwindcss/postcss": "^4.1.3",
|
||||
"@tailwindcss/vite": "^4.1.3",
|
||||
"@tanstack/vue-table": "^8.21.2",
|
||||
"@unhead/vue": "^2.0.3",
|
||||
"@vueuse/core": "^13.0.0",
|
||||
"@vueuse/integrations": "^13.0.0",
|
||||
"@unhead/vue": "^2.0.5",
|
||||
"@vueuse/core": "^13.1.0",
|
||||
"@vueuse/integrations": "^13.1.0",
|
||||
"colortranslator": "^4.1.0",
|
||||
"consola": "^3.4.2",
|
||||
"defu": "^6.1.4",
|
||||
"embla-carousel-auto-height": "^8.5.2",
|
||||
"embla-carousel-auto-scroll": "^8.5.2",
|
||||
"embla-carousel-autoplay": "^8.5.2",
|
||||
"embla-carousel-class-names": "^8.5.2",
|
||||
"embla-carousel-fade": "^8.5.2",
|
||||
"embla-carousel-vue": "^8.5.2",
|
||||
"embla-carousel-auto-height": "^8.6.0",
|
||||
"embla-carousel-auto-scroll": "^8.6.0",
|
||||
"embla-carousel-autoplay": "^8.6.0",
|
||||
"embla-carousel-class-names": "^8.6.0",
|
||||
"embla-carousel-fade": "^8.6.0",
|
||||
"embla-carousel-vue": "^8.6.0",
|
||||
"embla-carousel-wheel-gestures": "^8.0.2",
|
||||
"fuse.js": "^7.1.0",
|
||||
"hookable": "^5.5.3",
|
||||
@@ -113,23 +147,21 @@
|
||||
"reka-ui": "^2.2.0",
|
||||
"scule": "^1.3.0",
|
||||
"tailwind-variants": "^1.0.0",
|
||||
"tailwindcss": "^4.1.2",
|
||||
"tailwindcss": "^4.1.3",
|
||||
"tinyglobby": "^0.2.12",
|
||||
"unplugin": "^2.2.2",
|
||||
"unplugin": "^2.3.2",
|
||||
"unplugin-auto-import": "^19.1.2",
|
||||
"unplugin-vue-components": "^28.4.1",
|
||||
"vaul-vue": "^0.4.1",
|
||||
"vue": "^3.5.13",
|
||||
"vue-router": "^4.5.0"
|
||||
"unplugin-vue-components": "^28.5.0",
|
||||
"vaul-vue": "^0.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nuxt/eslint-config": "^1.3.0",
|
||||
"@nuxt/module-builder": "^0.8.4",
|
||||
"@nuxt/module-builder": "^1.0.1",
|
||||
"@nuxt/test-utils": "^3.17.2",
|
||||
"@release-it/conventional-changelog": "^10.0.0",
|
||||
"@vue/test-utils": "^2.4.6",
|
||||
"embla-carousel": "^8.5.2",
|
||||
"eslint": "^9.23.0",
|
||||
"embla-carousel": "^8.6.0",
|
||||
"eslint": "^9.24.0",
|
||||
"happy-dom": "^17.4.4",
|
||||
"nuxt": "^3.16.2",
|
||||
"release-it": "^18.1.2",
|
||||
@@ -138,14 +170,19 @@
|
||||
"vue-tsc": "^2.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@inertiajs/vue3": "^2.0.7",
|
||||
"joi": "^17.13.0",
|
||||
"superstruct": "^2.0.0",
|
||||
"typescript": "^5.6.3",
|
||||
"valibot": "^1.0.0",
|
||||
"vue-router": "^4.5.0",
|
||||
"yup": "^1.6.0",
|
||||
"zod": "^3.24.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@inertiajs/vue3": {
|
||||
"optional": true
|
||||
},
|
||||
"joi": {
|
||||
"optional": true
|
||||
},
|
||||
@@ -155,6 +192,9 @@
|
||||
"superstruct": {
|
||||
"optional": true
|
||||
},
|
||||
"vue-router": {
|
||||
"optional": true
|
||||
},
|
||||
"yup": {
|
||||
"optional": true
|
||||
},
|
||||
@@ -167,7 +207,7 @@
|
||||
"chokidar": "3.6.0",
|
||||
"debug": "4.3.7",
|
||||
"rollup": "4.34.9",
|
||||
"unplugin": "^2.2.2",
|
||||
"unplugin": "^2.3.2",
|
||||
"vue-tsc": "2.2.0"
|
||||
},
|
||||
"pnpm": {
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^5.2.3",
|
||||
"typescript": "^5.8.2",
|
||||
"vite": "^6.2.5",
|
||||
"typescript": "^5.8.3",
|
||||
"vite": "^6.2.6",
|
||||
"vue-tsc": "^2.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ defineShortcuts({
|
||||
<UButton label="Select label (popover)" color="neutral" variant="outline" />
|
||||
|
||||
<template #content>
|
||||
<UCommandPalette v-model="label" placeholder="Search labels..." :groups="[{ id: 'labels', items: labels }]" :ui="{ input: '[&>input]:h-9' }" />
|
||||
<UCommandPalette v-model="label" placeholder="Search labels..." :groups="[{ id: 'labels', items: labels }]" :ui="{ input: '[&>input]:h-8 [&>input]:text-sm' }" />
|
||||
</template>
|
||||
</UPopover>
|
||||
</div>
|
||||
|
||||
71
playground/app/pages/components/form-sandro.vue
Normal file
@@ -0,0 +1,71 @@
|
||||
<template>
|
||||
<UContainer>
|
||||
<UForm :schema :state @submit="onSubmit">
|
||||
<UFormField label="A" name="a">
|
||||
<UInput v-model="state.a" />
|
||||
</UFormField>
|
||||
<UFormField label="B" name="b">
|
||||
<UInput v-model="state.b" />
|
||||
</UFormField>
|
||||
|
||||
<UButton type="submit">
|
||||
Submit
|
||||
</UButton>
|
||||
</UForm>
|
||||
|
||||
{{ output }}
|
||||
</UContainer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { FormSubmitEvent } from '@nuxt/ui'
|
||||
import * as v from 'valibot'
|
||||
|
||||
const _schemaStringFiltered = v.pipe(v.string(), v.trim())
|
||||
const schema = v.object({
|
||||
a: v.string(),
|
||||
b: v.union([
|
||||
v.pipe(
|
||||
v.array(_schemaStringFiltered),
|
||||
v.filterItems((item, index, array) => (array.indexOf(item) === index || item !== ''))
|
||||
),
|
||||
v.pipe(
|
||||
v.string(),
|
||||
v.trim(),
|
||||
v.transform(
|
||||
(item) => {
|
||||
if (item === '') return undefined
|
||||
|
||||
return item
|
||||
.split(',')
|
||||
.map(val => val.trim())
|
||||
.filter(val => val !== '')
|
||||
}
|
||||
)
|
||||
)
|
||||
])
|
||||
})
|
||||
|
||||
const state = reactive<{
|
||||
a: string
|
||||
b: string
|
||||
}>({
|
||||
a: 'hello, world',
|
||||
b: 'hello, world'
|
||||
})
|
||||
const output = reactive<{
|
||||
a: string
|
||||
b?: string[]
|
||||
}>({
|
||||
a: '',
|
||||
b: []
|
||||
})
|
||||
|
||||
function onSubmit(event: FormSubmitEvent<v.InferOutput<typeof schema>>) {
|
||||
console.log('typeof `a`:', typeof event.data.a) // should be string
|
||||
console.log('typeof `b`:', typeof event.data.b) // should be object (array of strings)
|
||||
|
||||
output.a = event.data.a
|
||||
output.b = event.data.b
|
||||
}
|
||||
</script>
|
||||
@@ -59,6 +59,14 @@ const items = [{
|
||||
<template #custom="{ item }">
|
||||
<span class="text-(--ui-text-muted)">Custom: {{ item.content }}</span>
|
||||
</template>
|
||||
|
||||
<template #list-trailing>
|
||||
<UButton
|
||||
icon="lucide:refresh-cw"
|
||||
variant="soft"
|
||||
class="ml-2"
|
||||
/>
|
||||
</template>
|
||||
</UTabs>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -8,10 +8,10 @@
|
||||
"generate": "nuxi generate"
|
||||
},
|
||||
"dependencies": {
|
||||
"@iconify-json/lucide": "^1.2.34",
|
||||
"@iconify-json/simple-icons": "^1.2.30",
|
||||
"@iconify-json/lucide": "^1.2.36",
|
||||
"@iconify-json/simple-icons": "^1.2.32",
|
||||
"@nuxt/ui": "latest",
|
||||
"@nuxthub/core": "^0.8.23",
|
||||
"@nuxthub/core": "^0.8.24",
|
||||
"nuxt": "^3.16.2",
|
||||
"zod": "^3.24.2"
|
||||
}
|
||||
|
||||
2287
pnpm-lock.yaml
generated
@@ -2,6 +2,7 @@ import { defu } from 'defu'
|
||||
import { createResolver, defineNuxtModule, addComponentsDir, addImportsDir, addVitePlugin, addPlugin, installModule, hasNuxtModule } from '@nuxt/kit'
|
||||
import { addTemplates } from './templates'
|
||||
import { defaultOptions, getDefaultUiConfig, resolveColors } from './defaults'
|
||||
import { name, version } from '../package.json'
|
||||
|
||||
export type * from './runtime/types'
|
||||
|
||||
@@ -50,12 +51,13 @@ export interface ModuleOptions {
|
||||
|
||||
export default defineNuxtModule<ModuleOptions>({
|
||||
meta: {
|
||||
name: 'ui',
|
||||
name,
|
||||
version,
|
||||
docs: 'https://ui.nuxt.com/getting-started/installation/nuxt',
|
||||
configKey: 'ui',
|
||||
compatibility: {
|
||||
nuxt: '>=3.16.0'
|
||||
},
|
||||
docs: 'https://ui.nuxt.com/getting-started/installation/nuxt'
|
||||
}
|
||||
},
|
||||
defaults: defaultOptions,
|
||||
async setup(options, nuxt) {
|
||||
|
||||
@@ -18,6 +18,9 @@ export default function ComponentImportPlugin(options: NuxtUIOptions & { prefix:
|
||||
const overrides = globSync('**/*.vue', { cwd: join(runtimeDir, 'vue/components') })
|
||||
const overrideNames = new Set(overrides.map(c => `${options.prefix}${c.replace(/\.vue$/, '')}`))
|
||||
|
||||
const inertiaOverrides = globSync('**/*.vue', { cwd: join(runtimeDir, 'inertia/components') })
|
||||
const inertiaOverrideNames = new Set(inertiaOverrides.map(c => `${options.prefix}${c.replace(/\.vue$/, '')}`))
|
||||
|
||||
const pluginOptions = defu(options.components, <ComponentsOptions>{
|
||||
dts: options.dts ?? true,
|
||||
exclude: [
|
||||
@@ -27,6 +30,9 @@ export default function ComponentImportPlugin(options: NuxtUIOptions & { prefix:
|
||||
],
|
||||
resolvers: [
|
||||
(componentName) => {
|
||||
if (options.inertia && inertiaOverrideNames.has(componentName)) {
|
||||
return { name: 'default', from: join(runtimeDir, 'inertia/components', `${componentName.slice(options.prefix.length)}.vue`) }
|
||||
}
|
||||
if (overrideNames.has(componentName))
|
||||
return { name: 'default', from: join(runtimeDir, 'vue/components', `${componentName.slice(options.prefix.length)}.vue`) }
|
||||
if (componentNames.has(componentName))
|
||||
@@ -55,6 +61,9 @@ export default function ComponentImportPlugin(options: NuxtUIOptions & { prefix:
|
||||
}
|
||||
|
||||
const filename = id.match(/([^/]+)\.vue$/)?.[1]
|
||||
if (filename && options.inertia && inertiaOverrideNames.has(`${options.prefix}${filename}`)) {
|
||||
return join(runtimeDir, 'inertia/components', `${filename}.vue`)
|
||||
}
|
||||
if (filename && overrideNames.has(`${options.prefix}${filename}`)) {
|
||||
return join(runtimeDir, 'vue/components', `${filename}.vue`)
|
||||
}
|
||||
|
||||
@@ -3,13 +3,13 @@ import { normalize } from 'pathe'
|
||||
import { resolvePathSync } from 'mlly'
|
||||
import MagicString from 'magic-string'
|
||||
|
||||
import { runtimeDir } from '../unplugin'
|
||||
import { runtimeDir, type NuxtUIOptions } from '../unplugin'
|
||||
|
||||
/**
|
||||
* This plugin normalises Nuxt environment (#imports) and `import.meta.client` within the Nuxt UI components.
|
||||
*/
|
||||
export default function NuxtEnvironmentPlugin() {
|
||||
const stubPath = resolvePathSync('../runtime/vue/stubs', { extensions: ['.ts', '.mjs', '.js'], url: import.meta.url })
|
||||
export default function NuxtEnvironmentPlugin(options: NuxtUIOptions) {
|
||||
const stubPath = resolvePathSync(options.inertia ? '../runtime/inertia/stubs' : '../runtime/vue/stubs', { extensions: ['.ts', '.mjs', '.js'], url: import.meta.url })
|
||||
|
||||
return {
|
||||
name: 'nuxt:ui',
|
||||
|
||||
@@ -2,14 +2,10 @@
|
||||
<script lang="ts">
|
||||
import type { AccordionRootProps, AccordionRootEmits } from 'reka-ui'
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/accordion'
|
||||
import { tv } from '../utils/tv'
|
||||
import type { DynamicSlots } from '../types/utils'
|
||||
import type { DynamicSlots, ComponentConfig } from '../types/utils'
|
||||
|
||||
const appConfigAccordion = _appConfig as AppConfig & { ui: { accordion: Partial<typeof theme> } }
|
||||
|
||||
const accordion = tv({ extend: tv(theme), ...(appConfigAccordion.ui?.accordion || {}) })
|
||||
type Accordion = ComponentConfig<typeof theme, AppConfig, 'accordion'>
|
||||
|
||||
export interface AccordionItem {
|
||||
label?: string
|
||||
@@ -48,7 +44,7 @@ export interface AccordionProps<T extends AccordionItem = AccordionItem> extends
|
||||
*/
|
||||
labelKey?: string
|
||||
class?: any
|
||||
ui?: Partial<typeof accordion.slots>
|
||||
ui?: Accordion['slots']
|
||||
}
|
||||
|
||||
export interface AccordionEmits extends AccordionRootEmits {}
|
||||
@@ -71,6 +67,7 @@ import { AccordionRoot, AccordionItem, AccordionHeader, AccordionTrigger, Accord
|
||||
import { reactivePick } from '@vueuse/core'
|
||||
import { useAppConfig } from '#imports'
|
||||
import { get } from '../utils'
|
||||
import { tv } from '../utils/tv'
|
||||
import UIcon from './Icon.vue'
|
||||
|
||||
const props = withDefaults(defineProps<AccordionProps<T>>(), {
|
||||
@@ -82,10 +79,11 @@ const props = withDefaults(defineProps<AccordionProps<T>>(), {
|
||||
const emits = defineEmits<AccordionEmits>()
|
||||
const slots = defineSlots<AccordionSlots<T>>()
|
||||
|
||||
const appConfig = useAppConfig()
|
||||
const appConfig = useAppConfig() as Accordion['AppConfig']
|
||||
|
||||
const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'collapsible', 'defaultValue', 'disabled', 'modelValue', 'type', 'unmountOnHide'), emits)
|
||||
|
||||
const ui = computed(() => accordion({
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.accordion || {}) })({
|
||||
disabled: props.disabled
|
||||
}))
|
||||
</script>
|
||||
|
||||
@@ -1,16 +1,10 @@
|
||||
<script lang="ts">
|
||||
import type { VariantProps } from 'tailwind-variants'
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/alert'
|
||||
import { tv } from '../utils/tv'
|
||||
import type { AvatarProps, ButtonProps } from '../types'
|
||||
import type { ComponentConfig } from '../types/utils'
|
||||
|
||||
const appConfigAlert = _appConfig as AppConfig & { ui: { alert: Partial<typeof theme> } }
|
||||
|
||||
const alert = tv({ extend: tv(theme), ...(appConfigAlert.ui?.alert || {}) })
|
||||
|
||||
type AlertVariants = VariantProps<typeof alert>
|
||||
type Alert = ComponentConfig<typeof theme, AppConfig, 'alert'>
|
||||
|
||||
export interface AlertProps {
|
||||
/**
|
||||
@@ -28,16 +22,16 @@ export interface AlertProps {
|
||||
/**
|
||||
* @defaultValue 'primary'
|
||||
*/
|
||||
color?: AlertVariants['color']
|
||||
color?: Alert['variants']['color']
|
||||
/**
|
||||
* @defaultValue 'solid'
|
||||
*/
|
||||
variant?: AlertVariants['variant']
|
||||
variant?: Alert['variants']['variant']
|
||||
/**
|
||||
* The orientation between the content and the actions.
|
||||
* @defaultValue 'vertical'
|
||||
*/
|
||||
orientation?: AlertVariants['orientation']
|
||||
orientation?: Alert['variants']['orientation']
|
||||
/**
|
||||
* Display a list of actions:
|
||||
* - under the title and description when orientation is `vertical`
|
||||
@@ -59,7 +53,7 @@ export interface AlertProps {
|
||||
*/
|
||||
closeIcon?: string
|
||||
class?: any
|
||||
ui?: Partial<typeof alert.slots>
|
||||
ui?: Alert['slots']
|
||||
}
|
||||
|
||||
export interface AlertEmits {
|
||||
@@ -71,7 +65,7 @@ export interface AlertSlots {
|
||||
title(props?: {}): any
|
||||
description(props?: {}): any
|
||||
actions(props?: {}): any
|
||||
close(props: { ui: ReturnType<typeof alert> }): any
|
||||
close(props: { ui: { [K in keyof Required<Alert['slots']>]: (props?: Record<string, any>) => string } }): any
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -80,6 +74,7 @@ import { computed } from 'vue'
|
||||
import { Primitive } from 'reka-ui'
|
||||
import { useAppConfig } from '#imports'
|
||||
import { useLocale } from '../composables/useLocale'
|
||||
import { tv } from '../utils/tv'
|
||||
import UIcon from './Icon.vue'
|
||||
import UAvatar from './Avatar.vue'
|
||||
import UButton from './Button.vue'
|
||||
@@ -91,9 +86,9 @@ const emits = defineEmits<AlertEmits>()
|
||||
const slots = defineSlots<AlertSlots>()
|
||||
|
||||
const { t } = useLocale()
|
||||
const appConfig = useAppConfig()
|
||||
const appConfig = useAppConfig() as Alert['AppConfig']
|
||||
|
||||
const ui = computed(() => alert({
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.alert || {}) })({
|
||||
color: props.color,
|
||||
variant: props.variant,
|
||||
orientation: props.orientation,
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
<script lang="ts">
|
||||
import type { VariantProps } from 'tailwind-variants'
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/avatar'
|
||||
import { tv } from '../utils/tv'
|
||||
import type { ComponentConfig } from '../types/utils'
|
||||
|
||||
const appConfigAvatar = _appConfig as AppConfig & { ui: { avatar: Partial<typeof theme> } }
|
||||
|
||||
const avatar = tv({ extend: tv(theme), ...(appConfigAvatar.ui?.avatar || {}) })
|
||||
|
||||
type AvatarVariants = VariantProps<typeof avatar>
|
||||
type Avatar = ComponentConfig<typeof theme, AppConfig, 'avatar'>
|
||||
|
||||
export interface AvatarProps {
|
||||
/**
|
||||
@@ -27,10 +21,10 @@ export interface AvatarProps {
|
||||
/**
|
||||
* @defaultValue 'md'
|
||||
*/
|
||||
size?: AvatarVariants['size']
|
||||
size?: Avatar['variants']['size']
|
||||
class?: any
|
||||
style?: any
|
||||
ui?: Partial<typeof avatar.slots>
|
||||
ui?: Avatar['slots']
|
||||
}
|
||||
|
||||
export interface AvatarSlots {
|
||||
@@ -41,8 +35,10 @@ export interface AvatarSlots {
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { Primitive, Slot } from 'reka-ui'
|
||||
import { useAppConfig } from '#imports'
|
||||
import ImageComponent from '#build/ui-image-component'
|
||||
import { useAvatarGroup } from '../composables/useAvatarGroup'
|
||||
import { tv } from '../utils/tv'
|
||||
import UIcon from './Icon.vue'
|
||||
|
||||
defineOptions({ inheritAttrs: false })
|
||||
@@ -51,10 +47,11 @@ const props = withDefaults(defineProps<AvatarProps>(), { as: 'span' })
|
||||
|
||||
const fallback = computed(() => props.text || (props.alt || '').split(' ').map(word => word.charAt(0)).join('').substring(0, 2))
|
||||
|
||||
const appConfig = useAppConfig() as Avatar['AppConfig']
|
||||
const { size } = useAvatarGroup(props)
|
||||
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
const ui = computed(() => avatar({
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.avatar || {}) })({
|
||||
size: size.value
|
||||
}))
|
||||
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
<script lang="ts">
|
||||
import type { VariantProps } from 'tailwind-variants'
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/avatar-group'
|
||||
import { tv } from '../utils/tv'
|
||||
import type { ComponentConfig } from '../types/utils'
|
||||
|
||||
const appConfigAvatarGroup = _appConfig as AppConfig & { ui: { avatarGroup: Partial<typeof theme> } }
|
||||
|
||||
const avatarGroup = tv({ extend: tv(theme), ...(appConfigAvatarGroup.ui?.avatarGroup || {}) })
|
||||
|
||||
type AvatarGroupVariants = VariantProps<typeof avatarGroup>
|
||||
type AvatarGroup = ComponentConfig<typeof theme, AppConfig, 'avatarGroup'>
|
||||
|
||||
export interface AvatarGroupProps {
|
||||
/**
|
||||
@@ -20,13 +14,13 @@ export interface AvatarGroupProps {
|
||||
/**
|
||||
* @defaultValue 'md'
|
||||
*/
|
||||
size?: AvatarGroupVariants['size']
|
||||
size?: AvatarGroup['variants']['size']
|
||||
/**
|
||||
* The maximum number of avatars to display.
|
||||
*/
|
||||
max?: number | string
|
||||
class?: any
|
||||
ui?: Partial<typeof avatarGroup.slots>
|
||||
ui?: AvatarGroup['slots']
|
||||
}
|
||||
|
||||
export interface AvatarGroupSlots {
|
||||
@@ -37,13 +31,17 @@ export interface AvatarGroupSlots {
|
||||
<script setup lang="ts">
|
||||
import { computed, provide } from 'vue'
|
||||
import { Primitive } from 'reka-ui'
|
||||
import { useAppConfig } from '#imports'
|
||||
import { avatarGroupInjectionKey } from '../composables/useAvatarGroup'
|
||||
import { tv } from '../utils/tv'
|
||||
import UAvatar from './Avatar.vue'
|
||||
|
||||
const props = defineProps<AvatarGroupProps>()
|
||||
const slots = defineSlots<AvatarGroupSlots>()
|
||||
|
||||
const ui = computed(() => avatarGroup({
|
||||
const appConfig = useAppConfig() as AvatarGroup['AppConfig']
|
||||
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.avatarGroup || {}) })({
|
||||
size: props.size
|
||||
}))
|
||||
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
<script lang="ts">
|
||||
import type { VariantProps } from 'tailwind-variants'
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/badge'
|
||||
import type { UseComponentIconsProps } from '../composables/useComponentIcons'
|
||||
import { tv } from '../utils/tv'
|
||||
import type { AvatarProps } from '../types'
|
||||
import type { ComponentConfig } from '../types/utils'
|
||||
|
||||
const appConfigBadge = _appConfig as AppConfig & { ui: { badge: Partial<typeof theme> } }
|
||||
|
||||
const badge = tv({ extend: tv(theme), ...(appConfigBadge.ui?.badge || {}) })
|
||||
|
||||
type BadgeVariants = VariantProps<typeof badge>
|
||||
type Badge = ComponentConfig<typeof theme, AppConfig, 'badge'>
|
||||
|
||||
export interface BadgeProps extends Omit<UseComponentIconsProps, 'loading' | 'loadingIcon'> {
|
||||
/**
|
||||
@@ -23,17 +17,17 @@ export interface BadgeProps extends Omit<UseComponentIconsProps, 'loading' | 'lo
|
||||
/**
|
||||
* @defaultValue 'primary'
|
||||
*/
|
||||
color?: BadgeVariants['color']
|
||||
color?: Badge['variants']['color']
|
||||
/**
|
||||
* @defaultValue 'solid'
|
||||
*/
|
||||
variant?: BadgeVariants['variant']
|
||||
variant?: Badge['variants']['variant']
|
||||
/**
|
||||
* @defaultValue 'md'
|
||||
*/
|
||||
size?: BadgeVariants['size']
|
||||
size?: Badge['variants']['size']
|
||||
class?: any
|
||||
ui?: Partial<typeof badge.slots>
|
||||
ui?: Badge['slots']
|
||||
}
|
||||
|
||||
export interface BadgeSlots {
|
||||
@@ -46,8 +40,10 @@ export interface BadgeSlots {
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { Primitive } from 'reka-ui'
|
||||
import { useAppConfig } from '#imports'
|
||||
import { useButtonGroup } from '../composables/useButtonGroup'
|
||||
import { useComponentIcons } from '../composables/useComponentIcons'
|
||||
import { tv } from '../utils/tv'
|
||||
import UIcon from './Icon.vue'
|
||||
import UAvatar from './Avatar.vue'
|
||||
|
||||
@@ -56,10 +52,11 @@ const props = withDefaults(defineProps<BadgeProps>(), {
|
||||
})
|
||||
defineSlots<BadgeSlots>()
|
||||
|
||||
const appConfig = useAppConfig() as Badge['AppConfig']
|
||||
const { orientation, size: buttonGroupSize } = useButtonGroup<BadgeProps>(props)
|
||||
const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponentIcons(props)
|
||||
|
||||
const ui = computed(() => badge({
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.badge || {}) })({
|
||||
color: props.color,
|
||||
variant: props.variant,
|
||||
size: buttonGroupSize.value || props.size,
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
<!-- eslint-disable vue/block-tag-newline -->
|
||||
<script lang="ts">
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/breadcrumb'
|
||||
import { tv } from '../utils/tv'
|
||||
import type { AvatarProps, LinkProps } from '../types'
|
||||
import type { DynamicSlots, PartialString } from '../types/utils'
|
||||
import type { DynamicSlots, ComponentConfig } from '../types/utils'
|
||||
|
||||
const appConfigBreadcrumb = _appConfig as AppConfig & { ui: { breadcrumb: Partial<typeof theme> } }
|
||||
|
||||
const breadcrumb = tv({ extend: tv(theme), ...(appConfigBreadcrumb.ui?.breadcrumb || {}) })
|
||||
type Breadcrumb = ComponentConfig<typeof theme, AppConfig, 'breadcrumb'>
|
||||
|
||||
export interface BreadcrumbItem extends Omit<LinkProps, 'raw' | 'custom'> {
|
||||
label?: string
|
||||
@@ -41,7 +37,7 @@ export interface BreadcrumbProps<T extends BreadcrumbItem = BreadcrumbItem> {
|
||||
*/
|
||||
labelKey?: string
|
||||
class?: any
|
||||
ui?: PartialString<typeof breadcrumb.slots>
|
||||
ui?: Breadcrumb['slots']
|
||||
}
|
||||
|
||||
type SlotProps<T extends BreadcrumbItem> = (props: { item: T, index: number, active?: boolean }) => any
|
||||
@@ -62,6 +58,7 @@ import { Primitive } from 'reka-ui'
|
||||
import { useAppConfig } from '#imports'
|
||||
import { useLocale } from '../composables/useLocale'
|
||||
import { get } from '../utils'
|
||||
import { tv } from '../utils/tv'
|
||||
import { pickLinkProps } from '../utils/link'
|
||||
import UIcon from './Icon.vue'
|
||||
import UAvatar from './Avatar.vue'
|
||||
@@ -73,13 +70,14 @@ const props = withDefaults(defineProps<BreadcrumbProps<T>>(), {
|
||||
labelKey: 'label'
|
||||
})
|
||||
const slots = defineSlots<BreadcrumbSlots<T>>()
|
||||
|
||||
const { dir } = useLocale()
|
||||
const appConfig = useAppConfig()
|
||||
const appConfig = useAppConfig() as Breadcrumb['AppConfig']
|
||||
|
||||
const separatorIcon = computed(() => props.separatorIcon || (dir.value === 'rtl' ? appConfig.ui.icons.chevronLeft : appConfig.ui.icons.chevronRight))
|
||||
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
const ui = breadcrumb()
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.breadcrumb || {}) })())
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,36 +1,29 @@
|
||||
<script lang="ts">
|
||||
import type { VariantProps } from 'tailwind-variants'
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/button'
|
||||
import type { LinkProps } from './Link.vue'
|
||||
import type { UseComponentIconsProps } from '../composables/useComponentIcons'
|
||||
import { tv } from '../utils/tv'
|
||||
import type { AvatarProps } from '../types'
|
||||
import type { PartialString } from '../types/utils'
|
||||
import type { ComponentConfig } from '../types/utils'
|
||||
|
||||
const appConfigButton = _appConfig as AppConfig & { ui: { button: Partial<typeof theme> } }
|
||||
|
||||
const button = tv({ extend: tv(theme), ...(appConfigButton.ui?.button || {}) })
|
||||
|
||||
type ButtonVariants = VariantProps<typeof button>
|
||||
type Button = ComponentConfig<typeof theme, AppConfig, 'button'>
|
||||
|
||||
export interface ButtonProps extends UseComponentIconsProps, Omit<LinkProps, 'raw' | 'custom'> {
|
||||
label?: string
|
||||
/**
|
||||
* @defaultValue 'primary'
|
||||
*/
|
||||
color?: ButtonVariants['color']
|
||||
activeColor?: ButtonVariants['color']
|
||||
color?: Button['variants']['color']
|
||||
activeColor?: Button['variants']['color']
|
||||
/**
|
||||
* @defaultValue 'solid'
|
||||
*/
|
||||
variant?: ButtonVariants['variant']
|
||||
activeVariant?: ButtonVariants['variant']
|
||||
variant?: Button['variants']['variant']
|
||||
activeVariant?: Button['variants']['variant']
|
||||
/**
|
||||
* @defaultValue 'md'
|
||||
*/
|
||||
size?: ButtonVariants['size']
|
||||
size?: Button['variants']['size']
|
||||
/** Render the button with equal padding on all sides. */
|
||||
square?: boolean
|
||||
/** Render the button full width. */
|
||||
@@ -39,7 +32,7 @@ export interface ButtonProps extends UseComponentIconsProps, Omit<LinkProps, 'ra
|
||||
loadingAuto?: boolean
|
||||
onClick?: ((event: MouseEvent) => void | Promise<void>) | Array<((event: MouseEvent) => void | Promise<void>)>
|
||||
class?: any
|
||||
ui?: PartialString<typeof button.slots>
|
||||
ui?: Button['slots']
|
||||
}
|
||||
|
||||
export interface ButtonSlots {
|
||||
@@ -51,11 +44,14 @@ export interface ButtonSlots {
|
||||
|
||||
<script setup lang="ts">
|
||||
import { type Ref, computed, ref, inject } from 'vue'
|
||||
import { defu } from 'defu'
|
||||
import { useForwardProps } from 'reka-ui'
|
||||
import { useAppConfig } from '#imports'
|
||||
import { useComponentIcons } from '../composables/useComponentIcons'
|
||||
import { useButtonGroup } from '../composables/useButtonGroup'
|
||||
import { formLoadingInjectionKey } from '../composables/useFormField'
|
||||
import { omit } from '../utils'
|
||||
import { tv } from '../utils/tv'
|
||||
import { pickLinkProps } from '../utils/link'
|
||||
import UIcon from './Icon.vue'
|
||||
import UAvatar from './Avatar.vue'
|
||||
@@ -69,10 +65,11 @@ const props = withDefaults(defineProps<ButtonProps>(), {
|
||||
})
|
||||
const slots = defineSlots<ButtonSlots>()
|
||||
|
||||
const linkProps = useForwardProps(pickLinkProps(props))
|
||||
|
||||
const appConfig = useAppConfig() as Button['AppConfig']
|
||||
const { orientation, size: buttonSize } = useButtonGroup<ButtonProps>(props)
|
||||
|
||||
const linkProps = useForwardProps(pickLinkProps(props))
|
||||
|
||||
const loadingAutoState = ref(false)
|
||||
const formLoading = inject<Ref<boolean> | undefined>(formLoadingInjectionKey, undefined)
|
||||
|
||||
@@ -95,17 +92,19 @@ const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponen
|
||||
)
|
||||
|
||||
const ui = computed(() => tv({
|
||||
extend: button,
|
||||
variants: {
|
||||
active: {
|
||||
true: {
|
||||
base: props.activeClass
|
||||
},
|
||||
false: {
|
||||
base: props.inactiveClass
|
||||
extend: tv(theme),
|
||||
...defu({
|
||||
variants: {
|
||||
active: {
|
||||
true: {
|
||||
base: props.activeClass
|
||||
},
|
||||
false: {
|
||||
base: props.inactiveClass
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, appConfig.ui?.button || {})
|
||||
})({
|
||||
color: props.color,
|
||||
variant: props.variant,
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
<script lang="ts">
|
||||
import type { VariantProps } from 'tailwind-variants'
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/button-group'
|
||||
import { tv } from '../utils/tv'
|
||||
import type { ComponentConfig } from '../types/utils'
|
||||
|
||||
const appConfigButtonGroup = _appConfig as AppConfig & { ui: { buttonGroup: Partial<typeof theme> } }
|
||||
|
||||
const buttonGroup = tv({ extend: tv(theme), ...(appConfigButtonGroup.ui?.buttonGroup) })
|
||||
|
||||
type ButtonGroupVariants = VariantProps<typeof buttonGroup>
|
||||
type ButtonGroup = ComponentConfig<typeof theme, AppConfig, 'buttonGroup'>
|
||||
|
||||
export interface ButtonGroupProps {
|
||||
/**
|
||||
@@ -20,13 +14,14 @@ export interface ButtonGroupProps {
|
||||
/**
|
||||
* @defaultValue 'md'
|
||||
*/
|
||||
size?: ButtonGroupVariants['size']
|
||||
size?: ButtonGroup['variants']['size']
|
||||
/**
|
||||
* The orientation the buttons are laid out.
|
||||
* @defaultValue 'horizontal'
|
||||
*/
|
||||
orientation?: ButtonGroupVariants['orientation']
|
||||
orientation?: ButtonGroup['variants']['orientation']
|
||||
class?: any
|
||||
ui?: ButtonGroup['slots']
|
||||
}
|
||||
|
||||
export interface ButtonGroupSlots {
|
||||
@@ -37,13 +32,20 @@ export interface ButtonGroupSlots {
|
||||
<script setup lang="ts">
|
||||
import { provide, computed } from 'vue'
|
||||
import { Primitive } from 'reka-ui'
|
||||
import { useAppConfig } from '#imports'
|
||||
import { buttonGroupInjectionKey } from '../composables/useButtonGroup'
|
||||
import { tv } from '../utils/tv'
|
||||
|
||||
const props = withDefaults(defineProps<ButtonGroupProps>(), {
|
||||
orientation: 'horizontal'
|
||||
})
|
||||
defineSlots<ButtonGroupSlots>()
|
||||
|
||||
const appConfig = useAppConfig() as ButtonGroup['AppConfig']
|
||||
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.buttonGroup || {}) }))
|
||||
|
||||
provide(buttonGroupInjectionKey, computed(() => ({
|
||||
orientation: props.orientation,
|
||||
size: props.size
|
||||
@@ -51,7 +53,7 @@ provide(buttonGroupInjectionKey, computed(() => ({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Primitive :as="as" :class="buttonGroup({ orientation, class: props.class })">
|
||||
<Primitive :as="as" :class="ui({ orientation, class: props.class })">
|
||||
<slot />
|
||||
</Primitive>
|
||||
</template>
|
||||
|
||||
@@ -1,19 +1,12 @@
|
||||
<script lang="ts">
|
||||
import type { VariantProps } from 'tailwind-variants'
|
||||
import type { CalendarRootProps, CalendarRootEmits, RangeCalendarRootProps, RangeCalendarRootEmits, DateRange, CalendarCellTriggerProps } from 'reka-ui'
|
||||
import type { DateValue } from '@internationalized/date'
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import type { ButtonProps } from '../types'
|
||||
import _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/calendar'
|
||||
import { tv } from '../utils/tv'
|
||||
import type { PartialString } from '../types/utils'
|
||||
import type { ButtonProps } from '../types'
|
||||
import type { ComponentConfig } from '../types/utils'
|
||||
|
||||
const appConfigCalendar = _appConfig as AppConfig & { ui: { calendar: Partial<typeof theme> } }
|
||||
|
||||
const calendar = tv({ extend: tv(theme), ...(appConfigCalendar.ui?.calendar || {}) })
|
||||
|
||||
type CalendarVariants = VariantProps<typeof calendar>
|
||||
type Calendar = ComponentConfig<typeof theme, AppConfig, 'calendar'>
|
||||
|
||||
type CalendarDefaultValue<R extends boolean = false, M extends boolean = false> = R extends true
|
||||
? DateRange
|
||||
@@ -82,11 +75,11 @@ export interface CalendarProps<R extends boolean = false, M extends boolean = fa
|
||||
/**
|
||||
* @defaultValue 'primary'
|
||||
*/
|
||||
color?: CalendarVariants['color']
|
||||
color?: Calendar['variants']['color']
|
||||
/**
|
||||
* @defaultValue 'md'
|
||||
*/
|
||||
size?: CalendarVariants['size']
|
||||
size?: Calendar['variants']['size']
|
||||
/** Whether or not a range of dates can be selected */
|
||||
range?: R & boolean
|
||||
/** Whether or not multiple dates can be selected */
|
||||
@@ -98,7 +91,7 @@ export interface CalendarProps<R extends boolean = false, M extends boolean = fa
|
||||
defaultValue?: CalendarDefaultValue<R, M>
|
||||
modelValue?: CalendarModelValue<R, M>
|
||||
class?: any
|
||||
ui?: PartialString<typeof calendar.slots>
|
||||
ui?: Calendar['slots']
|
||||
}
|
||||
|
||||
export interface CalendarEmits<R extends boolean, M extends boolean> extends Omit<CalendarRootEmits & RangeCalendarRootEmits, 'update:modelValue'> {
|
||||
@@ -119,6 +112,7 @@ import { Calendar as SingleCalendar, RangeCalendar } from 'reka-ui/namespaced'
|
||||
import { reactiveOmit } from '@vueuse/core'
|
||||
import { useAppConfig } from '#imports'
|
||||
import { useLocale } from '../composables/useLocale'
|
||||
import { tv } from '../utils/tv'
|
||||
import UButton from './Button.vue'
|
||||
|
||||
const props = withDefaults(defineProps<CalendarProps<R, M>>(), {
|
||||
@@ -129,8 +123,8 @@ const props = withDefaults(defineProps<CalendarProps<R, M>>(), {
|
||||
const emits = defineEmits<CalendarEmits<R, M>>()
|
||||
defineSlots<CalendarSlots>()
|
||||
|
||||
const appConfig = useAppConfig()
|
||||
const { code: locale, dir, t } = useLocale()
|
||||
const appConfig = useAppConfig() as Calendar['AppConfig']
|
||||
|
||||
const rootProps = useForwardPropsEmits(reactiveOmit(props, 'range', 'modelValue', 'defaultValue', 'color', 'size', 'monthControls', 'yearControls', 'class', 'ui'), emits)
|
||||
|
||||
@@ -139,7 +133,7 @@ const nextMonthIcon = computed(() => props.nextMonthIcon || (dir.value === 'rtl'
|
||||
const prevYearIcon = computed(() => props.prevYearIcon || (dir.value === 'rtl' ? appConfig.ui.icons.chevronDoubleRight : appConfig.ui.icons.chevronDoubleLeft))
|
||||
const prevMonthIcon = computed(() => props.prevMonthIcon || (dir.value === 'rtl' ? appConfig.ui.icons.chevronRight : appConfig.ui.icons.chevronLeft))
|
||||
|
||||
const ui = computed(() => calendar({
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.calendar || {}) })({
|
||||
color: props.color,
|
||||
size: props.size
|
||||
}))
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
<script lang="ts">
|
||||
import type { VariantProps } from 'tailwind-variants'
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/card'
|
||||
import { tv } from '../utils/tv'
|
||||
import type { ComponentConfig } from '../types/utils'
|
||||
|
||||
const appConfigCard = _appConfig as AppConfig & { ui: { card: Partial<typeof theme> } }
|
||||
|
||||
const card = tv({ extend: tv(theme), ...(appConfigCard.ui?.card || {}) })
|
||||
|
||||
type CardVariants = VariantProps<typeof card>
|
||||
type Card = ComponentConfig<typeof theme, AppConfig, 'card'>
|
||||
|
||||
export interface CardProps {
|
||||
/**
|
||||
@@ -20,9 +14,9 @@ export interface CardProps {
|
||||
/**
|
||||
* @defaultValue 'outline'
|
||||
*/
|
||||
variant?: CardVariants['variant']
|
||||
variant?: Card['variants']['variant']
|
||||
class?: any
|
||||
ui?: Partial<typeof card.slots>
|
||||
ui?: Card['slots']
|
||||
}
|
||||
|
||||
export interface CardSlots {
|
||||
@@ -35,11 +29,17 @@ export interface CardSlots {
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { Primitive } from 'reka-ui'
|
||||
import { useAppConfig } from '#imports'
|
||||
import { tv } from '../utils/tv'
|
||||
|
||||
const props = defineProps<CardProps>()
|
||||
const slots = defineSlots<CardSlots>()
|
||||
|
||||
const ui = computed(() => card({ variant: props.variant }))
|
||||
const appConfig = useAppConfig() as Card['AppConfig']
|
||||
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.card || {}) })({
|
||||
variant: props.variant
|
||||
}))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||