feat(ion-router-outlet): adds router-outlet

This commit is contained in:
Manu Mtz.-Almeida
2018-03-21 15:48:49 +01:00
parent 17a3001af5
commit c03afabc0f
6 changed files with 324 additions and 27 deletions

View File

@ -43,6 +43,7 @@ import {
Side, Side,
} from './utils/helpers'; } from './utils/helpers';
import { import {
AnimationBuilder as AnimationBuilder2,
FrameworkDelegate as FrameworkDelegate2, FrameworkDelegate as FrameworkDelegate2,
} from '.'; } from '.';
import { import {
@ -2623,6 +2624,38 @@ declare global {
} }
import {
RouterOutlet as IonRouterOutlet
} from './components/router-outlet/route-outlet';
declare global {
interface HTMLIonRouterOutletElement extends IonRouterOutlet, HTMLStencilElement {
}
var HTMLIonRouterOutletElement: {
prototype: HTMLIonRouterOutletElement;
new (): HTMLIonRouterOutletElement;
};
interface HTMLElementTagNameMap {
"ion-router-outlet": HTMLIonRouterOutletElement;
}
interface ElementTagNameMap {
"ion-router-outlet": HTMLIonRouterOutletElement;
}
namespace JSX {
interface IntrinsicElements {
"ion-router-outlet": JSXElements.IonRouterOutletAttributes;
}
}
namespace JSXElements {
export interface IonRouterOutletAttributes extends HTMLAttributes {
animated?: boolean;
animationBuilder?: AnimationBuilder;
delegate?: FrameworkDelegate;
}
}
}
import { import {
Router as IonRouter Router as IonRouter
} from './components/router/router'; } from './components/router/router';

View File

@ -170,6 +170,35 @@ ion-app,
contain: layout size style; contain: layout size style;
} }
.hide-page {
opacity: 0;
}
// TODO: move to somewhere else
$navigation-ios-transition-background: #000 !default;
.nav-decor {
display: none;
}
.show-decor > .nav-decor {
@include position(0, null, null, 0);
// when ios pages transition, the leaving page grays out
// this is the black square behind all pages so they gray out
position: absolute;
z-index: 0;
display: block;
width: 100%;
height: 100%;
background: $navigation-ios-transition-background;
pointer-events: none;
}
// Misc // Misc
// -------------------------------------------------- // --------------------------------------------------

View File

