components icon indicating copy to clipboard operation
components copied to clipboard

Navigation drawer - Mini variant behavior

Open Daugoh opened this issue 9 years ago • 103 comments

Feature request:

Add the "Mini variant" behavior of the Navigation drawer. As defined in the Google Material guidelines: https://material.google.com/patterns/navigation-drawer.html#navigation-drawer-behavior

What is the expected behavior?

When toggled, the navigation drawer change its width. It's not fully hidden.

What is the current behavior?

Only two states are available: displayed, hidden

What are the steps to reproduce?

Create a sidenav and toggle it on and off. The sidenav can only be opened or closed. No intermediate state.

What is the use-case or motivation for changing an existing behavior?

Desktop users need to quickly change the current view. The sliding effect of the sidenav could be annoying when used a lot. It's common for desktop apps to keep the navigation opened by default. But in some cases the icons are self explanatory so no need to display the associated labels.

Which versions of Angular, Material, OS, browsers are affected?

Angular2

Is there anything else we should know?

A similar feature request was submitted for Angular 1: https://github.com/angular/material/issues/3158

Daugoh avatar Nov 04 '16 11:11 Daugoh

Is there any reason you couldn't control the width of the sidenav with css and toggle classes based on which width you want to display?

jelbourn avatar Nov 09 '16 04:11 jelbourn

Hello @jelbourn

Thanks for your answer.

Yes I tried to do it manually. I can show/hide the texts with a CSS class or use a ngIf. It works but then I have a problem of alignment for my icons. They are not correctly centered.

So yes we can do it ourselves but I think it would be really useful to have it natively. And then just use an attribute like textVisible or iconOnly for instance ...

Regards

Daugoh avatar Nov 14 '16 17:11 Daugoh

Is that feature has being planned yet? Is there any deadlines on that?

@daugoh have you come up with any proper soulution for this one?

Here is https://github.com/angular/material2/issues/2021 one of the ways http://plnkr.co/edit/LcRDIHNFjHKLFTaMdNEM?p=preview of doing that

kuncevic avatar Dec 15 '16 00:12 kuncevic

Probably, You can manage it buy css for intermediate state as below. You might need some css overrides as per need.

<md-sidenav-layout fullscreen> <md-sidenav #sidenav> <md-nav-list> <a md-list-item (click)="sidenav.toggle()"> <md-icon md-list-icon>menu</md-icon> </a> <a md-list-item *ngFor="let view of views"> <md-icon md-list-icon>{{view.icon}}</md-icon> <span md-line>{{view.name}}</span> </a> </md-nav-list> </md-sidenav> <app-contacts></app-contacts> </md-sidenav-layout>

/*left sidenav style override*/ md-sidenav { background-color: #f8921e; overflow: hidden; } md-nav-list a[md-list-item] { color: #fff; } md-nav-list a[md-list-item] .md-list-item{ padding: 0 10px !important; } .md-sidenav-over.md-sidenav-closed md-nav-list a[md-list-item] .md-list-text { display: none !important; } md-sidenav.md-sidenav-closed { transform: translate3d(0, 0, 0) !important; visibility: visible !important; min-width: 40px !important; }

MrNikunjPatel avatar Dec 24 '16 06:12 MrNikunjPatel

@kuncevic So far I'm just using a quick workaround with a ngIf to show/hide the text

Daugoh avatar Jan 09 '17 14:01 Daugoh

Any news?

vtolstov avatar Jan 17 '17 19:01 vtolstov

@rajamarketing can you help me and provide css with @media query so i don't need to watch in angular componnet for window resize events and css applied automatic?

vtolstov avatar Jan 19 '17 15:01 vtolstov

Hi people, news?

brunoracosta avatar May 30 '17 01:05 brunoracosta

from @rajamarketing comment here is what I've come up with, be it sass complains but it compiles...

    md-sidenav {
        width: 200px;

        md-icon {
            padding-right: 20px !important;
        }

        &.mat-sidenav-closed {
            transform: translate3d(0, 0, 0) !important; 
            visibility: visible !important;
            min-width: 64px !important;
            width: 64px !important;

            &~ /deep/.mat-sidenav-content {
                margin-left: 64px !important;
            }
        }
    }
}

