mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-14 20:19:34 +01:00
fix(components): improve generic types (#3331)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
This commit is contained in:
@@ -7,7 +7,14 @@ import _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/dropdown-menu'
|
||||
import { tv } from '../utils/tv'
|
||||
import type { AvatarProps, KbdProps, LinkProps } from '../types'
|
||||
import type { DynamicSlots, PartialString, EmitsToProps } from '../types/utils'
|
||||
import type {
|
||||
ArrayOrNested,
|
||||
DynamicSlots,
|
||||
MergeTypes,
|
||||
NestedItem,
|
||||
PartialString,
|
||||
EmitsToProps
|
||||
} from '../types/utils'
|
||||
|
||||
const appConfigDropdownMenu = _appConfig as AppConfig & { ui: { dropdownMenu: Partial<typeof theme> } }
|
||||
|
||||
@@ -36,17 +43,18 @@ export interface DropdownMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'cust
|
||||
checked?: boolean
|
||||
open?: boolean
|
||||
defaultOpen?: boolean
|
||||
children?: DropdownMenuItem[] | DropdownMenuItem[][]
|
||||
children?: ArrayOrNested<DropdownMenuItem>
|
||||
onSelect?(e: Event): void
|
||||
onUpdateChecked?(checked: boolean): void
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
export interface DropdownMenuProps<T> extends Omit<DropdownMenuRootProps, 'dir'> {
|
||||
export interface DropdownMenuProps<T extends ArrayOrNested<DropdownMenuItem> = ArrayOrNested<DropdownMenuItem>> extends Omit<DropdownMenuRootProps, 'dir'> {
|
||||
/**
|
||||
* @defaultValue 'md'
|
||||
*/
|
||||
size?: DropdownMenuVariants['size']
|
||||
items?: T[] | T[][]
|
||||
items?: T
|
||||
/**
|
||||
* The icon displayed when an item is checked.
|
||||
* @defaultValue appConfig.ui.icons.check
|
||||
@@ -85,7 +93,7 @@ export interface DropdownMenuProps<T> extends Omit<DropdownMenuRootProps, 'dir'>
|
||||
* The key used to get the label from the item.
|
||||
* @defaultValue 'label'
|
||||
*/
|
||||
labelKey?: string
|
||||
labelKey?: keyof NestedItem<T>
|
||||
disabled?: boolean
|
||||
class?: any
|
||||
ui?: PartialString<typeof dropdownMenu.slots>
|
||||
@@ -93,19 +101,22 @@ export interface DropdownMenuProps<T> extends Omit<DropdownMenuRootProps, 'dir'>
|
||||
|
||||
export interface DropdownMenuEmits extends DropdownMenuRootEmits {}
|
||||
|
||||
type SlotProps<T> = (props: { item: T, active?: boolean, index: number }) => any
|
||||
type SlotProps<T extends DropdownMenuItem> = (props: { item: T, active?: boolean, index: number }) => any
|
||||
|
||||
export type DropdownMenuSlots<T extends { slot?: string }> = {
|
||||
export type DropdownMenuSlots<
|
||||
A extends ArrayOrNested<DropdownMenuItem> = ArrayOrNested<DropdownMenuItem>,
|
||||
T extends NestedItem<A> = NestedItem<A>
|
||||
> = {
|
||||
'default'(props: { open: boolean }): any
|
||||
'item': SlotProps<T>
|
||||
'item-leading': SlotProps<T>
|
||||
'item-label': SlotProps<T>
|
||||
'item-trailing': SlotProps<T>
|
||||
} & DynamicSlots<T, SlotProps<T>>
|
||||
} & DynamicSlots<MergeTypes<T>, 'leading' | 'label' | 'trailing', { active?: boolean, index: number }>
|
||||
|
||||
</script>
|
||||
|
||||
<script setup lang="ts" generic="T extends DropdownMenuItem">
|
||||
<script setup lang="ts" generic="T extends ArrayOrNested<DropdownMenuItem>">
|
||||
import { computed, toRef } from 'vue'
|
||||
import { defu } from 'defu'
|
||||
import { DropdownMenuRoot, DropdownMenuTrigger, DropdownMenuArrow, useForwardPropsEmits } from 'reka-ui'
|
||||
@@ -125,7 +136,7 @@ const slots = defineSlots<DropdownMenuSlots<T>>()
|
||||
const rootProps = useForwardPropsEmits(reactivePick(props, 'defaultOpen', 'open', 'modal'), emits)
|
||||
const contentProps = toRef(() => defu(props.content, { side: 'bottom', sideOffset: 8, collisionPadding: 8 }) as DropdownMenuContentProps)
|
||||
const arrowProps = toRef(() => props.arrow as DropdownMenuArrowProps)
|
||||
const proxySlots = omit(slots, ['default']) as Record<string, DropdownMenuSlots<T>[string]>
|
||||
const proxySlots = omit(slots, ['default'])
|
||||
|
||||
const ui = computed(() => dropdownMenu({
|
||||
size: props.size
|
||||
@@ -145,13 +156,13 @@ const ui = computed(() => dropdownMenu({
|
||||
v-bind="contentProps"
|
||||
:items="items"
|
||||
:portal="portal"
|
||||
:label-key="labelKey"
|
||||
:label-key="(labelKey as keyof NestedItem<T>)"
|
||||
:checked-icon="checkedIcon"
|
||||
:loading-icon="loadingIcon"
|
||||
:external-icon="externalIcon"
|
||||
>
|
||||
<template v-for="(_, name) in proxySlots" #[name]="slotData">
|
||||
<slot :name="name" v-bind="slotData" />
|
||||
<slot :name="(name as keyof DropdownMenuSlots<T>)" v-bind="slotData" />
|
||||
</template>
|
||||
|
||||
<DropdownMenuArrow v-if="!!arrow" v-bind="arrowProps" :class="ui.arrow({ class: props.ui?.arrow })" />
|
||||
|
||||
Reference in New Issue
Block a user