fix(components): ui prop override with class (#136)

This commit is contained in:
Benjamin Canac
2024-07-03 14:35:58 +02:00
committed by GitHub
parent 5a22c62b2a
commit 235556d3e0
50 changed files with 383 additions and 375 deletions

View File

@@ -1,6 +1,6 @@
const useContentExamplesCodeState = () => useState('content-examples-code', () => ({})) const useContentExamplesCodeState = () => useState('content-examples-code', () => ({}))
export async function fetchContentExampleCode (name?: string) { export async function fetchContentExampleCode(name?: string) {
if (!name) return if (!name) return
const state = useContentExamplesCodeState() const state = useContentExamplesCodeState()
@@ -8,7 +8,9 @@ export async function fetchContentExampleCode (name?: string) {
await state.value[name] await state.value[name]
return state.value[name] return state.value[name]
} }
if (state.value[name]) { return state.value[name] } if (state.value[name]) {
return state.value[name]
}
// add to nitro prerender // add to nitro prerender
if (import.meta.server) { if (import.meta.server) {

View File

@@ -1,31 +0,0 @@
import { createShikiHighlighter } from '@nuxtjs/mdc/runtime/highlighter/shiki'
import MaterialTheme from 'shiki/themes/material-theme.mjs'
import MaterialThemeLighter from 'shiki/themes/material-theme-lighter.mjs'
import MaterialThemePalenight from 'shiki/themes/material-theme-palenight.mjs'
import HtmlLang from 'shiki/langs/html.mjs'
import MdcLang from 'shiki/langs/mdc.mjs'
import VueLang from 'shiki/langs/vue.mjs'
import YamlLang from 'shiki/langs/yaml.mjs'
import PostcssLang from 'shiki/langs/postcss.mjs'
let highlighter
export const useShikiHighlighter = () => {
if (!highlighter) {
highlighter = createShikiHighlighter({
bundledThemes: {
'material-theme': MaterialTheme,
'material-theme-lighter': MaterialThemeLighter,
'material-theme-palenight': MaterialThemePalenight
},
bundledLangs: {
html: HtmlLang,
mdc: MdcLang,
vue: VueLang,
yml: YamlLang,
postcss: PostcssLang
}
})
}
return highlighter
}

View File

@@ -2,10 +2,10 @@ import type { Options } from 'prettier'
import PrettierWorker from '@/workers/prettier.js?worker&inline' import PrettierWorker from '@/workers/prettier.js?worker&inline'
export interface SimplePrettier { export interface SimplePrettier {
format: (source: string, options?: Options) => Promise<string>; format: (source: string, options?: Options) => Promise<string>
} }
function createPrettierWorkerApi (worker: Worker): SimplePrettier { function createPrettierWorkerApi(worker: Worker): SimplePrettier {
let counter = 0 let counter = 0
const handlers = {} const handlers = {}
@@ -17,6 +17,7 @@ function createPrettierWorkerApi (worker: Worker): SimplePrettier {
} }
const [resolve, reject] = handlers[uid] const [resolve, reject] = handlers[uid]
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete handlers[uid] delete handlers[uid]
if (error) { if (error) {
@@ -26,7 +27,7 @@ function createPrettierWorkerApi (worker: Worker): SimplePrettier {
} }
}) })
function postMessage<T> (message) { function postMessage<T>(message) {
const uid = ++counter const uid = ++counter
return new Promise<T>((resolve, reject) => { return new Promise<T>((resolve, reject) => {
handlers[uid] = [resolve, reject] handlers[uid] = [resolve, reject]
@@ -35,19 +36,19 @@ function createPrettierWorkerApi (worker: Worker): SimplePrettier {
} }
return { return {
format (source: string, options?: Options) { format(source: string, options?: Options) {
return postMessage({ type: 'format', source, options }) return postMessage({ type: 'format', source, options })
} }
} }
} }
export default defineNuxtPlugin({ export default defineNuxtPlugin({
async setup () { async setup() {
let prettier: SimplePrettier let prettier: SimplePrettier
if (import.meta.server) { if (import.meta.server) {
const prettierModule = await import('prettier') const prettierModule = await import('prettier')
prettier = { prettier = {
format (source, options = { format(source, options = {
parser: 'markdown' parser: 'markdown'
}) { }) {
return prettierModule.format(source, options) return prettierModule.format(source, options)

View File

@@ -10,14 +10,14 @@ self.onmessage = async function (event) {
}) })
} }
function handleMessage (message) { function handleMessage(message) {
switch (message.type) { switch (message.type) {
case 'format': case 'format':
return handleFormatMessage(message) return handleFormatMessage(message)
} }
} }
async function handleFormatMessage (message) { async function handleFormatMessage(message) {
const { options, source } = message const { options, source } = message
const formatted = await prettier.format(source, { const formatted = await prettier.format(source, {
parser: 'markdown', parser: 'markdown',

View File

@@ -1,5 +1,5 @@
import { existsSync, readFileSync } from 'fs' import { existsSync, readFileSync } from 'node:fs'
import fsp from 'fs/promises' import fsp from 'node:fs/promises'
import { dirname, join } from 'pathe' import { dirname, join } from 'pathe'
import { defineNuxtModule, addTemplate, addServerHandler, createResolver } from '@nuxt/kit' import { defineNuxtModule, addTemplate, addServerHandler, createResolver } from '@nuxt/kit'
@@ -7,20 +7,20 @@ export default defineNuxtModule({
meta: { meta: {
name: 'content-examples-code' name: 'content-examples-code'
}, },
async setup (_options, nuxt) { async setup(_options, nuxt) {
const resolver = createResolver(import.meta.url) const resolver = createResolver(import.meta.url)
let _configResolved: any let _configResolved: any
let components: Record<string, any> let components: Record<string, any>
const outputPath = join(nuxt.options.buildDir, 'content-examples-code') const outputPath = join(nuxt.options.buildDir, 'content-examples-code')
async function stubOutput () { async function stubOutput() {
if (existsSync(outputPath + '.mjs')) { if (existsSync(outputPath + '.mjs')) {
return return
} }
await updateOutput('export default {}') await updateOutput('export default {}')
} }
async function fetchComponent (component: string | any) { async function fetchComponent(component: string | any) {
if (typeof component === 'string') { if (typeof component === 'string') {
if (components[component]) { if (components[component]) {
component = components[component] component = components[component]
@@ -51,7 +51,7 @@ export default defineNuxtModule({
const getVirtualModuleContent = () => const getVirtualModuleContent = () =>
`export default ${getStringifiedComponents()}` `export default ${getStringifiedComponents()}`
async function updateOutput (content?: string) { async function updateOutput(content?: string) {
const path = outputPath + '.mjs' const path = outputPath + '.mjs'
if (!existsSync(dirname(path))) { if (!existsSync(dirname(path))) {
await fsp.mkdir(dirname(path), { recursive: true }) await fsp.mkdir(dirname(path), { recursive: true })
@@ -62,13 +62,13 @@ export default defineNuxtModule({
await fsp.writeFile(path, content || getVirtualModuleContent(), 'utf-8') await fsp.writeFile(path, content || getVirtualModuleContent(), 'utf-8')
} }
async function fetchComponents () { async function fetchComponents() {
await Promise.all(Object.keys(components).map(fetchComponent)) await Promise.all(Object.keys(components).map(fetchComponent))
} }
nuxt.hook('components:extend', async (_components) => { nuxt.hook('components:extend', async (_components) => {
components = _components components = _components
.filter((v) => v.shortPath.includes('components/content/examples/')) .filter(v => v.shortPath.includes('components/content/examples/'))
.reduce((acc, component) => { .reduce((acc, component) => {
acc[component.pascalName] = component acc[component.pascalName] = component
return acc return acc
@@ -87,17 +87,17 @@ export default defineNuxtModule({
vite.config.plugins.push({ vite.config.plugins.push({
name: 'content-examples-code', name: 'content-examples-code',
enforce: 'post', enforce: 'post',
async buildStart () { async buildStart() {
if (_configResolved?.build.ssr) { if (_configResolved?.build.ssr) {
return return
} }
await fetchComponents() await fetchComponents()
await updateOutput() await updateOutput()
}, },
configResolved (config) { configResolved(config) {
_configResolved = config _configResolved = config
}, },
async handleHotUpdate ({ file }) { async handleHotUpdate({ file }) {
if ( if (
Object.entries(components).some( Object.entries(components).some(
([, comp]: any) => comp.filePath === file ([, comp]: any) => comp.filePath === file

View File

@@ -1,6 +1,6 @@
import { defineEventHandler, createError, appendHeader } from 'h3' import { defineEventHandler, createError, appendHeader } from 'h3'
import { pascalCase } from 'scule' import { pascalCase } from 'scule'
// @ts-expect-error // @ts-expect-error - no types available
import components from '#content-examples-code/nitro' import components from '#content-examples-code/nitro'
export default defineEventHandler((event) => { export default defineEventHandler((event) => {

View File

@@ -1,6 +1,6 @@
import { Octokit } from '@octokit/rest' import { Octokit } from '@octokit/rest'
function isUserABot (user) { function isUserABot(user) {
return user?.login?.endsWith('-bot') || user?.login?.endsWith('[bot]') return user?.login?.endsWith('-bot') || user?.login?.endsWith('[bot]')
} }

View File

@@ -65,7 +65,9 @@ const appConfig = useAppConfig()
const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'collapsible', 'defaultValue', 'disabled', 'modelValue', 'type'), emits) const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'collapsible', 'defaultValue', 'disabled', 'modelValue', 'type'), emits)
const contentProps = toRef(() => props.content) const contentProps = toRef(() => props.content)
const ui = computed(() => tv({ extend: accordion, slots: props.ui })({ disabled: props.disabled })) const ui = computed(() => accordion({
disabled: props.disabled
}))
</script> </script>
<template> <template>
@@ -76,25 +78,25 @@ const ui = computed(() => tv({ extend: accordion, slots: props.ui })({ disabled:
:key="index" :key="index"
:value="item.value || String(index)" :value="item.value || String(index)"
:disabled="item.disabled" :disabled="item.disabled"
:class="ui.item()" :class="ui.item({ class: props.ui?.item })"
> >
<AccordionHeader :class="ui.header()"> <AccordionHeader :class="ui.header({ class: props.ui?.header })">
<AccordionTrigger :class="ui.trigger({ disabled: item.disabled })"> <AccordionTrigger :class="ui.trigger({ class: props.ui?.trigger, disabled: item.disabled })">
<slot name="leading" :item="item" :index="index" :open="open"> <slot name="leading" :item="item" :index="index" :open="open">
<UIcon v-if="item.icon" :name="item.icon" :class="ui.leadingIcon()" /> <UIcon v-if="item.icon" :name="item.icon" :class="ui.leadingIcon({ class: props.ui?.leadingIcon })" />
</slot> </slot>
<span v-if="item.label || !!slots.default" :class="ui.label()"> <span v-if="item.label || !!slots.default" :class="ui.label({ class: props.ui?.label })">
<slot :item="item" :index="index" :open="open">{{ item.label }}</slot> <slot :item="item" :index="index" :open="open">{{ item.label }}</slot>
</span> </span>
<slot name="trailing" :item="item" :index="index" :open="open"> <slot name="trailing" :item="item" :index="index" :open="open">
<UIcon :name="item.trailingIcon || trailingIcon || appConfig.ui.icons.chevronDown" :class="ui.trailingIcon()" /> <UIcon :name="item.trailingIcon || trailingIcon || appConfig.ui.icons.chevronDown" :class="ui.trailingIcon({ class: props.ui?.trailingIcon })" />
</slot> </slot>
</AccordionTrigger> </AccordionTrigger>
</AccordionHeader> </AccordionHeader>
<AccordionContent v-if="item.content || !!slots.content || (item.slot && !!slots[item.slot])" v-bind="contentProps" :class="ui.content()"> <AccordionContent v-if="item.content || !!slots.content || (item.slot && !!slots[item.slot])" v-bind="contentProps" :class="ui.content({ class: props.ui?.content })">
<slot :name="item.slot || 'content'" :item="item" :index="index" :open="open"> <slot :name="item.slot || 'content'" :item="item" :index="index" :open="open">
{{ item.content }} {{ item.content }}
</slot> </slot>

View File

@@ -54,7 +54,7 @@ export interface AlertSlots {
title(props?: {}): any title(props?: {}): any
description(props?: {}): any description(props?: {}): any
actions(props?: {}): any actions(props?: {}): any
close(props: { class: string }): any close(props: { ui: any }): any
} }
</script> </script>
@@ -72,7 +72,7 @@ const appConfig = useAppConfig()
const multiline = computed(() => !!props.title && !!props.description) const multiline = computed(() => !!props.title && !!props.description)
const ui = computed(() => tv({ extend: alert, slots: props.ui })({ const ui = computed(() => alert({
color: props.color, color: props.color,
variant: props.variant variant: props.variant
})) }))
@@ -81,39 +81,39 @@ const ui = computed(() => tv({ extend: alert, slots: props.ui })({
<template> <template>
<Primitive :as="as" :class="ui.root({ class: props.class, multiline })"> <Primitive :as="as" :class="ui.root({ class: props.class, multiline })">
<slot name="leading"> <slot name="leading">
<UAvatar v-if="avatar" size="2xl" v-bind="avatar" :class="ui.avatar()" /> <UAvatar v-if="avatar" size="2xl" v-bind="avatar" :class="ui.avatar({ class: props.ui?.avatar })" />
<UIcon v-else-if="icon" :name="icon" :class="ui.icon()" /> <UIcon v-else-if="icon" :name="icon" :class="ui.icon({ class: props.ui?.icon })" />
</slot> </slot>
<div :class="ui.wrapper()"> <div :class="ui.wrapper({ class: props.ui?.wrapper })">
<div v-if="title || !!slots.title" :class="ui.title()"> <div v-if="title || !!slots.title" :class="ui.title({ class: props.ui?.title })">
<slot name="title"> <slot name="title">
{{ title }} {{ title }}
</slot> </slot>
</div> </div>
<template v-if="description || !!slots.description"> <template v-if="description || !!slots.description">
<div :class="ui.description()"> <div :class="ui.description({ class: props.ui?.description })">
<slot name="description"> <slot name="description">
{{ description }} {{ description }}
</slot> </slot>
</div> </div>
</template> </template>
<div v-if="multiline && actions?.length" :class="ui.actions({ multiline: true })"> <div v-if="multiline && actions?.length" :class="ui.actions({ class: props.ui?.actions, multiline: true })">
<slot name="actions"> <slot name="actions">
<UButton v-for="(action, index) in actions" :key="index" size="xs" v-bind="action" /> <UButton v-for="(action, index) in actions" :key="index" size="xs" v-bind="action" />
</slot> </slot>
</div> </div>
</div> </div>
<div v-if="(!multiline && actions?.length) || close" :class="ui.actions({ multiline: false })"> <div v-if="(!multiline && actions?.length) || close" :class="ui.actions({ class: props.ui?.actions, multiline: false })">
<template v-if="!multiline"> <template v-if="!multiline">
<slot name="actions"> <slot name="actions">
<UButton v-for="(action, index) in actions" :key="index" size="xs" v-bind="action" /> <UButton v-for="(action, index) in actions" :key="index" size="xs" v-bind="action" />
</slot> </slot>
</template> </template>
<slot name="close" :class="ui.close()"> <slot name="close" :ui="ui">
<UButton <UButton
v-if="close" v-if="close"
:icon="closeIcon || appConfig.ui.icons.close" :icon="closeIcon || appConfig.ui.icons.close"
@@ -122,7 +122,7 @@ const ui = computed(() => tv({ extend: alert, slots: props.ui })({
variant="link" variant="link"
aria-label="Close" aria-label="Close"
v-bind="typeof close === 'object' ? close : undefined" v-bind="typeof close === 'object' ? close : undefined"
:class="ui.close()" :class="ui.close({ class: props.ui?.close })"
@click="emits('close')" @click="emits('close')"
/> />
</slot> </slot>

View File

@@ -38,16 +38,19 @@ const fallback = computed(() => props.text || (props.alt || '').split(' ').map(w
const { size } = useAvatarGroup(props) const { size } = useAvatarGroup(props)
const ui = computed(() => tv({ extend: avatar, slots: props.ui })({ size: size.value })) // eslint-disable-next-line vue/no-dupe-keys
const ui = computed(() => avatar({
size: size.value
}))
</script> </script>
<template> <template>
<AvatarRoot :class="ui.root({ class: props.class })"> <AvatarRoot :class="ui.root({ class: props.class })">
<AvatarImage v-if="src" :as="as" :src="src" :alt="alt" :class="ui.image()" /> <AvatarImage v-if="src" :as="as" :src="src" :alt="alt" :class="ui.image({ class: props.ui?.image })" />
<AvatarFallback as-child v-bind="fallbackProps"> <AvatarFallback as-child v-bind="fallbackProps">
<UIcon v-if="icon" :name="icon" :class="ui.icon()" /> <UIcon v-if="icon" :name="icon" :class="ui.icon({ class: props.ui?.icon })" />
<span v-else :class="ui.fallback()">{{ fallback }}</span> <span v-else :class="ui.fallback({ class: props.ui?.fallback })">{{ fallback }}</span>
</AvatarFallback> </AvatarFallback>
</AvatarRoot> </AvatarRoot>
</template> </template>

View File

@@ -39,7 +39,7 @@ import { avatarGroupInjectionKey } from '#imports'
const props = defineProps<AvatarGroupProps>() const props = defineProps<AvatarGroupProps>()
const slots = defineSlots<AvatarGroupSlots>() const slots = defineSlots<AvatarGroupSlots>()
const ui = computed(() => tv({ extend: avatarGroup, slots: props.ui })({ const ui = computed(() => avatarGroup({
size: props.size size: props.size
})) }))
@@ -74,7 +74,7 @@ provide(avatarGroupInjectionKey, computed(() => ({
<template> <template>
<Primitive :as="as" :class="ui.root({ class: props.class })"> <Primitive :as="as" :class="ui.root({ class: props.class })">
<UAvatar v-if="hiddenCount > 0" :text="`+${hiddenCount}`" :class="ui.base()" /> <UAvatar v-if="hiddenCount > 0" :text="`+${hiddenCount}`" :class="ui.base({ class: props.ui?.base })" />
<component :is="avatar" v-for="(avatar, count) in visibleAvatars" :key="count" :class="ui.base()" /> <component :is="avatar" v-for="(avatar, count) in visibleAvatars" :key="count" :class="ui.base({ class: props.ui?.base })" />
</Primitive> </Primitive>
</template> </template>

View File

@@ -45,7 +45,6 @@ export type BreadcrumbSlots<T extends { slot?: string }> = {
</script> </script>
<script setup lang="ts" generic="T extends BreadcrumbItem"> <script setup lang="ts" generic="T extends BreadcrumbItem">
import { computed } from 'vue'
import { Primitive } from 'radix-vue' import { Primitive } from 'radix-vue'
import { useAppConfig } from '#imports' import { useAppConfig } from '#imports'
import { ULink, UIcon, UAvatar } from '#components' import { ULink, UIcon, UAvatar } from '#components'
@@ -56,23 +55,24 @@ const slots = defineSlots<BreadcrumbSlots<T>>()
const appConfig = useAppConfig() const appConfig = useAppConfig()
const ui = computed(() => tv({ extend: breadcrumb, slots: props.ui })()) // eslint-disable-next-line vue/no-dupe-keys
const ui = breadcrumb()
</script> </script>
<template> <template>
<Primitive :as="as" aria-label="breadcrumb" :class="ui.root({ class: props.class })"> <Primitive :as="as" aria-label="breadcrumb" :class="ui.root({ class: props.class })">
<ol :class="ui.list()"> <ol :class="ui.list({ class: props.ui?.list })">
<template v-for="(item, index) in items" :key="index"> <template v-for="(item, index) in items" :key="index">
<li :class="ui.item()"> <li :class="ui.item({ class: props.ui?.item })">
<ULink v-slot="{ active, ...slotProps }" v-bind="pickLinkProps(item)" custom> <ULink v-slot="{ active, ...slotProps }" v-bind="pickLinkProps(item)" custom>
<ULinkBase v-bind="slotProps" as="span" :aria-current="active && (index === items!.length - 1) ? 'page' : undefined" :class="ui.link({ active: index === items!.length - 1, disabled: !!item.disabled, to: !!item.to })"> <ULinkBase v-bind="slotProps" as="span" :aria-current="active && (index === items!.length - 1) ? 'page' : undefined" :class="ui.link({ class: props.ui?.link, active: index === items!.length - 1, disabled: !!item.disabled, to: !!item.to })">
<slot :name="item.slot || 'item'" :item="item" :index="index"> <slot :name="item.slot || 'item'" :item="item" :index="index">
<slot :name="item.slot ? `${item.slot}-leading`: 'item-leading'" :item="item" :active="index === items!.length - 1" :index="index"> <slot :name="item.slot ? `${item.slot}-leading`: 'item-leading'" :item="item" :active="index === items!.length - 1" :index="index">
<UAvatar v-if="item.avatar" size="2xs" v-bind="item.avatar" :class="ui.linkLeadingAvatar({ active: index === items!.length - 1 })" /> <UAvatar v-if="item.avatar" size="2xs" v-bind="item.avatar" :class="ui.linkLeadingAvatar({ class: props.ui?.linkLeadingAvatar, active: index === items!.length - 1 })" />
<UIcon v-else-if="item.icon" :name="item.icon" :class="ui.linkLeadingIcon({ active: index === items!.length - 1 })" /> <UIcon v-else-if="item.icon" :name="item.icon" :class="ui.linkLeadingIcon({ class: props.ui?.linkLeadingIcon, active: index === items!.length - 1 })" />
</slot> </slot>
<span v-if="item.label || !!slots[item.slot ? `${item.slot}-label`: 'item-label']" :class="ui.linkLabel()"> <span v-if="item.label || !!slots[item.slot ? `${item.slot}-label`: 'item-label']" :class="ui.linkLabel({ class: props.ui?.linkLabel })">
<slot :name="item.slot ? `${item.slot}-label`: 'item-label'" :item="item" :active="index === items!.length - 1" :index="index"> <slot :name="item.slot ? `${item.slot}-label`: 'item-label'" :item="item" :active="index === items!.length - 1" :index="index">
{{ item.label }} {{ item.label }}
</slot> </slot>
@@ -84,9 +84,9 @@ const ui = computed(() => tv({ extend: breadcrumb, slots: props.ui })())
</ULink> </ULink>
</li> </li>
<li v-if="index < items!.length - 1" role="presentation" :class="ui.separator()"> <li v-if="index < items!.length - 1" role="presentation" :class="ui.separator({ class: props.ui?.separator })">
<slot name="separator"> <slot name="separator">
<UIcon :name="separatorIcon || appConfig.ui.icons.chevronRight" :class="ui.separatorIcon()" /> <UIcon :name="separatorIcon || appConfig.ui.icons.chevronRight" :class="ui.separatorIcon({ class: props.ui?.separatorIcon })" />
</slot> </slot>
</li> </li>
</template> </template>

View File

@@ -48,7 +48,7 @@ const linkProps = useForwardProps(pickLinkProps(props))
const { orientation, size: buttonSize } = useButtonGroup<ButtonProps>(props) const { orientation, size: buttonSize } = useButtonGroup<ButtonProps>(props)
const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponentIcons(props) const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponentIcons(props)
const ui = computed(() => tv({ extend: button, slots: props.ui })({ const ui = computed(() => button({
color: props.color, color: props.color,
variant: props.variant, variant: props.variant,
size: buttonSize.value, size: buttonSize.value,
@@ -64,17 +64,17 @@ const ui = computed(() => tv({ extend: button, slots: props.ui })({
<template> <template>
<ULink :type="type" :disabled="disabled || loading" :class="ui.base({ class: props.class })" v-bind="linkProps" raw> <ULink :type="type" :disabled="disabled || loading" :class="ui.base({ class: props.class })" v-bind="linkProps" raw>
<slot name="leading"> <slot name="leading">
<UIcon v-if="isLeading && leadingIconName" :name="leadingIconName" :class="ui.leadingIcon()" /> <UIcon v-if="isLeading && leadingIconName" :name="leadingIconName" :class="ui.leadingIcon({ class: props.ui?.leadingIcon })" />
</slot> </slot>
<span v-if="label || !!slots.default" :class="ui.label()"> <span v-if="label || !!slots.default" :class="ui.label({ class: props.ui?.label })">
<slot> <slot>
{{ label }} {{ label }}
</slot> </slot>
</span> </span>
<slot name="trailing"> <slot name="trailing">
<UIcon v-if="isTrailing && trailingIconName" :name="trailingIconName" :class="ui.trailingIcon()" /> <UIcon v-if="isTrailing && trailingIconName" :name="trailingIconName" :class="ui.trailingIcon({ class: props.ui?.trailingIcon })" />
</slot> </slot>
</ULink> </ULink>
</template> </template>

View File

@@ -26,26 +26,26 @@ export interface CardSlots {
</script> </script>
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue'
import { Primitive } from 'radix-vue' import { Primitive } from 'radix-vue'
const props = defineProps<CardProps>() const props = defineProps<CardProps>()
const slots = defineSlots<CardSlots>() const slots = defineSlots<CardSlots>()
const ui = computed(() => tv({ extend: card, slots: props.ui })()) // eslint-disable-next-line vue/no-dupe-keys
const ui = card()
</script> </script>
<template> <template>
<Primitive :as="as" :class="ui.root({ class: props.class })"> <Primitive :as="as" :class="ui.root({ class: props.class })">
<div v-if="!!slots.header" :class="ui.header()"> <div v-if="!!slots.header" :class="ui.header({ class: props.ui?.header })">
<slot name="header" /> <slot name="header" />
</div> </div>
<div v-if="!!slots.default" :class="ui.body()"> <div v-if="!!slots.default" :class="ui.body({ class: props.ui?.body })">
<slot /> <slot />
</div> </div>
<div v-if="!!slots.footer" :class="ui.footer()"> <div v-if="!!slots.footer" :class="ui.footer({ class: props.ui?.footer })">
<slot name="footer" /> <slot name="footer" />
</div> </div>
</Primitive> </Primitive>

View File

@@ -74,7 +74,7 @@ const checked = computed({
} }
}) })
const ui = computed(() => tv({ extend: checkbox, slots: props.ui })({ const ui = computed(() => checkbox({
size: size.value, size: size.value,
color: color.value, color: color.value,
required: props.required, required: props.required,
@@ -93,7 +93,7 @@ function onUpdate(value: any) {
<template> <template>
<div :class="ui.root({ class: props.class })"> <div :class="ui.root({ class: props.class })">
<div :class="ui.container()"> <div :class="ui.container({ class: props.ui?.container })">
<CheckboxRoot <CheckboxRoot
:id="id" :id="id"
v-model:checked="checked" v-model:checked="checked"
@@ -101,23 +101,23 @@ function onUpdate(value: any) {
v-bind="rootProps" v-bind="rootProps"
:name="name" :name="name"
:disabled="disabled" :disabled="disabled"
:class="ui.base()" :class="ui.base({ class: props.ui?.base })"
@update:checked="onUpdate" @update:checked="onUpdate"
> >
<CheckboxIndicator as-child> <CheckboxIndicator as-child>
<UIcon v-if="indeterminate" :name="indeterminateIcon || appConfig.ui.icons.minus" :class="ui.icon()" /> <UIcon v-if="indeterminate" :name="indeterminateIcon || appConfig.ui.icons.minus" :class="ui.icon({ class: props.ui?.icon })" />
<UIcon v-else :name="icon || appConfig.ui.icons.check" :class="ui.icon()" /> <UIcon v-else :name="icon || appConfig.ui.icons.check" :class="ui.icon({ class: props.ui?.icon })" />
</CheckboxIndicator> </CheckboxIndicator>
</CheckboxRoot> </CheckboxRoot>
</div> </div>
<div v-if="(label || !!slots.label) || (description || !!slots.description)" :class="ui.wrapper()"> <div v-if="(label || !!slots.label) || (description || !!slots.description)" :class="ui.wrapper({ class: props.ui?.wrapper })">
<Label v-if="label || !!slots.label" :for="id" :class="ui.label()"> <Label v-if="label || !!slots.label" :for="id" :class="ui.label({ class: props.ui?.label })">
<slot name="label" :label="label"> <slot name="label" :label="label">
{{ label }} {{ label }}
</slot> </slot>
</Label> </Label>
<p v-if="description || !!slots.description" :class="ui.description()"> <p v-if="description || !!slots.description" :class="ui.description({ class: props.ui?.description })">
<slot name="description" :description="description"> <slot name="description" :description="description">
{{ description }} {{ description }}
</slot> </slot>

View File

@@ -51,7 +51,7 @@ const show = defineModel<boolean>('show', { default: true })
const { size } = useAvatarGroup(props) const { size } = useAvatarGroup(props)
const ui = computed(() => tv({ extend: chip, slots: props.ui })({ const ui = computed(() => chip({
color: props.color, color: props.color,
size: size.value, size: size.value,
position: props.position, position: props.position,
@@ -64,7 +64,7 @@ const ui = computed(() => tv({ extend: chip, slots: props.ui })({
<Primitive :as="as" :class="ui.root({ class: props.class })"> <Primitive :as="as" :class="ui.root({ class: props.class })">
<slot /> <slot />
<span v-if="show" :class="ui.base()"> <span v-if="show" :class="ui.base({ class: props.ui?.base })">
<slot name="content"> <slot name="content">
{{ text }} {{ text }}
</slot> </slot>

View File

@@ -28,7 +28,6 @@ export interface CollapsibleSlots {
</script> </script>
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue'
import { CollapsibleRoot, CollapsibleTrigger, CollapsibleContent, useForwardPropsEmits } from 'radix-vue' import { CollapsibleRoot, CollapsibleTrigger, CollapsibleContent, useForwardPropsEmits } from 'radix-vue'
import { reactivePick } from '@vueuse/core' import { reactivePick } from '@vueuse/core'
@@ -38,7 +37,8 @@ const slots = defineSlots<CollapsibleSlots>()
const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'defaultOpen', 'open', 'disabled'), emits) const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'defaultOpen', 'open', 'disabled'), emits)
const ui = computed(() => tv({ extend: collapsible, slots: props.ui })()) // eslint-disable-next-line vue/no-dupe-keys
const ui = collapsible()
</script> </script>
<template> <template>
@@ -47,7 +47,7 @@ const ui = computed(() => tv({ extend: collapsible, slots: props.ui })())
<slot :open="open" /> <slot :open="open" />
</CollapsibleTrigger> </CollapsibleTrigger>
<CollapsibleContent :class="ui.content()"> <CollapsibleContent :class="ui.content({ class: props.ui?.content })">
<slot name="content" /> <slot name="content" />
</CollapsibleContent> </CollapsibleContent>
</CollapsibleRoot> </CollapsibleRoot>

View File

@@ -84,7 +84,7 @@ type SlotProps<T> = (props: { item: T, index: number }) => any
export type CommandPaletteSlots<G extends { slot?: string }, T extends { slot?: string }> = { export type CommandPaletteSlots<G extends { slot?: string }, T extends { slot?: string }> = {
'empty'(props: { searchTerm?: string }): any 'empty'(props: { searchTerm?: string }): any
'close'(props?: {}): any 'close'(props: { ui: any }): any
'item': SlotProps<T> 'item': SlotProps<T>
'item-leading': SlotProps<T> 'item-leading': SlotProps<T>
'item-label': SlotProps<T> 'item-label': SlotProps<T>
@@ -116,7 +116,8 @@ const appConfig = useAppConfig()
const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'disabled', 'multiple', 'modelValue', 'defaultValue'), emits) const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'disabled', 'multiple', 'modelValue', 'defaultValue'), emits)
const inputProps = useForwardProps(reactivePick(props, 'loading', 'loadingIcon', 'placeholder')) const inputProps = useForwardProps(reactivePick(props, 'loading', 'loadingIcon', 'placeholder'))
const ui = computed(() => tv({ extend: commandPalette, slots: props.ui })()) // eslint-disable-next-line vue/no-dupe-keys
const ui = commandPalette()
const fuse = computed(() => defu({}, props.fuse, { const fuse = computed(() => defu({}, props.fuse, {
fuseOptions: { fuseOptions: {
@@ -187,10 +188,10 @@ const groups = computed(() => {
size="lg" size="lg"
v-bind="inputProps" v-bind="inputProps"
:icon="icon || appConfig.ui.icons.search" :icon="icon || appConfig.ui.icons.search"
:class="ui.input()" :class="ui.input({ class: props.ui?.input })"
> >
<template v-if="close || !!slots.close" #trailing> <template v-if="close || !!slots.close" #trailing>
<slot name="close" :class="ui.close()"> <slot name="close" :ui="ui">
<UButton <UButton
v-if="close" v-if="close"
:icon="closeIcon || appConfig.ui.icons.close" :icon="closeIcon || appConfig.ui.icons.close"
@@ -199,7 +200,7 @@ const groups = computed(() => {
variant="ghost" variant="ghost"
aria-label="Close" aria-label="Close"
v-bind="typeof close === 'object' ? close : undefined" v-bind="typeof close === 'object' ? close : undefined"
:class="ui.close()" :class="ui.close({ class: props.ui?.close })"
@click="emits('update:open', false)" @click="emits('update:open', false)"
/> />
</slot> </slot>
@@ -208,16 +209,16 @@ const groups = computed(() => {
</ComboboxInput> </ComboboxInput>
<ComboboxPortal disabled> <ComboboxPortal disabled>
<ComboboxContent :class="ui.content()" :dismissable="false"> <ComboboxContent :class="ui.content({ class: props.ui?.content })" :dismissable="false">
<ComboboxEmpty :class="ui.empty()"> <ComboboxEmpty :class="ui.empty({ class: props.ui?.empty })">
<slot name="empty" :search-term="searchTerm"> <slot name="empty" :search-term="searchTerm">
{{ searchTerm ? `No results for ${searchTerm}` : 'No results' }} {{ searchTerm ? `No results for ${searchTerm}` : 'No results' }}
</slot> </slot>
</ComboboxEmpty> </ComboboxEmpty>
<ComboboxViewport :class="ui.viewport()"> <ComboboxViewport :class="ui.viewport({ class: props.ui?.viewport })">
<ComboboxGroup v-for="(group, groupIndex) in groups" :key="`group-${groupIndex}`" :class="ui.group()"> <ComboboxGroup v-for="(group, groupIndex) in groups" :key="`group-${groupIndex}`" :class="ui.group({ class: props.ui?.group })">
<ComboboxLabel v-if="group.label" :class="ui.label()"> <ComboboxLabel v-if="group.label" :class="ui.label({ class: props.ui?.label })">
{{ group.label }} {{ group.label }}
</ComboboxLabel> </ComboboxLabel>
@@ -226,43 +227,43 @@ const groups = computed(() => {
:key="`group-${groupIndex}-${index}`" :key="`group-${groupIndex}-${index}`"
:value="omit(item, ['matches' as any, 'group' as any, 'select', 'labelHtml', 'suffixHtml'])" :value="omit(item, ['matches' as any, 'group' as any, 'select', 'labelHtml', 'suffixHtml'])"
:disabled="item.disabled" :disabled="item.disabled"
:class="ui.item()" :class="ui.item({ class: props.ui?.item })"
@select="item.select" @select="item.select"
> >
<slot :name="item.slot || group.slot || 'item'" :item="item" :index="index"> <slot :name="item.slot || group.slot || 'item'" :item="item" :index="index">
<slot :name="item.slot ? `${item.slot}-leading` : group.slot ? `${group.slot}-leading` : `item-leading`" :item="item" :index="index"> <slot :name="item.slot ? `${item.slot}-leading` : group.slot ? `${group.slot}-leading` : `item-leading`" :item="item" :index="index">
<UAvatar v-if="item.avatar" size="2xs" v-bind="item.avatar" :class="ui.itemLeadingAvatar()" /> <UAvatar v-if="item.avatar" size="2xs" v-bind="item.avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar })" />
<UIcon v-else-if="item.icon" :name="item.icon" :class="ui.itemLeadingIcon()" /> <UIcon v-else-if="item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ class: props.ui?.itemLeadingIcon })" />
<UChip <UChip
v-else-if="item.chip" v-else-if="item.chip"
size="md" size="md"
inset inset
standalone standalone
v-bind="item.chip" v-bind="item.chip"
:class="ui.itemLeadingChip()" :class="ui.itemLeadingChip({ class: props.ui?.itemLeadingChip })"
/> />
</slot> </slot>
<span v-if="item.label || !!slots[item.slot ? `${item.slot}-label` : group.slot ? `${group.slot}-label` : `item-label`]" :class="ui.itemLabel()"> <span v-if="item.label || !!slots[item.slot ? `${item.slot}-label` : group.slot ? `${group.slot}-label` : `item-label`]" :class="ui.itemLabel({ class: props.ui?.itemLabel })">
<slot :name="item.slot ? `${item.slot}-label` : group.slot ? `${group.slot}-label` : `item-label`" :item="item" :index="index"> <slot :name="item.slot ? `${item.slot}-label` : group.slot ? `${group.slot}-label` : `item-label`" :item="item" :index="index">
<span v-if="item.prefix" :class="ui.itemLabelPrefix()">{{ item.prefix }}</span> <span v-if="item.prefix" :class="ui.itemLabelPrefix({ class: props.ui?.itemLabelPrefix })">{{ item.prefix }}</span>
<span :class="ui.itemLabelBase()" v-html="item.labelHtml || item.label" /> <span :class="ui.itemLabelBase({ class: props.ui?.itemLabelBase })" v-html="item.labelHtml || item.label" />
<span :class="ui.itemLabelSuffix()" v-html="item.suffixHtml || item.suffix" /> <span :class="ui.itemLabelSuffix({ class: props.ui?.itemLabelSuffix })" v-html="item.suffixHtml || item.suffix" />
</slot> </slot>
</span> </span>
<span :class="ui.itemTrailing()"> <span :class="ui.itemTrailing({ class: props.ui?.itemTrailing })">
<slot :name="item.slot ? `${item.slot}-trailing` : group.slot ? `${group.slot}-trailing` : `item-trailing`" :item="item" :index="index"> <slot :name="item.slot ? `${item.slot}-trailing` : group.slot ? `${group.slot}-trailing` : `item-trailing`" :item="item" :index="index">
<span v-if="item.kbds?.length" :class="ui.itemTrailingKbds()"> <span v-if="item.kbds?.length" :class="ui.itemTrailingKbds({ class: props.ui?.itemTrailingKbds })">
<UKbd v-for="(kbd, kbdIndex) in item.kbds" :key="kbdIndex" size="md" v-bind="typeof kbd === 'string' ? { value: kbd } : kbd" /> <UKbd v-for="(kbd, kbdIndex) in item.kbds" :key="kbdIndex" size="md" v-bind="typeof kbd === 'string' ? { value: kbd } : kbd" />
</span> </span>
<UIcon v-else-if="group.highlightedIcon" :name="group.highlightedIcon" :class="ui.itemTrailingHighlightedIcon()" /> <UIcon v-else-if="group.highlightedIcon" :name="group.highlightedIcon" :class="ui.itemTrailingHighlightedIcon({ class: props.ui?.itemTrailingHighlightedIcon })" />
</slot> </slot>
<ComboboxItemIndicator as-child> <ComboboxItemIndicator as-child>
<UIcon :name="selectedIcon || appConfig.ui.icons.check" :class="ui.itemTrailingIcon()" /> <UIcon :name="selectedIcon || appConfig.ui.icons.check" :class="ui.itemTrailingIcon({ class: props.ui?.itemTrailingIcon })" />
</ComboboxItemIndicator> </ComboboxItemIndicator>
</span> </span>
</slot> </slot>

View File

@@ -76,7 +76,7 @@ const rootProps = useForwardPropsEmits(reactivePick(props, 'modal'), emits)
const contentProps = toRef(() => props.content as ContextMenuContentProps) const contentProps = toRef(() => props.content as ContextMenuContentProps)
const proxySlots = omit(slots, ['default']) as Record<string, ContextMenuSlots<T>[string]> const proxySlots = omit(slots, ['default']) as Record<string, ContextMenuSlots<T>[string]>
const ui = computed(() => tv({ extend: contextMenu, slots: props.ui })({ const ui = computed(() => contextMenu({
size: props.size size: props.size
})) }))
</script> </script>
@@ -87,7 +87,14 @@ const ui = computed(() => tv({ extend: contextMenu, slots: props.ui })({
<slot /> <slot />
</ContextMenuTrigger> </ContextMenuTrigger>
<UContextMenuContent :class="ui.content({ class: props.class })" :ui="ui" v-bind="contentProps" :items="items" :portal="portal"> <UContextMenuContent
:class="ui.content({ class: props.class })"
:ui="ui"
:ui-override="props.ui"
v-bind="contentProps"
:items="items"
:portal="portal"
>
<template v-for="(_, name) in proxySlots" #[name]="slotData: any"> <template v-for="(_, name) in proxySlots" #[name]="slotData: any">
<slot :name="name" v-bind="slotData" /> <slot :name="name" v-bind="slotData" />
</template> </template>

View File

@@ -12,6 +12,7 @@ interface ContextMenuContentProps<T> extends Omit<RadixContextMenuContentProps,
sub?: boolean sub?: boolean
class?: any class?: any
ui: typeof contextMenu ui: typeof contextMenu
uiOverride?: any
} }
interface ContextMenuContentEmits extends RadixContextMenuContentEmits {} interface ContextMenuContentEmits extends RadixContextMenuContentEmits {}
@@ -44,22 +45,22 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
<DefineItemTemplate v-slot="{ item, active, index }"> <DefineItemTemplate v-slot="{ item, active, index }">
<slot :name="item.slot || 'item'" :item="(item as T)" :index="index"> <slot :name="item.slot || 'item'" :item="(item as T)" :index="index">
<slot :name="item.slot ? `${item.slot}-leading`: 'item-leading'" :item="(item as T)" :active="active" :index="index"> <slot :name="item.slot ? `${item.slot}-leading`: 'item-leading'" :item="(item as T)" :active="active" :index="index">
<UAvatar v-if="item.avatar" :size="(ui.itemLeadingAvatarSize() as AvatarProps['size'])" v-bind="item.avatar" :class="ui.itemLeadingAvatar({ active })" /> <UAvatar v-if="item.avatar" :size="(ui.itemLeadingAvatarSize() as AvatarProps['size'])" v-bind="item.avatar" :class="ui.itemLeadingAvatar({ class: uiOverride?.itemLeadingAvatar, active })" />
<UIcon v-else-if="item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ active })" /> <UIcon v-else-if="item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ class: uiOverride?.itemLeadingIcon, active })" />
</slot> </slot>
<span v-if="item.label || !!slots[item.slot ? `${item.slot}-label`: 'item-label']" :class="ui.itemLabel({ active })"> <span v-if="item.label || !!slots[item.slot ? `${item.slot}-label`: 'item-label']" :class="ui.itemLabel({ class: uiOverride?.itemLabel, active })">
<slot :name="item.slot ? `${item.slot}-label`: 'item-label'" :item="(item as T)" :active="active" :index="index"> <slot :name="item.slot ? `${item.slot}-label`: 'item-label'" :item="(item as T)" :active="active" :index="index">
{{ item.label }} {{ item.label }}
</slot> </slot>
<UIcon v-if="item.target === '_blank'" :name="appConfig.ui.icons.external" :class="ui.itemLabelExternalIcon({ active })" /> <UIcon v-if="item.target === '_blank'" :name="appConfig.ui.icons.external" :class="ui.itemLabelExternalIcon({ class: uiOverride?.itemLabelExternalIcon, active })" />
</span> </span>
<span v-if="item.children?.length || item.kbds?.length || !!slots[item.slot ? `${item.slot}-trailing`: 'item-trailing']" :class="ui.itemTrailing()"> <span v-if="item.children?.length || item.kbds?.length || !!slots[item.slot ? `${item.slot}-trailing`: 'item-trailing']" :class="ui.itemTrailing({ class: uiOverride?.itemTrailing })">
<slot :name="item.slot ? `${item.slot}-trailing`: 'item-trailing'" :item="(item as T)" :active="active" :index="index"> <slot :name="item.slot ? `${item.slot}-trailing`: 'item-trailing'" :item="(item as T)" :active="active" :index="index">
<UIcon v-if="item.children?.length" :name="appConfig.ui.icons.chevronRight" :class="ui.itemTrailingIcon({ active })" /> <UIcon v-if="item.children?.length" :name="appConfig.ui.icons.chevronRight" :class="ui.itemTrailingIcon({ class: uiOverride?.itemTrailingIcon, active })" />
<span v-else-if="item.kbds?.length" :class="ui.itemTrailingKbds()"> <span v-else-if="item.kbds?.length" :class="ui.itemTrailingKbds({ class: uiOverride?.itemTrailingKbds })">
<UKbd v-for="(kbd, kbdIndex) in item.kbds" :key="kbdIndex" :size="(ui.itemTrailingKbdsSize() as KbdProps['size'])" v-bind="typeof kbd === 'string' ? { value: kbd } : kbd" /> <UKbd v-for="(kbd, kbdIndex) in item.kbds" :key="kbdIndex" :size="(ui.itemTrailingKbdsSize() as KbdProps['size'])" v-bind="typeof kbd === 'string' ? { value: kbd } : kbd" />
</span> </span>
</slot> </slot>
@@ -69,12 +70,12 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
<ContextMenu.Portal :disabled="!portal"> <ContextMenu.Portal :disabled="!portal">
<component :is="sub ? ContextMenu.SubContent : ContextMenu.Content" :class="props.class" v-bind="contentProps"> <component :is="sub ? ContextMenu.SubContent : ContextMenu.Content" :class="props.class" v-bind="contentProps">
<ContextMenu.Group v-for="(group, groupIndex) in groups" :key="`group-${groupIndex}`" :class="ui.group()"> <ContextMenu.Group v-for="(group, groupIndex) in groups" :key="`group-${groupIndex}`" :class="ui.group({ class: uiOverride?.group })">
<template v-for="(item, index) in group" :key="`group-${groupIndex}-${index}`"> <template v-for="(item, index) in group" :key="`group-${groupIndex}-${index}`">
<ContextMenu.Label v-if="item.type === 'label'" :class="ui.label()"> <ContextMenu.Label v-if="item.type === 'label'" :class="ui.label({ class: uiOverride?.label })">
<ReuseItemTemplate :item="item" :index="index" /> <ReuseItemTemplate :item="item" :index="index" />
</ContextMenu.Label> </ContextMenu.Label>
<ContextMenu.Separator v-else-if="item.type === 'separator'" :class="ui.separator()" /> <ContextMenu.Separator v-else-if="item.type === 'separator'" :class="ui.separator({ class: uiOverride?.separator })" />
<ContextMenu.Sub v-else-if="item?.children?.length"> <ContextMenu.Sub v-else-if="item?.children?.length">
<ContextMenu.SubTrigger <ContextMenu.SubTrigger
as="button" as="button"
@@ -83,7 +84,7 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
:open="item.open" :open="item.open"
:default-open="item.defaultOpen" :default-open="item.defaultOpen"
:text-value="item.label" :text-value="item.label"
:class="ui.item()" :class="ui.item({ class: uiOverride?.item })"
> >
<ReuseItemTemplate :item="item" :index="index" /> <ReuseItemTemplate :item="item" :index="index" />
</ContextMenu.SubTrigger> </ContextMenu.SubTrigger>
@@ -92,6 +93,7 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
sub sub
:class="props.class" :class="props.class"
:ui="ui" :ui="ui"
:ui-override="uiOverride"
:portal="portal" :portal="portal"
:items="item.children" :items="item.children"
:align-offset="-4" :align-offset="-4"
@@ -104,7 +106,7 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
</ContextMenu.Sub> </ContextMenu.Sub>
<ContextMenu.Item v-else as-child :disabled="item.disabled" :text-value="item.label" @select="item.select"> <ContextMenu.Item v-else as-child :disabled="item.disabled" :text-value="item.label" @select="item.select">
<ULink v-slot="{ active, ...slotProps }" v-bind="pickLinkProps(item as Omit<ContextMenuItem, 'type'>)" custom> <ULink v-slot="{ active, ...slotProps }" v-bind="pickLinkProps(item as Omit<ContextMenuItem, 'type'>)" custom>
<ULinkBase v-bind="slotProps" :class="ui.item({ active })"> <ULinkBase v-bind="slotProps" :class="ui.item({ class: uiOverride?.item, active })">
<ReuseItemTemplate :item="item" :active="active" :index="index" /> <ReuseItemTemplate :item="item" :active="active" :index="index" />
</ULinkBase> </ULinkBase>
</ULink> </ULink>

View File

@@ -45,7 +45,7 @@ export interface DrawerSlots {
</script> </script>
<script setup lang="ts"> <script setup lang="ts">
import { computed, toRef } from 'vue' import { toRef } from 'vue'
import { useForwardPropsEmits } from 'radix-vue' import { useForwardPropsEmits } from 'radix-vue'
import { DrawerRoot, DrawerTrigger, DrawerPortal, DrawerOverlay, DrawerContent, DrawerTitle, DrawerDescription } from 'vaul-vue' import { DrawerRoot, DrawerTrigger, DrawerPortal, DrawerOverlay, DrawerContent, DrawerTitle, DrawerDescription } from 'vaul-vue'
import { reactivePick } from '@vueuse/core' import { reactivePick } from '@vueuse/core'
@@ -60,7 +60,8 @@ const slots = defineSlots<DrawerSlots>()
const rootProps = useForwardPropsEmits(reactivePick(props, 'activeSnapPoint', 'closeThreshold', 'defaultOpen', 'dismissible', 'fadeFromIndex', 'fixed', 'modal', 'nested', 'open', 'scrollLockTimeout', 'shouldScaleBackground', 'snapPoints'), emits) const rootProps = useForwardPropsEmits(reactivePick(props, 'activeSnapPoint', 'closeThreshold', 'defaultOpen', 'dismissible', 'fadeFromIndex', 'fixed', 'modal', 'nested', 'open', 'scrollLockTimeout', 'shouldScaleBackground', 'snapPoints'), emits)
const contentProps = toRef(() => props.content) const contentProps = toRef(() => props.content)
const ui = computed(() => tv({ extend: drawer, slots: props.ui })()) // eslint-disable-next-line vue/no-dupe-keys
const ui = drawer()
</script> </script>
<template> <template>
@@ -70,24 +71,24 @@ const ui = computed(() => tv({ extend: drawer, slots: props.ui })())
</DrawerTrigger> </DrawerTrigger>
<DrawerPortal :disabled="!portal"> <DrawerPortal :disabled="!portal">
<DrawerOverlay v-if="overlay" :class="ui.overlay()" /> <DrawerOverlay v-if="overlay" :class="ui.overlay({ class: props.ui?.overlay })" />
<DrawerContent :class="ui.content({ class: props.class })" v-bind="contentProps"> <DrawerContent :class="ui.content({ class: props.class })" v-bind="contentProps">
<slot name="handle"> <slot name="handle">
<div :class="ui.handle()" /> <div :class="ui.handle({ class: props.ui?.handle })" />
</slot> </slot>
<slot name="content"> <slot name="content">
<div :class="ui.container()"> <div :class="ui.container({ class: props.ui?.container })">
<div v-if="!!slots.header || (title || !!slots.title) || (description || !!slots.description)" :class="ui.header()"> <div v-if="!!slots.header || (title || !!slots.title) || (description || !!slots.description)" :class="ui.header({ class: props.ui?.header })">
<slot name="header"> <slot name="header">
<DrawerTitle v-if="title || !!slots.title" :class="ui.title()"> <DrawerTitle v-if="title || !!slots.title" :class="ui.title({ class: props.ui?.title })">
<slot name="title"> <slot name="title">
{{ title }} {{ title }}
</slot> </slot>
</DrawerTitle> </DrawerTitle>
<DrawerDescription v-if="description || !!slots.description" :class="ui.description()"> <DrawerDescription v-if="description || !!slots.description" :class="ui.description({ class: props.ui?.description })">
<slot name="description"> <slot name="description">
{{ description }} {{ description }}
</slot> </slot>
@@ -95,11 +96,11 @@ const ui = computed(() => tv({ extend: drawer, slots: props.ui })())
</slot> </slot>
</div> </div>
<div v-if="!!slots.body" :class="ui.body()"> <div v-if="!!slots.body" :class="ui.body({ class: props.ui?.body })">
<slot name="body" /> <slot name="body" />
</div> </div>
<div v-if="!!slots.footer" :class="ui.footer()"> <div v-if="!!slots.footer" :class="ui.footer({ class: props.ui?.footer })">
<slot name="footer" /> <slot name="footer" />
</div> </div>
</div> </div>

View File

@@ -86,7 +86,7 @@ const contentProps = toRef(() => defu(props.content, { side: 'bottom', sideOffse
const arrowProps = toRef(() => props.arrow as DropdownMenuArrowProps) const arrowProps = toRef(() => props.arrow as DropdownMenuArrowProps)
const proxySlots = omit(slots, ['default']) as Record<string, DropdownMenuSlots<T>[string]> const proxySlots = omit(slots, ['default']) as Record<string, DropdownMenuSlots<T>[string]>
const ui = computed(() => tv({ extend: dropdownMenu, slots: props.ui })({ const ui = computed(() => dropdownMenu({
size: props.size size: props.size
})) }))
</script> </script>
@@ -97,12 +97,19 @@ const ui = computed(() => tv({ extend: dropdownMenu, slots: props.ui })({
<slot :open="open" /> <slot :open="open" />
</DropdownMenuTrigger> </DropdownMenuTrigger>
<UDropdownMenuContent :class="ui.content({ class: props.class })" :ui="ui" v-bind="contentProps" :items="items" :portal="portal"> <UDropdownMenuContent
:class="ui.content({ class: props.class })"
:ui="ui"
:ui-override="props.ui"
v-bind="contentProps"
:items="items"
:portal="portal"
>
<template v-for="(_, name) in proxySlots" #[name]="slotData: any"> <template v-for="(_, name) in proxySlots" #[name]="slotData: any">
<slot :name="name" v-bind="slotData" /> <slot :name="name" v-bind="slotData" />
</template> </template>
<DropdownMenuArrow v-if="!!arrow" v-bind="arrowProps" :class="ui.arrow()" /> <DropdownMenuArrow v-if="!!arrow" v-bind="arrowProps" :class="ui.arrow({ class: props.ui?.arrow })" />
</UDropdownMenuContent> </UDropdownMenuContent>
</DropdownMenuRoot> </DropdownMenuRoot>
</template> </template>

View File

@@ -12,6 +12,7 @@ interface DropdownMenuContentProps<T> extends Omit<RadixDropdownMenuContentProps
sub?: boolean sub?: boolean
class?: any class?: any
ui: typeof dropdownMenu ui: typeof dropdownMenu
uiOverride?: any
} }
interface DropdownMenuContentEmits extends RadixDropdownMenuContentEmits {} interface DropdownMenuContentEmits extends RadixDropdownMenuContentEmits {}
@@ -48,22 +49,22 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
<DefineItemTemplate v-slot="{ item, active, index }"> <DefineItemTemplate v-slot="{ item, active, index }">
<slot :name="item.slot || 'item'" :item="(item as T)" :index="index"> <slot :name="item.slot || 'item'" :item="(item as T)" :index="index">
<slot :name="item.slot ? `${item.slot}-leading`: 'item-leading'" :item="(item as T)" :active="active" :index="index"> <slot :name="item.slot ? `${item.slot}-leading`: 'item-leading'" :item="(item as T)" :active="active" :index="index">
<UAvatar v-if="item.avatar" :size="(ui.itemLeadingAvatarSize() as AvatarProps['size'])" v-bind="item.avatar" :class="ui.itemLeadingAvatar({ active })" /> <UAvatar v-if="item.avatar" :size="(ui.itemLeadingAvatarSize() as AvatarProps['size'])" v-bind="item.avatar" :class="ui.itemLeadingAvatar({ class: uiOverride?.itemLeadingAvatar, active })" />
<UIcon v-else-if="item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ active })" /> <UIcon v-else-if="item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ class: uiOverride?.itemLeadingIcon, active })" />
</slot> </slot>
<span v-if="item.label || !!slots[item.slot ? `${item.slot}-label`: 'item-label']" :class="ui.itemLabel({ active })"> <span v-if="item.label || !!slots[item.slot ? `${item.slot}-label`: 'item-label']" :class="ui.itemLabel({ class: uiOverride?.itemLabel, active })">
<slot :name="item.slot ? `${item.slot}-label`: 'item-label'" :item="(item as T)" :active="active" :index="index"> <slot :name="item.slot ? `${item.slot}-label`: 'item-label'" :item="(item as T)" :active="active" :index="index">
{{ item.label }} {{ item.label }}
</slot> </slot>
<UIcon v-if="item.target === '_blank'" :name="appConfig.ui.icons.external" :class="ui.itemLabelExternalIcon({ active })" /> <UIcon v-if="item.target === '_blank'" :name="appConfig.ui.icons.external" :class="ui.itemLabelExternalIcon({ class: uiOverride?.itemLabelExternalIcon, active })" />
</span> </span>
<span v-if="item.children?.length || item.kbds?.length || !!slots[item.slot ? `${item.slot}-trailing`: 'item-trailing']" :class="ui.itemTrailing()"> <span v-if="item.children?.length || item.kbds?.length || !!slots[item.slot ? `${item.slot}-trailing`: 'item-trailing']" :class="ui.itemTrailing({ class: uiOverride?.itemTrailing })">
<slot :name="item.slot ? `${item.slot}-trailing`: 'item-trailing'" :item="(item as T)" :active="active" :index="index"> <slot :name="item.slot ? `${item.slot}-trailing`: 'item-trailing'" :item="(item as T)" :active="active" :index="index">
<UIcon v-if="item.children?.length" :name="appConfig.ui.icons.chevronRight" :class="ui.itemTrailingIcon({ active })" /> <UIcon v-if="item.children?.length" :name="appConfig.ui.icons.chevronRight" :class="ui.itemTrailingIcon({ class: uiOverride?.itemTrailingIcon, active })" />
<span v-else-if="item.kbds?.length" :class="ui.itemTrailingKbds()"> <span v-else-if="item.kbds?.length" :class="ui.itemTrailingKbds({ class: uiOverride?.itemTrailingKbds })">
<UKbd v-for="(kbd, kbdIndex) in item.kbds" :key="kbdIndex" :size="(ui.itemTrailingKbdsSize() as KbdProps['size'])" v-bind="typeof kbd === 'string' ? { value: kbd } : kbd" /> <UKbd v-for="(kbd, kbdIndex) in item.kbds" :key="kbdIndex" :size="(ui.itemTrailingKbdsSize() as KbdProps['size'])" v-bind="typeof kbd === 'string' ? { value: kbd } : kbd" />
</span> </span>
</slot> </slot>
@@ -73,12 +74,12 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
<DropdownMenu.Portal :disabled="!portal"> <DropdownMenu.Portal :disabled="!portal">
<component :is="sub ? DropdownMenu.SubContent : DropdownMenu.Content" :class="props.class" v-bind="contentProps"> <component :is="sub ? DropdownMenu.SubContent : DropdownMenu.Content" :class="props.class" v-bind="contentProps">
<DropdownMenu.Group v-for="(group, groupIndex) in groups" :key="`group-${groupIndex}`" :class="ui.group()"> <DropdownMenu.Group v-for="(group, groupIndex) in groups" :key="`group-${groupIndex}`" :class="ui.group({ class: uiOverride?.group })">
<template v-for="(item, index) in group" :key="`group-${groupIndex}-${index}`"> <template v-for="(item, index) in group" :key="`group-${groupIndex}-${index}`">
<DropdownMenu.Label v-if="item.type === 'label'" :class="ui.label()"> <DropdownMenu.Label v-if="item.type === 'label'" :class="ui.label({ class: uiOverride?.label })">
<ReuseItemTemplate :item="item" :index="index" /> <ReuseItemTemplate :item="item" :index="index" />
</DropdownMenu.Label> </DropdownMenu.Label>
<DropdownMenu.Separator v-else-if="item.type === 'separator'" :class="ui.separator()" /> <DropdownMenu.Separator v-else-if="item.type === 'separator'" :class="ui.separator({ class: uiOverride?.separator })" />
<DropdownMenu.Sub v-else-if="item?.children?.length"> <DropdownMenu.Sub v-else-if="item?.children?.length">
<DropdownMenu.SubTrigger <DropdownMenu.SubTrigger
as="button" as="button"
@@ -87,7 +88,7 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
:open="item.open" :open="item.open"
:default-open="item.defaultOpen" :default-open="item.defaultOpen"
:text-value="item.label" :text-value="item.label"
:class="ui.item()" :class="ui.item({ class: uiOverride?.item })"
> >
<ReuseItemTemplate :item="item" :index="index" /> <ReuseItemTemplate :item="item" :index="index" />
</DropdownMenu.SubTrigger> </DropdownMenu.SubTrigger>
@@ -96,6 +97,7 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
sub sub
:class="props.class" :class="props.class"
:ui="ui" :ui="ui"
:ui-override="uiOverride"
:portal="portal" :portal="portal"
:items="item.children" :items="item.children"
side="right" side="right"
@@ -111,7 +113,7 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
</DropdownMenu.Sub> </DropdownMenu.Sub>
<DropdownMenu.Item v-else as-child :disabled="item.disabled" :text-value="item.label" @select="item.select"> <DropdownMenu.Item v-else as-child :disabled="item.disabled" :text-value="item.label" @select="item.select">
<ULink v-slot="{ active, ...slotProps }" v-bind="pickLinkProps(item as Omit<DropdownMenuItem, 'type'>)" custom> <ULink v-slot="{ active, ...slotProps }" v-bind="pickLinkProps(item as Omit<DropdownMenuItem, 'type'>)" custom>
<ULinkBase v-bind="slotProps" :class="ui.item({ active })"> <ULinkBase v-bind="slotProps" :class="ui.item({ class: uiOverride?.item, active })">
<ReuseItemTemplate :item="item" :active="active" :index="index" /> <ReuseItemTemplate :item="item" :active="active" :index="index" />
</ULinkBase> </ULinkBase>
</ULink> </ULink>

View File

@@ -44,7 +44,7 @@ import type { FormError } from '../types/form'
const props = defineProps<FormFieldProps>() const props = defineProps<FormFieldProps>()
const slots = defineSlots<FormFieldSlots>() const slots = defineSlots<FormFieldSlots>()
const ui = computed(() => tv({ extend: formField, slots: props.ui })({ const ui = computed(() => formField({
size: props.size, size: props.size,
required: props.required required: props.required
})) }))
@@ -72,21 +72,21 @@ provide(formFieldInjectionKey, computed(() => ({
<template> <template>
<div :class="ui.root({ class: props.class })"> <div :class="ui.root({ class: props.class })">
<div :class="ui.wrapper()"> <div :class="ui.wrapper({ class: props.ui?.wrapper })">
<div v-if="label || !!slots.label" :class="ui.labelWrapper()"> <div v-if="label || !!slots.label" :class="ui.labelWrapper({ class: props.ui?.labelWrapper })">
<Label :for="id" :class="ui.label()"> <Label :for="id" :class="ui.label({ class: props.ui?.label })">
<slot name="label" :label="label"> <slot name="label" :label="label">
{{ label }} {{ label }}
</slot> </slot>
</Label> </Label>
<span v-if="hint || !!slots.hint" :class="ui.hint()"> <span v-if="hint || !!slots.hint" :class="ui.hint({ class: props.ui?.hint })">
<slot name="hint" :hint="hint"> <slot name="hint" :hint="hint">
{{ hint }} {{ hint }}
</slot> </slot>
</span> </span>
</div> </div>
<p v-if="description || !!slots.description" :class="ui.description()"> <p v-if="description || !!slots.description" :class="ui.description({ class: props.ui?.description })">
<slot name="description" :description="description"> <slot name="description" :description="description">
{{ description }} {{ description }}
</slot> </slot>
@@ -96,12 +96,12 @@ provide(formFieldInjectionKey, computed(() => ({
<div :class="[label && ui.container()]"> <div :class="[label && ui.container()]">
<slot :error="error" /> <slot :error="error" />
<p v-if="(typeof error === 'string' && error) || !!slots.error" :class="ui.error()"> <p v-if="(typeof error === 'string' && error) || !!slots.error" :class="ui.error({ class: props.ui?.error })">
<slot name="error" :error="error"> <slot name="error" :error="error">
{{ error }} {{ error }}
</slot> </slot>
</p> </p>
<p v-else-if="help || !!slots.help" :class="ui.help()"> <p v-else-if="help || !!slots.help" :class="ui.help({ class: props.ui?.help })">
<slot name="help" :help="help"> <slot name="help" :help="help">
{{ help }} {{ help }}
</slot> </slot>

View File

@@ -65,7 +65,7 @@ const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponen
const inputSize = computed(() => buttonGroupSize.value || formGroupSize.value) const inputSize = computed(() => buttonGroupSize.value || formGroupSize.value)
const ui = computed(() => tv({ extend: input, slots: props.ui })({ const ui = computed(() => input({
type: props.type as InputVariants['type'], type: props.type as InputVariants['type'],
color: color.value, color: color.value,
variant: props.variant, variant: props.variant,
@@ -141,7 +141,7 @@ onMounted(() => {
:value="modelValue" :value="modelValue"
:name="name" :name="name"
:placeholder="placeholder" :placeholder="placeholder"
:class="ui.base()" :class="ui.base({ class: props.ui?.base })"
:disabled="disabled" :disabled="disabled"
:required="required" :required="required"
v-bind="$attrs" v-bind="$attrs"
@@ -152,15 +152,15 @@ onMounted(() => {
<slot /> <slot />
<span v-if="isLeading || !!slots.leading" :class="ui.leading()"> <span v-if="isLeading || !!slots.leading" :class="ui.leading({ class: props.ui?.leading })">
<slot name="leading"> <slot name="leading">
<UIcon v-if="leadingIconName" :name="leadingIconName" :class="ui.leadingIcon()" /> <UIcon v-if="leadingIconName" :name="leadingIconName" :class="ui.leadingIcon({ class: props.ui?.leadingIcon })" />
</slot> </slot>
</span> </span>
<span v-if="isTrailing || !!slots.trailing" :class="ui.trailing()"> <span v-if="isTrailing || !!slots.trailing" :class="ui.trailing({ class: props.ui?.trailing })">
<slot name="trailing"> <slot name="trailing">
<UIcon v-if="trailingIconName" :name="trailingIconName" :class="ui.trailingIcon()" /> <UIcon v-if="trailingIconName" :name="trailingIconName" :class="ui.trailingIcon({ class: props.ui?.trailingIcon })" />
</slot> </slot>
</span> </span>
</div> </div>

View File

@@ -137,7 +137,7 @@ const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponen
const inputSize = computed(() => buttonGroupSize.value || formGroupSize.value) const inputSize = computed(() => buttonGroupSize.value || formGroupSize.value)
const ui = computed(() => tv({ extend: inputMenu, slots: props.ui })({ const ui = computed(() => inputMenu({
color: color.value, color: color.value,
variant: props.variant, variant: props.variant,
size: inputSize?.value, size: inputSize?.value,
@@ -233,7 +233,7 @@ function onUpdateOpen(value: boolean) {
@update:open="onUpdateOpen" @update:open="onUpdateOpen"
@keydown.enter="$event.preventDefault()" @keydown.enter="$event.preventDefault()"
> >
<ComboboxAnchor :as-child="!multiple" :class="ui.base()"> <ComboboxAnchor :as-child="!multiple" :class="ui.base({ class: props.ui?.base })">
<TagsInputRoot <TagsInputRoot
v-if="multiple" v-if="multiple"
v-slot="{ modelValue: tags }: { modelValue: AcceptableValue[] }" v-slot="{ modelValue: tags }: { modelValue: AcceptableValue[] }"
@@ -243,16 +243,16 @@ function onUpdateOpen(value: boolean) {
as-child as-child
@blur="onBlur" @blur="onBlur"
> >
<TagsInputItem v-for="(item, index) in tags" :key="index" :value="(item as string)" :class="ui.tagsItem()"> <TagsInputItem v-for="(item, index) in tags" :key="index" :value="(item as string)" :class="ui.tagsItem({ class: props.ui?.tagsItem })">
<TagsInputItemText :class="ui.tagsItemText()"> <TagsInputItemText :class="ui.tagsItemText({ class: props.ui?.tagsItemText })">
<slot name="tags-item-text" :item="(item as T)" :index="index"> <slot name="tags-item-text" :item="(item as T)" :index="index">
{{ typeof item === 'object' ? item.label : item }} {{ typeof item === 'object' ? item.label : item }}
</slot> </slot>
</TagsInputItemText> </TagsInputItemText>
<TagsInputItemDelete :class="ui.tagsItemDelete()" :disabled="disabled"> <TagsInputItemDelete :class="ui.tagsItemDelete({ class: props.ui?.tagsItemDelete })" :disabled="disabled">
<slot name="tags-item-delete" :item="(item as T)" :index="index"> <slot name="tags-item-delete" :item="(item as T)" :index="index">
<UIcon :name="deleteIcon || appConfig.ui.icons.close" :class="ui.tagsItemDeleteIcon()" /> <UIcon :name="deleteIcon || appConfig.ui.icons.close" :class="ui.tagsItemDeleteIcon({ class: props.ui?.tagsItemDeleteIcon })" />
</slot> </slot>
</TagsInputItemDelete> </TagsInputItemDelete>
</TagsInputItem> </TagsInputItem>
@@ -263,7 +263,7 @@ function onUpdateOpen(value: boolean) {
v-bind="$attrs" v-bind="$attrs"
:placeholder="placeholder" :placeholder="placeholder"
:required="required" :required="required"
:class="ui.tagsInput()" :class="ui.tagsInput({ class: props.ui?.tagsInput })"
@keydown.enter.prevent @keydown.enter.prevent
/> />
</ComboboxInput> </ComboboxInput>
@@ -276,66 +276,66 @@ function onUpdateOpen(value: boolean) {
:type="type" :type="type"
:placeholder="placeholder" :placeholder="placeholder"
:required="required" :required="required"
:class="ui.base()" :class="ui.base({ class: props.ui?.base })"
@blur="onBlur" @blur="onBlur"
/> />
<span v-if="isLeading || !!slots.leading" :class="ui.leading()"> <span v-if="isLeading || !!slots.leading" :class="ui.leading({ class: props.ui?.leading })">
<slot name="leading" :model-value="(modelValue as T)" :open="open" :ui="ui"> <slot name="leading" :model-value="(modelValue as T)" :open="open" :ui="ui">
<UIcon v-if="leadingIconName" :name="leadingIconName" :class="ui.leadingIcon()" /> <UIcon v-if="leadingIconName" :name="leadingIconName" :class="ui.leadingIcon({ class: props.ui?.leadingIcon })" />
</slot> </slot>
</span> </span>
<ComboboxTrigger v-if="isTrailing || !!slots.trailing" :class="ui.trailing()"> <ComboboxTrigger v-if="isTrailing || !!slots.trailing" :class="ui.trailing({ class: props.ui?.trailing })">
<slot name="trailing" :model-value="(modelValue as T)" :open="open" :ui="ui"> <slot name="trailing" :model-value="(modelValue as T)" :open="open" :ui="ui">
<UIcon v-if="trailingIconName" :name="trailingIconName" :class="ui.trailingIcon()" /> <UIcon v-if="trailingIconName" :name="trailingIconName" :class="ui.trailingIcon({ class: props.ui?.trailingIcon })" />
</slot> </slot>
</ComboboxTrigger> </ComboboxTrigger>
</ComboboxAnchor> </ComboboxAnchor>
<ComboboxPortal :disabled="!portal"> <ComboboxPortal :disabled="!portal">
<ComboboxContent :class="ui.content()" v-bind="contentProps"> <ComboboxContent :class="ui.content({ class: props.ui?.content })" v-bind="contentProps">
<ComboboxEmpty :class="ui.empty()"> <ComboboxEmpty :class="ui.empty({ class: props.ui?.empty })">
<slot name="empty" :search-term="searchTerm"> <slot name="empty" :search-term="searchTerm">
{{ searchTerm ? `No results for ${searchTerm}` : 'No results' }} {{ searchTerm ? `No results for ${searchTerm}` : 'No results' }}
</slot> </slot>
</ComboboxEmpty> </ComboboxEmpty>
<ComboboxViewport :class="ui.viewport()"> <ComboboxViewport :class="ui.viewport({ class: props.ui?.viewport })">
<ComboboxGroup v-for="(group, groupIndex) in groups" :key="`group-${groupIndex}`" :class="ui.group()"> <ComboboxGroup v-for="(group, groupIndex) in groups" :key="`group-${groupIndex}`" :class="ui.group({ class: props.ui?.group })">
<template v-for="(item, index) in group" :key="`group-${groupIndex}-${index}`"> <template v-for="(item, index) in group" :key="`group-${groupIndex}-${index}`">
<ComboboxLabel v-if="item?.type === 'label'" :class="ui.label()"> <ComboboxLabel v-if="item?.type === 'label'" :class="ui.label({ class: props.ui?.label })">
{{ item.label }} {{ item.label }}
</ComboboxLabel> </ComboboxLabel>
<ComboboxSeparator v-else-if="item?.type === 'separator'" :class="ui.separator()" /> <ComboboxSeparator v-else-if="item?.type === 'separator'" :class="ui.separator({ class: props.ui?.separator })" />
<ComboboxItem v-else :class="ui.item()" :disabled="item.disabled" :value="item"> <ComboboxItem v-else :class="ui.item({ class: props.ui?.item })" :disabled="item.disabled" :value="item">
<slot name="item" :item="(item as T)" :index="index"> <slot name="item" :item="(item as T)" :index="index">
<slot name="item-leading" :item="(item as T)" :index="index"> <slot name="item-leading" :item="(item as T)" :index="index">
<UAvatar v-if="item.avatar" size="2xs" v-bind="item.avatar" :class="ui.itemLeadingAvatar()" /> <UAvatar v-if="item.avatar" size="2xs" v-bind="item.avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar })" />
<UIcon v-else-if="item.icon" :name="item.icon" :class="ui.itemLeadingIcon()" /> <UIcon v-else-if="item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ class: props.ui?.itemLeadingIcon })" />
<UChip <UChip
v-else-if="item.chip" v-else-if="item.chip"
size="md" size="md"
inset inset
standalone standalone
v-bind="item.chip" v-bind="item.chip"
:class="ui.itemLeadingChip()" :class="ui.itemLeadingChip({ class: props.ui?.itemLeadingChip })"
/> />
</slot> </slot>
<span :class="ui.itemLabel()"> <span :class="ui.itemLabel({ class: props.ui?.itemLabel })">
<slot name="item-label" :item="(item as T)" :index="index"> <slot name="item-label" :item="(item as T)" :index="index">
{{ displayValue(item as T) }} {{ displayValue(item as T) }}
</slot> </slot>
</span> </span>
<span :class="ui.itemTrailing()"> <span :class="ui.itemTrailing({ class: props.ui?.itemTrailing })">
<slot name="item-trailing" :item="(item as T)" :index="index" /> <slot name="item-trailing" :item="(item as T)" :index="index" />
<ComboboxItemIndicator as-child> <ComboboxItemIndicator as-child>
<UIcon :name="selectedIcon || appConfig.ui.icons.check" :class="ui.itemTrailingIcon()" /> <UIcon :name="selectedIcon || appConfig.ui.icons.check" :class="ui.itemTrailingIcon({ class: props.ui?.itemTrailingIcon })" />
</ComboboxItemIndicator> </ComboboxItemIndicator>
</span> </span>
</slot> </slot>

View File

@@ -48,7 +48,7 @@ export interface ModalSlots {
header(props?: {}): any header(props?: {}): any
title(props?: {}): any title(props?: {}): any
description(props?: {}): any description(props?: {}): any
close(props: { class: string }): any close(props: { ui: any }): any
body(props?: {}): any body(props?: {}): any
footer(props?: {}): any footer(props?: {}): any
} }
@@ -85,7 +85,7 @@ const contentEvents = computed(() => {
const appConfig = useAppConfig() const appConfig = useAppConfig()
const ui = computed(() => tv({ extend: modal, slots: props.ui })({ const ui = computed(() => modal({
transition: props.transition, transition: props.transition,
fullscreen: props.fullscreen fullscreen: props.fullscreen
})) }))
@@ -98,26 +98,26 @@ const ui = computed(() => tv({ extend: modal, slots: props.ui })({
</DialogTrigger> </DialogTrigger>
<DialogPortal :disabled="!portal"> <DialogPortal :disabled="!portal">
<DialogOverlay v-if="overlay" :class="ui.overlay()" /> <DialogOverlay v-if="overlay" :class="ui.overlay({ class: props.ui?.overlay })" />
<DialogContent :class="ui.content({ class: props.class })" v-bind="contentProps" v-on="contentEvents"> <DialogContent :class="ui.content({ class: props.class })" v-bind="contentProps" v-on="contentEvents">
<slot name="content"> <slot name="content">
<div v-if="!!slots.header || (title || !!slots.title) || (description || !!slots.description) || (close || !!slots.close)" :class="ui.header()"> <div v-if="!!slots.header || (title || !!slots.title) || (description || !!slots.description) || (close || !!slots.close)" :class="ui.header({ class: props.ui?.header })">
<slot name="header"> <slot name="header">
<DialogTitle v-if="title || !!slots.title" :class="ui.title()"> <DialogTitle v-if="title || !!slots.title" :class="ui.title({ class: props.ui?.title })">
<slot name="title"> <slot name="title">
{{ title }} {{ title }}
</slot> </slot>
</DialogTitle> </DialogTitle>
<DialogDescription v-if="description || !!slots.description" :class="ui.description()"> <DialogDescription v-if="description || !!slots.description" :class="ui.description({ class: props.ui?.description })">
<slot name="description"> <slot name="description">
{{ description }} {{ description }}
</slot> </slot>
</DialogDescription> </DialogDescription>
<DialogClose as-child> <DialogClose as-child>
<slot name="close" :class="ui.close()"> <slot name="close" :ui="ui">
<UButton <UButton
v-if="close" v-if="close"
:icon="closeIcon || appConfig.ui.icons.close" :icon="closeIcon || appConfig.ui.icons.close"
@@ -126,18 +126,18 @@ const ui = computed(() => tv({ extend: modal, slots: props.ui })({
variant="ghost" variant="ghost"
aria-label="Close" aria-label="Close"
v-bind="typeof close === 'object' ? close : undefined" v-bind="typeof close === 'object' ? close : undefined"
:class="ui.close()" :class="ui.close({ class: props.ui?.close })"
/> />
</slot> </slot>
</DialogClose> </DialogClose>
</slot> </slot>
</div> </div>
<div v-if="!!slots.body" :class="ui.body()"> <div v-if="!!slots.body" :class="ui.body({ class: props.ui?.body })">
<slot name="body" /> <slot name="body" />
</div> </div>
<div v-if="!!slots.footer" :class="ui.footer()"> <div v-if="!!slots.footer" :class="ui.footer({ class: props.ui?.footer })">
<slot name="footer" /> <slot name="footer" />
</div> </div>
</slot> </slot>

View File

@@ -93,7 +93,7 @@ const slots = defineSlots<NavigationMenuSlots<T>>()
const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'modelValue', 'defaultValue', 'delayDuration', 'skipDelayDuration', 'orientation'), emits) const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'modelValue', 'defaultValue', 'delayDuration', 'skipDelayDuration', 'orientation'), emits)
const contentProps = toRef(() => props.content) const contentProps = toRef(() => props.content)
const ui = computed(() => tv({ extend: navigationMenu, slots: props.ui })({ const ui = computed(() => navigationMenu({
orientation: props.orientation, orientation: props.orientation,
color: props.color, color: props.color,
variant: props.variant, variant: props.variant,
@@ -107,8 +107,8 @@ const lists = computed(() => props.items?.length ? (Array.isArray(props.items[0]
<template> <template>
<NavigationMenuRoot v-bind="rootProps" :class="ui.root({ class: props.class })"> <NavigationMenuRoot v-bind="rootProps" :class="ui.root({ class: props.class })">
<template v-for="(list, listIndex) in lists" :key="`list-${listIndex}`"> <template v-for="(list, listIndex) in lists" :key="`list-${listIndex}`">
<NavigationMenuList :class="ui.list()"> <NavigationMenuList :class="ui.list({ class: props.ui?.list })">
<NavigationMenuItem v-for="(item, index) in list" :key="`list-${listIndex}-${index}`" :value="item.value || String(index)" :class="ui.item()"> <NavigationMenuItem v-for="(item, index) in list" :key="`list-${listIndex}-${index}`" :value="item.value || String(index)" :class="ui.item({ class: props.ui?.item })">
<ULink v-slot="{ active, ...slotProps }" v-bind="pickLinkProps(item)" custom> <ULink v-slot="{ active, ...slotProps }" v-bind="pickLinkProps(item)" custom>
<component <component
:is="item.children?.length ? NavigationMenuTrigger : NavigationMenuLink" :is="item.children?.length ? NavigationMenuTrigger : NavigationMenuLink"
@@ -117,52 +117,52 @@ const lists = computed(() => props.items?.length ? (Array.isArray(props.items[0]
:active="active" :active="active"
@select="item.select" @select="item.select"
> >
<ULinkBase v-bind="slotProps" :class="ui.link({ active, disabled: !!item.disabled })"> <ULinkBase v-bind="slotProps" :class="ui.link({ class: props.ui?.link, active, disabled: !!item.disabled })">
<slot :name="item.slot || 'item'" :item="item" :index="index"> <slot :name="item.slot || 'item'" :item="item" :index="index">
<slot :name="item.slot ? `${item.slot}-leading`: 'item-leading'" :item="item" :active="active" :index="index"> <slot :name="item.slot ? `${item.slot}-leading`: 'item-leading'" :item="item" :active="active" :index="index">
<UAvatar v-if="item.avatar" size="2xs" v-bind="item.avatar" :class="ui.linkLeadingAvatar({ active, disabled: !!item.disabled })" /> <UAvatar v-if="item.avatar" size="2xs" v-bind="item.avatar" :class="ui.linkLeadingAvatar({ class: props.ui?.linkLeadingAvatar, active, disabled: !!item.disabled })" />
<UIcon v-else-if="item.icon" :name="item.icon" :class="ui.linkLeadingIcon({ active, disabled: !!item.disabled })" /> <UIcon v-else-if="item.icon" :name="item.icon" :class="ui.linkLeadingIcon({ class: props.ui?.linkLeadingIcon, active, disabled: !!item.disabled })" />
</slot> </slot>
<span v-if="item.label || !!slots[item.slot ? `${item.slot}-label`: 'item-label']" :class="ui.linkLabel()"> <span v-if="item.label || !!slots[item.slot ? `${item.slot}-label`: 'item-label']" :class="ui.linkLabel({ class: props.ui?.linkLabel })">
<slot :name="item.slot ? `${item.slot}-label`: 'item-label'" :item="item" :active="active" :index="index"> <slot :name="item.slot ? `${item.slot}-label`: 'item-label'" :item="item" :active="active" :index="index">
{{ item.label }} {{ item.label }}
</slot> </slot>
<UIcon v-if="item.target === '_blank'" :name="appConfig.ui.icons.external" :class="ui.linkLabelExternalIcon({ active })" /> <UIcon v-if="item.target === '_blank'" :name="appConfig.ui.icons.external" :class="ui.linkLabelExternalIcon({ class: props.ui?.linkLabelExternalIcon, active })" />
</span> </span>
<span v-if="item.badge || (item.children?.length && orientation === 'horizontal') || !!slots[item.slot ? `${item.slot}-trailing`: 'item-trailing']" :class="ui.linkTrailing()"> <span v-if="item.badge || (item.children?.length && orientation === 'horizontal') || !!slots[item.slot ? `${item.slot}-trailing`: 'item-trailing']" :class="ui.linkTrailing({ class: props.ui?.linkTrailing })">
<slot :name="item.slot ? `${item.slot}-trailing`: 'item-trailing'" :item="item" :active="active" :index="index"> <slot :name="item.slot ? `${item.slot}-trailing`: 'item-trailing'" :item="item" :active="active" :index="index">
<UBadge <UBadge
v-if="item.badge" v-if="item.badge"
color="white" color="white"
size="sm" size="sm"
v-bind="(typeof item.badge === 'string' || typeof item.badge === 'number') ? { label: item.badge } : item.badge" v-bind="(typeof item.badge === 'string' || typeof item.badge === 'number') ? { label: item.badge } : item.badge"
:class="ui.linkTrailingBadge()" :class="ui.linkTrailingBadge({ class: props.ui?.linkTrailingBadge })"
/> />
<UIcon v-if="item.children?.length && orientation === 'horizontal'" :name="item.trailingIcon || trailingIcon || appConfig.ui.icons.chevronDown" :class="ui.linkTrailingIcon({ active })" /> <UIcon v-if="item.children?.length && orientation === 'horizontal'" :name="item.trailingIcon || trailingIcon || appConfig.ui.icons.chevronDown" :class="ui.linkTrailingIcon({ class: props.ui?.linkTrailingIcon, active })" />
</slot> </slot>
</span> </span>
</slot> </slot>
</ULinkBase> </ULinkBase>
</component> </component>
<NavigationMenuContent v-if="item.children?.length && orientation === 'horizontal'" v-bind="contentProps" :class="ui.content()"> <NavigationMenuContent v-if="item.children?.length && orientation === 'horizontal'" v-bind="contentProps" :class="ui.content({ class: props.ui?.content })">
<ul :class="ui.childList()"> <ul :class="ui.childList({ class: props.ui?.childList })">
<li v-for="(childItem, childIndex) in item.children" :key="childIndex" :class="ui.childItem()"> <li v-for="(childItem, childIndex) in item.children" :key="childIndex" :class="ui.childItem({ class: props.ui?.childItem })">
<ULink v-slot="{ active: childActive, ...childSlotProps }" v-bind="pickLinkProps(childItem)" custom> <ULink v-slot="{ active: childActive, ...childSlotProps }" v-bind="pickLinkProps(childItem)" custom>
<NavigationMenuLink as-child :active="childActive" @select="childItem.select"> <NavigationMenuLink as-child :active="childActive" @select="childItem.select">
<ULinkBase v-bind="childSlotProps" :class="ui.childLink({ active: childActive })"> <ULinkBase v-bind="childSlotProps" :class="ui.childLink({ class: props.ui?.childLink, active: childActive })">
<UIcon v-if="childItem.icon" :name="childItem.icon" :class="ui.childLinkIcon({ active: childActive })" /> <UIcon v-if="childItem.icon" :name="childItem.icon" :class="ui.childLinkIcon({ class: props.ui?.childLinkIcon, active: childActive })" />
<div :class="ui.childLinkWrapper()"> <div :class="ui.childLinkWrapper({ class: props.ui?.childLinkWrapper })">
<p :class="ui.childLinkLabel({ active: childActive })"> <p :class="ui.childLinkLabel({ class: props.ui?.childLinkLabel, active: childActive })">
{{ childItem.label }} {{ childItem.label }}
<UIcon v-if="childItem.target === '_blank'" :name="appConfig.ui.icons.external" :class="ui.childLinkLabelExternalIcon({ active: childActive })" /> <UIcon v-if="childItem.target === '_blank'" :name="appConfig.ui.icons.external" :class="ui.childLinkLabelExternalIcon({ class: props.ui?.childLinkLabelExternalIcon, active: childActive })" />
</p> </p>
<p v-if="childItem.description" :class="ui.childLinkDescription({ active: childActive })"> <p v-if="childItem.description" :class="ui.childLinkDescription({ class: props.ui?.childLinkDescription, active: childActive })">
{{ childItem.description }} {{ childItem.description }}
</p> </p>
</div> </div>
@@ -176,15 +176,15 @@ const lists = computed(() => props.items?.length ? (Array.isArray(props.items[0]
</NavigationMenuItem> </NavigationMenuItem>
</NavigationMenuList> </NavigationMenuList>
<div v-if="orientation === 'vertical' && listIndex < lists.length - 1" :class="ui.separator()" /> <div v-if="orientation === 'vertical' && listIndex < lists.length - 1" :class="ui.separator({ class: props.ui?.separator })" />
</template> </template>
<div v-if="orientation === 'horizontal'" :class="ui.viewportWrapper()"> <div v-if="orientation === 'horizontal'" :class="ui.viewportWrapper({ class: props.ui?.viewportWrapper })">
<NavigationMenuIndicator v-if="arrow" :class="ui.indicator()"> <NavigationMenuIndicator v-if="arrow" :class="ui.indicator({ class: props.ui?.indicator })">
<div :class="ui.arrow()" /> <div :class="ui.arrow({ class: props.ui?.arrow })" />
</NavigationMenuIndicator> </NavigationMenuIndicator>
<NavigationMenuViewport :class="ui.viewport()" /> <NavigationMenuViewport :class="ui.viewport({ class: props.ui?.viewport })" />
</div> </div>
</NavigationMenuRoot> </NavigationMenuRoot>
</template> </template>

View File

@@ -100,7 +100,6 @@ export interface PaginationSlots {
</script> </script>
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue'
import { PaginationRoot, PaginationList, PaginationListItem, PaginationFirst, PaginationPrev, PaginationEllipsis, PaginationNext, PaginationLast, useForwardPropsEmits } from 'radix-vue' import { PaginationRoot, PaginationList, PaginationListItem, PaginationFirst, PaginationPrev, PaginationEllipsis, PaginationNext, PaginationLast, useForwardPropsEmits } from 'radix-vue'
import { reactivePick } from '@vueuse/core' import { reactivePick } from '@vueuse/core'
import { useAppConfig } from '#imports' import { useAppConfig } from '#imports'
@@ -119,12 +118,13 @@ const appConfig = useAppConfig()
const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'defaultPage', 'disabled', 'itemsPerPage', 'page', 'showEdges', 'siblingCount', 'total'), emits) const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'defaultPage', 'disabled', 'itemsPerPage', 'page', 'showEdges', 'siblingCount', 'total'), emits)
const ui = computed(() => tv({ extend: pagination, slots: props.ui })()) // eslint-disable-next-line vue/no-dupe-keys
const ui = pagination()
</script> </script>
<template> <template>
<PaginationRoot v-slot="{ page, pageCount }" v-bind="rootProps" :class="ui.root({ class: props.class })"> <PaginationRoot v-slot="{ page, pageCount }" v-bind="rootProps" :class="ui.root({ class: props.class })">
<PaginationList v-slot="{ items }" :class="ui.list()"> <PaginationList v-slot="{ items }" :class="ui.list({ class: props.ui?.list })">
<PaginationFirst v-if="showControls || !!slots.first" as-child> <PaginationFirst v-if="showControls || !!slots.first" as-child>
<slot name="first"> <slot name="first">
<UButton :color="color" :variant="variant" :size="size" :icon="firstIcon || appConfig.ui.icons.chevronDoubleLeft" :to="to?.(1)" /> <UButton :color="color" :variant="variant" :size="size" :icon="firstIcon || appConfig.ui.icons.chevronDoubleLeft" :to="to?.(1)" />
@@ -153,7 +153,7 @@ const ui = computed(() => tv({ extend: pagination, slots: props.ui })())
<PaginationEllipsis v-else :key="item.type" :index="index" as-child> <PaginationEllipsis v-else :key="item.type" :index="index" as-child>
<slot name="ellipsis"> <slot name="ellipsis">
<UButton :color="color" :variant="variant" :size="size" :icon="ellipsisIcon || appConfig.ui.icons.ellipsis" :class="ui.ellipsis()" /> <UButton :color="color" :variant="variant" :size="size" :icon="ellipsisIcon || appConfig.ui.icons.ellipsis" :class="ui.ellipsis({ class: props.ui?.ellipsis })" />
</slot> </slot>
</PaginationEllipsis> </PaginationEllipsis>
</template> </template>

View File

@@ -63,7 +63,10 @@ const rootProps = useForwardPropsEmits(pick, emits)
const contentProps = toRef(() => defu(props.content, { side: 'bottom', sideOffset: 8 }) as PopoverContentProps) const contentProps = toRef(() => defu(props.content, { side: 'bottom', sideOffset: 8 }) as PopoverContentProps)
const arrowProps = toRef(() => props.arrow as PopoverArrowProps) const arrowProps = toRef(() => props.arrow as PopoverArrowProps)
const ui = computed(() => tv({ extend: popover, slots: props.ui })({ side: contentProps.value.side })) // eslint-disable-next-line vue/no-dupe-keys
const ui = computed(() => popover({
side: contentProps.value.side
}))
const Component = computed(() => props.mode === 'hover' ? HoverCard : Popover) const Component = computed(() => props.mode === 'hover' ? HoverCard : Popover)
</script> </script>
@@ -78,7 +81,7 @@ const Component = computed(() => props.mode === 'hover' ? HoverCard : Popover)
<Component.Content v-bind="contentProps" :class="ui.content({ class: props.class })"> <Component.Content v-bind="contentProps" :class="ui.content({ class: props.class })">
<slot name="content" /> <slot name="content" />
<Component.Arrow v-if="!!arrow" v-bind="arrowProps" :class="ui.arrow()" /> <Component.Arrow v-if="!!arrow" v-bind="arrowProps" :class="ui.arrow({ class: props.ui?.arrow })" />
</Component.Content> </Component.Content>
</Component.Portal> </Component.Portal>
</Component.Root> </Component.Root>

View File

@@ -129,7 +129,7 @@ function stepVariant(index: number | string) {
return 'other' return 'other'
} }
const ui = computed(() => tv({ extend: progress, slots: props.ui })({ const ui = computed(() => progress({
animation: props.animation, animation: props.animation,
size: props.size, size: props.size,
color: props.color, color: props.color,
@@ -140,18 +140,18 @@ const ui = computed(() => tv({ extend: progress, slots: props.ui })({
<template> <template>
<div :class="ui.root({ class: props.class })"> <div :class="ui.root({ class: props.class })">
<div v-if="!isIndeterminate && (status || $slots.status)" :class="ui.status()" :style="statusStyle"> <div v-if="!isIndeterminate && (status || $slots.status)" :class="ui.status({ class: props.ui?.status })" :style="statusStyle">
<slot name="status" :percent="percent"> <slot name="status" :percent="percent">
{{ percent }}% {{ percent }}%
</slot> </slot>
</div> </div>
<ProgressRoot v-bind="rootProps" :max="realMax" :class="ui.base()" style="transform: translateZ(0)"> <ProgressRoot v-bind="rootProps" :max="realMax" :class="ui.base({ class: props.ui?.base })" style="transform: translateZ(0)">
<ProgressIndicator :class="ui.indicator()" :style="indicatorStyle" /> <ProgressIndicator :class="ui.indicator({ class: props.ui?.indicator })" :style="indicatorStyle" />
</ProgressRoot> </ProgressRoot>
<div v-if="hasSteps" :class="ui.steps()"> <div v-if="hasSteps" :class="ui.steps({ class: props.ui?.steps })">
<div v-for="(step, index) in max" :key="index" :class="ui.step({ step: stepVariant(index) })"> <div v-for="(step, index) in max" :key="index" :class="ui.step({ class: props.ui?.step, step: stepVariant(index) })">
<slot :name="`step-${index}`" :step="step"> <slot :name="`step-${index}`" :step="step">
{{ step }} {{ step }}
</slot> </slot>

View File

@@ -50,7 +50,7 @@ export interface RadioGroupSlots<T> {
</script> </script>
<script setup lang="ts" generic="T extends RadioGroupItem | AcceptableValue"> <script setup lang="ts" generic="T extends RadioGroupItem | AcceptableValue">
import { ref, computed } from 'vue' import { computed } from 'vue'
import { RadioGroupRoot, RadioGroupItem, RadioGroupIndicator, Label, useForwardPropsEmits } from 'radix-vue' import { RadioGroupRoot, RadioGroupItem, RadioGroupIndicator, Label, useForwardPropsEmits } from 'radix-vue'
import { reactivePick } from '@vueuse/core' import { reactivePick } from '@vueuse/core'
import { useId, useFormField } from '#imports' import { useId, useFormField } from '#imports'
@@ -66,7 +66,7 @@ const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'modelValue', '
const { emitFormChange, emitFormInput, color, name, size, id: _id, disabled } = useFormField<RadioGroupProps<T>>(props) const { emitFormChange, emitFormInput, color, name, size, id: _id, disabled } = useFormField<RadioGroupProps<T>>(props)
const id = _id.value ?? useId() const id = _id.value ?? useId()
const ui = computed(() => tv({ extend: radioGroup, slots: props.ui })({ const ui = computed(() => radioGroup({
size: size.value, size: size.value,
color: color.value, color: color.value,
disabled: disabled.value, disabled: disabled.value,
@@ -113,29 +113,29 @@ function onUpdate(value: any) {
:class="ui.root({ class: props.class })" :class="ui.root({ class: props.class })"
@update:model-value="onUpdate" @update:model-value="onUpdate"
> >
<fieldset :class="ui.fieldset()"> <fieldset :class="ui.fieldset({ class: props.ui?.fieldset })">
<legend v-if="legend || !!slots.legend" :class="ui.legend()"> <legend v-if="legend || !!slots.legend" :class="ui.legend({ class: props.ui?.legend })">
<slot name="legend"> <slot name="legend">
{{ legend }} {{ legend }}
</slot> </slot>
</legend> </legend>
<div v-for="item in normalizedItems" :key="item.value" :class="ui.item()"> <div v-for="item in normalizedItems" :key="item.value" :class="ui.item({ class: props.ui?.item })">
<div :class="ui.container()"> <div :class="ui.container({ class: props.ui?.container })">
<RadioGroupItem <RadioGroupItem
:id="item.id" :id="item.id"
:value="item.value" :value="item.value"
:disabled="disabled" :disabled="disabled"
:class="ui.base()" :class="ui.base({ class: props.ui?.base })"
> >
<RadioGroupIndicator :class="ui.indicator()" /> <RadioGroupIndicator :class="ui.indicator({ class: props.ui?.indicator })" />
</RadioGroupItem> </RadioGroupItem>
</div> </div>
<div :class="ui.wrapper()"> <div :class="ui.wrapper({ class: props.ui?.wrapper })">
<Label :class="ui.label()" :for="item.id"> <Label :class="ui.label({ class: props.ui?.label })" :for="item.id">
<slot name="label" :item="item" :model-value="modelValue">{{ item.label }}</slot> <slot name="label" :item="item" :model-value="modelValue">{{ item.label }}</slot>
</Label> </Label>
<p v-if="item.description || !!slots.description" :class="ui.description()"> <p v-if="item.description || !!slots.description" :class="ui.description({ class: props.ui?.description })">
<slot name="description" :item="item" :model-value="modelValue"> <slot name="description" :item="item" :model-value="modelValue">
{{ item.description }} {{ item.description }}
</slot> </slot>

View File

@@ -105,7 +105,7 @@ const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponen
const selectSize = computed(() => buttonGroupSize.value || formGroupSize.value) const selectSize = computed(() => buttonGroupSize.value || formGroupSize.value)
const ui = computed(() => tv({ extend: select, slots: props.ui })({ const ui = computed(() => select({
color: color.value, color: color.value,
variant: props.variant, variant: props.variant,
size: selectSize?.value, size: selectSize?.value,
@@ -148,56 +148,56 @@ function onUpdateOpen(value: boolean) {
@update:open="onUpdateOpen" @update:open="onUpdateOpen"
> >
<SelectTrigger :class="ui.base({ class: props.class })"> <SelectTrigger :class="ui.base({ class: props.class })">
<span v-if="isLeading || !!slots.leading" :class="ui.leading()"> <span v-if="isLeading || !!slots.leading" :class="ui.leading({ class: props.ui?.leading })">
<slot name="leading" :model-value="modelValue" :open="open" :ui="ui"> <slot name="leading" :model-value="modelValue" :open="open" :ui="ui">
<UIcon v-if="leadingIconName" :name="leadingIconName" :class="ui.leadingIcon()" /> <UIcon v-if="leadingIconName" :name="leadingIconName" :class="ui.leadingIcon({ class: props.ui?.leadingIcon })" />
</slot> </slot>
</span> </span>
<SelectValue :placeholder="placeholder ?? '&nbsp;'" :class="ui.value()" /> <SelectValue :placeholder="placeholder ?? '&nbsp;'" :class="ui.value({ class: props.ui?.value })" />
<span v-if="isTrailing || !!slots.trailing" :class="ui.trailing()"> <span v-if="isTrailing || !!slots.trailing" :class="ui.trailing({ class: props.ui?.trailing })">
<slot name="trailing" :model-value="modelValue" :open="open" :ui="ui"> <slot name="trailing" :model-value="modelValue" :open="open" :ui="ui">
<UIcon v-if="trailingIconName" :name="trailingIconName" :class="ui.trailingIcon()" /> <UIcon v-if="trailingIconName" :name="trailingIconName" :class="ui.trailingIcon({ class: props.ui?.trailingIcon })" />
</slot> </slot>
</span> </span>
</SelectTrigger> </SelectTrigger>
<SelectPortal :disabled="!portal"> <SelectPortal :disabled="!portal">
<SelectContent :class="ui.content()" v-bind="contentProps"> <SelectContent :class="ui.content({ class: props.ui?.content })" v-bind="contentProps">
<SelectViewport :class="ui.viewport()"> <SelectViewport :class="ui.viewport({ class: props.ui?.viewport })">
<SelectGroup v-for="(group, groupIndex) in groups" :key="`group-${groupIndex}`" :class="ui.group()"> <SelectGroup v-for="(group, groupIndex) in groups" :key="`group-${groupIndex}`" :class="ui.group({ class: props.ui?.group })">
<template v-for="(item, index) in group" :key="`group-${groupIndex}-${index}`"> <template v-for="(item, index) in group" :key="`group-${groupIndex}-${index}`">
<SelectLabel v-if="item?.type === 'label'" :class="ui.label()"> <SelectLabel v-if="item?.type === 'label'" :class="ui.label({ class: props.ui?.label })">
{{ item.label }} {{ item.label }}
</SelectLabel> </SelectLabel>
<SelectSeparator v-else-if="item?.type === 'separator'" :class="ui.separator()" /> <SelectSeparator v-else-if="item?.type === 'separator'" :class="ui.separator({ class: props.ui?.separator })" />
<SelectItem v-else :class="ui.item()" :disabled="item.disabled" :value="typeof item === 'object' ? item.value : item"> <SelectItem v-else :class="ui.item({ class: props.ui?.item })" :disabled="item.disabled" :value="typeof item === 'object' ? item.value : item">
<slot name="item" :item="(item as T)" :index="index"> <slot name="item" :item="(item as T)" :index="index">
<slot name="item-leading" :item="(item as T)" :index="index"> <slot name="item-leading" :item="(item as T)" :index="index">
<UAvatar v-if="item.avatar" :size="(ui.itemLeadingAvatarSize() as AvatarProps['size'])" v-bind="item.avatar" :class="ui.itemLeadingAvatar()" /> <UAvatar v-if="item.avatar" :size="(ui.itemLeadingAvatarSize() as AvatarProps['size'])" v-bind="item.avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar })" />
<UIcon v-else-if="item.icon" :name="item.icon" :class="ui.itemLeadingIcon()" /> <UIcon v-else-if="item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ class: props.ui?.itemLeadingIcon })" />
<UChip <UChip
v-else-if="item.chip" v-else-if="item.chip"
:size="(ui.itemLeadingChipSize() as ChipProps['size'])" :size="(ui.itemLeadingChipSize() as ChipProps['size'])"
inset inset
standalone standalone
v-bind="item.chip" v-bind="item.chip"
:class="ui.itemLeadingChip()" :class="ui.itemLeadingChip({ class: props.ui?.itemLeadingChip })"
/> />
</slot> </slot>
<SelectItemText :class="ui.itemLabel()"> <SelectItemText :class="ui.itemLabel({ class: props.ui?.itemLabel })">
<slot name="item-label" :item="(item as T)" :index="index"> <slot name="item-label" :item="(item as T)" :index="index">
{{ typeof item === 'object' ? item.label : item }} {{ typeof item === 'object' ? item.label : item }}
</slot> </slot>
</SelectItemText> </SelectItemText>
<span :class="ui.itemTrailing()"> <span :class="ui.itemTrailing({ class: props.ui?.itemTrailing })">
<slot name="item-trailing" :item="(item as T)" :index="index" /> <slot name="item-trailing" :item="(item as T)" :index="index" />
<SelectItemIndicator as-child> <SelectItemIndicator as-child>
<UIcon :name="selectedIcon || appConfig.ui.icons.check" :class="ui.itemTrailingIcon()" /> <UIcon :name="selectedIcon || appConfig.ui.icons.check" :class="ui.itemTrailingIcon({ class: props.ui?.itemTrailingIcon })" />
</SelectItemIndicator> </SelectItemIndicator>
</span> </span>
</slot> </slot>

View File

@@ -121,7 +121,7 @@ const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponen
const selectSize = computed(() => buttonGroupSize.value || formGroupSize.value) const selectSize = computed(() => buttonGroupSize.value || formGroupSize.value)
const ui = computed(() => tv({ extend: selectMenu, slots: props.ui })({ const ui = computed(() => selectMenu({
color: color.value, color: color.value,
variant: props.variant, variant: props.variant,
size: selectSize?.value, size: selectSize?.value,
@@ -201,74 +201,74 @@ function onUpdateOpen(value: boolean) {
> >
<ComboboxAnchor as-child> <ComboboxAnchor as-child>
<ComboboxTrigger :class="ui.base({ class: props.class })" tabindex="0"> <ComboboxTrigger :class="ui.base({ class: props.class })" tabindex="0">
<span v-if="isLeading || !!slots.leading" :class="ui.leading()"> <span v-if="isLeading || !!slots.leading" :class="ui.leading({ class: props.ui?.leading })">
<slot name="leading" :model-value="(modelValue as T)" :open="open" :ui="ui"> <slot name="leading" :model-value="(modelValue as T)" :open="open" :ui="ui">
<UIcon v-if="leadingIconName" :name="leadingIconName" :class="ui.leadingIcon()" /> <UIcon v-if="leadingIconName" :name="leadingIconName" :class="ui.leadingIcon({ class: props.ui?.leadingIcon })" />
</slot> </slot>
</span> </span>
<slot :model-value="(modelValue as T)" :open="open"> <slot :model-value="(modelValue as T)" :open="open">
<span v-if="multiple ? !!modelValue?.length : !!modelValue" :class="ui.value()"> <span v-if="multiple ? !!modelValue?.length : !!modelValue" :class="ui.value({ class: props.ui?.value })">
{{ displayValue(modelValue as T, multiple) }} {{ displayValue(modelValue as T, multiple) }}
</span> </span>
<span v-else :class="ui.placeholder()"> <span v-else :class="ui.placeholder({ class: props.ui?.placeholder })">
{{ placeholder ?? '&nbsp;' }} {{ placeholder ?? '&nbsp;' }}
</span> </span>
</slot> </slot>
<span v-if="isTrailing || !!slots.trailing" :class="ui.trailing()"> <span v-if="isTrailing || !!slots.trailing" :class="ui.trailing({ class: props.ui?.trailing })">
<slot name="trailing" :model-value="(modelValue as T)" :open="open" :ui="ui"> <slot name="trailing" :model-value="(modelValue as T)" :open="open" :ui="ui">
<UIcon v-if="trailingIconName" :name="trailingIconName" :class="ui.trailingIcon()" /> <UIcon v-if="trailingIconName" :name="trailingIconName" :class="ui.trailingIcon({ class: props.ui?.trailingIcon })" />
</slot> </slot>
</span> </span>
</ComboboxTrigger> </ComboboxTrigger>
</ComboboxAnchor> </ComboboxAnchor>
<ComboboxPortal :disabled="!portal"> <ComboboxPortal :disabled="!portal">
<ComboboxContent :class="ui.content()" v-bind="contentProps"> <ComboboxContent :class="ui.content({ class: props.ui?.content })" v-bind="contentProps">
<ComboboxInput :placeholder="searchPlaceholder" :class="ui.input()" autofocus autocomplete="off" /> <ComboboxInput :placeholder="searchPlaceholder" :class="ui.input({ class: props.ui?.input })" autofocus autocomplete="off" />
<ComboboxEmpty :class="ui.empty()"> <ComboboxEmpty :class="ui.empty({ class: props.ui?.empty })">
<slot name="empty" :search-term="searchTerm"> <slot name="empty" :search-term="searchTerm">
{{ searchTerm ? `No results for ${searchTerm}` : 'No results' }} {{ searchTerm ? `No results for ${searchTerm}` : 'No results' }}
</slot> </slot>
</ComboboxEmpty> </ComboboxEmpty>
<ComboboxViewport :class="ui.viewport()"> <ComboboxViewport :class="ui.viewport({ class: props.ui?.viewport })">
<ComboboxGroup v-for="(group, groupIndex) in groups" :key="`group-${groupIndex}`" :class="ui.group()"> <ComboboxGroup v-for="(group, groupIndex) in groups" :key="`group-${groupIndex}`" :class="ui.group({ class: props.ui?.group })">
<template v-for="(item, index) in group" :key="`group-${groupIndex}-${index}`"> <template v-for="(item, index) in group" :key="`group-${groupIndex}-${index}`">
<ComboboxLabel v-if="item?.type === 'label'" :class="ui.label()"> <ComboboxLabel v-if="item?.type === 'label'" :class="ui.label({ class: props.ui?.label })">
{{ item.label }} {{ item.label }}
</ComboboxLabel> </ComboboxLabel>
<ComboboxSeparator v-else-if="item?.type === 'separator'" :class="ui.separator()" /> <ComboboxSeparator v-else-if="item?.type === 'separator'" :class="ui.separator({ class: props.ui?.separator })" />
<ComboboxItem v-else :class="ui.item()" :disabled="item.disabled" :value="item"> <ComboboxItem v-else :class="ui.item({ class: props.ui?.item })" :disabled="item.disabled" :value="item">
<slot name="item" :item="(item as T)" :index="index"> <slot name="item" :item="(item as T)" :index="index">
<slot name="item-leading" :item="(item as T)" :index="index"> <slot name="item-leading" :item="(item as T)" :index="index">
<UAvatar v-if="item.avatar" :size="(ui.itemLeadingAvatarSize() as AvatarProps['size'])" v-bind="item.avatar" :class="ui.itemLeadingAvatar()" /> <UAvatar v-if="item.avatar" :size="(ui.itemLeadingAvatarSize() as AvatarProps['size'])" v-bind="item.avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar })" />
<UIcon v-else-if="item.icon" :name="item.icon" :class="ui.itemLeadingIcon()" /> <UIcon v-else-if="item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ class: props.ui?.itemLeadingIcon })" />
<UChip <UChip
v-else-if="item.chip" v-else-if="item.chip"
:size="(ui.itemLeadingChipSize() as ChipProps['size'])" :size="(ui.itemLeadingChipSize() as ChipProps['size'])"
inset inset
standalone standalone
v-bind="item.chip" v-bind="item.chip"
:class="ui.itemLeadingChip()" :class="ui.itemLeadingChip({ class: props.ui?.itemLeadingChip })"
/> />
</slot> </slot>
<span :class="ui.itemLabel()"> <span :class="ui.itemLabel({ class: props.ui?.itemLabel })">
<slot name="item-label" :item="(item as T)" :index="index"> <slot name="item-label" :item="(item as T)" :index="index">
{{ displayValue(item as T) }} {{ displayValue(item as T) }}
</slot> </slot>
</span> </span>
<span :class="ui.itemTrailing()"> <span :class="ui.itemTrailing({ class: props.ui?.itemTrailing })">
<slot name="item-trailing" :item="(item as T)" :index="index" /> <slot name="item-trailing" :item="(item as T)" :index="index" />
<ComboboxItemIndicator as-child> <ComboboxItemIndicator as-child>
<UIcon :name="selectedIcon || appConfig.ui.icons.check" :class="ui.itemTrailingIcon()" /> <UIcon :name="selectedIcon || appConfig.ui.icons.check" :class="ui.itemTrailingIcon({ class: props.ui?.itemTrailingIcon })" />
</ComboboxItemIndicator> </ComboboxItemIndicator>
</span> </span>
</slot> </slot>

View File

@@ -54,7 +54,7 @@ const slots = defineSlots<SeparatorSlots>()
const rootProps = useForwardProps(reactivePick(props, 'as', 'decorative', 'orientation')) const rootProps = useForwardProps(reactivePick(props, 'as', 'decorative', 'orientation'))
const ui = computed(() => tv({ extend: separator, slots: props.ui })({ const ui = computed(() => separator({
color: props.color, color: props.color,
orientation: props.orientation, orientation: props.orientation,
size: props.size, size: props.size,
@@ -64,18 +64,18 @@ const ui = computed(() => tv({ extend: separator, slots: props.ui })({
<template> <template>
<Separator v-bind="rootProps" :class="ui.root({ class: props.class })"> <Separator v-bind="rootProps" :class="ui.root({ class: props.class })">
<div :class="ui.border()" /> <div :class="ui.border({ class: props.ui?.border })" />
<template v-if="label || icon || avatar || !!slots.default"> <template v-if="label || icon || avatar || !!slots.default">
<div :class="ui.container()"> <div :class="ui.container({ class: props.ui?.container })">
<slot> <slot>
<span v-if="label" :class="ui.label()">{{ label }}</span> <span v-if="label" :class="ui.label({ class: props.ui?.label })">{{ label }}</span>
<UIcon v-else-if="icon" :name="icon" :class="ui.icon()" /> <UIcon v-else-if="icon" :name="icon" :class="ui.icon({ class: props.ui?.icon })" />
<UAvatar v-else-if="avatar" size="2xs" v-bind="avatar" :class="ui.avatar()" /> <UAvatar v-else-if="avatar" size="2xs" v-bind="avatar" :class="ui.avatar({ class: props.ui?.avatar })" />
</slot> </slot>
</div> </div>
<div :class="ui.border()" /> <div :class="ui.border({ class: props.ui?.border })" />
</template> </template>
</Separator> </Separator>
</template> </template>

View File

@@ -58,7 +58,7 @@ export interface SlideoverSlots {
header(props?: {}): any header(props?: {}): any
title(props?: {}): any title(props?: {}): any
description(props?: {}): any description(props?: {}): any
close(props: { class: string }): any close(props: { ui: any }): any
body(props?: {}): any body(props?: {}): any
footer(props?: {}): any footer(props?: {}): any
} }
@@ -96,7 +96,7 @@ const contentEvents = computed(() => {
const appConfig = useAppConfig() const appConfig = useAppConfig()
const ui = computed(() => tv({ extend: slideover, slots: props.ui })({ const ui = computed(() => slideover({
transition: props.transition, transition: props.transition,
side: props.side side: props.side
})) }))
@@ -109,26 +109,26 @@ const ui = computed(() => tv({ extend: slideover, slots: props.ui })({
</DialogTrigger> </DialogTrigger>
<DialogPortal :disabled="!portal"> <DialogPortal :disabled="!portal">
<DialogOverlay v-if="overlay" :class="ui.overlay()" /> <DialogOverlay v-if="overlay" :class="ui.overlay({ class: props.ui?.overlay })" />
<DialogContent :data-side="side" :class="ui.content({ class: props.class })" v-bind="contentProps" v-on="contentEvents"> <DialogContent :data-side="side" :class="ui.content({ class: props.class })" v-bind="contentProps" v-on="contentEvents">
<slot name="content"> <slot name="content">
<div v-if="!!slots.header || (title || !!slots.title) || (description || !!slots.description) || (close || !!slots.close)" :class="ui.header()"> <div v-if="!!slots.header || (title || !!slots.title) || (description || !!slots.description) || (close || !!slots.close)" :class="ui.header({ class: props.ui?.header })">
<slot name="header"> <slot name="header">
<DialogTitle v-if="title || !!slots.title" :class="ui.title()"> <DialogTitle v-if="title || !!slots.title" :class="ui.title({ class: props.ui?.title })">
<slot name="title"> <slot name="title">
{{ title }} {{ title }}
</slot> </slot>
</DialogTitle> </DialogTitle>
<DialogDescription v-if="description || !!slots.description" :class="ui.description()"> <DialogDescription v-if="description || !!slots.description" :class="ui.description({ class: props.ui?.description })">
<slot name="description"> <slot name="description">
{{ description }} {{ description }}
</slot> </slot>
</DialogDescription> </DialogDescription>
<DialogClose as-child> <DialogClose as-child>
<slot name="close" :class="ui.close()"> <slot name="close" :ui="ui">
<UButton <UButton
v-if="close" v-if="close"
:icon="closeIcon || appConfig.ui.icons.close" :icon="closeIcon || appConfig.ui.icons.close"
@@ -137,18 +137,18 @@ const ui = computed(() => tv({ extend: slideover, slots: props.ui })({
variant="ghost" variant="ghost"
aria-label="Close" aria-label="Close"
v-bind="typeof close === 'object' ? close : undefined" v-bind="typeof close === 'object' ? close : undefined"
:class="ui.close()" :class="ui.close({ class: props.ui?.close })"
/> />
</slot> </slot>
</DialogClose> </DialogClose>
</slot> </slot>
</div> </div>
<div :class="ui.body()"> <div :class="ui.body({ class: props.ui?.body })">
<slot name="body" /> <slot name="body" />
</div> </div>
<div v-if="!!slots.footer" :class="ui.footer()"> <div v-if="!!slots.footer" :class="ui.footer({ class: props.ui?.footer })">
<slot name="footer" /> <slot name="footer" />
</div> </div>
</slot> </slot>

View File

@@ -76,7 +76,7 @@ const sliderValue = computed({
const thumbsCount = computed(() => sliderValue.value?.length ?? 1) const thumbsCount = computed(() => sliderValue.value?.length ?? 1)
const ui = computed(() => tv({ extend: slider, slots: props.ui })({ const ui = computed(() => slider({
disabled: disabled.value, disabled: disabled.value,
size: size.value, size: size.value,
color: color.value, color: color.value,
@@ -103,10 +103,10 @@ function onChange(value: any) {
@update:model-value="emitFormInput()" @update:model-value="emitFormInput()"
@value-commit="onChange" @value-commit="onChange"
> >
<SliderTrack :class="ui.track()"> <SliderTrack :class="ui.track({ class: props.ui?.track })">
<SliderRange :class="ui.range()" /> <SliderRange :class="ui.range({ class: props.ui?.range })" />
</SliderTrack> </SliderTrack>
<SliderThumb v-for="count in thumbsCount" :key="count" :class="ui.thumb()" /> <SliderThumb v-for="count in thumbsCount" :key="count" :class="ui.thumb({ class: props.ui?.thumb })" />
</SliderRoot> </SliderRoot>
</template> </template>

View File

@@ -68,7 +68,7 @@ const rootProps = useForwardProps(reactivePick(props, 'as', 'required', 'value')
const { id: _id, emitFormChange, emitFormInput, size, color, name, disabled } = useFormField<SwitchProps>(props) const { id: _id, emitFormChange, emitFormInput, size, color, name, disabled } = useFormField<SwitchProps>(props)
const id = _id.value ?? useId() const id = _id.value ?? useId()
const ui = computed(() => tv({ extend: switchTv, slots: props.ui })({ const ui = computed(() => switchTv({
size: size.value, size: size.value,
color: color.value, color: color.value,
required: props.required, required: props.required,
@@ -87,7 +87,7 @@ function onUpdate(value: any) {
<template> <template>
<div :class="ui.root({ class: props.class })"> <div :class="ui.root({ class: props.class })">
<div :class="ui.container()"> <div :class="ui.container({ class: props.ui?.container })">
<SwitchRoot <SwitchRoot
:id="id" :id="id"
v-model:checked="modelValue" v-model:checked="modelValue"
@@ -95,25 +95,25 @@ function onUpdate(value: any) {
v-bind="rootProps" v-bind="rootProps"
:name="name" :name="name"
:disabled="disabled || loading" :disabled="disabled || loading"
:class="ui.base()" :class="ui.base({ class: props.ui?.base })"
@update:checked="onUpdate" @update:checked="onUpdate"
> >
<SwitchThumb :class="ui.thumb()"> <SwitchThumb :class="ui.thumb({ class: props.ui?.thumb })">
<UIcon v-if="loading" :name="loadingIcon || appConfig.ui.icons.loading" :class="ui.icon({ checked: true, unchecked: true })" /> <UIcon v-if="loading" :name="loadingIcon || appConfig.ui.icons.loading" :class="ui.icon({ class: props.ui?.icon, checked: true, unchecked: true })" />
<template v-else> <template v-else>
<UIcon v-if="checkedIcon" :name="checkedIcon" :class="ui.icon({ checked: true })" /> <UIcon v-if="checkedIcon" :name="checkedIcon" :class="ui.icon({ class: props.ui?.icon, checked: true })" />
<UIcon v-if="uncheckedIcon" :name="uncheckedIcon" :class="ui.icon({ unchecked: true })" /> <UIcon v-if="uncheckedIcon" :name="uncheckedIcon" :class="ui.icon({ class: props.ui?.icon, unchecked: true })" />
</template> </template>
</SwitchThumb> </SwitchThumb>
</SwitchRoot> </SwitchRoot>
</div> </div>
<div v-if="(label || !!slots.label) || (description || !!slots.description)" :class="ui.wrapper()"> <div v-if="(label || !!slots.label) || (description || !!slots.description)" :class="ui.wrapper({ class: props.ui?.wrapper })">
<Label v-if="label || !!slots.label" :for="id" :class="ui.label()"> <Label v-if="label || !!slots.label" :for="id" :class="ui.label({ class: props.ui?.label })">
<slot name="label" :label="label"> <slot name="label" :label="label">
{{ label }} {{ label }}
</slot> </slot>
</Label> </Label>
<p v-if="description || !!slots.description" :class="ui.description()"> <p v-if="description || !!slots.description" :class="ui.description({ class: props.ui?.description })">
<slot name="description" :description="description"> <slot name="description" :description="description">
{{ description }} {{ description }}
</slot> </slot>

View File

@@ -74,7 +74,7 @@ const slots = defineSlots<TabsSlots<T>>()
const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'defaultValue', 'orientation', 'activationMode', 'modelValue'), emits) const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'defaultValue', 'orientation', 'activationMode', 'modelValue'), emits)
const contentProps = toRef(() => defu(props.content || {}, { forceMount: true }) as TabsContentProps) const contentProps = toRef(() => defu(props.content || {}, { forceMount: true }) as TabsContentProps)
const ui = computed(() => tv({ extend: tabs, slots: props.ui })({ const ui = computed(() => tabs({
color: props.color, color: props.color,
variant: props.variant, variant: props.variant,
size: props.size, size: props.size,
@@ -84,16 +84,16 @@ const ui = computed(() => tv({ extend: tabs, slots: props.ui })({
<template> <template>
<TabsRoot v-bind="rootProps" :class="ui.root({ class: props.class })"> <TabsRoot v-bind="rootProps" :class="ui.root({ class: props.class })">
<TabsList :class="ui.list()"> <TabsList :class="ui.list({ class: props.ui?.list })">
<TabsIndicator :class="ui.indicator()" /> <TabsIndicator :class="ui.indicator({ class: props.ui?.indicator })" />
<TabsTrigger v-for="(item, index) of items" :key="index" :value="item.value || String(index)" :disabled="item.disabled" :class="ui.trigger()"> <TabsTrigger v-for="(item, index) of items" :key="index" :value="item.value || String(index)" :disabled="item.disabled" :class="ui.trigger({ class: props.ui?.trigger })">
<slot name="leading" :item="item" :index="index"> <slot name="leading" :item="item" :index="index">
<UAvatar v-if="item.avatar" :size="(ui.leadingAvatarSize() as any)" v-bind="item.avatar" :class="ui.leadingAvatar()" /> <UAvatar v-if="item.avatar" :size="(ui.leadingAvatarSize() as any)" v-bind="item.avatar" :class="ui.leadingAvatar({ class: props.ui?.leadingAvatar })" />
<UIcon v-else-if="item.icon" :name="item.icon" :class="ui.leadingIcon()" /> <UIcon v-else-if="item.icon" :name="item.icon" :class="ui.leadingIcon({ class: props.ui?.leadingIcon })" />
</slot> </slot>
<span v-if="item.label || !!slots.default" :class="ui.label()"> <span v-if="item.label || !!slots.default" :class="ui.label({ class: props.ui?.label })">
<slot :item="item" :index="index">{{ item.label }}</slot> <slot :item="item" :index="index">{{ item.label }}</slot>
</span> </span>
@@ -102,7 +102,7 @@ const ui = computed(() => tv({ extend: tabs, slots: props.ui })({
</TabsList> </TabsList>
<template v-if="!!content"> <template v-if="!!content">
<TabsContent v-for="(item, index) of items" :key="index" v-bind="contentProps" :value="item.value || String(index)" :class="ui.content()"> <TabsContent v-for="(item, index) of items" :key="index" v-bind="contentProps" :value="item.value || String(index)" :class="ui.content({ class: props.ui?.content })">
<slot :name="item.slot || 'content'" :item="item" :index="index"> <slot :name="item.slot || 'content'" :item="item" :index="index">
{{ item.content }} {{ item.content }}
</slot> </slot>

View File

@@ -59,7 +59,7 @@ const [modelValue, modelModifiers] = defineModel<string | number>()
const { emitFormBlur, emitFormInput, emitFormChange, size, color, id, name, disabled } = useFormField<TextareaProps>(props) const { emitFormBlur, emitFormInput, emitFormChange, size, color, id, name, disabled } = useFormField<TextareaProps>(props)
const ui = computed(() => tv({ extend: textarea, slots: props.ui })({ const ui = computed(() => textarea({
color: color.value, color: color.value,
variant: props.variant, variant: props.variant,
size: size?.value size: size?.value
@@ -164,7 +164,7 @@ onMounted(() => {
:name="name" :name="name"
:rows="rows" :rows="rows"
:placeholder="placeholder" :placeholder="placeholder"
:class="ui.base()" :class="ui.base({ class: props.ui?.base })"
:disabled="disabled" :disabled="disabled"
:required="required" :required="required"
v-bind="$attrs" v-bind="$attrs"

View File

@@ -51,7 +51,7 @@ export interface ToastSlots {
title(props?: {}): any title(props?: {}): any
description(props?: {}): any description(props?: {}): any
actions(props?: {}): any actions(props?: {}): any
close(props: { class: string }): any close(props: { ui: any }): any
} }
</script> </script>
@@ -76,7 +76,9 @@ const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'defaultOpen',
const multiline = computed(() => !!props.title && !!props.description) const multiline = computed(() => !!props.title && !!props.description)
const duration = computed(() => props.duration || toaster?.value.duration) const duration = computed(() => props.duration || toaster?.value.duration)
const ui = computed(() => tv({ extend: toast, slots: props.ui })({ color: props.color })) const ui = computed(() => toast({
color: props.color
}))
const el = ref() const el = ref()
const height = ref(0) const height = ref(0)
@@ -105,25 +107,25 @@ defineExpose({
:style="{ '--height': height }" :style="{ '--height': height }"
> >
<slot name="leading"> <slot name="leading">
<UAvatar v-if="avatar" size="2xl" v-bind="avatar" :class="ui.avatar()" /> <UAvatar v-if="avatar" size="2xl" v-bind="avatar" :class="ui.avatar({ class: props.ui?.avatar })" />
<UIcon v-else-if="icon" :name="icon" :class="ui.icon()" /> <UIcon v-else-if="icon" :name="icon" :class="ui.icon({ class: props.ui?.icon })" />
</slot> </slot>
<div :class="ui.wrapper()"> <div :class="ui.wrapper({ class: props.ui?.wrapper })">
<ToastTitle v-if="title || !!slots.title" :class="ui.title()"> <ToastTitle v-if="title || !!slots.title" :class="ui.title({ class: props.ui?.title })">
<slot name="title"> <slot name="title">
{{ title }} {{ title }}
</slot> </slot>
</ToastTitle> </ToastTitle>
<template v-if="description || !!slots.description"> <template v-if="description || !!slots.description">
<ToastDescription :class="ui.description()"> <ToastDescription :class="ui.description({ class: props.ui?.description })">
<slot name="description"> <slot name="description">
{{ description }} {{ description }}
</slot> </slot>
</ToastDescription> </ToastDescription>
</template> </template>
<div v-if="multiline && actions?.length" :class="ui.actions({ multiline: true })"> <div v-if="multiline && actions?.length" :class="ui.actions({ class: props.ui?.actions, multiline: true })">
<slot name="actions"> <slot name="actions">
<ToastAction v-for="(action, index) in actions" :key="index" :alt-text="action.label || 'Action'" as-child @click.stop> <ToastAction v-for="(action, index) in actions" :key="index" :alt-text="action.label || 'Action'" as-child @click.stop>
<UButton size="xs" :color="color" v-bind="action" /> <UButton size="xs" :color="color" v-bind="action" />
@@ -132,7 +134,7 @@ defineExpose({
</div> </div>
</div> </div>
<div v-if="(!multiline && actions?.length) || close !== null" :class="ui.actions({ multiline: false })"> <div v-if="(!multiline && actions?.length) || close !== null" :class="ui.actions({ class: props.ui?.actions, multiline: false })">
<template v-if="!multiline"> <template v-if="!multiline">
<slot name="actions"> <slot name="actions">
<ToastAction v-for="(action, index) in actions" :key="index" :alt-text="action.label || 'Action'" as-child @click.stop> <ToastAction v-for="(action, index) in actions" :key="index" :alt-text="action.label || 'Action'" as-child @click.stop>
@@ -142,7 +144,7 @@ defineExpose({
</template> </template>
<ToastClose as-child> <ToastClose as-child>
<slot name="close" :class="ui.close()"> <slot name="close" :ui="ui">
<UButton <UButton
v-if="close" v-if="close"
:icon="closeIcon || appConfig.ui.icons.close" :icon="closeIcon || appConfig.ui.icons.close"
@@ -151,13 +153,13 @@ defineExpose({
variant="link" variant="link"
aria-label="Close" aria-label="Close"
v-bind="typeof close === 'object' ? close : undefined" v-bind="typeof close === 'object' ? close : undefined"
:class="ui.close()" :class="ui.close({ class: props.ui?.close })"
@click.stop @click.stop
/> />
</slot> </slot>
</ToastClose> </ToastClose>
</div> </div>
<div v-if="remaining > 0 && duration" :class="ui.progress()" :style="{ width: `${remaining / duration * 100}%` }" /> <div v-if="remaining > 0 && duration" :class="ui.progress({ class: props.ui?.progress })" :style="{ width: `${remaining / duration * 100}%` }" />
</ToastRoot> </ToastRoot>
</template> </template>

View File

@@ -66,7 +66,7 @@ const swipeDirection = computed(() => {
return 'right' return 'right'
}) })
const ui = computed(() => tv({ extend: toaster, slots: props.ui })({ const ui = computed(() => toaster({
position: props.position, position: props.position,
swipeDirection: swipeDirection.value swipeDirection: swipeDirection.value
})) }))

View File

@@ -59,7 +59,10 @@ const rootProps = useForwardPropsEmits(reactivePick(props, 'defaultOpen', 'open'
const contentProps = toRef(() => defu(props.content, { side: 'bottom', sideOffset: 8 }) as TooltipContentProps) const contentProps = toRef(() => defu(props.content, { side: 'bottom', sideOffset: 8 }) as TooltipContentProps)
const arrowProps = toRef(() => props.arrow as TooltipArrowProps) const arrowProps = toRef(() => props.arrow as TooltipArrowProps)
const ui = computed(() => tv({ extend: tooltip, slots: props.ui })({ side: contentProps.value.side })) // eslint-disable-next-line vue/no-dupe-keys
const ui = computed(() => tooltip({
side: contentProps.value.side
}))
</script> </script>
<template> <template>
@@ -71,14 +74,14 @@ const ui = computed(() => tv({ extend: tooltip, slots: props.ui })({ side: conte
<TooltipPortal :disabled="!portal"> <TooltipPortal :disabled="!portal">
<TooltipContent v-bind="contentProps" :class="ui.content({ class: props.class })"> <TooltipContent v-bind="contentProps" :class="ui.content({ class: props.class })">
<slot name="content"> <slot name="content">
<span v-if="text" :class="ui.text()">{{ text }}</span> <span v-if="text" :class="ui.text({ class: props.ui?.text })">{{ text }}</span>
<span v-if="kbds?.length" :class="ui.kbds()"> <span v-if="kbds?.length" :class="ui.kbds({ class: props.ui?.kbds })">
<UKbd v-for="(kbd, index) in kbds" :key="index" size="sm" v-bind="typeof kbd === 'string' ? { value: kbd } : kbd" /> <UKbd v-for="(kbd, index) in kbds" :key="index" size="sm" v-bind="typeof kbd === 'string' ? { value: kbd } : kbd" />
</span> </span>
</slot> </slot>
<TooltipArrow v-if="!!arrow" v-bind="arrowProps" :class="ui.arrow()" /> <TooltipArrow v-if="!!arrow" v-bind="arrowProps" :class="ui.arrow({ class: props.ui?.arrow })" />
</TooltipContent> </TooltipContent>
</TooltipPortal> </TooltipPortal>
</TooltipRoot> </TooltipRoot>

View File

@@ -65,6 +65,9 @@ export default (options: Required<ModuleOptions>) => ({
}, },
other: { other: {
step: 'opacity-0' step: 'opacity-0'
},
last: {
step: ''
} }
}, },
orientation: { orientation: {

View File

@@ -120,11 +120,11 @@ exports[`Breadcrumb > renders with separatorIcon correctly 1`] = `
exports[`Breadcrumb > renders with ui correctly 1`] = ` exports[`Breadcrumb > renders with ui correctly 1`] = `
"<div aria-label="breadcrumb" class="relative min-w-0"> "<div aria-label="breadcrumb" class="relative min-w-0">
<ol class="flex items-center gap-1.5"> <ol class="flex items-center gap-1.5">
<li class="flex min-w-0"><a href="/" class="group relative flex items-center gap-1.5 text-sm min-w-0 text-gray-500 dark:text-gray-400 font-medium hover:text-gray-700 dark:hover:text-gray-200 transition-colors"><span class="inline-flex items-center justify-center select-none overflow-hidden rounded-full align-middle bg-gray-100 dark:bg-gray-800 size-5 text-[10px] shrink-0"><img role="img" src="https://avatars.githubusercontent.com/u/739984?v=4" class="h-full w-full rounded-[inherit] object-cover" style="display: none;"><span class="font-medium leading-none text-gray-500 dark:text-gray-400 truncate"></span></span><span class="truncate">Home</span></a></li> <li class="flex min-w-0"><a href="/" class="group relative flex items-center gap-1.5 text-sm min-w-0 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 transition-colors font-bold"><span class="inline-flex items-center justify-center select-none overflow-hidden rounded-full align-middle bg-gray-100 dark:bg-gray-800 size-5 text-[10px] shrink-0"><img role="img" src="https://avatars.githubusercontent.com/u/739984?v=4" class="h-full w-full rounded-[inherit] object-cover" style="display: none;"><span class="font-medium leading-none text-gray-500 dark:text-gray-400 truncate"></span></span><span class="truncate">Home</span></a></li>
<li role="presentation" class="flex"><span class="iconify i-heroicons:chevron-right-20-solid shrink-0 size-5 text-gray-500 dark:text-gray-400" aria-hidden="true"></span></li> <li role="presentation" class="flex"><span class="iconify i-heroicons:chevron-right-20-solid shrink-0 size-5 text-gray-500 dark:text-gray-400" aria-hidden="true"></span></li>
<li class="flex min-w-0"><span class="group relative flex items-center gap-1.5 text-sm min-w-0 text-gray-500 dark:text-gray-400 font-medium cursor-not-allowed opacity-75"><span class="iconify i-heroicons:square-3-stack-3d shrink-0 size-5" aria-hidden="true"></span><span class="truncate">Navigation</span></span></li> <li class="flex min-w-0"><span class="group relative flex items-center gap-1.5 text-sm min-w-0 text-gray-500 dark:text-gray-400 cursor-not-allowed opacity-75 font-bold"><span class="iconify i-heroicons:square-3-stack-3d shrink-0 size-5" aria-hidden="true"></span><span class="truncate">Navigation</span></span></li>
<li role="presentation" class="flex"><span class="iconify i-heroicons:chevron-right-20-solid shrink-0 size-5 text-gray-500 dark:text-gray-400" aria-hidden="true"></span></li> <li role="presentation" class="flex"><span class="iconify i-heroicons:chevron-right-20-solid shrink-0 size-5 text-gray-500 dark:text-gray-400" aria-hidden="true"></span></li>
<li class="flex min-w-0"><a href="/breadcrumb" class="group relative flex items-center gap-1.5 text-sm min-w-0 text-primary-500 dark:text-primary-400 font-semibold"><span class="iconify i-heroicons:link shrink-0 size-5" aria-hidden="true"></span><span class="truncate">Breadcrumb</span></a></li> <li class="flex min-w-0"><a href="/breadcrumb" class="group relative flex items-center gap-1.5 text-sm min-w-0 text-primary-500 dark:text-primary-400 font-bold"><span class="iconify i-heroicons:link shrink-0 size-5" aria-hidden="true"></span><span class="truncate">Breadcrumb</span></a></li>
<!--v-if--> <!--v-if-->
</ol> </ol>
</div>" </div>"

View File

@@ -50,7 +50,7 @@ exports[`Chip > renders with size xs correctly 1`] = `"<div class="relative inli
exports[`Chip > renders with text correctly 1`] = `"<div class="relative inline-flex items-center justify-center shrink-0"><span class="rounded-full ring ring-white dark:ring-gray-900 flex items-center justify-center text-white dark:text-gray-900 font-medium whitespace-nowrap bg-primary-500 dark:bg-primary-400 h-[8px] min-w-[8px] text-[8px] top-0 right-0 absolute -translate-y-1/2 translate-x-1/2 transform">Text</span></div>"`; exports[`Chip > renders with text correctly 1`] = `"<div class="relative inline-flex items-center justify-center shrink-0"><span class="rounded-full ring ring-white dark:ring-gray-900 flex items-center justify-center text-white dark:text-gray-900 font-medium whitespace-nowrap bg-primary-500 dark:bg-primary-400 h-[8px] min-w-[8px] text-[8px] top-0 right-0 absolute -translate-y-1/2 translate-x-1/2 transform">Text</span></div>"`;
exports[`Chip > renders with ui correctly 1`] = `"<div class="relative inline-flex items-center justify-center shrink-0"><span class="rounded-full ring ring-white dark:ring-gray-900 flex items-center justify-center font-medium whitespace-nowrap text-gray-500 dark:text-gray-400 bg-primary-500 dark:bg-primary-400 h-[8px] min-w-[8px] text-[8px] top-0 right-0 absolute -translate-y-1/2 translate-x-1/2 transform"></span></div>"`; exports[`Chip > renders with ui correctly 1`] = `"<div class="relative inline-flex items-center justify-center shrink-0"><span class="rounded-full ring ring-white dark:ring-gray-900 flex items-center justify-center font-medium whitespace-nowrap bg-primary-500 dark:bg-primary-400 h-[8px] min-w-[8px] text-[8px] top-0 right-0 absolute -translate-y-1/2 translate-x-1/2 transform text-gray-500 dark:text-gray-400"></span></div>"`;
exports[`Chip > renders without show correctly 1`] = ` exports[`Chip > renders without show correctly 1`] = `
"<div class="relative inline-flex items-center justify-center shrink-0"> "<div class="relative inline-flex items-center justify-center shrink-0">

View File

@@ -855,7 +855,7 @@ exports[`DropdownMenu > renders with ui correctly 1`] = `
<div data-radix-popper-content-wrapper="" style="position: fixed; left: 0px; top: 0px; transform: translate(0, -200%); min-width: max-content;"> <div data-radix-popper-content-wrapper="" style="position: fixed; left: 0px; top: 0px; transform: translate(0, -200%); min-width: max-content;">
<div role="menu" aria-orientation="vertical" data-radix-menu-content="" data-state="open" dir="ltr" tabindex="-1" data-orientation="vertical" style="outline-color: none; outline-style: none; outline-width: initial; --radix-dropdown-menu-content-transform-origin: var(--radix-popper-transform-origin); --radix-dropdown-menu-content-available-width: var(--radix-popper-available-width); --radix-dropdown-menu-content-available-height: var(--radix-popper-available-height); --radix-dropdown-menu-trigger-width: var(--radix-popper-anchor-width); --radix-dropdown-menu-trigger-height: var(--radix-popper-anchor-height); animation: none;" data-dismissable-layer="" id="radix-vue-dropdown-menu-content-28" aria-labelledby="" class="min-w-32 bg-white dark:bg-gray-900 shadow-lg rounded-md ring ring-gray-200 dark:ring-gray-800 divide-y divide-gray-200 dark:divide-gray-800 overflow-y-auto scroll-py-1 data-[state=open]:animate-[scale-in_100ms_ease-out] data-[state=closed]:animate-[scale-out_100ms_ease-in]" data-side="bottom" data-align="center"> <div role="menu" aria-orientation="vertical" data-radix-menu-content="" data-state="open" dir="ltr" tabindex="-1" data-orientation="vertical" style="outline-color: none; outline-style: none; outline-width: initial; --radix-dropdown-menu-content-transform-origin: var(--radix-popper-transform-origin); --radix-dropdown-menu-content-available-width: var(--radix-popper-available-width); --radix-dropdown-menu-content-available-height: var(--radix-popper-available-height); --radix-dropdown-menu-trigger-width: var(--radix-popper-anchor-width); --radix-dropdown-menu-trigger-height: var(--radix-popper-anchor-height); animation: none;" data-dismissable-layer="" id="radix-vue-dropdown-menu-content-28" aria-labelledby="" class="min-w-32 bg-white dark:bg-gray-900 shadow-lg rounded-md ring ring-gray-200 dark:ring-gray-800 divide-y divide-gray-200 dark:divide-gray-800 overflow-y-auto scroll-py-1 data-[state=open]:animate-[scale-in_100ms_ease-out] data-[state=closed]:animate-[scale-out_100ms_ease-in]" uioverride="[object Object]" data-side="bottom" data-align="center">
<div role="group" class="p-1 isolate"> <div role="group" class="p-1 isolate">
<div class="w-full flex items-center font-semibold text-gray-900 dark:text-white p-1.5 text-sm gap-1.5"><span class="inline-flex items-center justify-center select-none overflow-hidden rounded-full align-middle bg-gray-100 dark:bg-gray-800 size-5 text-[10px] shrink-0"><img role="img" src="https://avatars.githubusercontent.com/u/739984?v=4" class="h-full w-full rounded-[inherit] object-cover" style="display: none;"><span class="font-medium leading-none text-gray-500 dark:text-gray-400 truncate"></span></span><span class="truncate">My account<!--v-if--></span> <div class="w-full flex items-center font-semibold text-gray-900 dark:text-white p-1.5 text-sm gap-1.5"><span class="inline-flex items-center justify-center select-none overflow-hidden rounded-full align-middle bg-gray-100 dark:bg-gray-800 size-5 text-[10px] shrink-0"><img role="img" src="https://avatars.githubusercontent.com/u/739984?v=4" class="h-full w-full rounded-[inherit] object-cover" style="display: none;"><span class="font-medium leading-none text-gray-500 dark:text-gray-400 truncate"></span></span><span class="truncate">My account<!--v-if--></span>
<!--v-if--> <!--v-if-->

View File

@@ -181,7 +181,7 @@ exports[`Input > renders with type correctly 1`] = `
`; `;
exports[`Input > renders with ui correctly 1`] = ` exports[`Input > renders with ui correctly 1`] = `
"<div class="relative inline-flex items-center"><input type="text" class="w-full border-0 placeholder-gray-400 dark:placeholder-gray-500 focus:outline-none disabled:cursor-not-allowed disabled:opacity-75 rounded-full px-2.5 py-1.5 text-sm gap-1.5 shadow-sm bg-white dark:bg-gray-900 text-gray-900 dark:text-white ring ring-inset ring-gray-300 dark:ring-gray-700 focus-visible:ring-2 focus-visible:ring-primary-500 dark:focus-visible:ring-primary-400"> "<div class="relative inline-flex items-center"><input type="text" class="w-full border-0 placeholder-gray-400 dark:placeholder-gray-500 focus:outline-none disabled:cursor-not-allowed disabled:opacity-75 px-2.5 py-1.5 text-sm gap-1.5 shadow-sm bg-white dark:bg-gray-900 text-gray-900 dark:text-white ring ring-inset ring-gray-300 dark:ring-gray-700 focus-visible:ring-2 focus-visible:ring-primary-500 dark:focus-visible:ring-primary-400 rounded-full">
<!--v-if--> <!--v-if-->
<!--v-if--> <!--v-if-->
</div>" </div>"

View File

@@ -262,7 +262,7 @@ exports[`Progress > renders with status slot correctly 1`] = `
exports[`Progress > renders with ui correctly 1`] = ` exports[`Progress > renders with ui correctly 1`] = `
"<div class="gap-2 w-full flex flex-col"> "<div class="gap-2 w-full flex flex-col">
<!--v-if--> <!--v-if-->
<div aria-valuemax="100" aria-valuemin="0" aria-valuetext="0%" aria-label="0%" role="progressbar" data-state="indeterminate" data-max="100" class="relative overflow-hidden rounded-full bg-white dark:bg-gray-900 w-full h-2" style="transform: translateZ(0);"> <div aria-valuemax="100" aria-valuemin="0" aria-valuetext="0%" aria-label="0%" role="progressbar" data-state="indeterminate" data-max="100" class="relative overflow-hidden rounded-full w-full h-2 bg-white dark:bg-gray-900" style="transform: translateZ(0);">
<div data-state="indeterminate" data-max="100" class="rounded-full size-full transition-transform duration-200 ease-out bg-primary-500 dark:bg-primary-400 data-[state=indeterminate]:animate-[carousel_2s_ease-in-out_infinite]"></div> <div data-state="indeterminate" data-max="100" class="rounded-full size-full transition-transform duration-200 ease-out bg-primary-500 dark:bg-primary-400 data-[state=indeterminate]:animate-[carousel_2s_ease-in-out_infinite]"></div>
</div> </div>
<!--v-if--> <!--v-if-->

View File

@@ -111,7 +111,7 @@ exports[`Slider > renders with size xs correctly 1`] = `
`; `;
exports[`Slider > renders with ui correctly 1`] = ` exports[`Slider > renders with ui correctly 1`] = `
"<span data-slider-impl="" dir="ltr" data-orientation="horizontal" style="--radix-slider-thumb-transform: translateX(-50%);" class="relative flex items-center select-none touch-none w-full" aria-disabled="false"><span data-orientation="horizontal" class="relative overflow-hidden rounded-full grow bg-gray-100 dark:bg-gray-800 h-[8px]"><span data-orientation="horizontal" style="left: 0%; right: 100%;" class="absolute rounded-full bg-primary-500 dark:bg-primary-400 h-full"></span></span> "<span data-slider-impl="" dir="ltr" data-orientation="horizontal" style="--radix-slider-thumb-transform: translateX(-50%);" class="relative flex items-center select-none touch-none w-full" aria-disabled="false"><span data-orientation="horizontal" class="relative overflow-hidden rounded-full grow h-[8px] bg-gray-100 dark:bg-gray-800"><span data-orientation="horizontal" style="left: 0%; right: 100%;" class="absolute rounded-full bg-primary-500 dark:bg-primary-400 h-full"></span></span>
<div class="rounded-full bg-white dark:bg-gray-900 ring-2 focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-2 ring-primary-500 dark:ring-primary-400 focus-visible:outline-primary-500/50 dark:focus-visible:outline-primary-400/50 size-4" role="slider" data-radix-vue-collection-item="" tabindex="0" data-orientation="horizontal" aria-valuemin="0" aria-valuemax="100" aria-orientation="horizontal" style="transform: var(--radix-slider-thumb-transform); position: absolute; left: calc(0% + 0px);" aria-valuenow="0"></div></span> <div class="rounded-full bg-white dark:bg-gray-900 ring-2 focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-2 ring-primary-500 dark:ring-primary-400 focus-visible:outline-primary-500/50 dark:focus-visible:outline-primary-400/50 size-4" role="slider" data-radix-vue-collection-item="" tabindex="0" data-orientation="horizontal" aria-valuemin="0" aria-valuemax="100" aria-orientation="horizontal" style="transform: var(--radix-slider-thumb-transform); position: absolute; left: calc(0% + 0px);" aria-valuenow="0"></div></span>
<!---->" <!---->"
`; `;