ionic-team / ionic-framework

A powerful cross-platform UI toolkit for building native-quality iOS, Android, and Progressive Web Apps with HTML, CSS, and JavaScript.
https://ionicframework.com
MIT License
50.71k stars 13.51k forks source link

feat: toggle menu when ion-split-pane is visible #18692

Open IsaacHub opened 5 years ago

IsaacHub commented 5 years ago

I'm using <ion-split-pane> and I need the menu toggle button on all screen sizes, how can I achieve this?

In desktop toggle menu doesn't appear, and <ion-menu-button></ion-menu-button> exists in all pages' toolbar.

Toggle menu only show up in the mobile view, but I want this in desktop view also.

ionicsplitpane

joelmeaders commented 5 years ago

I had the same problem and found a fix. Previously I was just using:

<ion-buttons>
    <ion-menu-button></ion-menu-button>
        ...other code
</ion-buttons>

image

To fix it, I am now using:

<ion-buttons>
    <ion-menu-toggle color="light">
        <ion-menu-button color="light"></ion-menu-button>
    </ion-menu-toggle>
        ...other code
</ion-buttons>

image image ...Sorry for all the edits lol

brandyscarney commented 5 years ago

Thanks for the issue! If you just want the ion-menu-button to always show, you can add the auto-hide attribute / autoHide property to the element.

I'm not sure what framework you're using, but in vanilla JS it would be:

<ion-menu-button auto-hide="false">

and in Angular:

<ion-menu-button autoHide="false">

Documentation on menu button properties: https://ionicframework.com/docs/api/menu-button#properties

If you're running into the problem where there is a space even though the menu button is hidden, please subscribe to this issue: https://github.com/ionic-team/ionic/issues/18666

Thanks!

IsaacHub commented 5 years ago

