fix(routing): tabs integration

This commit is contained in:
Manu Mtz.-Almeida
2018-02-08 19:43:29 +01:00
parent bf143fe96a
commit a337bd019c
14 changed files with 356 additions and 51 deletions

View File

@ -0,0 +1,166 @@
/**
* This is an autogenerated file created by the Stencil build process.
* It contains typing information for all components that exist in this project
* and imports for stencil collections that might be configured in your stencil.config.js file
*/
declare global {
interface HTMLStencilElement extends HTMLElement {
componentOnReady(): Promise<this>;
componentOnReady(done: (ele?: this) => void): void;
}
}
import {
PageOne as PageOne
} from './components/page-one/page-one';
declare global {
interface HTMLPageOneElement extends PageOne, HTMLStencilElement {
}
var HTMLPageOneElement: {
prototype: HTMLPageOneElement;
new (): HTMLPageOneElement;
};
interface HTMLElementTagNameMap {
"page-one": HTMLPageOneElement;
}
interface ElementTagNameMap {
"page-one": HTMLPageOneElement;
}
namespace JSX {
interface IntrinsicElements {
"page-one": JSXElements.PageOneAttributes;
}
}
namespace JSXElements {
export interface PageOneAttributes extends HTMLAttributes {
}
}
}
import {
PageTwo as PageTwo
} from './components/page-two/page-two';
declare global {
interface HTMLPageTwoElement extends PageTwo, HTMLStencilElement {
}
var HTMLPageTwoElement: {
prototype: HTMLPageTwoElement;
new (): HTMLPageTwoElement;
};
interface HTMLElementTagNameMap {
"page-two": HTMLPageTwoElement;
}
interface ElementTagNameMap {
"page-two": HTMLPageTwoElement;
}
namespace JSX {
interface IntrinsicElements {
"page-two": JSXElements.PageTwoAttributes;
}
}
namespace JSXElements {
export interface PageTwoAttributes extends HTMLAttributes {
}
}
}
import {
TabOne as TabOne
} from './components/tab-one/tab-one';
declare global {
interface HTMLTabOneElement extends TabOne, HTMLStencilElement {
}
var HTMLTabOneElement: {
prototype: HTMLTabOneElement;
new (): HTMLTabOneElement;
};
interface HTMLElementTagNameMap {
"tab-one": HTMLTabOneElement;
}
interface ElementTagNameMap {
"tab-one": HTMLTabOneElement;
}
namespace JSX {
interface IntrinsicElements {
"tab-one": JSXElements.TabOneAttributes;
}
}
namespace JSXElements {
export interface TabOneAttributes extends HTMLAttributes {
}
}
}
import {
TabThree as TabThree
} from './components/tab-three/tab-three';
declare global {
interface HTMLTabThreeElement extends TabThree, HTMLStencilElement {
}
var HTMLTabThreeElement: {
prototype: HTMLTabThreeElement;
new (): HTMLTabThreeElement;
};
interface HTMLElementTagNameMap {
"tab-three": HTMLTabThreeElement;
}
interface ElementTagNameMap {
"tab-three": HTMLTabThreeElement;
}
namespace JSX {
interface IntrinsicElements {
"tab-three": JSXElements.TabThreeAttributes;
}
}
namespace JSXElements {
export interface TabThreeAttributes extends HTMLAttributes {
}
}
}
import {
TabTwo as TabTwo
} from './components/tab-two/tab-two';
declare global {
interface HTMLTabTwoElement extends TabTwo, HTMLStencilElement {
}
var HTMLTabTwoElement: {
prototype: HTMLTabTwoElement;
new (): HTMLTabTwoElement;
};
interface HTMLElementTagNameMap {
"tab-two": HTMLTabTwoElement;
}
interface ElementTagNameMap {
"tab-two": HTMLTabTwoElement;
}
namespace JSX {
interface IntrinsicElements {
"tab-two": JSXElements.TabTwoAttributes;
}
}
namespace JSXElements {
export interface TabTwoAttributes extends HTMLAttributes {
}
}
}
declare global { namespace JSX { interface StencilJSX {} } }

View File

@ -0,0 +1,22 @@
import { Component } from '@stencil/core';
@Component({
tag: 'tab-one',
})
export class TabOne {
render() {
return [
<ion-page>
<ion-header>
<ion-toolbar>
<ion-title>Page One</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
tab one
</ion-content>
</ion-page>
];
}
}

View File

@ -0,0 +1,22 @@
import { Component } from '@stencil/core';
@Component({
tag: 'tab-three',
})
export class TabThree {
render() {
return [
<ion-page>
<ion-header>
<ion-toolbar>
<ion-title>Tab 3</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
tab three
</ion-content>
</ion-page>
];
}
}

View File

@ -0,0 +1,22 @@
import { Component } from '@stencil/core';
@Component({
tag: 'tab-two',
})
export class TabTwo {
render() {
return [
<ion-page>
<ion-header>
<ion-toolbar>
<ion-title>Tab two (2)</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-nav></ion-nav>
</ion-content>
</ion-page>
];
}
}

