mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-15 17:42:15 +08:00
fix(routing): tabs integration
This commit is contained in:
166
packages/core/scripts/test-components/src/components.d.ts
vendored
Normal file
166
packages/core/scripts/test-components/src/components.d.ts
vendored
Normal 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 {} } }
|
@ -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>
|
||||
];
|
||||
}
|
||||
}
|
@ -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>
|
||||
];
|
||||
}
|
||||
}
|
@ -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>
|
||||
];
|
||||
}
|
||||
}
|
4
packages/core/src/components.d.ts
vendored
4
packages/core/src/components.d.ts
vendored
@ -2437,9 +2437,9 @@ declare global {
|
||||
}
|
||||
namespace JSXElements {
|
||||
export interface IonRouteAttributes extends HTMLAttributes {
|
||||
component?: string;
|
||||
path?: string;
|
||||
props?: any;
|
||||
sel?: string;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3002,7 +3002,7 @@ declare global {
|
||||
delegate?: FrameworkDelegate;
|
||||
disabled?: boolean;
|
||||
icon?: string;
|
||||
path?: string;
|
||||
name?: string;
|
||||
selected?: boolean;
|
||||
show?: boolean;
|
||||
tabsHideOnSubPages?: boolean;
|
||||
|
@ -7,11 +7,6 @@
|
||||
|
||||
## Properties
|
||||
|
||||
#### component
|
||||
|
||||
string
|
||||
|
||||
|
||||
#### path
|
||||
|
||||
string
|
||||
@ -22,13 +17,13 @@ string
|
||||
any
|
||||
|
||||
|
||||
#### sel
|
||||
|
||||
string
|
||||
|
||||
|
||||
## Attributes
|
||||
|
||||
#### component
|
||||
|
||||
string
|
||||
|
||||
|
||||
#### path
|
||||
|
||||
string
|
||||
@ -39,6 +34,11 @@ string
|
||||
any
|
||||
|
||||
|
||||
#### sel
|
||||
|
||||
string
|
||||
|
||||
|
||||
|
||||
----------------------------------------------
|
||||
|
||||
|
@ -7,7 +7,7 @@ import { Component, Prop } from '@stencil/core';
|
||||
export class Route {
|
||||
|
||||
@Prop() path: string;
|
||||
@Prop() component: string;
|
||||
@Prop() sel: string;
|
||||
@Prop() props: any = {};
|
||||
|
||||
}
|
||||
|
@ -70,11 +70,11 @@ export function readNavState(node: HTMLElement) {
|
||||
}
|
||||
return {
|
||||
stack: stack,
|
||||
pivot: pivot
|
||||
pivot: pivot,
|
||||
};
|
||||
}
|
||||
|
||||
export function matchPath(stack: string[], routes: RouterEntries): string[] {
|
||||
export function matchPath(stack: string[], routes: RouterEntries) {
|
||||
const path: string[] = [];
|
||||
for (const id of stack) {
|
||||
const route = routes.find(r => r.id === id);
|
||||
@ -85,7 +85,10 @@ export function matchPath(stack: string[], routes: RouterEntries): string[] {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return path;
|
||||
return {
|
||||
path: path,
|
||||
routes: routes,
|
||||
};
|
||||
}
|
||||
|
||||
export function matchRouteChain(path: string[], routes: RouterEntries): RouterEntries {
|
||||
@ -140,7 +143,7 @@ export function readRoutes(root: Element): RouterEntries {
|
||||
.filter(el => el.tagName === 'ION-ROUTE')
|
||||
.map(el => ({
|
||||
path: parsePath(el.path),
|
||||
id: el.component,
|
||||
id: el.sel,
|
||||
props: el.props,
|
||||
subroutes: readRoutes(el)
|
||||
}));
|
||||
|
@ -46,15 +46,15 @@ export class Router {
|
||||
|
||||
console.debug('[IN] nav changed -> update URL');
|
||||
const { stack, pivot } = this.readNavState();
|
||||
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, []);
|
||||
this.writeNavState(pivot, [], routes);
|
||||
}
|
||||
|
||||
const isPop = ev.detail.isPop === true;
|
||||
const segments = matchPath(stack, this.routes);
|
||||
this.writePath(segments, isPop);
|
||||
this.writePath(path, isPop);
|
||||
}
|
||||
|
||||
|
||||
@ -62,13 +62,13 @@ export class Router {
|
||||
const node = document.querySelector('ion-app') as HTMLElement;
|
||||
const currentPath = this.readPath();
|
||||
if (currentPath) {
|
||||
return this.writeNavState(node, currentPath);
|
||||
return this.writeNavState(node, currentPath, this.routes);
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
private writeNavState(node: any, path: string[]): Promise<any> {
|
||||
const chain = matchRouteChain(path, this.routes);
|
||||
private writeNavState(node: any, path: string[], routes: RouterEntries): Promise<any> {
|
||||
const chain = matchRouteChain(path, routes);
|
||||
|
||||
this.busy = true;
|
||||
return writeNavState(node, chain)
|
||||
|
70
packages/core/src/components/router/test/basic/index.html
Normal file
70
packages/core/src/components/router/test/basic/index.html
Normal 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>
|
@ -97,12 +97,10 @@ string
|
||||
The icon for the tab button.
|
||||
|
||||
|
||||
#### path
|
||||
#### name
|
||||
|
||||
string
|
||||
|
||||
The URL path name to represent this tab within the URL.
|
||||
|
||||
|
||||
#### selected
|
||||
|
||||
@ -180,12 +178,10 @@ string
|
||||
The icon for the tab button.
|
||||
|
||||
|
||||
#### path
|
||||
#### name
|
||||
|
||||
string
|
||||
|
||||
The URL path name to represent this tab within the URL.
|
||||
|
||||
|
||||
#### selected
|
||||
|
||||
@ -223,7 +219,7 @@ Emitted when the current tab is selected.
|
||||
|
||||
## Methods
|
||||
|
||||
#### getPath()
|
||||
#### getRouteId()
|
||||
|
||||
|
||||
#### setActive()
|
||||
|
@ -19,11 +19,6 @@ export class Tab {
|
||||
*/
|
||||
@Prop() btnId: string;
|
||||
|
||||
/**
|
||||
* The URL path name to represent this tab within the URL.
|
||||
*/
|
||||
@Prop() path: string;
|
||||
|
||||
/**
|
||||
* The title of the tab button.
|
||||
*/
|
||||
@ -43,6 +38,7 @@ export class Tab {
|
||||
* The badge for the tab button.
|
||||
*/
|
||||
@Prop() component: any;
|
||||
@Prop() name: string;
|
||||
|
||||
/**
|
||||
* The badge color for the tab button.
|
||||
@ -102,7 +98,18 @@ export class Tab {
|
||||
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);
|
||||
if (nav && nav.getViews().length === 0 && nav.root) {
|
||||
// we need to initialize
|
||||
@ -112,17 +119,6 @@ export class Tab {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
@Method()
|
||||
getPath(): string {
|
||||
if (this.path != null) {
|
||||
return this.path;
|
||||
}
|
||||
if (this.title) {
|
||||
return this.title.toLowerCase();
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
hostData() {
|
||||
const visible = this.active && this.selected;
|
||||
return {
|
||||
|
@ -217,6 +217,9 @@ Only affects `ios` mode. Defaults to `false`.
|
||||
Emitted when the tab changes.
|
||||
|
||||
|
||||
#### ionNavChanged
|
||||
|
||||
|
||||
## Methods
|
||||
|
||||
#### getByIndex()
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Component, Element, Event, EventEmitter, Listen, Method, Prop, State } from '@stencil/core';
|
||||
import { Config, NavOutlet } from '../../index';
|
||||
import { Config, NavOutlet, NavEventDetail } from '../../index';
|
||||
|
||||
|
||||
@Component({
|
||||
@ -66,6 +66,7 @@ export class Tabs implements NavOutlet {
|
||||
* Emitted when the tab changes.
|
||||
*/
|
||||
@Event() ionChange: EventEmitter;
|
||||
@Event() ionNavChanged: EventEmitter<NavEventDetail>;
|
||||
|
||||
componentDidLoad() {
|
||||
this.loadConfig('tabsPlacement', 'bottom');
|
||||
@ -116,10 +117,10 @@ export class Tabs implements NavOutlet {
|
||||
|
||||
return promise.then(() => {
|
||||
this.ionChange.emit(selectedTab);
|
||||
this.ionNavChanged.emit({isPop: false});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param {number} index Index of the tab you want to get
|
||||
* @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) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return this.select(id);
|
||||
const tab = this.tabs.find(t => id === t.getRouteId());
|
||||
return this.select(tab);
|
||||
}
|
||||
|
||||
|
||||
@Method()
|
||||
getRouteId(): string|null {
|
||||
if (this.selectedTab) {
|
||||
return this.selectedTab.tagName;
|
||||
return this.selectedTab.getRouteId();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -180,6 +182,9 @@ export class Tabs implements NavOutlet {
|
||||
}
|
||||
|
||||
private initSelect() {
|
||||
if (document.querySelector('ion-router')) {
|
||||
return;
|
||||
}
|
||||
// find pre-selected tabs
|
||||
const selectedTab = this.tabs.find(t => t.selected) ||
|
||||
this.tabs.find(t => t.show && !t.disabled);
|
||||
|
Reference in New Issue
Block a user