Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(sidenav): add swipe functionality #5712

Open
rafaelss95 opened this issue Jul 12, 2017 · 40 comments
Open

feat(sidenav): add swipe functionality #5712

rafaelss95 opened this issue Jul 12, 2017 · 40 comments
Labels
area: material/sidenav feature This issue represents a new feature or feature request rather than a bug or bug fix P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent

Comments

@rafaelss95
Copy link
Contributor

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.

@jelbourn jelbourn added the feature This issue represents a new feature or feature request rather than a bug or bug fix label Jul 13, 2017
@jelbourn jelbourn added the P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent label Jul 13, 2017
@jelbourn
Copy link
Member

We do plan on doing this.

@thomasmoon
Copy link

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.

@bradyshutt
Copy link

@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.

@Zwilla
Copy link

Zwilla commented Oct 1, 2017

Is there any workaround?
thx

@mahmoudajawad
Copy link

@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!

@Airblader
Copy link
Contributor

@AjawadMahmoud material-ui is for React.

@mahmoudajawad
Copy link

Wasn't my comment clear enough, @Airblader?

@Airblader
Copy link
Contributor

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

@mahmoudajawad
Copy link

mahmoudajawad commented Dec 1, 2017 via email

@christophbuehler
Copy link

christophbuehler commented Jan 6, 2018

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.

@Swoox
Copy link

Swoox commented Jan 10, 2018

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()">

@osouza-de
Copy link

Nothing new on this?

@Splaktar Splaktar changed the title Add swipe functionality in md-sidenav feat(sidenav): add swipe functionality Mar 29, 2018
@intellix
Copy link

intellix commented May 16, 2018

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

@maxfriedmann
Copy link

@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.

@tonysamperi
Copy link

Thanks @maxfriedmann works like a charm!!! 👍 👍 👍 👍 👍 👍 👍 👍 👍

@jeanpul
Copy link

jeanpul commented Jul 22, 2018

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

@tonysamperi
Copy link

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

@mackelito
Copy link

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

@maxfriedmann
Copy link

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...

@mackelito
Copy link

@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?

@PascalTemel
Copy link

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();
			}
		});

@tonysamperi
Copy link

@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.

@martindzejky
Copy link

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
    }

@gxg10
Copy link

gxg10 commented Dec 1, 2018

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

@kyranjamie
Copy link

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?

@jalfcolombia
Copy link

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
Copy link

jalfcolombia commented Feb 28, 2019

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

@SvenBudak
Copy link

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

@rubenheymans
Copy link

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

@10eputzen
Copy link

Any update on this?

@lud-hu
Copy link

lud-hu commented Oct 21, 2019

Any updates on this?
The home assistant project has a very well working swiping functionality for it's sidebar. You can try it here:
https://demo.home-assistant.io

Maybe it is possible to use the same library for that? (Polymer?)

@SvenBudak
Copy link

Can you share the code @lud-hu ?

i have the auto hide toolbar if someone needs this: #16659

@jelbourn
Copy link
Member

As of Angular v9 we're removing our dependency on HammerJS and officially deciding that Angular Material will be agnostic to any gesture recognition framework. I would still like to add documentation that gives some guidance on how to do this, but we won't be adding support directly into the sidenav.

@mackelito
Copy link

@jelbourn does that mean that also other components such as mat-slide-toggle, mat-slider etc will not have swipe support?

@jelbourn
Copy link
Member

Slider will still slide since that's inherent to its use, but for now that does mean the sliding/dragging behavior on slide-toggle is going away too. The main reason for that, though, isn't HammerJS. We working on new versions of the components based on top of MDC Web, and MDC's slide-toggle (they call it switch) doesn't support dragging either.

@vanstinator
Copy link

This is an incredibly disappointing decision. The sidenav should ship with basic common sense gesture support.

@dplarina
Copy link

I built a working prototype in stackblitz that you can slide open or closed with native touch events that are passive.
Because it uses touch you'll need to open in new window and use the mobile emulator to test it.
https://stackblitz.com/edit/angular-b5gmd8

This ONLY works with the sidenav in over mode.
Unfortunately iOS still requires preventDefault for touchmove to prevent scrolling so we can't use passive. It assumes if the user has moved the touch 5px horizontally they are attempting to manipulate the drawer and disables scrolling, otherwise ignores it.

This isn't the usual, swipe and it fully opens/closes solution that I've seen in most places. You can actually pull the drawer out and let go and have it animate. It was difficult getting it to behave with the baked in animations and had to disable them while the drawer is being dragged.

There are probably a lot better ways of doing much of this and it might be brittle due to future changes in the material library but I was just interested in getting something functional that maintained the material feel and a pleasant UX, while still allowing the button toggle functionality to continue to work

HTH

@menelai
Copy link

menelai commented May 16, 2021

Any updates on this?
The home assistant project has a very well working swiping functionality for it's sidebar. You can try it here:
https://demo.home-assistant.io

Maybe it is possible to use the same library for that? (Polymer?)

Respect for the hui prefix

@Totati
Copy link
Contributor

Totati commented May 19, 2021

The "funny" part is, I think Google is slowly getting rid of the sidenav in native apps (Store, Photos, Map). Now that Android has gesture navigation, these two features (go back & open sidenav) collide, and I think this is their solution, get rid of usages of sidenav. (I switched back to 3 dot navigation because of this collision) But this is just my theory, and I think we won't see this inplemented :)

@bitrxjs
Copy link

bitrxjs commented Feb 16, 2024

@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.

angular have helpers for hammer which uses zone runOutsideOfAngular to improve perf
import { HAMMER_GESTURE_CONFIG, HammerGestureConfig } from '@angular/platform-browser';

no need to use hammerjs directly

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: material/sidenav feature This issue represents a new feature or feature request rather than a bug or bug fix P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent
Projects
None yet
Development

No branches or pull requests