Search code examples
cssangularangular-materialmat-select

How to flip Angular Material Select panel side to make sure it stays inside the screen


I have a mat-select inside my site's main navbar. It contains a list of currencies and is positioned at the right of the navbar. Since I strictly want to display the currency abbreviation (ex: USD) in my navbar, but want to display a flag icon, currency name, and abbreviation inside the panel, I decided to resize the panel to be larger than the select. This leads to a problem where the panel overflows the screen and is not displayed entirely. I was wondering if there was a simple way to make sure the panel stays inside the browser, in other words, make sure the panel position does not go past the end position of the select.

Current Behavior:

Current Behavior

Desired Behavior:

Desired Behavior

HTML:

<header>
    <mat-toolbar class="navbar">
      <button mat-icon-button aria-label="Menu Icon">
      <mat-icon>menu</mat-icon>
      </button>
      <span>My Website</span>
      <div class="navbar-currency">
        <mat-form-field class="currency-select">
          <mat-select name="currency" panelClass="currency-panel" [(value)]="selectedCurrency">
            <mat-select-trigger>{{selectedCurrency}}</mat-select-trigger>
            <mat-option *ngFor="let currency of currencyArr" [value]="currency.key">
              <span class="fi fi-{{currency.icon}}"></span> {{currency.key }} - {{ currency.name}}
            </mat-option>
          </mat-select>
        </mat-form-field>
      </div>
    </mat-toolbar>
</header>

CSS:

.navbar {
  background: #ffffff;
  display: flex;
  padding-left: 2rem;
  padding-right: 2rem;
}

.navbar-currency{
  margin-left: auto;
  margin-top: 1.3rem;
}

.currency-select{
  font-size: 0.85rem;
  width: 80px;
}

.currency-panel.mat-mdc-select-panel {
  font-size: 0.85rem;
  min-width: calc(100% + 120px)
}

.currency-panel.mat-mdc-select-panel .fi{
  margin-right: 0.1rem;
}

.currency-panel.mat-mdc-select-panel .mat-mdc-option{
  min-height: 36px;
}

Solution

  • Simple Solution

    If your Currency Mat-Select is always on the right side of the screen then you could solve this with some css. Set the container position to absolute and the right to zero:

    .currency-panel.mat-mdc-select-panel {
      font-size: 0.85rem;
      min-width: calc(100% + 120px);
      position: absolute !important;
      right: 0;
    }
    

    Mat-Menu Solution

    Use Mat-Menu instead, which has a built in handling for the menu positioning. Mat-Select consider the width of your select element as the width of the dropdown select options and that does not require handling the left/right overflow.

    You will just need to rewrite your css and you are good to go.

    Component Template:

     <button mat-button [matMenuTriggerFor]="menu" class="currency-select">
            <span>{{ selectedCurrency }}</span>
            <mat-icon
              fontIcon="arrow_drop_down"
              style="vertical-align: bottom"
            ></mat-icon>
          </button>
    
          <mat-menu #menu="matMenu" panelClass="currency-panel">
            <button
              *ngFor="let currency of currencyArr"
              mat-menu-item
              (click)="selectedCurrency = currency.key"
            >
              <span class="fi fi-{{ currency.icon }}"></span>{{ currency.key }} -
              {{ currency.name }}
            </button>
          </mat-menu>
    

    Component CSS:

    .navbar-currency {
      margin-left: auto;
    }
    
    .currency-select {
      font-size: 0.85rem;
    }
    
    .mat-mdc-menu-panel .fi {
      margin-right: 0.2rem;
    }
    
    .currency-panel .mat-mdc-menu-item {
      min-height: 36px;
    }
    

    Checkout my stackblitz example

    If your Mat-Select will be used on both corners, then you should write your own position handler for the Mat-Select.