feature(angular): action-sheet, alert, loading, toast

This commit is contained in:
Dan Bucholtz
2017-12-06 11:48:24 -06:00
committed by GitHub
parent c76b74029a
commit cefbee9ea2
29 changed files with 1301 additions and 3536 deletions

View File

@ -0,0 +1,104 @@
import { Injectable } from '@angular/core';
import { ActionSheetDismissEvent, ActionSheetOptions } from '@ionic/core';
import { ensureElementInBody, hydrateElement } from '../util/util';
let actionSheetId = 0;
@Injectable()
export class ActionSheetController {
create(opts?: ActionSheetOptions): ActionSheetProxy {
return getActionSheetProxy(opts);
}
}
export function getActionSheetProxy(opts: ActionSheetOptions){
return {
id: actionSheetId++,
state: PRESENTING,
opts: opts,
present: function() { return present(this)},
dismiss: function() { return dismiss(this)},
onDidDismiss: function(callback: (data: any, role: string) => void) {
(this as ActionSheetProxyInternal).onDidDismissHandler = callback;
},
onWillDismiss: function(callback: (data: any, role: string) => void) {
(this as ActionSheetProxyInternal).onWillDismissHandler = callback;
},
}
}
export function present(actionSheetProxy: ActionSheetProxyInternal): Promise<any> {
actionSheetProxy.state = PRESENTING;
return loadOverlay(actionSheetProxy.opts).then((actionSheetElement: HTMLIonActionSheetElement) => {
actionSheetProxy.element = actionSheetElement;
const onDidDismissHandler = (event: ActionSheetDismissEvent) => {
actionSheetElement.removeEventListener(ION_ACTION_SHEET_DID_DISMISS_EVENT, onDidDismissHandler);
if (actionSheetProxy.onDidDismissHandler) {
actionSheetProxy.onDidDismissHandler(event.detail.data, event.detail.role);
}
};
const onWillDismissHandler = (event: ActionSheetDismissEvent) => {
actionSheetElement.removeEventListener(ION_ACTION_SHEET_WILL_DISMISS_EVENT, onWillDismissHandler);
if (actionSheetProxy.onWillDismissHandler) {
actionSheetProxy.onWillDismissHandler(event.detail.data, event.detail.role);
}
};
actionSheetElement.addEventListener(ION_ACTION_SHEET_DID_DISMISS_EVENT, onDidDismissHandler);
actionSheetElement.addEventListener(ION_ACTION_SHEET_WILL_DISMISS_EVENT, onWillDismissHandler);
if (actionSheetProxy.state === PRESENTING) {
return actionSheetElement.present();
}
// we'll only ever get here if someone tried to dismiss the overlay or mess with it's internal state
// attribute before it could async load and present itself.
// with that in mind, just return null to make the TS compiler happy
return null;
});
}
export function dismiss(actionSheetProxy: ActionSheetProxyInternal): Promise<any> {
actionSheetProxy.state = DISMISSING;
if (actionSheetProxy.element) {
if (actionSheetProxy.state === DISMISSING) {
return actionSheetProxy.element.dismiss();
}
}
// either we're not in the dismissing state
// or we're calling this before the element is created
// so just return a resolved promise
return Promise.resolve();
}
export function loadOverlay(opts: ActionSheetOptions): Promise<HTMLIonActionSheetElement> {
const element = ensureElementInBody('ion-action-sheet-controller') as HTMLIonActionSheetControllerElement;
return hydrateElement(element).then(() => {
return element.create(opts);
});
}
export interface ActionSheetProxy {
present(): Promise<void>
dismiss(): Promise<void>
onDidDismiss(callback: (data: any, role: string) => void): void;
onWillDismiss(callback: (data: any, role: string) => void): void;
}
export interface ActionSheetProxyInternal extends ActionSheetProxy {
id: number;
opts: ActionSheetOptions;
state: number;
element: HTMLIonActionSheetElement;
onDidDismissHandler?: (data: any, role: string) => void;
onWillDismissHandler?: (data: any, role: string) => void;
}
export const PRESENTING = 1;
export const DISMISSING = 2;
const ION_ACTION_SHEET_DID_DISMISS_EVENT = 'ionActionSheetDidDismiss';
const ION_ACTION_SHEET_WILL_DISMISS_EVENT = 'ionActionSheetWillDismiss';

