Skip to content

Commit

Permalink
fix(angular): add swipe-to-go-back gesture
Browse files Browse the repository at this point in the history
  • Loading branch information
manucorporat committed Nov 14, 2018
1 parent bfbbeca commit 108691d
Show file tree
Hide file tree
Showing 29 changed files with 351 additions and 338 deletions.
1 change: 0 additions & 1 deletion angular/src/directives/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ export { RadioValueAccessor } from './control-value-accessors/radio-value-access
export { SelectValueAccessor } from './control-value-accessors/select-value-accessor';
export { TextValueAccessor } from './control-value-accessors/text-value-accessor';

export { RouterDirection } from './navigation/router-direction';
export { IonBackButton } from './navigation/ion-back-button';
export { NavDelegate } from './navigation/nav-delegate';
export { TabDelegate } from './navigation/tab-delegate';
Expand Down
18 changes: 17 additions & 1 deletion angular/src/directives/navigation/href-delegate.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import { Directive, ElementRef, HostListener, Input, Optional } from '@angular/core';
import { Router } from '@angular/router';
import { NavController, NavIntent } from '../../providers/nav-controller';

export type RouterDirection = 'forward' | 'back' | 'root' | 'auto';

@Directive({
selector: 'ion-anchor,ion-button,ion-item'
selector: '[routerDirection],ion-anchor,ion-button,ion-item'
})
export class HrefDelegate {

@Input() routerDirection: RouterDirection = 'forward';

@Input()
set routerLink(_: any) {
this.elementRef.nativeElement.button = true;
Expand All @@ -21,6 +26,7 @@ export class HrefDelegate {

constructor(
@Optional() private router: Router,
private navCtrl: NavController,
private elementRef: ElementRef
) {}

Expand All @@ -29,7 +35,17 @@ export class HrefDelegate {
const url = this.href;
if (this.router && url != null && url[0] !== '#' && url.indexOf('://') === -1) {
ev.preventDefault();
this.navCtrl.setIntent(textToIntent(this.routerDirection));
this.router.navigateByUrl(url);
}
}
}

function textToIntent(direction: RouterDirection) {
switch (direction) {
case 'forward': return NavIntent.Forward;
case 'back': return NavIntent.Back;
case 'root': return NavIntent.Root;
default: return NavIntent.Auto;
}
}
41 changes: 31 additions & 10 deletions angular/src/directives/navigation/ion-router-outlet.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Attribute, ChangeDetectorRef, ComponentFactoryResolver, ComponentRef, Directive, ElementRef, EventEmitter, Injector, Input, OnDestroy, OnInit, Optional, Output, ViewContainerRef } from '@angular/core';
import { Attribute, ChangeDetectorRef, ComponentFactoryResolver, ComponentRef, Directive, ElementRef, EventEmitter, Injector, Input, NgZone, OnDestroy, OnInit, Optional, Output, ViewContainerRef } from '@angular/core';
import { ActivatedRoute, ChildrenOutletContexts, OutletContext, PRIMARY_OUTLET, Router } from '@angular/router';
import { RouteView, StackController } from './router-controller';
import { RouteView, StackController } from './stack-controller';
import { NavController } from '../../providers/nav-controller';
import { bindLifecycleEvents } from '../../providers/angular-delegate';

Expand All @@ -14,32 +14,47 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
private activatedView: RouteView | null = null;

private _activatedRoute: ActivatedRoute | null = null;
private _swipeGesture?: boolean;
private name: string;
private stackCtrl: StackController;
private nativeEl: HTMLIonRouterOutletElement;
private hasStack = false;

@Output('activate') activateEvents = new EventEmitter<any>();
@Output('deactivate') deactivateEvents = new EventEmitter<any>();

@Input()
set animated(animated: boolean) {
(this.elementRef.nativeElement as HTMLIonRouterOutletElement).animated = animated;
this.nativeEl.animated = animated;
}

@Input()
set swipeGesture(swipe: boolean) {
this._swipeGesture = swipe;
this.nativeEl.swipeHandler = (swipe && this.hasStack) ? {
canStart: () => this.stackCtrl.canGoBack(1),
onStart: () => this.stackCtrl.startBackTransition(),
onEnd: shouldContinue => this.stackCtrl.endBackTransition(shouldContinue)
} : undefined;
}

constructor(
private parentContexts: ChildrenOutletContexts,
private location: ViewContainerRef,
private resolver: ComponentFactoryResolver,
private elementRef: ElementRef,
@Attribute('name') name: string,
@Optional() @Attribute('stack') stack: any,
private changeDetector: ChangeDetectorRef,
private navCtrl: NavController,
router: Router
elementRef: ElementRef,
router: Router,
zone: NgZone
) {
this.nativeEl = elementRef.nativeElement;
this.name = name || PRIMARY_OUTLET;
parentContexts.onChildOutletCreated(this.name, this as any);
const hasStack = stack !== 'false' && stack !== false;
this.stackCtrl = new StackController(hasStack, elementRef.nativeElement, router, this.navCtrl);
this.hasStack = stack !== 'false' && stack !== false;
this.stackCtrl = new StackController(this.hasStack, this.nativeEl, router, this.navCtrl, zone);
}

ngOnDestroy(): void {
Expand All @@ -60,9 +75,16 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
this.activateWith(context.route, context.resolver || null);
}
}
this.nativeEl.componentOnReady().then(() => {
if (this._swipeGesture === undefined) {
this.swipeGesture = this.nativeEl.mode === 'ios';
}
});
}

