components icon indicating copy to clipboard operation
components copied to clipboard

feat(sidenav): add swipe functionality

Open rafaelss95 opened this issue 8 years ago β€’ 40 comments
trafficstars

Bug, feature request, or proposal:

Feature request

What is the expected behavior?

It'd be great to have the functionality of swipe in md-sidenav, like in https://material.io/guidelines/.

What are the steps to reproduce?

Open https://material.io/guidelines/ in mobile and see how the swipe on menu works.

Is there anything else we should know?

The same feature request was requested in material(1) here and at some point it was implemented, but not merged.

rafaelss95 avatar Jul 12 '17 23:07 rafaelss95

We do plan on doing this.

jelbourn avatar Jul 13 '17 02:07 jelbourn

Hi, I have only recently jumped on the angular wagon and find all of the different versions to be completely confusing. At the moment I have an app underway using Angular2 and the material web components which seems to be a fairly reliable stack, however following the evolution of this feature I'm beginning to question my choice.

This is absolutely basic functionality for a sidenav, which I have first encountered when using a similar component in Nativescript. The fact that this was first suggested in 2014 and yet 3 years later is only in the plans is extremely frustrating! Apparently someone has implemented the feature, but because of the management of the project and "surge focus on material 2" it never got merged. This is enough to make me dump the whole Angular2 project and take Polymer web components into use since they have implemented drag on their sidenav component. This is needless to say disappointing since otherwise the material library has worked reasonably well. There were a lot of plus ones originally on this feature, I suspect it should have been given higher priority.

thomasmoon avatar Aug 04 '17 13:08 thomasmoon

@jelbourn, is there an ETA for when this will be implemented?

I don't see any reference to this in the "In progress, planned, and non-planned features" section of the readme.

bradyshutt avatar Aug 25 '17 18:08 bradyshutt

Is there any workaround? thx

Zwilla avatar Oct 01 '17 16:10 Zwilla

@rafaelss99, in fact there's a better example from the rival library: http://www.material-ui.com/#/components/drawer

Facebook-backed JS library has better material drawer (yes, they also use the official term) than Google-backed TS framework!

mahmoudajawad avatar Nov 23 '17 02:11 mahmoudajawad

@AjawadMahmoud material-ui is for React.

Airblader avatar Nov 30 '17 21:11 Airblader

Wasn't my comment clear enough, @airblader?

mahmoudajawad avatar Nov 30 '17 23:11 mahmoudajawad

Well you are presenting it as an alternative to Angular Material. But it works with an entirely different technology.

Airblader avatar Dec 01 '17 06:12 Airblader

This was just an example. At the end of the day it's JavaScript beneath it. I can rewrite the exact procedure back for Angular.

On Fri, 1 Dec 2017, 10:03 Ingo BΓΌrk, [email protected] wrote:

Well you are presenting it as an alternative to Angular Material. But it works with an entirely different technology.

β€” You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/angular/material2/issues/5712#issuecomment-348409445, or mute the thread https://github.com/notifications/unsubscribe-auth/AATVPmaVKb9QpeazZxuXF4QBP-hW7yhNks5s75atgaJpZM4OWUqo .

mahmoudajawad avatar Dec 01 '17 06:12 mahmoudajawad

Polymer's swipeable sidenav is the only implementation I know of that enhances UX and it has been like that for at least two years: http://polymerelements.github.io/app-layout/templates/shrine A poor implementation (material-ui for instance) is worse than no implementation.

christophbuehler avatar Jan 06 '18 02:01 christophbuehler

This is maybe a workaround but it works for the dialog should work for the sidenav to. This will require hammerjs.

Place a div around your content in the html:

<div style="width: 100%; height: 100%; position: absolute;" (swipeleft)="closeDialog()" (click)="closeDialog()">

Swoox avatar Jan 10 '18 08:01 Swoox

Nothing new on this?

osouza-de avatar Mar 27 '18 20:03 osouza-de

Currently using a forked ionic-split-pane to get a swipe/slideable sidenav: https://github.com/xcaliber-tech/ionic-split-pane

Which involved a lot of hacking and ripping out random ion dependencies on other unrelated things like ion-nav, ion-button and ion-content. Would be awesome to remove that monstrosity when swipe behaviour lands here :)

intellix avatar May 16 '18 09:05 intellix

@Swoox's solution didn't work for me, but brought me to hammerjs and this solution:

app-container.component.scss

.app-container {
    height: 100%;
    width: 100%;
    position: absolute;
}

app-container.component.html

<mat-sidenav-container class="app-container">
  <mat-sidenav #sidenav>Sidenav content</mat-sidenav>
  <mat-sidenav-content>
    Main Content
  </mat-sidenav-content>
</mat-sidenav-container>

app-container.component.ts

import { Component, Input, ElementRef, ViewChild } from '@angular/core';
import * as Hammer from 'hammerjs';
import { MatSidenav } from '@angular/material';

@Component({
    selector: 'app-container',
    templateUrl: './app-container.component.html',
    styleUrls: ['./app-container.component.scss']
})
export class AppContainerComponent {

    @ViewChild(MatSidenav)
    public sidenav: MatSidenav;

    constructor(elementRef: ElementRef) {
        const hammertime = new Hammer(elementRef.nativeElement, {});
        hammertime.on('panright', (ev) => {
            this.sidenav.open();
        });
        hammertime.on('panleft', (ev) => {
            this.sidenav.close();
        });
    }
}

It works as long as the content doesn't exceed the width of 100%, otherwise it will scroll the page and open the menu.

maxfriedmann avatar May 29 '18 10:05 maxfriedmann

