feat(router): links trigger animations

This commit is contained in:
Manu Mtz.-Almeida
2018-02-14 14:57:33 +01:00
parent 31a8c60609
commit 77cf37127e
7 changed files with 88 additions and 73 deletions

View File

@@ -15,6 +15,7 @@ export class PageOne {
</ion-header>
<ion-content>
page one
<a href='#/two/second-page'>Ir a la page 2</a>
</ion-content>
</ion-page>
];

View File

@@ -193,13 +193,25 @@ export class Nav implements PublicNav, NavOutlet {
}
@Method()
setRouteId(id: string, _: any = {}): Promise<any> {
assert(this.useRouter, 'routing is disabled');
setRouteId(id: string, _: any = {}, direction: number): Promise<boolean> {
const active = this.getActive();
if (active && active.component === id) {
return Promise.resolve(null);
return Promise.resolve(false);
}
return this.setRoot(id);
if (direction === 1) {
return this.push(id).then(() => true);
} else if (direction === -1 && this._canGoBack(id)) {
return this.pop().then(() => true);
}
return this.setRoot(id).then(() => true);
}
private _canGoBack(id: any) {
if (!this.canGoBack()) {
return false;
}
const view = this.views[this.views.length - 1];
return view.component === id;
}
@Method()
@@ -215,7 +227,7 @@ export class Nav implements PublicNav, NavOutlet {
getContentElement(): HTMLElement {
const active = getActiveImpl(this);
if (active) {
active.element;
return active.element;
}
return null;
}

View File

@@ -1,5 +1,5 @@
export interface NavOutlet {
setRouteId(id: any, data?: any): Promise<any>;
setRouteId(id: any, data: any, direction: number): Promise<boolean>;
getRouteId(): string;
getContentElement(): HTMLElement | null;
}
@@ -28,22 +28,24 @@ export class RouterSegments {
}
}
export function writeNavState(root: HTMLElement, chain: RouterEntries, index = 0): Promise<void> {
export function writeNavState(root: HTMLElement, chain: RouterEntries, index: number, direction: number): Promise<void> {
if (index >= chain.length) {
return Promise.resolve();
}
const route = chain[index];
const node = breadthFirstSearch(root);
if (!node) {
return Promise.resolve();
}
return node.componentOnReady()
.then(() => node.setRouteId(route.id, route.props))
.then(() => {
.then(() => node.setRouteId(route.id, route.props, direction))
.then(changed => {
if (changed) {
direction = 0;
}
const nextEl = node.getContentElement();
if (nextEl) {
return writeNavState(nextEl, chain, index + 1);
return writeNavState(nextEl, chain, index + 1, direction);
}
return null;
});
@@ -199,18 +201,20 @@ export function breadthFirstSearch(root: HTMLElement): NavOutletElement | null {
return null;
}
export function writePath(history: History, base: string, usePath: boolean, path: string[], isPop: boolean) {
export function writePath(history: History, base: string, usePath: boolean, path: string[], isPop: boolean, state: number) {
path = [base, ...path];
let url = generatePath(path);
if (usePath) {
url = '#' + url;
}
state++;
if (isPop) {
history.back();
history.replaceState(null, null, url);
history.replaceState(state, null, url);
} else {
history.pushState(null, null, url);
history.pushState(state, null, url);
}
return state;
}
export function readPath(loc: Location, base: string, useHash: boolean): string[] | null {

View File

@@ -10,6 +10,7 @@ export class Router {
private routes: RouterEntries;
private busy = false;
private state = 0;
@Prop({ context: 'config' }) config: Config;
@Prop({ context: 'dom' }) dom: DomController;
@@ -30,8 +31,12 @@ export class Router {
});
}
@Listen('window:hashchange')
@Listen('window:popstate')
protected onURLHashChanged() {
if (window.history.state === null) {
this.state++;
window.history.replaceState(this.state, document.title, document.location.href);
}
if (!this.busy) {
console.debug('[OUT] hash changed -> write nav state');
this.writeNavStateRoot();
@@ -43,35 +48,34 @@ export class Router {
if (this.busy) {
return;
}
console.debug('[IN] nav changed -> update URL');
const { stack, pivot } = this.readNavState();
const { path, routes} = matchPath(stack, this.routes);
const { path, routes } = matchPath(stack, this.routes);
if (pivot) {
// readNavState() found a pivot that is not initialized
console.debug('[IN] pivot uninitialized -> write partial nav state');
this.writeNavState(pivot, [], routes);
this.writeNavState(pivot, [], routes, 0);
}
const isPop = ev.detail.isPop === true;
this.writePath(path, isPop);
}
private writeNavStateRoot(): Promise<any> {
const node = document.querySelector('ion-app') as HTMLElement;
const currentPath = this.readPath();
const direction = window.history.state >= this.state ? 1 : -1;
if (currentPath) {
return this.writeNavState(node, currentPath, this.routes);
return this.writeNavState(node, currentPath, this.routes, direction);
}
return Promise.resolve();
}
private writeNavState(node: any, path: string[], routes: RouterEntries): Promise<any> {
private writeNavState(node: any, path: string[], routes: RouterEntries, direction: number): Promise<any> {
const chain = matchRouteChain(path, routes);
this.busy = true;
return writeNavState(node, chain)
return writeNavState(node, chain, 0, direction)
.catch(err => console.error(err))
.then(() => this.busy = false);
}
@@ -82,7 +86,7 @@ export class Router {
}
private writePath(path: string[], isPop: boolean) {
writePath(window.history, this.base, this.useHash, path, isPop);
this.state = writePath(window.history, this.base, this.useHash, path, isPop, this.state);
}
private readPath(): string[] | null {

View File

@@ -14,12 +14,17 @@
<ion-router>
<ion-route path="/" sel="tab1"> </ion-route>
<ion-route path="/two" sel="tab2">
<ion-route path="/" sel="page-one"> </ion-route>
<ion-route path="/second-page" sel="page-two"> </ion-route>
<ion-route path="/thee-page" sel="page-three"> </ion-route>
</ion-route>
<ion-route path="/three" sel="tab-three"> </ion-route>
<ion-route path="/four" sel="tab4"> </ion-route>
<ion-route path="/four" sel="tab4">
<ion-route path="/" sel="page-two"> </ion-route>
</ion-route>
</ion-router>
<ion-tabs>
@@ -43,7 +48,7 @@
title="Messages"
icon="chatboxes"
name="tab-four">
INLINE CONTENT
<ion-nav></ion-nav>
</ion-tab>
</ion-tabs>

View File

@@ -8,7 +8,7 @@ import { FrameworkDelegate } from '../..';
})
export class Tab {
// private loaded = false;
private loaded = false;
@Element() el: HTMLElement;
@State() init = false;
@@ -81,36 +81,12 @@ export class Tab {
@Method()
setActive(active: boolean): Promise<any> {
this.active = active;
if (active) {
const nav = getNavAsChildIfExists(this.el);
if (nav) {
// the tab's nav has been initialized externally
return ensureExternalRounterController().then((externalRouterController) => {
if (externalRouterController.getExternalNavPromise()) {
return (externalRouterController.getExternalNavPromise() as Promise<any>).then(() => {
externalRouterController.clearExternalNavPromise();
});
}
// the tab's nav has not been initialized externally, so
// check if we need to initiailize it
return (nav as any).componentOnReady().then(() => {
return nav.onAllTransitionsComplete();
}).then(() => {
if (!nav.getViews().length && !nav.isTransitioning() && !nav.initialized) {
return nav.setRoot(nav.root);
}
return Promise.resolve();
});
});
}
}
/*if (this.loaded || !active) {
if (!active) {
return Promise.resolve();
}
if (this.loaded) {
return this.configChildgNav();
}
this.loaded = true;
let promise: Promise<any>;
@@ -122,9 +98,7 @@ export class Tab {
} else {
promise = Promise.resolve();
}
return promise.then(() => this.fireChildren());
*/
return Promise.resolve();
return promise.then(() => this.configChildgNav());
}
@Method()
@@ -138,16 +112,32 @@ export class Tab {
return null;
}
/*private fireChildren() {
private configChildgNav(): Promise<any|void> {
const nav = getNavAsChildIfExists(this.el);
if (nav && nav.getViews().length === 0 && nav.root) {
// we need to initialize
return nav.setRoot(nav.root);
if (nav) {
// the tab's nav has been initialized externally
return ensureExternalRounterController().then<void|any>((externalRouterController) => {
const externalNavPromise = externalRouterController.getExternalNavPromise();
if (externalNavPromise) {
return externalNavPromise.then(() => {
externalRouterController.clearExternalNavPromise();
});
}
// the tab's nav has not been initialized externally, so
// check if we need to initiailize it
return nav.componentOnReady()
.then(() => nav.onAllTransitionsComplete())
.then<any>(() => {
if (nav.getViews().length === 0 && !nav.isTransitioning() && !nav.initialized) {
return nav.setRoot(nav.root);
}
return Promise.resolve();
});
});
}
// it's already been initialized if it exists
return Promise.resolve();
}
*/
hostData() {
const visible = this.active && this.selected;
@@ -166,7 +156,7 @@ export class Tab {
}
}
/*function attachViewToDom(container: HTMLElement, cmp: string): Promise<any> {
function attachViewToDom(container: HTMLElement, cmp: string): Promise<any> {
const el = document.createElement(cmp) as HTMLStencilElement;
container.appendChild(el);
if (el.componentOnReady) {
@@ -174,4 +164,3 @@ export class Tab {
}
return Promise.resolve();
}
*/

View File

@@ -160,12 +160,12 @@ export class Tabs implements NavOutlet {
}
@Method()
setRouteId(id: any, _: any = {}): Promise<void> {
if (this.selectedTab === id) {
return Promise.resolve();
setRouteId(id: any): Promise<boolean> {
if (this.selectedTab && this.selectedTab.getRouteId() === id) {
return Promise.resolve(false);
}
const tab = this.tabs.find(t => id === t.getRouteId());
return this.select(tab);
return this.select(tab).then(() => true);
}
@@ -185,13 +185,13 @@ export class Tabs implements NavOutlet {
private initTabs() {
const tabs = this.tabs = Array.from(this.el.querySelectorAll('ion-tab'));
const tabPromises: Promise<any>[] = [];
for (const tab of tabs) {
const tabPromises = tabs.map(tab => {
const id = `t-${this.tabsId}-${++this.ids}`;
tab.btnId = 'tab-' + id;
tab.id = 'tabpanel-' + id;
tabPromises.push((tab as any).componentOnReady());
}
return tab.componentOnReady();
});
return Promise.all(tabPromises);
}