{{ currentStep?.content }}
diff --git a/src/runtime/components/Tabs.vue b/src/runtime/components/Tabs.vue
index 3fc4bbea12..07e53c2159 100644
--- a/src/runtime/components/Tabs.vue
+++ b/src/runtime/components/Tabs.vue
@@ -25,11 +25,12 @@ export interface TabsItem {
/** A unique value for the tab item. Defaults to the index. */
value?: string | number
disabled?: boolean
+ [key: string]: any
}
type TabsVariants = VariantProps
-export interface TabsProps extends Pick, 'defaultValue' | 'modelValue' | 'activationMode' | 'unmountOnHide'> {
+export interface TabsProps extends Pick, 'defaultValue' | 'modelValue' | 'activationMode' | 'unmountOnHide'> {
/**
* The element or component this component should render as.
* @defaultValue 'div'
@@ -69,14 +70,14 @@ export interface TabsProps extends Pick, 'defa
export interface TabsEmits extends TabsRootEmits {}
-type SlotProps = (props: { item: T, index: number }) => any
+type SlotProps = (props: { item: T, index: number }) => any
-export type TabsSlots = {
+export type TabsSlots = {
leading: SlotProps
default: SlotProps
trailing: SlotProps
content: SlotProps
-} & DynamicSlots>
+} & DynamicSlots
@@ -129,7 +130,7 @@ const ui = computed(() => tabs({
-
+
{{ item.content }}
diff --git a/src/runtime/components/Toast.vue b/src/runtime/components/Toast.vue
index ef2dc7ac96..6fb459e8fb 100644
--- a/src/runtime/components/Toast.vue
+++ b/src/runtime/components/Toast.vue
@@ -66,7 +66,7 @@ export interface ToastSlots {
title(props?: {}): any
description(props?: {}): any
actions(props?: {}): any
- close(props: { ui: any }): any
+ close(props: { ui: ReturnType }): any
}
diff --git a/src/runtime/components/Tree.vue b/src/runtime/components/Tree.vue
index 919513a690..0f2547b45c 100644
--- a/src/runtime/components/Tree.vue
+++ b/src/runtime/components/Tree.vue
@@ -6,7 +6,14 @@ import type { AppConfig } from '@nuxt/schema'
import _appConfig from '#build/app.config'
import theme from '#build/ui/tree'
import { tv } from '../utils/tv'
-import type { PartialString, DynamicSlots, MaybeMultipleModelValue, SelectItemKey } from '../types/utils'
+import type {
+ DynamicSlots,
+ GetItemKeys,
+ GetModelValue,
+ GetModelValueEmits,
+ NestedItem,
+ PartialString
+} from '../types/utils'
const appConfig = _appConfig as AppConfig & { ui: { tree: Partial } }
@@ -31,9 +38,10 @@ export type TreeItem = {
children?: TreeItem[]
onToggle?(e: Event): void
onSelect?(e?: Event): void
+ [key: string]: any
}
-export interface TreeProps | undefined = undefined> extends Pick, 'expanded' | 'defaultExpanded' | 'selectionBehavior' | 'propagateSelect' | 'disabled'> {
+export interface TreeProps = 'value', M extends boolean = false> extends Pick, 'expanded' | 'defaultExpanded' | 'selectionBehavior' | 'propagateSelect' | 'disabled'> {
/**
* The element or component this component should render as.
* @defaultValue 'ul'
@@ -51,12 +59,12 @@ export interface TreeProps
/**
* The icon displayed on the right side of a parent node.
* @defaultValue appConfig.ui.icons.chevronDown
@@ -75,33 +83,34 @@ export interface TreeProps
+ modelValue?: GetModelValue
/** The value of the Tree when initially rendered. Use when you do not need to control the state of the Tree. */
- defaultValue?: MaybeMultipleModelValue
+ defaultValue?: GetModelValue
/** Whether multiple options can be selected or not. */
multiple?: M & boolean
class?: any
ui?: PartialString
}
-export type TreeEmits = Omit & {
- 'update:modelValue': [payload: MaybeMultipleModelValue]
-}
+export type TreeEmits | undefined, M extends boolean> = Omit & GetModelValueEmits
-type SlotProps = (props: { item: T, index: number, level: number, expanded: boolean, selected: boolean }) => any
+type SlotProps = (props: { item: T, index: number, level: number, expanded: boolean, selected: boolean }) => any
-export type TreeSlots = {
+export type TreeSlots<
+ A extends TreeItem[] = TreeItem[],
+ T extends NestedItem = NestedItem
+> = {
'item': SlotProps
'item-leading': SlotProps
'item-label': SlotProps
'item-trailing': SlotProps
-} & DynamicSlots>
+} & DynamicSlots
-
@@ -165,8 +179,8 @@ const defaultExpanded = computed(() => props.defaultExpanded ?? props.items?.fla
@select="item.onSelect"
>
-
+
diff --git a/src/runtime/types/utils.ts b/src/runtime/types/utils.ts
index e28fa20971..91ad5d63d9 100644
--- a/src/runtime/types/utils.ts
+++ b/src/runtime/types/utils.ts
@@ -1,3 +1,4 @@
+import type { AcceptableValue as _AcceptableValue } from 'reka-ui'
import type { VNode } from 'vue'
export interface TightMap {
@@ -14,8 +15,19 @@ export type DeepPartial = {
[key: string]: O | TightMap
}
-export type DynamicSlots =
- Record & (Slot extends string ? Record : Record)
+export type DynamicSlots<
+ T extends { slot?: string },
+ S extends string | undefined = undefined,
+ D extends object = {}
+> = {
+ [
+ K in T['slot'] as K extends string
+ ? S extends string
+ ? (K | `${K}-${S}`)
+ : K
+ : never
+ ]?: (props: { item: Extract } & D) => any
+}
export type GetObjectField = MaybeObject extends Record
? MaybeObject[Key]
@@ -25,18 +37,49 @@ export type PartialString = {
[K in keyof T]?: string
}
-export type MaybeArrayOfArray = T[] | T[][]
-export type MaybeArrayOfArrayItem = I extends Array ? T extends Array ? U : T : never
+export type AcceptableValue = Exclude<_AcceptableValue, Record>
+export type ArrayOrNested = T[] | T[][]
+export type NestedItem = T extends Array ? NestedItem : T
+type AllKeys = T extends any ? keyof T : never
+type NonCommonKeys = Exclude, keyof T>
+type PickTypeOf = K extends AllKeys
+ ? T extends { [k in K]?: any }
+ ? T[K]
+ : undefined
+ : never
+export type MergeTypes = {
+ [k in keyof T]: PickTypeOf;
+} & {
+ [k in NonCommonKeys]?: PickTypeOf;
+}
-export type SelectModelValue = (T extends Record ? V extends keyof T ? T[V] : DV : T) extends infer U ? M extends true ? U[] : U : never
+export type GetItemKeys = keyof Extract, object>
-export type SelectItemKey = T extends Record ? keyof T : string
+export type GetItemValue | undefined, T extends NestedItem = NestedItem> =
+T extends object
+ ? VK extends undefined
+ ? T
+ : VK extends keyof T
+ ? T[VK]
+ : never
+ : T
-export type SelectModelValueEmits = {
- 'update:modelValue': [payload: SelectModelValue]
-}
+export type GetModelValue<
+ T,
+ VK extends GetItemKeys | undefined,
+ M extends boolean
+> = M extends true
+ ? GetItemValue[]
+ : GetItemValue
-export type MaybeMultipleModelValue = (T extends infer U ? M extends true ? U[] : U : never)
+export type GetModelValueEmits<
+ T,
+ VK extends GetItemKeys | undefined,
+ M extends boolean
+> = {
+ /** Event handler called when the value changes. */
+ 'update:modelValue': [payload: GetModelValue]
+}
export type StringOrVNode =
| string
diff --git a/src/runtime/utils/index.ts b/src/runtime/utils/index.ts
index 45de77f638..b38eb579a6 100644
--- a/src/runtime/utils/index.ts
+++ b/src/runtime/utils/index.ts
@@ -81,3 +81,7 @@ export function compare(value?: T, currentValue?: T, comparator?: string | ((
return isEqual(value, currentValue)
}
+
+export function isArrayOfArray(item: A[] | A[][]): item is A[][] {
+ return Array.isArray(item[0])
+}
diff --git a/test/components/Accordion.spec.ts b/test/components/Accordion.spec.ts
index c9e2507edf..12cbc977b1 100644
--- a/test/components/Accordion.spec.ts
+++ b/test/components/Accordion.spec.ts
@@ -29,7 +29,7 @@ describe('Accordion', () => {
icon: 'i-lucide-wrench',
trailingIcon: 'i-lucide-sun',
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed neque elit, tristique placerat feugiat ac, facilisis vitae arcu. Proin eget egestas augue. Praesent ut sem nec arcu pellentesque aliquet. Duis dapibus diam vel metus tempus vulputate.',
- slot: 'custom'
+ slot: 'custom' as const
}]
const props = { items }
@@ -57,7 +57,7 @@ describe('Accordion', () => {
['with body slot', { props: { ...props, modelValue: '1' }, slots: { body: () => 'Body slot' } }],
['with custom slot', { props: { ...props, modelValue: '5' }, slots: { custom: () => 'Custom slot' } }],
['with custom body slot', { props: { ...props, modelValue: '5' }, slots: { 'custom-body': () => 'Custom body slot' } }]
- ])('renders %s correctly', async (nameOrHtml: string, options: { props?: AccordionProps, slots?: Partial> }) => {
+ ])('renders %s correctly', async (nameOrHtml: string, options: { props?: AccordionProps, slots?: Partial 'Custom slot' } & { 'custom-body': () => 'Custom body slot' }> }) => {
const html = await ComponentRender(nameOrHtml, options, Accordion)
expect(html).toMatchSnapshot()
})
diff --git a/test/components/Breadcrumb.spec.ts b/test/components/Breadcrumb.spec.ts
index ef567511d4..d074f42a71 100644
--- a/test/components/Breadcrumb.spec.ts
+++ b/test/components/Breadcrumb.spec.ts
@@ -37,7 +37,7 @@ describe('Breadcrumb', () => {
['with item-trailing slot', { props, slots: { 'item-trailing': () => 'Item trailing slot' } }],
['with custom slot', { props, slots: { custom: () => 'Custom slot' } }],
['with separator slot', { props, slots: { separator: () => '/' } }]
- ])('renders %s correctly', async (nameOrHtml: string, options: { props?: BreadcrumbProps