get isActivated(): boolean { return !!this.activated; }
get isActivated(): boolean {
return !!this.activated;
}

get component(): object {
if (!this.activated) {
Expand Down Expand Up @@ -151,9 +173,8 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
this.activatedView = enteringView;
this.stackCtrl.setActive(enteringView, direction, animated).then(() => {
this.activateEvents.emit(cmpRef.instance);
emitEvent(this.elementRef.nativeElement);
emitEvent(this.nativeEl);
});

}

canGoBack(deep = 1) {
Expand Down
28 changes: 0 additions & 28 deletions angular/src/directives/navigation/router-direction.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ComponentRef } from '@angular/core';
import { ComponentRef, NgZone } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';

import { NavController } from '../../providers/nav-controller';
Expand All @@ -8,12 +8,15 @@ export class StackController {

private viewsSnapshot: RouteView[] = [];
private views: RouteView[] = [];
private runningTransition?: Promise<boolean>;
private skipTransition = false;

constructor(
private stack: boolean,
private containerEl: HTMLIonRouterOutletElement,
private router: Router,
private navCtrl: NavController,
private zone: NgZone,
) {}

createView(enteringRef: ComponentRef<any>, route: ActivatedRoute): RouteView {
Expand All @@ -22,7 +25,6 @@ export class StackController {
element: (enteringRef && enteringRef.location && enteringRef.location.nativeElement) as HTMLElement,
url: this.getUrl(route),
fullpath: document.location!.pathname,
deactivatedId: -1
};
}

Expand All @@ -31,22 +33,43 @@ export class StackController {
return this.views.find(vw => vw.url === activatedUrlKey);
}

canGoBack(deep: number): boolean {
return this.views.length > deep;
}

async setActive(enteringView: RouteView, direction: number, animated: boolean) {
const leavingView = this.getActive();
this.insertView(enteringView, direction);
await this.transition(enteringView, leavingView, direction, animated, this.canGoBack(1));
await this.transition(enteringView, leavingView, direction, animated, this.canGoBack(1), false);
this.cleanup();
}

canGoBack(deep: number): boolean {
return this.views.length > deep;
}

pop(deep: number) {
const view = this.views[this.views.length - deep - 1];
this.navCtrl.navigateBack(view.url);
this.zone.run(() => {
const view = this.views[this.views.length - deep - 1];
this.navCtrl.navigateBack(view.url);
});
}

startBackTransition() {
this.transition(
this.views[this.views.length - 2],
this.views[this.views.length - 1],
-1,
true,
true,
true
);
}

endBackTransition(shouldComplete: boolean) {
if (shouldComplete) {
this.skipTransition = true;
this.pop(1);
}
}


private insertView(enteringView: RouteView, direction: number) {
// no stack
if (!this.stack) {
Expand Down Expand Up @@ -100,8 +123,17 @@ export class StackController {
leavingView: RouteView | undefined,
direction: number,
animated: boolean,
showGoBack: boolean
showGoBack: boolean,
progressAnimation: boolean
) {
if (this.runningTransition) {
await this.runningTransition;
this.runningTransition = undefined;
}
if (this.skipTransition) {
this.skipTransition = false;
return;
}
const enteringEl = enteringView ? enteringView.element : undefined;
const leavingEl = leavingView ? leavingView.element : undefined;
const containerEl = this.containerEl;
Expand All @@ -112,46 +144,34 @@ export class StackController {
}

await containerEl.componentOnReady();
await containerEl.commit(enteringEl, leavingEl, {
this.runningTransition = containerEl.commit(enteringEl, leavingEl, {
duration: !animated ? 0 : undefined,
direction: direction === 1 ? 'forward' : 'back',
deepWait: true,
showGoBack
showGoBack,
progressAnimation
});
await this.runningTransition;
}
}

private getUrl(activatedRoute: ActivatedRoute) {
const urlTree = this.router.createUrlTree(['.'], { relativeTo: activatedRoute });
return this.router.serializeUrl(urlTree);
}

}

export function destroyView(view: RouteView) {
function destroyView(view: RouteView) {
if (view) {
// TODO lifecycle event
view.ref.destroy();
}
}

export function getLastDeactivatedRef(views: RouteView[]) {
if (views.length < 2) {
return null;
}

return views.sort((a, b) => {
if (a.deactivatedId > b.deactivatedId) return -1;
if (a.deactivatedId < b.deactivatedId) return 1;
return 0;
})[0].ref;
}

export interface RouteView {
url: string;
fullpath: string;
element: HTMLElement;
ref: ComponentRef<any>;
deactivatedId: number;
savedData?: any;
}
1 change: 0 additions & 1 deletion angular/src/ionic-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,6 @@ const DECLARATIONS = [
// navigation
c.IonBackButton,
c.IonRouterOutlet,
c.RouterDirection,
c.NavDelegate,
c.TabDelegate,
c.TabsDelegate,
Expand Down
Loading

0 comments on commit 108691d

Please sign in to comment.