Stoom avatar Jun 12 '17 12:06 Stoom

Any updates?

unstephenk avatar Jul 20 '17 20:07 unstephenk

Seems like @daxsom has a solution. Any chance to get this with the next stable release?

JanOschii avatar Jul 23 '17 21:07 JanOschii

firebase and google analytics has this functionality. It would be awesome to have this build in and working by default

Misiu avatar Sep 04 '17 11:09 Misiu

yes pls!

rui-cruz avatar Sep 11 '17 21:09 rui-cruz

this may help .. https://github.com/Ravi-Rajpurohit/mini-md-sidenav

Ravi-Rajpurohit avatar Sep 28 '17 18:09 Ravi-Rajpurohit

Would be extremely helpful to have this as a native feature. As for now it requires some hacky css to make it work both for desktop (mini-sidenav) and mobile (classic one), which are breaking with almost every release :)

liri2006 avatar Oct 11 '17 19:10 liri2006

Any news?

matte00 avatar Nov 15 '17 15:11 matte00

Yes, any news, please?

haskelcurry avatar Nov 16 '17 09:11 haskelcurry

could be on the steps. I mentioned it to Thomas Burleson from the Angular Material team at Angular Connect. He said it shouldn't take long to make. Meanwhile, we could use the CSS hack method.

I'll just follow up on it.

morten-kirstein avatar Nov 16 '17 10:11 morten-kirstein

I hope and expect...

51Ray avatar Nov 21 '17 20:11 51Ray

any stable release with these changes?

AyushiSrivastava20 avatar Nov 29 '17 14:11 AyushiSrivastava20

Thanks to https://github.com/angular/material2/pull/8488 you can now use autosize in mat-sidenav-container, so you can resize the sidenav without css hacks.

Here's an example adapted from the "autosize sidenav" example: https://stackblitz.com/edit/angular-b4gmby

Melanie-M avatar Dec 15 '17 14:12 Melanie-M

@Melanie-M so just a question... If a remove "autosize"... I still see the same result?

mackelito avatar Jan 14 '18 18:01 mackelito

@mackelito I think without autosize the main content doesn't resize properly, the sidenav overlaps it instead. But so depending on what behavior you want, I guess it can work without autosize

Melanie-M avatar Jan 15 '18 08:01 Melanie-M

Answering @jelbourn's comment from 2 years ago. It cannot be achieved easily with css: mat-sidenav-content gets it's margin-left from mat-sidenav and you have to add the autosize to the mat-sidenav-container (wich adds perf issues) and even if you hide manually the mat-list-item's labels, there are no fancy animations on the icons/labels out-of-the-box. I think this should be the library's responsibility, not user's (because you have to override a lot of css if you want to avoid the autosize attr, enable the animations, etc. on your own, being this decisions not future-proof)

paulmelero avatar Apr 30 '18 13:04 paulmelero

must have feat

isrmicha avatar May 23 '18 16:05 isrmicha

If autosize causes performance problems -- which isn't surprising if it makes the size get rechecked on every change cycle -- can we just get a method that will force resize manually?

The code is already in the component to do all the work, and we (usually) know when we want to make it resize. Seems fair to just expose a way to force it to happen when needed, and not bother with having it check all the time.

FWIW, we're getting around this by (at time to resize) turning on autosize via a ViewChild reference, then turning it off again a second later via setTimeout. It's hacky though.

rellis-of-rhindleton avatar May 25 '18 17:05 rellis-of-rhindleton

must have :)

kyusupov33 avatar Aug 03 '18 15:08 kyusupov33

I am just going to leave this here in the hope that similar features are introduced in Angular. https://vuetifyjs.com/en/components/navigation-drawers#example-mini

adamdyson avatar Aug 08 '18 15:08 adamdyson

https://stackoverflow.com/questions/48479524/how-to-impliment-a-sidenav-navigation-drawer-with-a-mini-variant/49884854?noredirect=1#comment90117208_49884854

ghost avatar Aug 25 '18 18:08 ghost

