Skip to content
Draft
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions docs/app/components/content/ComponentExample.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { camelCase } from 'scule'
import { hash } from 'ohash'
import { useElementSize } from '@vueuse/core'
import { get, set } from '#ui/utils'
import type { SlotsReturn } from '#ui/types/utils'

const props = withDefaults(defineProps<{
name: string
Expand Down Expand Up @@ -65,8 +66,8 @@ const props = withDefaults(defineProps<{
})

const slots = defineSlots<{
options(props?: {}): any
code(props?: {}): any
options?(props?: {}): SlotsReturn
code?(props?: {}): SlotsReturn
}>()

const el = ref<HTMLElement | null>(null)
Expand Down
4 changes: 3 additions & 1 deletion docs/app/components/theme-picker/ThemePickerButton.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<script setup lang="ts">
import type { SlotsReturn } from '#ui/types/utils'

defineProps<{
label: string
icon?: string
Expand All @@ -7,7 +9,7 @@ defineProps<{
}>()

const slots = defineSlots<{
leading: () => any
leading?(): SlotsReturn
}>()
</script>

Expand Down
4 changes: 3 additions & 1 deletion playgrounds/nuxt/app/components/Matrix.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
<script setup lang="ts" generic="T extends Record<string, string[]>">
import type { SlotsReturn } from '#ui/types/utils'

const props = defineProps<{
attrs: T
containerClass?: string
containerProps?: Record<string, string>
}>()

defineSlots<{
default: (props?: { [K in keyof T]: T[K] extends (infer U)[] ? U : never }) => any
default?(props?: { [K in keyof T]: T[K] extends (infer U)[] ? U : never }): SlotsReturn
}>()

const combinations = computed(() => {
Expand Down
14 changes: 7 additions & 7 deletions src/runtime/components/Accordion.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { AccordionRootProps, AccordionRootEmits } from 'reka-ui'
import type { AppConfig } from '@nuxt/schema'
import theme from '#build/ui/accordion'
import type { IconProps } from '../types'
import type { DynamicSlots, GetItemKeys } from '../types/utils'
import type { DynamicSlots, GetItemKeys, SlotsReturn } from '../types/utils'
import type { ComponentConfig } from '../types/tv'

type Accordion = ComponentConfig<typeof theme, AppConfig, 'accordion'>
Expand Down Expand Up @@ -53,14 +53,14 @@ export interface AccordionProps<T extends AccordionItem = AccordionItem> extends

export interface AccordionEmits extends AccordionRootEmits {}

type SlotProps<T extends AccordionItem> = (props: { item: T, index: number, open: boolean }) => any
type SlotProps<T extends AccordionItem> = (props: { item: T, index: number, open: boolean }) => SlotsReturn

export type AccordionSlots<T extends AccordionItem = AccordionItem> = {
leading: SlotProps<T>
default: SlotProps<T>
trailing: SlotProps<T>
content: SlotProps<T>
body: SlotProps<T>
leading?: SlotProps<T>
default?: SlotProps<T>
trailing?: SlotProps<T>
content?: SlotProps<T>
body?: SlotProps<T>
} & DynamicSlots<T, 'body', { index: number, open: boolean }>

</script>
Expand Down
11 changes: 6 additions & 5 deletions src/runtime/components/Alert.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { AppConfig } from '@nuxt/schema'
import theme from '#build/ui/alert'
import type { AvatarProps, ButtonProps, IconProps } from '../types'
import type { ComponentConfig } from '../types/tv'
import type { SlotsReturn } from '../types/utils'

type Alert = ComponentConfig<typeof theme, AppConfig, 'alert'>

Expand Down Expand Up @@ -61,11 +62,11 @@ export interface AlertEmits {
}

export interface AlertSlots {
leading(props?: {}): any
title(props?: {}): any
description(props?: {}): any
actions(props?: {}): any
close(props: { ui: { [K in keyof Required<Alert['slots']>]: (props?: Record<string, any>) => string } }): any
leading?(props?: {}): SlotsReturn
title?(props?: {}): SlotsReturn
description?(props?: {}): SlotsReturn
actions?(props?: {}): SlotsReturn
close?(props: { ui: { [K in keyof Required<Alert['slots']>]: (props?: Record<string, any>) => string } }): SlotsReturn
}
</script>

Expand Down
3 changes: 2 additions & 1 deletion src/runtime/components/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import type { ConfigProviderProps, TooltipProviderProps } from 'reka-ui'
import type { ToasterProps } from '../types'
import type { Locale, Messages } from '../types/locale'
import type { SlotsReturn } from '../types/utils'

export interface AppProps<T extends Messages = Messages> extends Omit<ConfigProviderProps, 'useId' | 'dir' | 'locale'> {
tooltip?: TooltipProviderProps
Expand All @@ -11,7 +12,7 @@ export interface AppProps<T extends Messages = Messages> extends Omit<ConfigProv
}

export interface AppSlots {
default(props?: {}): any
default(props?: {}): SlotsReturn
}

export default {
Expand Down
21 changes: 11 additions & 10 deletions src/runtime/components/AuthForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import theme from '#build/ui/auth-form'
import type { ButtonProps, FormProps, FormFieldProps, SeparatorProps, InputProps, CheckboxProps, SelectMenuProps, PinInputProps, IconProps } from '../types'
import type { FormSchema, FormSubmitEvent, InferInput } from '../types/form'
import type { ComponentConfig } from '../types/tv'
import type { SlotsReturn } from '../types/utils'

type AuthForm = ComponentConfig<typeof theme, AppConfig, 'authForm'>

Expand Down Expand Up @@ -90,19 +91,19 @@ export type AuthFormEmits<T extends object> = {
submit: [payload: FormSubmitEvent<T>]
}

type DynamicFieldSlots<T, F, SlotProps = { field: F, state: T }> = Record<string, (props: SlotProps) => any> & Record<`${keyof T extends string ? keyof T : never}-field`, (props: SlotProps) => any>
type DynamicFieldSlots<T, F, SlotProps = { field: F, state: T }> = Record<string, (props: SlotProps) => SlotsReturn> & Record<`${keyof T extends string ? keyof T : never}-field`, (props: SlotProps) => SlotsReturn>

type DynamicFormFieldSlots<T> = Record<string, (props?: {}) => any> & Record<`${keyof T extends string ? keyof T : never}-${'label' | 'description' | 'hint' | 'help' | 'error'}`, (props?: {}) => any>
type DynamicFormFieldSlots<T> = Record<string, (props?: {}) => SlotsReturn> & Record<`${keyof T extends string ? keyof T : never}-${'label' | 'description' | 'hint' | 'help' | 'error'}`, (props?: {}) => SlotsReturn>

export type AuthFormSlots<T extends object = object, F extends AuthFormField = AuthFormField> = {
header(props?: {}): any
leading(props?: {}): any
title(props?: {}): any
description(props?: {}): any
providers(props?: {}): any
validation(props?: {}): any
submit(props: { loading: boolean }): any
footer(props?: {}): any
header?(props?: {}): SlotsReturn
leading?(props?: {}): SlotsReturn
title?(props?: {}): SlotsReturn
description?(props?: {}): SlotsReturn
providers?(props?: {}): SlotsReturn
validation?(props?: {}): SlotsReturn
submit?(props: { loading: boolean }): SlotsReturn
footer?(props?: {}): SlotsReturn
} & DynamicFieldSlots<T, F> & DynamicFormFieldSlots<T>

</script>
Expand Down
3 changes: 2 additions & 1 deletion src/runtime/components/Avatar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { AppConfig } from '@nuxt/schema'
import theme from '#build/ui/avatar'
import type { ChipProps, IconProps } from '../types'
import type { ComponentConfig } from '../types/tv'
import type { SlotsReturn } from '../types/utils'

type Avatar = ComponentConfig<typeof theme, AppConfig, 'avatar'>

Expand Down Expand Up @@ -30,7 +31,7 @@ export interface AvatarProps {
}

export interface AvatarSlots {
default(props?: {}): any
default(props?: {}): SlotsReturn
}
</script>

Expand Down
6 changes: 5 additions & 1 deletion src/runtime/components/AvatarGroup.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import type { AppConfig } from '@nuxt/schema'
import theme from '#build/ui/avatar-group'
import type { ComponentConfig } from '../types/tv'
import type { SlotsReturn } from '../types/utils'

type AvatarGroup = ComponentConfig<typeof theme, AppConfig, 'avatarGroup'>

Expand All @@ -24,7 +25,7 @@ export interface AvatarGroupProps {
}

export interface AvatarGroupSlots {
default(props?: {}): any
default?(props?: {}): SlotsReturn
}
</script>

Expand All @@ -49,6 +50,9 @@ const max = computed(() => typeof props.max === 'string' ? Number.parseInt(props

const children = computed(() => {
let children = slots.default?.()
if (!Array.isArray(children)) {
children = children ? [children] : []
}
if (children?.length) {
children = children.flatMap((child: any) => {
if (typeof child.type === 'symbol') {
Expand Down
7 changes: 4 additions & 3 deletions src/runtime/components/Badge.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import theme from '#build/ui/badge'
import type { UseComponentIconsProps } from '../composables/useComponentIcons'
import type { AvatarProps } from '../types'
import type { ComponentConfig } from '../types/tv'
import type { SlotsReturn } from '../types/utils'

type Badge = ComponentConfig<typeof theme, AppConfig, 'badge'>

Expand Down Expand Up @@ -33,9 +34,9 @@ export interface BadgeProps extends Omit<UseComponentIconsProps, 'loading' | 'lo
}

export interface BadgeSlots {
leading(props?: {}): any
default(props?: {}): any
trailing(props?: {}): any
leading?(props?: {}): SlotsReturn
default?(props?: {}): SlotsReturn
trailing?(props?: {}): SlotsReturn
}
</script>

Expand Down
9 changes: 5 additions & 4 deletions src/runtime/components/Banner.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { AppConfig } from '@nuxt/schema'
import theme from '#build/ui/banner'
import type { ButtonProps, IconProps, LinkProps } from '../types'
import type { ComponentConfig } from '../types/tv'
import type { SlotsReturn } from '../types/utils'

type Banner = ComponentConfig<typeof theme, AppConfig, 'banner'>

Expand Down Expand Up @@ -53,10 +54,10 @@ export interface BannerProps {
}

export interface BannerSlots {
leading(props?: {}): any
title(props?: {}): any
actions(props?: {}): any
close(props: { ui: any }): any
leading?(props?: {}): SlotsReturn
title?(props?: {}): SlotsReturn
actions?(props?: {}): SlotsReturn
close?(props: { ui: any }): SlotsReturn
}

export interface BannerEmits {
Expand Down
17 changes: 9 additions & 8 deletions src/runtime/components/BlogPost.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { AppConfig } from '@nuxt/schema'
import theme from '#build/ui/blog-post'
import type { BadgeProps, LinkProps, UserProps } from '../types'
import type { ComponentConfig } from '../types/tv'
import type { SlotsReturn } from '../types/utils'

type BlogPost = ComponentConfig<typeof theme, AppConfig, 'blogPost'>

Expand Down Expand Up @@ -43,14 +44,14 @@ export interface BlogPostProps {
}

export interface BlogPostSlots {
date(props?: {}): any
badge(props?: {}): any
title(props?: {}): any
description(props?: {}): any
authors(props?: {}): any
header(props?: {}): any
body(props?: {}): any
footer(props?: {}): any
date?(props?: {}): SlotsReturn
badge?(props?: {}): SlotsReturn
title?(props?: {}): SlotsReturn
description?(props?: {}): SlotsReturn
authors?(props?: {}): SlotsReturn
header?(props?: {}): SlotsReturn
body?(props?: {}): SlotsReturn
footer?(props?: {}): SlotsReturn
}
</script>

Expand Down
3 changes: 2 additions & 1 deletion src/runtime/components/BlogPosts.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { AppConfig } from '@nuxt/schema'
import theme from '#build/ui/blog-posts'
import type { BlogPostProps } from '../types'
import type { ComponentConfig } from '../types/tv'
import type { SlotsReturn } from '../types/utils'

type BlogPosts = ComponentConfig<typeof theme, AppConfig, 'blogPosts'>

Expand All @@ -22,7 +23,7 @@ export interface BlogPostsProps {
}

export interface BlogPostsSlots {
default(props?: {}): any
default?(props?: {}): SlotsReturn
}
</script>

Expand Down
22 changes: 11 additions & 11 deletions src/runtime/components/Breadcrumb.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import type { AppConfig } from '@nuxt/schema'
import theme from '#build/ui/breadcrumb'
import type { AvatarProps, IconProps, LinkProps } from '../types'
import type { DynamicSlots, GetItemKeys } from '../types/utils'
import type { DynamicSlots, GetItemKeys, SlotsReturn } from '../types/utils'
import type { ComponentConfig } from '../types/tv'

type Breadcrumb = ComponentConfig<typeof theme, AppConfig, 'breadcrumb'>
Expand Down Expand Up @@ -43,14 +43,14 @@ export interface BreadcrumbProps<T extends BreadcrumbItem = BreadcrumbItem> {
ui?: Breadcrumb['slots']
}

type SlotProps<T extends BreadcrumbItem> = (props: { item: T, index: number, active?: boolean }) => any
type SlotProps<T extends BreadcrumbItem> = (props: { item: T, index: number, active?: boolean }) => SlotsReturn

export type BreadcrumbSlots<T extends BreadcrumbItem = BreadcrumbItem> = {
'item': SlotProps<T>
'item-leading': SlotProps<T>
'item-label': SlotProps<T>
'item-trailing': SlotProps<T>
'separator': any
'item'?: SlotProps<T>
'item-leading'?: SlotProps<T>
'item-label'?: SlotProps<T>
'item-trailing'?: SlotProps<T>
'separator'?: (props?: {}) => SlotsReturn
} & DynamicSlots<T, 'leading' | 'label' | 'trailing', { index: number, active?: boolean }>

</script>
Expand Down Expand Up @@ -90,19 +90,19 @@ const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.breadcrumb |
<li :class="ui.item({ class: [props.ui?.item, item.ui?.item] })">
<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({ class: [props.ui?.link, item.ui?.link, item.class], active: index === items!.length - 1, disabled: !!item.disabled, to: !!item.to })">
<slot :name="((item.slot || 'item') as keyof BreadcrumbSlots<T>)" :item="item" :index="index">
<slot :name="((item.slot ? `${item.slot}-leading`: 'item-leading') as keyof BreadcrumbSlots<T>)" :item="item" :active="index === items!.length - 1" :index="index">
<slot :name="((item.slot || 'item') as keyof BreadcrumbSlots<T>)" :item="(item as Extract<T, { slot: string }>)" :index="index">
<slot :name="((item.slot ? `${item.slot}-leading`: 'item-leading') as keyof BreadcrumbSlots<T>)" :item="(item as Extract<T, { slot: string }>)" :active="index === items!.length - 1" :index="index">
<UIcon v-if="item.icon" :name="item.icon" :class="ui.linkLeadingIcon({ class: [props.ui?.linkLeadingIcon, item.ui?.linkLeadingIcon], active: index === items!.length - 1 })" />
<UAvatar v-else-if="item.avatar" :size="((props.ui?.linkLeadingAvatarSize || ui.linkLeadingAvatarSize()) as AvatarProps['size'])" v-bind="item.avatar" :class="ui.linkLeadingAvatar({ class: [props.ui?.linkLeadingAvatar, item.ui?.linkLeadingAvatar], active: index === items!.length - 1 })" />
</slot>

<span v-if="get(item, props.labelKey as string) || !!slots[(item.slot ? `${item.slot}-label`: 'item-label') as keyof BreadcrumbSlots<T>]" :class="ui.linkLabel({ class: [props.ui?.linkLabel, item.ui?.linkLabel] })">
<slot :name="((item.slot ? `${item.slot}-label`: 'item-label') as keyof BreadcrumbSlots<T>)" :item="item" :active="index === items!.length - 1" :index="index">
<slot :name="((item.slot ? `${item.slot}-label`: 'item-label') as keyof BreadcrumbSlots<T>)" :item="(item as Extract<T, { slot: string }>)" :active="index === items!.length - 1" :index="index">
{{ get(item, props.labelKey as string) }}
</slot>
</span>

<slot :name="((item.slot ? `${item.slot}-trailing`: 'item-trailing') as keyof BreadcrumbSlots<T>)" :item="item" :active="index === items!.length - 1" :index="index" />
<slot :name="((item.slot ? `${item.slot}-trailing`: 'item-trailing') as keyof BreadcrumbSlots<T>)" :item="(item as Extract<T, { slot: string }>)" :active="index === items!.length - 1" :index="index" />
</slot>
</ULinkBase>
</ULink>
Expand Down
9 changes: 5 additions & 4 deletions src/runtime/components/Button.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
<script lang="ts">
import type { Ref } from 'vue'
import type { AppConfig } from '@nuxt/schema'
import theme from '#build/ui/button'
import type { UseComponentIconsProps } from '../composables/useComponentIcons'
import type { LinkProps, AvatarProps } from '../types'
import type { ComponentConfig } from '../types/tv'
import type { SlotsReturn } from '../types/utils'

type Button = ComponentConfig<typeof theme, AppConfig, 'button'>

Expand Down Expand Up @@ -35,15 +37,14 @@ export interface ButtonProps extends UseComponentIconsProps, Omit<LinkProps, 'ra
}

export interface ButtonSlots {
leading(props?: {}): any
default(props?: {}): any
trailing(props?: {}): any
leading?(props?: {}): SlotsReturn
default?(props?: {}): SlotsReturn
trailing?(props?: {}): SlotsReturn
}
</script>

<script setup lang="ts">
import { computed, ref, inject } from 'vue'
import type { Ref } from 'vue'
import { defu } from 'defu'
import { useForwardProps } from 'reka-ui'
import { useAppConfig } from '#imports'
Expand Down
Loading
Loading