Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
4 changes: 3 additions & 1 deletion packages/api-generator/src/locale/en/VBreadcrumbs.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
{
"props": {
"collapseInMenu": "When true, overflowing breadcrumb items are collapsed into a contextual menu rather than a static ellipsis.",
"divider": "Specifies the dividing character between items.",
"icons": "Specifies that the dividers between items are [v-icon](/components/icons)s.",
"justifyCenter": "Align the breadcrumbs center.",
"justifyEnd": "Align the breadcrumbs at the end.",
"large": "Increase the font-size of the breadcrumb item text to 16px (14px default)."
"large": "Increase the font-size of the breadcrumb item text to 16px (14px default).",
"maxItems": "Determines how many breadcrumb items can be shown before middle items are collapsed."
},
"slots": {
"divider": "The slot used for dividers.",
Expand Down
68 changes: 68 additions & 0 deletions packages/docs/src/examples/v-breadcrumbs/slot-collapse-in-menu.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<template>
<v-breadcrumbs :items="items" :max-items="3" collapse-in-menu>
</v-breadcrumbs>
</template>

<script setup>
const items = [
{
title: 'Dashboard',
disabled: false,
href: 'breadcrumbs_dashboard',
},
{
title: 'Link 1',
disabled: false,
href: 'breadcrumbs_link_1',
},
{
title: 'Link 2',
disabled: false,
href: 'breadcrumbs_link_2',
},
{
title: 'Link 3',
disabled: false,
href: 'breadcrumbs_link_3',
},
{
title: 'Link 4',
disabled: true,
href: 'breadcrumbs_link_4',
},
]
</script>

<script>
export default {
data: () => ({
items: [
{
title: 'Dashboard',
disabled: false,
href: 'breadcrumbs_dashboard',
},
{
title: 'Link 1',
disabled: false,
href: 'breadcrumbs_link_1',
},
{
title: 'Link 2',
disabled: false,
href: 'breadcrumbs_link_2',
},
{
title: 'Link 3',
disabled: false,
href: 'breadcrumbs_link_3',
},
{
title: 'Link 4',
disabled: true,
href: 'breadcrumbs_link_4',
},
],
}),
}
</script>
68 changes: 68 additions & 0 deletions packages/docs/src/examples/v-breadcrumbs/slot-max-items.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<template>
<v-breadcrumbs :items="items" :max-items="3">
</v-breadcrumbs>
</template>

<script setup>
const items = [
{
title: 'Dashboard',
disabled: false,
href: 'breadcrumbs_dashboard',
},
{
title: 'Link 1',
disabled: false,
href: 'breadcrumbs_link_1',
},
{
title: 'Link 2',
disabled: false,
href: 'breadcrumbs_link_2',
},
{
title: 'Link 3',
disabled: false,
href: 'breadcrumbs_link_3',
},
{
title: 'Link 4',
disabled: true,
href: 'breadcrumbs_link_4',
},
]
</script>

<script>
export default {
data: () => ({
items: [
{
title: 'Dashboard',
disabled: false,
href: 'breadcrumbs_dashboard',
},
{
title: 'Link 1',
disabled: false,
href: 'breadcrumbs_link_1',
},
{
title: 'Link 2',
disabled: false,
href: 'breadcrumbs_link_2',
},
{
title: 'Link 3',
disabled: false,
href: 'breadcrumbs_link_3',
},
{
title: 'Link 4',
disabled: true,
href: 'breadcrumbs_link_4',
},
],
}),
}
</script>
12 changes: 12 additions & 0 deletions packages/docs/src/pages/en/components/breadcrumbs.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,15 @@ To customize the divider, use the `divider` slot.
You can use the `title` slot to customize each breadcrumb title.

<ExamplesExample file="v-breadcrumbs/slot-title" />

#### Collapsed breadcrumbs

You can use the `maxItems` prop to redefine the maximum number of breadcrumb items displayed before the component collapses.

<ExamplesExample file="v-breadcrumbs/slot-max-items" />

#### Collapsed with menu

You can use the `collapseInMenu` prop to display the collapsed breadcrumb items inside a dropdown menu.

<ExamplesExample file="v-breadcrumbs/slot-collapse-in-menu" />
17 changes: 17 additions & 0 deletions packages/vuetify/playgrounds/Playground.breadcrumbs.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<template>
<v-app>
<v-container>
<v-breadcrumbs :items="items" :max-items="3" />
<v-breadcrumbs :items="items" :max-items="3" collapse-in-menu />
</v-container>
</v-app>
</template>

<script>
export default {
name: 'Playground',
data: () => ({
items: Array.from({ length: 4 }, (k, v) => ({ title: `Link ${v + 1}`, href: `breadcrumbs_link_${v + 1}` })),
}),
}
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
.v-breadcrumbs
display: flex
align-items: center
flex-wrap: wrap
line-height: $breadcrumbs-line-height
padding: $breadcrumbs-padding-y $breadcrumbs-padding-x

Expand Down
120 changes: 105 additions & 15 deletions packages/vuetify/src/components/VBreadcrumbs/VBreadcrumbs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ import './VBreadcrumbs.sass'
// Components
import { VBreadcrumbsDivider } from './VBreadcrumbsDivider'
import { VBreadcrumbsItem } from './VBreadcrumbsItem'
import { VBtn } from '../VBtn'
import { VIcon } from '../VIcon'
import { VList, VListItem, VListItemTitle } from '../VList'
import { VMenu } from '../VMenu'
import { VDefaultsProvider } from '@/components/VDefaultsProvider'
import { VIcon } from '@/components/VIcon'

// Composables
import { useBackgroundColor } from '@/composables/color'
Expand All @@ -17,7 +20,7 @@ import { makeRoundedProps, useRounded } from '@/composables/rounded'
import { makeTagProps } from '@/composables/tag'

// Utilities
import { computed, toRef } from 'vue'
import { computed, ref, toRef } from 'vue'
import { genericComponent, isObject, propsFactory, useRender } from '@/util'

// Types
Expand All @@ -36,6 +39,7 @@ export const makeVBreadcrumbsProps = propsFactory({
activeClass: String,
activeColor: String,
bgColor: String,
collapseInMenu: Boolean,
color: String,
disabled: Boolean,
divider: {
Expand All @@ -48,11 +52,15 @@ export const makeVBreadcrumbsProps = propsFactory({
default: () => ([]),
},
itemProps: Boolean,
maxItems: {
type: Number,
default: 8,
},

...makeComponentProps(),
...makeDensityProps(),
...makeRoundedProps(),
...makeTagProps({ tag: 'ul' }),
...makeTagProps({ tag: 'nav' }),
}, 'VBreadcrumbs')

export const VBreadcrumbs = genericComponent<new <T extends BreadcrumbItem>(
Expand Down Expand Up @@ -91,24 +99,33 @@ export const VBreadcrumbs = genericComponent<new <T extends BreadcrumbItem>(
const items = computed(() => props.items.map(item => {
return typeof item === 'string' ? { item: { title: item }, raw: item } : { item, raw: item }
}))
const showEllipsis = computed(() => items.value.length >= props.maxItems)
const enableEllipsis = ref(showEllipsis.value)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This prevents the limit from being responsive. Let's say I resize the window and the limit changes, it won't be reflected until the component or page is fully refreshed.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree. I added a watcher on showEllipsis to handle the responsive behavior.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we leave it like this, it will only support on/off change while someone might want to change the limit based on the available width (e.g. `:total-visible='$vuetify.display.smAndUp ? 4 : 2').

BTW. it might be a personal preference, but I feel like those variables names should swap:

  • showEllipsis » ellipsisEnabled
  • enableEllipsis » showEllipsis or hasEllipsis

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I'll renamed it


const onClickEllipsis = () => {
enableEllipsis.value = false
}

useRender(() => {
const hasPrepend = !!(slots.prepend || props.icon)

return (
<props.tag
class={[
'v-breadcrumbs',
backgroundColorClasses.value,
densityClasses.value,
roundedClasses.value,
props.class,
]}
style={[
backgroundColorStyles.value,
props.style,
]}
aria-label="breadcrumbs"
>
<ol
class={[
'v-breadcrumbs',
backgroundColorClasses.value,
densityClasses.value,
roundedClasses.value,
props.class,
]}
style={[
backgroundColorStyles.value,
props.style,
]}
>
{ hasPrepend && (
<li key="prepend" class="v-breadcrumbs__prepend">
{ !slots.prepend ? (
Expand All @@ -133,7 +150,7 @@ export const VBreadcrumbs = genericComponent<new <T extends BreadcrumbItem>(
</li>
)}

{ items.value.map(({ item, raw }, index, array) => (
{ !enableEllipsis.value && items.value.map(({ item, raw }, index, array) => (
<>
{ slots.item?.({ item, index }) ?? (
<VBreadcrumbsItem
Expand All @@ -157,7 +174,80 @@ export const VBreadcrumbs = genericComponent<new <T extends BreadcrumbItem>(
</>
))}

{ enableEllipsis.value && (
<>
{ (() => {
const { item } = items.value[0]
return (
<>
{ slots.item?.({ item, index: 0 }) ?? (
<VBreadcrumbsItem
disabled={ false }
{ ...(typeof item === 'string' ? { title: item } : item) }
/>
)}
</>
)
})()}

<VBreadcrumbsDivider />

<VBreadcrumbsItem
disabled={ false }
onClick={ onClickEllipsis }
>
{ props.collapseInMenu ? (
<VMenu>
{{
activator: ({ props: activatorProps }) => (
<VBtn
icon="mdi-dots-horizontal"
variant="text"
size="x-small"
{ ...activatorProps }
/>
),
default: () => (
<VList>
{ items.value.slice(1, items.value.length - 1).map(({ item }, index) => (
<VListItem key={ index } value={ index } component="a" href={ 'href' in item ? item.href : undefined }>
<VListItemTitle>{ item.title }</VListItemTitle>
</VListItem>
))}
</VList>
),
}}
</VMenu>
) : (
<VBtn
icon="mdi-dots-horizontal"
variant="text"
size="x-small"
/>
)}
</VBreadcrumbsItem>

<VBreadcrumbsDivider />

{ (() => {
const lastIndex = items.value.length - 1
const { item } = items.value[lastIndex]
return (
<>
{ slots.item?.({ item, index: lastIndex }) ?? (
<VBreadcrumbsItem
disabled
{ ...(typeof item === 'string' ? { title: item } : item) }
/>
)}
</>
)
})()}
</>
)}

{ slots.default?.() }
</ol>
</props.tag>
)
})
Expand Down
Loading