@@ -39,6 +39,10 @@ export const makeVBreadcrumbsProps = propsFactory({
3939activeColor :String ,
4040bgColor :String ,
4141collapseInMenu :Boolean ,
42+ collapseFrom :{
43+ type :Number ,
44+ default :0 ,
45+ } ,
4246color :String ,
4347disabled :Boolean ,
4448divider :{
@@ -61,7 +65,10 @@ export const makeVBreadcrumbsProps = propsFactory({
6165menuProps :{
6266type :Object as PropType < VMenu [ '$props' ] > ,
6367} ,
64- totalVisible :Number ,
68+ totalVisible :{
69+ type :Number ,
70+ default :Number . MAX_VALUE ,
71+ } ,
6572
6673 ...makeComponentProps ( ) ,
6774 ...makeDensityProps ( ) ,
@@ -86,7 +93,7 @@ export const VBreadcrumbs = genericComponent<new <T extends BreadcrumbItem>(
8693
8794props :makeVBreadcrumbsProps ( ) ,
8895
89- setup ( props , { slots} ) {
96+ setup ( props , { slots, expose } ) {
9097const { backgroundColorClasses, backgroundColorStyles} = useBackgroundColor ( ( ) => props . bgColor )
9198const { densityClasses} = useDensity ( props )
9299const { roundedClasses} = useRounded ( props )
@@ -103,12 +110,28 @@ export const VBreadcrumbs = genericComponent<new <T extends BreadcrumbItem>(
103110} ,
104111} )
105112
106- const items = computed ( ( ) => props . items . map ( item => {
107- return typeof item === 'string' ?{ item :{ title :item } , raw :item } :{ item, raw :item }
108- } ) )
109- const ellipsisEnabled = toRef ( ( ) => props . totalVisible ? items . value . length > props . totalVisible : false )
113+ const items = computed ( ( ) => props . items . map ( item =>
114+ typeof item === 'string' ?{ item :{ title :item } , raw :item } :{ item, raw :item }
115+ ) )
116+ const ellipsisEnabled = toRef ( ( ) => items . value . length > props . totalVisible )
110117const hasEllipsis = ref ( ellipsisEnabled . value )
118+ const collapseStartIndex = toRef ( ( ) => Math . min ( props . collapseFrom , props . totalVisible ) )
119+ const collapseEndIndex = toRef ( ( ) => collapseStartIndex . value + items . value . length - props . totalVisible )
120+ const visibleItemsStart = toRef ( ( ) =>
121+ collapseStartIndex . value < items . value . length ?items . value . slice ( 0 , collapseStartIndex . value ) :items . value
122+ )
123+ const collapsedItems = toRef ( ( ) =>
124+ items . value . slice ( collapseStartIndex . value , collapseEndIndex . value )
125+ )
126+ const visibleItemsEnd = toRef ( ( ) => {
127+ const sliceIndex = props . totalVisible - collapseStartIndex . value
128+
129+ return sliceIndex <= 0 ?[ ] :items . value . slice ( - sliceIndex )
130+ } )
111131
132+ const collapse = ( ) => {
133+ hasEllipsis . value = ellipsisEnabled . value
134+ }
112135const onClickEllipsis = ( ) => {
113136hasEllipsis . value = false
114137}
@@ -135,7 +158,7 @@ export const VBreadcrumbs = genericComponent<new <T extends BreadcrumbItem>(
135158props . style ,
136159] }
137160>
138- < ol >
161+ < ol role = "list" >
139162{ hasPrepend && (
140163< li key = "prepend" class = "v-breadcrumbs__prepend" >
141164{ ! slots . prepend ?(
@@ -188,41 +211,66 @@ export const VBreadcrumbs = genericComponent<new <T extends BreadcrumbItem>(
188211{ hasEllipsis . value && (
189212< >
190213{ ( ( ) => {
191- const { item} = items . value [ 0 ]
192- return (
193- < >
194- { slots . item ?.( { item, index :0 } ) ?? (
195- < VBreadcrumbsItem
196- disabled = { false }
197- { ...( typeof item === 'string' ?{ title :item } :item ) }
198- />
199- ) }
200- </ >
201- )
214+ return visibleItemsStart . value . map ( ( { item} , i ) => {
215+ const isLast = i === visibleItemsStart . value . length - 1
216+
217+ return (
218+ < >
219+ { slots . item ?.( { item, index :i } ) ?? (
220+ < VBreadcrumbsItem
221+ key = { i }
222+ disabled = { false }
223+ { ...( typeof item === 'string' ?{ title :item } :item ) }
224+ />
225+ ) }
226+
227+ { ! isLast && (
228+ < VBreadcrumbsDivider />
229+ ) }
230+ </ >
231+ )
232+ } )
202233} ) ( ) }
203234
204- < VBreadcrumbsDivider />
235+ { collapseStartIndex . value > 0 && < VBreadcrumbsDivider /> }
205236
206237< VBreadcrumbsItem
207238tabindex = "0"
208239onClick = { props . collapseInMenu ?noop :onClickEllipsis }
240+ onKeydown = { ( e :KeyboardEvent ) => {
241+ if ( ! [ 'Enter' , ' ' ] . includes ( e . key ) ) return
242+ e . preventDefault ( )
243+ props . collapseInMenu ?( e . currentTarget as HTMLElement | null ) ?. click ( ) :onClickEllipsis ( )
244+ } }
209245class = "v-breadcrumbs-item--ellipsis"
246+ role = "button"
247+ aria-haspopup = { props . collapseInMenu ?'menu' :undefined }
248+ aria-expanded = { ! hasEllipsis . value }
249+ aria-label = "show more breadcrumb items"
210250>
211251{ props . ellipsis }
252+
212253{ props . collapseInMenu ?(
213- < VMenu
214- activator = "parent"
215- { ...props . menuProps }
216- >
254+ < VMenu activator = "parent" { ...props . menuProps } role = "menu" aria-label = "hidden breadcrumb items" >
217255{ {
218256default :( ) => (
219257< VList { ...props . listProps } >
220- { items . value . slice ( 1 , items . value . length - 1 ) . map ( ( { item} , index ) => {
258+ { collapsedItems . value . map ( ( { item} , index ) => {
259+ const isLastCollapsedItem = index === collapsedItems . value . length - 1
260+ const isCurrentPage = ! visibleItemsEnd . value . length && isLastCollapsedItem
221261if ( slots [ 'list-item' ] ) {
222262return slots [ 'list-item' ] ( { item, index} )
223263}
224264return (
225- < VListItem key = { index } value = { index } href = { 'href' in item ?item . href :undefined } >
265+ < VListItem
266+ key = { index }
267+ value = { index }
268+ active = { isCurrentPage }
269+ aria-current = { isCurrentPage ?'page' :undefined }
270+ disabled = { isCurrentPage }
271+ href = { 'href' in item ?item . href :undefined }
272+ role = "menuitem"
273+ >
226274< VListItemTitle > { item . title } </ VListItemTitle >
227275</ VListItem >
228276)
@@ -234,22 +282,29 @@ export const VBreadcrumbs = genericComponent<new <T extends BreadcrumbItem>(
234282) :null }
235283</ VBreadcrumbsItem >
236284
237- < VBreadcrumbsDivider />
285+ { visibleItemsEnd . value . length > 0 && < VBreadcrumbsDivider /> }
238286
239287{ ( ( ) => {
240- const lastIndex = items . value . length - 1
241- const { item} = items . value [ lastIndex ]
242- return (
243- < >
244- { slots . item ?.( { item, index :lastIndex } ) ?? (
245- < VBreadcrumbsItem
246- disabled
247- active
248- { ...( typeof item === 'string' ?{ title :item } :item ) }
249- />
250- ) }
251- </ >
252- )
288+ return visibleItemsEnd . value . map ( ( { item} , i ) => {
289+ const isLast = i === visibleItemsEnd . value . length - 1
290+
291+ return (
292+ < >
293+ { slots . item ?.( { item, index :i } ) ?? (
294+ < VBreadcrumbsItem
295+ key = { i }
296+ disabled = { i === items . value . length - 1 }
297+ active = { i === items . value . length - 1 }
298+ { ...( typeof item === 'string' ?{ title :item } :item ) }
299+ />
300+ ) }
301+
302+ { ! isLast && (
303+ < VBreadcrumbsDivider key = { `divider-last-${ i } ` } />
304+ ) }
305+ </ >
306+ )
307+ } )
253308} ) ( ) }
254309</ >
255310) }
@@ -259,7 +314,7 @@ export const VBreadcrumbs = genericComponent<new <T extends BreadcrumbItem>(
259314</ props . tag >
260315)
261316} )
262-
317+ expose ( { collapse } )
263318return { }
264319} ,
265320} )