Skip to content

Commit

Permalink
feat(Accordion/Breadcrumb/ContextMenu/DropdownMenu/NavigationMenu/Tab…
Browse files Browse the repository at this point in the history
…s): add `labelKey` prop
  • Loading branch information
benjamincanac committed Oct 11, 2024
1 parent f6f9823 commit 15b4949
Show file tree
Hide file tree
Showing 20 changed files with 247 additions and 31 deletions.
11 changes: 9 additions & 2 deletions src/runtime/components/Accordion.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ export interface AccordionProps<T> extends Pick<AccordionRootProps, 'collapsible
* @defaultValue appConfig.ui.icons.chevronDown
*/
trailingIcon?: string
/**
* The key used to get the label from the item.
* @defaultValue 'label'
*/
labelKey?: string
class?: any
ui?: Partial<typeof accordion.slots>
}
Expand All @@ -56,10 +61,12 @@ import { computed } from 'vue'
import { AccordionRoot, AccordionItem, AccordionHeader, AccordionTrigger, AccordionContent, useForwardPropsEmits } from 'radix-vue'
import { reactivePick } from '@vueuse/core'
import { useAppConfig } from '#imports'
import { get } from '../utils'
const props = withDefaults(defineProps<AccordionProps<T>>(), {
type: 'single',
collapsible: true
collapsible: true,
labelKey: 'label'
})
const emits = defineEmits<AccordionEmits>()
const slots = defineSlots<AccordionSlots<T>>()
Expand Down Expand Up @@ -89,7 +96,7 @@ const ui = computed(() => accordion({
</slot>
<span v-if="item.label || !!slots.default" :class="ui.label({ class: props.ui?.label })">
<slot :item="item" :index="index" :open="open">{{ item.label }}</slot>
<slot :item="item" :index="index" :open="open">{{ get(item, props.labelKey as string) }}</slot>
</span>
<slot name="trailing" :item="item" :index="index" :open="open">
Expand Down
12 changes: 10 additions & 2 deletions src/runtime/components/Breadcrumb.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ export interface BreadcrumbProps<T> {
* @defaultValue appConfig.ui.icons.chevronRight
*/
separatorIcon?: string
/**
* The key to use to get the label from the item.
* @defaultValue 'label'
*/
labelKey?: string
class?: any
ui?: PartialString<typeof breadcrumb.slots>
}
Expand All @@ -49,13 +54,16 @@ export type BreadcrumbSlots<T extends { slot?: string }> = {
<script setup lang="ts" generic="T extends BreadcrumbItem">
import { Primitive } from 'radix-vue'
import { useAppConfig } from '#imports'
import { get } from '../utils'
import { pickLinkProps } from '../utils/link'
import UIcon from './Icon.vue'
import UAvatar from './Avatar.vue'
import ULinkBase from './LinkBase.vue'
import ULink from './Link.vue'
const props = defineProps<BreadcrumbProps<T>>()
const props = withDefaults(defineProps<BreadcrumbProps<T>>(), {
labelKey: 'label'
})
const slots = defineSlots<BreadcrumbSlots<T>>()
const appConfig = useAppConfig()
Expand All @@ -79,7 +87,7 @@ const ui = breadcrumb()
<span v-if="item.label || !!slots[item.slot ? `${item.slot}-label`: 'item-label']" :class="ui.linkLabel({ class: props.ui?.linkLabel })">
<slot :name="item.slot ? `${item.slot}-label`: 'item-label'" :item="item" :active="index === items!.length - 1" :index="index">
{{ item.label }}
{{ get(item, props.labelKey as string) }}
</slot>
</span>
Expand Down
9 changes: 8 additions & 1 deletion src/runtime/components/ContextMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ export interface ContextMenuProps<T> extends Omit<ContextMenuRootProps, 'dir'>,
* @defaultValue true
*/
portal?: boolean
/**
* The key used to get the label from the item.
* @defaultValue 'label'
*/
labelKey?: string
class?: any
ui?: PartialString<typeof contextMenu.slots>
}
Expand Down Expand Up @@ -69,7 +74,8 @@ import UContextMenuContent from './ContextMenuContent.vue'
const props = withDefaults(defineProps<ContextMenuProps<T>>(), {
portal: true,
modal: true
modal: true,
labelKey: 'label'
})
const emits = defineEmits<ContextMenuEmits>()
const slots = defineSlots<ContextMenuSlots<T>>()
Expand All @@ -96,6 +102,7 @@ const ui = computed(() => contextMenu({
v-bind="contentProps"
:items="items"
:portal="portal"
:label-key="labelKey"
>
<template v-for="(_, name) in proxySlots" #[name]="slotData: any">
<slot :name="name" v-bind="slotData" />
Expand Down
6 changes: 4 additions & 2 deletions src/runtime/components/ContextMenuContent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ interface ContextMenuContentProps<T> extends Omit<RadixContextMenuContentProps,
items?: T[] | T[][]
portal?: boolean
sub?: boolean
labelKey: string
class?: any
ui: typeof _contextMenu
uiOverride?: any
Expand All @@ -24,7 +25,7 @@ import { ContextMenu } from 'radix-vue/namespaced'
import { useForwardPropsEmits } from 'radix-vue'
import { reactiveOmit, createReusableTemplate } from '@vueuse/core'
import { useAppConfig } from '#imports'
import { omit } from '../utils'
import { omit, get } from '../utils'
import { pickLinkProps } from '../utils/link'
import ULinkBase from './LinkBase.vue'
import ULink from './Link.vue'
Expand Down Expand Up @@ -55,7 +56,7 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
<span v-if="item.label || !!slots[item.slot ? `${item.slot}-label`: 'item-label']" :class="ui.itemLabel({ class: uiOverride?.itemLabel, active })">
<slot :name="item.slot ? `${item.slot}-label`: 'item-label'" :item="(item as T)" :active="active" :index="index">
{{ item.label }}
{{ 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 })" />
Expand Down Expand Up @@ -99,6 +100,7 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
:portal="portal"
:items="item.children"
:align-offset="-4"
:label-key="labelKey"
v-bind="item.content"
>
<template v-for="(_, name) in proxySlots" #[name]="slotData: any">
Expand Down
9 changes: 8 additions & 1 deletion src/runtime/components/DropdownMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ export interface DropdownMenuProps<T> extends Omit<DropdownMenuRootProps, 'dir'>
* @defaultValue true
*/
portal?: boolean
/**
* The key used to get the label from the item.
* @defaultValue 'label'
*/
labelKey?: string
class?: any
ui?: PartialString<typeof dropdownMenu.slots>
}
Expand Down Expand Up @@ -78,7 +83,8 @@ import UDropdownMenuContent from './DropdownMenuContent.vue'
const props = withDefaults(defineProps<DropdownMenuProps<T>>(), {
portal: true,
modal: true
modal: true,
labelKey: 'label'
})
const emits = defineEmits<DropdownMenuEmits>()
const slots = defineSlots<DropdownMenuSlots<T>>()
Expand Down Expand Up @@ -106,6 +112,7 @@ const ui = computed(() => dropdownMenu({
v-bind="contentProps"
:items="items"
:portal="portal"
:label-key="labelKey"
>
<template v-for="(_, name) in proxySlots" #[name]="slotData: any">
<slot :name="name" v-bind="slotData" />
Expand Down
6 changes: 4 additions & 2 deletions src/runtime/components/DropdownMenuContent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ interface DropdownMenuContentProps<T> extends Omit<RadixDropdownMenuContentProps
items?: T[] | T[][]
portal?: boolean
sub?: boolean
labelKey: string
class?: any
ui: typeof _dropdownMenu
uiOverride?: any
Expand All @@ -28,7 +29,7 @@ import { DropdownMenu } from 'radix-vue/namespaced'
import { useForwardPropsEmits } from 'radix-vue'
import { reactiveOmit, createReusableTemplate } from '@vueuse/core'
import { useAppConfig } from '#imports'
import { omit } from '../utils'
import { omit, get } from '../utils'
import { pickLinkProps } from '../utils/link'
import ULinkBase from './LinkBase.vue'
import ULink from './Link.vue'
Expand Down Expand Up @@ -59,7 +60,7 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
<span v-if="item.label || !!slots[item.slot ? `${item.slot}-label`: 'item-label']" :class="ui.itemLabel({ class: uiOverride?.itemLabel, active })">
<slot :name="item.slot ? `${item.slot}-label`: 'item-label'" :item="(item as T)" :active="active" :index="index">
{{ item.label }}
{{ 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 })" />
Expand Down Expand Up @@ -106,6 +107,7 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
align="start"
:align-offset="-4"
:side-offset="3"
:label-key="labelKey"
v-bind="item.content"
>
<template v-for="(_, name) in proxySlots" #[name]="slotData: any">
Expand Down
13 changes: 10 additions & 3 deletions src/runtime/components/NavigationMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ export interface NavigationMenuProps<T> extends Pick<NavigationMenuRootProps, 'd
* @defaultValue false
*/
arrow?: boolean
/**
* The key used to get the label from the item.
* @defaultValue 'label'
*/
labelKey?: string
class?: any
ui?: PartialString<typeof navigationMenu.slots>
}
Expand All @@ -82,6 +87,7 @@ export type NavigationMenuSlots<T extends { slot?: string }> = {
import { computed, toRef } from 'vue'
import { NavigationMenuRoot, NavigationMenuList, NavigationMenuItem, NavigationMenuTrigger, NavigationMenuContent, NavigationMenuLink, NavigationMenuIndicator, NavigationMenuViewport, useForwardPropsEmits } from 'radix-vue'
import { reactivePick } from '@vueuse/core'
import { get } from '../utils'
import { pickLinkProps } from '../utils/link'
import ULinkBase from './LinkBase.vue'
import ULink from './Link.vue'
Expand All @@ -91,7 +97,8 @@ import UBadge from './Badge.vue'
const props = withDefaults(defineProps<NavigationMenuProps<T>>(), {
orientation: 'horizontal',
delayDuration: 0
delayDuration: 0,
labelKey: 'label'
})
const emits = defineEmits<NavigationMenuEmits>()
const slots = defineSlots<NavigationMenuSlots<T>>()
Expand Down Expand Up @@ -132,7 +139,7 @@ const lists = computed(() => props.items?.length ? (Array.isArray(props.items[0]
<span v-if="item.label || !!slots[item.slot ? `${item.slot}-label`: 'item-label']" :class="ui.linkLabel({ class: props.ui?.linkLabel })">
<slot :name="item.slot ? `${item.slot}-label`: 'item-label'" :item="item" :active="active" :index="index">
{{ item.label }}
{{ get(item, props.labelKey as string) }}
</slot>
<UIcon v-if="item.target === '_blank'" :name="appConfig.ui.icons.external" :class="ui.linkLabelExternalIcon({ class: props.ui?.linkLabelExternalIcon, active })" />
Expand Down Expand Up @@ -165,7 +172,7 @@ const lists = computed(() => props.items?.length ? (Array.isArray(props.items[0]
<div :class="ui.childLinkWrapper({ class: props.ui?.childLinkWrapper })">
<p :class="ui.childLinkLabel({ class: props.ui?.childLinkLabel, active: childActive })">
{{ childItem.label }}
{{ get(childItem, props.labelKey as string) }}
<UIcon v-if="childItem.target === '_blank'" :name="appConfig.ui.icons.external" :class="ui.childLinkLabelExternalIcon({ class: props.ui?.childLinkLabelExternalIcon, active: childActive })" />
</p>
Expand Down
11 changes: 9 additions & 2 deletions src/runtime/components/Tabs.vue
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ export interface TabsProps<T> extends Pick<TabsRootProps<string | number>, 'defa
* @defaultValue true
*/
content?: boolean | Omit<TabsContentProps, 'as' | 'asChild' | 'value'>
/**
* The key used to get the label from the item.
* @defaultValue 'label'
*/
labelKey?: string
class?: any
ui?: PartialString<typeof tabs.slots>
}
Expand All @@ -66,11 +71,13 @@ import { computed, toRef } from 'vue'
import { defu } from 'defu'
import { TabsRoot, TabsList, TabsIndicator, TabsTrigger, TabsContent, useForwardPropsEmits } from 'radix-vue'
import { reactivePick } from '@vueuse/core'
import { get } from '../utils'
const props = withDefaults(defineProps<TabsProps<T>>(), {
content: true,
defaultValue: '0',
orientation: 'horizontal'
orientation: 'horizontal',
labelKey: 'label'
})
const emits = defineEmits<TabsEmits>()
const slots = defineSlots<TabsSlots<T>>()
Expand Down Expand Up @@ -98,7 +105,7 @@ const ui = computed(() => tabs({
</slot>
<span v-if="item.label || !!slots.default" :class="ui.label({ class: props.ui?.label })">
<slot :item="item" :index="index">{{ item.label }}</slot>
<slot :item="item" :index="index">{{ get(item, props.labelKey as string) }}</slot>
</span>
<slot name="trailing" :item="item" :index="index" />
Expand Down
1 change: 1 addition & 0 deletions test/components/Accordion.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ describe('Accordion', () => {
['with items', { props }],
['with modelValue', { props: { ...props, modelValue: '1' } }],
['with defaultValue', { props: { ...props, defaultValue: '1' } }],
['with labelKey', { props: { ...props, labelKey: 'icon' } }],
['with as', { props: { ...props, as: 'section' } }],
['with type', { props: { ...props, type: 'multiple' as const } }],
['with disabled', { props: { ...props, disabled: true } }],
Expand Down
1 change: 1 addition & 0 deletions test/components/Breadcrumb.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ describe('Breadcrumb', () => {
it.each([
// Props
['with items', { props }],
['with labelKey', { props: { ...props, labelKey: 'icon' } }],
['with separatorIcon', { props: { ...props, separatorIcon: 'i-heroicons-minus' } }],
['with class', { props: { ...props, class: 'w-48' } }],
['with ui', { props: { ...props, ui: { link: 'font-bold' } } }],
Expand Down
1 change: 1 addition & 0 deletions test/components/ContextMenu.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ describe('ContextMenu', () => {
it.each([
// Props
['with items', { props }],
['with labelKey', { props: { ...props, labelKey: 'icon' } }],
['with disabled', { props: { ...props, disabled: true } }],
...sizes.map((size: string) => [`with size ${size}`, { props: { ...props, size } }]),
['with class', { props: { ...props, class: 'min-w-96' } }],
Expand Down
1 change: 1 addition & 0 deletions test/components/DropdownMenu.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ describe('DropdownMenu', () => {
it.each([
// Props
['with items', { props }],
['with labelKey', { props: { ...props, labelKey: 'icon' } }],
['with disabled', { props: { ...props, disabled: true } }],
['with arrow', { props: { ...props, arrow: true } }],
...sizes.map((size: string) => [`with size ${size}`, { props: { ...props, size } }]),
Expand Down
1 change: 1 addition & 0 deletions test/components/NavigationMenu.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ describe('NavigationMenu', () => {
it.each([
// Props
['with items', { props }],
['with labelKey', { props: { ...props, labelKey: 'icon' } }],
['with arrow', { props: { ...props, arrow: true } }],
['with orientation vertical', { props: { ...props, orientation: 'vertical' as const } }],
...variants.map((variant: string) => [`with primary variant ${variant}`, { props: { ...props, variant } }]),
Expand Down
1 change: 1 addition & 0 deletions test/components/Tabs.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ describe('Tabs', () => {
it.each([
// Props
['with items', { props }],
['with labelKey', { props: { ...props, labelKey: 'icon' } }],
['with modelValue', { props: { ...props, modelValue: '1' } }],
['with defaultValue', { props: { ...props, defaultValue: '1' } }],
['with orientation vertical', { props: { ...props, orientation: 'vertical' as const } }],
Expand Down
Loading

0 comments on commit 15b4949

Please sign in to comment.