mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-18 03:00:58 +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({
|
@Component({
|
||||||
tag: 'ion-app',
|
tag: 'ion-app',
|
||||||
@ -12,8 +25,119 @@ import { Component } from '@stencil/core';
|
|||||||
theme: 'app'
|
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() {
|
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);
|
init(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
ionViewDidLoad() {
|
componentDidLoad() {
|
||||||
ionViewDidLoadImpl(this);
|
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);
|
nav.navInit.emit(nav);
|
||||||
if (nav.root) {
|
if (nav.root) {
|
||||||
nav.setRoot(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);
|
return isReady(nav.navController as any as HTMLElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function navInitializedImpl(nav: Nav, event: CustomEvent) {
|
export function navInitializedImpl(potentialParent: Nav, event: CustomEvent) {
|
||||||
if (nav.element !== event.target) {
|
if (potentialParent.element !== event.target) {
|
||||||
console.log('nav.id is parent of: ', (event as any).detail.id);
|
|
||||||
// set the parent on the child nav that dispatched the event
|
// 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
|
// kill the event so it doesn't propagate further
|
||||||
event.stopPropagation();
|
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>;
|
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 {
|
export interface Nav {
|
||||||
id?: number;
|
id?: number;
|
||||||
element?: HTMLElement;
|
element?: HTMLElement;
|
||||||
@ -32,19 +42,24 @@ export interface Nav {
|
|||||||
mode?: string;
|
mode?: string;
|
||||||
|
|
||||||
// public methods
|
// public methods
|
||||||
getActive(): ViewController;
|
getActive?(): ViewController;
|
||||||
getPrevious(view?: ViewController): ViewController;
|
getPrevious?(view?: ViewController): ViewController;
|
||||||
getViews(): ViewController[];
|
getViews?(): ViewController[];
|
||||||
push(component: any, data?: any, opts?: NavOptions): Promise<any>;
|
push?(component: any, data?: any, opts?: NavOptions): Promise<any>;
|
||||||
pop(opts?: NavOptions): Promise<any>;
|
pop?(opts?: NavOptions): Promise<any>;
|
||||||
setRoot(component: any, data?: any, opts?: NavOptions): Promise<any>;
|
setRoot?(component: any, data?: any, opts?: NavOptions): Promise<any>;
|
||||||
insert(insertIndex: number, page: any, params?: 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>;
|
insertPages?(insertIndex: number, insertPages: any[], opts?: NavOptions): Promise<any>;
|
||||||
popToRoot(opts?: NavOptions): Promise<any>;
|
popToRoot?(opts?: NavOptions): Promise<any>;
|
||||||
popTo(indexOrViewCtrl: any, opts?: NavOptions): Promise<any>;
|
popTo?(indexOrViewCtrl: any, opts?: NavOptions): Promise<any>;
|
||||||
remove(startIndex: number, removeCount?: number, opts?: NavOptions): Promise<any>;
|
remove?(startIndex: number, removeCount?: number, opts?: NavOptions): Promise<any>;
|
||||||
removeView(viewController: ViewController, opts?: NavOptions): Promise<any>;
|
removeView?(viewController: ViewController, opts?: NavOptions): Promise<any>;
|
||||||
setPages(componentDataPairs: ComponentDataPair[], opts? : NavOptions): Promise<any>;
|
setPages?(componentDataPairs: ComponentDataPair[], opts? : NavOptions): Promise<any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OverlayPortal extends Nav {
|
||||||
|
type?: string;
|
||||||
|
registerPortal?: EventEmitter;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NavController {
|
export interface NavController {
|
||||||
@ -63,6 +78,7 @@ export interface NavController {
|
|||||||
animationCtrl?: AnimationController;
|
animationCtrl?: AnimationController;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export interface ViewController {
|
export interface ViewController {
|
||||||
id: string;
|
id: string;
|
||||||
component: any;
|
component: any;
|
||||||
|
@ -160,7 +160,7 @@ export function swipeShouldReset(isResetDirection: boolean, isMovingFast: boolea
|
|||||||
return (!isMovingFast && isOnResetZone) || (isResetDirection && isMovingFast);
|
return (!isMovingFast && isOnResetZone) || (isResetDirection && isMovingFast);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isReady(element: HTMLElement) {
|
export function isReady(element: Element): Promise<any> {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
(element as StencilElement).componentOnReady((elm: HTMLElement) => {
|
(element as StencilElement).componentOnReady((elm: HTMLElement) => {
|
||||||
resolve(elm);
|
resolve(elm);
|
||||||
|
@ -5,7 +5,7 @@ exports.config = {
|
|||||||
generateCollection: true,
|
generateCollection: true,
|
||||||
bundles: [
|
bundles: [
|
||||||
{ components: ['ion-animation-controller'] },
|
{ 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-action-sheet', 'ion-action-sheet-controller'] },
|
||||||
{ components: ['ion-alert', 'ion-alert-controller'] },
|
{ components: ['ion-alert', 'ion-alert-controller'] },
|
||||||
{ components: ['ion-avatar', 'ion-badge', 'ion-thumbnail'] },
|
{ components: ['ion-avatar', 'ion-badge', 'ion-thumbnail'] },
|
||||||
|
Reference in New Issue
Block a user