Compare commits
29 Commits
fix-naviga
...
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 |
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
|
||||
|
||||
@@ -6,8 +6,14 @@ export default defineBuildConfig({
|
||||
'./src/unplugin',
|
||||
'./src/vite'
|
||||
],
|
||||
replace: {
|
||||
'process.env.DEV': 'false'
|
||||
rollup: {
|
||||
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>
|
||||
|
||||
@@ -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<Schema>) {
|
||||
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<typeof schema>) {
|
||||
async function onSubmit(event: FormSubmitEvent<typeof state>) {
|
||||
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<Schema>) {
|
||||
<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>
|
||||
|
||||
@@ -51,7 +51,14 @@ async function onSubmit(event: FormSubmitEvent<Schema>) {
|
||||
<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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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' }
|
||||
|
||||
@@ -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')
|
||||
])
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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.35",
|
||||
"@iconify-json/simple-icons": "^1.2.31",
|
||||
"@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.1.0",
|
||||
"capture-website": "^4.2.0",
|
||||
"@vueuse/integrations": "^13.1.0",
|
||||
"sortablejs": "^1.15.6",
|
||||
"@vueuse/nuxt": "^13.1.0",
|
||||
"ai": "^4.3.6",
|
||||
"capture-website": "^4.2.0",
|
||||
"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.6.1",
|
||||
"valibot": "^1.0.0",
|
||||
"workers-ai-provider": "^0.3.0",
|
||||
"yup": "^1.6.1",
|
||||
"zod": "^3.24.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"wrangler": "^4.8.0"
|
||||
"wrangler": "^4.10.0"
|
||||
}
|
||||
}
|
||||
|
||||
|
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()
|
||||
})
|
||||
34
package.json
@@ -96,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",
|
||||
@@ -113,10 +113,10 @@
|
||||
},
|
||||
"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",
|
||||
@@ -149,16 +149,14 @@
|
||||
"tailwind-variants": "^1.0.0",
|
||||
"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": "^1.0.0",
|
||||
"@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",
|
||||
@@ -172,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
|
||||
},
|
||||
@@ -189,6 +192,9 @@
|
||||
"superstruct": {
|
||||
"optional": true
|
||||
},
|
||||
"vue-router": {
|
||||
"optional": true
|
||||
},
|
||||
"yup": {
|
||||
"optional": true
|
||||
},
|
||||
@@ -201,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": {
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^5.2.3",
|
||||
"typescript": "^5.8.3",
|
||||
"vite": "^6.2.5",
|
||||
"vite": "^6.2.6",
|
||||
"vue-tsc": "^2.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
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.35",
|
||||
"@iconify-json/simple-icons": "^1.2.31",
|
||||
"@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"
|
||||
}
|
||||
|
||||
939
pnpm-lock.yaml
generated
@@ -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>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<!-- eslint-disable vue/block-tag-newline -->
|
||||
<script lang="ts">
|
||||
import type { VariantProps } from 'tailwind-variants'
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import type { AcceptableValue } from 'reka-ui'
|
||||
import type { EmblaCarouselType, EmblaOptionsType, EmblaPluginType } from 'embla-carousel'
|
||||
@@ -10,17 +9,11 @@ import type { AutoHeightOptionsType } from 'embla-carousel-auto-height'
|
||||
import type { ClassNamesOptionsType } from 'embla-carousel-class-names'
|
||||
import type { FadeOptionsType } from 'embla-carousel-fade'
|
||||
import type { WheelGesturesPluginOptions } from 'embla-carousel-wheel-gestures'
|
||||
import _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/carousel'
|
||||
import { tv } from '../utils/tv'
|
||||
import type { ButtonProps } from '../types'
|
||||
import type { PartialString } from '../types/utils'
|
||||
import type { ComponentConfig } from '../types/utils'
|
||||
|
||||
const appConfigCarousel = _appConfig as AppConfig & { ui: { carousel: Partial<typeof theme> } }
|
||||
|
||||
const carousel = tv({ extend: tv(theme), ...(appConfigCarousel.ui?.carousel || {}) })
|
||||
|
||||
type CarouselVariants = VariantProps<typeof carousel>
|
||||
type Carousel = ComponentConfig<typeof theme, AppConfig, 'carousel'>
|
||||
|
||||
export type CarouselItem = AcceptableValue
|
||||
|
||||
@@ -66,7 +59,7 @@ export interface CarouselProps<T extends CarouselItem = CarouselItem> extends Om
|
||||
* The orientation of the carousel.
|
||||
* @defaultValue 'horizontal'
|
||||
*/
|
||||
orientation?: CarouselVariants['orientation']
|
||||
orientation?: Carousel['variants']['orientation']
|
||||
items?: T[]
|
||||
/**
|
||||
* Enable Autoplay plugin
|
||||
@@ -99,7 +92,7 @@ export interface CarouselProps<T extends CarouselItem = CarouselItem> extends Om
|
||||
*/
|
||||
wheelGestures?: boolean | WheelGesturesPluginOptions
|
||||
class?: any
|
||||
ui?: PartialString<typeof carousel.slots>
|
||||
ui?: Carousel['slots']
|
||||
}
|
||||
|
||||
export type CarouselSlots<T extends CarouselItem = CarouselItem> = {
|
||||
@@ -115,6 +108,7 @@ import { Primitive, useForwardProps } from 'reka-ui'
|
||||
import { reactivePick, computedAsync } 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<CarouselProps<T>>(), {
|
||||
@@ -148,14 +142,15 @@ const props = withDefaults(defineProps<CarouselProps<T>>(), {
|
||||
})
|
||||
defineSlots<CarouselSlots<T>>()
|
||||
|
||||
const appConfig = useAppConfig()
|
||||
const { dir, t } = useLocale()
|
||||
const appConfig = useAppConfig() as Carousel['AppConfig']
|
||||
|
||||
const rootProps = useForwardProps(reactivePick(props, 'active', 'align', 'breakpoints', 'containScroll', 'dragFree', 'dragThreshold', 'duration', 'inViewThreshold', 'loop', 'skipSnaps', 'slidesToScroll', 'startIndex', 'watchDrag', 'watchResize', 'watchSlides', 'watchFocus'))
|
||||
|
||||
const prevIcon = computed(() => props.prevIcon || (dir.value === 'rtl' ? appConfig.ui.icons.arrowRight : appConfig.ui.icons.arrowLeft))
|
||||
const nextIcon = computed(() => props.nextIcon || (dir.value === 'rtl' ? appConfig.ui.icons.arrowLeft : appConfig.ui.icons.arrowRight))
|
||||
|
||||
const ui = computed(() => carousel({
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.carousel || {}) })({
|
||||
orientation: props.orientation
|
||||
}))
|
||||
|
||||
|
||||
@@ -1,16 +1,10 @@
|
||||
<script lang="ts">
|
||||
import type { VariantProps } from 'tailwind-variants'
|
||||
import type { CheckboxRootProps } from 'reka-ui'
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/checkbox'
|
||||
import { tv } from '../utils/tv'
|
||||
import type { ComponentConfig } from '../types/utils'
|
||||
|
||||
const appConfigCheckbox = _appConfig as AppConfig & { ui: { checkbox: Partial<typeof theme> } }
|
||||
|
||||
const checkbox = tv({ extend: tv(theme), ...(appConfigCheckbox.ui?.checkbox || {}) })
|
||||
|
||||
type CheckboxVariants = VariantProps<typeof checkbox>
|
||||
type Checkbox = ComponentConfig<typeof theme, AppConfig, 'checkbox'>
|
||||
|
||||
export interface CheckboxProps extends Pick<CheckboxRootProps, 'disabled' | 'required' | 'name' | 'value' | 'id' | 'defaultValue'> {
|
||||
/**
|
||||
@@ -23,11 +17,11 @@ export interface CheckboxProps extends Pick<CheckboxRootProps, 'disabled' | 'req
|
||||
/**
|
||||
* @defaultValue 'primary'
|
||||
*/
|
||||
color?: CheckboxVariants['color']
|
||||
color?: Checkbox['variants']['color']
|
||||
/**
|
||||
* @defaultValue 'md'
|
||||
*/
|
||||
size?: CheckboxVariants['size']
|
||||
size?: Checkbox['variants']['size']
|
||||
/**
|
||||
* The icon displayed when checked.
|
||||
* @defaultValue appConfig.ui.icons.check
|
||||
@@ -41,7 +35,7 @@ export interface CheckboxProps extends Pick<CheckboxRootProps, 'disabled' | 'req
|
||||
*/
|
||||
indeterminateIcon?: string
|
||||
class?: any
|
||||
ui?: Partial<typeof checkbox.slots>
|
||||
ui?: Checkbox['slots']
|
||||
}
|
||||
|
||||
export type CheckboxEmits = {
|
||||
@@ -60,6 +54,7 @@ import { Primitive, CheckboxRoot, CheckboxIndicator, Label, useForwardProps } fr
|
||||
import { reactivePick } from '@vueuse/core'
|
||||
import { useAppConfig } from '#imports'
|
||||
import { useFormField } from '../composables/useFormField'
|
||||
import { tv } from '../utils/tv'
|
||||
import UIcon from './Icon.vue'
|
||||
|
||||
defineOptions({ inheritAttrs: false })
|
||||
@@ -70,13 +65,14 @@ const emits = defineEmits<CheckboxEmits>()
|
||||
|
||||
const modelValue = defineModel<boolean | 'indeterminate'>({ default: undefined })
|
||||
|
||||
const appConfig = useAppConfig() as Checkbox['AppConfig']
|
||||
|
||||
const rootProps = useForwardProps(reactivePick(props, 'required', 'value', 'defaultValue'))
|
||||
|
||||
const appConfig = useAppConfig()
|
||||
const { id: _id, emitFormChange, emitFormInput, size, color, name, disabled, ariaAttrs } = useFormField<CheckboxProps>(props)
|
||||
const id = _id.value ?? useId()
|
||||
|
||||
const ui = computed(() => checkbox({
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.checkbox || {}) })({
|
||||
size: size.value,
|
||||
color: color.value,
|
||||
required: props.required,
|
||||
|
||||
@@ -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/chip'
|
||||
import { tv } from '../utils/tv'
|
||||
import type { ComponentConfig } from '../types/utils'
|
||||
|
||||
const appConfigChip = _appConfig as AppConfig & { ui: { chip: Partial<typeof theme> } }
|
||||
|
||||
const chip = tv({ extend: tv(theme), ...(appConfigChip.ui?.chip || {}) })
|
||||
|
||||
type ChipVariants = VariantProps<typeof chip>
|
||||
type Chip = ComponentConfig<typeof theme, AppConfig, 'chip'>
|
||||
|
||||
export interface ChipProps {
|
||||
/**
|
||||
@@ -22,22 +16,22 @@ export interface ChipProps {
|
||||
/**
|
||||
* @defaultValue 'primary'
|
||||
*/
|
||||
color?: ChipVariants['color']
|
||||
color?: Chip['variants']['color']
|
||||
/**
|
||||
* @defaultValue 'md'
|
||||
*/
|
||||
size?: ChipVariants['size']
|
||||
size?: Chip['variants']['size']
|
||||
/**
|
||||
* The position of the chip.
|
||||
* @defaultValue 'top-right'
|
||||
*/
|
||||
position?: ChipVariants['position']
|
||||
position?: Chip['variants']['position']
|
||||
/** When `true`, keep the chip inside the component for rounded elements. */
|
||||
inset?: boolean
|
||||
/** When `true`, render the chip relatively to the parent. */
|
||||
standalone?: boolean
|
||||
class?: any
|
||||
ui?: Partial<typeof chip.slots>
|
||||
ui?: Chip['slots']
|
||||
}
|
||||
|
||||
export interface ChipEmits {
|
||||
@@ -53,7 +47,9 @@ export interface ChipSlots {
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { Primitive, Slot } from 'reka-ui'
|
||||
import { useAppConfig } from '#imports'
|
||||
import { useAvatarGroup } from '../composables/useAvatarGroup'
|
||||
import { tv } from '../utils/tv'
|
||||
|
||||
defineOptions({ inheritAttrs: false })
|
||||
|
||||
@@ -66,8 +62,9 @@ defineSlots<ChipSlots>()
|
||||
const show = defineModel<boolean>('show', { default: true })
|
||||
|
||||
const { size } = useAvatarGroup(props)
|
||||
const appConfig = useAppConfig() as Chip['AppConfig']
|
||||
|
||||
const ui = computed(() => chip({
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.chip || {}) })({
|
||||
color: props.color,
|
||||
size: size.value,
|
||||
position: props.position,
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
<script lang="ts">
|
||||
import type { CollapsibleRootProps, CollapsibleRootEmits } from 'reka-ui'
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/collapsible'
|
||||
import { tv } from '../utils/tv'
|
||||
import type { ComponentConfig } from '../types/utils'
|
||||
|
||||
const appConfigCollapsible = _appConfig as AppConfig & { ui: { collapsible: Partial<typeof theme> } }
|
||||
|
||||
const collapsible = tv({ extend: tv(theme), ...(appConfigCollapsible.ui?.collapsible || {}) })
|
||||
type Collapsible = ComponentConfig<typeof theme, AppConfig, 'collapsible'>
|
||||
|
||||
export interface CollapsibleProps extends Pick<CollapsibleRootProps, 'defaultOpen' | 'open' | 'disabled' | 'unmountOnHide'> {
|
||||
/**
|
||||
@@ -16,7 +13,7 @@ export interface CollapsibleProps extends Pick<CollapsibleRootProps, 'defaultOpe
|
||||
*/
|
||||
as?: any
|
||||
class?: any
|
||||
ui?: Partial<typeof collapsible.slots>
|
||||
ui?: Collapsible['slots']
|
||||
}
|
||||
|
||||
export interface CollapsibleEmits extends CollapsibleRootEmits {}
|
||||
@@ -28,8 +25,11 @@ export interface CollapsibleSlots {
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { CollapsibleRoot, CollapsibleTrigger, CollapsibleContent, useForwardPropsEmits } from 'reka-ui'
|
||||
import { reactivePick } from '@vueuse/core'
|
||||
import { useAppConfig } from '#imports'
|
||||
import { tv } from '../utils/tv'
|
||||
|
||||
const props = withDefaults(defineProps<CollapsibleProps>(), {
|
||||
unmountOnHide: true
|
||||
@@ -37,10 +37,12 @@ const props = withDefaults(defineProps<CollapsibleProps>(), {
|
||||
const emits = defineEmits<CollapsibleEmits>()
|
||||
const slots = defineSlots<CollapsibleSlots>()
|
||||
|
||||
const appConfig = useAppConfig() as Collapsible['AppConfig']
|
||||
|
||||
const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'defaultOpen', 'open', 'disabled', 'unmountOnHide'), emits)
|
||||
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
const ui = collapsible()
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.collapsible || {}) })())
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
<!-- eslint-disable vue/block-tag-newline -->
|
||||
<script lang="ts">
|
||||
import type { VariantProps } from 'tailwind-variants'
|
||||
import type { MaybeRefOrGetter } from '@vueuse/shared'
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/color-picker'
|
||||
import { tv } from '../utils/tv'
|
||||
import type { HSLObject } from 'colortranslator'
|
||||
import type { ComponentConfig } from '../types/utils'
|
||||
|
||||
const appConfigColorPicker = _appConfig as AppConfig & { ui: { colorPicker: Partial<typeof theme> } }
|
||||
|
||||
const colorPicker = tv({ extend: tv(theme), ...(appConfigColorPicker.ui?.colorPicker || {}) })
|
||||
|
||||
type ColorPickerVariants = VariantProps<typeof colorPicker>
|
||||
type ColorPicker = ComponentConfig<typeof theme, AppConfig, 'colorPicker'>
|
||||
|
||||
type HSVColor = {
|
||||
h: number
|
||||
@@ -67,9 +61,9 @@ export type ColorPickerProps = {
|
||||
/**
|
||||
* @defaultValue 'md'
|
||||
*/
|
||||
size?: ColorPickerVariants['size']
|
||||
size?: ColorPicker['variants']['size']
|
||||
class?: any
|
||||
ui?: Partial<typeof colorPicker.slots>
|
||||
ui?: ColorPicker['slots']
|
||||
}
|
||||
|
||||
</script>
|
||||
@@ -80,6 +74,8 @@ import { Primitive } from 'reka-ui'
|
||||
import { useEventListener, useElementBounding, watchThrottled, watchPausable } from '@vueuse/core'
|
||||
import { isClient } from '@vueuse/shared'
|
||||
import { ColorTranslator } from 'colortranslator'
|
||||
import { useAppConfig } from '#imports'
|
||||
import { tv } from '../utils/tv'
|
||||
|
||||
const props = withDefaults(defineProps<ColorPickerProps>(), {
|
||||
format: 'hex',
|
||||
@@ -88,6 +84,12 @@ const props = withDefaults(defineProps<ColorPickerProps>(), {
|
||||
})
|
||||
const modelValue = defineModel<string>(undefined)
|
||||
|
||||
const appConfig = useAppConfig() as ColorPicker['AppConfig']
|
||||
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.colorPicker || {}) })({
|
||||
size: props.size
|
||||
}))
|
||||
|
||||
const pickedColor = computed<HSVColor>({
|
||||
get() {
|
||||
try {
|
||||
@@ -258,10 +260,6 @@ const trackThumbStyle = computed(() => ({
|
||||
backgroundColor: trackThumbColor.value,
|
||||
top: `${trackThumbPosition.value.y}%`
|
||||
}))
|
||||
|
||||
const ui = computed(() => colorPicker({
|
||||
size: props.size
|
||||
}))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -4,16 +4,12 @@ import type { ListboxRootProps, ListboxRootEmits } from 'reka-ui'
|
||||
import type { FuseResult } from 'fuse.js'
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import type { UseFuseOptions } from '@vueuse/integrations/useFuse'
|
||||
import _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/command-palette'
|
||||
import type { UseComponentIconsProps } from '../composables/useComponentIcons'
|
||||
import { tv } from '../utils/tv'
|
||||
import type { AvatarProps, ButtonProps, ChipProps, KbdProps, InputProps, LinkProps } from '../types'
|
||||
import type { PartialString } from '../types/utils'
|
||||
import type { ComponentConfig } from '../types/utils'
|
||||
|
||||
const appConfigCommandPalette = _appConfig as AppConfig & { ui: { commandPalette: Partial<typeof theme> } }
|
||||
|
||||
const commandPalette = tv({ extend: tv(theme), ...(appConfigCommandPalette.ui?.commandPalette || {}) })
|
||||
type CommandPalette = ComponentConfig<typeof theme, AppConfig, 'commandPalette'>
|
||||
|
||||
export interface CommandPaletteItem extends Omit<LinkProps, 'type' | 'raw' | 'custom'> {
|
||||
prefix?: string
|
||||
@@ -115,7 +111,7 @@ export interface CommandPaletteProps<G, T> extends Pick<ListboxRootProps, 'multi
|
||||
*/
|
||||
labelKey?: string
|
||||
class?: any
|
||||
ui?: PartialString<typeof commandPalette.slots>
|
||||
ui?: CommandPalette['slots']
|
||||
}
|
||||
|
||||
export type CommandPaletteEmits<T> = ListboxRootEmits<T> & {
|
||||
@@ -126,7 +122,7 @@ type SlotProps<T> = (props: { item: T, index: number }) => any
|
||||
|
||||
export type CommandPaletteSlots<G extends { slot?: string }, T extends { slot?: string }> = {
|
||||
'empty'(props: { searchTerm?: string }): any
|
||||
'close'(props: { ui: ReturnType<typeof commandPalette> }): any
|
||||
'close'(props: { ui: { [K in keyof Required<CommandPalette['slots']>]: (props?: Record<string, any>) => string } }): any
|
||||
'item': SlotProps<T>
|
||||
'item-leading': SlotProps<T>
|
||||
'item-label': SlotProps<T>
|
||||
@@ -144,6 +140,7 @@ import { useFuse } from '@vueuse/integrations/useFuse'
|
||||
import { useAppConfig } from '#imports'
|
||||
import { useLocale } from '../composables/useLocale'
|
||||
import { omit, get } from '../utils'
|
||||
import { tv } from '../utils/tv'
|
||||
import { highlight } from '../utils/fuse'
|
||||
import { pickLinkProps } from '../utils/link'
|
||||
import UIcon from './Icon.vue'
|
||||
@@ -166,13 +163,13 @@ const slots = defineSlots<CommandPaletteSlots<G, T>>()
|
||||
const searchTerm = defineModel<string>('searchTerm', { default: '' })
|
||||
|
||||
const { t } = useLocale()
|
||||
const appConfig = useAppConfig()
|
||||
const appConfig = useAppConfig() as CommandPalette['AppConfig']
|
||||
|
||||
const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'disabled', 'multiple', 'modelValue', 'defaultValue', 'highlightOnHover'), emits)
|
||||
const inputProps = useForwardProps(reactivePick(props, 'loading', 'loadingIcon'))
|
||||
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
const ui = commandPalette()
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.commandPalette || {}) })())
|
||||
|
||||
const fuse = computed(() => defu({}, props.fuse, {
|
||||
fuseOptions: {
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
<script lang="ts">
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/container'
|
||||
import { tv } from '../utils/tv'
|
||||
import type { ComponentConfig } from '../types/utils'
|
||||
|
||||
const appConfigContainer = _appConfig as AppConfig & { ui: { container: Partial<typeof theme> } }
|
||||
|
||||
const container = tv({ extend: tv(theme), ...(appConfigContainer.ui?.container || {}) })
|
||||
type Container = ComponentConfig<typeof theme, AppConfig, 'container'>
|
||||
|
||||
export interface ContainerProps {
|
||||
/**
|
||||
@@ -23,14 +20,21 @@ export interface ContainerSlots {
|
||||
</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<ContainerProps>()
|
||||
defineSlots<ContainerSlots>()
|
||||
|
||||
const appConfig = useAppConfig() as Container['AppConfig']
|
||||
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.container || {}) }))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Primitive :as="as" :class="container({ class: props.class })">
|
||||
<Primitive :as="as" :class="ui({ class: props.class })">
|
||||
<slot />
|
||||
</Primitive>
|
||||
</template>
|
||||
|
||||
@@ -1,26 +1,12 @@
|
||||
<!-- eslint-disable vue/block-tag-newline -->
|
||||
<script lang="ts">
|
||||
import type { VariantProps } from 'tailwind-variants'
|
||||
import type { ContextMenuRootProps, ContextMenuRootEmits, ContextMenuContentProps, ContextMenuContentEmits } from 'reka-ui'
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/context-menu'
|
||||
import { tv } from '../utils/tv'
|
||||
import type { AvatarProps, KbdProps, LinkProps } from '../types'
|
||||
import type {
|
||||
ArrayOrNested,
|
||||
DynamicSlots,
|
||||
MergeTypes,
|
||||
NestedItem,
|
||||
PartialString,
|
||||
EmitsToProps
|
||||
} from '../types/utils'
|
||||
import type { ArrayOrNested, DynamicSlots, MergeTypes, NestedItem, EmitsToProps, ComponentConfig } from '../types/utils'
|
||||
|
||||
const appConfigContextMenu = _appConfig as AppConfig & { ui: { contextMenu: Partial<typeof theme> } }
|
||||
|
||||
const contextMenu = tv({ extend: tv(theme), ...(appConfigContextMenu.ui?.contextMenu || {}) })
|
||||
|
||||
type ContextMenuVariants = VariantProps<typeof contextMenu>
|
||||
type ContextMenu = ComponentConfig<typeof theme, AppConfig, 'contextMenu'>
|
||||
|
||||
export interface ContextMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'custom'> {
|
||||
label?: string
|
||||
@@ -28,7 +14,7 @@ export interface ContextMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'custo
|
||||
* @IconifyIcon
|
||||
*/
|
||||
icon?: string
|
||||
color?: ContextMenuVariants['color']
|
||||
color?: ContextMenu['variants']['color']
|
||||
avatar?: AvatarProps
|
||||
content?: Omit<ContextMenuContentProps, 'as' | 'asChild' | 'forceMount'> & Partial<EmitsToProps<ContextMenuContentEmits>>
|
||||
kbds?: KbdProps['value'][] | KbdProps[]
|
||||
@@ -53,7 +39,7 @@ export interface ContextMenuProps<T extends ArrayOrNested<ContextMenuItem> = Arr
|
||||
/**
|
||||
* @defaultValue 'md'
|
||||
*/
|
||||
size?: ContextMenuVariants['size']
|
||||
size?: ContextMenu['variants']['size']
|
||||
items?: T
|
||||
/**
|
||||
* The icon displayed when an item is checked.
|
||||
@@ -88,7 +74,7 @@ export interface ContextMenuProps<T extends ArrayOrNested<ContextMenuItem> = Arr
|
||||
labelKey?: keyof NestedItem<T>
|
||||
disabled?: boolean
|
||||
class?: any
|
||||
ui?: PartialString<typeof contextMenu.slots>
|
||||
ui?: ContextMenu['slots']
|
||||
}
|
||||
|
||||
export interface ContextMenuEmits extends ContextMenuRootEmits {}
|
||||
@@ -112,7 +98,9 @@ export type ContextMenuSlots<
|
||||
import { computed, toRef } from 'vue'
|
||||
import { ContextMenuRoot, ContextMenuTrigger, useForwardPropsEmits } from 'reka-ui'
|
||||
import { reactivePick } from '@vueuse/core'
|
||||
import { useAppConfig } from '#imports'
|
||||
import { omit } from '../utils'
|
||||
import { tv } from '../utils/tv'
|
||||
import UContextMenuContent from './ContextMenuContent.vue'
|
||||
|
||||
const props = withDefaults(defineProps<ContextMenuProps<T>>(), {
|
||||
@@ -124,12 +112,13 @@ const props = withDefaults(defineProps<ContextMenuProps<T>>(), {
|
||||
const emits = defineEmits<ContextMenuEmits>()
|
||||
const slots = defineSlots<ContextMenuSlots<T>>()
|
||||
|
||||
const rootProps = useForwardPropsEmits(reactivePick(props, 'modal'), emits)
|
||||
const appConfig = useAppConfig() as ContextMenu['AppConfig']
|
||||
|
||||
const rootProps = useForwardPropsEmits(reactivePick(props, 'modal'), emits)
|
||||
const contentProps = toRef(() => props.content)
|
||||
const proxySlots = omit(slots, ['default'])
|
||||
|
||||
const ui = computed(() => contextMenu({
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.contextMenu || {}) })({
|
||||
size: props.size
|
||||
}))
|
||||
</script>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<script lang="ts">
|
||||
import type { ContextMenuContentProps as RekaContextMenuContentProps, ContextMenuContentEmits as RekaContextMenuContentEmits } from 'reka-ui'
|
||||
import theme from '#build/ui/context-menu'
|
||||
import { tv } from '../utils/tv'
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import type theme from '#build/ui/context-menu'
|
||||
import type { AvatarProps, ContextMenuItem, ContextMenuSlots, KbdProps } from '../types'
|
||||
import type { ArrayOrNested, NestedItem } from '../types/utils'
|
||||
import type { ArrayOrNested, NestedItem, ComponentConfig } from '../types/utils'
|
||||
|
||||
const _contextMenu = tv(theme)()
|
||||
type ContextMenu = ComponentConfig<typeof theme, AppConfig, 'contextMenu'>
|
||||
|
||||
interface ContextMenuContentProps<T extends ArrayOrNested<ContextMenuItem>> extends Omit<RekaContextMenuContentProps, 'as' | 'asChild' | 'forceMount'> {
|
||||
items?: T
|
||||
@@ -25,8 +25,8 @@ interface ContextMenuContentProps<T extends ArrayOrNested<ContextMenuItem>> exte
|
||||
*/
|
||||
externalIcon?: boolean | string
|
||||
class?: any
|
||||
ui: typeof _contextMenu
|
||||
uiOverride?: any
|
||||
ui: { [K in keyof Required<ContextMenu['slots']>]: (props?: Record<string, any>) => string }
|
||||
uiOverride?: ContextMenu['slots']
|
||||
}
|
||||
|
||||
interface ContextMenuContentEmits extends RekaContextMenuContentEmits {}
|
||||
@@ -53,8 +53,9 @@ const props = defineProps<ContextMenuContentProps<T>>()
|
||||
const emits = defineEmits<ContextMenuContentEmits>()
|
||||
const slots = defineSlots<ContextMenuSlots<T>>()
|
||||
|
||||
const appConfig = useAppConfig()
|
||||
const { dir } = useLocale()
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const contentProps = useForwardPropsEmits(reactiveOmit(props, 'sub', 'items', 'portal', 'labelKey', 'checkedIcon', 'loadingIcon', 'externalIcon', 'class', 'ui', 'uiOverride'), emits)
|
||||
const proxySlots = omit(slots, ['default'])
|
||||
|
||||
|
||||
@@ -2,14 +2,10 @@
|
||||
import type { DrawerRootProps, DrawerRootEmits } from 'vaul-vue'
|
||||
import type { DialogContentProps, DialogContentEmits } from 'reka-ui'
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/drawer'
|
||||
import { tv } from '../utils/tv'
|
||||
import type { EmitsToProps } from '../types/utils'
|
||||
import type { EmitsToProps, ComponentConfig } from '../types/utils'
|
||||
|
||||
const appConfigDrawer = _appConfig as AppConfig & { ui: { drawer: Partial<typeof theme> } }
|
||||
|
||||
const drawer = tv({ extend: tv(theme), ...(appConfigDrawer.ui?.drawer || {}) })
|
||||
type Drawer = ComponentConfig<typeof theme, AppConfig, 'drawer'>
|
||||
|
||||
export interface DrawerProps extends Pick<DrawerRootProps, 'activeSnapPoint' | 'closeThreshold' | 'shouldScaleBackground' | 'setBackgroundColorOnScale' | 'scrollLockTimeout' | 'fixed' | 'dismissible' | 'modal' | 'open' | 'defaultOpen' | 'nested' | 'direction' | 'noBodyStyles' | 'handleOnly' | 'preventScrollRestoration' | 'snapPoints'> {
|
||||
/**
|
||||
@@ -42,7 +38,7 @@ export interface DrawerProps extends Pick<DrawerRootProps, 'activeSnapPoint' | '
|
||||
*/
|
||||
portal?: boolean
|
||||
class?: any
|
||||
ui?: Partial<typeof drawer.slots>
|
||||
ui?: Drawer['slots']
|
||||
}
|
||||
|
||||
export interface DrawerEmits extends DrawerRootEmits {}
|
||||
@@ -63,6 +59,8 @@ import { computed, toRef } from 'vue'
|
||||
import { useForwardPropsEmits } from 'reka-ui'
|
||||
import { DrawerRoot, DrawerTrigger, DrawerPortal, DrawerOverlay, DrawerContent, DrawerTitle, DrawerDescription, DrawerHandle } from 'vaul-vue'
|
||||
import { reactivePick } from '@vueuse/core'
|
||||
import { useAppConfig } from '#imports'
|
||||
import { tv } from '../utils/tv'
|
||||
|
||||
const props = withDefaults(defineProps<DrawerProps>(), {
|
||||
direction: 'bottom',
|
||||
@@ -75,13 +73,15 @@ const props = withDefaults(defineProps<DrawerProps>(), {
|
||||
const emits = defineEmits<DrawerEmits>()
|
||||
const slots = defineSlots<DrawerSlots>()
|
||||
|
||||
const appConfig = useAppConfig() as Drawer['AppConfig']
|
||||
|
||||
const rootProps = useForwardPropsEmits(reactivePick(props, 'activeSnapPoint', 'closeThreshold', 'shouldScaleBackground', 'setBackgroundColorOnScale', 'scrollLockTimeout', 'fixed', 'dismissible', 'modal', 'open', 'defaultOpen', 'nested', 'direction', 'noBodyStyles', 'handleOnly', 'preventScrollRestoration', 'snapPoints'), emits)
|
||||
const contentProps = toRef(() => props.content)
|
||||
const contentEvents = {
|
||||
closeAutoFocus: (e: Event) => e.preventDefault()
|
||||
}
|
||||
|
||||
const ui = computed(() => drawer({
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.drawer || {}) })({
|
||||
direction: props.direction,
|
||||
inset: props.inset
|
||||
}))
|
||||
|
||||
@@ -1,26 +1,12 @@
|
||||
<!-- eslint-disable vue/block-tag-newline -->
|
||||
<script lang="ts">
|
||||
import type { VariantProps } from 'tailwind-variants'
|
||||
import type { DropdownMenuRootProps, DropdownMenuRootEmits, DropdownMenuContentProps, DropdownMenuContentEmits, DropdownMenuArrowProps } from 'reka-ui'
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/dropdown-menu'
|
||||
import { tv } from '../utils/tv'
|
||||
import type { AvatarProps, KbdProps, LinkProps } from '../types'
|
||||
import type {
|
||||
ArrayOrNested,
|
||||
DynamicSlots,
|
||||
MergeTypes,
|
||||
NestedItem,
|
||||
PartialString,
|
||||
EmitsToProps
|
||||
} from '../types/utils'
|
||||
import type { ArrayOrNested, DynamicSlots, MergeTypes, NestedItem, EmitsToProps, ComponentConfig } from '../types/utils'
|
||||
|
||||
const appConfigDropdownMenu = _appConfig as AppConfig & { ui: { dropdownMenu: Partial<typeof theme> } }
|
||||
|
||||
const dropdownMenu = tv({ extend: tv(theme), ...(appConfigDropdownMenu.ui?.dropdownMenu || {}) })
|
||||
|
||||
type DropdownMenuVariants = VariantProps<typeof dropdownMenu>
|
||||
type DropdownMenu = ComponentConfig<typeof theme, AppConfig, 'dropdownMenu'>
|
||||
|
||||
export interface DropdownMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'custom'> {
|
||||
label?: string
|
||||
@@ -28,7 +14,7 @@ export interface DropdownMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'cust
|
||||
* @IconifyIcon
|
||||
*/
|
||||
icon?: string
|
||||
color?: DropdownMenuVariants['color']
|
||||
color?: DropdownMenu['variants']['color']
|
||||
avatar?: AvatarProps
|
||||
content?: Omit<DropdownMenuContentProps, 'as' | 'asChild' | 'forceMount'> & Partial<EmitsToProps<DropdownMenuContentEmits>>
|
||||
kbds?: KbdProps['value'][] | KbdProps[]
|
||||
@@ -53,7 +39,7 @@ export interface DropdownMenuProps<T extends ArrayOrNested<DropdownMenuItem> = A
|
||||
/**
|
||||
* @defaultValue 'md'
|
||||
*/
|
||||
size?: DropdownMenuVariants['size']
|
||||
size?: DropdownMenu['variants']['size']
|
||||
items?: T
|
||||
/**
|
||||
* The icon displayed when an item is checked.
|
||||
@@ -96,7 +82,7 @@ export interface DropdownMenuProps<T extends ArrayOrNested<DropdownMenuItem> = A
|
||||
labelKey?: keyof NestedItem<T>
|
||||
disabled?: boolean
|
||||
class?: any
|
||||
ui?: PartialString<typeof dropdownMenu.slots>
|
||||
ui?: DropdownMenu['slots']
|
||||
}
|
||||
|
||||
export interface DropdownMenuEmits extends DropdownMenuRootEmits {}
|
||||
@@ -121,7 +107,9 @@ import { computed, toRef } from 'vue'
|
||||
import { defu } from 'defu'
|
||||
import { DropdownMenuRoot, DropdownMenuTrigger, DropdownMenuArrow, useForwardPropsEmits } from 'reka-ui'
|
||||
import { reactivePick } from '@vueuse/core'
|
||||
import { useAppConfig } from '#imports'
|
||||
import { omit } from '../utils'
|
||||
import { tv } from '../utils/tv'
|
||||
import UDropdownMenuContent from './DropdownMenuContent.vue'
|
||||
|
||||
const props = withDefaults(defineProps<DropdownMenuProps<T>>(), {
|
||||
@@ -133,12 +121,14 @@ const props = withDefaults(defineProps<DropdownMenuProps<T>>(), {
|
||||
const emits = defineEmits<DropdownMenuEmits>()
|
||||
const slots = defineSlots<DropdownMenuSlots<T>>()
|
||||
|
||||
const appConfig = useAppConfig() as DropdownMenu['AppConfig']
|
||||
|
||||
const rootProps = useForwardPropsEmits(reactivePick(props, 'defaultOpen', 'open', 'modal'), emits)
|
||||
const contentProps = toRef(() => defu(props.content, { side: 'bottom', sideOffset: 8, collisionPadding: 8 }) as DropdownMenuContentProps)
|
||||
const arrowProps = toRef(() => props.arrow as DropdownMenuArrowProps)
|
||||
const proxySlots = omit(slots, ['default'])
|
||||
|
||||
const ui = computed(() => dropdownMenu({
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.dropdownMenu || {}) })({
|
||||
size: props.size
|
||||
}))
|
||||
</script>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<!-- eslint-disable vue/block-tag-newline -->
|
||||
<script lang="ts">
|
||||
import type { DropdownMenuContentProps as RekaDropdownMenuContentProps, DropdownMenuContentEmits as RekaDropdownMenuContentEmits } from 'reka-ui'
|
||||
import theme from '#build/ui/dropdown-menu'
|
||||
import { tv } from '../utils/tv'
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import type theme from '#build/ui/dropdown-menu'
|
||||
import type { KbdProps, AvatarProps, DropdownMenuItem, DropdownMenuSlots } from '../types'
|
||||
import type { ArrayOrNested, NestedItem } from '../types/utils'
|
||||
import type { ArrayOrNested, NestedItem, ComponentConfig } from '../types/utils'
|
||||
|
||||
const _dropdownMenu = tv(theme)()
|
||||
type DropdownMenu = ComponentConfig<typeof theme, AppConfig, 'dropdownMenu'>
|
||||
|
||||
interface DropdownMenuContentProps<T extends ArrayOrNested<DropdownMenuItem>> extends Omit<RekaDropdownMenuContentProps, 'as' | 'asChild' | 'forceMount'> {
|
||||
items?: T
|
||||
@@ -26,8 +26,8 @@ interface DropdownMenuContentProps<T extends ArrayOrNested<DropdownMenuItem>> ex
|
||||
*/
|
||||
externalIcon?: boolean | string
|
||||
class?: any
|
||||
ui: typeof _dropdownMenu
|
||||
uiOverride?: any
|
||||
ui: { [K in keyof Required<DropdownMenu['slots']>]: (props?: Record<string, any>) => string }
|
||||
uiOverride?: DropdownMenu['slots']
|
||||
}
|
||||
|
||||
interface DropdownMenuContentEmits extends RekaDropdownMenuContentEmits {}
|
||||
@@ -59,8 +59,9 @@ const props = defineProps<DropdownMenuContentProps<T>>()
|
||||
const emits = defineEmits<DropdownMenuContentEmits>()
|
||||
const slots = defineSlots<DropdownMenuContentSlots<T>>()
|
||||
|
||||
const appConfig = useAppConfig()
|
||||
const { dir } = useLocale()
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const contentProps = useForwardPropsEmits(reactiveOmit(props, 'sub', 'items', 'portal', 'labelKey', 'checkedIcon', 'loadingIcon', 'externalIcon', 'class', 'ui', 'uiOverride'), emits)
|
||||
const proxySlots = omit(slots, ['default'])
|
||||
|
||||
|
||||
@@ -1,27 +1,25 @@
|
||||
<script lang="ts">
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/form'
|
||||
import { tv } from '../utils/tv'
|
||||
import type { FormSchema, FormError, FormInputEvents, FormErrorEvent, FormSubmitEvent, FormEvent, Form, FormErrorWithId } from '../types/form'
|
||||
import type { DeepReadonly } from 'vue'
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import theme from '#build/ui/form'
|
||||
import type { FormSchema, FormError, FormInputEvents, FormErrorEvent, FormSubmitEvent, FormEvent, Form, FormErrorWithId } from '../types/form'
|
||||
import type { ComponentConfig } from '../types/utils'
|
||||
|
||||
const appConfigForm = _appConfig as AppConfig & { ui: { form: Partial<typeof theme> } }
|
||||
type FormConfig = ComponentConfig<typeof theme, AppConfig, 'form'>
|
||||
|
||||
const form = tv({ extend: tv(theme), ...(appConfigForm.ui?.form || {}) })
|
||||
|
||||
export interface FormProps<T extends object> {
|
||||
export interface FormProps<I extends object, O extends object = I> {
|
||||
id?: string | number
|
||||
/** Schema to validate the form state. Supports Standard Schema objects, Yup, Joi, and Superstructs. */
|
||||
schema?: FormSchema<T>
|
||||
schema?: FormSchema<I, O>
|
||||
|
||||
/** An object representing the current state of the form. */
|
||||
state: Partial<T>
|
||||
state: Partial<I>
|
||||
/**
|
||||
* Custom validation function to validate the form state.
|
||||
* @param state - The current state of the form.
|
||||
* @returns A promise that resolves to an array of FormError objects, or an array of FormError objects directly.
|
||||
*/
|
||||
validate?: (state: Partial<T>) => Promise<FormError[]> | FormError[]
|
||||
validate?: (state: Partial<I>) => Promise<FormError[]> | FormError[]
|
||||
/**
|
||||
* The list of input events that trigger the form validation.
|
||||
* @defaultValue `['blur', 'change', 'input']`
|
||||
@@ -34,17 +32,23 @@ export interface FormProps<T extends object> {
|
||||
* @defaultValue `300`
|
||||
*/
|
||||
validateOnInputDelay?: number
|
||||
|
||||
/**
|
||||
* If true, schema transformations will be applied to the state on submit.
|
||||
* If true and nested in another form, this form will attach to its parent and validate at the same time.
|
||||
*/
|
||||
nested?: boolean
|
||||
/**
|
||||
* When `true`, all form elements will be disabled on `@submit` event.
|
||||
* This will cause any focused input elements to lose their focus state.
|
||||
* @defaultValue `true`
|
||||
*/
|
||||
transform?: boolean
|
||||
loadingAuto?: boolean
|
||||
class?: any
|
||||
onSubmit?: ((event: FormSubmitEvent<T>) => void | Promise<void>) | (() => void | Promise<void>)
|
||||
onSubmit?: ((event: FormSubmitEvent<O>) => void | Promise<void>) | (() => void | Promise<void>)
|
||||
}
|
||||
|
||||
export interface FormEmits<T extends object> {
|
||||
(e: 'submit', payload: FormSubmitEvent<T>): void
|
||||
export interface FormEmits<I extends object, O extends object = I> {
|
||||
(e: 'submit', payload: FormSubmitEvent<O>): void
|
||||
(e: 'error', payload: FormErrorEvent): void
|
||||
}
|
||||
|
||||
@@ -53,27 +57,33 @@ export interface FormSlots {
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup generic="T extends object">
|
||||
<script lang="ts" setup generic="I extends object, O extends object = I">
|
||||
import { provide, inject, nextTick, ref, onUnmounted, onMounted, computed, useId, readonly } from 'vue'
|
||||
import { useEventBus } from '@vueuse/core'
|
||||
import { useAppConfig } from '#imports'
|
||||
import { formOptionsInjectionKey, formInputsInjectionKey, formBusInjectionKey, formLoadingInjectionKey } from '../composables/useFormField'
|
||||
import { tv } from '../utils/tv'
|
||||
import { validateSchema } from '../utils/form'
|
||||
import { FormValidationException } from '../types/form'
|
||||
|
||||
const props = withDefaults(defineProps<FormProps<T>>(), {
|
||||
const props = withDefaults(defineProps<FormProps<I, O>>(), {
|
||||
validateOn() {
|
||||
return ['input', 'blur', 'change'] as FormInputEvents[]
|
||||
},
|
||||
validateOnInputDelay: 300,
|
||||
transform: true
|
||||
loadingAuto: true
|
||||
})
|
||||
|
||||
const emits = defineEmits<FormEmits<T>>()
|
||||
const emits = defineEmits<FormEmits<I, O>>()
|
||||
defineSlots<FormSlots>()
|
||||
|
||||
const appConfig = useAppConfig() as FormConfig['AppConfig']
|
||||
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.form || {}) }))
|
||||
|
||||
const formId = props.id ?? useId() as string
|
||||
|
||||
const bus = useEventBus<FormEvent<T>>(`form-${formId}`)
|
||||
const bus = useEventBus<FormEvent<I>>(`form-${formId}`)
|
||||
const parentBus = inject(
|
||||
formBusInjectionKey,
|
||||
undefined
|
||||
@@ -116,14 +126,14 @@ onUnmounted(() => {
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
if (parentBus) {
|
||||
if (props.nested && parentBus) {
|
||||
await nextTick()
|
||||
parentBus.emit({ type: 'attach', validate: _validate, formId })
|
||||
}
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
if (parentBus) {
|
||||
if (props.nested && parentBus) {
|
||||
parentBus.emit({ type: 'detach', formId })
|
||||
}
|
||||
})
|
||||
@@ -131,12 +141,12 @@ onUnmounted(() => {
|
||||
const errors = ref<FormErrorWithId[]>([])
|
||||
provide('form-errors', errors)
|
||||
|
||||
const inputs = ref<{ [P in keyof T]?: { id?: string, pattern?: RegExp } }>({})
|
||||
const inputs = ref<{ [P in keyof I]?: { id?: string, pattern?: RegExp } }>({})
|
||||
provide(formInputsInjectionKey, inputs as any)
|
||||
|
||||
const dirtyFields = new Set<keyof T>()
|
||||
const touchedFields = new Set<keyof T>()
|
||||
const blurredFields = new Set<keyof T>()
|
||||
const dirtyFields = new Set<keyof I>()
|
||||
const touchedFields = new Set<keyof I>()
|
||||
const blurredFields = new Set<keyof I>()
|
||||
|
||||
function resolveErrorIds(errs: FormError[]): FormErrorWithId[] {
|
||||
return errs.map(err => ({
|
||||
@@ -145,7 +155,7 @@ function resolveErrorIds(errs: FormError[]): FormErrorWithId[] {
|
||||
}))
|
||||
}
|
||||
|
||||
const transformedState = ref<T | null>(null)
|
||||
const transformedState = ref<I | null>(null)
|
||||
|
||||
async function getErrors(): Promise<FormErrorWithId[]> {
|
||||
let errs = props.validate ? (await props.validate(props.state)) ?? [] : []
|
||||
@@ -162,8 +172,8 @@ async function getErrors(): Promise<FormErrorWithId[]> {
|
||||
return resolveErrorIds(errs)
|
||||
}
|
||||
|
||||
async function _validate(opts: { name?: keyof T | (keyof T)[], silent?: boolean, nested?: boolean, transform?: boolean } = { silent: false, nested: true, transform: false }): Promise<T | false> {
|
||||
const names = opts.name && !Array.isArray(opts.name) ? [opts.name] : opts.name as (keyof T)[]
|
||||
async function _validate(opts: { name?: keyof I | (keyof I)[], silent?: boolean, nested?: boolean } = { silent: false, nested: true }): Promise<O | false> {
|
||||
const names = opts.name && !Array.isArray(opts.name) ? [opts.name] : opts.name as (keyof I)[]
|
||||
|
||||
const nestedValidatePromises = !names && opts.nested
|
||||
? Array.from(nestedForms.value.values()).map(
|
||||
@@ -199,23 +209,19 @@ async function _validate(opts: { name?: keyof T | (keyof T)[], silent?: boolean,
|
||||
throw new FormValidationException(formId, errors.value, childErrors)
|
||||
}
|
||||
|
||||
if (opts.transform) {
|
||||
Object.assign(props.state, transformedState.value)
|
||||
}
|
||||
|
||||
return props.state as T
|
||||
return transformedState.value
|
||||
}
|
||||
|
||||
const loading = ref(false)
|
||||
provide(formLoadingInjectionKey, readonly(loading))
|
||||
|
||||
async function onSubmitWrapper(payload: Event) {
|
||||
loading.value = true
|
||||
loading.value = props.loadingAuto && true
|
||||
|
||||
const event = payload as FormSubmitEvent<any>
|
||||
|
||||
try {
|
||||
event.data = await _validate({ nested: true, transform: props.transform })
|
||||
event.data = await _validate({ nested: true })
|
||||
await props.onSubmit?.(event)
|
||||
dirtyFields.clear()
|
||||
} catch (error) {
|
||||
@@ -241,11 +247,11 @@ provide(formOptionsInjectionKey, computed(() => ({
|
||||
validateOnInputDelay: props.validateOnInputDelay
|
||||
})))
|
||||
|
||||
defineExpose<Form<T>>({
|
||||
defineExpose<Form<I, O>>({
|
||||
validate: _validate,
|
||||
errors,
|
||||
|
||||
setErrors(errs: FormError[], name?: keyof T) {
|
||||
setErrors(errs: FormError[], name?: keyof I) {
|
||||
if (name) {
|
||||
errors.value = errors.value
|
||||
.filter(error => error.name !== name)
|
||||
@@ -259,7 +265,7 @@ defineExpose<Form<T>>({
|
||||
await onSubmitWrapper(new Event('submit'))
|
||||
},
|
||||
|
||||
getErrors(name?: keyof T) {
|
||||
getErrors(name?: keyof I) {
|
||||
if (name) {
|
||||
return errors.value.filter(err => err.name === name)
|
||||
}
|
||||
@@ -275,11 +281,12 @@ defineExpose<Form<T>>({
|
||||
},
|
||||
|
||||
disabled,
|
||||
loading,
|
||||
dirty: computed(() => !!dirtyFields.size),
|
||||
|
||||
dirtyFields: readonly(dirtyFields) as DeepReadonly<Set<keyof T>>,
|
||||
blurredFields: readonly(blurredFields) as DeepReadonly<Set<keyof T>>,
|
||||
touchedFields: readonly(touchedFields) as DeepReadonly<Set<keyof T>>
|
||||
dirtyFields: readonly(dirtyFields) as DeepReadonly<Set<keyof I>>,
|
||||
blurredFields: readonly(blurredFields) as DeepReadonly<Set<keyof I>>,
|
||||
touchedFields: readonly(touchedFields) as DeepReadonly<Set<keyof I>>
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -287,7 +294,7 @@ defineExpose<Form<T>>({
|
||||
<component
|
||||
:is="parentBus ? 'div' : 'form'"
|
||||
:id="formId"
|
||||
:class="form({ class: props.class })"
|
||||
:class="ui({ class: props.class })"
|
||||
@submit.prevent="onSubmitWrapper"
|
||||
>
|
||||
<slot :errors="errors" />
|
||||
|
||||
@@ -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/form-field'
|
||||
import { tv } from '../utils/tv'
|
||||
import type { ComponentConfig } from '../types/utils'
|
||||
|
||||
const appConfigFormField = _appConfig as AppConfig & { ui: { formField: Partial<typeof theme> } }
|
||||
|
||||
const formField = tv({ extend: tv(theme), ...(appConfigFormField.ui?.formField || {}) })
|
||||
|
||||
type FormFieldVariants = VariantProps<typeof formField>
|
||||
type FormField = ComponentConfig<typeof theme, AppConfig, 'formField'>
|
||||
|
||||
export interface FormFieldProps {
|
||||
/**
|
||||
@@ -29,7 +23,7 @@ export interface FormFieldProps {
|
||||
/**
|
||||
* @defaultValue 'md'
|
||||
*/
|
||||
size?: FormFieldVariants['size']
|
||||
size?: FormField['variants']['size']
|
||||
required?: boolean
|
||||
/** If true, validation on input will be active immediately instead of waiting for a blur event. */
|
||||
eagerValidation?: boolean
|
||||
@@ -39,7 +33,7 @@ export interface FormFieldProps {
|
||||
*/
|
||||
validateOnInputDelay?: number
|
||||
class?: any
|
||||
ui?: Partial<typeof formField.slots>
|
||||
ui?: FormField['slots']
|
||||
}
|
||||
|
||||
export interface FormFieldSlots {
|
||||
@@ -55,13 +49,17 @@ export interface FormFieldSlots {
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, inject, provide, type Ref, useId } from 'vue'
|
||||
import { Primitive, Label } from 'reka-ui'
|
||||
import { useAppConfig } from '#imports'
|
||||
import { formFieldInjectionKey, inputIdInjectionKey } from '../composables/useFormField'
|
||||
import { tv } from '../utils/tv'
|
||||
import type { FormError, FormFieldInjectedOptions } from '../types/form'
|
||||
|
||||
const props = defineProps<FormFieldProps>()
|
||||
const slots = defineSlots<FormFieldSlots>()
|
||||
|
||||
const ui = computed(() => formField({
|
||||
const appConfig = useAppConfig() as FormField['AppConfig']
|
||||
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.formField || {}) })({
|
||||
size: props.size,
|
||||
required: props.required
|
||||
}))
|
||||
|
||||
@@ -1,19 +1,12 @@
|
||||
<script lang="ts">
|
||||
import type { InputHTMLAttributes } from 'vue'
|
||||
import type { VariantProps } from 'tailwind-variants'
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/input'
|
||||
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 appConfigInput = _appConfig as AppConfig & { ui: { input: Partial<typeof theme> } }
|
||||
|
||||
const input = tv({ extend: tv(theme), ...(appConfigInput.ui?.input || {}) })
|
||||
|
||||
type InputVariants = VariantProps<typeof input>
|
||||
type Input = ComponentConfig<typeof theme, AppConfig, 'input'>
|
||||
|
||||
export interface InputProps extends UseComponentIconsProps {
|
||||
/**
|
||||
@@ -29,15 +22,15 @@ export interface InputProps extends UseComponentIconsProps {
|
||||
/**
|
||||
* @defaultValue 'primary'
|
||||
*/
|
||||
color?: InputVariants['color']
|
||||
color?: Input['variants']['color']
|
||||
/**
|
||||
* @defaultValue 'outline'
|
||||
*/
|
||||
variant?: InputVariants['variant']
|
||||
variant?: Input['variants']['variant']
|
||||
/**
|
||||
* @defaultValue 'md'
|
||||
*/
|
||||
size?: InputVariants['size']
|
||||
size?: Input['variants']['size']
|
||||
required?: boolean
|
||||
autocomplete?: InputHTMLAttributes['autocomplete']
|
||||
autofocus?: boolean
|
||||
@@ -46,7 +39,7 @@ export interface InputProps extends UseComponentIconsProps {
|
||||
/** Highlight the ring color like a focus state. */
|
||||
highlight?: boolean
|
||||
class?: any
|
||||
ui?: PartialString<typeof input.slots>
|
||||
ui?: Input['slots']
|
||||
}
|
||||
|
||||
export interface InputEmits {
|
||||
@@ -65,10 +58,12 @@ export interface InputSlots {
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { Primitive } from 'reka-ui'
|
||||
import { useAppConfig } from '#imports'
|
||||
import { useButtonGroup } from '../composables/useButtonGroup'
|
||||
import { useComponentIcons } from '../composables/useComponentIcons'
|
||||
import { useFormField } from '../composables/useFormField'
|
||||
import { looseToNumber } from '../utils'
|
||||
import { tv } from '../utils/tv'
|
||||
import UIcon from './Icon.vue'
|
||||
import UAvatar from './Avatar.vue'
|
||||
|
||||
@@ -84,14 +79,15 @@ const slots = defineSlots<InputSlots>()
|
||||
|
||||
const [modelValue, modelModifiers] = defineModel<string | number | null>()
|
||||
|
||||
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 { orientation, size: buttonGroupSize } = useButtonGroup<InputProps>(props)
|
||||
const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponentIcons(props)
|
||||
|
||||
const inputSize = computed(() => buttonGroupSize.value || formGroupSize.value)
|
||||
|
||||
const ui = computed(() => input({
|
||||
type: props.type as InputVariants['type'],
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.input || {}) })({
|
||||
type: props.type as Input['variants']['type'],
|
||||
color: color.value,
|
||||
variant: props.variant,
|
||||
size: inputSize?.value,
|
||||
|
||||
@@ -1,27 +1,13 @@
|
||||
<script lang="ts">
|
||||
import type { InputHTMLAttributes } from 'vue'
|
||||
import type { VariantProps } from 'tailwind-variants'
|
||||
import type { ComboboxRootProps, ComboboxRootEmits, ComboboxContentProps, ComboboxContentEmits, ComboboxArrowProps } from 'reka-ui'
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/input-menu'
|
||||
import type { UseComponentIconsProps } from '../composables/useComponentIcons'
|
||||
import { tv } from '../utils/tv'
|
||||
import type { AvatarProps, ChipProps, InputProps } from '../types'
|
||||
import type {
|
||||
AcceptableValue,
|
||||
ArrayOrNested,
|
||||
GetItemKeys,
|
||||
GetModelValue,
|
||||
GetModelValueEmits,
|
||||
NestedItem,
|
||||
PartialString,
|
||||
EmitsToProps
|
||||
} from '../types/utils'
|
||||
import type { AcceptableValue, ArrayOrNested, GetItemKeys, GetModelValue, GetModelValueEmits, NestedItem, EmitsToProps, ComponentConfig } from '../types/utils'
|
||||
|
||||
const appConfigInputMenu = _appConfig as AppConfig & { ui: { inputMenu: Partial<typeof theme> } }
|
||||
|
||||
const inputMenu = tv({ extend: tv(theme), ...(appConfigInputMenu.ui?.inputMenu || {}) })
|
||||
type InputMenu = ComponentConfig<typeof theme, AppConfig, 'inputMenu'>
|
||||
|
||||
interface _InputMenuItem {
|
||||
label?: string
|
||||
@@ -42,8 +28,6 @@ interface _InputMenuItem {
|
||||
}
|
||||
export type InputMenuItem = _InputMenuItem | AcceptableValue | boolean
|
||||
|
||||
type InputMenuVariants = VariantProps<typeof inputMenu>
|
||||
|
||||
export interface InputMenuProps<T extends ArrayOrNested<InputMenuItem> = ArrayOrNested<InputMenuItem>, VK extends GetItemKeys<T> | undefined = undefined, M extends boolean = false> extends Pick<ComboboxRootProps<T>, 'open' | 'defaultOpen' | 'disabled' | 'name' | 'resetSearchTermOnBlur' | 'resetSearchTermOnSelect' | 'highlightOnHover'>, UseComponentIconsProps {
|
||||
/**
|
||||
* The element or component this component should render as.
|
||||
@@ -57,15 +41,15 @@ export interface InputMenuProps<T extends ArrayOrNested<InputMenuItem> = ArrayOr
|
||||
/**
|
||||
* @defaultValue 'primary'
|
||||
*/
|
||||
color?: InputMenuVariants['color']
|
||||
color?: InputMenu['variants']['color']
|
||||
/**
|
||||
* @defaultValue 'outline'
|
||||
*/
|
||||
variant?: InputMenuVariants['variant']
|
||||
variant?: InputMenu['variants']['variant']
|
||||
/**
|
||||
* @defaultValue 'md'
|
||||
*/
|
||||
size?: InputMenuVariants['size']
|
||||
size?: InputMenu['variants']['size']
|
||||
required?: boolean
|
||||
autofocus?: boolean
|
||||
autofocusDelay?: number
|
||||
@@ -138,7 +122,7 @@ export interface InputMenuProps<T extends ArrayOrNested<InputMenuItem> = ArrayOr
|
||||
*/
|
||||
ignoreFilter?: boolean
|
||||
class?: any
|
||||
ui?: PartialString<typeof inputMenu.slots>
|
||||
ui?: InputMenu['slots']
|
||||
}
|
||||
|
||||
export type InputMenuEmits<A extends ArrayOrNested<InputMenuItem>, VK extends GetItemKeys<A> | undefined, M extends boolean> = Pick<ComboboxRootEmits, 'update:open'> & {
|
||||
@@ -161,8 +145,16 @@ export interface InputMenuSlots<
|
||||
M extends boolean = false,
|
||||
T extends NestedItem<A> = NestedItem<A>
|
||||
> {
|
||||
'leading'(props: { modelValue?: GetModelValue<A, VK, M>, open: boolean, ui: ReturnType<typeof inputMenu> }): any
|
||||
'trailing'(props: { modelValue?: GetModelValue<A, VK, M>, open: boolean, ui: ReturnType<typeof inputMenu> }): any
|
||||
'leading'(props: {
|
||||
modelValue?: GetModelValue<A, VK, M>
|
||||
open: boolean
|
||||
ui: { [K in keyof Required<InputMenu['slots']>]: (props?: Record<string, any>) => string }
|
||||
}): any
|
||||
'trailing'(props: {
|
||||
modelValue?: GetModelValue<A, VK, M>
|
||||
open: boolean
|
||||
ui: { [K in keyof Required<InputMenu['slots']>]: (props?: Record<string, any>) => string }
|
||||
}): any
|
||||
'empty'(props: { searchTerm?: string }): any
|
||||
'item': SlotProps<T>
|
||||
'item-leading': SlotProps<T>
|
||||
@@ -186,6 +178,7 @@ import { useComponentIcons } from '../composables/useComponentIcons'
|
||||
import { useFormField } from '../composables/useFormField'
|
||||
import { useLocale } from '../composables/useLocale'
|
||||
import { compare, get, isArrayOfArray } from '../utils'
|
||||
import { tv } from '../utils/tv'
|
||||
import UIcon from './Icon.vue'
|
||||
import UAvatar from './Avatar.vue'
|
||||
import UChip from './Chip.vue'
|
||||
@@ -206,7 +199,7 @@ const slots = defineSlots<InputMenuSlots<T, VK, M>>()
|
||||
const searchTerm = defineModel<string>('searchTerm', { default: '' })
|
||||
|
||||
const { t } = useLocale()
|
||||
const appConfig = useAppConfig()
|
||||
const appConfig = useAppConfig() as InputMenu['AppConfig']
|
||||
const { contains } = useFilter({ sensitivity: 'base' })
|
||||
|
||||
const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'modelValue', 'defaultValue', 'open', 'defaultOpen', 'required', 'multiple', 'resetSearchTermOnBlur', 'resetSearchTermOnSelect', 'highlightOnHover', 'ignoreFilter'), emits)
|
||||
@@ -221,7 +214,7 @@ const inputSize = computed(() => buttonGroupSize.value || formGroupSize.value)
|
||||
|
||||
const [DefineCreateItemTemplate, ReuseCreateItemTemplate] = createReusableTemplate()
|
||||
|
||||
const ui = computed(() => inputMenu({
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.inputMenu || {}) })({
|
||||
color: color.value,
|
||||
variant: props.variant,
|
||||
size: inputSize?.value,
|
||||
|
||||
@@ -1,18 +1,11 @@
|
||||
<script lang="ts">
|
||||
import type { VariantProps } from 'tailwind-variants'
|
||||
import type { NumberFieldRootProps } from 'reka-ui'
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/input-number'
|
||||
import { tv } from '../utils/tv'
|
||||
import type { ButtonProps } from '../types'
|
||||
import type { PartialString } from '../types/utils'
|
||||
import type { ComponentConfig } from '../types/utils'
|
||||
|
||||
const appConfigInputNumber = _appConfig as AppConfig & { ui: { inputNumber: Partial<typeof theme> } }
|
||||
|
||||
const inputNumber = tv({ extend: tv(theme), ...(appConfigInputNumber.ui?.inputNumber || {}) })
|
||||
|
||||
type InputNumberVariants = VariantProps<typeof inputNumber>
|
||||
type InputNumber = ComponentConfig<typeof theme, AppConfig, 'inputNumber'>
|
||||
|
||||
export interface InputNumberProps extends Pick<NumberFieldRootProps, 'modelValue' | 'defaultValue' | 'min' | 'max' | 'step' | 'stepSnapping' | 'disabled' | 'required' | 'id' | 'name' | 'formatOptions' | 'disableWheelChange'> {
|
||||
/**
|
||||
@@ -22,9 +15,9 @@ export interface InputNumberProps extends Pick<NumberFieldRootProps, 'modelValue
|
||||
as?: any
|
||||
/** The placeholder text when the input is empty. */
|
||||
placeholder?: string
|
||||
color?: InputNumberVariants['color']
|
||||
variant?: InputNumberVariants['variant']
|
||||
size?: InputNumberVariants['size']
|
||||
color?: InputNumber['variants']['color']
|
||||
variant?: InputNumber['variants']['variant']
|
||||
size?: InputNumber['variants']['size']
|
||||
/** Highlight the ring color like a focus state. */
|
||||
highlight?: boolean
|
||||
/**
|
||||
@@ -62,7 +55,7 @@ export interface InputNumberProps extends Pick<NumberFieldRootProps, 'modelValue
|
||||
*/
|
||||
locale?: string
|
||||
class?: any
|
||||
ui?: PartialString<typeof inputNumber.slots>
|
||||
ui?: InputNumber['slots']
|
||||
}
|
||||
|
||||
export interface InputNumberEmits {
|
||||
@@ -84,6 +77,7 @@ import { reactivePick } from '@vueuse/core'
|
||||
import { useAppConfig } from '#imports'
|
||||
import { useFormField } from '../composables/useFormField'
|
||||
import { useLocale } from '../composables/useLocale'
|
||||
import { tv } from '../utils/tv'
|
||||
import UButton from './Button.vue'
|
||||
|
||||
defineOptions({ inheritAttrs: false })
|
||||
@@ -94,15 +88,16 @@ const props = withDefaults(defineProps<InputNumberProps>(), {
|
||||
const emits = defineEmits<InputNumberEmits>()
|
||||
defineSlots<InputNumberSlots>()
|
||||
|
||||
const appConfig = useAppConfig() as InputNumber['AppConfig']
|
||||
|
||||
const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'modelValue', 'defaultValue', 'min', 'max', 'step', 'stepSnapping', 'formatOptions', 'disableWheelChange'), emits)
|
||||
|
||||
const appConfig = useAppConfig()
|
||||
const { emitFormBlur, emitFormFocus, emitFormChange, emitFormInput, id, color, size, name, highlight, disabled, ariaAttrs } = useFormField<InputNumberProps>(props)
|
||||
|
||||
const { t, code: codeLocale } = useLocale()
|
||||
const locale = computed(() => props.locale || codeLocale.value)
|
||||
|
||||
const ui = computed(() => inputNumber({
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.inputNumber || {}) })({
|
||||
color: color.value,
|
||||
variant: props.variant,
|
||||
size: size.value,
|
||||
|
||||
@@ -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/kbd'
|
||||
import type { KbdKey } from '../composables/useKbd'
|
||||
import { tv } from '../utils/tv'
|
||||
import type { ComponentConfig } from '../types/utils'
|
||||
|
||||
const appConfigKbd = _appConfig as AppConfig & { ui: { kbd: Partial<typeof theme> } }
|
||||
|
||||
const kbd = tv({ extend: tv(theme), ...(appConfigKbd.ui?.kbd || {}) })
|
||||
|
||||
type KbdVariants = VariantProps<typeof kbd>
|
||||
type Kbd = ComponentConfig<typeof theme, AppConfig, 'kbd'>
|
||||
|
||||
export interface KbdProps {
|
||||
/**
|
||||
@@ -22,11 +16,11 @@ export interface KbdProps {
|
||||
/**
|
||||
* @defaultValue 'outline'
|
||||
*/
|
||||
variant?: KbdVariants['variant']
|
||||
variant?: Kbd['variants']['variant']
|
||||
/**
|
||||
* @defaultValue 'md'
|
||||
*/
|
||||
size?: KbdVariants['size']
|
||||
size?: Kbd['variants']['size']
|
||||
class?: any
|
||||
}
|
||||
|
||||
@@ -36,8 +30,11 @@ export interface KbdSlots {
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { Primitive } from 'reka-ui'
|
||||
import { useAppConfig } from '#imports'
|
||||
import { useKbd } from '../composables/useKbd'
|
||||
import { tv } from '../utils/tv'
|
||||
|
||||
const props = withDefaults(defineProps<KbdProps>(), {
|
||||
as: 'kbd'
|
||||
@@ -45,10 +42,13 @@ const props = withDefaults(defineProps<KbdProps>(), {
|
||||
defineSlots<KbdSlots>()
|
||||
|
||||
const { getKbdKey } = useKbd()
|
||||
const appConfig = useAppConfig() as Kbd['AppConfig']
|
||||
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.kbd || {}) }))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Primitive :as="as" :class="kbd({ variant, size, class: props.class })">
|
||||
<Primitive :as="as" :class="ui({ variant, size, class: props.class })">
|
||||
<slot>
|
||||
{{ getKbdKey(value) }}
|
||||
</slot>
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
<script lang="ts">
|
||||
import type { ButtonHTMLAttributes } from 'vue'
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import _appConfig from '#build/app.config'
|
||||
import type { RouterLinkProps, RouteLocationRaw } from 'vue-router'
|
||||
import theme from '#build/ui/link'
|
||||
import { tv } from '../utils/tv'
|
||||
import type { ComponentConfig } from '../types/utils'
|
||||
|
||||
type Link = ComponentConfig<typeof theme, AppConfig, 'link'>
|
||||
|
||||
interface NuxtLinkProps extends Omit<RouterLinkProps, 'to'> {
|
||||
/**
|
||||
@@ -52,10 +53,6 @@ interface NuxtLinkProps extends Omit<RouterLinkProps, 'to'> {
|
||||
noPrefetch?: boolean
|
||||
}
|
||||
|
||||
const appConfigLink = _appConfig as AppConfig & { ui: { link: Partial<typeof theme> } }
|
||||
|
||||
const link = tv({ extend: tv(theme), ...(appConfigLink.ui?.link || {}) })
|
||||
|
||||
export interface LinkProps extends NuxtLinkProps {
|
||||
/**
|
||||
* The element or component this component should render as when not a link.
|
||||
@@ -91,10 +88,12 @@ export interface LinkSlots {
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { defu } from 'defu'
|
||||
import { isEqual, diff } from 'ohash/utils'
|
||||
import { useForwardProps } from 'reka-ui'
|
||||
import { reactiveOmit } from '@vueuse/core'
|
||||
import { useRoute } from '#imports'
|
||||
import { useRoute, useAppConfig } from '#imports'
|
||||
import { tv } from '../utils/tv'
|
||||
import ULinkBase from './LinkBase.vue'
|
||||
|
||||
defineOptions({ inheritAttrs: false })
|
||||
@@ -110,16 +109,20 @@ const props = withDefaults(defineProps<LinkProps>(), {
|
||||
defineSlots<LinkSlots>()
|
||||
|
||||
const route = useRoute()
|
||||
const appConfig = useAppConfig() as Link['AppConfig']
|
||||
|
||||
const nuxtLinkProps = useForwardProps(reactiveOmit(props, 'as', 'type', 'disabled', 'active', 'exact', 'exactQuery', 'exactHash', 'activeClass', 'inactiveClass', 'raw', 'class'))
|
||||
|
||||
const ui = computed(() => tv({
|
||||
extend: link,
|
||||
variants: {
|
||||
active: {
|
||||
true: props.activeClass,
|
||||
false: props.inactiveClass
|
||||
extend: tv(theme),
|
||||
...defu({
|
||||
variants: {
|
||||
active: {
|
||||
true: props.activeClass,
|
||||
false: props.inactiveClass
|
||||
}
|
||||
}
|
||||
}
|
||||
}, appConfig.ui?.link || {})
|
||||
}))
|
||||
|
||||
function isPartiallyEqual(item1: any, item2: any) {
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
<script lang="ts">
|
||||
import type { DialogRootProps, DialogRootEmits, DialogContentProps, DialogContentEmits } from 'reka-ui'
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/modal'
|
||||
import { tv } from '../utils/tv'
|
||||
import type { ButtonProps } from '../types'
|
||||
import type { EmitsToProps } from '../types/utils'
|
||||
import type { EmitsToProps, ComponentConfig } from '../types/utils'
|
||||
|
||||
const appConfigModal = _appConfig as AppConfig & { ui: { modal: Partial<typeof theme> } }
|
||||
|
||||
const modal = tv({ extend: tv(theme), ...(appConfigModal.ui?.modal || {}) })
|
||||
type Modal = ComponentConfig<typeof theme, AppConfig, 'modal'>
|
||||
|
||||
export interface ModalProps extends DialogRootProps {
|
||||
title?: string
|
||||
@@ -54,7 +50,7 @@ export interface ModalProps extends DialogRootProps {
|
||||
*/
|
||||
dismissible?: boolean
|
||||
class?: any
|
||||
ui?: Partial<typeof modal.slots>
|
||||
ui?: Modal['slots']
|
||||
}
|
||||
|
||||
export interface ModalEmits extends DialogRootEmits {
|
||||
@@ -67,7 +63,7 @@ export interface ModalSlots {
|
||||
header(props?: {}): any
|
||||
title(props?: {}): any
|
||||
description(props?: {}): any
|
||||
close(props: { ui: ReturnType<typeof modal> }): any
|
||||
close(props: { ui: { [K in keyof Required<Modal['slots']>]: (props?: Record<string, any>) => string } }): any
|
||||
body(props?: {}): any
|
||||
footer(props?: {}): any
|
||||
}
|
||||
@@ -79,6 +75,7 @@ import { DialogRoot, DialogTrigger, DialogPortal, DialogOverlay, DialogContent,
|
||||
import { reactivePick } 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<ModalProps>(), {
|
||||
@@ -93,7 +90,7 @@ const emits = defineEmits<ModalEmits>()
|
||||
const slots = defineSlots<ModalSlots>()
|
||||
|
||||
const { t } = useLocale()
|
||||
const appConfig = useAppConfig()
|
||||
const appConfig = useAppConfig() as Modal['AppConfig']
|
||||
|
||||
const rootProps = useForwardPropsEmits(reactivePick(props, 'open', 'defaultOpen', 'modal'), emits)
|
||||
const contentProps = toRef(() => props.content)
|
||||
@@ -114,7 +111,7 @@ const contentEvents = computed(() => {
|
||||
return events
|
||||
})
|
||||
|
||||
const ui = computed(() => modal({
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.modal || {}) })({
|
||||
transition: props.transition,
|
||||
fullscreen: props.fullscreen
|
||||
}))
|
||||
|
||||
@@ -1,24 +1,12 @@
|
||||
<!-- eslint-disable vue/block-tag-newline -->
|
||||
<script lang="ts">
|
||||
import type { VariantProps } from 'tailwind-variants'
|
||||
import type { NavigationMenuRootProps, NavigationMenuRootEmits, NavigationMenuContentProps, NavigationMenuContentEmits, CollapsibleRootProps } from 'reka-ui'
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/navigation-menu'
|
||||
import { tv } from '../utils/tv'
|
||||
import type { AvatarProps, BadgeProps, LinkProps } from '../types'
|
||||
import type {
|
||||
ArrayOrNested,
|
||||
DynamicSlots,
|
||||
MergeTypes,
|
||||
NestedItem,
|
||||
PartialString,
|
||||
EmitsToProps
|
||||
} from '../types/utils'
|
||||
import type { ArrayOrNested, DynamicSlots, MergeTypes, NestedItem, EmitsToProps, ComponentConfig } from '../types/utils'
|
||||
|
||||
const appConfigNavigationMenu = _appConfig as AppConfig & { ui: { navigationMenu: Partial<typeof theme> } }
|
||||
|
||||
const navigationMenu = tv({ extend: tv(theme), ...(appConfigNavigationMenu.ui?.navigationMenu || {}) })
|
||||
type NavigationMenu = ComponentConfig<typeof theme, AppConfig, 'navigationMenu'>
|
||||
|
||||
export interface NavigationMenuChildItem extends Omit<NavigationMenuItem, 'type'> {
|
||||
/** Description is only used when `orientation` is `horizontal`. */
|
||||
@@ -55,8 +43,6 @@ export interface NavigationMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'cu
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
type NavigationMenuVariants = VariantProps<typeof navigationMenu>
|
||||
|
||||
export interface NavigationMenuProps<T extends ArrayOrNested<NavigationMenuItem> = ArrayOrNested<NavigationMenuItem>> extends Pick<NavigationMenuRootProps, 'modelValue' | 'defaultValue' | 'delayDuration' | 'disableClickTrigger' | 'disableHoverTrigger' | 'skipDelayDuration' | 'disablePointerLeaveClose' | 'unmountOnHide'> {
|
||||
/**
|
||||
* The element or component this component should render as.
|
||||
@@ -80,11 +66,11 @@ export interface NavigationMenuProps<T extends ArrayOrNested<NavigationMenuItem>
|
||||
/**
|
||||
* @defaultValue 'primary'
|
||||
*/
|
||||
color?: NavigationMenuVariants['color']
|
||||
color?: NavigationMenu['variants']['color']
|
||||
/**
|
||||
* @defaultValue 'pill'
|
||||
*/
|
||||
variant?: NavigationMenuVariants['variant']
|
||||
variant?: NavigationMenu['variants']['variant']
|
||||
/**
|
||||
* The orientation of the menu.
|
||||
* @defaultValue 'horizontal'
|
||||
@@ -101,7 +87,7 @@ export interface NavigationMenuProps<T extends ArrayOrNested<NavigationMenuItem>
|
||||
/**
|
||||
* @defaultValue 'primary'
|
||||
*/
|
||||
highlightColor?: NavigationMenuVariants['highlightColor']
|
||||
highlightColor?: NavigationMenu['variants']['highlightColor']
|
||||
/** The content of the menu. */
|
||||
content?: Omit<NavigationMenuContentProps, 'as' | 'asChild' | 'forceMount'> & Partial<EmitsToProps<NavigationMenuContentEmits>>
|
||||
/**
|
||||
@@ -109,7 +95,7 @@ export interface NavigationMenuProps<T extends ArrayOrNested<NavigationMenuItem>
|
||||
* Only works when `orientation` is `horizontal`.
|
||||
* @defaultValue 'horizontal'
|
||||
*/
|
||||
contentOrientation?: NavigationMenuVariants['contentOrientation']
|
||||
contentOrientation?: NavigationMenu['variants']['contentOrientation']
|
||||
/**
|
||||
* Display an arrow alongside the menu.
|
||||
* @defaultValue false
|
||||
@@ -121,7 +107,7 @@ export interface NavigationMenuProps<T extends ArrayOrNested<NavigationMenuItem>
|
||||
*/
|
||||
labelKey?: keyof NestedItem<T>
|
||||
class?: any
|
||||
ui?: PartialString<typeof navigationMenu.slots>
|
||||
ui?: NavigationMenu['slots']
|
||||
}
|
||||
|
||||
export interface NavigationMenuEmits extends NavigationMenuRootEmits {}
|
||||
@@ -147,6 +133,7 @@ import { NavigationMenuRoot, NavigationMenuList, NavigationMenuItem, NavigationM
|
||||
import { createReusableTemplate } from '@vueuse/core'
|
||||
import { useAppConfig } from '#imports'
|
||||
import { get, isArrayOfArray } from '../utils'
|
||||
import { tv } from '../utils/tv'
|
||||
import { pickLinkProps } from '../utils/link'
|
||||
import ULinkBase from './LinkBase.vue'
|
||||
import ULink from './Link.vue'
|
||||
@@ -166,6 +153,8 @@ const props = withDefaults(defineProps<NavigationMenuProps<T>>(), {
|
||||
const emits = defineEmits<NavigationMenuEmits>()
|
||||
const slots = defineSlots<NavigationMenuSlots<T>>()
|
||||
|
||||
const appConfig = useAppConfig() as NavigationMenu['AppConfig']
|
||||
|
||||
const rootProps = useForwardPropsEmits(computed(() => ({
|
||||
as: props.as,
|
||||
modelValue: props.modelValue,
|
||||
@@ -178,11 +167,8 @@ const rootProps = useForwardPropsEmits(computed(() => ({
|
||||
disablePointerLeaveClose: props.disablePointerLeaveClose,
|
||||
unmountOnHide: props.unmountOnHide
|
||||
})), emits)
|
||||
|
||||
const contentProps = toRef(() => props.content)
|
||||
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const [DefineLinkTemplate, ReuseLinkTemplate] = createReusableTemplate<{ item: NavigationMenuItem, index: number, active?: boolean }>()
|
||||
const [DefineItemTemplate, ReuseItemTemplate] = createReusableTemplate<{ item: NavigationMenuItem, index: number, level?: number }>({
|
||||
props: {
|
||||
@@ -192,7 +178,7 @@ const [DefineItemTemplate, ReuseItemTemplate] = createReusableTemplate<{ item: N
|
||||
}
|
||||
})
|
||||
|
||||
const ui = computed(() => navigationMenu({
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.navigationMenu || {}) })({
|
||||
orientation: props.orientation,
|
||||
contentOrientation: props.contentOrientation,
|
||||
collapsed: props.collapsed,
|
||||
|
||||
@@ -22,7 +22,7 @@ const onClose = (id: symbol, value: any) => {
|
||||
v-for="overlay in mountedOverlays"
|
||||
:key="overlay.id"
|
||||
v-bind="overlay.props"
|
||||
v-model:open="overlay.modelValue"
|
||||
v-model:open="overlay.isOpen"
|
||||
@close="(value:any) => onClose(overlay.id, value)"
|
||||
@after:leave="onAfterLeave(overlay.id)"
|
||||
/>
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
<script lang="ts">
|
||||
import type { PaginationRootProps, PaginationRootEmits } from 'reka-ui'
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/pagination'
|
||||
import { tv } from '../utils/tv'
|
||||
import type { ButtonProps } from '../types'
|
||||
import type { ComponentConfig } from '../types/utils'
|
||||
|
||||
const appConfigPagination = _appConfig as AppConfig & { ui: { pagination: Partial<typeof theme> } }
|
||||
|
||||
const pagination = tv({ extend: tv(theme), ...(appConfigPagination.ui?.pagination || {}) })
|
||||
type Pagination = ComponentConfig<typeof theme, AppConfig, 'pagination'>
|
||||
|
||||
export interface PaginationProps extends Partial<Pick<PaginationRootProps, 'defaultPage' | 'disabled' | 'itemsPerPage' | 'page' | 'showEdges' | 'siblingCount' | 'total'>> {
|
||||
/**
|
||||
@@ -79,7 +76,7 @@ export interface PaginationProps extends Partial<Pick<PaginationRootProps, 'defa
|
||||
*/
|
||||
to?: (page: number) => ButtonProps['to']
|
||||
class?: any
|
||||
ui?: Partial<typeof pagination.slots>
|
||||
ui?: Pagination['slots']
|
||||
}
|
||||
|
||||
export interface PaginationEmits extends PaginationRootEmits {}
|
||||
@@ -110,6 +107,7 @@ import { PaginationRoot, PaginationList, PaginationListItem, PaginationFirst, Pa
|
||||
import { reactivePick } 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<PaginationProps>(), {
|
||||
@@ -127,8 +125,9 @@ const props = withDefaults(defineProps<PaginationProps>(), {
|
||||
const emits = defineEmits<PaginationEmits>()
|
||||
const slots = defineSlots<PaginationSlots>()
|
||||
|
||||
const appConfig = useAppConfig()
|
||||
const { dir } = useLocale()
|
||||
const appConfig = useAppConfig() as Pagination['AppConfig']
|
||||
|
||||
const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'defaultPage', 'disabled', 'itemsPerPage', 'page', 'showEdges', 'siblingCount', 'total'), emits)
|
||||
|
||||
const firstIcon = computed(() => props.firstIcon || (dir.value === 'rtl' ? appConfig.ui.icons.chevronDoubleRight : appConfig.ui.icons.chevronDoubleLeft))
|
||||
@@ -137,7 +136,7 @@ const nextIcon = computed(() => props.nextIcon || (dir.value === 'rtl' ? appConf
|
||||
const lastIcon = computed(() => props.lastIcon || (dir.value === 'rtl' ? appConfig.ui.icons.chevronDoubleLeft : appConfig.ui.icons.chevronDoubleRight))
|
||||
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
const ui = pagination()
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.pagination || {}) })())
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,18 +1,11 @@
|
||||
<!-- eslint-disable vue/block-tag-newline -->
|
||||
<script lang="ts">
|
||||
import type { VariantProps } from 'tailwind-variants'
|
||||
import type { PinInputRootEmits, PinInputRootProps } from 'reka-ui'
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/pin-input'
|
||||
import { tv } from '../utils/tv'
|
||||
import type { PartialString } from '../types/utils'
|
||||
import type { ComponentConfig } from '../types/utils'
|
||||
|
||||
const appConfigPinInput = _appConfig as AppConfig & { ui: { pinInput: Partial<typeof theme> } }
|
||||
|
||||
const pinInput = tv({ extend: tv(theme), ...(appConfigPinInput.ui?.pinInput || {}) })
|
||||
|
||||
type PinInputVariants = VariantProps<typeof pinInput>
|
||||
type PinInput = ComponentConfig<typeof theme, AppConfig, 'pinInput'>
|
||||
|
||||
export interface PinInputProps extends Pick<PinInputRootProps, 'defaultValue' | 'disabled' | 'id' | 'mask' | 'modelValue' | 'name' | 'otp' | 'placeholder' | 'required' | 'type'> {
|
||||
/**
|
||||
@@ -23,15 +16,15 @@ export interface PinInputProps extends Pick<PinInputRootProps, 'defaultValue' |
|
||||
/**
|
||||
* @defaultValue 'primary'
|
||||
*/
|
||||
color?: PinInputVariants['color']
|
||||
color?: PinInput['variants']['color']
|
||||
/**
|
||||
* @defaultValue 'outline'
|
||||
*/
|
||||
variant?: PinInputVariants['variant']
|
||||
variant?: PinInput['variants']['variant']
|
||||
/**
|
||||
* @defaultValue 'md'
|
||||
*/
|
||||
size?: PinInputVariants['size']
|
||||
size?: PinInput['variants']['size']
|
||||
/**
|
||||
* The number of input fields.
|
||||
* @defaultValue 5
|
||||
@@ -41,7 +34,7 @@ export interface PinInputProps extends Pick<PinInputRootProps, 'defaultValue' |
|
||||
autofocusDelay?: number
|
||||
highlight?: boolean
|
||||
class?: any
|
||||
ui?: PartialString<typeof pinInput.slots>
|
||||
ui?: PinInput['slots']
|
||||
}
|
||||
|
||||
export type PinInputEmits = PinInputRootEmits & {
|
||||
@@ -56,8 +49,10 @@ import type { ComponentPublicInstance } from 'vue'
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { PinInputInput, PinInputRoot, useForwardPropsEmits } from 'reka-ui'
|
||||
import { reactivePick } from '@vueuse/core'
|
||||
import { useAppConfig } from '#imports'
|
||||
import { useFormField } from '../composables/useFormField'
|
||||
import { looseToNumber } from '../utils'
|
||||
import { tv } from '../utils/tv'
|
||||
|
||||
const props = withDefaults(defineProps<PinInputProps>(), {
|
||||
type: 'text',
|
||||
@@ -66,10 +61,13 @@ const props = withDefaults(defineProps<PinInputProps>(), {
|
||||
})
|
||||
const emits = defineEmits<PinInputEmits>()
|
||||
|
||||
const appConfig = useAppConfig() as PinInput['AppConfig']
|
||||
|
||||
const rootProps = useForwardPropsEmits(reactivePick(props, 'defaultValue', 'disabled', 'id', 'mask', 'modelValue', 'name', 'otp', 'placeholder', 'required', 'type'), emits)
|
||||
|
||||
const { emitFormInput, emitFormFocus, emitFormChange, emitFormBlur, size, color, id, name, highlight, disabled, ariaAttrs } = useFormField<PinInputProps>(props)
|
||||
|
||||
const ui = computed(() => pinInput({
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.pinInput || {}) })({
|
||||
color: color.value,
|
||||
variant: props.variant,
|
||||
size: size.value,
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
<script lang="ts">
|
||||
import type { PopoverRootProps, HoverCardRootProps, PopoverRootEmits, PopoverContentProps, PopoverContentEmits, PopoverArrowProps } from 'reka-ui'
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/popover'
|
||||
import { tv } from '../utils/tv'
|
||||
import type { EmitsToProps } from '../types/utils'
|
||||
import type { EmitsToProps, ComponentConfig } from '../types/utils'
|
||||
|
||||
const appConfigPopover = _appConfig as AppConfig & { ui: { popover: Partial<typeof theme> } }
|
||||
|
||||
const popover = tv({ extend: tv(theme), ...(appConfigPopover.ui?.popover || {}) })
|
||||
type Popover = ComponentConfig<typeof theme, AppConfig, 'popover'>
|
||||
|
||||
export interface PopoverProps extends PopoverRootProps, Pick<HoverCardRootProps, 'openDelay' | 'closeDelay'> {
|
||||
/**
|
||||
@@ -37,7 +33,7 @@ export interface PopoverProps extends PopoverRootProps, Pick<HoverCardRootProps,
|
||||
*/
|
||||
dismissible?: boolean
|
||||
class?: any
|
||||
ui?: Partial<typeof popover.slots>
|
||||
ui?: Popover['slots']
|
||||
}
|
||||
|
||||
export interface PopoverEmits extends PopoverRootEmits {}
|
||||
@@ -54,6 +50,8 @@ import { defu } from 'defu'
|
||||
import { useForwardPropsEmits } from 'reka-ui'
|
||||
import { Popover, HoverCard } from 'reka-ui/namespaced'
|
||||
import { reactivePick } from '@vueuse/core'
|
||||
import { useAppConfig } from '#imports'
|
||||
import { tv } from '../utils/tv'
|
||||
|
||||
const props = withDefaults(defineProps<PopoverProps>(), {
|
||||
portal: true,
|
||||
@@ -65,6 +63,8 @@ const props = withDefaults(defineProps<PopoverProps>(), {
|
||||
const emits = defineEmits<PopoverEmits>()
|
||||
const slots = defineSlots<PopoverSlots>()
|
||||
|
||||
const appConfig = useAppConfig() as Popover['AppConfig']
|
||||
|
||||
const pick = props.mode === 'hover' ? reactivePick(props, 'defaultOpen', 'open', 'openDelay', 'closeDelay') : reactivePick(props, 'defaultOpen', 'open', 'modal')
|
||||
const rootProps = useForwardPropsEmits(pick, emits)
|
||||
const contentProps = toRef(() => defu(props.content, { side: 'bottom', sideOffset: 8, collisionPadding: 8 }) as PopoverContentProps)
|
||||
@@ -82,7 +82,7 @@ const contentEvents = computed(() => {
|
||||
const arrowProps = toRef(() => props.arrow as PopoverArrowProps)
|
||||
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
const ui = computed(() => popover({
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.popover || {}) })({
|
||||
side: contentProps.value.side
|
||||
}))
|
||||
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
<!-- eslint-disable vue/block-tag-newline -->
|
||||
<script lang="ts">
|
||||
import type { VariantProps } from 'tailwind-variants'
|
||||
import type { ProgressRootProps, ProgressRootEmits } from 'reka-ui'
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/progress'
|
||||
import { tv } from '../utils/tv'
|
||||
import type { ComponentConfig } from '../types/utils'
|
||||
|
||||
const appConfigProgress = _appConfig as AppConfig & { ui: { progress: Partial<typeof theme> } }
|
||||
|
||||
const progress = tv({ extend: tv(theme), ...(appConfigProgress.ui?.progress || {}) })
|
||||
|
||||
type ProgressVariants = VariantProps<typeof progress>
|
||||
type Progress = ComponentConfig<typeof theme, AppConfig, 'progress'>
|
||||
|
||||
export interface ProgressProps extends Pick<ProgressRootProps, 'getValueLabel' | 'modelValue'> {
|
||||
/**
|
||||
@@ -28,23 +22,23 @@ export interface ProgressProps extends Pick<ProgressRootProps, 'getValueLabel' |
|
||||
/**
|
||||
* @defaultValue 'md'
|
||||
*/
|
||||
size?: ProgressVariants['size']
|
||||
size?: Progress['variants']['size']
|
||||
/**
|
||||
* @defaultValue 'primary'
|
||||
*/
|
||||
color?: ProgressVariants['color']
|
||||
color?: Progress['variants']['color']
|
||||
/**
|
||||
* The orientation of the progress bar.
|
||||
* @defaultValue 'horizontal'
|
||||
*/
|
||||
orientation?: ProgressVariants['orientation']
|
||||
orientation?: Progress['variants']['orientation']
|
||||
/**
|
||||
* The animation of the progress bar.
|
||||
* @defaultValue 'carousel'
|
||||
*/
|
||||
animation?: ProgressVariants['animation']
|
||||
animation?: Progress['variants']['animation']
|
||||
class?: any
|
||||
ui?: Partial<typeof progress.slots>
|
||||
ui?: Progress['slots']
|
||||
}
|
||||
|
||||
export interface ProgressEmits extends ProgressRootEmits {}
|
||||
@@ -61,7 +55,9 @@ export type ProgressSlots = {
|
||||
import { computed } from 'vue'
|
||||
import { Primitive, ProgressRoot, ProgressIndicator, useForwardPropsEmits } from 'reka-ui'
|
||||
import { reactivePick } from '@vueuse/core'
|
||||
import { useAppConfig } from '#imports'
|
||||
import { useLocale } from '../composables/useLocale'
|
||||
import { tv } from '../utils/tv'
|
||||
|
||||
const props = withDefaults(defineProps<ProgressProps>(), {
|
||||
inverted: false,
|
||||
@@ -72,6 +68,7 @@ const emits = defineEmits<ProgressEmits>()
|
||||
const slots = defineSlots<ProgressSlots>()
|
||||
|
||||
const { dir } = useLocale()
|
||||
const appConfig = useAppConfig() as Progress['AppConfig']
|
||||
|
||||
const rootProps = useForwardPropsEmits(reactivePick(props, 'getValueLabel', 'modelValue'), emits)
|
||||
|
||||
@@ -160,7 +157,7 @@ function stepVariant(index: number | string) {
|
||||
return 'other'
|
||||
}
|
||||
|
||||
const ui = computed(() => progress({
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.progress || {}) })({
|
||||
animation: props.animation,
|
||||
size: props.size,
|
||||
color: props.color,
|
||||
|
||||
@@ -1,17 +1,10 @@
|
||||
<script lang="ts">
|
||||
import type { VariantProps } from 'tailwind-variants'
|
||||
import type { RadioGroupRootProps, RadioGroupRootEmits } from 'reka-ui'
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/radio-group'
|
||||
import { tv } from '../utils/tv'
|
||||
import type { AcceptableValue } from '../types/utils'
|
||||
import type { AcceptableValue, ComponentConfig } from '../types/utils'
|
||||
|
||||
const appConfigRadioGroup = _appConfig as AppConfig & { ui: { radioGroup: Partial<typeof theme> } }
|
||||
|
||||
const radioGroup = tv({ extend: tv(theme), ...(appConfigRadioGroup.ui?.radioGroup || {}) })
|
||||
|
||||
type RadioGroupVariants = VariantProps<typeof radioGroup>
|
||||
type RadioGroup = ComponentConfig<typeof theme, AppConfig, 'radioGroup'>
|
||||
|
||||
export type RadioGroupValue = AcceptableValue
|
||||
export type RadioGroupItem = {
|
||||
@@ -48,15 +41,15 @@ export interface RadioGroupProps<T extends RadioGroupItem = RadioGroupItem> exte
|
||||
/**
|
||||
* @defaultValue 'md'
|
||||
*/
|
||||
size?: RadioGroupVariants['size']
|
||||
size?: RadioGroup['variants']['size']
|
||||
/**
|
||||
* @defaultValue 'list'
|
||||
*/
|
||||
variant?: RadioGroupVariants['variant']
|
||||
variant?: RadioGroup['variants']['variant']
|
||||
/**
|
||||
* @defaultValue 'primary'
|
||||
*/
|
||||
color?: RadioGroupVariants['color']
|
||||
color?: RadioGroup['variants']['color']
|
||||
/**
|
||||
* The orientation the radio buttons are laid out.
|
||||
* @defaultValue 'vertical'
|
||||
@@ -66,9 +59,9 @@ export interface RadioGroupProps<T extends RadioGroupItem = RadioGroupItem> exte
|
||||
* Position of the indicator.
|
||||
* @defaultValue 'start'
|
||||
*/
|
||||
indicator?: RadioGroupVariants['indicator']
|
||||
indicator?: RadioGroup['variants']['indicator']
|
||||
class?: any
|
||||
ui?: Partial<typeof radioGroup.slots>
|
||||
ui?: RadioGroup['slots']
|
||||
}
|
||||
|
||||
export type RadioGroupEmits = RadioGroupRootEmits & {
|
||||
@@ -88,8 +81,10 @@ export interface RadioGroupSlots<T extends RadioGroupItem = RadioGroupItem> {
|
||||
import { computed, useId } from 'vue'
|
||||
import { RadioGroupRoot, RadioGroupItem, RadioGroupIndicator, Label, useForwardPropsEmits } from 'reka-ui'
|
||||
import { reactivePick } from '@vueuse/core'
|
||||
import { useAppConfig } from '#imports'
|
||||
import { useFormField } from '../composables/useFormField'
|
||||
import { get } from '../utils'
|
||||
import { tv } from '../utils/tv'
|
||||
|
||||
const props = withDefaults(defineProps<RadioGroupProps<T>>(), {
|
||||
valueKey: 'value',
|
||||
@@ -100,12 +95,14 @@ const props = withDefaults(defineProps<RadioGroupProps<T>>(), {
|
||||
const emits = defineEmits<RadioGroupEmits>()
|
||||
const slots = defineSlots<RadioGroupSlots<T>>()
|
||||
|
||||
const appConfig = useAppConfig() as RadioGroup['AppConfig']
|
||||
|
||||
const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'modelValue', 'defaultValue', 'orientation', 'loop', 'required'), emits)
|
||||
|
||||
const { emitFormChange, emitFormInput, color, name, size, id: _id, disabled, ariaAttrs } = useFormField<RadioGroupProps<T>>(props, { bind: false })
|
||||
const id = _id.value ?? useId()
|
||||
|
||||
const ui = computed(() => radioGroup({
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.radioGroup || {}) })({
|
||||
size: size.value,
|
||||
color: color.value,
|
||||
disabled: disabled.value,
|
||||
|
||||
@@ -1,27 +1,12 @@
|
||||
<script lang="ts">
|
||||
import type { VariantProps } from 'tailwind-variants'
|
||||
import type { SelectRootProps, SelectRootEmits, SelectContentProps, SelectContentEmits, SelectArrowProps } from 'reka-ui'
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/select'
|
||||
import type { UseComponentIconsProps } from '../composables/useComponentIcons'
|
||||
import { tv } from '../utils/tv'
|
||||
import type { AvatarProps, ChipProps, InputProps } from '../types'
|
||||
import type {
|
||||
AcceptableValue,
|
||||
ArrayOrNested,
|
||||
GetItemKeys,
|
||||
GetItemValue,
|
||||
GetModelValue,
|
||||
GetModelValueEmits,
|
||||
NestedItem,
|
||||
PartialString,
|
||||
EmitsToProps
|
||||
} from '../types/utils'
|
||||
import type { AcceptableValue, ArrayOrNested, GetItemKeys, GetItemValue, GetModelValue, GetModelValueEmits, NestedItem, EmitsToProps, ComponentConfig } from '../types/utils'
|
||||
|
||||
const appConfigSelect = _appConfig as AppConfig & { ui: { select: Partial<typeof theme> } }
|
||||
|
||||
const select = tv({ extend: tv(theme), ...(appConfigSelect.ui?.select || {}) })
|
||||
type Select = ComponentConfig<typeof theme, AppConfig, 'select'>
|
||||
|
||||
interface SelectItemBase {
|
||||
label?: string
|
||||
@@ -43,8 +28,6 @@ interface SelectItemBase {
|
||||
}
|
||||
export type SelectItem = SelectItemBase | AcceptableValue | boolean
|
||||
|
||||
type SelectVariants = VariantProps<typeof select>
|
||||
|
||||
export interface SelectProps<T extends ArrayOrNested<SelectItem> = ArrayOrNested<SelectItem>, VK extends GetItemKeys<T> = 'value', M extends boolean = false> extends Omit<SelectRootProps<T>, 'dir' | 'multiple' | 'modelValue' | 'defaultValue' | 'by'>, UseComponentIconsProps {
|
||||
id?: string
|
||||
/** The placeholder text when the select is empty. */
|
||||
@@ -52,15 +35,15 @@ export interface SelectProps<T extends ArrayOrNested<SelectItem> = ArrayOrNested
|
||||
/**
|
||||
* @defaultValue 'primary'
|
||||
*/
|
||||
color?: SelectVariants['color']
|
||||
color?: Select['variants']['color']
|
||||
/**
|
||||
* @defaultValue 'outline'
|
||||
*/
|
||||
variant?: SelectVariants['variant']
|
||||
variant?: Select['variants']['variant']
|
||||
/**
|
||||
* @defaultValue 'md'
|
||||
*/
|
||||
size?: SelectVariants['size']
|
||||
size?: Select['variants']['size']
|
||||
/**
|
||||
* The icon displayed to open the menu.
|
||||
* @defaultValue appConfig.ui.icons.chevronDown
|
||||
@@ -108,7 +91,7 @@ export interface SelectProps<T extends ArrayOrNested<SelectItem> = ArrayOrNested
|
||||
/** Highlight the ring color like a focus state. */
|
||||
highlight?: boolean
|
||||
class?: any
|
||||
ui?: PartialString<typeof select.slots>
|
||||
ui?: Select['slots']
|
||||
}
|
||||
|
||||
export type SelectEmits<A extends ArrayOrNested<SelectItem>, VK extends GetItemKeys<A> | undefined, M extends boolean> = Omit<SelectRootEmits, 'update:modelValue'> & {
|
||||
@@ -125,9 +108,20 @@ export interface SelectSlots<
|
||||
M extends boolean = false,
|
||||
T extends NestedItem<A> = NestedItem<A>
|
||||
> {
|
||||
'leading'(props: { modelValue?: GetModelValue<A, VK, M>, open: boolean, ui: ReturnType<typeof select> }): any
|
||||
'default'(props: { modelValue?: GetModelValue<A, VK, M>, open: boolean }): any
|
||||
'trailing'(props: { modelValue?: GetModelValue<A, VK, M>, open: boolean, ui: ReturnType<typeof select> }): any
|
||||
'leading'(props: {
|
||||
modelValue?: GetModelValue<A, VK, M>
|
||||
open: boolean
|
||||
ui: { [K in keyof Required<Select['slots']>]: (props?: Record<string, any>) => string }
|
||||
}): any
|
||||
'default'(props: {
|
||||
modelValue?: GetModelValue<A, VK, M>
|
||||
open: boolean
|
||||
}): any
|
||||
'trailing'(props: {
|
||||
modelValue?: GetModelValue<A, VK, M>
|
||||
open: boolean
|
||||
ui: { [K in keyof Required<Select['slots']>]: (props?: Record<string, any>) => string }
|
||||
}): any
|
||||
'item': SlotProps<T>
|
||||
'item-leading': SlotProps<T>
|
||||
'item-label': SlotProps<T>
|
||||
@@ -145,6 +139,7 @@ import { useButtonGroup } from '../composables/useButtonGroup'
|
||||
import { useComponentIcons } from '../composables/useComponentIcons'
|
||||
import { useFormField } from '../composables/useFormField'
|
||||
import { compare, get, isArrayOfArray } from '../utils'
|
||||
import { tv } from '../utils/tv'
|
||||
import UIcon from './Icon.vue'
|
||||
import UAvatar from './Avatar.vue'
|
||||
import UChip from './Chip.vue'
|
||||
@@ -159,7 +154,8 @@ const props = withDefaults(defineProps<SelectProps<T, VK, M>>(), {
|
||||
const emits = defineEmits<SelectEmits<T, VK, M>>()
|
||||
const slots = defineSlots<SelectSlots<T, VK, M>>()
|
||||
|
||||
const appConfig = useAppConfig()
|
||||
const appConfig = useAppConfig() as Select['AppConfig']
|
||||
|
||||
const rootProps = useForwardPropsEmits(reactivePick(props, 'open', 'defaultOpen', 'disabled', 'autocomplete', 'required', 'multiple'), emits)
|
||||
const contentProps = toRef(() => defu(props.content, { side: 'bottom', sideOffset: 8, collisionPadding: 8, position: 'popper' }) as SelectContentProps)
|
||||
const arrowProps = toRef(() => props.arrow as SelectArrowProps)
|
||||
@@ -170,7 +166,7 @@ const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponen
|
||||
|
||||
const selectSize = computed(() => buttonGroupSize.value || formGroupSize.value)
|
||||
|
||||
const ui = computed(() => select({
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.select || {}) })({
|
||||
color: color.value,
|
||||
variant: props.variant,
|
||||
size: selectSize?.value,
|
||||
|
||||