[Long text solution] My goal was to make mat-tabs scrollable by default, without controls, but with the ability to auto-scroll when clicking on partially visible mat-tab to move it to the visible center-ish viewport.
1) This behavior already partially realized in this example "from the box" - https://stackblitz.com/edit/angular-mat-tabs-scrollalble-initial-behavior.
The problem is that you can't scroll backward normally - there some buggy behavior to push tabs to the left. So it does not work as I wanted.
2) The next variant was with scrolling event - take a (wheel)="event"
on mat-tab-group - but its also not work with mobile. https://stackblitz.com/edit/angular-mat-tabs-scrollable-by-wheel-event Got it from this awesome comment above.
3) My own scroll of mat-tabs with scrolling on mobile and autoscrolling clicked tab to center of screen' viewport when you click on the tab was not too simple but it works! :)
first, you need to disable "from the box" scrolling when tapping on tabs and pagination buttons:
mat-tabs-override.scss:
$black: #121212;
@mixin media-query-for-mobile {
@media (max-width: 768px) and (min-width: 1px) {
@content;
}
}
mat-tab-group {
.mat-tab-header {
.mat-tab-header-pagination {
display: none !important; // <== disable pagination
}
.mat-tab-label-container {
left: 0px; // if you need to use it on mobile - set left position to 0
width: 100%;
.mat-tab-list {
overflow-x: auto !important; // <== set horisontal scroll bar imperatively
// below rule prevents sliding of buttons' container - because it not sliding properly - to left it not slide as well
transform: none !important;
.mat-tab-labels {
// some tweaks for tabs - up to you
@include media-query-for-mobile {
justify-content: unset !important;
}
.mat-tab-label {
// min-width: 20% !important;
padding: 1.25% !important;
margin: 0px !important;
text-transform: uppercase;
color: $black;
font-weight: 600;
min-width: 140px !important;
}
}
}
}
}
}
in this case you will see that all tabs are similar by width and scrollable on mobile.
Next, you need to make auto-scroll for tabs when you clicking on them and change their position to the center of the screen, based on the current viewport - let's do it!
We can create a directive, which will listen to the main container of <mat-tabs-group>
, check width of scrollable container .mat-tab-labels
and move the tab to be visible in viewport by auto-scrolling .mat-tabs-labels
container to needed way:
directive in template:
<mat-tab-group scrollToCenter>
<mat-tab label="tab 1"></mat-tab>
<mat-tab label="tab 2"></mat-tab>
<mat-tab label="tab 3"></mat-tab>
<mat-tab label="tab 4"></mat-tab>
<mat-tab label="tab 5"></mat-tab>
<mat-tab label="tab 6"></mat-tab>
<mat-tab label="tab 7"></mat-tab>
<mat-tab label="tab 8"></mat-tab>
</mat-tab-group>
directive.ts:
import { Directive, ElementRef, OnDestroy } from '@angular/core';
import { fromEvent, Subscription } from 'rxjs';
interface DOMRectI {
bottom: number;
height: number;
left: number; // position start of element
right: number; // position end of element
top: number;
width: number; // width of element
x?: number;
y?: number;
}
@Directive({
// tslint:disable-next-line:directive-selector
selector: '[scrollToCenter]',
})
export class MatTabScrollToCenterDirective implements OnDestroy {
isMobile: boolean;
subs = new Subscription();
constructor(
private element: ElementRef
) {
this.subs.add(
fromEvent(this.element.nativeElement, 'click').subscribe((clickedContainer: MouseEvent) => {
const scrollContainer = this.element.nativeElement.querySelector('.mat-tab-list');
const currentScrolledContainerPosition: number = scrollContainer.scrollLeft;
const newPositionScrollTo = this.calcScrollToCenterValue(clickedContainer, currentScrolledContainerPosition);
})
);
}
/** calculate scroll position to center of viewport */
calcScrollToCenterValue(clickedContainer, currentScrolledContainerPosition): number {
const scrolledButton: DOMRectI = (clickedContainer.target as HTMLElement).getBoundingClientRect();
const leftXOffset = (window.innerWidth - scrolledButton.width) / 2;
const currentVisibleViewportLeft = scrolledButton.left;
const neededLeftOffset = currentVisibleViewportLeft - leftXOffset;
console.log(scrolledButton);
const newValueToSCroll = currentScrolledContainerPosition + neededLeftOffset;
return newValueToSCroll;
}
ngOnDestroy() {
this.subs.unsubscribe();
}
}
And it works! :0 But not in ios and IE...
Why? Because ios and IE don't support Element.scroll()
Solution - npm i element-scroll-polyfill
and set to polyfills.ts
/** enable polufill for element.scroll() on IE and ios */
import 'element-scroll-polyfill';
Great! but now scroll is not so smooth... IE and ios not support smooth-scroll-behavior.
Solution - npm i smoothscroll-polyfill
and add to polyfills.ts
import smoothscroll from 'smoothscroll-polyfill';
// enable polyfill
smoothscroll.polyfill();
Finally it works everywhere.
Hope it helps somebody to fix mat-tabs autoscrolling emptiness :)
DEMO Enjoy it :)