mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-17 18:54:11 +08:00
feat(ion-router-outlet): adds router-outlet
This commit is contained in:
33
core/src/components.d.ts
vendored
33
core/src/components.d.ts
vendored
@ -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';
|
||||||
|
@ -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
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
|
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
|
59
core/src/components/router-outlet/readme.md
Normal file
59
core/src/components/router-outlet/readme.md
Normal 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/)*
|
125
core/src/components/router-outlet/route-outlet.tsx
Normal file
125
core/src/components/router-outlet/route-outlet.tsx
Normal 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};
|
78
core/src/components/router-outlet/test/basic/index.html
Normal file
78
core/src/components/router-outlet/test/basic/index.html
Normal 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>
|
Reference in New Issue
Block a user