View File

@ -2437,9 +2437,9 @@ declare global {
} }
namespace JSXElements { namespace JSXElements {
export interface IonRouteAttributes extends HTMLAttributes { export interface IonRouteAttributes extends HTMLAttributes {
component?: string;
path?: string; path?: string;
props?: any; props?: any;
sel?: string;
} }
} }
} }
@ -3002,7 +3002,7 @@ declare global {
delegate?: FrameworkDelegate; delegate?: FrameworkDelegate;
disabled?: boolean; disabled?: boolean;
icon?: string; icon?: string;
path?: string; name?: string;
selected?: boolean; selected?: boolean;
show?: boolean; show?: boolean;
tabsHideOnSubPages?: boolean; tabsHideOnSubPages?: boolean;

View File

@ -7,11 +7,6 @@
## Properties ## Properties
#### component
string
#### path #### path
string string
@ -22,13 +17,13 @@ string
any any
#### sel
string
## Attributes ## Attributes
#### component
string
#### path #### path
string string
@ -39,6 +34,11 @@ string
any any
#### sel
string
---------------------------------------------- ----------------------------------------------

View File

@ -7,7 +7,7 @@ import { Component, Prop } from '@stencil/core';
export class Route { export class Route {
@Prop() path: string; @Prop() path: string;
@Prop() component: string; @Prop() sel: string;
@Prop() props: any = {}; @Prop() props: any = {};
} }

View File

@ -70,11 +70,11 @@ export function readNavState(node: HTMLElement) {
} }
return { return {
stack: stack, stack: stack,
pivot: pivot pivot: pivot,
}; };
} }
export function matchPath(stack: string[], routes: RouterEntries): string[] { export function matchPath(stack: string[], routes: RouterEntries) {
const path: string[] = []; const path: string[] = [];
for (const id of stack) { for (const id of stack) {
const route = routes.find(r => r.id === id); const route = routes.find(r => r.id === id);
@ -85,7 +85,10 @@ export function matchPath(stack: string[], routes: RouterEntries): string[] {
break; break;
} }
} }
return path; return {
path: path,
routes: routes,
};
} }
export function matchRouteChain(path: string[], routes: RouterEntries): RouterEntries { export function matchRouteChain(path: string[], routes: RouterEntries): RouterEntries {
@ -140,7 +143,7 @@ export function readRoutes(root: Element): RouterEntries {
.filter(el => el.tagName === 'ION-ROUTE') .filter(el => el.tagName === 'ION-ROUTE')
.map(el => ({ .map(el => ({
path: parsePath(el.path), path: parsePath(el.path),
id: el.component, id: el.sel,
props: el.props, props: el.props,
subroutes: readRoutes(el) subroutes: readRoutes(el)
})); }));

View File

@ -46,15 +46,15 @@ export class Router {
console.debug('[IN] nav changed -> update URL'); console.debug('[IN] nav changed -> update URL');
const { stack, pivot } = this.readNavState(); const { stack, pivot } = this.readNavState();
const { path, routes} = matchPath(stack, this.routes);
if (pivot) { if (pivot) {
// readNavState() found a pivot that is not initialized // readNavState() found a pivot that is not initialized
console.debug('[IN] pivot uninitialized -> write partial nav state'); console.debug('[IN] pivot uninitialized -> write partial nav state');
this.writeNavState(pivot, []); this.writeNavState(pivot, [], routes);
} }
const isPop = ev.detail.isPop === true; const isPop = ev.detail.isPop === true;
const segments = matchPath(stack, this.routes); this.writePath(path, isPop);
this.writePath(segments, isPop);
} }
@ -62,13 +62,13 @@ export class Router {
const node = document.querySelector('ion-app') as HTMLElement; const node = document.querySelector('ion-app') as HTMLElement;
const currentPath = this.readPath(); const currentPath = this.readPath();
if (currentPath) { if (currentPath) {
return this.writeNavState(node, currentPath); return this.writeNavState(node, currentPath, this.routes);
} }
return Promise.resolve(); return Promise.resolve();
} }
private writeNavState(node: any, path: string[]): Promise<any> { private writeNavState(node: any, path: string[], routes: RouterEntries): Promise<any> {
const chain = matchRouteChain(path, this.routes); const chain = matchRouteChain(path, routes);
this.busy = true; this.busy = true;
return writeNavState(node, chain) return writeNavState(node, chain)

View File

@ -0,0 +1,70 @@
<!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 src="/test-components/build/app.js"></script>
</head>
<body>
<ion-app>
<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>
<ion-route path="/three" sel="tab-three"> </ion-route>
<ion-route path="/four" sel="tab4"> </ion-route>
</ion-router>
<ion-tabs>
<ion-tab name="tab1"
title="Plain List"
icon="star"
component="tab-one"></ion-tab>
<ion-tab name="tab2"
title="Schedule"
icon="globe"
component="tab-two"></ion-tab>
<ion-tab
title="Stopwatch"
icon="logo-facebook"
component="tab-three"></ion-tab>
<ion-tab name="tab4"
title="Messages"
icon="chatboxes"
name="tab-four">
INLINE CONTENT
</ion-tab>
</ion-tabs>
</ion-app>
<style>
f {
display: block;
margin: 15px auto;
max-width: 150px;
height: 150px;
background: blue;
}
f:last-of-type {
background: yellow;
}
</style>
</body>
<script>
</script>
</html>

View File

@ -97,12 +97,10 @@ string
The icon for the tab button. The icon for the tab button.
#### path #### name
string string
The URL path name to represent this tab within the URL.
#### selected #### selected
@ -180,12 +178,10 @@ string
The icon for the tab button. The icon for the tab button.
#### path #### name
string string
The URL path name to represent this tab within the URL.
#### selected #### selected
@ -223,7 +219,7 @@ Emitted when the current tab is selected.
## Methods ## Methods
#### getPath() #### getRouteId()
#### setActive() #### setActive()

View File

@ -19,11 +19,6 @@ export class Tab {
*/ */
@Prop() btnId: string; @Prop() btnId: string;
/**
* The URL path name to represent this tab within the URL.
*/
@Prop() path: string;
/** /**
* The title of the tab button. * The title of the tab button.
*/ */
@ -43,6 +38,7 @@ export class Tab {
* The badge for the tab button. * The badge for the tab button.
*/ */
@Prop() component: any; @Prop() component: any;
@Prop() name: string;
/** /**
* The badge color for the tab button. * The badge color for the tab button.
@ -102,7 +98,18 @@ export class Tab {
return promise.then(() => this.fireChildren()); return promise.then(() => this.fireChildren());
} }
fireChildren() { @Method()
getRouteId(): string|null {
if (this.name) {
return this.name;
}
if (typeof this.component === 'string') {
return this.component;
}
return null;
}
private fireChildren() {
const nav = getNavAsChildIfExists(this.el); const nav = getNavAsChildIfExists(this.el);
if (nav && nav.getViews().length === 0 && nav.root) { if (nav && nav.getViews().length === 0 && nav.root) {
// we need to initialize // we need to initialize
@ -112,17 +119,6 @@ export class Tab {
return Promise.resolve(); return Promise.resolve();
} }
@Method()
getPath(): string {
if (this.path != null) {
return this.path;
}
if (this.title) {
return this.title.toLowerCase();
}
return '';
}
hostData() { hostData() {
const visible = this.active && this.selected; const visible = this.active && this.selected;
return { return {

View File

@ -217,6 +217,9 @@ Only affects `ios` mode. Defaults to `false`.
Emitted when the tab changes. Emitted when the tab changes.
#### ionNavChanged
## Methods ## Methods
#### getByIndex() #### getByIndex()

View File

@ -1,5 +1,5 @@
import { Component, Element, Event, EventEmitter, Listen, Method, Prop, State } from '@stencil/core'; import { Component, Element, Event, EventEmitter, Listen, Method, Prop, State } from '@stencil/core';
import { Config, NavOutlet } from '../../index'; import { Config, NavOutlet, NavEventDetail } from '../../index';
@Component({ @Component({
@ -66,6 +66,7 @@ export class Tabs implements NavOutlet {
* Emitted when the tab changes. * Emitted when the tab changes.
*/ */
@Event() ionChange: EventEmitter; @Event() ionChange: EventEmitter;
@Event() ionNavChanged: EventEmitter<NavEventDetail>;
componentDidLoad() { componentDidLoad() {
this.loadConfig('tabsPlacement', 'bottom'); this.loadConfig('tabsPlacement', 'bottom');
@ -116,10 +117,10 @@ export class Tabs implements NavOutlet {
return promise.then(() => { return promise.then(() => {
this.ionChange.emit(selectedTab); this.ionChange.emit(selectedTab);
this.ionNavChanged.emit({isPop: false});
}); });
} }
/** /**
* @param {number} index Index of the tab you want to get * @param {number} index Index of the tab you want to get
* @returns {HTMLIonTabElement} Returns the tab who's index matches the one passed * @returns {HTMLIonTabElement} Returns the tab who's index matches the one passed
@ -152,14 +153,15 @@ export class Tabs implements NavOutlet {
if (this.selectedTab === id) { if (this.selectedTab === id) {
return Promise.resolve(); return Promise.resolve();
} }
return this.select(id); const tab = this.tabs.find(t => id === t.getRouteId());
return this.select(tab);
} }
@Method() @Method()
getRouteId(): string|null { getRouteId(): string|null {
if (this.selectedTab) { if (this.selectedTab) {
return this.selectedTab.tagName; return this.selectedTab.getRouteId();
} }
return null; return null;
} }
@ -180,6 +182,9 @@ export class Tabs implements NavOutlet {
} }
private initSelect() { private initSelect() {
if (document.querySelector('ion-router')) {
return;
}
// find pre-selected tabs // find pre-selected tabs
const selectedTab = this.tabs.find(t => t.selected) || const selectedTab = this.tabs.find(t => t.selected) ||
this.tabs.find(t => t.show && !t.disabled); this.tabs.find(t => t.show && !t.disabled);