@@ -39,6 +39,10 @@ export const makeVBreadcrumbsProps = propsFactory({
3939 activeColor : String ,
4040 bgColor : String ,
4141 collapseInMenu : Boolean ,
42+ collapseFrom : {
43+ type : Number ,
44+ default : 0 ,
45+ } ,
4246 color : String ,
4347 disabled : Boolean ,
4448 divider : {
@@ -61,7 +65,10 @@ export const makeVBreadcrumbsProps = propsFactory({
6165 menuProps : {
6266 type : 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
8794 props : makeVBreadcrumbsProps ( ) ,
8895
89- setup ( props , { slots } ) {
96+ setup ( props , { slots, expose } ) {
9097 const { backgroundColorClasses, backgroundColorStyles } = useBackgroundColor ( ( ) => props . bgColor )
9198 const { densityClasses } = useDensity ( props )
9299 const { 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 )
110117 const 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+ }
112135 const onClickEllipsis = ( ) => {
113136 hasEllipsis . value = false
114137 }
@@ -135,7 +158,7 @@ export const VBreadcrumbs = genericComponent<new <T extends BreadcrumbItem>(
135158 props . 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
207238 tabindex = "0"
208239 onClick = { props . collapseInMenu ? noop : onClickEllipsis }
240+ onKeydown = { ( e : KeyboardEvent ) => {
241+ if ( ! [ 'Enter' , ' ' ] . includes ( e . key ) ) return
242+ e . preventDefault ( )
243+ props . collapseInMenu ? e . currentTarget . click ( ) : onClickEllipsis ( )
244+ } }
209245 class = "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 { {
218256 default : ( ) => (
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
221261 if ( slots [ 'list-item' ] ) {
222262 return slots [ 'list-item' ] ( { item, index } )
223263 }
224264 return (
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 } )
263318 return { }
264319 } ,
265320} )
0 commit comments