feat(DropdownMenu/ContextMenu): handle color field in items (#2510)

Co-authored-by: Benjamin Canac <canacb1@gmail.com>
This commit is contained in:
Alex
2024-11-06 01:21:01 +05:00
committed by GitHub
parent 2d52834529
commit f66c96e277
16 changed files with 214 additions and 54 deletions

View File

@@ -12,9 +12,12 @@ const appConfig = _appConfig as AppConfig & { ui: { contextMenu: Partial<typeof
const contextMenu = tv({ extend: tv(theme), ...(appConfig.ui?.contextMenu || {}) })
type ContextMenuVariants = VariantProps<typeof contextMenu>
export interface ContextMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'custom'> {
label?: string
icon?: string
color?: ContextMenuVariants['color']
avatar?: AvatarProps
content?: Omit<ContextMenuContentProps, 'as' | 'asChild' | 'forceMount'>
kbds?: KbdProps['value'][] | KbdProps[]
@@ -34,8 +37,6 @@ export interface ContextMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'custo
onUpdateChecked?(checked: boolean): void
}
type ContextMenuVariants = VariantProps<typeof contextMenu>
export interface ContextMenuProps<T> extends Omit<ContextMenuRootProps, 'dir'> {
size?: ContextMenuVariants['size']
items?: T[] | T[][]

View File

@@ -52,8 +52,8 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
<DefineItemTemplate v-slot="{ item, active, 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">
<UIcon v-if="item.loading" :name="loadingIcon || appConfig.ui.icons.loading" :class="ui.itemLeadingIcon({ class: uiOverride?.itemLeadingIcon, loading: true })" />
<UIcon v-else-if="item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ class: uiOverride?.itemLeadingIcon, active })" />
<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 })" />
</slot>
@@ -62,19 +62,19 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
{{ get(item, props.labelKey as string) }}
</slot>
<UIcon v-if="item.target === '_blank'" :name="appConfig.ui.icons.external" :class="ui.itemLabelExternalIcon({ class: uiOverride?.itemLabelExternalIcon, active })" />
<UIcon v-if="item.target === '_blank'" :name="appConfig.ui.icons.external" :class="ui.itemLabelExternalIcon({ class: uiOverride?.itemLabelExternalIcon, color: item?.color, active })" />
</span>
<span :class="ui.itemTrailing({ class: uiOverride?.itemTrailing })">
<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({ class: uiOverride?.itemTrailingIcon, active })" />
<UIcon v-if="item.children?.length" :name="appConfig.ui.icons.chevronRight" :class="ui.itemTrailingIcon({ class: uiOverride?.itemTrailingIcon, color: item?.color, active })" />
<span v-else-if="item.kbds?.length" :class="ui.itemTrailingKbds({ class: uiOverride?.itemTrailingKbds })">
<UKbd v-for="(kbd, kbdIndex) in item.kbds" :key="kbdIndex" :size="((props.uiOverride?.itemTrailingKbdsSize || ui.itemTrailingKbdsSize()) as KbdProps['size'])" v-bind="typeof kbd === 'string' ? { value: kbd } : kbd" />
</span>
</slot>
<ContextMenu.ItemIndicator as-child>
<UIcon :name="checkedIcon || appConfig.ui.icons.check" :class="ui.itemTrailingIcon({ class: uiOverride?.itemTrailingIcon })" />
<UIcon :name="checkedIcon || appConfig.ui.icons.check" :class="ui.itemTrailingIcon({ class: uiOverride?.itemTrailingIcon, color: item?.color })" />
</ContextMenu.ItemIndicator>
</span>
</slot>
@@ -94,7 +94,7 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
type="button"
:disabled="item.disabled"
:text-value="get(item, props.labelKey as string)"
:class="ui.item({ class: uiOverride?.item })"
:class="ui.item({ class: uiOverride?.item, color: item?.color })"
>
<ReuseItemTemplate :item="item" :index="index" />
</ContextMenu.SubTrigger>
@@ -122,7 +122,7 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
:checked="item.checked"
:disabled="item.disabled"
:text-value="get(item, props.labelKey as string)"
:class="ui.item({ class: [uiOverride?.item, item.class] })"
:class="ui.item({ class: [uiOverride?.item, item.class], color: item?.color })"
@update:checked="item.onUpdateChecked"
@select="item.onSelect"
>
@@ -136,7 +136,7 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
@select="item.onSelect"
>
<ULink v-slot="{ active, ...slotProps }" v-bind="pickLinkProps(item as Omit<ContextMenuItem, 'type'>)" custom>
<ULinkBase v-bind="slotProps" :class="ui.item({ class: [uiOverride?.item, item.class], active })">
<ULinkBase v-bind="slotProps" :class="ui.item({ class: [uiOverride?.item, item.class], active, color: item?.color })">
<ReuseItemTemplate :item="item" :active="active" :index="index" />
</ULinkBase>
</ULink>

View File

@@ -12,9 +12,12 @@ const appConfig = _appConfig as AppConfig & { ui: { dropdownMenu: Partial<typeof
const dropdownMenu = tv({ extend: tv(theme), ...(appConfig.ui?.dropdownMenu || {}) })
type DropdownMenuVariants = VariantProps<typeof dropdownMenu>
export interface DropdownMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'custom'> {
label?: string
icon?: string
color?: DropdownMenuVariants['color']
avatar?: AvatarProps
content?: Omit<DropdownMenuContentProps, 'as' | 'asChild' | 'forceMount'>
kbds?: KbdProps['value'][] | KbdProps[]
@@ -34,8 +37,6 @@ export interface DropdownMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'cust
onUpdateChecked?(checked: boolean): void
}
type DropdownMenuVariants = VariantProps<typeof dropdownMenu>
export interface DropdownMenuProps<T> extends Omit<DropdownMenuRootProps, 'dir'> {
size?: DropdownMenuVariants['size']
items?: T[] | T[][]

View File

@@ -58,8 +58,8 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
<DefineItemTemplate v-slot="{ item, active, 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">
<UIcon v-if="item.loading" :name="loadingIcon || appConfig.ui.icons.loading" :class="ui.itemLeadingIcon({ class: uiOverride?.itemLeadingIcon, loading: true })" />
<UIcon v-else-if="item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ class: uiOverride?.itemLeadingIcon, active })" />
<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 })" />
</slot>
@@ -68,19 +68,19 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
{{ get(item, props.labelKey as string) }}
</slot>
<UIcon v-if="item.target === '_blank'" :name="appConfig.ui.icons.external" :class="ui.itemLabelExternalIcon({ class: uiOverride?.itemLabelExternalIcon, active })" />
<UIcon v-if="item.target === '_blank'" :name="appConfig.ui.icons.external" :class="ui.itemLabelExternalIcon({ class: uiOverride?.itemLabelExternalIcon, color: item?.color, active })" />
</span>
<span :class="ui.itemTrailing({ class: uiOverride?.itemTrailing })">
<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({ class: uiOverride?.itemTrailingIcon, active })" />
<UIcon v-if="item.children?.length" :name="appConfig.ui.icons.chevronRight" :class="ui.itemTrailingIcon({ class: uiOverride?.itemTrailingIcon, color: item?.color, active })" />
<span v-else-if="item.kbds?.length" :class="ui.itemTrailingKbds({ class: uiOverride?.itemTrailingKbds })">
<UKbd v-for="(kbd, kbdIndex) in item.kbds" :key="kbdIndex" :size="((props.uiOverride?.itemTrailingKbdsSize || ui.itemTrailingKbdsSize()) as KbdProps['size'])" v-bind="typeof kbd === 'string' ? { value: kbd } : kbd" />
</span>
</slot>
<DropdownMenu.ItemIndicator as-child>
<UIcon :name="checkedIcon || appConfig.ui.icons.check" :class="ui.itemTrailingIcon({ class: uiOverride?.itemTrailingIcon })" />
<UIcon :name="checkedIcon || appConfig.ui.icons.check" :class="ui.itemTrailingIcon({ class: uiOverride?.itemTrailingIcon, color: item?.color })" />
</DropdownMenu.ItemIndicator>
</span>
</slot>
@@ -100,7 +100,7 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
type="button"
:disabled="item.disabled"
:text-value="get(item, props.labelKey as string)"
:class="ui.item({ class: uiOverride?.item })"
:class="ui.item({ class: uiOverride?.item, color: item?.color })"
>
<ReuseItemTemplate :item="item" :index="index" />
</DropdownMenu.SubTrigger>
@@ -131,7 +131,7 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
:checked="item.checked"
:disabled="item.disabled"
:text-value="get(item, props.labelKey as string)"
:class="ui.item({ class: [uiOverride?.item, item.class] })"
:class="ui.item({ class: [uiOverride?.item, item.class], color: item?.color })"
@update:checked="item.onUpdateChecked"
@select="item.onSelect"
>
@@ -145,7 +145,7 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
@select="item.onSelect"
>
<ULink v-slot="{ active, ...slotProps }" v-bind="pickLinkProps(item as Omit<DropdownMenuItem, 'type'>)" custom>
<ULinkBase v-bind="slotProps" :class="ui.item({ class: [uiOverride?.item, item.class], active })">
<ULinkBase v-bind="slotProps" :class="ui.item({ class: [uiOverride?.item, item.class], color: item?.color, active })">
<ReuseItemTemplate :item="item" :active="active" :index="index" />
</ULinkBase>
</ULink>