You can also check this link: https://medium.com/@harkiratsingh.026/angular-6-mini-variant-drawer-d5326be55dd1

Harkiratsingh026 avatar Sep 04 '18 06:09 Harkiratsingh026

@jelbourn are you guys open for a PR about this? Not easy to pinpoint the specs because in current Material Design guidelines the "mini" variant is nowhere to be found, but if you tell me old specs are ok, I can try a PR which just defines a 'mini' status with user-defined width value (with sensible default values).

This issue actually proposed some kind of API for it already #9640

IlCallo avatar Oct 22 '18 13:10 IlCallo

At this point we would probably hold off on the mini variant to check with the Material Design team to see if they plan for this to still be part of the canon.

jelbourn avatar Oct 23 '18 23:10 jelbourn

@jelbourn any news from Material Design Team?

Misiu avatar Nov 06 '18 13:11 Misiu

in css: /deep/.mat-drawer.mat-drawer-end[ng-reflect-opened=false]{ left: 35px !important; visibility: visible !important; }

it working!!!

but in prod - no....

malcaESayYes avatar Nov 08 '18 12:11 malcaESayYes

try this instead ::ng-deep .mat-drawer.mat-drawer-end[ng-reflect-opened=false]{

mackelito avatar Nov 08 '18 12:11 mackelito

ng-reflect-opened property not appearing in prod - after build (im wrote the last answer)

zipporaSay avatar Nov 08 '18 13:11 zipporaSay

@zipporaSay I would suggest you to have your own selector. When you close it add class "closed" an use that selector instead

mackelito avatar Nov 08 '18 15:11 mackelito

/deep/ and ::ng-deep are deprecated because they break incapsulation, not really a good solution IMHO... Also, just setting the sidenav width should suffice to get what you're trying to do with that CSS

IlCallo avatar Nov 08 '18 17:11 IlCallo

Are there other ways to force styles to components outside the material components?

mackelito avatar Nov 08 '18 17:11 mackelito

Not from a component to another AFAIK You should use global CSS rules (aka non-scoped CSS) for that, even if it's a lot less maintenable because it moves some rules relative to a component outside the component folder/files

IlCallo avatar Nov 09 '18 08:11 IlCallo

Okay, so it's been a little over 2 years. Any core team member who would like to comment on this?

danielheddelin avatar Dec 19 '18 07:12 danielheddelin

...this would be nice!

Tyler-V avatar Dec 22 '18 19:12 Tyler-V

+1 Must have here. Thanks

Fourie-r avatar Jan 29 '19 15:01 Fourie-r

Same here, we need this.

Lakston avatar Feb 14 '19 15:02 Lakston

This is a good request, Any updates?

edit: Ravi-Rajpurohit's & Harkiratsingh026's links work great

S-amuel avatar Feb 27 '19 20:02 S-amuel

#2 This is a good request, Any updates?

lramondev avatar Mar 25 '19 20:03 lramondev

Any updates? Need this

EagleInNight avatar Apr 16 '19 18:04 EagleInNight

the angular materials team is working together with the angular core team to launch the ivy compiler, do not expect any new feature until the ivy be released

Rodrigo54 avatar Apr 17 '19 19:04 Rodrigo54

(I'm going through the highest voted issues today an commenting on their status)

We still consider this something that we want to add, but it's still one of the lower-priority features in the backlog (compared to things like virtual scrolling for table, density theming, improving CDK documentation, etc.). As I mentioned in my recent ng-conf talk, we're doing some work now to integrate MDC Web into our components, so we would likely hold off on working on this until we have a better idea of what the MDC-based sidenav would look like.

jelbourn avatar May 17 '19 23:05 jelbourn

@jelbourn just so that we can get a grasp of what is to come and what is priority.. Is https://github.com/angular/components/projects/25 "up to date"?

Also how many devs are currently focusing on angular components?

mackelito avatar Jun 24 '19 08:06 mackelito

It's up-to-date. It's hard to say an exact number since people tend to work on a number of different things around Angular.

jelbourn avatar Jun 24 '19 16:06 jelbourn

Sweet!.. all the "Build MDC-based.." are those rewrites of existing components? (sorry for hogging the issue like this but there is no clear way where to ask these questions.. and the README is on a really high level (Jan-Jun) so I kind of expect that there we not be any bigger changes or news in the near future (judging from the state in project)

Is that a fare assumption?

mackelito avatar Jun 24 '19 19:06 mackelito

There should be more to see by the end of the year

jelbourn avatar Jun 25 '19 17:06 jelbourn

I am really looking forward to this feature.

dwdc529 avatar Jul 24 '19 01:07 dwdc529

It's tin 😡🤬 You will still wait ☠️100 years, it is better to change the UI Alternatively take a look at ng-zorro

4val0v avatar Jul 30 '19 06:07 4val0v

+1

The example cited above actually works, the only problem is that, if you try using animations and autosize, it screws up the layout (you must click anywhere in the interface to "fix" it, or wait a few seconds till the digest cycle).

mrmokwa avatar Aug 08 '19 18:08 mrmokwa

Will we see this feature only in 2020? 2021...? ;)

lramondev avatar Dec 11 '19 12:12 lramondev

There are so many different implementations outside... May it become necessary there is a standard somewhere. And why not inside of this framework itself. Would be a nice xmas present ;-) But seriously if so many people already here and some repos' outside for feature that all expect its all-in material-angular... may it needs to become true...

desianonhubject avatar Dec 12 '19 16:12 desianonhubject

Would be great to see!

chris-clem avatar Dec 16 '19 15:12 chris-clem

Do I take it the issue here is that there no point in developing new features when you working towards MDC version?

Jordan-Hall avatar Jan 03 '20 11:01 Jordan-Hall

If anyone struggling to get this open without wanting to create a component. You should use a directive as its the most likely what angular team may use.

import { Directive, ElementRef, OnInit, OnDestroy, Host, Self, Optional, Renderer2, Input } from '@angular/core';
import { group, query, animateChild, animate, style, AnimationBuilder } from '@angular/animations'
import type { AnimationMetadata, AnimationStyleMetadata, AnimationGroupMetadata } from '@angular/animations';
import { MatSidenav, MatDrawer } from '@angular/material/sidenav';

import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { sidebarAnimationCloseGroup } from '../animations/animations';

const defaultDuration = '100ms';
const defaultMinWidth = '100px';
const defaultMaxWidth = '200px';

export function sidebarClose(minWidth: string = defaultMinWidth): AnimationStyleMetadata {
  return style({
    width: minWidth,
    visibility: 'visible',
  })
}



export function sidebarOpen(maxWidth: string = defaultMaxWidth): AnimationStyleMetadata {
  return style({
    width: maxWidth,
    visibility: 'visible',
  })
}


export function sidebarAnimationOpenGroup(
  animationDuration: string = defaultDuration,
  maxWidth: string = defaultMaxWidth

): AnimationGroupMetadata {
  return group([
    query('@iconAnimation', animateChild(), { optional: true }),
    query('@labelAnimation', animateChild(), { optional: true }),
    animate(`${animationDuration} ease-in-out`, sidebarOpen(maxWidth))
  ])
}

@Directive({ selector: '[mat-nav-mini]' })
export class MatNavMiniDirective implements OnInit, OnDestroy {

  public onDestory: Subject<void> = new Subject();

  private drawer: MatSidenav | MatDrawer;

  @Input()
  public openAnimation: AnimationMetadata | AnimationMetadata[];

  @Input()
  public closeAnimation: AnimationMetadata | AnimationMetadata[];

  @Input()
  public closeWidth: string = defaultMinWidth;

  @Input()
  public expandedWidth: string = defaultMaxWidth;

  constructor(
    private builder: AnimationBuilder,
    private el: ElementRef<HTMLElement>,
    private renderer2: Renderer2,
    @Host() @Self() @Optional() sidenav: MatSidenav,
    @Host() @Self() @Optional() drawer: MatDrawer,
  ) {
    this.drawer = sidenav || drawer;
  }
  public ngOnInit(): void {

    this.closeAnimation = this.closeAnimation || sidebarAnimationCloseGroup(defaultDuration, this.closeWidth);
    this.openAnimation = this.openAnimation || sidebarAnimationOpenGroup(defaultDuration, this.expandedWidth);

    this.renderer2.setStyle(this.el.nativeElement, 'overflow', 'hidden');

    this.drawer.closedStart.pipe(takeUntil(this.onDestory)).subscribe(() => {

      const factory = this.builder.build(this.closeAnimation);
      const player = factory.create(this.el.nativeElement);
      player.play();

    });

    this.drawer.openedStart.pipe(takeUntil(this.onDestory)).subscribe(() => {
      const factory = this.builder.build(this.openAnimation);
      const player = factory.create(this.el.nativeElement);
      player.play();
    });


  }

  public ngOnDestroy(): void {
    this.onDestory.next();
    this.onDestory.complete();
  }
}

Jordan-Hall avatar Jan 21 '20 16:01 Jordan-Hall

@Jordan-Hall when importing there's errors for the import type statement at the top and the ../animations/animations import.

Helveg avatar Feb 07 '20 02:02 Helveg

@Helveg change import type to import. I'm running on bleeding edge typescript and Angular. It will still work on older version of angular and typescript. Just cant use new typescript syntax :)

Jordan-Hall avatar Feb 07 '20 09:02 Jordan-Hall

@Jordan-Hall just out of curiosity. if i got your snippet right, the width property will be animated, right?. I'm not exactly sure how the web animation api handles this - but animating width in CSS is a pain point as the browser has to recalculate the whole document(-part) in every frame to relayout sibling elements. (could lead to jank on slower devices like older phones). Any chance this is possible with the efficient 4? (translate, transform, scale, opacity).

nlappe avatar Feb 07 '20 22:02 nlappe

@nlappe You are correct in the sense it might not be efficient. I only wrote it in just under 2 hours after studying the component internals. I tried to override or "prevent" closing animation by angular but couldn't work it out.

I'll take a look this weekend with the efficient 4 and create a test project for it.

Jordan-Hall avatar Feb 07 '20 23:02 Jordan-Hall

Thanks to #8488 you can now use autosize in mat-sidenav-container, so you can resize the sidenav without css hacks.

Here's an example adapted from the "autosize sidenav" example: https://stackblitz.com/edit/angular-b4gmby

Just what I was looking for, thanks for sharing -__-

rfgonzalezweb avatar Mar 04 '20 17:03 rfgonzalezweb

I am wondering about how old is this issue.

Use at your own risk! Enabling this option can cause layout thrashing by measuring the drawers on every change detection cycle. Can be configured globally via the MAT_DRAWER_DEFAULT_AUTOSIZE token.

Since autosize flag description doesn't look very friendly, I am trying it via css.

https://stackblitz.com/edit/angular-mini-sidenav-by-css

It is not perfect but it fits my needs, I hope it helps anybody. 😃

angelfraga avatar Apr 15 '20 15:04 angelfraga

I am wondering about how old is this issue.

Use at your own risk! Enabling this option can cause layout thrashing by measuring the drawers on every change detection cycle. Can be configured globally via the MAT_DRAWER_DEFAULT_AUTOSIZE token.

Since autosize flag description doesn't look very friendly, I am trying it via css.

https://stackblitz.com/edit/angular-mini-sidenav-by-css

It is not perfect but it fits my needs, I hope it helps anybody. 😃

Thanks for your contribution. I'm sure there is a communication problem between the development team.

lramondev avatar Apr 15 '20 17:04 lramondev

@lramondev It's not a lack of communication. Material design team haven't come up with a standard for the Mini variant.

Jordan-Hall avatar Apr 15 '20 21:04 Jordan-Hall

Exact. I consider the lack of communication between the team to be a very requested resource

lramondev avatar Apr 16 '20 13:04 lramondev

Guys just do it with some css .mat-drawer-side { border-right: none !important; } .mat-drawer-content { background-color: #fafafa; margin-left: 64px !important; border-left: solid 1px rgba(0, 0, 0, 0.12); z-index: 10 !important; will-change: margin-left; transition: margin-left 0.3s ease-in-out; } .is-expanded { margin-left: 230px !important; } then toggle is-expanded class with ngClass in ts file toggleMenu() { this.isExpanded = !this.isExpanded; } in html <mat-sidenav-content [ngClass]="{ 'is-expanded': isExpanded }"> if you want hover support too <mat-sidenav #snav [mode]="mobileQuery.matches ? 'over' : 'side'" [opened]="true" (mouseenter)="toggleMenu()" (mouseleave)="toggleMenu()" >

aram-m avatar Apr 21 '20 09:04 aram-m

@aram-m the issue with animation apparently. However this is basically how i manged it with a directive https://github.com/angular/components/issues/1728#issuecomment-576750289

Jordan-Hall avatar Apr 21 '20 10:04 Jordan-Hall

@Jordan-Hall yeap but this is much simpler solution with few lines of css, you're solution es very good too :)

aram-m avatar Apr 21 '20 11:04 aram-m

@aram-m the issue with animation apparently. However this is basically how i manged it with a directive #1728 (comment)

I 've noticed there is a reference to import { sidebarAnimationCloseGroup } from '../animations/animations';,

@Jordan-Hall could you add a working example in Stackblitz or similar? 🚀

angelfraga avatar Apr 21 '20 18:04 angelfraga

@angelfraga I've improved the code 😊 so it now works on mode="mini" and thats all you need to do.

Blog post: https://medium.com/@LostDeveloper/angular-material-navigation-drawer-adding-support-mode-rail-mini-variant-behaviour-8f7b107700b3

Demo: https://angular-material-mini-variant.stackblitz.io/

Code: default.config.ts:

export const miniConfig = {
  defaultDuration: '100ms',
  defaultMinWidth: '60px',
  defaultMaxWidth: '300px',
}

animations.settings.ts:

import { group, query, animateChild, animate, style, AnimationBuilder } from '@angular/animations'
import { AnimationStyleMetadata, AnimationGroupMetadata } from '@angular/animations';
import { miniConfig } from './default.config';

export function sidebarClose(minWidth: string = miniConfig.defaultMinWidth): AnimationStyleMetadata {
  return style({
    width: minWidth,
    visibility: 'visible',
    transform: 'none',
    overflow: 'hidden'
  })
}



export function sidebarOpen(maxWidth: string = miniConfig.defaultMaxWidth): AnimationStyleMetadata {
  return style({
    width: maxWidth,
    visibility: 'visible',
  })
}


export function sidebarAnimationOpenGroup(
  animationDuration: string = miniConfig.defaultDuration,
  maxWidth: string = miniConfig.defaultMaxWidth

): AnimationGroupMetadata {
  return group([
    query('@iconAnimation', animateChild(), { optional: true }),
    query('@labelAnimation', animateChild(), { optional: true }),
    animate(`${animationDuration} ease-in-out`, sidebarOpen(maxWidth))
  ])
}

export function sidebarAnimationCloseGroup(
  animationDuration: string = miniConfig.defaultDuration,
  minWidth: string = miniConfig.defaultMinWidth

): AnimationGroupMetadata {
  return group([
    query('@iconAnimation', animateChild(), { optional: true }),
    query('@labelAnimation', animateChild(), { optional: true }),
    animate(`${animationDuration} ease-in-out`, sidebarClose(minWidth))
  ])
}

mini.directive.ts:

import { forwardRef, Inject, Directive, ElementRef, OnInit, OnDestroy, Host, Self, Optional, Renderer2, Input, AfterContentInit } from '@angular/core';
import { AnimationMetadata, AnimationStyleMetadata, AnimationGroupMetadata } from '@angular/animations';
import { AnimationBuilder } from '@angular/animations'

import { MatSidenav, MatDrawer, MatSidenavContainer, MatDrawerContainer } from '@angular/material';

import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

import { miniConfig } from './default.config';
import { sidebarAnimationCloseGroup, sidebarAnimationOpenGroup  } from './animations.settings';
import {Directionality} from '@angular/cdk/bidi';

@Directive({
  selector: 'mat-sidenav[mode="mini"], mat-drawer[mode="mini"]',
  host: {
    '[class.mat-drawer-side]': 'true',
  }
})
export class MatNavMiniDirective implements OnInit, OnDestroy, AfterContentInit {

  public onDestory: Subject<void> = new Subject();

  private drawer: MatSidenav | MatDrawer;
  private container: MatSidenavContainer | MatDrawerContainer;

  @Input()
  public openAnimation: AnimationMetadata | AnimationMetadata[];

  @Input()
  public closeAnimation: AnimationMetadata | AnimationMetadata[];

  @Input()
  public closeWidth: string = miniConfig.defaultMinWidth;

  @Input()
  public expandedWidth: string = miniConfig.defaultMaxWidth;

  constructor(
    private builder: AnimationBuilder,
    private el: ElementRef<HTMLElement>,
    private renderer2: Renderer2,
    @Host() @Self() @Optional() sidenav: MatSidenav,
    @Host() @Self() @Optional() drawer: MatDrawer,
    @Inject(forwardRef(() => MatSidenavContainer)) @Optional() matSideNavContainer: MatSidenavContainer,
    @Inject(forwardRef(() => MatDrawerContainer)) @Optional() matDrawerContainer: MatDrawerContainer,
    @Optional() private _dir: Directionality,
    
  ) {
    this.container = matSideNavContainer || matDrawerContainer;
    this.drawer = sidenav || drawer;
    this.container.hasBackdrop = false;
    this.container.autosize = true;
  }

  public ngOnInit(): void {
    this.closeAnimation = this.closeAnimation || sidebarAnimationCloseGroup(miniConfig.defaultDuration, this.closeWidth);
    this.openAnimation = this.openAnimation || sidebarAnimationOpenGroup(miniConfig.defaultDuration, this.expandedWidth);  
    this.renderer2.setStyle(this.el.nativeElement.querySelector('.mat-drawer-inner-container'), 'overflow', 'hidden');

    this.drawer.closedStart.pipe(takeUntil(this.onDestory)).subscribe(() => {
       const containerContent = this.el.nativeElement.parentElement.querySelector('.mat-drawer-content');
        if (this.drawer.position != 'end' || this._dir && this._dir.value != 'rtl') {
          this.renderer2.setStyle(containerContent, 'marginLeft', this.closeWidth);
        } else {
          this.renderer2.setStyle(containerContent, 'marginRight', this.closeWidth);
        }
      const factory = this.builder.build(this.closeAnimation);
      const player = factory.create(this.el.nativeElement);
      player.play();
      
    });

    this.drawer.openedStart.pipe(takeUntil(this.onDestory)).subscribe(() => {
      const containerContent = this.el.nativeElement.parentElement.querySelector('.mat-drawer-content');
        if (this.drawer.position != 'end' || this._dir && this._dir.value != 'rtl') {
          this.renderer2.setStyle(containerContent, 'marginLeft', this.expandedWidth);
        } else {
          this.renderer2.setStyle(containerContent, 'marginRight', this.expandedWidth);
        }
      const factory = this.builder.build(this.openAnimation);
      const player = factory.create(this.el.nativeElement);
      player.play();
    });

    
  }

  ngAfterContentInit() {
     const containerContent = this.el.nativeElement.parentElement.querySelector('.mat-drawer-content');
        if (this.drawer.position != 'end' || this._dir && this._dir.value != 'rtl') {
          this.renderer2.setStyle(containerContent, 'marginLeft', this.drawer._width + 'px');
        } else {
          this.renderer2.setStyle(containerContent, 'marginRight', this.drawer._width + 'px');
        }
  }

  public ngOnDestroy(): void {
    this.onDestory.next();
    this.onDestory.complete();
  }
}

Disclaimer: This code isn't supported. You may not get response to any issues relating to this code. This code has also not been tested in production system. It's been developed adhoc and is a snippet of the code that's currently in development

Jordan-Hall avatar May 09 '20 12:05 Jordan-Hall

Looks like specs have finally been given by the material team https://material.io/components/navigation-rail#behavior

In it you can merge with the navigation drawer

Jordan-Hall avatar May 09 '20 15:05 Jordan-Hall

@Jordan-Hall Thank you for your solution. I was trying to find a solution yesterday as well. Do you want to create a package for this?

Btw, I think we can improve the performance by setting

this.container.autosize = true; after openedStart/ closedStart

and set it as false like

player.onDone(() => {
				this.container.autosize = false;
			});

maxisam avatar May 11 '20 16:05 maxisam

Sure, I'll create a package shortly. I'm also going to try and create the Rail component and maybe try and make a PR for this.

Jordan-Hall avatar May 11 '20 17:05 Jordan-Hall

@maxisam created the NPM package https://www.npmjs.com/package/angular-material-rail-drawer Repo here https://github.com/Jordan-Hall/angular-material-rail-drawer-plugin

Any issues please let me know. I've just picked up next blog I need to write and a small freelance role then I'll hopefully have a PR ready for here

Jordan-Hall avatar May 11 '20 23:05 Jordan-Hall

Tried that but getting the below issue. @Jordan-Hall

error TS2322: Type '"rail"' is not assignable to type 'MatDrawerMode'. <mat-sidenav #appDrawer mode="rail" opened="true">

KaranPato avatar Sep 10 '21 08:09 KaranPato

this may help .. https://github.com/Ravi-Rajpurohit/mini-md-sidenav

@KaranPato duniya gol hai mere bhai.

Ravi-Rajpurohit avatar Sep 10 '21 08:09 Ravi-Rajpurohit

I am able to implement what you have implemented @Ravi-Rajpurohit, it's just the transition is not there which you can see here

https://stackblitz.com/edit/angular-material-mini-variant

KaranPato avatar Sep 10 '21 08:09 KaranPato

any news on this ? years after ....

anymos avatar Jul 25 '22 07:07 anymos

@anymos Last i heard the scope for rail has been considerably .

Jordan-Hall avatar Jul 25 '22 07:07 Jordan-Hall

I created an example based on scss. Maybe someone can help to create the mobile version according to this sample.

  • Step 1: styles.scss
// src/styles.scss
:root {
  --menu-width-open: 200px;
  --menu-width-closed: 64px;
}

.mat-drawer-container {
  .mat-drawer {
    box-sizing: content-box;
    width: var(--menu-width-open);
    transition: all 0.3s ease-in-out !important;
  }

  .mat-drawer-content {
    // transform: translateX(200px);
    margin-left: var(--menu-width-open) !important;
    transition: all 0.3s ease-in-out !important;
  }

  &.container-closed {
    .mat-drawer {
      width: var(--menu-width-closed);
    }

    .mat-drawer-content {
      // transform: translateX(64px) !important;
      margin-left: var(--menu-width-closed) !important;
    }
  }
}
  • Step 2: app.componenet.html Add: [ngClass]="{ 'container-closed': !isExpanded }" to mat-drawer-container
<mat-drawer-container class="example-container" autosize [ngClass]="{ 'container-closed': !isExpanded }">
  <mat-drawer #drawer class="example-sidenav" mode="side" disableClose="true" opened="true">
    <button (click)="isExpanded = !isExpanded" mat-raised-button>click</button>
  </mat-drawer>
  <div class="example-sidenav-content">
    <router-outlet></router-outlet>
    <button type="button" mat-button (click)="drawer.toggle()">Toggle sidenav</button>
  </div>
</mat-drawer-container>
  • Step 3: app.component.ts And add isExpanded = false; to app.component.ts file.
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})
export class AppComponent {
  isExpanded = false;
}

serkon avatar Jul 30 '22 21:07 serkon

@serkon have you taken a look at this one I've done https://github.com/Jordan-Hall/angular-material-rail-drawer-plugin

Jordan-Hall avatar Jul 31 '22 09:07 Jordan-Hall

@serkon have you taken a look at this one I've done https://github.com/Jordan-Hall/angular-material-rail-drawer-plugin

Hi Jordan, thanks for suggestion. I have tried yesterday. It was not worked for me. I have used Angular Metarial Latest, and get error about 'rail' directive. It say that it's not an acceptable key by the "mat-sidenav". Maybe it was my fault or check please again maybe there are an error.

So, I decided to forward with my own solution.

serkon avatar Jul 31 '22 12:07 serkon