Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/support tailwind prefix in v3 #3009

Open
wants to merge 10 commits into
base: v3
Choose a base branch
from
2 changes: 1 addition & 1 deletion playground-vue/src/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ defineShortcuts({

<template>
<UApp :toaster="(appConfig.toaster as any)">
<div class="h-screen w-screen overflow-hidden flex min-h-0 bg-[var(--ui-bg)]" vaul-drawer-wrapper>
<div class="h-screen w-screen overflow-hidden flex flex-col lg:flex-row min-h-0 bg-[var(--ui-bg)]" vaul-drawer-wrapper>
<UNavigationMenu :items="items" orientation="vertical" class="hidden lg:flex border-e border-[var(--ui-border)] overflow-y-auto w-48 p-4" />
<UNavigationMenu :items="items" orientation="horizontal" class="lg:hidden border-b border-[var(--ui-border)] [&>div]:min-w-min overflow-x-auto" />

Expand Down
7 changes: 7 additions & 0 deletions src/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,10 @@ export const resolveColors = (colors?: string[]) => {
? [...new Set(['primary', ...colors])]
: ['primary', 'secondary', 'success', 'info', 'warning', 'error']
}

export const getTailwindPrefix = (prefix?: string) => {
return {
variable: prefix ? prefix + '-' : '',
utilitie: prefix ? prefix + ':' : ''
}
}
10 changes: 8 additions & 2 deletions src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,16 @@ export default defineNuxtModule<ModuleOptions>({
}

async function registerModule(name: string, options: Record<string, any>) {
const configKeyMap: Record<string, string> = {
'@nuxt/icon': 'icon',
'@nuxt/fonts': 'fonts',
'@nuxtjs/color-mode': 'colorMode'
}
const key = configKeyMap[name]
if (!hasNuxtModule(name)) {
await installModule(name, options)
await installModule(name, defu((nuxt.options as any)[key], options))
} else {
(nuxt.options as any)[name] = defu((nuxt.options as any)[name], options)
(nuxt.options as any)[key] = defu((nuxt.options as any)[key], options)
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/runtime/components/Alert.vue
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ const ui = computed(() => alert({
<template>
<Primitive :as="as" :class="ui.root({ class: [props.class, props.ui?.root], multiline })">
<slot name="leading">
<UAvatar v-if="avatar" :size="((props.ui?.avatarSize || ui.avatarSize()) as AvatarProps['size'])" v-bind="avatar" :class="ui.avatar({ class: props.ui?.avatar })" />
<UAvatar v-if="avatar" :size="((props.ui?.avatarSize || ui.avatarSize().split(' ').pop()) as AvatarProps['size'])" v-bind="avatar" :class="ui.avatar({ class: props.ui?.avatar })" />
<UIcon v-else-if="icon" :name="icon" :class="ui.icon({ class: props.ui?.icon })" />
</slot>

Expand Down
2 changes: 1 addition & 1 deletion src/runtime/components/Badge.vue
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ extendDevtoolsMeta({ defaultProps: { label: 'Badge' } })
<Primitive :as="as" :class="ui.base({ class: [props.class, props.ui?.base] })">
<slot name="leading">
<UIcon v-if="isLeading && leadingIconName" :name="leadingIconName" :class="ui.leadingIcon({ class: props.ui?.leadingIcon })" />
<UAvatar v-else-if="!!avatar" :size="((props.ui?.leadingAvatarSize || ui.leadingAvatarSize()) as AvatarProps['size'])" v-bind="avatar" :class="ui.leadingAvatar({ class: props.ui?.leadingAvatar })" />
<UAvatar v-else-if="!!avatar" :size="((props.ui?.leadingAvatarSize || ui.leadingAvatarSize().split(' ').pop()) as AvatarProps['size'])" v-bind="avatar" :class="ui.leadingAvatar({ class: props.ui?.leadingAvatar })" />
</slot>

<slot>
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/components/Button.vue
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ const ui = computed(() => button({
>
<slot name="leading">
<UIcon v-if="isLeading && leadingIconName" :name="leadingIconName" :class="ui.leadingIcon({ class: props.ui?.leadingIcon })" />
<UAvatar v-else-if="!!avatar" :size="((props.ui?.leadingAvatarSize || ui.leadingAvatarSize()) as AvatarProps['size'])" v-bind="avatar" :class="ui.leadingAvatar({ class: props.ui?.leadingAvatar })" />
<UAvatar v-else-if="!!avatar" :size="((props.ui?.leadingAvatarSize || ui.leadingAvatarSize().split(' ').pop()) as AvatarProps['size'])" v-bind="avatar" :class="ui.leadingAvatar({ class: props.ui?.leadingAvatar })" />
</slot>

<slot>
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/components/CommandPalette.vue
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ const groups = computed(() => {
<slot :name="item.slot ? `${item.slot}-leading` : group.slot ? `${group.slot}-leading` : `item-leading`" :item="item" :index="index">
<UIcon v-if="item.loading" :name="loadingIcon || appConfig.ui.icons.loading" :class="ui.itemLeadingIcon({ class: props.ui?.itemLeadingIcon, loading: true })" />
<UIcon v-else-if="item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ class: props.ui?.itemLeadingIcon, active: item.active })" />
<UAvatar v-else-if="item.avatar" :size="((props.ui?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize()) as AvatarProps['size'])" v-bind="item.avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar, active: item.active })" />
<UAvatar v-else-if="item.avatar" :size="((props.ui?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize().split(' ').pop()) as AvatarProps['size'])" v-bind="item.avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar, active: item.active })" />
<UChip
v-else-if="item.chip"
:size="((props.ui?.itemLeadingChipSize || ui.itemLeadingChipSize()) as ChipProps['size'])"
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/components/ContextMenuContent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
<slot :name="item.slot ? `${item.slot}-leading`: 'item-leading'" :item="(item as T)" :active="active" :index="index">
<UIcon v-if="item.loading" :name="loadingIcon || appConfig.ui.icons.loading" :class="ui.itemLeadingIcon({ class: uiOverride?.itemLeadingIcon, color: item?.color, loading: true })" />
<UIcon v-else-if="item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ class: uiOverride?.itemLeadingIcon, color: item?.color, active })" />
<UAvatar v-else-if="item.avatar" :size="((props.uiOverride?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize()) as AvatarProps['size'])" v-bind="item.avatar" :class="ui.itemLeadingAvatar({ class: uiOverride?.itemLeadingAvatar, active })" />
<UAvatar v-else-if="item.avatar" :size="((props.uiOverride?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize().split(' ').pop()) as AvatarProps['size'])" v-bind="item.avatar" :class="ui.itemLeadingAvatar({ class: uiOverride?.itemLeadingAvatar, active })" />
</slot>

<span v-if="get(item, props.labelKey as string) || !!slots[item.slot ? `${item.slot}-label`: 'item-label']" :class="ui.itemLabel({ class: uiOverride?.itemLabel, active })">
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/components/DropdownMenuContent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
<slot :name="item.slot ? `${item.slot}-leading`: 'item-leading'" :item="(item as T)" :active="active" :index="index">
<UIcon v-if="item.loading" :name="loadingIcon || appConfig.ui.icons.loading" :class="ui.itemLeadingIcon({ class: uiOverride?.itemLeadingIcon, color: item?.color, loading: true })" />
<UIcon v-else-if="item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ class: uiOverride?.itemLeadingIcon, color: item?.color, active })" />
<UAvatar v-else-if="item.avatar" :size="((props.uiOverride?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize()) as AvatarProps['size'])" v-bind="item.avatar" :class="ui.itemLeadingAvatar({ class: uiOverride?.itemLeadingAvatar, active })" />
<UAvatar v-else-if="item.avatar" :size="((props.uiOverride?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize().split(' ').pop()) as AvatarProps['size'])" v-bind="item.avatar" :class="ui.itemLeadingAvatar({ class: uiOverride?.itemLeadingAvatar, active })" />
</slot>

<span v-if="get(item, props.labelKey as string) || !!slots[item.slot ? `${item.slot}-label`: 'item-label']" :class="ui.itemLabel({ class: uiOverride?.itemLabel, active })">
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/components/Input.vue
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ onMounted(() => {
<span v-if="isLeading || !!avatar || !!slots.leading" :class="ui.leading({ class: props.ui?.leading })">
<slot name="leading">
<UIcon v-if="isLeading && leadingIconName" :name="leadingIconName" :class="ui.leadingIcon({ class: props.ui?.leadingIcon })" />
<UAvatar v-else-if="!!avatar" :size="((props.ui?.leadingAvatarSize || ui.leadingAvatarSize()) as AvatarProps['size'])" v-bind="avatar" :class="ui.leadingAvatar({ class: props.ui?.leadingAvatar })" />
<UAvatar v-else-if="!!avatar" :size="((props.ui?.leadingAvatarSize || ui.leadingAvatarSize()).split(' ').pop() as AvatarProps['size'])" v-bind="avatar" :class="ui.leadingAvatar({ class: props.ui?.leadingAvatar })" />
</slot>
</span>

Expand Down
4 changes: 2 additions & 2 deletions src/runtime/components/InputMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ defineExpose({
<span v-if="isLeading || !!avatar || !!slots.leading" :class="ui.leading({ class: props.ui?.leading })">
<slot name="leading" :model-value="(modelValue as M extends true ? T[] : T)" :open="open" :ui="ui">
<UIcon v-if="isLeading && leadingIconName" :name="leadingIconName" :class="ui.leadingIcon({ class: props.ui?.leadingIcon })" />
<UAvatar v-else-if="!!avatar" :size="((props.ui?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize()) as AvatarProps['size'])" v-bind="avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar })" />
<UAvatar v-else-if="!!avatar" :size="((props.ui?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize().split(' ').pop()) as AvatarProps['size'])" v-bind="avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar })" />
</slot>
</span>

Expand Down Expand Up @@ -419,7 +419,7 @@ defineExpose({
<slot name="item" :item="(item as T)" :index="index">
<slot name="item-leading" :item="(item as T)" :index="index">
<UIcon v-if="item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ class: props.ui?.itemLeadingIcon })" />
<UAvatar v-else-if="item.avatar" :size="((props.ui?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize()) as AvatarProps['size'])" v-bind="item.avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar })" />
<UAvatar v-else-if="item.avatar" :size="((props.ui?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize()).split(' ').pop() as AvatarProps['size'])" v-bind="item.avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar })" />
<UChip
v-else-if="item.chip"
:size="((props.ui?.itemLeadingChipSize || ui.itemLeadingChipSize()) as ChipProps['size'])"
Expand Down
4 changes: 2 additions & 2 deletions src/runtime/components/Select.vue
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ function onUpdateOpen(value: boolean) {
<span v-if="isLeading || !!avatar || !!slots.leading" :class="ui.leading({ class: props.ui?.leading })">
<slot name="leading" :model-value="(modelValue as M extends true ? AcceptableValue[] : AcceptableValue)" :open="open" :ui="ui">
<UIcon v-if="isLeading && leadingIconName" :name="leadingIconName" :class="ui.leadingIcon({ class: props.ui?.leadingIcon })" />
<UAvatar v-else-if="!!avatar" :size="((props.ui?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize()) as AvatarProps['size'])" v-bind="avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar })" />
<UAvatar v-else-if="!!avatar" :size="((props.ui?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize().split(' ').pop()) as AvatarProps['size'])" v-bind="avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar })" />
</slot>
</span>

Expand Down Expand Up @@ -240,7 +240,7 @@ function onUpdateOpen(value: boolean) {
<slot name="item" :item="(item as T)" :index="index">
<slot name="item-leading" :item="(item as T)" :index="index">
<UIcon v-if="item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ class: props.ui?.itemLeadingIcon })" />
<UAvatar v-else-if="item.avatar" :size="((props.ui?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize()) as AvatarProps['size'])" v-bind="item.avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar })" />
<UAvatar v-else-if="item.avatar" :size="((props.ui?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize().split(' ').pop()) as AvatarProps['size'])" v-bind="item.avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar })" />
<UChip
v-else-if="item.chip"
:size="((props.ui?.itemLeadingChipSize || ui.itemLeadingChipSize()) as ChipProps['size'])"
Expand Down
4 changes: 2 additions & 2 deletions src/runtime/components/SelectMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ function onUpdateOpen(value: boolean) {
<span v-if="isLeading || !!avatar || !!slots.leading" :class="ui.leading({ class: props.ui?.leading })">
<slot name="leading" :model-value="(modelValue as M extends true ? T[] : T)" :open="open" :ui="ui">
<UIcon v-if="isLeading && leadingIconName" :name="leadingIconName" :class="ui.leadingIcon({ class: props.ui?.leadingIcon })" />
<UAvatar v-else-if="!!avatar" :size="((props.ui?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize()) as AvatarProps['size'])" v-bind="avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar })" />
<UAvatar v-else-if="!!avatar" :size="((props.ui?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize().split(' ').pop()) as AvatarProps['size'])" v-bind="avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar })" />
</slot>
</span>

Expand Down Expand Up @@ -351,7 +351,7 @@ function onUpdateOpen(value: boolean) {
<slot name="item" :item="(item as T)" :index="index">
<slot name="item-leading" :item="(item as T)" :index="index">
<UIcon v-if="item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ class: props.ui?.itemLeadingIcon })" />
<UAvatar v-else-if="item.avatar" :size="((props.ui?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize()) as AvatarProps['size'])" v-bind="item.avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar })" />
<UAvatar v-else-if="item.avatar" :size="((props.ui?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize().split(' ').pop()) as AvatarProps['size'])" v-bind="item.avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar })" />
<UChip
v-else-if="item.chip"
:size="((props.ui?.itemLeadingChipSize || ui.itemLeadingChipSize()) as ChipProps['size'])"
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/components/Separator.vue
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ const ui = computed(() => separator({
<slot>
<span v-if="label" :class="ui.label({ class: props.ui?.label })">{{ label }}</span>
<UIcon v-else-if="icon" :name="icon" :class="ui.icon({ class: props.ui?.icon })" />
<UAvatar v-else-if="avatar" :size="((props.ui?.avatarSize || ui.avatarSize()) as AvatarProps['size'])" v-bind="avatar" :class="ui.avatar({ class: props.ui?.avatar })" />
<UAvatar v-else-if="avatar" :size="((props.ui?.avatarSize || ui.avatarSize().split(' ').pop()) as AvatarProps['size'])" v-bind="avatar" :class="ui.avatar({ class: props.ui?.avatar })" />
</slot>
</div>

Expand Down
2 changes: 1 addition & 1 deletion src/runtime/components/Tabs.vue
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ const ui = computed(() => tabs({
<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">
<UIcon v-if="item.icon" :name="item.icon" :class="ui.leadingIcon({ class: props.ui?.leadingIcon })" />
<UAvatar v-else-if="item.avatar" :size="((props.ui?.leadingAvatarSize || ui.leadingAvatarSize()) as AvatarProps['size'])" v-bind="item.avatar" :class="ui.leadingAvatar({ class: props.ui?.leadingAvatar })" />
<UAvatar v-else-if="item.avatar" :size="((props.ui?.leadingAvatarSize || ui.leadingAvatarSize().split(' ').pop()) as AvatarProps['size'])" v-bind="item.avatar" :class="ui.leadingAvatar({ class: props.ui?.leadingAvatar })" />
</slot>

<span v-if="get(item, props.labelKey as string) || !!slots.default" :class="ui.label({ class: props.ui?.label })">
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/components/Toast.vue
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ defineExpose({
:style="{ '--height': height }"
>
<slot name="leading">
<UAvatar v-if="avatar" :size="((props.ui?.avatarSize || ui.avatarSize()) as AvatarProps['size'])" v-bind="avatar" :class="ui.avatar({ class: props.ui?.avatar })" />
<UAvatar v-if="avatar" :size="((props.ui?.avatarSize || ui.avatarSize().split(' ').pop()) as AvatarProps['size'])" v-bind="avatar" :class="ui.avatar({ class: props.ui?.avatar })" />
<UIcon v-else-if="icon" :name="icon" :class="ui.icon({ class: props.ui?.icon })" />
</slot>

Expand Down
22 changes: 13 additions & 9 deletions src/runtime/index.css
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
@import './keyframes.css';

@variant dark (&:where(.dark, .dark *));

@layer base {
body {
@apply antialiased text-[var(--ui-text)] bg-[var(--ui-bg)] scheme-light dark:scheme-dark;
color: var(--ui-text);
background-color: var(--ui-bg);
color-scheme: light;
}

body:where(.dark, .dark *) {
color-scheme: dark;
}

:root {
Expand All @@ -14,7 +18,7 @@
--ui-text: var(--ui-color-neutral-700);
--ui-text-highlighted: var(--ui-color-neutral-900);

--ui-bg: var(--color-white);
--ui-bg: var(--ui-color-white);
--ui-bg-muted: var(--ui-color-neutral-50);
--ui-bg-elevated: var(--ui-color-neutral-100);
--ui-bg-accented: var(--ui-color-neutral-200);
Expand All @@ -25,26 +29,26 @@
--ui-border-accented: var(--ui-color-neutral-300);
--ui-border-inverted: var(--ui-color-neutral-900);

--ui-radius: var(--radius-sm);
--ui-container: var(--container-7xl);
--ui-radius: var(--ui-radius-sm);
--ui-container: var(--ui-container-7xl);
}

.dark {
--ui-text-dimmed: var(--ui-color-neutral-500);
--ui-text-muted: var(--ui-color-neutral-400);
--ui-text-toned: var(--ui-color-neutral-300);
--ui-text: var(--ui-color-neutral-200);
--ui-text-highlighted: var(--color-white);
--ui-text-highlighted: var(--ui-color-white);

--ui-bg: var(--ui-color-neutral-900);
--ui-bg-muted: var(--ui-color-neutral-800);
--ui-bg-elevated: var(--ui-color-neutral-800);
--ui-bg-accented: var(--ui-color-neutral-700);
--ui-bg-inverted: var(--color-white);
--ui-bg-inverted: var(--ui-color-white);

--ui-border: var(--ui-color-neutral-800);
--ui-border-muted: var(--ui-color-neutral-700);
--ui-border-accented: var(--ui-color-neutral-700);
--ui-border-inverted: var(--color-white);
--ui-border-inverted: var(--ui-color-white);
}
}
Loading