Thanks @maxfriedmann works like a charm!!! πŸ‘ πŸ‘ πŸ‘ πŸ‘ πŸ‘ πŸ‘ πŸ‘ πŸ‘ πŸ‘

tonysamperi avatar Jul 09 '18 15:07 tonysamperi

Thanks @maxfriedmann, If you need to prevent closing when you want to scroll up/down when your sidenav is more thant 100% height then add the following to your code :

const hammertime = new Hammer(elementRef.nativeElement, {});
hammertime.get('pan').set({direction:` Hammer.DIRECTION_ALL});
hammertime.on('panright', (ev) => {
            this.sidenav.open();
        });
        hammertime.on('panleft', (ev) => {
            this.sidenav.close();
        });
hammertime.on('panup', (ev) => false);
hammertime.on('pandown', (ev) => false);

jeanpul avatar Jul 22 '18 11:07 jeanpul

anyway...isn't this deadly to performance? I removed it since on mobile I'm wrapping the app in Ionic 3.9..

tonysamperi avatar Jul 22 '18 11:07 tonysamperi

@maxfriedmann Like you solution but what if you add another drawer on the right side? How would you solve that?

mackelito avatar Jul 25 '18 15:07 mackelito

I haven't had that problem, only used a single drawer.

Please have a look at https://hammerjs.github.io/recognizer-pan, you actually get the start and stop positions of the pan action. You could e.g. split the screen in 1/3 and 2/3 and decide programmatically, which drawer to open...

maxfriedmann avatar Jul 25 '18 15:07 maxfriedmann

@maxfriedmann thx for pointing me in the right direction... but at this time it needs to much work to fine tune so I need to wait for a better and proper implementation.

@jelbourn is there any work started on you end for this feature?

mackelito avatar Jul 26 '18 10:07 mackelito

If you only want to check for touch device swipes (and not mouse drags on desktop), you can check the event.pointerType property.

		hammertime.on('panright', (event) => {
			if (event.pointerType !== 'mouse') {
				this.sidenav.open();
			}
		});
		hammertime.on('panleft', (event) => {
			if (event.pointerType !== 'mouse') {
				this.sidenav.close();
			}
		});

PascalTemel avatar Nov 19 '18 23:11 PascalTemel

@PascalTemel Didn't try this, since we decided to remove this snippet from the desktop and use the native menu in ionic...but, nice approach.

tonysamperi avatar Nov 20 '18 08:11 tonysamperi

A slightly better option is to use the HostListener decorator. Then there is no need to create a new Hammer instance. For example, place this into the root AppComponent:

    @HostListener('panright')
    openSidenav() {
        // open the sidenav
    }

    @HostListener('panleft')
    closeSidenav() {
       // close the sidenav
    }

martindzejky avatar Nov 26 '18 17:11 martindzejky

Does this implementation have any drawbacks ? (memory leaks, performance issues) Kind regards

gxg10 avatar Dec 01 '18 11:12 gxg10

Does this functionality not present issues in mobile browsers (like Chrome iOS) where a similar gesture is used for navigate forward and back in your history?

kyranjamie avatar Jan 11 '19 14:01 kyranjamie

Hello everyone and thank you for those spectacular contributions. In my case they have helped me a lot. But I have a small inconvenience:

  1. Is it possible through Hammer to improve the animation performance of the sidenav?

When I open and close the sidenav, it has a lot of lag in the mobile devices, I have looked for how to change that for CSS and I have tried many solutions, but none has served me. I'm using Angular Material 7.3.1

  1. Is there any solution through Hammer or any other way that really works to improve the animation performance of sidenav?

And an even bolder question.

  1. Is it possible any solution through Hammer to move the sidenav according to touch of the finger? there is a very old library that does it and I have it for reference. http://jakiestfu.github.io/Snap.js/demo/apps/default.html

Thank you so much for the help you can give me.

jalfcolombia avatar Feb 23 '19 12:02 jalfcolombia

This is my contribution for the sidenav to react on the left side of the screen to open.

    const hammertime = new Hammer(elementRef.nativeElement, {});
    hammertime.get('pan').set({ direction: Hammer.DIRECTION_ALL });
    hammertime.on('panright', event => {
      if (
        event.pointerType !== 'mouse' &&
        event.center.x >= 1 && event.center.x <= 50
      ) {
        this.sidenav.open();
      }
    });
    hammertime.on('panleft', event => {
      if (event.pointerType !== 'mouse') {
        this.sidenav.close();
      }
    });
    hammertime.on('panup', event => false);
    hammertime.on('pandown', event => false);

jalfcolombia avatar Feb 28 '19 03:02 jalfcolombia

We really need a clean solution for that. Simply triggering the open is not an option.

Users are used to deferring the SideNav slowly. Depending on the slide progress, the cdk background layer must appear.

For example, with mode = "push", the content is moved slowly. Once more than 50% of the Sidenav wide is swiped, let the sidenav slide on. Less than 50% let go again.

Everything else is not a viable solution :)

SvenBudak avatar Apr 16 '19 08:04 SvenBudak

We really need a clean solution for that. Simply triggering the open is not an option.

Users are used to deferring the SideNav slowly. Depending on the slide progress, the cdk background layer must appear.

For example, with mode = "push", the content is moved slowly. Once more than 50% of the Sidenav wide is swiped, let the sidenav slide on. Less than 50% let go again.

Everything else is not a viable solution :)

yes it should work exactly like it would in android studio, thinks like this is what makes your pwa feel native

rubenheymans avatar Apr 22 '19 20:04 rubenheymans

Any update on this?

10eputzen avatar Aug 13 '19 19:08 10eputzen