mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-24 23:01:57 +08:00
fix(ion-menu): finish ion-menu and ion-split-pane
This commit is contained in:
7
packages/core/src/components.d.ts
vendored
7
packages/core/src/components.d.ts
vendored
@ -1541,7 +1541,7 @@ declare global {
|
||||
_register?: any,
|
||||
_unregister?: any,
|
||||
_setActiveMenu?: any,
|
||||
create?: any,
|
||||
createAnimation?: any,
|
||||
animationCtrl?: any
|
||||
}
|
||||
}
|
||||
@ -1572,6 +1572,11 @@ declare global {
|
||||
mode?: string,
|
||||
color?: string,
|
||||
|
||||
isOpen?: any,
|
||||
setOpen?: any,
|
||||
open?: any,
|
||||
close?: any,
|
||||
toggle?: any,
|
||||
lazyMenuCtrl?: any,
|
||||
content?: string,
|
||||
menuId?: string,
|
||||
|
@ -8,7 +8,6 @@ import { PanRecognizer } from './recognizers';
|
||||
})
|
||||
export class Gesture {
|
||||
|
||||
@Element() private el: HTMLElement;
|
||||
private detail: GestureDetail = {};
|
||||
private positions: number[] = [];
|
||||
private ctrl: GestureController;
|
||||
@ -21,13 +20,8 @@ export class Gesture {
|
||||
private hasFiredStart = true;
|
||||
private isMoveQueued = false;
|
||||
private blocker: BlockerDelegate;
|
||||
private fireOnMoveFunc: any;
|
||||
|
||||
@Event() private ionGestureMove: EventEmitter;
|
||||
@Event() private ionGestureStart: EventEmitter;
|
||||
@Event() private ionGestureEnd: EventEmitter;
|
||||
@Event() private ionGestureNotCaptured: EventEmitter;
|
||||
@Event() private ionPress: EventEmitter;
|
||||
@Element() private el: HTMLElement;
|
||||
|
||||
@Prop() enabled: boolean = true;
|
||||
@Prop() attachTo: ElementRef = 'child';
|
||||
@ -49,9 +43,12 @@ export class Gesture {
|
||||
@Prop() onPress: GestureCallback;
|
||||
@Prop() notCaptured: GestureCallback;
|
||||
|
||||
constructor() {
|
||||
this.fireOnMoveFunc = this.fireOnMove.bind(this);
|
||||
}
|
||||
@Event() private ionGestureMove: EventEmitter;
|
||||
@Event() private ionGestureStart: EventEmitter;
|
||||
@Event() private ionGestureEnd: EventEmitter;
|
||||
@Event() private ionGestureNotCaptured: EventEmitter;
|
||||
@Event() private ionPress: EventEmitter;
|
||||
|
||||
|
||||
protected ionViewDidLoad() {
|
||||
// in this case, we already know the GestureController and Gesture are already
|
||||
@ -203,7 +200,7 @@ export class Gesture {
|
||||
if (!this.isMoveQueued && this.hasFiredStart) {
|
||||
this.isMoveQueued = true;
|
||||
this.calcGestureData(ev);
|
||||
Context.dom.write(this.fireOnMoveFunc);
|
||||
Context.dom.write(this.fireOnMove.bind(this));
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -221,6 +218,11 @@ export class Gesture {
|
||||
}
|
||||
|
||||
private fireOnMove() {
|
||||
// Since fireOnMove is called inside a RAF, onEnd() might be called,
|
||||
// we must double check hasCapturedPan
|
||||
if (!this.hasCapturedPan) {
|
||||
return;
|
||||
}
|
||||
const detail = this.detail;
|
||||
this.isMoveQueued = false;
|
||||
if (this.onMove) {
|
||||
@ -312,6 +314,7 @@ export class Gesture {
|
||||
private reset() {
|
||||
this.hasCapturedPan = false;
|
||||
this.hasStartedPan = false;
|
||||
this.isMoveQueued = false;
|
||||
this.hasFiredStart = true;
|
||||
this.gesture && this.gesture.release();
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import baseAnimation from './base';
|
||||
*/
|
||||
export default function(Animation: Animation, _: HTMLElement, menu: Menu): Animation {
|
||||
let closedX: string, openedX: string;
|
||||
const width = menu.getWidth();
|
||||
const width = menu.width;
|
||||
if (menu.isRightSide) {
|
||||
// right side
|
||||
closedX = 8 + width + 'px';
|
||||
@ -22,11 +22,11 @@ export default function(Animation: Animation, _: HTMLElement, menu: Menu): Anima
|
||||
}
|
||||
|
||||
const menuAni = new Animation()
|
||||
.addElement(menu.getMenuElement())
|
||||
.addElement(menu.menuInnerEl)
|
||||
.fromTo('translateX', closedX, openedX);
|
||||
|
||||
const backdropApi = new Animation()
|
||||
.addElement(menu.getBackdropElement())
|
||||
.addElement(menu.backdropEl)
|
||||
.fromTo('opacity', 0.01, 0.35);
|
||||
|
||||
return baseAnimation(Animation)
|
||||
|
@ -10,7 +10,7 @@ import baseAnimation from './base';
|
||||
export default function(Animation: Animation, _: HTMLElement, menu: Menu): Animation {
|
||||
|
||||
let contentOpenedX: string, menuClosedX: string, menuOpenedX: string;
|
||||
const width = menu.getWidth();
|
||||
const width = menu.width;
|
||||
|
||||
if (menu.isRightSide) {
|
||||
contentOpenedX = -width + 'px';
|
||||
@ -23,11 +23,11 @@ export default function(Animation: Animation, _: HTMLElement, menu: Menu): Anima
|
||||
menuClosedX = -width + 'px';
|
||||
}
|
||||
const menuAni = new Animation()
|
||||
.addElement(menu.getMenuElement())
|
||||
.addElement(menu.menuInnerEl)
|
||||
.fromTo('translateX', menuClosedX, menuOpenedX);
|
||||
|
||||
const contentAni = new Animation()
|
||||
.addElement(menu.getContentElement())
|
||||
.addElement(menu.contentEl)
|
||||
.fromTo('translateX', '0px', contentOpenedX);
|
||||
|
||||
return baseAnimation(Animation)
|
||||
|
@ -8,10 +8,10 @@ import baseAnimation from './base';
|
||||
* The menu itself, which is under the content, does not move.
|
||||
*/
|
||||
export default function(Animation: Animation, _: HTMLElement, menu: Menu): Animation {
|
||||
const openedX = (menu.getWidth() * (menu.isRightSide ? -1 : 1)) + 'px';
|
||||
const openedX = (menu.width * (menu.isRightSide ? -1 : 1)) + 'px';
|
||||
|
||||
const contentOpen = new Animation()
|
||||
.addElement(menu.getContentElement())
|
||||
.addElement(menu.contentEl)
|
||||
.fromTo('translateX', '0px', openedX);
|
||||
|
||||
return baseAnimation(Animation)
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { Animation, AnimationBuilder, AnimationController, Menu } from '../../index';
|
||||
import { Component, Method, Prop } from '@stencil/core';
|
||||
import { HTMLIonMenuElement } from '../../index';
|
||||
|
||||
import MenuOverlayAnimation from './animations/overlay';
|
||||
import MenuRevealAnimation from './animations/reveal';
|
||||
@ -48,22 +49,13 @@ export class MenuController {
|
||||
*/
|
||||
@Method()
|
||||
close(menuId?: string): Promise<boolean> {
|
||||
let menu: Menu;
|
||||
|
||||
if (menuId) {
|
||||
// find the menu by its id
|
||||
menu = this.get(menuId);
|
||||
|
||||
} else {
|
||||
// find the menu that is open
|
||||
menu = this.getOpen();
|
||||
}
|
||||
const menu = (menuId)
|
||||
? this.get(menuId)
|
||||
: this.getOpen();
|
||||
|
||||
if (menu) {
|
||||
// close the menu
|
||||
return menu.close();
|
||||
}
|
||||
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
@ -96,9 +88,12 @@ export class MenuController {
|
||||
* @return {Menu} Returns the instance of the menu, which is useful for chaining.
|
||||
*/
|
||||
@Method()
|
||||
enable(shouldEnable: boolean, menuId?: string): Menu {
|
||||
enable(shouldEnable: boolean, menuId?: string): HTMLIonMenuElement {
|
||||
const menu = this.get(menuId);
|
||||
return (menu && menu.enable(shouldEnable)) || null;
|
||||
if (menu) {
|
||||
menu.enabled = shouldEnable;
|
||||
}
|
||||
return menu;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -108,9 +103,12 @@ export class MenuController {
|
||||
* @return {Menu} Returns the instance of the menu, which is useful for chaining.
|
||||
*/
|
||||
@Method()
|
||||
swipeEnable(shouldEnable: boolean, menuId?: string): Menu {
|
||||
swipeEnable(shouldEnable: boolean, menuId?: string): HTMLIonMenuElement {
|
||||
const menu = this.get(menuId);
|
||||
return (menu && menu.swipeEnable(shouldEnable)) || null;
|
||||
if (menu) {
|
||||
menu.swipeEnabled = shouldEnable;
|
||||
}
|
||||
return menu;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -123,9 +121,8 @@ export class MenuController {
|
||||
if (menuId) {
|
||||
var menu = this.get(menuId);
|
||||
return menu && menu.isOpen() || false;
|
||||
} else {
|
||||
return !!this.getOpen();
|
||||
}
|
||||
return !!this.getOpen();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -135,7 +132,10 @@ export class MenuController {
|
||||
@Method()
|
||||
isEnabled(menuId?: string): boolean {
|
||||
const menu = this.get(menuId);
|
||||
return menu && menu.enabled || false;
|
||||
if (menu) {
|
||||
return menu.enabled;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -148,7 +148,7 @@ export class MenuController {
|
||||
* @return {Menu} Returns the instance of the menu if found, otherwise `null`.
|
||||
*/
|
||||
@Method()
|
||||
get(menuId?: string): Menu {
|
||||
get(menuId?: string): HTMLIonMenuElement {
|
||||
var menu: Menu;
|
||||
|
||||
if (menuId === 'left' || menuId === 'right') {
|
||||
@ -156,43 +156,43 @@ export class MenuController {
|
||||
// so first try to get the enabled one
|
||||
menu = this.menus.find(m => m.side === menuId && m.enabled);
|
||||
if (menu) {
|
||||
return menu;
|
||||
return menu.el;
|
||||
}
|
||||
|
||||
// didn't find a menu side that is enabled
|
||||
// so try to get the first menu side found
|
||||
return this.menus.find(m => m.side === menuId) || null;
|
||||
return this.find(m => m.side === menuId) || null;
|
||||
|
||||
} else if (menuId) {
|
||||
// the menuId was not left or right
|
||||
// so try to get the menu by its "id"
|
||||
return this.menus.find(m => m.menuId === menuId) || null;
|
||||
return this.find(m => m.menuId === menuId) || null;
|
||||
}
|
||||
|
||||
// return the first enabled menu
|
||||
menu = this.menus.find(m => m.enabled);
|
||||
if (menu) {
|
||||
return menu;
|
||||
return menu.el;
|
||||
}
|
||||
|
||||
// get the first menu in the array, if one exists
|
||||
return (this.menus.length > 0 ? this.menus[0] : null);
|
||||
return (this.menus.length > 0 ? this.menus[0].el : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Menu} Returns the instance of the menu already opened, otherwise `null`.
|
||||
*/
|
||||
@Method()
|
||||
getOpen(): Menu {
|
||||
return this.menus.find(m => m.isOpen());
|
||||
getOpen(): HTMLIonMenuElement {
|
||||
return this.find(m => m.isOpen());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Array<Menu>} Returns an array of all menu instances.
|
||||
*/
|
||||
@Method()
|
||||
getMenus(): Menu[] {
|
||||
return this.menus;
|
||||
getMenus(): HTMLIonMenuElement[] {
|
||||
return this.menus.map(menu => menu.el);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -201,7 +201,7 @@ export class MenuController {
|
||||
*/
|
||||
@Method()
|
||||
isAnimating(): boolean {
|
||||
return this.menus.some(menu => menu.isAnimating());
|
||||
return this.menus.some(menu => menu.isAnimating);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -236,24 +236,28 @@ export class MenuController {
|
||||
const side = menu.side;
|
||||
this.menus
|
||||
.filter(m => m.side === side && m !== menu)
|
||||
.map(m => m.enable(false));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
registerAnimation(name: string, cls: AnimationBuilder) {
|
||||
this.menuAnimations[name] = cls;
|
||||
.map(m => m.enabled = false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
@Method()
|
||||
create(type: string, menuCmp: Menu): Promise<Animation> {
|
||||
createAnimation(type: string, menuCmp: Menu): Promise<Animation> {
|
||||
const animationBuilder = this.menuAnimations[type];
|
||||
return this.animationCtrl.create(animationBuilder, null, menuCmp);
|
||||
}
|
||||
|
||||
private registerAnimation(name: string, cls: AnimationBuilder) {
|
||||
this.menuAnimations[name] = cls;
|
||||
}
|
||||
|
||||
private find(predicate: (menu: Menu) => boolean): HTMLIonMenuElement {
|
||||
const instance = this.menus.find(predicate);
|
||||
if (instance) {
|
||||
return instance.el;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -15,18 +15,21 @@ $menu-ios-box-shadow-color: rgba(0, 0, 0, .25) !default;
|
||||
$menu-ios-box-shadow: 0 0 10px $menu-ios-box-shadow-color !default;
|
||||
|
||||
|
||||
.menu-ios {
|
||||
.menu-ios .menu-inner {
|
||||
background: $menu-ios-background;
|
||||
}
|
||||
|
||||
.menu-ios .menu-content-reveal {
|
||||
.menu-ios.menu-type-overlay .menu-inner {
|
||||
box-shadow: $menu-ios-box-shadow;
|
||||
}
|
||||
|
||||
.menu-ios .menu-content-push {
|
||||
// iOS Menu Content
|
||||
// --------------------------------------------------
|
||||
|
||||
.app-ios .menu-content-reveal {
|
||||
box-shadow: $menu-ios-box-shadow;
|
||||
}
|
||||
|
||||
ion-menu[type=overlay] .menu-ios {
|
||||
.app-ios .menu-content-push {
|
||||
box-shadow: $menu-ios-box-shadow;
|
||||
}
|
||||
|
@ -19,14 +19,17 @@ $menu-md-box-shadow: 0 0 10px $menu-md-box-shadow-color !default;
|
||||
background: $menu-md-background;
|
||||
}
|
||||
|
||||
.menu-md .menu-content-reveal {
|
||||
.menu-md.menu-type-overlay .menu-inner {
|
||||
box-shadow: $menu-md-box-shadow;
|
||||
}
|
||||
|
||||
.menu-md .menu-content-push {
|
||||
// MD Menu Content
|
||||
// --------------------------------------------------
|
||||
|
||||
.app-md .menu-content-reveal {
|
||||
box-shadow: $menu-md-box-shadow;
|
||||
}
|
||||
|
||||
ion-menu[type=overlay] .menu-md {
|
||||
.app-md .menu-content-push {
|
||||
box-shadow: $menu-md-box-shadow;
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ ion-menu.show-menu {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
ion-menu[side=left] > .menu-inner {
|
||||
.menu-side-left > .menu-inner {
|
||||
@include multi-dir() {
|
||||
// scss-lint:disable PropertySpelling
|
||||
right: auto;
|
||||
@ -54,7 +54,7 @@ ion-menu[side=left] > .menu-inner {
|
||||
}
|
||||
}
|
||||
|
||||
ion-menu[side=right] > .menu-inner {
|
||||
.menu-side-right > .menu-inner {
|
||||
@include multi-dir() {
|
||||
// scss-lint:disable PropertySpelling
|
||||
right: 0;
|
||||
@ -62,10 +62,6 @@ ion-menu[side=right] > .menu-inner {
|
||||
}
|
||||
}
|
||||
|
||||
ion-menu[side=end] > .menu-inner {
|
||||
@include position-horizontal(auto, 0);
|
||||
}
|
||||
|
||||
ion-menu ion-backdrop {
|
||||
z-index: -1;
|
||||
display: none;
|
||||
@ -84,6 +80,7 @@ ion-menu ion-backdrop {
|
||||
}
|
||||
|
||||
.menu-content-open ion-pane,
|
||||
.menu-content-open .ion-pane,
|
||||
.menu-content-open ion-content,
|
||||
.menu-content-open .toolbar {
|
||||
// the containing element itself should be clickable but
|
||||
@ -106,11 +103,11 @@ ion-menu ion-backdrop {
|
||||
// The content slides over to reveal the menu underneath.
|
||||
// The menu itself, which is under the content, does not move.
|
||||
|
||||
ion-menu[type=reveal] {
|
||||
ion-menu.menu-type-reveal {
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
ion-menu[type=reveal].show-menu .menu-inner {
|
||||
ion-menu.menu-type-reveal.show-menu .menu-inner {
|
||||
@include transform(translate3d(0, 0, 0));
|
||||
}
|
||||
|
||||
@ -120,10 +117,11 @@ ion-menu[type=reveal].show-menu .menu-inner {
|
||||
// The menu slides over the content. The content
|
||||
// itself, which is under the menu, does not move.
|
||||
|
||||
ion-menu[type=overlay] {
|
||||
ion-menu.menu-type-overlay {
|
||||
z-index: $z-index-menu-overlay;
|
||||
}
|
||||
|
||||
ion-menu[type=overlay] .show-backdrop {
|
||||
ion-menu.menu-type-overlay .show-backdrop {
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Component, Element, Event, EventEmitter, Listen, Prop, PropDidChange } from '@stencil/core';
|
||||
import { Animation, Config, SplitPaneAlert } from '../../index';
|
||||
import { Component, Element, Event, EventEmitter, Listen, Method, Prop, PropDidChange, PropWillChange } from '@stencil/core';
|
||||
import { Animation, Config, GestureDetail, HTMLIonMenuElement, SplitPaneAlert } from '../../index';
|
||||
import { MenuController } from './menu-controller';
|
||||
import { Side, assert, checkEdgeSide, isRightSide } from '../../utils/helpers';
|
||||
|
||||
@ -20,38 +20,27 @@ export type Lazy<T> = T &
|
||||
})
|
||||
export class Menu {
|
||||
|
||||
private _backdropEle: HTMLElement;
|
||||
private _menuInnerEle: HTMLElement;
|
||||
private _unregCntClick: Function;
|
||||
private _unregBdClick: Function;
|
||||
private _activeBlock: string;
|
||||
|
||||
private _cntElm: HTMLElement;
|
||||
private _animation: Animation;
|
||||
private _init = false;
|
||||
private _isPane = false;
|
||||
private _isAnimating: boolean = false;
|
||||
private gestureBlocker: string;
|
||||
private animation: Animation;
|
||||
private isPane = false;
|
||||
private _isOpen: boolean = false;
|
||||
private _width: number = null;
|
||||
private lastOnEnd = 0;
|
||||
|
||||
mode: string;
|
||||
color: string;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
isAnimating: boolean = false;
|
||||
isRightSide: boolean = false;
|
||||
width: number = null;
|
||||
|
||||
@Element() private el: HTMLElement;
|
||||
backdropEl: HTMLElement;
|
||||
menuInnerEl: HTMLElement;
|
||||
contentEl: HTMLElement;
|
||||
menuCtrl: MenuController;
|
||||
|
||||
@Event() ionDrag: EventEmitter;
|
||||
@Event() ionOpen: EventEmitter;
|
||||
@Event() ionClose: EventEmitter;
|
||||
@Element() el: HTMLIonMenuElement;
|
||||
|
||||
@Prop({ context: 'config' }) config: Config;
|
||||
|
||||
@Prop({ connect: 'ion-menu-controller' }) lazyMenuCtrl: Lazy<MenuController>;
|
||||
menuCtrl: MenuController;
|
||||
|
||||
/**
|
||||
* @input {string} The content's id the menu should use.
|
||||
@ -68,22 +57,48 @@ export class Menu {
|
||||
* see the `menuType` in the [config](../../config/Config). Available options:
|
||||
* `"overlay"`, `"reveal"`, `"push"`.
|
||||
*/
|
||||
@Prop() type: string = 'overlay';
|
||||
@Prop({ mutable: true }) type: string = 'overlay';
|
||||
@PropWillChange('type')
|
||||
typeChanged(type: string) {
|
||||
if (this.contentEl) {
|
||||
this.contentEl.classList.remove('menu-content-' + this.type);
|
||||
this.contentEl.classList.add('menu-content-' + type);
|
||||
this.contentEl.removeAttribute('style');
|
||||
}
|
||||
if (this.menuInnerEl) {
|
||||
// Remove effects of previous animations
|
||||
this.menuInnerEl.removeAttribute('style');
|
||||
}
|
||||
this.animation = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @input {boolean} If true, the menu is enabled. Default `true`.
|
||||
*/
|
||||
@Prop({ mutable: true }) enabled: boolean;
|
||||
@PropDidChange('enabled')
|
||||
enabledChanged() {
|
||||
this.updateState();
|
||||
}
|
||||
|
||||
/**
|
||||
* @input {string} Which side of the view the menu should be placed. Default `"start"`.
|
||||
*/
|
||||
@Prop() side: Side = 'start';
|
||||
@PropDidChange('side')
|
||||
sideChanged() {
|
||||
const isRTL = false;
|
||||
this.isRightSide = isRightSide(this.side, isRTL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @input {boolean} If true, swiping the menu is enabled. Default `true`.
|
||||
*/
|
||||
@Prop() swipeEnabled: boolean = true;
|
||||
@PropDidChange('swipeEnabled')
|
||||
swipeEnabledChange() {
|
||||
this.updateState();
|
||||
}
|
||||
|
||||
/**
|
||||
* @input {boolean} If true, the menu will persist on child pages.
|
||||
@ -96,57 +111,36 @@ export class Menu {
|
||||
@Prop() maxEdgeStart: number = 50;
|
||||
|
||||
|
||||
// @PropDidChange('side')
|
||||
// sideChanged(side: Side) {
|
||||
// // TODO: const isRTL = this._plt.isRTL;
|
||||
// const isRTL = false;
|
||||
// // this.isRightSide = isRightSide(side, isRTL);
|
||||
// }
|
||||
@Listen('body:ionSplitPaneDidChange')
|
||||
splitPaneChanged(ev: SplitPaneAlert) {
|
||||
this._isPane = ev.detail.splitPane.isPane(this.el);
|
||||
this._updateState();
|
||||
}
|
||||
|
||||
@PropDidChange('enabled')
|
||||
enabledChanged() {
|
||||
this._updateState();
|
||||
}
|
||||
|
||||
@PropDidChange('swipeEnabled')
|
||||
swipeEnabledChange() {
|
||||
this._updateState();
|
||||
}
|
||||
@Event() ionDrag: EventEmitter;
|
||||
@Event() ionOpen: EventEmitter;
|
||||
@Event() ionClose: EventEmitter;
|
||||
|
||||
protected ionViewWillLoad() {
|
||||
return this.lazyMenuCtrl.componentOnReady()
|
||||
.then(menu => this.menuCtrl = menu);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
protected ionViewDidLoad() {
|
||||
assert(!!this.menuCtrl, 'menucontroller was not initialized');
|
||||
|
||||
this._menuInnerEle = this.el.querySelector('.menu-inner') as HTMLElement;
|
||||
this._backdropEle = this.el.querySelector('.menu-backdrop') as HTMLElement;
|
||||
|
||||
const el = this.el;
|
||||
const contentQuery = (this.content)
|
||||
? '> #' + this.content
|
||||
? '#' + this.content
|
||||
: '[main]';
|
||||
const parent = this.el.parentElement;
|
||||
const content = this._cntElm = parent.querySelector(contentQuery) as HTMLElement;
|
||||
const parent = el.parentElement;
|
||||
const content = this.contentEl = parent.querySelector(contentQuery) as HTMLElement;
|
||||
if (!content || !content.tagName) {
|
||||
// requires content element
|
||||
return console.error('Menu: must have a "content" element to listen for drag events on.');
|
||||
}
|
||||
// TODO: make PropDidChange work
|
||||
this.isRightSide = isRightSide(this.side, false);
|
||||
this.menuInnerEl = el.querySelector('.menu-inner') as HTMLElement;
|
||||
this.backdropEl = el.querySelector('.menu-backdrop') as HTMLElement;
|
||||
|
||||
// add menu's content classes
|
||||
content.classList.add('menu-content');
|
||||
content.classList.add('menu-content-' + this.type);
|
||||
|
||||
this.typeChanged(this.type);
|
||||
this.sideChanged();
|
||||
|
||||
let isEnabled = this.enabled;
|
||||
if (isEnabled === true || typeof isEnabled === 'undefined') {
|
||||
@ -159,100 +153,88 @@ export class Menu {
|
||||
this.menuCtrl._register(this);
|
||||
|
||||
// mask it as enabled / disabled
|
||||
this.enable(isEnabled);
|
||||
this._init = true;
|
||||
this.enabled = isEnabled;
|
||||
}
|
||||
|
||||
hostData() {
|
||||
return {
|
||||
'role': 'navigation',
|
||||
'side': this.getSide(),
|
||||
'type': this.type,
|
||||
class: {
|
||||
'menu-enabled': this._canOpen()
|
||||
}
|
||||
};
|
||||
protected ionViewDidUnload() {
|
||||
this.menuCtrl._unregister(this);
|
||||
this.animation && this.animation.destroy();
|
||||
|
||||
this.menuCtrl = this.animation = null;
|
||||
this.contentEl = this.backdropEl = this.menuInnerEl = null;
|
||||
}
|
||||
|
||||
getSide(): string {
|
||||
return this.isRightSide ? 'right' : 'left';
|
||||
@Listen('body:ionSplitPaneDidChange')
|
||||
splitPaneChanged(ev: SplitPaneAlert) {
|
||||
this.isPane = ev.detail.splitPane.isPane(this.el);
|
||||
this.updateState();
|
||||
}
|
||||
|
||||
protected render() {
|
||||
return ([
|
||||
<div class='menu-inner'>
|
||||
<slot></slot>
|
||||
</div>,
|
||||
<ion-backdrop class='menu-backdrop'></ion-backdrop> ,
|
||||
<ion-gesture {...{
|
||||
'canStart': this.canStart.bind(this),
|
||||
'onWillStart': this._swipeWillStart.bind(this),
|
||||
'onStart': this._swipeStart.bind(this),
|
||||
'onMove': this._swipeProgress.bind(this),
|
||||
'onEnd': this._swipeEnd.bind(this),
|
||||
'maxEdgeStart': this.maxEdgeStart,
|
||||
'edge': this.side,
|
||||
'enabled': this._canOpen() && this.swipeEnabled,
|
||||
'gestureName': 'menu-swipe',
|
||||
'gesturePriority': 10,
|
||||
'type': 'pan',
|
||||
'direction': 'x',
|
||||
'threshold': 10,
|
||||
'attachTo': 'body',
|
||||
'disableScroll': true,
|
||||
'block': this._activeBlock
|
||||
}}></ion-gesture>
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
@Listen('body:click', { enabled: false, capture: true })
|
||||
onBackdropClick(ev: UIEvent) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
this.close();
|
||||
const el = ev.target as HTMLElement;
|
||||
if (!el.closest('.menu-inner') && this.lastOnEnd < (ev.timeStamp - 100)) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
private prepareAnimation(): Promise<void> {
|
||||
const width = this._menuInnerEle.offsetWidth;
|
||||
if (width === this._width) {
|
||||
@Method()
|
||||
isOpen(): boolean {
|
||||
return this._isOpen;
|
||||
}
|
||||
|
||||
@Method()
|
||||
setOpen(shouldOpen: boolean, animated: boolean = true): Promise<boolean> {
|
||||
// If the menu is disabled or it is currenly being animated, let's do nothing
|
||||
if (!this.isActive() || this.isAnimating || (shouldOpen === this._isOpen)) {
|
||||
return Promise.resolve(this._isOpen);
|
||||
}
|
||||
|
||||
this.beforeAnimation();
|
||||
return this.loadAnimation()
|
||||
.then(() => this.startAnimation(shouldOpen, animated))
|
||||
.then(() => this.afterAnimation(shouldOpen));
|
||||
}
|
||||
|
||||
@Method()
|
||||
open(): Promise<boolean> {
|
||||
return this.setOpen(true);
|
||||
}
|
||||
|
||||
@Method()
|
||||
close(): Promise<boolean> {
|
||||
return this.setOpen(false);
|
||||
}
|
||||
|
||||
@Method()
|
||||
toggle(): Promise<boolean> {
|
||||
return this.setOpen(!this._isOpen);
|
||||
}
|
||||
|
||||
private loadAnimation(): Promise<void> {
|
||||
// Menu swipe animation takes the menu's inner width as parameter,
|
||||
// If `offsetWidth` changes, we need to create a new animation.
|
||||
const width = this.menuInnerEl.offsetWidth;
|
||||
if (width === this.width && this.animation !== null) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (this._animation) {
|
||||
this._animation.destroy();
|
||||
this._animation = null;
|
||||
}
|
||||
this._width = width;
|
||||
return this.menuCtrl.create(this.type, this).then(ani => {
|
||||
this._animation = ani;
|
||||
// Destroy existing animation
|
||||
this.animation && this.animation.destroy();
|
||||
this.animation = null;
|
||||
this.width = width;
|
||||
|
||||
// Create new animation
|
||||
return this.menuCtrl.createAnimation(this.type, this).then(ani => {
|
||||
this.animation = ani;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
setOpen(shouldOpen: boolean, animated: boolean = true): Promise<boolean> {
|
||||
// If the menu is disabled or it is currenly being animated, let's do nothing
|
||||
if ((shouldOpen === this._isOpen) || !this._canOpen() || this._isAnimating) {
|
||||
return Promise.resolve(this._isOpen);
|
||||
}
|
||||
this._before();
|
||||
return this.prepareAnimation()
|
||||
.then(() => this._startAnimation(shouldOpen, animated))
|
||||
.then(() => {
|
||||
this._after(shouldOpen);
|
||||
return this._isOpen;
|
||||
});
|
||||
}
|
||||
|
||||
_startAnimation(shouldOpen: boolean, animated: boolean): Promise<Animation> {
|
||||
private startAnimation(shouldOpen: boolean, animated: boolean): Promise<Animation> {
|
||||
let done;
|
||||
const promise = new Promise<Animation>(resolve => done = resolve);
|
||||
const ani = this._animation
|
||||
const ani = this.animation
|
||||
.onFinish(done, {oneTimeCallback: true, clearExistingCallacks: true })
|
||||
.reverse(!shouldOpen);
|
||||
|
||||
@ -265,98 +247,86 @@ export class Menu {
|
||||
return promise;
|
||||
}
|
||||
|
||||
_forceClosing() {
|
||||
assert(this._isOpen, 'menu cannot be closed');
|
||||
|
||||
this._isAnimating = true;
|
||||
this._startAnimation(false, false);
|
||||
this._after(false);
|
||||
private isActive(): boolean {
|
||||
return this.enabled && !this.isPane;
|
||||
}
|
||||
|
||||
getWidth(): number {
|
||||
return this._width;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
canSwipe(): boolean {
|
||||
private canSwipe(): boolean {
|
||||
return this.swipeEnabled &&
|
||||
!this._isAnimating &&
|
||||
this._canOpen();
|
||||
// TODO: && this._app.isEnabled();
|
||||
!this.isAnimating &&
|
||||
this.isActive();
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
isAnimating(): boolean {
|
||||
return this._isAnimating;
|
||||
private canStart(detail: GestureDetail): boolean {
|
||||
if (!this.canSwipe()) {
|
||||
return false;
|
||||
}
|
||||
if (this._isOpen) {
|
||||
return true;
|
||||
} else if (this.menuCtrl.getOpen()) {
|
||||
return false;
|
||||
}
|
||||
return checkEdgeSide(detail.currentX, this.isRightSide, this.maxEdgeStart);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
isOpen(): boolean {
|
||||
return this._isOpen;
|
||||
private onWillStart(): Promise<void> {
|
||||
this.beforeAnimation();
|
||||
return this.loadAnimation();
|
||||
}
|
||||
|
||||
_swipeWillStart(): Promise<void> {
|
||||
this._before();
|
||||
return this.prepareAnimation();
|
||||
}
|
||||
|
||||
_swipeStart() {
|
||||
assert(!!this._animation, '_type is undefined');
|
||||
if (!this._isAnimating) {
|
||||
assert(false, '_isAnimating has to be true');
|
||||
private onDragStart() {
|
||||
assert(!!this.animation, '_type is undefined');
|
||||
if (!this.isAnimating) {
|
||||
assert(false, 'isAnimating has to be true');
|
||||
return;
|
||||
}
|
||||
|
||||
// the cloned animation should not use an easing curve during seek
|
||||
this._animation
|
||||
this.animation
|
||||
.reverse(this._isOpen)
|
||||
.progressStart();
|
||||
}
|
||||
|
||||
_swipeProgress(slide: any) {
|
||||
assert(!!this._animation, '_type is undefined');
|
||||
if (!this._isAnimating) {
|
||||
assert(false, '_isAnimating has to be true');
|
||||
private onDragMove(detail: GestureDetail) {
|
||||
assert(!!this.animation, '_type is undefined');
|
||||
if (!this.isAnimating) {
|
||||
assert(false, 'isAnimating has to be true');
|
||||
return;
|
||||
}
|
||||
|
||||
const delta = computeDelta(slide.deltaX, this._isOpen, this.isRightSide);
|
||||
const stepValue = delta / this._width;
|
||||
this._animation.progressStep(stepValue);
|
||||
const delta = computeDelta(detail.deltaX, this._isOpen, this.isRightSide);
|
||||
const stepValue = delta / this.width;
|
||||
this.animation.progressStep(stepValue);
|
||||
}
|
||||
|
||||
_swipeEnd(slide: any) {
|
||||
assert(!!this._animation, '_type is undefined');
|
||||
if (!this._isAnimating) {
|
||||
assert(false, '_isAnimating has to be true');
|
||||
private onDragEnd(detail: GestureDetail) {
|
||||
assert(!!this.animation, '_type is undefined');
|
||||
if (!this.isAnimating) {
|
||||
assert(false, 'isAnimating has to be true');
|
||||
return;
|
||||
}
|
||||
console.log('end');
|
||||
|
||||
const isOpen = this._isOpen;
|
||||
const isRightSide = this.isRightSide;
|
||||
const delta = computeDelta(slide.deltaX, this._isOpen, isRightSide);
|
||||
const width = this._width;
|
||||
const delta = computeDelta(detail.deltaX, isOpen, isRightSide);
|
||||
const width = this.width;
|
||||
const stepValue = delta / width;
|
||||
const velocity = slide.velocityX;
|
||||
const z = width / 2;
|
||||
const velocity = detail.velocityX;
|
||||
const z = width / 2.0;
|
||||
const shouldCompleteRight = (velocity >= 0)
|
||||
&& (velocity > 0.2 || slide.deltaX > z);
|
||||
&& (velocity > 0.2 || detail.deltaX > z);
|
||||
|
||||
const shouldCompleteLeft = (velocity <= 0)
|
||||
&& (velocity < -0.2 || slide.deltaX < -z);
|
||||
&& (velocity < -0.2 || detail.deltaX < -z);
|
||||
|
||||
const opening = !this._isOpen;
|
||||
const shouldComplete = (opening)
|
||||
? isRightSide ? shouldCompleteLeft : shouldCompleteRight
|
||||
: isRightSide ? shouldCompleteRight : shouldCompleteLeft;
|
||||
const shouldComplete = (isOpen)
|
||||
? isRightSide ? shouldCompleteRight : shouldCompleteLeft
|
||||
: isRightSide ? shouldCompleteLeft : shouldCompleteRight;
|
||||
|
||||
let isOpen = (opening && shouldComplete);
|
||||
if (!opening && !shouldComplete) {
|
||||
isOpen = true;
|
||||
let shouldOpen = (!isOpen && shouldComplete);
|
||||
if (isOpen && !shouldComplete) {
|
||||
shouldOpen = true;
|
||||
}
|
||||
|
||||
const missing = shouldComplete ? 1 - stepValue : stepValue;
|
||||
@ -367,25 +337,24 @@ export class Menu {
|
||||
realDur = Math.min(dur, 380);
|
||||
}
|
||||
|
||||
this._animation
|
||||
.onFinish(() => this._after(isOpen), { clearExistingCallacks: true })
|
||||
this.lastOnEnd = detail.timeStamp;
|
||||
this.animation
|
||||
.onFinish(() => this.afterAnimation(shouldOpen), { clearExistingCallacks: true })
|
||||
.progressEnd(shouldComplete, stepValue, realDur);
|
||||
}
|
||||
|
||||
private _before() {
|
||||
assert(!this._isAnimating, '_before() should not be called while animating');
|
||||
private beforeAnimation() {
|
||||
assert(!this.isAnimating, '_before() should not be called while animating');
|
||||
|
||||
// this places the menu into the correct location before it animates in
|
||||
// this css class doesn't actually kick off any animations
|
||||
this.el.classList.add('show-menu');
|
||||
this._backdropEle.classList.add('show-backdrop');
|
||||
|
||||
this.resize();
|
||||
this._isAnimating = true;
|
||||
this.el.classList.add(SHOW_MENU);
|
||||
this.backdropEl.classList.add(SHOW_BACKDROP);
|
||||
this.isAnimating = true;
|
||||
}
|
||||
|
||||
private _after(isOpen: boolean) {
|
||||
assert(this._isAnimating, '_before() should be called while animating');
|
||||
private afterAnimation(isOpen: boolean): boolean {
|
||||
assert(this.isAnimating, '_before() should be called while animating');
|
||||
|
||||
// TODO: this._app.setEnabled(false, 100);
|
||||
|
||||
@ -394,193 +363,105 @@ export class Menu {
|
||||
// and only remove listeners/css if it's not open
|
||||
// emit opened/closed events
|
||||
this._isOpen = isOpen;
|
||||
this._isAnimating = false;
|
||||
this.isAnimating = false;
|
||||
|
||||
// add/remove backdrop click listeners
|
||||
this._backdropClick(isOpen);
|
||||
Context.enableListener(this, 'body:click', isOpen);
|
||||
|
||||
if (isOpen) {
|
||||
// disable swipe to go back gesture
|
||||
this._activeBlock = GESTURE_BLOCKER;
|
||||
this.gestureBlocker = GESTURE_BLOCKER;
|
||||
|
||||
// add css class
|
||||
this._cntElm.classList.add('menu-content-open');
|
||||
this.contentEl.classList.add(MENU_CONTENT_OPEN);
|
||||
|
||||
// emit open event
|
||||
this.ionOpen.emit({ menu: this });
|
||||
|
||||
} else {
|
||||
// enable swipe to go back gesture
|
||||
this._activeBlock = null;
|
||||
this.gestureBlocker = null;
|
||||
|
||||
// remove css classes
|
||||
this.el.classList.remove('show-menu');
|
||||
this._cntElm.classList.remove('menu-content-open');
|
||||
this._backdropEle.classList.remove('show-menu');
|
||||
this.el.classList.remove(SHOW_MENU);
|
||||
this.contentEl.classList.remove(MENU_CONTENT_OPEN);
|
||||
this.backdropEl.classList.remove(SHOW_BACKDROP);
|
||||
|
||||
// emit close event
|
||||
this.ionClose.emit({ menu: this });
|
||||
}
|
||||
return isOpen;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
open(): Promise<boolean> {
|
||||
return this.setOpen(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
close(): Promise<boolean> {
|
||||
return this.setOpen(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
resize() {
|
||||
// TODO
|
||||
// const content: Content | Nav = this.menuContent
|
||||
// ? this.menuContent
|
||||
// : this.menuNav;
|
||||
// content && content.resize();
|
||||
}
|
||||
|
||||
canStart(detail: any): boolean {
|
||||
if (!this.canSwipe()) {
|
||||
return false;
|
||||
}
|
||||
if (this._isOpen) {
|
||||
return true;
|
||||
} else if (this.getMenuController().getOpen()) {
|
||||
return false;
|
||||
}
|
||||
return checkEdgeSide(detail.currentX, this.isRightSide, this.maxEdgeStart);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
toggle(): Promise<boolean> {
|
||||
return this.setOpen(!this._isOpen);
|
||||
}
|
||||
|
||||
_canOpen(): boolean {
|
||||
return this.enabled && !this._isPane;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
// @PropDidChange('swipeEnabled')
|
||||
// @PropDidChange('enabled')
|
||||
_updateState() {
|
||||
const canOpen = this._canOpen();
|
||||
private updateState() {
|
||||
const isActive = this.isActive();
|
||||
|
||||
// Close menu inmediately
|
||||
if (!canOpen && this._isOpen) {
|
||||
assert(this._init, 'menu must be initialized');
|
||||
if (!isActive && this._isOpen) {
|
||||
// close if this menu is open, and should not be enabled
|
||||
this._forceClosing();
|
||||
this.forceClosing();
|
||||
}
|
||||
|
||||
if (this.enabled && this.menuCtrl) {
|
||||
this.menuCtrl._setActiveMenu(this);
|
||||
}
|
||||
|
||||
if (!this._init) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._isOpen || (this._isPane && this.enabled)) {
|
||||
this.resize();
|
||||
}
|
||||
assert(!this._isAnimating, 'can not be animating');
|
||||
assert(!this.isAnimating, 'can not be animating');
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
enable(shouldEnable: boolean): Menu {
|
||||
this.enabled = shouldEnable;
|
||||
return this;
|
||||
private forceClosing() {
|
||||
assert(this._isOpen, 'menu cannot be closed');
|
||||
|
||||
this.isAnimating = true;
|
||||
this.startAnimation(false, false);
|
||||
this.afterAnimation(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
initPane(): boolean {
|
||||
return false;
|
||||
protected hostData() {
|
||||
const typeClass = 'menu-type-' + this.type;
|
||||
return {
|
||||
role: 'navigation',
|
||||
class: {
|
||||
'menu-enabled': this.isActive(),
|
||||
'menu-side-right': this.isRightSide,
|
||||
'menu-side-left': !this.isRightSide,
|
||||
[typeClass]: true,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
swipeEnable(shouldEnable: boolean): Menu {
|
||||
this.swipeEnabled = shouldEnable;
|
||||
return this;
|
||||
protected render() {
|
||||
return ([
|
||||
<div class='menu-inner page-inner'>
|
||||
<slot></slot>
|
||||
</div>,
|
||||
<ion-backdrop class='menu-backdrop'></ion-backdrop> ,
|
||||
<ion-gesture {...{
|
||||
'canStart': this.canStart.bind(this),
|
||||
'onWillStart': this.onWillStart.bind(this),
|
||||
'onStart': this.onDragStart.bind(this),
|
||||
'onMove': this.onDragMove.bind(this),
|
||||
'onEnd': this.onDragEnd.bind(this),
|
||||
'maxEdgeStart': this.maxEdgeStart,
|
||||
'edge': this.side,
|
||||
'enabled': this.isActive() && this.swipeEnabled,
|
||||
'gestureName': 'menu-swipe',
|
||||
'gesturePriority': 10,
|
||||
'type': 'pan',
|
||||
'direction': 'x',
|
||||
'threshold': 10,
|
||||
'attachTo': 'body',
|
||||
'disableScroll': true,
|
||||
'block': this.gestureBlocker
|
||||
}}></ion-gesture>
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
getMenuElement(): HTMLElement {
|
||||
return this.el.querySelector('.menu-inner') as HTMLElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
getContentElement(): HTMLElement {
|
||||
return this._cntElm;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
getBackdropElement(): HTMLElement {
|
||||
return this._backdropEle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
getMenuController(): MenuController {
|
||||
return this.menuCtrl;
|
||||
}
|
||||
|
||||
private _backdropClick(shouldAdd: boolean) {
|
||||
const onBackdropClick = this.onBackdropClick.bind(this);
|
||||
|
||||
if (shouldAdd && !this._unregBdClick) {
|
||||
this._unregBdClick = Context.addListener(this._backdropEle, 'click', onBackdropClick, { capture: true });
|
||||
this._unregCntClick = Context.addListener(this._backdropEle, 'click', onBackdropClick, { capture: true });
|
||||
|
||||
} else if (!shouldAdd && this._unregBdClick) {
|
||||
this._unregBdClick();
|
||||
this._unregCntClick();
|
||||
this._unregBdClick = this._unregCntClick = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
protected ionViewDidUnload() {
|
||||
this._backdropClick(false);
|
||||
|
||||
this.menuCtrl._unregister(this);
|
||||
this._animation && this._animation.destroy();
|
||||
|
||||
this.menuCtrl = this._animation = this._cntElm = this._backdropEle = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function computeDelta(deltaX: number, isOpen: boolean, isRightSide: boolean): number {
|
||||
return Math.max(0, (isOpen !== isRightSide) ? -deltaX : deltaX);
|
||||
}
|
||||
|
||||
const SHOW_MENU = 'show-menu';
|
||||
const SHOW_BACKDROP = 'show-backdrop';
|
||||
const MENU_CONTENT_OPEN = 'menu-content-open';
|
||||
const GESTURE_BLOCKER = 'goback-swipe';
|
||||
|
@ -64,8 +64,15 @@
|
||||
</ion-header>
|
||||
|
||||
<ion-content padding>
|
||||
<ion-button onclick="openLeft()">Open left menu</ion-button>
|
||||
<ion-button onclick="openRight()">Open right menu</ion-button>
|
||||
<p>
|
||||
<ion-button onclick="openLeft()">Open left menu</ion-button>
|
||||
<ion-button onclick="openRight()">Open right menu</ion-button>
|
||||
</p>
|
||||
<p>
|
||||
<ion-button onclick="setPush()">Set Push</ion-button>
|
||||
<ion-button onclick="setOverlay()">Set Overlay</ion-button>
|
||||
<ion-button onclick="setReveal()">Set Reveal</ion-button>
|
||||
</p>
|
||||
</ion-content>
|
||||
|
||||
</ion-page>
|
||||
@ -83,6 +90,18 @@
|
||||
console.log('Open right menu');
|
||||
menu.open('right');
|
||||
}
|
||||
function setPush() {
|
||||
menu.get('left').type = 'push';
|
||||
menu.get('right').type = 'push';
|
||||
}
|
||||
function setOverlay() {
|
||||
menu.get('left').type = 'overlay';
|
||||
menu.get('right').type = 'overlay';
|
||||
}
|
||||
function setReveal() {
|
||||
menu.get('left').type = 'reveal';
|
||||
menu.get('right').type = 'reveal';
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
Reference in New Issue
Block a user