@joelmeaders @brandyscarney You both completely got me wrong. I said I should be able to collapse and expand the side menu whenever I want. (I'm talking about desktop browser only). It's possible only if the menu button shows up. This is my requirement.

I have another choice and get rid of ion-split-pane completely, and just use ion-menu without wrapping in ion-split-pane, but we have a problem with that also, it has only <ion-menu type="push | reveal | overlay"> these 3 options, and all these options pushes the content off-screen. This is not expected behavior.

I want the side-menu to be fixed, not overlay or something. It's unbelievable this tiny feature is missing from this popular framework.

quynh-ng commented 5 years ago

I still got this issue even upgrade to dev version of 4.6.1 In previous version, the ion-menu-button works correctly with ion-split-pane: hide on large screen and shown on small screen. To get the expected behavior (in current dev version), need to wrap into ion-menu-toggle (just like the way of @joelmeaders does). This is just a bug of ion-menu-button component @brandyscarney.

brandyscarney commented 5 years ago

@quynh-ng You are thinking of the following issue: https://github.com/ionic-team/ionic/issues/18666

This is fixed by https://github.com/ionic-team/ionic/pull/18702 but it has not been in a release yet.

brandyscarney commented 5 years ago

@IsaacHub Thanks, I can update your original issue, but it was not clear from your description.

daveshirman commented 4 years ago

Solution: For anyone wanting a right side menu that is always hidden in a SplitPane, just put it outside the SplitPane in your app.html. Then just programmatically show/hide it using MenuController.open/close.

This should be documented!


@IsaacHub Did you ever find a fix for this? I have a right side menu that I want to optionally show/hide on desktop, same as you do with your left.

@brandyscarney How this still not a documented feature is beyond me!

Thanks.

joelmeaders commented 4 years ago

@daveshirman I posted a detailed way to accomplish what you want here: https://github.com/ionic-team/ionic/issues/15538#issuecomment-479090705

daveshirman commented 4 years ago

@daveshirman I posted a detailed way to accomplish what you want here: #15538 (comment)

Thanks - I ended up (by chance) figuring out that you can just put the second menu outside the split pane and it works as I wanted, with the left menu collapsing as normal and the right one always hidden. See my edit. Don't know if that achieves anything different to your solution which I've read. Cheers!

Note: I see yours is V4, mine is V3.

mickdelaney commented 4 years ago

Is there a solution yet to the original problem ? i.e. being able to collapse the menu on desktop ?

joelmeaders commented 4 years ago

Is there a solution yet to the original problem ? i.e. being able to collapse the menu on desktop ?

If the menu is inside of a split pane use the "when" property on it. Pass in a boolean value of false. If it's false the menu is hidden on desktop unless it's swiped open or toggled. An observable in a singleton service does the trick nicely. It also plays nice with the toggle buttons and they're visible as they should be when the menu is not showing.

<ion-split-pane when="menuService.$showMenu | async">

You can also just pass in "false" right there in the HTML to quickly test it out.

corysmc commented 4 years ago

Here's my solution to this problem - works really well:

  1. when on a mobile size screen - the menu toggles open and close
  2. when on desktop the split pane displays the menu
  3. The menu icon still displays so you can close the menu while on desktop

ion-split-pane

toggleMenu() {
    const splitPane = document.querySelector('ion-split-pane');
    const windowWidth = window.innerWidth;
    const splitPaneShownAt = 992;
    const when = `(min-width: ${splitPaneShownAt}px)`;
    if (windowWidth >= splitPaneShownAt) {
      // split pane view is visible
      const open = splitPane.when === when;
      splitPane.when = open ? false : when;
    } else {
      // split pane view is not visible
      // toggle menu open
      const menu = splitPane.querySelector('ion-menu');
      return menu.open();
    }
  }

Then in your view use an ion-button instead of the ion-menu-button:

<ion-buttons slot="start">
  <ion-button onClick={() => this.toggleMenu()}>
    <ion-icon name="menu" slot="icon-only" />
  </ion-button>
</ion-buttons>
IsaacHub commented 4 years ago

@corysmc I tried your code snippet, and it worked.

cameronrr commented 4 years ago

Add a click handler on the standard ion-menu-toggle button. Make sure 'auto-hide="false"' is set on the toggle so the button is always visible even in split pane mode.

In the event handler, check the ion-split-pane 'when' attribute against the ionic media query breakpoints (or directly if not using a breakpoint alias). If the screen width is large enough to usually show the split panel, toggle the visibility to toggle the menu display.

The default click handler to animate menu in single pane view still works as per normal.

<ion-buttons slot="start">
    <ion-menu-toggle auto-hide="false" menu="main-menu">
        <ion-button @click="toggleMenu">
            <ion-icon slot="icon-only" name="menu"></ion-icon>
        </ion-button>
    </ion-menu-toggle>
</ion-buttons>
import { SIZE_TO_MEDIA } from '@ionic/core/dist/collection/utils/media'

toggleMenu: () => {
    const splitPane = document.querySelector('ion-split-pane')
    if (window.matchMedia(SIZE_TO_MEDIA[splitPane.when] || splitPane.when).matches)
        splitPane.classList.toggle('split-pane-visible')
}
siutau commented 3 years ago

Only you need to add when="false" in ion-menu-toggle

Example:

<ion-split-pane contentId="main-content" when="false">...</ion-split-pane>
aacassandra commented 3 years ago

Only you need to add when="false" in ion-menu-toggle

Example:

<ion-split-pane contentId="main-content" when="false">...</ion-split-pane>

this method make a sidebar was autohide for first look

trick of @cameronrr, it works like a charm 🎉 but like a hacky, but unfortunately Ionic doesn't provide this functionality.

y0nd0 commented 3 years ago

The current behavior is great. The only problem is that the menu button is not able to trigger the menu anymore when the split-pane shows the menu. The auto-hide="false" option is useless in this case. But I'm not sure if that will work with the split-pane. We need an drawer. The menu should work like type=push. But in this case the app content (e.g. router-outlet) should not be pushed, it should be act like a flex element. Complete width. The side menu should just take some width when displayed. Other frameworks like Angular Material or Vuetify works like that. ... But the current behavior of Ionic is also a great case. But some developer want to keep toggle the menu.

vicatcu commented 3 years ago

Isn't the actual solution to this to bind [when]="false" to the ion-split-pane? ... or an application-specific boolean-valued variable that tracks whether or not the given ion-split-pane should be open or not?

y0nd0 commented 3 years ago

@vicatcu In short: No, because the menu button still not work in this case. (as I wrote).

folencao commented 2 years ago

Only you need to add when="false" in ion-menu-toggle

Example:

<ion-split-pane contentId="main-content" when="false">...</ion-split-pane>

This is the best simple solution.

RZMiRaN commented 2 years ago

@corysmc @cameronrr you guys are the best! I've been trying to animate the .split-pane-visible class, any idea how can I do that?

cgonza85 commented 10 months ago

toggle menu in web format with animation, under the 992 it works normally, since only in web format the behavior was very rough.

Can be improve but it works.

<ion-split-pane contentId="main">
   <ion-menu contentId="main">
     ...
   </ion-menu>
   <div class="ion-page" id="main">
      <ion-header [translucent]="true">
         <ion-toolbar>
            <ion-buttons slot="start">
               <ion-menu-button autoHide="false" (click)="toggleMenu()"></ion-menu-button>
            </ion-buttons>
         </ion-toolbar>
      </ion-header>
     <ion-router-outlet></ion-router-outlet>
   </div>
</ion-split-pane>
    toggleMenu() {
    const splitPane = document.querySelector('ion-split-pane');
    if(splitPane) {
      const main = splitPane.querySelector('#main');
      const menu = splitPane.querySelector('ion-menu');
      if(menu && main) {
        main.classList.toggle('split-pane-main-hide-menu');
        const windowWidth = window.innerWidth;
        const splitPaneShownAt = 992;
        if (windowWidth >= splitPaneShownAt) {
          if(!main.classList.contains('split-pane-main-hide-menu')){
            menu.classList.remove('menu-pane-visible-animation-out');
            menu.classList.add('menu-pane-visible-animation-in');
          } else {
            menu.classList.add('menu-pane-visible-animation-out');
            menu.classList.remove('menu-pane-visible-animation-in');
          }
        } else {
          menu.classList.remove('menu-pane-visible-animation-in');
          menu.classList.remove('menu-pane-visible-animation-out');
        }
      }
    }
  }
@keyframes slideInFromLeft {
  0% {
    transform: translateX(-100%);
  }
  100% {
    transform: translateX(0);
  }
}

@keyframes slideOutFromRight {
  0% {
    transform: translateX(0);
  }
  100% {
    transform: translateX(-100%);
  }
}

.menu-pane-visible-animation-out {
  animation: 0.5s ease 0s 1 slideOutFromRight;
}

.menu-pane-visible-animation-in {
  animation: 0.5s ease 0s 1 slideInFromLeft;
}

.split-pane-visible > .ion-page.split-pane-main {
  position: absolute;
  margin-left: 300px;
}

.split-pane-main-hide-menu {
  margin-left: 0px !important;
}

.ion-page.split-pane-main {
  transition: all 500ms ease;
}

There is one thing that I could not solve, when changing the resolution manually the menu disappears without animation, but the content has animation so it is not very noticeable.

GarryLowther commented 5 months ago

Is anyone able to post a complete index.html page with this split pane effect working?