@ -1,7 +1,5 @@
@import "../../themes/util"; @import "../../themes/util";
$navigation-ios-transition-background: #000 !default;
ion-nav { ion-nav {
@include position(0); @include position(0);
@ -14,28 +12,3 @@ ion-nav {
contain: layout size style; contain: layout size style;
} }
.hide-page {
opacity: 0;
}
.nav-decor {
display: none;
}
.show-decor > .nav-decor {
@include position(0, null, null, 0);
// when ios pages transition, the leaving page grays out
// this is the black square behind all pages so they gray out
position: absolute;
z-index: 0;
display: block;
width: 100%;
height: 100%;
background: $navigation-ios-transition-background;
pointer-events: none;
}

View File

@ -0,0 +1,59 @@
# ion-router-outlet
<!-- Auto Generated Below -->
## Properties
#### animated
boolean
#### animationBuilder
#### delegate
## Attributes
#### animated
boolean
#### animation-builder
#### delegate
## Methods
#### commit()
#### getRouteId()
#### setRoot()
#### setRouteId()
----------------------------------------------
*Built with [StencilJS](https://stenciljs.com/)*

View File

@ -0,0 +1,125 @@
import { Component, Element, Method, Prop } from '@stencil/core';
import { transition } from '../../utils';
import { NavDirection } from '../nav/nav-util';
import { AnimationBuilder, Config, FrameworkDelegate, NavOutlet } from '../..';
import { attachComponent, detachComponent } from '../../utils/overlays';
import iosTransitionAnimation from '../nav/animations/ios.transition';
import mdTransitionAnimation from '../nav/animations/md.transition';
import { RouteID, RouteWrite } from '../router/utils/interfaces';
@Component({
tag: 'ion-router-outlet'
})
export class RouterOutlet implements NavOutlet {
private isTransitioning = false;
private activeEl: HTMLElement = undefined;
mode: string;
@Element() el: HTMLElement;
@Prop({context: 'config'}) config: Config;
@Prop({connect: 'ion-animation-controller'}) animationCtrl: HTMLIonAnimationControllerElement;
@Prop() animated = true;
@Prop() animationBuilder: AnimationBuilder;
@Prop() delegate: FrameworkDelegate;
componentDidUnload() {
this.activeEl = undefined;
}
@Method()
async setRoot(component: HTMLElement|string, params?: {[key: string]: any}, opts?: RouterOutletOptions): Promise<boolean> {
if (this.isTransitioning) {
return false;
}
// attach entering view to DOM
const enteringEl = await attachComponent(this.delegate, this.el, component, NAV_CLASSES, params);
const leavingEl = this.activeEl;
// commit animation
await this.commit(enteringEl, opts);
// remove leaving view
detachComponent(this.delegate, leavingEl);
return true;
}
@Method()
async commit(enteringEl: HTMLElement, opts?: RouterOutletOptions): Promise<boolean> {
// isTransitioning acts as a lock to prevent reentering
if (this.isTransitioning || this.activeEl === enteringEl) {
return false;
}
this.isTransitioning = true;
opts = opts || {};
await transition({
animationBuilder: this.getAnimationBuilder(opts),
direction: opts.direction,
duration: opts.duration,
easing: opts.easing,
animationCtrl: this.animationCtrl,
enteringEl: enteringEl,
leavingEl: this.activeEl,
baseEl: this.el,
});
this.activeEl = enteringEl;
this.isTransitioning = false;
return true;
}
@Method()
async setRouteId(id: string, data: any, direction: number): Promise<RouteWrite> {
const changed = await this.setRoot(id, data, {
duration: direction === 0 ? 0 : undefined,
direction: direction === -1 ? NavDirection.back : NavDirection.forward,
});
return {
changed,
element: this.activeEl
};
}
@Method()
getRouteId(): RouteID|undefined {
const active = this.activeEl;
return active ? {
id: active.tagName,
element: active,
} : undefined;
}
private getAnimationBuilder(opts: RouterOutletOptions) {
if (opts.duration === 0 || this.animated === false || this.activeEl === undefined) {
return undefined;
}
const mode = opts.mode || this.config.get('pageTransition', this.mode);
return opts.animationBuilder
|| this.animationBuilder
|| mode === 'ios' ? iosTransitionAnimation : mdTransitionAnimation;
}
render() {
return [
this.mode === 'ios' && <div class='nav-decor'/>,
<slot/>
];
}
}
export interface RouterOutletOptions {
animationBuilder?: AnimationBuilder;
duration?: number;
easing?: string;
direction?: NavDirection;
mode?: 'md' | 'ios';
}
const NAV_CLASSES = {'ion-page': true, 'hide-page': true};

View File

@ -0,0 +1,78 @@
<!DOCTYPE html>
<html dir="ltr">
<head>
<meta charset="UTF-8">
<title>Nav</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<script src="/dist/ionic.js"></script>
<script>
class PageOne extends HTMLElement {
connectedCallback() {
this.innerHTML = `
<ion-header>
<ion-toolbar>
<ion-title>Page One</ion-title>
</ion-toolbar>
</ion-header>
<ion-content padding>
<h1>Page One</h1>
<ion-button href="#/two">Go to Page Two</ion-button>
</ion-content>
`;
}
}
class PageTwo extends HTMLElement {
connectedCallback() {
this.innerHTML = `
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button text="Page One"></ion-back-button>
</ion-buttons>
<ion-title>Page Two</ion-title>
</ion-toolbar>
</ion-header>
<ion-content padding>
<h1>Page Two</h1>
<div>
<ion-nav-push component="page-three">
<ion-button href="#/page-3">Go to Page Two</ion-button>
</ion-nav-push>
</div>
</ion-content>
`;
}
}
class PageThree extends HTMLElement {
connectedCallback() {
this.innerHTML = `
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button text="Page Two"></ion-back-button>
</ion-buttons>
<ion-title>Page Three</ion-title>
</ion-toolbar>
</ion-header>
<ion-content padding>
<h1>Page Three</h1>
</ion-content>
`;
}
}
customElements.define('page-one', PageOne);
customElements.define('page-two', PageTwo);
customElements.define('page-three', PageThree);
</script>
</head>
<body>
<ion-app>
<ion-router>
<ion-route url="/" component="page-one"> </ion-route>
<ion-route url="/two" component="page-two"> </ion-route>
<ion-route url="/page-3" component="page-three"> </ion-route>
</ion-router>
<ion-router-outlet></ion-router-outlet>
</ion-app>
</body>
</html>