Making Material tabs scrollable

前端 未结 3 1352
长情又很酷
长情又很酷 2021-01-06 12:17

I\'m using Material tabs in my application (mat-tab s inside mat-tab-group) When there are more tabs than can be displayed, two navigation buttons

3条回答
  •  一向
    一向 (楼主)
    2021-01-06 12:57

    [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 , 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:

    
        
        
        
        
        
        
        
        
    
    

    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 :)

提交回复
热议问题