View File

@ -53,6 +53,11 @@ export function present(alertProxy: AlertProxyInternal): Promise<any> {
if (alertProxy.state === PRESENTING) {
return alertElement.present();
}
// we'll only ever get here if someone tried to dismiss the overlay or mess with it's internal state
// attribute before it could async load and present itself.
// with that in mind, just return null to make the TS compiler happy
return null;
});
}
@ -69,7 +74,7 @@ export function dismiss(alertProxy: AlertProxyInternal): Promise<any> {
return Promise.resolve();
}
export function loadOverlay(opts: AlertOptions) {
export function loadOverlay(opts: AlertOptions): Promise<HTMLIonAlertElement> {
const element = ensureElementInBody('ion-alert-controller') as HTMLIonAlertControllerElement;
return hydrateElement(element).then(() => {
return element.create(opts);

View File

@ -0,0 +1,60 @@
import {
ChangeDetectorRef,
ComponentFactoryResolver,
ComponentRef,
Injectable,
Injector,
NgZone,
ReflectiveInjector,
Type,
ViewContainerRef,
} from '@angular/core';
import { ComponentFactory } from '@angular/core/src/linker/component_factory';
@Injectable()
export class AngularFrameworkDelegate {
constructor(private crf: ComponentFactoryResolver, private zone: NgZone) {
}
attachViewToDom(componentToMount: Type<any>, viewport: ViewContainerRef, providers: any, changeDetection: ChangeDetectorRef): Promise<AngularMountingData> {
return new Promise((resolve) => {
this.zone.run(() => {
const mountingData = attachViewToDom(this.crf, componentToMount, viewport, providers, changeDetection);
resolve(mountingData);
});
});
}
removeViewFromDom(componentRef: ComponentRef<any>): Promise<any> {
return new Promise((resolve) => {
this.zone.run(() => {
componentRef.destroy();
resolve();
});
});
}
}
export function attachViewToDom(crf: ComponentFactoryResolver, componentToMount: Type<any>, viewport: ViewContainerRef, providers: any, changeDetection: ChangeDetectorRef): AngularMountingData{
const componentFactory = crf.resolveComponentFactory(componentToMount);
const componentProviders = ReflectiveInjector.resolve(providers);
const childInjector = ReflectiveInjector.fromResolvedProviders(componentProviders, viewport.parentInjector);
const componentRef = componentFactory.create(childInjector, []);
viewport.insert(componentRef.hostView, viewport.length);
changeDetection.detectChanges();
return {
componentFactory,
childInjector: childInjector,
componentRef: componentRef
}
}
export interface AngularMountingData {
componentFactory: ComponentFactory<any>;
childInjector: Injector;
componentRef: ComponentRef<any>;
}

View File

@ -1,13 +1,8 @@
import { Injectable } from '@angular/core';
import { NavContainer } from '@ionic/core';
import { hydrateElement } from '../util/util';
@Injectable()
export class App {
private element: HTMLIonAppElement;
constructor() {
this.element = document.querySelector('ion-app') as HTMLIonAppElement;
constructor(public _element: HTMLIonAppElement) {
}
setTitle(title: string) {
@ -15,30 +10,46 @@ export class App {
}
isScrolling(): boolean {
if (this.element.isScrolling) {
return this.element.isScrolling();
}
return false;
return isScrollingImpl(this);
}
getRootNavs(): NavContainer[] {
if (this.element.getRootNavs) {
return this.element.getRootNavs();
}
return [];
return getRootNavsImpl(this);
}
getActiveNavs(rootNavId?: number): NavContainer[] {
if (this.element.getActiveNavs) {
return this.element.getActiveNavs(rootNavId);
}
return [];
return getActiveNavsImpl(this, rootNavId);
}
getNavByIdOrName(nameOrId: number | string): NavContainer {
if (this.element.getNavByIdOrName) {
return this.element.getNavByIdOrName(nameOrId);
}
return null;
return getNavByIdOrNameImpl(this, nameOrId);
}
}
export function isScrollingImpl(app: App) {
if (app._element && app._element.isScrolling) {
return app._element.isScrolling();
}
return false;
}
export function getRootNavsImpl(app: App) {
if (app._element && app._element.getRootNavs) {
return app._element.getRootNavs();
}
return [];
}
export function getActiveNavsImpl(app: App, rootNavId?: number): NavContainer[] {
if (app._element && app._element.getActiveNavs) {
return app._element.getActiveNavs(rootNavId);
}
return [];
}
export function getNavByIdOrNameImpl(app: App, nameOrId: number | string): NavContainer {
if (app._element && app._element.getNavByIdOrName) {
return app._element.getNavByIdOrName(nameOrId);
}
return null;
}

View File

@ -0,0 +1,104 @@
import { Injectable } from '@angular/core';
import { LoadingDismissEvent, LoadingOptions } from '@ionic/core';
import { ensureElementInBody, hydrateElement } from '../util/util';
let loadingId = 0;
@Injectable()
export class LoadingController {
create(opts?: LoadingOptions): LoadingProxy {
return getLoadingProxy(opts);
}
}
export function getLoadingProxy(opts: LoadingOptions){
return {
id: loadingId++,
state: PRESENTING,
opts: opts,
present: function() { return present(this)},
dismiss: function() { return dismiss(this)},
onDidDismiss: function(callback: (data: any, role: string) => void) {
(this as LoadingProxyInternal).onDidDismissHandler = callback;
},
onWillDismiss: function(callback: (data: any, role: string) => void) {
(this as LoadingProxyInternal).onWillDismissHandler = callback;
},
}
}
export function present(loadingProxy: LoadingProxyInternal): Promise<any> {
loadingProxy.state = PRESENTING;
return loadOverlay(loadingProxy.opts).then((loadingElement: HTMLIonLoadingElement) => {
loadingProxy.element = loadingElement;
const onDidDismissHandler = (event: LoadingDismissEvent) => {
loadingElement.removeEventListener(ION_LOADING_DID_DISMISS_EVENT, onDidDismissHandler);
if (loadingProxy.onDidDismissHandler) {
loadingProxy.onDidDismissHandler(event.detail.data, event.detail.role);
}
};
const onWillDismissHandler = (event: LoadingDismissEvent) => {
loadingElement.removeEventListener(ION_LOADING_WILL_DISMISS_EVENT, onWillDismissHandler);
if (loadingProxy.onWillDismissHandler) {
loadingProxy.onWillDismissHandler(event.detail.data, event.detail.role);
}
};
loadingElement.addEventListener(ION_LOADING_DID_DISMISS_EVENT, onDidDismissHandler);
loadingElement.addEventListener(ION_LOADING_WILL_DISMISS_EVENT, onWillDismissHandler);
if (loadingProxy.state === PRESENTING) {
return loadingElement.present();
}
// we'll only ever get here if someone tried to dismiss the overlay or mess with it's internal state
// attribute before it could async load and present itself.
// with that in mind, just return null to make the TS compiler happy
return null;
});
}
export function dismiss(loadingProxy: LoadingProxyInternal): Promise<any> {
loadingProxy.state = DISMISSING;
if (loadingProxy.element) {
if (loadingProxy.state === DISMISSING) {
return loadingProxy.element.dismiss();
}
}
// either we're not in the dismissing state
// or we're calling this before the element is created
// so just return a resolved promise
return Promise.resolve();
}
export function loadOverlay(opts: LoadingOptions): Promise<HTMLIonLoadingElement> {
const element = ensureElementInBody('ion-loading-controller') as HTMLIonLoadingControllerElement;
return hydrateElement(element).then(() => {
return element.create(opts);
});
}
export interface LoadingProxy {
present(): Promise<void>
dismiss(): Promise<void>
onDidDismiss(callback: (data: any, role: string) => void): void;
onWillDismiss(callback: (data: any, role: string) => void): void;
}
export interface LoadingProxyInternal extends LoadingProxy {
id: number;
opts: LoadingOptions;
state: number;
element: HTMLIonLoadingElement;
onDidDismissHandler?: (data: any, role: string) => void;
onWillDismissHandler?: (data: any, role: string) => void;
}
export const PRESENTING = 1;
export const DISMISSING = 2;
const ION_LOADING_DID_DISMISS_EVENT = 'ionLoadingDidDismiss';
const ION_LOADING_WILL_DISMISS_EVENT = 'ionLoadingWillDismiss';

View File

@ -1,4 +1,3 @@
import { Injectable } from '@angular/core';
import { NavOptions, PublicNav, PublicViewController } from '@ionic/core';
import { hydrateElement } from '../util/util';

View File

@ -0,0 +1,104 @@
import { Injectable } from '@angular/core';
import { ToastDismissEvent, ToastOptions } from '@ionic/core';
import { ensureElementInBody, hydrateElement } from '../util/util';
let toastId = 0;
@Injectable()
export class ToastController {
create(opts?: ToastOptions): ToastProxy {
return getToastProxy(opts);
}
}
export function getToastProxy(opts: ToastOptions){
return {
id: toastId++,
state: PRESENTING,
opts: opts,
present: function() { return present(this)},
dismiss: function() { return dismiss(this)},
onDidDismiss: function(callback: (data: any, role: string) => void) {
(this as ToastProxyInternal).onDidDismissHandler = callback;
},
onWillDismiss: function(callback: (data: any, role: string) => void) {
(this as ToastProxyInternal).onWillDismissHandler = callback;
},
}
}
export function present(toastProxy: ToastProxyInternal): Promise<any> {
toastProxy.state = PRESENTING;
return loadOverlay(toastProxy.opts).then((toastElement: HTMLIonToastElement) => {
toastProxy.element = toastElement;
const onDidDismissHandler = (event: ToastDismissEvent) => {
toastElement.removeEventListener(ION_TOAST_DID_DISMISS_EVENT, onDidDismissHandler);
if (toastProxy.onDidDismissHandler) {
toastProxy.onDidDismissHandler(event.detail.data, event.detail.role);
}
};
const onWillDismissHandler = (event: ToastDismissEvent) => {
toastElement.removeEventListener(ION_TOAST_WILL_DISMISS_EVENT, onWillDismissHandler);
if (toastProxy.onWillDismissHandler) {
toastProxy.onWillDismissHandler(event.detail.data, event.detail.role);
}
};
toastElement.addEventListener(ION_TOAST_DID_DISMISS_EVENT, onDidDismissHandler);
toastElement.addEventListener(ION_TOAST_WILL_DISMISS_EVENT, onWillDismissHandler);
if (toastProxy.state === PRESENTING) {
return toastElement.present();
}
// we'll only ever get here if someone tried to dismiss the overlay or mess with it's internal state
// attribute before it could async load and present itself.
// with that in mind, just return null to make the TS compiler happy
return null;
});
}
export function dismiss(toastProxy: ToastProxyInternal): Promise<any> {
toastProxy.state = DISMISSING;
if (toastProxy.element) {
if (toastProxy.state === DISMISSING) {
return toastProxy.element.dismiss();
}
}
// either we're not in the dismissing state
// or we're calling this before the element is created
// so just return a resolved promise
return Promise.resolve();
}
export function loadOverlay(opts: ToastOptions): Promise<HTMLIonToastElement> {
const element = ensureElementInBody('ion-toast-controller') as HTMLIonToastControllerElement;
return hydrateElement(element).then(() => {
return element.create(opts);
});
}
export interface ToastProxy {
present(): Promise<void>
dismiss(): Promise<void>
onDidDismiss(callback: (data: any, role: string) => void): void;
onWillDismiss(callback: (data: any, role: string) => void): void;
}
export interface ToastProxyInternal extends ToastProxy {
id: number;
opts: ToastOptions;
state: number;
element: HTMLIonToastElement;
onDidDismissHandler?: (data: any, role: string) => void;
onWillDismissHandler?: (data: any, role: string) => void;
}
export const PRESENTING = 1;
export const DISMISSING = 2;
const ION_TOAST_DID_DISMISS_EVENT = 'ionToastDidDismiss';
const ION_TOAST_WILL_DISMISS_EVENT = 'ionToastWillDismiss';