mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-17 10:41:13 +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,
|
||||
} from './utils/helpers';
|
||||
import {
|
||||
AnimationBuilder as AnimationBuilder2,
|
||||
FrameworkDelegate as FrameworkDelegate2,
|
||||
} from '.';
|
||||
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 {
|
||||
Router as IonRouter
|
||||
} from './components/router/router';
|
||||
|
@ -170,6 +170,35 @@ ion-app,
|
||||
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
|
||||
// --------------------------------------------------
|
||||
|
||||
|
@ -1,7 +1,5 @@
|
||||
@import "../../themes/util";
|
||||
|
||||
$navigation-ios-transition-background: #000 !default;
|
||||
|
||||
ion-nav {
|
||||
@include position(0);
|
||||
|
||||
@ -14,28 +12,3 @@ ion-nav {
|
||||
|
||||
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