mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-20 12:29:55 +08:00
refactor(navigation): make the framework delegate more generic
This commit is contained in:
@ -1,5 +1,8 @@
|
|||||||
import { Directive, ElementRef, HostListener, Renderer2 } from '@angular/core';
|
import { Directive, ElementRef, HostListener, Renderer2 } from '@angular/core';
|
||||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
import {
|
||||||
|
ControlValueAccessor,
|
||||||
|
NG_VALUE_ACCESSOR
|
||||||
|
} from '@angular/forms';
|
||||||
|
|
||||||
@Directive({
|
@Directive({
|
||||||
/* tslint:disable-next-line:directive-selector */
|
/* tslint:disable-next-line:directive-selector */
|
||||||
|
@ -12,5 +12,4 @@ export interface AngularViewController extends PublicViewController {
|
|||||||
componentRef?: ComponentRef<any>;
|
componentRef?: ComponentRef<any>;
|
||||||
instance?: any;
|
instance?: any;
|
||||||
angularHostElement?: HTMLElement;
|
angularHostElement?: HTMLElement;
|
||||||
|
|
||||||
}
|
}
|
6
packages/core/package-lock.json
generated
6
packages/core/package-lock.json
generated
@ -14,9 +14,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@stencil/core": {
|
"@stencil/core": {
|
||||||
"version": "0.0.8-10",
|
"version": "0.0.8-12",
|
||||||
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-0.0.8-10.tgz",
|
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-0.0.8-12.tgz",
|
||||||
"integrity": "sha512-SfZt+0bFaicndqncbWwRIOD1bb4ds3YUjAEYBiouonI392WxKLKHIHo1l2ONgtqQLa8yKrR71ZUatbdfRIXyXw==",
|
"integrity": "sha512-hVHl80opV8ZKa5dCyLZW0V+Fx8ePsTzIjZrQ94+bSsDvi6y3Eo4IK1CEsoG5CxfwZhQQyqE42miWkI9sRZRdsw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"chokidar": "1.7.0",
|
"chokidar": "1.7.0",
|
||||||
|
4
packages/core/src/components.d.ts
vendored
4
packages/core/src/components.d.ts
vendored
@ -1622,8 +1622,8 @@ declare global {
|
|||||||
export interface IonModalAttributes extends HTMLAttributes {
|
export interface IonModalAttributes extends HTMLAttributes {
|
||||||
mode?: string;
|
mode?: string;
|
||||||
color?: string;
|
color?: string;
|
||||||
component?: string;
|
component?: any;
|
||||||
componentProps?: any;
|
data?: any;
|
||||||
cssClass?: string;
|
cssClass?: string;
|
||||||
enableBackdropDismiss?: boolean;
|
enableBackdropDismiss?: boolean;
|
||||||
modalId?: string;
|
modalId?: string;
|
||||||
|
@ -62,8 +62,8 @@ export class Modal {
|
|||||||
@Prop({ context: 'config' }) config: Config;
|
@Prop({ context: 'config' }) config: Config;
|
||||||
@Prop() mode: string;
|
@Prop() mode: string;
|
||||||
@Prop() color: string;
|
@Prop() color: string;
|
||||||
@Prop() component: string;
|
@Prop() component: any;
|
||||||
@Prop() componentProps: any = {};
|
@Prop() data: any = {};
|
||||||
@Prop() cssClass: string;
|
@Prop() cssClass: string;
|
||||||
@Prop() enableBackdropDismiss: boolean = true;
|
@Prop() enableBackdropDismiss: boolean = true;
|
||||||
|
|
||||||
@ -161,15 +161,7 @@ export class Modal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const ThisComponent = this.component;
|
|
||||||
|
|
||||||
let userCssClasses = 'ion-page';
|
|
||||||
if (this.cssClass) {
|
|
||||||
userCssClasses += ` ${this.cssClass}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const dialogClasses = createThemedClasses(this.mode, this.color, 'modal-wrapper');
|
const dialogClasses = createThemedClasses(this.mode, this.color, 'modal-wrapper');
|
||||||
|
|
||||||
return [
|
return [
|
||||||
<div
|
<div
|
||||||
onClick={this.backdropClick.bind(this)}
|
onClick={this.backdropClick.bind(this)}
|
||||||
@ -182,11 +174,6 @@ export class Modal {
|
|||||||
role='dialog'
|
role='dialog'
|
||||||
class={dialogClasses}
|
class={dialogClasses}
|
||||||
>
|
>
|
||||||
<ThisComponent
|
|
||||||
{...this.componentProps}
|
|
||||||
class={userCssClasses}
|
|
||||||
>
|
|
||||||
</ThisComponent>
|
|
||||||
</div>
|
</div>
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@ -194,8 +181,8 @@ export class Modal {
|
|||||||
|
|
||||||
|
|
||||||
export interface ModalOptions {
|
export interface ModalOptions {
|
||||||
component: string;
|
component: any;
|
||||||
componentProps?: any;
|
data?: any;
|
||||||
showBackdrop?: boolean;
|
showBackdrop?: boolean;
|
||||||
enableBackdropDismiss?: boolean;
|
enableBackdropDismiss?: boolean;
|
||||||
enterAnimation?: AnimationBuilder;
|
enterAnimation?: AnimationBuilder;
|
||||||
|
@ -19,11 +19,6 @@ string
|
|||||||
|
|
||||||
#### component
|
#### component
|
||||||
|
|
||||||
string
|
|
||||||
|
|
||||||
|
|
||||||
#### componentProps
|
|
||||||
|
|
||||||
any
|
any
|
||||||
|
|
||||||
|
|
||||||
@ -32,6 +27,11 @@ any
|
|||||||
string
|
string
|
||||||
|
|
||||||
|
|
||||||
|
#### data
|
||||||
|
|
||||||
|
any
|
||||||
|
|
||||||
|
|
||||||
#### enableBackdropDismiss
|
#### enableBackdropDismiss
|
||||||
|
|
||||||
boolean
|
boolean
|
||||||
@ -76,11 +76,6 @@ string
|
|||||||
|
|
||||||
#### component
|
#### component
|
||||||
|
|
||||||
string
|
|
||||||
|
|
||||||
|
|
||||||
#### componentProps
|
|
||||||
|
|
||||||
any
|
any
|
||||||
|
|
||||||
|
|
||||||
@ -89,6 +84,11 @@ any
|
|||||||
string
|
string
|
||||||
|
|
||||||
|
|
||||||
|
#### data
|
||||||
|
|
||||||
|
any
|
||||||
|
|
||||||
|
|
||||||
#### enableBackdropDismiss
|
#### enableBackdropDismiss
|
||||||
|
|
||||||
boolean
|
boolean
|
||||||
|
@ -1,18 +1,28 @@
|
|||||||
import { FrameworkDelegate, Nav, ViewController } from '../../index';
|
import { FrameworkDelegate, FrameworkMountingData, } from '../../index';
|
||||||
|
import { isString } from '../../utils/helpers';
|
||||||
|
|
||||||
export class DomFrameworkDelegate implements FrameworkDelegate {
|
export class DomFrameworkDelegate implements FrameworkDelegate {
|
||||||
attachViewToDom(nav: Nav, enteringView: ViewController): Promise<any> {
|
attachViewToDom(parentElement: HTMLElement, tagOrElement: string | HTMLElement, classesToAdd: string[] = []): Promise<FrameworkMountingData> {
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const usersElement = document.createElement(enteringView.component) as HTMLElement;
|
const usersElement = (isString(tagOrElement) ? document.createElement(tagOrElement) : tagOrElement) as HTMLElement;
|
||||||
usersElement.classList.add('ion-page');
|
if (classesToAdd.length) {
|
||||||
enteringView.element = usersElement;
|
for (const clazz of classesToAdd) {
|
||||||
nav.element.appendChild(usersElement);
|
usersElement.classList.add(clazz);
|
||||||
resolve();
|
}
|
||||||
|
}
|
||||||
|
parentElement.appendChild(usersElement);
|
||||||
|
resolve({
|
||||||
|
element: usersElement
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
removeViewFromDom(nav: Nav, leavingView: ViewController): Promise<any> {
|
removeViewFromDom(parentElement: HTMLElement, childElement: HTMLElement): Promise<FrameworkMountingData> {
|
||||||
nav.element.removeChild(leavingView.element);
|
|
||||||
return Promise.resolve();
|
parentElement.removeChild(childElement);
|
||||||
|
return Promise.resolve({
|
||||||
|
element: null
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,10 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
<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="/dist/ionic.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body onload="loadFirstPage()">
|
||||||
<ion-nav-delegate></ion-nav-delegate>
|
|
||||||
<ion-nav-controller></ion-nav-controller>
|
<ion-nav-controller></ion-nav-controller>
|
||||||
<ion-app>
|
<ion-app>
|
||||||
<ion-nav root="page-one"></ion-nav>
|
<ion-nav></ion-nav>
|
||||||
</ion-app>
|
</ion-app>
|
||||||
<style>
|
<style>
|
||||||
f {
|
f {
|
||||||
@ -26,4 +25,87 @@
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
async function loadFirstPage() {
|
||||||
|
const nav = document.querySelector('ion-nav');
|
||||||
|
await nav.componentOnReady();
|
||||||
|
const firstPage = document.createElement('div');
|
||||||
|
firstPage.classList.add('first-page');
|
||||||
|
firstPage.innerHTML = `
|
||||||
|
<ion-header>
|
||||||
|
<ion-toolbar>
|
||||||
|
<ion-title>Page One</ion-title>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
|
<ion-content padding>
|
||||||
|
<h1>Page One</h1>
|
||||||
|
<ion-button>Go to Page Two</ion-button>
|
||||||
|
</ion-content>
|
||||||
|
`;
|
||||||
|
|
||||||
|
await nav.push(firstPage);
|
||||||
|
|
||||||
|
// okay cool, we're in the DOM now
|
||||||
|
const button = firstPage.querySelector('ion-button');
|
||||||
|
button.addEventListener('click', async () => {
|
||||||
|
await goToPageTwo(nav);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function goToPageTwo(nav) {
|
||||||
|
const secondPage = document.createElement('div');
|
||||||
|
secondPage.classList.add('second-page');
|
||||||
|
secondPage.innerHTML = `
|
||||||
|
<ion-header>
|
||||||
|
<ion-toolbar>
|
||||||
|
<ion-title>Page Two</ion-title>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
|
<ion-content padding>
|
||||||
|
<h1>Page Two</h1>
|
||||||
|
<ion-button class="next">Go to Page Three</ion-button>
|
||||||
|
<ion-button class="previous">Go Back</ion-button>
|
||||||
|
</ion-content>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// okay cool, we're in the DOM now
|
||||||
|
await nav.push(secondPage);
|
||||||
|
|
||||||
|
const previousButton = secondPage.querySelector('ion-button .previous');
|
||||||
|
previousButton.addEventListener('click', async () => {
|
||||||
|
await nav.pop();
|
||||||
|
});
|
||||||
|
|
||||||
|
const nextButton = secondPage.querySelector('ion-button .next');
|
||||||
|
nextButton.addEventListener('click', async () => {
|
||||||
|
await goToPageThree(nav);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function goToPageThree(nav) {
|
||||||
|
const thirdPage = document.createElement('div');
|
||||||
|
thirdPage.classList.add('third-page');
|
||||||
|
thirdPage.innerHTML = `
|
||||||
|
<ion-header>
|
||||||
|
<ion-toolbar>
|
||||||
|
<ion-title>Page Three</ion-title>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
|
<ion-content padding>
|
||||||
|
<h1>Page Three</h1>
|
||||||
|
<ion-button class="previous">Go Back</ion-button>
|
||||||
|
</ion-content>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// okay cool, we're in the DOM now
|
||||||
|
await nav.push(thirdPage);
|
||||||
|
|
||||||
|
const previousButton = thirdPage.querySelector('ion-button .previous');
|
||||||
|
previousButton.addEventListener('click', async () => {
|
||||||
|
await nav.pop();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
</html>
|
</html>
|
@ -446,7 +446,8 @@ export function fireViewWillLifecycles(enteringView: ViewController, leavingView
|
|||||||
|
|
||||||
export function attachViewToDom(nav: Nav, enteringView: ViewController, delegate: FrameworkDelegate) {
|
export function attachViewToDom(nav: Nav, enteringView: ViewController, delegate: FrameworkDelegate) {
|
||||||
if (enteringView && enteringView.state === STATE_NEW) {
|
if (enteringView && enteringView.state === STATE_NEW) {
|
||||||
return delegate.attachViewToDom(nav, enteringView).then(() => {
|
return delegate.attachViewToDom(nav.element, enteringView.component, ['ion-page']).then((mountingData) => {
|
||||||
|
Object.assign(enteringView, mountingData);
|
||||||
enteringView.state = STATE_ATTACHED;
|
enteringView.state = STATE_ATTACHED;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,13 @@ import {
|
|||||||
} from '../index';
|
} from '../index';
|
||||||
|
|
||||||
export interface FrameworkDelegate {
|
export interface FrameworkDelegate {
|
||||||
attachViewToDom(navController: Nav, enteringView: ViewController): Promise<any>;
|
|
||||||
removeViewFromDom(navController: Nav, leavingView: ViewController): Promise<any>;
|
attachViewToDom(elementOrContainerToMountTo: any, elementOrComponentToMount: any, classesToAdd?: string[]): Promise<FrameworkMountingData>;
|
||||||
|
removeViewFromDom(elementOrContainerToUnmountFrom: any, elementOrComponentToUnmount: any): Promise<FrameworkMountingData>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FrameworkMountingData {
|
||||||
|
element: HTMLElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NavContainer {
|
export interface NavContainer {
|
||||||
|
@ -99,7 +99,7 @@ export function dismiss(navCtrl: any, dismissProxy: any, data?: any, role?: stri
|
|||||||
|
|
||||||
export function destroy(viewController: ViewController, delegate?: FrameworkDelegate): Promise<any> {
|
export function destroy(viewController: ViewController, delegate?: FrameworkDelegate): Promise<any> {
|
||||||
assert(viewController.state !== STATE_DESTROYED, 'view state must be attached');
|
assert(viewController.state !== STATE_DESTROYED, 'view state must be attached');
|
||||||
return delegate ? delegate.removeViewFromDom(viewController.nav, viewController) : Promise.resolve().then(() => {
|
return delegate ? delegate.removeViewFromDom(viewController.nav.element, viewController.element) : Promise.resolve().then(() => {
|
||||||
|
|
||||||
if (viewController.component) {
|
if (viewController.component) {
|
||||||
// TODO - consider removing classes and styles as thats what we do in ionic-angular
|
// TODO - consider removing classes and styles as thats what we do in ionic-angular
|
||||||
|
2
packages/demos/angular/package-lock.json
generated
2
packages/demos/angular/package-lock.json
generated
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "demo",
|
"name": "@ionic/angular-demo",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
|
Reference in New Issue
Block a user