Merge branch 'master' into v4

This commit is contained in:
Adam Bradley
2017-04-23 21:54:56 -05:00
17 changed files with 210 additions and 107 deletions

View File

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

View File

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

View File

@ -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;
}
} }
/** /**

View File

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

View File

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

View File

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

View File

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

View File

@ -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()', () => {

View File

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

View File

@ -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;
} }

View File

@ -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);
}
} }
/** /**

View File

@ -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;
} }
} }

View File

@ -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,
}; };

View File

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

View File

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

View File

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

View File

@ -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[] {