refactor(gestures): using new DomController

This commit is contained in:
Manu Mtz.-Almeida
2016-12-02 10:55:46 +01:00
parent 3532a71c29
commit c08c21a4a0
15 changed files with 241 additions and 165 deletions

View File

@ -4,7 +4,7 @@ import { List } from '../list/list';
import { GestureController, GesturePriority, GESTURE_ITEM_SWIPE } from '../../gestures/gesture-controller';
import { PanGesture } from '../../gestures/drag-gesture';
import { pointerCoord } from '../../util/dom';
import { NativeRafDebouncer } from '../../util/debouncer';
import { DomController } from '../../util/dom-controller';
/**
* @private
@ -17,12 +17,16 @@ export class ItemSlidingGesture extends PanGesture {
private firstCoordX: number;
private firstTimestamp: number;
constructor(public list: List, gestureCtrl: GestureController) {
constructor(
public list: List,
gestureCtrl: GestureController,
domCtrl: DomController
) {
super(list.getNativeElement(), {
maxAngle: 20,
threshold: 10,
threshold: 5,
zone: false,
debouncer: new NativeRafDebouncer(),
domController: domCtrl,
gesture: gestureCtrl.createGesture({
name: GESTURE_ITEM_SWIPE,
priority: GesturePriority.SlidingItem,

View File

@ -5,6 +5,7 @@ import { Ion } from '../ion';
import { isTrueProperty } from '../../util/util';
import { ItemSlidingGesture } from '../item/item-sliding-gesture';
import { GestureController } from '../../gestures/gesture-controller';
import { DomController } from '../../util/dom-controller';
/**
* The List is a widely used interface element in almost any mobile app,
@ -53,7 +54,8 @@ export class List extends Ion {
config: Config,
elementRef: ElementRef,
renderer: Renderer,
public _gestureCtrl: GestureController
private _gestureCtrl: GestureController,
private _domCtrl: DomController,
) {
super(config, elementRef, renderer, 'list');
}
@ -95,7 +97,7 @@ export class List extends Ion {
} else if (!this._slidingGesture) {
console.debug('enableSlidingItems');
this._slidingGesture = new ItemSlidingGesture(this, this._gestureCtrl);
this._slidingGesture = new ItemSlidingGesture(this, this._gestureCtrl, this._domCtrl);
this._slidingGesture.listen();
}
}

View File

@ -1,9 +1,8 @@
import { Menu } from './menu';
import { SlideEdgeGesture } from '../../gestures/slide-edge-gesture';
import { SlideData } from '../../gestures/slide-gesture';
import { assign } from '../../util/util';
import { GestureController, GesturePriority, GESTURE_MENU_SWIPE } from '../../gestures/gesture-controller';
import { NativeRafDebouncer } from '../../util/debouncer';
import { DomController } from '../../util/dom-controller';
/**
* Gesture attached to the content which the menu is assigned to
@ -12,24 +11,23 @@ export class MenuContentGesture extends SlideEdgeGesture {
constructor(
public menu: Menu,
contentEle: HTMLElement,
gestureCtrl: GestureController,
options: any = {}
domCtrl: DomController,
) {
super(contentEle, assign({
super(document.body, {
direction: 'x',
edge: menu.side,
threshold: 5,
maxEdgeStart: menu.maxEdgeStart || 50,
zone: false,
passive: true,
debouncer: new NativeRafDebouncer(),
domController: domCtrl,
gesture: gestureCtrl.createGesture({
name: GESTURE_MENU_SWIPE,
priority: GesturePriority.MenuSwipe,
disableScroll: true
})
}, options));
});
}
canStart(ev: any): boolean {
@ -48,19 +46,19 @@ export class MenuContentGesture extends SlideEdgeGesture {
// Set CSS, then wait one frame for it to apply before sliding starts
onSlideBeforeStart(ev: any) {
console.debug('menu gesture, onSlideBeforeStart', this.menu.side);
this.menu.swipeBeforeStart();
this.menu._swipeBeforeStart();
}
onSlideStart() {
console.debug('menu gesture, onSlideStart', this.menu.side);
this.menu.swipeStart();
this.menu._swipeStart();
}
onSlide(slide: SlideData, ev: any) {
let z = (this.menu.side === 'right' ? slide.min : slide.max);
let stepValue = (slide.distance / z);
this.menu.swipeProgress(stepValue);
this.menu._swipeProgress(stepValue);
}
onSlideEnd(slide: SlideData, ev: any) {
@ -84,7 +82,7 @@ export class MenuContentGesture extends SlideEdgeGesture {
'shouldCompleteRight', shouldCompleteRight,
'currentStepValue', currentStepValue);
this.menu.swipeEnd(shouldCompleteLeft, shouldCompleteRight, currentStepValue, velocity);
this.menu._swipeEnd(shouldCompleteLeft, shouldCompleteRight, currentStepValue, velocity);
}
getElementStartPos(slide: SlideData, ev: any) {

View File

@ -12,6 +12,7 @@ import { Platform } from '../../platform/platform';
import { BlockerDelegate, GestureController, GESTURE_GO_BACK_SWIPE } from '../../gestures/gesture-controller';
import { UIEventManager } from '../../util/ui-event-manager';
import { Content } from '../content/content';
import { DomController } from '../../util/dom-controller';
/**
* @name Menu
@ -190,13 +191,14 @@ import { Content } from '../content/content';
encapsulation: ViewEncapsulation.None,
})
export class Menu {
private _cntEle: HTMLElement;
private _cntGesture: MenuContentGesture;
private _gesture: MenuContentGesture;
private _type: MenuType;
private _isEnabled: boolean = true;
private _isSwipeEnabled: boolean = true;
private _isAnimating: boolean = false;
private _isPers: boolean = false;
private _isPersistent: boolean = false;
private _init: boolean = false;
private _events: UIEventManager = new UIEventManager();
private _gestureBlocker: BlockerDelegate;
@ -269,11 +271,11 @@ export class Menu {
*/
@Input()
get persistent(): boolean {
return this._isPers;
return this._isPersistent;
}
set persistent(val: boolean) {
this._isPers = isTrueProperty(val);
this._isPersistent = isTrueProperty(val);
}
/**
@ -305,7 +307,8 @@ export class Menu {
private _keyboard: Keyboard,
private _zone: NgZone,
private _gestureCtrl: GestureController,
private _app: App
private _domCtrl: DomController,
private _app: App,
) {
this._gestureBlocker = _gestureCtrl.createBlocker({
disable: [GESTURE_GO_BACK_SWIPE]
@ -339,7 +342,7 @@ export class Menu {
this.setElementAttribute('type', this.type);
// add the gestures
this._cntGesture = new MenuContentGesture(this, <any>document, this._gestureCtrl);
this._gesture = new MenuContentGesture(this, this._gestureCtrl, this._domCtrl);
// register listeners if this menu is enabled
// check if more than one menu is on the same side
@ -362,11 +365,10 @@ export class Menu {
/**
* @private
*/
onBackdropClick(ev: UIEvent): boolean {
onBackdropClick(ev: UIEvent) {
ev.preventDefault();
ev.stopPropagation();
this._menuCtrl.close();
return false;
}
/**
@ -376,17 +378,17 @@ export class Menu {
if (!this._init) {
return;
}
const gesture = this._gesture;
// only listen/unlisten if the menu has initialized
if (this._isEnabled && this._isSwipeEnabled && !this._cntGesture.isListening) {
if (this._isEnabled && this._isSwipeEnabled && !gesture.isListening) {
// should listen, but is not currently listening
console.debug('menu, gesture listen', this.side);
this._cntGesture.listen();
gesture.listen();
} else if (this._cntGesture.isListening && (!this._isEnabled || !this._isSwipeEnabled)) {
} else if (gesture.isListening && (!this._isEnabled || !this._isSwipeEnabled)) {
// should not listen, but is currently listening
console.debug('menu, gesture unlisten', this.side);
this._cntGesture.unlisten();
gesture.unlisten();
}
}
@ -424,7 +426,6 @@ export class Menu {
});
}
/**
* @private
*/
@ -435,10 +436,7 @@ export class Menu {
this._app.isEnabled();
}
/**
* @private
*/
swipeBeforeStart() {
_swipeBeforeStart() {
if (!this.canSwipe()) {
assert(false, 'canSwipe() has to be true');
return;
@ -446,44 +444,36 @@ export class Menu {
this._before();
}
/**
* @private
*/
swipeStart() {
// user actively dragging the menu
_swipeStart() {
if (!this._isAnimating) {
assert(false, '_isAnimating has to be true');
return;
}
this._getType().setProgressStart(this.isOpen);
}
/**
* @private
*/
swipeProgress(stepValue: number) {
// user actively dragging the menu
_swipeProgress(stepValue: number) {
if (!this._isAnimating) {
assert(false, '_isAnimating has to be true');
return;
}
this._getType().setProgessStep(stepValue);
let ionDrag = this.ionDrag;
this._getType().setProgessStep(stepValue);
const ionDrag = this.ionDrag;
if (ionDrag.observers.length > 0) {
ionDrag.emit(stepValue);
}
}
/**
* @private
*/
swipeEnd(shouldCompleteLeft: boolean, shouldCompleteRight: boolean, stepValue: number, velocity: number) {
_swipeEnd(shouldCompleteLeft: boolean, shouldCompleteRight: boolean, stepValue: number, velocity: number) {
if (!this._isAnimating) {
assert(false, '_isAnimating has to be true');
return;
}
// user has finished dragging the menu
let opening = !this.isOpen;
const opening = !this.isOpen;
let shouldComplete = false;
if (opening) {
shouldComplete = (this.side === 'right') ? shouldCompleteLeft : shouldCompleteRight;
@ -511,6 +501,7 @@ export class Menu {
private _after(isOpen: boolean) {
assert(this._isAnimating, '_before() should be called while animating');
// keep opening/closing the menu disabled for a touch more yet
// only add listeners/css if it's enabled and isOpen
// and only remove listeners/css if it's not open
@ -660,10 +651,10 @@ export class Menu {
ngOnDestroy() {
this._menuCtrl.unregister(this);
this._events.unlistenAll();
this._cntGesture && this._cntGesture.destroy();
this._gesture && this._gesture.destroy();
this._type && this._type.destroy();
this._cntGesture = null;
this._gesture = null;
this._type = null;
this._cntEle = null;
}

View File

@ -10,6 +10,7 @@ import { NavControllerBase } from '../../navigation/nav-controller-base';
import { NavOptions } from '../../navigation/nav-util';
import { TransitionController } from '../../transitions/transition-controller';
import { ViewController } from '../../navigation/view-controller';
import { DomController } from '../../util/dom-controller';
/**
* @name Nav
@ -66,9 +67,10 @@ export class Nav extends NavControllerBase implements AfterViewInit {
cfr: ComponentFactoryResolver,
gestureCtrl: GestureController,
transCtrl: TransitionController,
@Optional() linker: DeepLinker
@Optional() linker: DeepLinker,
domCtrl: DomController,
) {
super(parent, app, config, keyboard, elementRef, zone, renderer, cfr, gestureCtrl, transCtrl, linker);
super(parent, app, config, keyboard, elementRef, zone, renderer, cfr, gestureCtrl, transCtrl, linker, domCtrl);
if (viewCtrl) {
// an ion-nav can also act as an ion-page within a parent ion-nav
@ -149,6 +151,7 @@ export class Nav extends NavControllerBase implements AfterViewInit {
}
set swipeBackEnabled(val: boolean) {
this._sbEnabled = isTrueProperty(val);
this._swipeBackCheck();
}
/**

View File

@ -7,6 +7,7 @@ import { GestureController } from '../../gestures/gesture-controller';
import { Keyboard } from '../../util/keyboard';
import { NavControllerBase } from '../../navigation/nav-controller-base';
import { TransitionController } from '../../transitions/transition-controller';
import { DomController } from '../../util/dom-controller';
/**
* @private
@ -26,9 +27,10 @@ export class OverlayPortal extends NavControllerBase {
gestureCtrl: GestureController,
transCtrl: TransitionController,
@Optional() linker: DeepLinker,
viewPort: ViewContainerRef
viewPort: ViewContainerRef,
domCtrl: DomController,
) {
super(null, app, config, keyboard, elementRef, zone, renderer, cfr, gestureCtrl, transCtrl, linker);
super(null, app, config, keyboard, elementRef, zone, renderer, cfr, gestureCtrl, transCtrl, linker, domCtrl);
this._isPortal = true;
this._init = true;
this.setViewport(viewPort);

View File

@ -13,7 +13,7 @@ import { TabButton } from './tab-button';
import { Tabs } from './tabs';
import { TransitionController } from '../../transitions/transition-controller';
import { ViewController } from '../../navigation/view-controller';
import { DomController } from '../../util/dom-controller';
/**
* @name Tab
@ -273,10 +273,11 @@ export class Tab extends NavControllerBase {
private _cd: ChangeDetectorRef,
gestureCtrl: GestureController,
transCtrl: TransitionController,
@Optional() private linker: DeepLinker
@Optional() private linker: DeepLinker,
domCtrl: DomController,
) {
// A Tab is a NavController for its child pages
super(parent, app, config, keyboard, elementRef, zone, renderer, cfr, gestureCtrl, transCtrl, linker);
super(parent, app, config, keyboard, elementRef, zone, renderer, cfr, gestureCtrl, transCtrl, linker, domCtrl);
this.id = parent.add(this);
this._tabsHideOnSubPages = config.getBoolean('tabsHideOnSubPages');

View File

@ -1,7 +1,7 @@
import { GestureController, GesturePriority, GESTURE_TOGGLE } from '../../gestures/gesture-controller';
import { PanGesture } from '../../gestures/drag-gesture';
import { pointerCoord } from '../../util/dom';
import { NativeRafDebouncer } from '../../util/debouncer';
import { DomController } from '../../util/dom-controller';
import { Toggle } from './toggle';
/**
@ -9,11 +9,14 @@ import { Toggle } from './toggle';
*/
export class ToggleGesture extends PanGesture {
constructor(public toogle: Toggle, gestureCtrl: GestureController) {
constructor(
public toogle: Toggle,
gestureCtrl: GestureController,
domCtrl: DomController
) {
super(toogle.getNativeElement(), {
maxAngle: 20,
threshold: 0,
debouncer: new NativeRafDebouncer(),
domController: domCtrl,
gesture: gestureCtrl.createGesture({
name: GESTURE_TOGGLE,
priority: GesturePriority.Toggle,

View File

@ -2,14 +2,15 @@ import { AfterContentInit, Component, ElementRef, EventEmitter, forwardRef, Host
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Config } from '../../config/config';
import { Form, IonicTapInput } from '../../util/form';
import { isTrueProperty, assert } from '../../util/util';
import { Ion } from '../ion';
import { Item } from '../item/item';
import { Key } from '../../util/key';
import { Haptic } from '../../util/haptic';
import { ToggleGesture } from './toggle-gesture';
import { GestureController } from '../../gestures/gesture-controller';
import { Key } from '../../util/key';
import { Haptic } from '../../util/haptic';
import { Form, IonicTapInput } from '../../util/form';
import { isTrueProperty, assert } from '../../util/util';
import { DomController } from '../../util/dom-controller';
export const TOGGLE_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
@ -118,7 +119,8 @@ export class Toggle extends Ion implements IonicTapInput, AfterContentInit, Cont
renderer: Renderer,
private _haptic: Haptic,
@Optional() public _item: Item,
private _gestureCtrl: GestureController
private _gestureCtrl: GestureController,
private _domCtrl: DomController
) {
super(config, elementRef, renderer, 'toggle');
_form.register(this);
@ -135,7 +137,7 @@ export class Toggle extends Ion implements IonicTapInput, AfterContentInit, Cont
*/
ngAfterContentInit() {
this._init = true;
this._gesture = new ToggleGesture(this, this._gestureCtrl);
this._gesture = new ToggleGesture(this, this._gestureCtrl, this._domCtrl);
this._gesture.listen();
}
@ -143,6 +145,9 @@ export class Toggle extends Ion implements IonicTapInput, AfterContentInit, Cont
* @private
*/
_onDragStart(startX: number) {
assert(startX, 'startX must be valid');
console.debug('toggle, _onDragStart', startX);
this._startX = startX;
this._activated = true;
}
@ -151,9 +156,12 @@ export class Toggle extends Ion implements IonicTapInput, AfterContentInit, Cont
* @private
*/
_onDragMove(currentX: number) {
assert(this._startX, '_startX must be valid');
if (!this._startX) {
assert(false, '_startX must be valid');
return;
}
console.debug('toggle, pointerMove', currentX);
console.debug('toggle, _onDragMove', currentX);
if (this._checked) {
if (currentX + 15 < this._startX) {
@ -175,7 +183,11 @@ export class Toggle extends Ion implements IonicTapInput, AfterContentInit, Cont
* @private
*/
_onDragEnd(endX: number) {
assert(this._startX, '_startX must be valid');
if (!this._startX) {
assert(false, '_startX must be valid');
return;
}
console.debug('toggle, _onDragEnd', endX);
if (this.checked) {
if (this._startX + 4 > endX) {

View File

@ -3,7 +3,7 @@ import { GestureDelegate } from '../gestures/gesture-controller';
import { PanRecognizer } from './recognizers';
import { PointerEvents, PointerEventsConfig, UIEventManager } from '../util/ui-event-manager';
import { pointerCoord } from '../util/dom';
import { Debouncer, FakeDebouncer } from '../util/debouncer';
import { DomDebouncer, DomController } from '../util/dom-controller';
/**
* @private
@ -13,7 +13,7 @@ export interface PanGestureConfig {
maxAngle?: number;
direction?: 'x' | 'y';
gesture?: GestureDelegate;
debouncer?: Debouncer;
domController?: DomController;
zone?: boolean;
capture?: boolean;
passive?: boolean;
@ -24,7 +24,7 @@ export interface PanGestureConfig {
*/
export class PanGesture {
private debouncer: Debouncer;
private debouncer: DomDebouncer;
private events: UIEventManager = new UIEventManager(false);
private pointerEvents: PointerEvents;
private detector: PanRecognizer;
@ -44,10 +44,9 @@ export class PanGesture {
capture: false,
passive: false,
});
this.debouncer = (opts.debouncer)
? opts.debouncer
: new FakeDebouncer();
if (opts.domController) {
this.debouncer = opts.domController.debouncer();
}
this.gestute = opts.gesture;
this.direction = opts.direction;
this.eventsConfig = {
@ -124,7 +123,7 @@ export class PanGesture {
pointerMove(ev: any) {
assert(this.started === true, 'started must be true');
if (this.captured) {
this.debouncer.debounce(() => {
this.debouncer.write(() => {
this.onDragMove(ev);
});
return;

View File

@ -120,6 +120,7 @@ export class GestureController {
if (maxPriority === priority) {
this.capturedID = id;
this.requestedStart = {};
console.debug(`${gestureName} captured!`);
return true;
}
delete requestedStart[id];
@ -170,12 +171,13 @@ export class GestureController {
canStart(gestureName: string): boolean {
if (this.capturedID) {
console.debug(`${gestureName} can not start becuse gesture was already captured`);
// a gesture already captured
return false;
}
if (this.isDisabled(gestureName)) {
console.debug('GestureController: Disabled', gestureName);
console.debug(`${gestureName} is disabled`);
return false;
}
return true;

View File

@ -17,7 +17,7 @@ import { NavParams } from './nav-params';
import { SwipeBackGesture } from './swipe-back';
import { Transition } from '../transitions/transition';
import { TransitionController } from '../transitions/transition-controller';
import { DomController } from '../util/dom-controller';
/**
* @private
@ -32,7 +32,6 @@ export class NavControllerBase extends Ion implements NavController {
_queue: TransitionInstruction[] = [];
_sbEnabled: boolean;
_sbGesture: SwipeBackGesture;
_sbThreshold: number;
_sbTrns: Transition;
_trnsId: number = null;
_trnsTm: boolean = false;
@ -60,12 +59,12 @@ export class NavControllerBase extends Ion implements NavController {
public _cfr: ComponentFactoryResolver,
public _gestureCtrl: GestureController,
public _trnsCtrl: TransitionController,
public _linker: DeepLinker
public _linker: DeepLinker,
private _domCtrl: DomController
) {
super(config, elementRef, renderer);
this._sbEnabled = config.getBoolean('swipeBackEnabled');
this._sbThreshold = config.getNumber('swipeBackThreshold', 0);
this.id = 'n' + (++ctrlIds);
}
@ -151,12 +150,12 @@ export class NavControllerBase extends Ion implements NavController {
}
setRoot(pageOrViewCtrl: any, params?: any, opts?: NavOptions, done?: Function): Promise<any> {
let viewControllers = [convertToView(this._linker, pageOrViewCtrl, params)];
const viewControllers = [convertToView(this._linker, pageOrViewCtrl, params)];
return this._setPages(viewControllers, opts, done);
}
setPages(pages: any[], opts?: NavOptions, done?: Function): Promise<any> {
let viewControllers = convertToViews(this._linker, pages);
const viewControllers = convertToViews(this._linker, pages);
return this._setPages(viewControllers, opts, done);
}
@ -199,7 +198,7 @@ export class NavControllerBase extends Ion implements NavController {
// let's see if there's another to kick off
this.setTransitioning(false);
this._sbCheck();
this._swipeBackCheck();
this._nextTrns();
};
@ -226,7 +225,7 @@ export class NavControllerBase extends Ion implements NavController {
// let's see if there's another to kick off
this.setTransitioning(false);
this._sbCheck();
this._swipeBackCheck();
this._nextTrns();
};
@ -306,7 +305,7 @@ export class NavControllerBase extends Ion implements NavController {
assert(isPresent(ti.removeStart), 'removeView needs removeStart');
assert(isPresent(ti.removeCount), 'removeView needs removeCount');
let index = this._views.indexOf(ti.removeView);
var index = this._views.indexOf(ti.removeView);
if (index >= 0) {
ti.removeStart += index;
}
@ -342,10 +341,10 @@ export class NavControllerBase extends Ion implements NavController {
const removeStart = ti.removeStart;
if (isPresent(removeStart)) {
const views = this._views;
const removeEnd = removeStart + ti.removeCount;
let i: number;
let view: ViewController;
var views = this._views;
var removeEnd = removeStart + ti.removeCount;
var i: number;
var view: ViewController;
for (i = views.length - 1; i >= 0; i--) {
view = views[i];
if ((i < removeStart || i >= removeEnd) && view !== leavingView) {
@ -507,7 +506,7 @@ export class NavControllerBase extends Ion implements NavController {
const promises: Promise<any>[] = [];
if (leavingView) {
const leavingTestResult = leavingView._lifecycleTest('Leave');
var leavingTestResult = leavingView._lifecycleTest('Leave');
if (leavingTestResult === false) {
// synchronous reject
@ -520,7 +519,7 @@ export class NavControllerBase extends Ion implements NavController {
}
if (enteringView) {
const enteringTestResult = enteringView._lifecycleTest('Enter');
var enteringTestResult = enteringView._lifecycleTest('Enter');
if (enteringTestResult === false) {
// synchronous reject
@ -564,7 +563,7 @@ export class NavControllerBase extends Ion implements NavController {
direction: opts.direction,
duration: (opts.animate === false ? 0 : opts.duration),
easing: opts.easing,
isRTL: this.config.platform.isRTL(),
isRTL: this._config.platform.isRTL(),
ev: opts.ev,
};
@ -627,9 +626,9 @@ export class NavControllerBase extends Ion implements NavController {
// we should animate (duration > 0) if the pushed page is not the first one (startup)
// or if it is a portal (modal, actionsheet, etc.)
let isFirstPage = !this._init && this._views.length === 1;
let shouldNotAnimate = isFirstPage && !this._isPortal;
let canNotAnimate = this.config.get('animate') === false;
const isFirstPage = !this._init && this._views.length === 1;
const shouldNotAnimate = isFirstPage && !this._isPortal;
const canNotAnimate = this._config.get('animate') === false;
if (shouldNotAnimate || canNotAnimate) {
opts.animate = false;
}
@ -743,7 +742,7 @@ export class NavControllerBase extends Ion implements NavController {
}
_insertViewAt(view: ViewController, index: number) {
var existingIndex = this._views.indexOf(view);
const existingIndex = this._views.indexOf(view);
if (existingIndex > -1) {
// this view is already in the stack!!
// move it to its new location
@ -897,10 +896,14 @@ export class NavControllerBase extends Ion implements NavController {
}
destroy() {
for (var view of this._views) {
const views = this._views;
let view: ViewController;
for (var i = 0; i < views.length; i++) {
view = views[i];
view._willUnload();
view._destroy(this._renderer);
}
// purge stack
this._views.length = 0;
@ -947,35 +950,26 @@ export class NavControllerBase extends Ion implements NavController {
swipeBackEnd(shouldComplete: boolean, currentStepValue: number, velocity: number) {
if (this._sbTrns && this._sbGesture) {
// the swipe back gesture has ended
const dur = this._sbTrns.getDuration() / (Math.abs(velocity) + 1);
var dur = this._sbTrns.getDuration() / (Math.abs(velocity) + 1);
this._sbTrns.progressEnd(shouldComplete, currentStepValue, dur);
}
}
_sbCheck() {
if (!this._sbEnabled && this._isPortal) {
return;
}
// this nav controller can have swipe to go back
if (!this._sbGesture) {
// create the swipe back gesture if we haven't already
const opts = {
edge: 'left',
threshold: this._sbThreshold
};
this._sbGesture = new SwipeBackGesture(this, document.body, this._gestureCtrl, opts);
}
_swipeBackCheck() {
if (this.canSwipeBack()) {
if (!this._sbGesture) {
this._sbGesture = new SwipeBackGesture(this, this._gestureCtrl, this._domCtrl);
}
this._sbGesture.listen();
} else {
} else if (this._sbGesture) {
this._sbGesture.unlisten();
}
}
canSwipeBack(): boolean {
return (this._sbEnabled &&
!this._isPortal &&
!this._children.length &&
!this.isTransitioning() &&
this._app.isEnabled() &&
@ -984,7 +978,7 @@ export class NavControllerBase extends Ion implements NavController {
canGoBack(): boolean {
const activeView = this.getActive();
return !!(activeView && activeView.enableBack()) || false;
return !!(activeView && activeView.enableBack());
}
isTransitioning(): boolean {

View File

@ -1,9 +1,9 @@
import { assign, swipeShouldReset } from '../util/util';
import { swipeShouldReset } from '../util/util';
import { GestureController, GesturePriority, GESTURE_GO_BACK_SWIPE } from '../gestures/gesture-controller';
import { NavControllerBase } from './nav-controller-base';
import { SlideData } from '../gestures/slide-gesture';
import { SlideEdgeGesture } from '../gestures/slide-edge-gesture';
import { NativeRafDebouncer } from '../util/debouncer';
import { DomController } from '../util/dom-controller';
/**
* @private
@ -12,22 +12,22 @@ export class SwipeBackGesture extends SlideEdgeGesture {
constructor(
private _nav: NavControllerBase,
element: HTMLElement,
gestureCtlr: GestureController,
options: any,
domCtrl: DomController,
) {
super(element, assign({
super(document.body, {
direction: 'x',
edge: 'left',
maxEdgeStart: 75,
zone: false,
threshold: 5,
debouncer: new NativeRafDebouncer(),
zone: false,
domController: domCtrl,
gesture: gestureCtlr.createGesture({
name: GESTURE_GO_BACK_SWIPE,
priority: GesturePriority.GoBackSwipe,
disableScroll: true
})
}, options));
});
}
canStart(ev: any): boolean {

View File

@ -7,29 +7,83 @@ import { nativeRaf } from './dom';
import { removeArrayItem } from './util';
export type DomCallback = { (timeStamp: number) };
export class DomDebouncer {
private writeTask: Function = null;
private readTask: Function = null;
constructor(private dom: DomController) { }
read(fn: DomCallback): Function {
if (this.readTask) {
return;
}
return this.readTask = this.dom.read((t) => {
this.readTask = null;
fn(t);
});
}
write(fn: DomCallback, ctx?: any): Function {
if (this.writeTask) {
return;
}
return this.writeTask = this.dom.write((t) => {
this.writeTask = null;
fn(t);
});
}
cancel() {
const writeTask = this.writeTask;
writeTask && this.dom.cancelW(writeTask);
this.writeTask = null;
const readTask = this.readTask;
readTask && this.dom.cancelR(readTask);
this.readTask = null;
}
}
export class DomController {
private r: Function[] = [];
private w: Function[] = [];
private q: boolean;
read(fn: {(timeStamp: number)}, ctx?: any): Function {
debouncer(): DomDebouncer {
return new DomDebouncer(this);
}
read(fn: DomCallback, ctx?: any): Function {
const task = !ctx ? fn : fn.bind(ctx);
this.r.push(task);
this.queue();
return task;
}
write(fn: {(timeStamp: number)}, ctx?: any): Function {
write(fn: DomCallback, ctx?: any): Function {
const task = !ctx ? fn : fn.bind(ctx);
this.w.push(task);
this.queue();
return task;
}
cancel(task: any) {
cancel(task: any): boolean {
return removeArrayItem(this.r, task) || removeArrayItem(this.w, task);
}
cancelW(task: any): boolean {
return removeArrayItem(this.w, task);
}
cancelR(task: any): boolean {
return removeArrayItem(this.r, task);
}
protected queue() {
const self = this;
if (!self.q) {
@ -41,32 +95,32 @@ export class DomController {
}
protected flush(timeStamp: number) {
let err;
let task;
try {
this.dispatch(timeStamp);
} finally {
this.q = false;
}
}
private dispatch(timeStamp: number) {
let i: number;
const r = this.r;
const rLen = r.length;
const w = this.w;
const wLen = w.length;
// ******** DOM READS ****************
while (task = this.r.shift()) {
task(timeStamp);
for (i = 0; i < rLen; i++) {
r[i](timeStamp);
}
// ******** DOM WRITES ****************
while (task = this.w.shift()) {
task(timeStamp);
}
} catch (e) {
err = e;
for (i = 0; i < wLen; i++) {
w[i](timeStamp);
}
this.q = false;
if (this.r.length || this.w.length) {
this.queue();
}
if (err) {
throw err;
}
r.length = 0;
w.length = 0;
}
}

View File

@ -22,6 +22,7 @@ import { ViewController } from '../navigation/view-controller';
import { NavControllerBase } from '../navigation/nav-controller-base';
import { Haptic } from './haptic';
import { DomController } from './dom-controller';
export const mockConfig = function(config?: any, url: string = '/', platform?: Platform) {
const c = new Config();
@ -246,6 +247,8 @@ export const mockNavController = function(): NavControllerBase {
let trnsCtrl = mockTrasitionController(config);
let dom = new DomController();
let nav = new NavControllerBase(
null,
app,
@ -257,7 +260,8 @@ export const mockNavController = function(): NavControllerBase {
componentFactoryResolver,
gestureCtrl,
trnsCtrl,
linker
linker,
dom,
);
nav._viewInit = function(enteringView: ViewController) {
@ -296,6 +300,8 @@ export const mockOverlayPortal = function(app: App, config: Config, platform: Pl
let deepLinker = new DeepLinker(app, serializer, location);
let dom = new DomController();
return new OverlayPortal(
app,
config,
@ -307,7 +313,8 @@ export const mockOverlayPortal = function(app: App, config: Config, platform: Pl
gestureCtrl,
null,
deepLinker,
null
null,
dom
);
};
@ -334,6 +341,8 @@ export const mockTab = function(parentTabs: Tabs): Tab {
let linker = mockDeepLinker(null, app);
let dom = new DomController();
let tab = new Tab(
parentTabs,
app,
@ -346,7 +355,8 @@ export const mockTab = function(parentTabs: Tabs): Tab {
changeDetectorRef,
gestureCtrl,
null,
linker
linker,
dom
);
tab.load = (opts: any, cb: Function) => {
@ -371,7 +381,8 @@ export const mockTabs = function(app?: App): Tabs {
export const mockMenu = function (): Menu {
let app = mockApp();
let gestureCtrl = new GestureController(app);
return new Menu(null, null, null, null, null, null, null, gestureCtrl, app);
let dom = new DomController();
return new Menu(null, null, null, null, null, null, null, gestureCtrl, dom, app);
};
export const mockDeepLinkConfig = function(links?: any[]): DeepLinkConfig {