mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-20 12:29:55 +08:00
Merge branch 'master' into v4
This commit is contained in:
@ -248,7 +248,9 @@ export class MenuController {
|
|||||||
// there could be more than one menu on the same side
|
// there could be more than one menu on the same side
|
||||||
// so first try to get the enabled one
|
// so first try to get the enabled one
|
||||||
menu = this._menus.find(m => m.side === menuId && m.enabled);
|
menu = this._menus.find(m => m.side === menuId && m.enabled);
|
||||||
if (menu) return menu;
|
if (menu) {
|
||||||
|
return menu;
|
||||||
|
}
|
||||||
|
|
||||||
// didn't find a menu side that is enabled
|
// didn't find a menu side that is enabled
|
||||||
// so try to get the first menu side found
|
// so try to get the first menu side found
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Animation } from '../../animations/animation';
|
import { Animation } from '../../animations/animation';
|
||||||
|
import { Side } from '../../util/util';
|
||||||
|
|
||||||
export interface Menu {
|
export interface Menu {
|
||||||
setOpen(shouldOpen: boolean, animated: boolean): Promise<boolean>;
|
setOpen(shouldOpen: boolean, animated: boolean): Promise<boolean>;
|
||||||
@ -9,8 +10,9 @@ export interface Menu {
|
|||||||
swipeEnable(shouldEnable: boolean): Menu;
|
swipeEnable(shouldEnable: boolean): Menu;
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
side: string;
|
side: Side;
|
||||||
id: string;
|
id: string;
|
||||||
|
isRightSide: boolean;
|
||||||
isAnimating(): boolean;
|
isAnimating(): boolean;
|
||||||
width(): number;
|
width(): number;
|
||||||
getContentElement(): HTMLElement;
|
getContentElement(): HTMLElement;
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { Directive, ElementRef, EventEmitter, Input, Output, Renderer } from '@angular/core';
|
import { Directive, ElementRef, EventEmitter, Input, Output, Renderer } from '@angular/core';
|
||||||
|
|
||||||
import { Platform } from '../../platform/platform';
|
import { Platform } from '../../platform/platform';
|
||||||
import { ITEM_SIDE_FLAG_LEFT, ITEM_SIDE_FLAG_RIGHT, ItemSliding } from './item-sliding';
|
import { Side, isRightSide } from '../../util/util';
|
||||||
|
import { ItemSliding } from './item-sliding';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name ItemOptions
|
* @name ItemOptions
|
||||||
@ -33,7 +34,7 @@ export class ItemOptions {
|
|||||||
* @input {string} The side the option button should be on. Defaults to `"right"`.
|
* @input {string} The side the option button should be on. Defaults to `"right"`.
|
||||||
* If you have multiple `ion-item-options`, a side must be provided for each.
|
* If you have multiple `ion-item-options`, a side must be provided for each.
|
||||||
*/
|
*/
|
||||||
@Input() side: string;
|
@Input() side: Side;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @output {event} Emitted when the item has been fully swiped.
|
* @output {event} Emitted when the item has been fully swiped.
|
||||||
@ -49,17 +50,8 @@ export class ItemOptions {
|
|||||||
/**
|
/**
|
||||||
* @hidden
|
* @hidden
|
||||||
*/
|
*/
|
||||||
getSides(): number {
|
isRightSide(): boolean {
|
||||||
switch (this.side) {
|
return isRightSide(this.side, this._plt.isRTL);
|
||||||
case 'left':
|
|
||||||
return ITEM_SIDE_FLAG_LEFT;
|
|
||||||
case 'right':
|
|
||||||
return ITEM_SIDE_FLAG_RIGHT;
|
|
||||||
case 'start':
|
|
||||||
return this._plt.isRTL() ? ITEM_SIDE_FLAG_RIGHT : ITEM_SIDE_FLAG_LEFT;
|
|
||||||
default: // end
|
|
||||||
return this._plt.isRTL() ? ITEM_SIDE_FLAG_LEFT : ITEM_SIDE_FLAG_RIGHT;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -10,10 +10,10 @@ import { ItemOptions } from './item-options';
|
|||||||
const SWIPE_MARGIN = 30;
|
const SWIPE_MARGIN = 30;
|
||||||
const ELASTIC_FACTOR = 0.55;
|
const ELASTIC_FACTOR = 0.55;
|
||||||
|
|
||||||
export const ITEM_SIDE_FLAG_NONE = 0;
|
const ITEM_SIDE_FLAG_NONE = 0;
|
||||||
export const ITEM_SIDE_FLAG_LEFT = 1 << 0;
|
const ITEM_SIDE_FLAG_LEFT = 1 << 0;
|
||||||
export const ITEM_SIDE_FLAG_RIGHT = 1 << 1;
|
const ITEM_SIDE_FLAG_RIGHT = 1 << 1;
|
||||||
export const ITEM_SIDE_FLAG_BOTH = ITEM_SIDE_FLAG_LEFT | ITEM_SIDE_FLAG_RIGHT;
|
const ITEM_SIDE_FLAG_BOTH = ITEM_SIDE_FLAG_LEFT | ITEM_SIDE_FLAG_RIGHT;
|
||||||
|
|
||||||
|
|
||||||
const enum SlidingState {
|
const enum SlidingState {
|
||||||
@ -121,6 +121,7 @@ const enum SlidingState {
|
|||||||
encapsulation: ViewEncapsulation.None
|
encapsulation: ViewEncapsulation.None
|
||||||
})
|
})
|
||||||
export class ItemSliding {
|
export class ItemSliding {
|
||||||
|
|
||||||
private _openAmount: number = 0;
|
private _openAmount: number = 0;
|
||||||
private _startX: number = 0;
|
private _startX: number = 0;
|
||||||
private _optsWidthRightSide: number = 0;
|
private _optsWidthRightSide: number = 0;
|
||||||
@ -166,7 +167,8 @@ export class ItemSliding {
|
|||||||
private _dom: DomController,
|
private _dom: DomController,
|
||||||
private _renderer: Renderer,
|
private _renderer: Renderer,
|
||||||
private _elementRef: ElementRef,
|
private _elementRef: ElementRef,
|
||||||
private _zone: NgZone) {
|
private _zone: NgZone
|
||||||
|
) {
|
||||||
list && list.containsSlidingItem(true);
|
list && list.containsSlidingItem(true);
|
||||||
_elementRef.nativeElement.$ionComponent = this;
|
_elementRef.nativeElement.$ionComponent = this;
|
||||||
this.setElementClass('item-wrapper', true);
|
this.setElementClass('item-wrapper', true);
|
||||||
@ -180,13 +182,13 @@ export class ItemSliding {
|
|||||||
this._leftOptions = this._rightOptions = null;
|
this._leftOptions = this._rightOptions = null;
|
||||||
|
|
||||||
for (var item of itemOptions.toArray()) {
|
for (var item of itemOptions.toArray()) {
|
||||||
var side = item.getSides();
|
if (item.isRightSide()) {
|
||||||
if (side === ITEM_SIDE_FLAG_LEFT) {
|
|
||||||
this._leftOptions = item;
|
|
||||||
} else {
|
|
||||||
this._rightOptions = item;
|
this._rightOptions = item;
|
||||||
|
sides |= ITEM_SIDE_FLAG_RIGHT;
|
||||||
|
} else {
|
||||||
|
this._leftOptions = item;
|
||||||
|
sides |= ITEM_SIDE_FLAG_LEFT;
|
||||||
}
|
}
|
||||||
sides |= item.getSides();
|
|
||||||
}
|
}
|
||||||
this._optsDirty = true;
|
this._optsDirty = true;
|
||||||
this._sides = sides;
|
this._sides = sides;
|
||||||
@ -203,7 +205,7 @@ export class ItemSliding {
|
|||||||
* @hidden
|
* @hidden
|
||||||
*/
|
*/
|
||||||
getSlidingPercent(): number {
|
getSlidingPercent(): number {
|
||||||
let openAmount = this._openAmount;
|
const openAmount = this._openAmount;
|
||||||
if (openAmount > 0) {
|
if (openAmount > 0) {
|
||||||
return openAmount / this._optsWidthRightSide;
|
return openAmount / this._optsWidthRightSide;
|
||||||
} else if (openAmount < 0) {
|
} else if (openAmount < 0) {
|
||||||
@ -272,9 +274,9 @@ export class ItemSliding {
|
|||||||
|
|
||||||
// Check if the drag didn't clear the buttons mid-point
|
// Check if the drag didn't clear the buttons mid-point
|
||||||
// and we aren't moving fast enough to swipe open
|
// and we aren't moving fast enough to swipe open
|
||||||
let isResetDirection = (this._openAmount > 0) === !(velocity < 0);
|
const isResetDirection = (this._openAmount > 0) === !(velocity < 0);
|
||||||
let isMovingFast = Math.abs(velocity) > 0.3;
|
const isMovingFast = Math.abs(velocity) > 0.3;
|
||||||
let isOnCloseZone = Math.abs(this._openAmount) < Math.abs(restingPoint / 2);
|
const isOnCloseZone = Math.abs(this._openAmount) < Math.abs(restingPoint / 2);
|
||||||
if (swipeShouldReset(isResetDirection, isMovingFast, isOnCloseZone)) {
|
if (swipeShouldReset(isResetDirection, isMovingFast, isOnCloseZone)) {
|
||||||
restingPoint = 0;
|
restingPoint = 0;
|
||||||
}
|
}
|
||||||
@ -330,14 +332,14 @@ export class ItemSliding {
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (openAmount > 0) {
|
if (openAmount > 0) {
|
||||||
let state = (openAmount >= (this._optsWidthRightSide + SWIPE_MARGIN))
|
var state = (openAmount >= (this._optsWidthRightSide + SWIPE_MARGIN))
|
||||||
? SlidingState.Right | SlidingState.SwipeRight
|
? SlidingState.Right | SlidingState.SwipeRight
|
||||||
: SlidingState.Right;
|
: SlidingState.Right;
|
||||||
|
|
||||||
this._setState(state);
|
this._setState(state);
|
||||||
|
|
||||||
} else if (openAmount < 0) {
|
} else if (openAmount < 0) {
|
||||||
let state = (openAmount <= (-this._optsWidthLeftSide - SWIPE_MARGIN))
|
var state = (openAmount <= (-this._optsWidthLeftSide - SWIPE_MARGIN))
|
||||||
? SlidingState.Left | SlidingState.SwipeLeft
|
? SlidingState.Left | SlidingState.SwipeLeft
|
||||||
: SlidingState.Left;
|
: SlidingState.Left;
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ export class MenuContentGesture extends SlideEdgeGesture {
|
|||||||
}
|
}
|
||||||
|
|
||||||
canStart(ev: any): boolean {
|
canStart(ev: any): boolean {
|
||||||
let menu = this.menu;
|
const menu = this.menu;
|
||||||
if (!menu.canSwipe()) {
|
if (!menu.canSwipe()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -57,21 +57,21 @@ export class MenuContentGesture extends SlideEdgeGesture {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onSlide(slide: SlideData, ev: any) {
|
onSlide(slide: SlideData, ev: any) {
|
||||||
let z = (this.menu.side === 'right' ? slide.min : slide.max);
|
const z = (this.menu.isRightSide ? slide.min : slide.max);
|
||||||
let stepValue = (slide.distance / z);
|
const stepValue = (slide.distance / z);
|
||||||
|
|
||||||
this.menu._swipeProgress(stepValue);
|
this.menu._swipeProgress(stepValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
onSlideEnd(slide: SlideData, ev: any) {
|
onSlideEnd(slide: SlideData, ev: any) {
|
||||||
let z = (this.menu.side === 'right' ? slide.min : slide.max);
|
let z = (this.menu.isRightSide ? slide.min : slide.max);
|
||||||
let currentStepValue = (slide.distance / z);
|
const currentStepValue = (slide.distance / z);
|
||||||
let velocity = slide.velocity;
|
const velocity = slide.velocity;
|
||||||
z = Math.abs(z * 0.5);
|
z = Math.abs(z * 0.5);
|
||||||
let shouldCompleteRight = (velocity >= 0)
|
const shouldCompleteRight = (velocity >= 0)
|
||||||
&& (velocity > 0.2 || slide.delta > z);
|
&& (velocity > 0.2 || slide.delta > z);
|
||||||
|
|
||||||
let shouldCompleteLeft = (velocity <= 0)
|
const shouldCompleteLeft = (velocity <= 0)
|
||||||
&& (velocity < -0.2 || slide.delta < -z);
|
&& (velocity < -0.2 || slide.delta < -z);
|
||||||
|
|
||||||
console.debug('menu gesture, onSlideEnd', this.menu.side,
|
console.debug('menu gesture, onSlideEnd', this.menu.side,
|
||||||
@ -88,24 +88,26 @@ export class MenuContentGesture extends SlideEdgeGesture {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getElementStartPos(slide: SlideData, ev: any) {
|
getElementStartPos(slide: SlideData, ev: any) {
|
||||||
if (this.menu.side === 'right') {
|
const menu = this.menu;
|
||||||
return this.menu.isOpen ? slide.min : slide.max;
|
if (menu.isRightSide) {
|
||||||
|
return menu.isOpen ? slide.min : slide.max;
|
||||||
}
|
}
|
||||||
// left menu
|
// left menu
|
||||||
return this.menu.isOpen ? slide.max : slide.min;
|
return menu.isOpen ? slide.max : slide.min;
|
||||||
}
|
}
|
||||||
|
|
||||||
getSlideBoundaries(): {min: number, max: number} {
|
getSlideBoundaries(): { min: number, max: number } {
|
||||||
if (this.menu.side === 'right') {
|
const menu = this.menu;
|
||||||
|
if (menu.isRightSide) {
|
||||||
return {
|
return {
|
||||||
min: -this.menu.width(),
|
min: -menu.width(),
|
||||||
max: 0
|
max: 0
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// left menu
|
// left menu
|
||||||
return {
|
return {
|
||||||
min: 0,
|
min: 0,
|
||||||
max: this.menu.width()
|
max: menu.width()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import { Platform } from '../../platform/platform';
|
|||||||
* and registers itself with Menu.
|
* and registers itself with Menu.
|
||||||
*/
|
*/
|
||||||
export class MenuType implements IMenuType {
|
export class MenuType implements IMenuType {
|
||||||
|
|
||||||
ani: Animation;
|
ani: Animation;
|
||||||
isOpening: boolean;
|
isOpening: boolean;
|
||||||
|
|
||||||
@ -67,7 +68,7 @@ export class MenuType implements IMenuType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
this.ani && this.ani.destroy();
|
this.ani.destroy();
|
||||||
this.ani = null;
|
this.ani = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,8 +85,8 @@ class MenuRevealType extends MenuType {
|
|||||||
constructor(menu: Menu, plt: Platform) {
|
constructor(menu: Menu, plt: Platform) {
|
||||||
super(plt);
|
super(plt);
|
||||||
|
|
||||||
let openedX = (menu.width() * (menu.side === 'right' ? -1 : 1)) + 'px';
|
const openedX = (menu.width() * (menu.isRightSide ? -1 : 1)) + 'px';
|
||||||
let contentOpen = new Animation(plt, menu.getContentElement());
|
const contentOpen = new Animation(plt, menu.getContentElement());
|
||||||
contentOpen.fromTo('translateX', '0px', openedX);
|
contentOpen.fromTo('translateX', '0px', openedX);
|
||||||
this.ani.add(contentOpen);
|
this.ani.add(contentOpen);
|
||||||
}
|
}
|
||||||
@ -104,24 +105,24 @@ class MenuPushType extends MenuType {
|
|||||||
super(plt);
|
super(plt);
|
||||||
|
|
||||||
let contentOpenedX: string, menuClosedX: string, menuOpenedX: string;
|
let contentOpenedX: string, menuClosedX: string, menuOpenedX: string;
|
||||||
|
const width = menu.width();
|
||||||
if (menu.side === 'right') {
|
if (menu.isRightSide) {
|
||||||
// right side
|
// right side
|
||||||
contentOpenedX = -menu.width() + 'px';
|
contentOpenedX = -width + 'px';
|
||||||
menuClosedX = menu.width() + 'px';
|
menuClosedX = width + 'px';
|
||||||
menuOpenedX = '0px';
|
menuOpenedX = '0px';
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
contentOpenedX = menu.width() + 'px';
|
contentOpenedX = width + 'px';
|
||||||
menuOpenedX = '0px';
|
menuOpenedX = '0px';
|
||||||
menuClosedX = -menu.width() + 'px';
|
menuClosedX = -width + 'px';
|
||||||
}
|
}
|
||||||
|
|
||||||
let menuAni = new Animation(plt, menu.getMenuElement());
|
const menuAni = new Animation(plt, menu.getMenuElement());
|
||||||
menuAni.fromTo('translateX', menuClosedX, menuOpenedX);
|
menuAni.fromTo('translateX', menuClosedX, menuOpenedX);
|
||||||
this.ani.add(menuAni);
|
this.ani.add(menuAni);
|
||||||
|
|
||||||
let contentApi = new Animation(plt, menu.getContentElement());
|
const contentApi = new Animation(plt, menu.getContentElement());
|
||||||
contentApi.fromTo('translateX', '0px', contentOpenedX);
|
contentApi.fromTo('translateX', '0px', contentOpenedX);
|
||||||
this.ani.add(contentApi);
|
this.ani.add(contentApi);
|
||||||
}
|
}
|
||||||
@ -140,22 +141,23 @@ class MenuOverlayType extends MenuType {
|
|||||||
super(plt);
|
super(plt);
|
||||||
|
|
||||||
let closedX: string, openedX: string;
|
let closedX: string, openedX: string;
|
||||||
if (menu.side === 'right') {
|
const width = menu.width();
|
||||||
|
if (menu.isRightSide) {
|
||||||
// right side
|
// right side
|
||||||
closedX = 8 + menu.width() + 'px';
|
closedX = 8 + width + 'px';
|
||||||
openedX = '0px';
|
openedX = '0px';
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// left side
|
// left side
|
||||||
closedX = -(8 + menu.width()) + 'px';
|
closedX = -(8 + width) + 'px';
|
||||||
openedX = '0px';
|
openedX = '0px';
|
||||||
}
|
}
|
||||||
|
|
||||||
let menuAni = new Animation(plt, menu.getMenuElement());
|
const menuAni = new Animation(plt, menu.getMenuElement());
|
||||||
menuAni.fromTo('translateX', closedX, openedX);
|
menuAni.fromTo('translateX', closedX, openedX);
|
||||||
this.ani.add(menuAni);
|
this.ani.add(menuAni);
|
||||||
|
|
||||||
let backdropApi = new Animation(plt, menu.getBackdropElement());
|
const backdropApi = new Animation(plt, menu.getBackdropElement());
|
||||||
backdropApi.fromTo('opacity', 0.01, 0.35);
|
backdropApi.fromTo('opacity', 0.01, 0.35);
|
||||||
this.ani.add(backdropApi);
|
this.ani.add(backdropApi);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { ChangeDetectionStrategy, Component, ContentChild, ElementRef, EventEmitter, forwardRef, Input, NgZone, Output, Renderer, ViewChild, ViewEncapsulation } from '@angular/core';
|
import { OnInit, OnDestroy, ChangeDetectionStrategy, Component, ContentChild, ElementRef, EventEmitter, forwardRef, Input, NgZone, Output, Renderer, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||||
|
|
||||||
import { App } from '../app/app';
|
import { App } from '../app/app';
|
||||||
import { Backdrop } from '../backdrop/backdrop';
|
import { Backdrop } from '../backdrop/backdrop';
|
||||||
@ -6,7 +6,7 @@ import { Config } from '../../config/config';
|
|||||||
import { Content } from '../content/content';
|
import { Content } from '../content/content';
|
||||||
import { DomController } from '../../platform/dom-controller';
|
import { DomController } from '../../platform/dom-controller';
|
||||||
import { GestureController, GESTURE_GO_BACK_SWIPE, BlockerDelegate } from '../../gestures/gesture-controller';
|
import { GestureController, GESTURE_GO_BACK_SWIPE, BlockerDelegate } from '../../gestures/gesture-controller';
|
||||||
import { isTrueProperty, assert } from '../../util/util';
|
import { isTrueProperty, Side, isRightSide, assert } from '../../util/util';
|
||||||
import { Keyboard } from '../../platform/keyboard';
|
import { Keyboard } from '../../platform/keyboard';
|
||||||
import { MenuContentGesture } from './menu-gestures';
|
import { MenuContentGesture } from './menu-gestures';
|
||||||
import { Menu as MenuInterface } from '../app/menu-interface';
|
import { Menu as MenuInterface } from '../app/menu-interface';
|
||||||
@ -193,7 +193,7 @@ import { RootNode } from '../split-pane/split-pane';
|
|||||||
encapsulation: ViewEncapsulation.None,
|
encapsulation: ViewEncapsulation.None,
|
||||||
providers: [{provide: RootNode, useExisting: forwardRef(() => Menu) }]
|
providers: [{provide: RootNode, useExisting: forwardRef(() => Menu) }]
|
||||||
})
|
})
|
||||||
export class Menu implements RootNode, MenuInterface {
|
export class Menu implements RootNode, MenuInterface, OnInit, OnDestroy {
|
||||||
|
|
||||||
private _cntEle: HTMLElement;
|
private _cntEle: HTMLElement;
|
||||||
private _gesture: MenuContentGesture;
|
private _gesture: MenuContentGesture;
|
||||||
@ -206,12 +206,18 @@ export class Menu implements RootNode, MenuInterface {
|
|||||||
private _events: UIEventManager;
|
private _events: UIEventManager;
|
||||||
private _gestureBlocker: BlockerDelegate;
|
private _gestureBlocker: BlockerDelegate;
|
||||||
private _isPane: boolean = false;
|
private _isPane: boolean = false;
|
||||||
|
private _side: Side;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @hidden
|
* @hidden
|
||||||
*/
|
*/
|
||||||
isOpen: boolean = false;
|
isOpen: boolean = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hidden
|
||||||
|
*/
|
||||||
|
isRightSide: boolean = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @hidden
|
* @hidden
|
||||||
*/
|
*/
|
||||||
@ -237,11 +243,6 @@ export class Menu implements RootNode, MenuInterface {
|
|||||||
*/
|
*/
|
||||||
@Input() id: string;
|
@Input() id: string;
|
||||||
|
|
||||||
/**
|
|
||||||
* @input {string} Which side of the view the menu should be placed. Default `"left"`.
|
|
||||||
*/
|
|
||||||
@Input() side: string;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @input {string} The display type of the menu. Default varies based on the mode,
|
* @input {string} The display type of the menu. Default varies based on the mode,
|
||||||
* see the `menuType` in the [config](../../config/Config). Available options:
|
* see the `menuType` in the [config](../../config/Config). Available options:
|
||||||
@ -262,6 +263,23 @@ export class Menu implements RootNode, MenuInterface {
|
|||||||
this.enable(isEnabled);
|
this.enable(isEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @input {string} Which side of the view the menu should be placed. Default `"left"`.
|
||||||
|
*/
|
||||||
|
@Input()
|
||||||
|
get side(): Side {
|
||||||
|
return this._side;
|
||||||
|
}
|
||||||
|
|
||||||
|
set side(val: Side) {
|
||||||
|
this.isRightSide = isRightSide(val, this._plt.isRTL);
|
||||||
|
if (this.isRightSide) {
|
||||||
|
this._side = 'right';
|
||||||
|
} else {
|
||||||
|
this._side = 'left';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @input {boolean} If true, swiping the menu is enabled. Default `true`.
|
* @input {boolean} If true, swiping the menu is enabled. Default `true`.
|
||||||
*/
|
*/
|
||||||
@ -323,6 +341,7 @@ export class Menu implements RootNode, MenuInterface {
|
|||||||
this._gestureBlocker = _gestureCtrl.createBlocker({
|
this._gestureBlocker = _gestureCtrl.createBlocker({
|
||||||
disable: [GESTURE_GO_BACK_SWIPE]
|
disable: [GESTURE_GO_BACK_SWIPE]
|
||||||
});
|
});
|
||||||
|
this.side = 'start';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -339,11 +358,7 @@ export class Menu implements RootNode, MenuInterface {
|
|||||||
return console.error('Menu: must have a [content] element to listen for drag events on. Example:\n\n<ion-menu [content]="content"></ion-menu>\n\n<ion-nav #content></ion-nav>');
|
return console.error('Menu: must have a [content] element to listen for drag events on. Example:\n\n<ion-menu [content]="content"></ion-menu>\n\n<ion-nav #content></ion-nav>');
|
||||||
}
|
}
|
||||||
|
|
||||||
// normalize the "side"
|
this.setElementAttribute('side', this._side);
|
||||||
if (this.side !== 'left' && this.side !== 'right') {
|
|
||||||
this.side = 'left';
|
|
||||||
}
|
|
||||||
this.setElementAttribute('side', this.side);
|
|
||||||
|
|
||||||
// normalize the "type"
|
// normalize the "type"
|
||||||
if (!this.type) {
|
if (!this.type) {
|
||||||
@ -475,13 +490,11 @@ export class Menu implements RootNode, MenuInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// user has finished dragging the menu
|
// user has finished dragging the menu
|
||||||
|
const isRightSide = this.isRightSide;
|
||||||
const opening = !this.isOpen;
|
const opening = !this.isOpen;
|
||||||
let shouldComplete = false;
|
const shouldComplete = (opening)
|
||||||
if (opening) {
|
? isRightSide ? shouldCompleteLeft : shouldCompleteRight
|
||||||
shouldComplete = (this.side === 'right') ? shouldCompleteLeft : shouldCompleteRight;
|
: isRightSide ? shouldCompleteRight : shouldCompleteLeft;
|
||||||
} else {
|
|
||||||
shouldComplete = (this.side === 'right') ? shouldCompleteRight : shouldCompleteLeft;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._getType().setProgressEnd(shouldComplete, stepValue, velocity, (isOpen: boolean) => {
|
this._getType().setProgressEnd(shouldComplete, stepValue, velocity, (isOpen: boolean) => {
|
||||||
console.debug('menu, swipeEnd', this.side);
|
console.debug('menu, swipeEnd', this.side);
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { MenuController } from '../../app/menu-controller';
|
import { MenuController } from '../../app/menu-controller';
|
||||||
import { mockMenu } from '../../../util/mock-providers';
|
import { mockMenu, mockPlatform } from '../../../util/mock-providers';
|
||||||
|
|
||||||
|
|
||||||
describe('MenuController', () => {
|
describe('MenuController', () => {
|
||||||
|
|
||||||
@ -116,6 +115,30 @@ describe('MenuController', () => {
|
|||||||
expect(menu).toEqual(someMenu);
|
expect(menu).toEqual(someMenu);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should get the only left menu on start ltr', () => {
|
||||||
|
let someMenu = mockMenu();
|
||||||
|
someMenu.side = 'start';
|
||||||
|
menuCtrl._register(someMenu);
|
||||||
|
|
||||||
|
let menu = menuCtrl.get('left');
|
||||||
|
expect(menu).toEqual(someMenu);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get the only left menu on end rtl', () => {
|
||||||
|
let platform = mockPlatform();
|
||||||
|
platform.setDir('rtl', true);
|
||||||
|
expect(platform.dir()).toEqual('rtl');
|
||||||
|
|
||||||
|
let someMenu = mockMenu(platform);
|
||||||
|
someMenu.side = 'end';
|
||||||
|
menuCtrl._register(someMenu);
|
||||||
|
|
||||||
|
expect(someMenu.side).toEqual('left');
|
||||||
|
|
||||||
|
let menu = menuCtrl.get('left');
|
||||||
|
expect(menu).toEqual(someMenu);
|
||||||
|
});
|
||||||
|
|
||||||
it('should get the enabled left menu', () => {
|
it('should get the enabled left menu', () => {
|
||||||
let someMenu1 = mockMenu();
|
let someMenu1 = mockMenu();
|
||||||
someMenu1.side = 'left';
|
someMenu1.side = 'left';
|
||||||
@ -155,6 +178,30 @@ describe('MenuController', () => {
|
|||||||
expect(menu).toEqual(someMenu);
|
expect(menu).toEqual(someMenu);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should get the only right menu on end ltr', () => {
|
||||||
|
let someMenu = mockMenu();
|
||||||
|
someMenu.side = 'end';
|
||||||
|
menuCtrl._register(someMenu);
|
||||||
|
|
||||||
|
let menu = menuCtrl.get('right');
|
||||||
|
expect(menu).toEqual(someMenu);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get the only right menu on start rtl', () => {
|
||||||
|
let platform = mockPlatform();
|
||||||
|
platform.setDir('rtl', true);
|
||||||
|
expect(platform.dir()).toEqual('rtl');
|
||||||
|
|
||||||
|
let someMenu = mockMenu(platform);
|
||||||
|
someMenu.side = 'start';
|
||||||
|
menuCtrl._register(someMenu);
|
||||||
|
|
||||||
|
expect(someMenu.side).toEqual('right');
|
||||||
|
|
||||||
|
let menu = menuCtrl.get('right');
|
||||||
|
expect(menu).toEqual(someMenu);
|
||||||
|
});
|
||||||
|
|
||||||
it('should get the menu by left with id', () => {
|
it('should get the menu by left with id', () => {
|
||||||
let someMenu = mockMenu();
|
let someMenu = mockMenu();
|
||||||
someMenu.id = 'myMenu';
|
someMenu.id = 'myMenu';
|
||||||
@ -165,6 +212,19 @@ describe('MenuController', () => {
|
|||||||
expect(menu).toEqual(someMenu);
|
expect(menu).toEqual(someMenu);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should switch menu side in runtime', () => {
|
||||||
|
let someMenu = mockMenu();
|
||||||
|
menuCtrl._register(someMenu);
|
||||||
|
|
||||||
|
['left', 'right'].forEach((side: any) => {
|
||||||
|
someMenu.side = side;
|
||||||
|
expect(someMenu.side).toEqual(side);
|
||||||
|
|
||||||
|
let menu = menuCtrl.get(side);
|
||||||
|
expect(menu).toEqual(someMenu);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('enable()', () => {
|
describe('enable()', () => {
|
||||||
|
@ -16,7 +16,7 @@ export class PageOne {
|
|||||||
console.log('platforms', plt.platforms());
|
console.log('platforms', plt.platforms());
|
||||||
console.log('mode', config.get('mode'));
|
console.log('mode', config.get('mode'));
|
||||||
|
|
||||||
console.log('isRTL', plt.isRTL());
|
console.log('isRTL', plt.isRTL);
|
||||||
console.log('core', plt.is('core'));
|
console.log('core', plt.is('core'));
|
||||||
console.log('cordova', plt.is('cordova'));
|
console.log('cordova', plt.is('cordova'));
|
||||||
console.log('mobile', plt.is('mobile'));
|
console.log('mobile', plt.is('mobile'));
|
||||||
|
@ -22,7 +22,7 @@ $split-pane-side-max-width: 28% !default;
|
|||||||
contain: strict;
|
contain: strict;
|
||||||
}
|
}
|
||||||
|
|
||||||
.split-pane.split-pane-rtl {
|
[dir="rtl"] .split-pane {
|
||||||
flex-direction: row-reverse;
|
flex-direction: row-reverse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,9 +217,6 @@ export class SplitPane extends Ion implements RootNode {
|
|||||||
renderer: Renderer
|
renderer: Renderer
|
||||||
) {
|
) {
|
||||||
super(config, elementRef, renderer, 'split-pane');
|
super(config, elementRef, renderer, 'split-pane');
|
||||||
if (_plt.isRTL()) {
|
|
||||||
this.setElementClass('split-pane-rtl', true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -7,7 +7,8 @@ import { Platform } from '../platform/platform';
|
|||||||
* @hidden
|
* @hidden
|
||||||
*/
|
*/
|
||||||
export class SlideEdgeGesture extends SlideGesture {
|
export class SlideEdgeGesture extends SlideGesture {
|
||||||
public edges: Array<string>;
|
|
||||||
|
public edges: string[];
|
||||||
public maxEdgeStart: any;
|
public maxEdgeStart: any;
|
||||||
private _d: any;
|
private _d: any;
|
||||||
|
|
||||||
@ -18,32 +19,47 @@ export class SlideEdgeGesture extends SlideGesture {
|
|||||||
});
|
});
|
||||||
super(plt, element, opts);
|
super(plt, element, opts);
|
||||||
// Can check corners through use of eg 'left top'
|
// Can check corners through use of eg 'left top'
|
||||||
this.edges = opts.edge.split(' ');
|
this.setEdges(opts.edge);
|
||||||
this.maxEdgeStart = opts.maxEdgeStart;
|
this.maxEdgeStart = opts.maxEdgeStart;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setEdges(edges: string) {
|
||||||
|
const isRTL = this.plt.isRTL;
|
||||||
|
this.edges = edges.split(' ').map((value) => {
|
||||||
|
switch (value) {
|
||||||
|
case 'start': return isRTL ? 'right' : 'left';
|
||||||
|
case 'end': return isRTL ? 'left' : 'right';
|
||||||
|
default: value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
canStart(ev: any): boolean {
|
canStart(ev: any): boolean {
|
||||||
let coord = pointerCoord(ev);
|
const coord = pointerCoord(ev);
|
||||||
this._d = this.getContainerDimensions();
|
this._d = this.getContainerDimensions();
|
||||||
return this.edges.every(edge => this._checkEdge(edge, coord));
|
return this.edges.every(edge => this._checkEdge(edge, coord));
|
||||||
}
|
}
|
||||||
|
|
||||||
getContainerDimensions() {
|
getContainerDimensions() {
|
||||||
|
const plt = this.plt;
|
||||||
return {
|
return {
|
||||||
left: 0,
|
left: 0,
|
||||||
top: 0,
|
top: 0,
|
||||||
width: this.plt.width(),
|
width: plt.width(),
|
||||||
height: this.plt.height()
|
height: plt.height()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
_checkEdge(edge: string, pos: any) {
|
_checkEdge(edge: string, pos: any): boolean {
|
||||||
|
const data = this._d;
|
||||||
|
const maxEdgeStart = this.maxEdgeStart;
|
||||||
switch (edge) {
|
switch (edge) {
|
||||||
case 'left': return pos.x <= this._d.left + this.maxEdgeStart;
|
case 'left': return pos.x <= data.left + maxEdgeStart;
|
||||||
case 'right': return pos.x >= this._d.width - this.maxEdgeStart;
|
case 'right': return pos.x >= data.width - maxEdgeStart;
|
||||||
case 'top': return pos.y <= this._d.top + this.maxEdgeStart;
|
case 'top': return pos.y <= data.top + maxEdgeStart;
|
||||||
case 'bottom': return pos.y >= this._d.height - this.maxEdgeStart;
|
case 'bottom': return pos.y >= data.height - maxEdgeStart;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -587,7 +587,7 @@ export class NavControllerBase extends Ion implements NavController {
|
|||||||
direction: opts.direction,
|
direction: opts.direction,
|
||||||
duration: (opts.animate === false ? 0 : opts.duration),
|
duration: (opts.animate === false ? 0 : opts.duration),
|
||||||
easing: opts.easing,
|
easing: opts.easing,
|
||||||
isRTL: this._config.plt.isRTL(),
|
isRTL: this._config.plt.isRTL,
|
||||||
ev: opts.ev,
|
ev: opts.ev,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ export class SwipeBackGesture extends SlideEdgeGesture {
|
|||||||
) {
|
) {
|
||||||
super(plt, plt.doc().body, {
|
super(plt, plt.doc().body, {
|
||||||
direction: 'x',
|
direction: 'x',
|
||||||
edge: 'left',
|
edge: 'start',
|
||||||
maxEdgeStart: 75,
|
maxEdgeStart: 75,
|
||||||
threshold: 5,
|
threshold: 5,
|
||||||
zone: false,
|
zone: false,
|
||||||
@ -50,7 +50,7 @@ export class SwipeBackGesture extends SlideEdgeGesture {
|
|||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
|
|
||||||
let stepValue = (slide.distance / slide.max);
|
const stepValue = (slide.distance / slide.max);
|
||||||
this._nav.swipeBackProgress(stepValue);
|
this._nav.swipeBackProgress(stepValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@ import { removeArrayItem } from '../util/util';
|
|||||||
* @demo /docs/demos/src/platform/
|
* @demo /docs/demos/src/platform/
|
||||||
*/
|
*/
|
||||||
export class Platform {
|
export class Platform {
|
||||||
|
|
||||||
private _win: Window;
|
private _win: Window;
|
||||||
private _doc: HTMLDocument;
|
private _doc: HTMLDocument;
|
||||||
private _versions: {[name: string]: PlatformVersion} = {};
|
private _versions: {[name: string]: PlatformVersion} = {};
|
||||||
@ -313,9 +314,12 @@ export class Platform {
|
|||||||
* direction needs to be dynamically changed per user/session.
|
* direction needs to be dynamically changed per user/session.
|
||||||
* [W3C: Structural markup and right-to-left text in HTML](http://www.w3.org/International/questions/qa-html-dir)
|
* [W3C: Structural markup and right-to-left text in HTML](http://www.w3.org/International/questions/qa-html-dir)
|
||||||
* @param {string} dir Examples: `rtl`, `ltr`
|
* @param {string} dir Examples: `rtl`, `ltr`
|
||||||
|
* @param {boolean} updateDocument
|
||||||
*/
|
*/
|
||||||
setDir(dir: string, updateDocument: boolean) {
|
setDir(dir: string, updateDocument: boolean) {
|
||||||
this._dir = (dir || '').toLowerCase();
|
this._dir = dir = (dir || '').toLowerCase();
|
||||||
|
this.isRTL = (dir === 'rtl');
|
||||||
|
|
||||||
if (updateDocument !== false) {
|
if (updateDocument !== false) {
|
||||||
this._doc['documentElement'].setAttribute('dir', dir);
|
this._doc['documentElement'].setAttribute('dir', dir);
|
||||||
}
|
}
|
||||||
@ -339,9 +343,7 @@ export class Platform {
|
|||||||
* [W3C: Structural markup and right-to-left text in HTML](http://www.w3.org/International/questions/qa-html-dir)
|
* [W3C: Structural markup and right-to-left text in HTML](http://www.w3.org/International/questions/qa-html-dir)
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
isRTL(): boolean {
|
isRTL: boolean;
|
||||||
return (this._dir === 'rtl');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the app's language and optionally the country code, which will update
|
* Set the app's language and optionally the country code, which will update
|
||||||
|
@ -543,13 +543,14 @@ export function mockTabs(app?: App): Tabs {
|
|||||||
return new Tabs(null, null, app, config, elementRef, platform, renderer, linker);
|
return new Tabs(null, null, app, config, elementRef, platform, renderer, linker);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mockMenu(): Menu {
|
export function mockMenu(platform: MockPlatform = null): Menu {
|
||||||
let app = mockApp();
|
let app = mockApp();
|
||||||
let gestureCtrl = new GestureController(app);
|
let gestureCtrl = new GestureController(app);
|
||||||
let dom = mockDomController();
|
let dom = mockDomController();
|
||||||
let elementRef = mockElementRef();
|
let elementRef = mockElementRef();
|
||||||
let renderer = mockRenderer();
|
let renderer = mockRenderer();
|
||||||
return new Menu(null, elementRef, null, null, renderer, null, null, gestureCtrl, dom, app);
|
let plt = platform === null ? mockPlatform() : platform;
|
||||||
|
return new Menu(null, elementRef, null, plt, renderer, null, null, gestureCtrl, dom, app);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mockDeepLinkConfig(links?: any[]): DeepLinkConfig {
|
export function mockDeepLinkConfig(links?: any[]): DeepLinkConfig {
|
||||||
|
@ -128,6 +128,18 @@ export function isCheckedProperty(a: any, b: any): boolean {
|
|||||||
return (a == b); // tslint:disable-line
|
return (a == b); // tslint:disable-line
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type Side = 'left' | 'right' | 'start' | 'end';
|
||||||
|
|
||||||
|
export function isRightSide(side: Side, isRTL: boolean): boolean {
|
||||||
|
switch (side) {
|
||||||
|
case 'right': return true;
|
||||||
|
case 'left': return false;
|
||||||
|
case 'end': return !isRTL;
|
||||||
|
// 'start' by default
|
||||||
|
default: return isRTL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
export function reorderArray(array: any[], indexes: {from: number, to: number}): any[] {
|
export function reorderArray(array: any[], indexes: {from: number, to: number}): any[] {
|
||||||
|
Reference in New Issue
Block a user