mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-17 02:31:34 +08:00
wip nav
This commit is contained in:
4
packages/core/src/components/app/app-constants.ts
Normal file
4
packages/core/src/components/app/app-constants.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export const PORTAL_DEFAULT = 'general';
|
||||
export const PORTAL_LOADING = 'loading';
|
||||
export const PORTAL_MODAL = 'modal';
|
||||
export const PORTAL_TOAST = 'toast';
|
5
packages/core/src/components/app/app-interfaces.d.ts
vendored
Normal file
5
packages/core/src/components/app/app-interfaces.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
import { Config } from '../..';
|
||||
export interface App {
|
||||
element?: HTMLElement;
|
||||
config?: Config;
|
||||
}
|
@ -1,5 +1,18 @@
|
||||
import { Component } from '@stencil/core';
|
||||
import { Element, Component, Listen, Prop } from '@stencil/core';
|
||||
import { Nav, NavContainer, OverlayPortal } from '../../navigation/nav-interfaces';
|
||||
import { Config } from '../..';
|
||||
import { App } from './app-interfaces';
|
||||
import { isReady } from '../../utils/helpers';
|
||||
|
||||
import {
|
||||
PORTAL_DEFAULT,
|
||||
PORTAL_LOADING,
|
||||
PORTAL_MODAL,
|
||||
PORTAL_TOAST
|
||||
} from './app-constants';
|
||||
|
||||
const rootNavs = new Map<number, Nav>();
|
||||
const portals = new Map<string, OverlayPortal>();
|
||||
|
||||
@Component({
|
||||
tag: 'ion-app',
|
||||
@ -12,8 +25,119 @@ import { Component } from '@stencil/core';
|
||||
theme: 'app'
|
||||
}
|
||||
})
|
||||
export class App {
|
||||
export class IonApp implements App {
|
||||
|
||||
@Element() element: HTMLElement;
|
||||
@Prop({ context: 'config' }) config: Config;
|
||||
|
||||
@Listen('body:navInit')
|
||||
registerRootNav(event: CustomEvent) {
|
||||
rootNavs.set((event.detail as Nav).id, (event.detail as Nav));
|
||||
}
|
||||
|
||||
@Listen('body:registerPortal')
|
||||
registerPortal(event: CustomEvent) {
|
||||
portals.set((event.detail as OverlayPortal).type, (event.detail as OverlayPortal));
|
||||
}
|
||||
|
||||
componentWillLoad() {
|
||||
componentDidLoadImpl(this);
|
||||
}
|
||||
|
||||
getActiveNavs(rootNavId?: number): Nav[] {
|
||||
const portal = portals.get(PORTAL_MODAL);
|
||||
if (portal && portal.views && portal.views.length) {
|
||||
return findTopNavs(portal);
|
||||
}
|
||||
if (!rootNavs.size) {
|
||||
return [];
|
||||
}
|
||||
if (rootNavId) {
|
||||
return findTopNavs(rootNavs.get(rootNavId));
|
||||
}
|
||||
if (rootNavs.size === 1) {
|
||||
return findTopNavs(rootNavs.values().next().value);
|
||||
}
|
||||
// fallback to just using all root navs
|
||||
let activeNavs: Nav[] = [];
|
||||
rootNavs.forEach(nav => {
|
||||
activeNavs = activeNavs.concat(findTopNavs(nav));
|
||||
});
|
||||
return activeNavs;
|
||||
}
|
||||
|
||||
getNavByIdOrName(nameOrId: number | string) {
|
||||
const navs = Array.from(rootNavs.values());
|
||||
for (const navContainer of navs) {
|
||||
const match = getNavByIdOrNameImpl(navContainer, nameOrId);
|
||||
if (match) {
|
||||
return match;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
render() {
|
||||
return <slot></slot>;
|
||||
return ([
|
||||
<slot></slot>,
|
||||
<ion-overlay-portal type={PORTAL_MODAL}></ion-overlay-portal>,
|
||||
<ion-overlay-portal type={PORTAL_DEFAULT}></ion-overlay-portal>,
|
||||
<ion-overlay-portal type={PORTAL_LOADING}></ion-overlay-portal>,
|
||||
<ion-overlay-portal type={PORTAL_TOAST}></ion-overlay-portal>,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function findTopNavs(nav: NavContainer): NavContainer[] {
|
||||
let containers: NavContainer[] = [];
|
||||
const childNavs = nav.getActiveChildNavs();
|
||||
if (!childNavs || !childNavs.length) {
|
||||
containers.push(nav);
|
||||
} else {
|
||||
childNavs.forEach(childNav => {
|
||||
const topNavs = findTopNavs(childNav);
|
||||
containers = containers.concat(topNavs);
|
||||
});
|
||||
}
|
||||
return containers;
|
||||
}
|
||||
|
||||
export function getNavByIdOrNameImpl(nav: NavContainer, id: number | string): NavContainer {
|
||||
if (nav.id === id || nav.name === id) {
|
||||
return nav;
|
||||
}
|
||||
for (const child of nav.getAllChildNavs()) {
|
||||
const tmp = getNavByIdOrNameImpl(child, id);
|
||||
if (tmp) {
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function componentDidLoadImpl(app: App) {
|
||||
app.element.classList.add(app.config.get('mode'));
|
||||
// TODO add platform classes
|
||||
if (app.config.getBoolean('hoverCSS', true)) {
|
||||
app.element.classList.add('enable-hover');
|
||||
}
|
||||
// TODO fire platform ready
|
||||
}
|
||||
|
||||
export function handleBackButtonClick(): Promise<any> {
|
||||
// if there is a menu controller dom element, hydrate it, otherwise move on
|
||||
// TODO ensure ion-menu-controller is the name
|
||||
const menuControllerElement = document.querySelector('ion-menu-controller'); // TODO - use menu controller types
|
||||
const promise = menuControllerElement ? isReady(menuControllerElement) : Promise.resolve();
|
||||
return promise.then(() => {
|
||||
// TODO check if the menu is open, close it if so
|
||||
console.log('todo');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -34,8 +34,8 @@ export class IonNav implements Nav {
|
||||
init(this);
|
||||
}
|
||||
|
||||
ionViewDidLoad() {
|
||||
ionViewDidLoadImpl(this);
|
||||
componentDidLoad() {
|
||||
componentDidLoadImpl(this);
|
||||
|
||||
}
|
||||
|
||||
@ -129,7 +129,7 @@ export class IonNav implements Nav {
|
||||
}
|
||||
}
|
||||
|
||||
export function ionViewDidLoadImpl(nav: Nav) {
|
||||
export function componentDidLoadImpl(nav: Nav) {
|
||||
nav.navInit.emit(nav);
|
||||
if (nav.root) {
|
||||
nav.setRoot(nav.root);
|
||||
@ -205,11 +205,14 @@ export function getNavController(nav: Nav): Promise<any> {
|
||||
return isReady(nav.navController as any as HTMLElement);
|
||||
}
|
||||
|
||||
export function navInitializedImpl(nav: Nav, event: CustomEvent) {
|
||||
if (nav.element !== event.target) {
|
||||
console.log('nav.id is parent of: ', (event as any).detail.id);
|
||||
export function navInitializedImpl(potentialParent: Nav, event: CustomEvent) {
|
||||
if (potentialParent.element !== event.target) {
|
||||
// set the parent on the child nav that dispatched the event
|
||||
(event.detail as Nav).parent = nav;
|
||||
(event.detail as Nav).parent = potentialParent;
|
||||
if (!potentialParent.childNavs) {
|
||||
potentialParent.childNavs = [];
|
||||
}
|
||||
potentialParent.childNavs.push((event.detail as Nav));
|
||||
// kill the event so it doesn't propagate further
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
@ -0,0 +1,45 @@
|
||||
|
||||
import { Component, Element, Event, EventEmitter, Prop } from '@stencil/core';
|
||||
import { Nav, NavContainer, OverlayPortal } from '../../navigation/nav-interfaces';
|
||||
|
||||
@Component({
|
||||
tag: 'ion-overlay-portal'
|
||||
})
|
||||
export class IonOverlayPortal implements NavContainer, OverlayPortal {
|
||||
|
||||
id: number;
|
||||
name: string;
|
||||
parent: Nav;
|
||||
|
||||
@Element() element: HTMLElement;
|
||||
@Prop() type: string;
|
||||
@Event() registerPortal: EventEmitter;
|
||||
|
||||
getActiveChildNavs(): NavContainer[] {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
getAllChildNavs?(): NavContainer[] {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
getType(): string {
|
||||
return 'portal';
|
||||
}
|
||||
|
||||
getSecondaryIdentifier(): string {
|
||||
return null;
|
||||
}
|
||||
|
||||
componentWillLoad() {
|
||||
componentWillLoadImpl(this);
|
||||
}
|
||||
|
||||
render() {
|
||||
return <slot></slot>;
|
||||
}
|
||||
}
|
||||
|
||||
export function componentWillLoadImpl(overlayPortal: OverlayPortal) {
|
||||
overlayPortal.registerPortal.emit(overlayPortal);
|
||||
}
|
15
packages/core/src/components/util/util.tsx
Normal file
15
packages/core/src/components/util/util.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
import { Component } from '@stencil/core';
|
||||
|
||||
@Component({
|
||||
tag: 'ion-utils'
|
||||
})
|
||||
export class IonUtils {
|
||||
|
||||
setTitle(newTitle: string): void {
|
||||
if (document.title !== newTitle) {
|
||||
document.title = newTitle;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
42
packages/core/src/navigation/nav-interfaces.d.ts
vendored
42
packages/core/src/navigation/nav-interfaces.d.ts
vendored
@ -12,6 +12,16 @@ export interface FrameworkDelegate {
|
||||
removeViewFromDom(navController: Nav, leavingView: ViewController): Promise<any>;
|
||||
}
|
||||
|
||||
export interface NavContainer {
|
||||
id?: number;
|
||||
name?: string;
|
||||
parent?: Nav;
|
||||
getActiveChildNavs?(): NavContainer[];
|
||||
getAllChildNavs?(): NavContainer[];
|
||||
getType?(): string;
|
||||
getSecondaryIdentifier?(): string;
|
||||
}
|
||||
|
||||
export interface Nav {
|
||||
id?: number;
|
||||
element?: HTMLElement;
|
||||
@ -32,19 +42,24 @@ export interface Nav {
|
||||
mode?: string;
|
||||
|
||||
// public methods
|
||||
getActive(): ViewController;
|
||||
getPrevious(view?: ViewController): ViewController;
|
||||
getViews(): ViewController[];
|
||||
push(component: any, data?: any, opts?: NavOptions): Promise<any>;
|
||||
pop(opts?: NavOptions): Promise<any>;
|
||||
setRoot(component: any, data?: any, opts?: NavOptions): Promise<any>;
|
||||
insert(insertIndex: number, page: any, params?: any, opts?: NavOptions): Promise<any>;
|
||||
insertPages(insertIndex: number, insertPages: any[], opts?: NavOptions): Promise<any>;
|
||||
popToRoot(opts?: NavOptions): Promise<any>;
|
||||
popTo(indexOrViewCtrl: any, opts?: NavOptions): Promise<any>;
|
||||
remove(startIndex: number, removeCount?: number, opts?: NavOptions): Promise<any>;
|
||||
removeView(viewController: ViewController, opts?: NavOptions): Promise<any>;
|
||||
setPages(componentDataPairs: ComponentDataPair[], opts? : NavOptions): Promise<any>;
|
||||
getActive?(): ViewController;
|
||||
getPrevious?(view?: ViewController): ViewController;
|
||||
getViews?(): ViewController[];
|
||||
push?(component: any, data?: any, opts?: NavOptions): Promise<any>;
|
||||
pop?(opts?: NavOptions): Promise<any>;
|
||||
setRoot?(component: any, data?: any, opts?: NavOptions): Promise<any>;
|
||||
insert?(insertIndex: number, page: any, params?: any, opts?: NavOptions): Promise<any>;
|
||||
insertPages?(insertIndex: number, insertPages: any[], opts?: NavOptions): Promise<any>;
|
||||
popToRoot?(opts?: NavOptions): Promise<any>;
|
||||
popTo?(indexOrViewCtrl: any, opts?: NavOptions): Promise<any>;
|
||||
remove?(startIndex: number, removeCount?: number, opts?: NavOptions): Promise<any>;
|
||||
removeView?(viewController: ViewController, opts?: NavOptions): Promise<any>;
|
||||
setPages?(componentDataPairs: ComponentDataPair[], opts? : NavOptions): Promise<any>;
|
||||
}
|
||||
|
||||
export interface OverlayPortal extends Nav {
|
||||
type?: string;
|
||||
registerPortal?: EventEmitter;
|
||||
}
|
||||
|
||||
export interface NavController {
|
||||
@ -63,6 +78,7 @@ export interface NavController {
|
||||
animationCtrl?: AnimationController;
|
||||
}
|
||||
|
||||
|
||||
export interface ViewController {
|
||||
id: string;
|
||||
component: any;
|
||||
|
@ -160,7 +160,7 @@ export function swipeShouldReset(isResetDirection: boolean, isMovingFast: boolea
|
||||
return (!isMovingFast && isOnResetZone) || (isResetDirection && isMovingFast);
|
||||
}
|
||||
|
||||
export function isReady(element: HTMLElement) {
|
||||
export function isReady(element: Element): Promise<any> {
|
||||
return new Promise((resolve) => {
|
||||
(element as StencilElement).componentOnReady((elm: HTMLElement) => {
|
||||
resolve(elm);
|
||||
|
@ -5,7 +5,7 @@ exports.config = {
|
||||
generateCollection: true,
|
||||
bundles: [
|
||||
{ components: ['ion-animation-controller'] },
|
||||
{ components: ['ion-app', 'ion-content', 'ion-fixed', 'ion-footer', 'ion-header', 'ion-navbar', 'ion-page', 'ion-title', 'ion-toolbar'] },
|
||||
{ components: ['ion-app', 'ion-content', 'ion-fixed', 'ion-footer', 'ion-header', 'ion-navbar', 'ion-overlay-portal', 'ion-page', 'ion-title', 'ion-toolbar'] },
|
||||
{ components: ['ion-action-sheet', 'ion-action-sheet-controller'] },
|
||||
{ components: ['ion-alert', 'ion-alert-controller'] },
|
||||
{ components: ['ion-avatar', 'ion-badge', 'ion-thumbnail'] },
|
||||
|
Reference in New Issue
Block a user