mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-16 01:52:19 +08:00
refactor(modal): use framework delegate for mounting the user's component
This commit is contained in:
@ -1,65 +1,33 @@
|
|||||||
import {
|
import {
|
||||||
ChangeDetectorRef,
|
|
||||||
Component,
|
|
||||||
ComponentFactoryResolver,
|
ComponentFactoryResolver,
|
||||||
ComponentRef,
|
Directive,
|
||||||
ElementRef,
|
ElementRef,
|
||||||
Injector,
|
Injector,
|
||||||
ReflectiveInjector,
|
|
||||||
Type,
|
Type,
|
||||||
ViewContainerRef,
|
|
||||||
ViewChild
|
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
|
||||||
import { FrameworkDelegate } from '@ionic/core';
|
import { FrameworkDelegate } from '@ionic/core';
|
||||||
|
|
||||||
import { getProviders } from '../di/di';
|
|
||||||
import { AngularComponentMounter } from '../providers/angular-component-mounter';
|
import { AngularComponentMounter } from '../providers/angular-component-mounter';
|
||||||
import { AngularMountingData } from '../types/interfaces';
|
import { AngularMountingData } from '../types/interfaces';
|
||||||
|
|
||||||
const elementToComponentRefMap = new Map<HTMLElement, ComponentRef<any>>();
|
@Directive({
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'ion-nav',
|
selector: 'ion-nav',
|
||||||
template: `
|
|
||||||
<div #viewport class="ng-nav-viewport"></div>
|
|
||||||
`
|
|
||||||
})
|
})
|
||||||
export class IonNavDelegate implements FrameworkDelegate {
|
export class IonNavDelegate implements FrameworkDelegate {
|
||||||
|
|
||||||
@ViewChild('viewport', { read: ViewContainerRef}) viewport: ViewContainerRef;
|
constructor(private elementRef: ElementRef, private angularComponentMounter: AngularComponentMounter, private componentResolveFactory: ComponentFactoryResolver, private injector: Injector) {
|
||||||
|
|
||||||
constructor(private elementRef: ElementRef, private changeDetection: ChangeDetectorRef, private angularComponentMounter: AngularComponentMounter, private injector: Injector, private componentResolveFactory: ComponentFactoryResolver) {
|
|
||||||
this.elementRef.nativeElement.delegate = this;
|
this.elementRef.nativeElement.delegate = this;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async attachViewToDom(elementOrContainerToMountTo: HTMLIonNavElement, elementOrComponentToMount: Type<any>,
|
attachViewToDom(elementOrContainerToMountTo: HTMLIonNavElement, elementOrComponentToMount: Type<any>, _propsOrDataObj?: any, classesToAdd?: string[]): Promise<AngularMountingData> {
|
||||||
_propsOrDataObj?: any, _classesToAdd?: string[]): Promise<AngularMountingData> {
|
|
||||||
|
|
||||||
const componentProviders = ReflectiveInjector.resolve(getProviders(elementOrContainerToMountTo));
|
const hostElement = document.createElement('div');
|
||||||
console.log('componentProviders: ', componentProviders);
|
return this.angularComponentMounter.attachViewToDom(elementOrContainerToMountTo, hostElement, elementOrComponentToMount, this.componentResolveFactory, this.injector, classesToAdd);
|
||||||
|
|
||||||
const element = document.createElement('ion-page');
|
|
||||||
for (const clazz of _classesToAdd) {
|
|
||||||
element.classList.add(clazz);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
elementOrContainerToMountTo.appendChild(element);
|
removeViewFromDom(_parentElement: HTMLElement, childElement: HTMLElement) {
|
||||||
const mountingData = await this.angularComponentMounter.attachViewToDom(element, elementOrComponentToMount, [], this.changeDetection, this.componentResolveFactory, this.injector);
|
return this.angularComponentMounter.removeViewFromDom(childElement);
|
||||||
mountingData.element = element;
|
|
||||||
|
|
||||||
elementToComponentRefMap.set(mountingData.angularHostElement, mountingData.componentRef);
|
|
||||||
|
|
||||||
return mountingData;
|
|
||||||
}
|
|
||||||
|
|
||||||
async removeViewFromDom(_parentElement: HTMLElement, childElement: HTMLElement) {
|
|
||||||
const componentRef = elementToComponentRefMap.get(childElement);
|
|
||||||
if (componentRef) {
|
|
||||||
return this.angularComponentMounter.removeViewFromDom(componentRef);
|
|
||||||
}
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,10 +8,16 @@ export const NavControllerToken = new InjectionToken<any>('NavControllerToken');
|
|||||||
export const ViewControllerToken = new InjectionToken<any>('ViewControllerToken');
|
export const ViewControllerToken = new InjectionToken<any>('ViewControllerToken');
|
||||||
export const AppToken = new InjectionToken<any>('AppToken');
|
export const AppToken = new InjectionToken<any>('AppToken');
|
||||||
|
|
||||||
export function getProviders(element: HTMLIonNavElement) {
|
export function getProviders(element: HTMLElement) {
|
||||||
|
if (element.tagName !== 'ion-nav') {
|
||||||
|
element.closest('ion-nav');
|
||||||
|
}
|
||||||
|
|
||||||
|
const nearestNavElement = (element.tagName.toLowerCase() === 'ion-nav' ? element : element.closest('ion-nav')) as HTMLIonNavElement;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
provide: NavControllerToken, useValue: element
|
provide: NavControllerToken, useValue: nearestNavElement
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
export { IonNavDelegate } from './components/ion-nav';
|
|
||||||
export { IonicAngularModule } from './module';
|
export { IonicAngularModule } from './module';
|
||||||
|
|
||||||
|
/* Directives/Components */
|
||||||
|
export { IonNavDelegate } from './components/ion-nav';
|
||||||
|
|
||||||
|
/* Providers */
|
||||||
export { ActionSheetController, ActionSheetProxy } from './providers/action-sheet-controller';
|
export { ActionSheetController, ActionSheetProxy } from './providers/action-sheet-controller';
|
||||||
export { AlertController, AlertProxy } from './providers/alert-controller';
|
export { AlertController, AlertProxy } from './providers/alert-controller';
|
||||||
export { App } from './providers/app';
|
export { App } from './providers/app';
|
||||||
export { LoadingController, LoadingProxy } from './providers/loading-controller';
|
export { LoadingController, LoadingProxy } from './providers/loading-controller';
|
||||||
|
export { ModalController, ModalProxy } from './providers/modal-controller';
|
||||||
export { NavController } from './providers/nav-controller';
|
export { NavController } from './providers/nav-controller';
|
||||||
export { ToastController, ToastProxy } from './providers/toast-controller';
|
export { ToastController, ToastProxy } from './providers/toast-controller';
|
@ -1,3 +1,4 @@
|
|||||||
|
import { CommonModule } from '@angular/common';
|
||||||
import {
|
import {
|
||||||
ModuleWithProviders,
|
ModuleWithProviders,
|
||||||
NgModule,
|
NgModule,
|
||||||
@ -11,6 +12,7 @@ import { SelectValueAccessor } from './control-value-accessors/select-value-acce
|
|||||||
import { TextValueAccessor } from './control-value-accessors/text-value-accessor';
|
import { TextValueAccessor } from './control-value-accessors/text-value-accessor';
|
||||||
|
|
||||||
|
|
||||||
|
/* Components */
|
||||||
import { IonNavDelegate } from './components/ion-nav';
|
import { IonNavDelegate } from './components/ion-nav';
|
||||||
|
|
||||||
/* Providers */
|
/* Providers */
|
||||||
@ -18,6 +20,7 @@ import { ActionSheetController } from './providers/action-sheet-controller';
|
|||||||
import { AlertController } from './providers/alert-controller';
|
import { AlertController } from './providers/alert-controller';
|
||||||
import { AngularComponentMounter } from './providers/angular-component-mounter';
|
import { AngularComponentMounter } from './providers/angular-component-mounter';
|
||||||
import { LoadingController } from './providers/loading-controller';
|
import { LoadingController } from './providers/loading-controller';
|
||||||
|
import { ModalController } from './providers/modal-controller';
|
||||||
import { ToastController } from './providers/toast-controller';
|
import { ToastController } from './providers/toast-controller';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
@ -37,6 +40,9 @@ import { ToastController } from './providers/toast-controller';
|
|||||||
SelectValueAccessor,
|
SelectValueAccessor,
|
||||||
TextValueAccessor
|
TextValueAccessor
|
||||||
],
|
],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
],
|
||||||
schemas: [
|
schemas: [
|
||||||
CUSTOM_ELEMENTS_SCHEMA
|
CUSTOM_ELEMENTS_SCHEMA
|
||||||
],
|
],
|
||||||
@ -50,6 +56,7 @@ export class IonicAngularModule {
|
|||||||
ActionSheetController,
|
ActionSheetController,
|
||||||
AngularComponentMounter,
|
AngularComponentMounter,
|
||||||
LoadingController,
|
LoadingController,
|
||||||
|
ModalController,
|
||||||
ToastController
|
ToastController
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
@ -1,51 +1,70 @@
|
|||||||
import {
|
import {
|
||||||
ChangeDetectorRef,
|
ApplicationRef,
|
||||||
ComponentFactoryResolver,
|
ComponentFactoryResolver,
|
||||||
ComponentRef,
|
ComponentRef,
|
||||||
Injectable,
|
Injectable,
|
||||||
Injector,
|
Injector,
|
||||||
NgZone,
|
NgZone,
|
||||||
ReflectiveInjector,
|
ReflectiveInjector,
|
||||||
Type,
|
Type
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
|
||||||
|
import { getProviders } from '../di/di';
|
||||||
import { AngularMountingData } from '../types/interfaces';
|
import { AngularMountingData } from '../types/interfaces';
|
||||||
|
|
||||||
|
const elementToComponentRefMap = new Map<HTMLElement, ComponentRef<any>>();
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AngularComponentMounter {
|
export class AngularComponentMounter {
|
||||||
|
|
||||||
constructor(private defaultCfr: ComponentFactoryResolver, private zone: NgZone) {
|
constructor(private defaultCfr: ComponentFactoryResolver, private zone: NgZone, private appRef: ApplicationRef) {
|
||||||
}
|
}
|
||||||
|
|
||||||
attachViewToDom(parentElement: HTMLElement, componentToMount: Type<any>, providers: any[], changeDetection: ChangeDetectorRef, componentResolveFactory: ComponentFactoryResolver, injector: Injector): Promise<AngularMountingData> {
|
attachViewToDom(parentElement: HTMLElement, hostElement: HTMLElement, componentToMount: Type<any>, componentResolveFactory: ComponentFactoryResolver, injector: Injector, classesToAdd: string[]): Promise<AngularMountingData> {
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
this.zone.run(() => {
|
this.zone.run(() => {
|
||||||
console.log('parentElement: ', parentElement);
|
|
||||||
const crf = componentResolveFactory ? componentResolveFactory : this.defaultCfr;
|
const crf = componentResolveFactory ? componentResolveFactory : this.defaultCfr;
|
||||||
const mountingData = attachViewToDom(crf, componentToMount, parentElement, providers, changeDetection, injector);
|
|
||||||
|
const mountingData = attachViewToDom(crf, parentElement, hostElement, componentToMount, injector, this.appRef, classesToAdd);
|
||||||
resolve(mountingData);
|
resolve(mountingData);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
removeViewFromDom(componentRef: ComponentRef<any>): Promise<any> {
|
removeViewFromDom(childElement: HTMLElement): Promise<any> {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
this.zone.run(() => {
|
this.zone.run(() => {
|
||||||
componentRef.destroy();
|
removeViewFromDom(childElement);
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function attachViewToDom(crf: ComponentFactoryResolver, componentToMount: Type<any>, element: HTMLElement, providers: any, changeDetection: ChangeDetectorRef, injector: Injector): AngularMountingData {
|
export function removeViewFromDom(childElement: HTMLElement) {
|
||||||
const componentFactory = crf.resolveComponentFactory(componentToMount);
|
const componentRef = elementToComponentRefMap.get(childElement);
|
||||||
const componentProviders = ReflectiveInjector.resolve(providers);
|
if (componentRef) {
|
||||||
const childInjector = ReflectiveInjector.fromResolvedProviders(componentProviders, injector);
|
componentRef.destroy();
|
||||||
const componentRef = componentFactory.create(childInjector, [], element);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
changeDetection.detectChanges();
|
export function attachViewToDom(crf: ComponentFactoryResolver, parentElement: HTMLElement, hostElement: HTMLElement, componentToMount: Type<any>, injector: Injector, appRef: ApplicationRef, classesToAdd: string[]): AngularMountingData {
|
||||||
|
|
||||||
|
const componentProviders = ReflectiveInjector.resolve(getProviders(parentElement));
|
||||||
|
const componentFactory = crf.resolveComponentFactory(componentToMount);
|
||||||
|
const childInjector = ReflectiveInjector.fromResolvedProviders(componentProviders, injector);
|
||||||
|
const componentRef = componentFactory.create(childInjector, [], hostElement);
|
||||||
|
for (const clazz of classesToAdd) {
|
||||||
|
hostElement.classList.add(clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
parentElement.appendChild(hostElement);
|
||||||
|
|
||||||
|
appRef.attachView(componentRef.hostView);
|
||||||
|
|
||||||
|
elementToComponentRefMap.set(hostElement, componentRef);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
componentFactory,
|
componentFactory,
|
||||||
@ -53,7 +72,7 @@ export function attachViewToDom(crf: ComponentFactoryResolver, componentToMount:
|
|||||||
componentRef: componentRef,
|
componentRef: componentRef,
|
||||||
instance: componentRef.instance,
|
instance: componentRef.instance,
|
||||||
angularHostElement: componentRef.location.nativeElement,
|
angularHostElement: componentRef.location.nativeElement,
|
||||||
element: componentRef.location.nativeElement,
|
element: hostElement,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
134
packages/angular/src/providers/modal-controller.ts
Normal file
134
packages/angular/src/providers/modal-controller.ts
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
import {
|
||||||
|
ComponentFactoryResolver,
|
||||||
|
Injectable,
|
||||||
|
Injector,
|
||||||
|
Type,
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
|
import {
|
||||||
|
FrameworkDelegate,
|
||||||
|
ModalDismissEvent,
|
||||||
|
ModalOptions
|
||||||
|
} from '@ionic/core';
|
||||||
|
|
||||||
|
import { AngularComponentMounter } from '../providers/angular-component-mounter';
|
||||||
|
import { AngularMountingData } from '../types/interfaces';
|
||||||
|
|
||||||
|
import { ensureElementInBody, hydrateElement } from '../util/util';
|
||||||
|
|
||||||
|
let modalId = 0;
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ModalController implements FrameworkDelegate {
|
||||||
|
|
||||||
|
constructor(private angularComponentMounter: AngularComponentMounter, private componentResolveFactory: ComponentFactoryResolver, private injector: Injector) {
|
||||||
|
}
|
||||||
|
|
||||||
|
create(opts?: ModalOptions): ModalProxy {
|
||||||
|
opts.delegate = this;
|
||||||
|
return getModalProxy(opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
attachViewToDom(elementOrContainerToMountTo: HTMLElement, elementOrComponentToMount: Type<any>, _propsOrDataObj?: any, classesToAdd?: string[]): Promise<AngularMountingData> {
|
||||||
|
|
||||||
|
const hostElement = document.createElement('div');
|
||||||
|
return this.angularComponentMounter.attachViewToDom(elementOrContainerToMountTo, hostElement, elementOrComponentToMount, this.componentResolveFactory, this.injector, classesToAdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeViewFromDom(_parentElement: HTMLElement, childElement: HTMLElement) {
|
||||||
|
return this.angularComponentMounter.removeViewFromDom(childElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getModalProxy(opts: ModalOptions) {
|
||||||
|
return {
|
||||||
|
id: modalId++,
|
||||||
|
state: PRESENTING,
|
||||||
|
opts: opts,
|
||||||
|
present: function() { return present(this); },
|
||||||
|
dismiss: function() { return dismiss(this); },
|
||||||
|
onDidDismiss: function(callback: (data: any, role: string) => void) {
|
||||||
|
(this as ModalProxyInternal).onDidDismissHandler = callback;
|
||||||
|
},
|
||||||
|
onWillDismiss: function(callback: (data: any, role: string) => void) {
|
||||||
|
(this as ModalProxyInternal).onWillDismissHandler = callback;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function present(modalProxy: ModalProxyInternal): Promise<any> {
|
||||||
|
modalProxy.state = PRESENTING;
|
||||||
|
return loadOverlay(modalProxy.opts).then((modalElement: HTMLIonModalElement) => {
|
||||||
|
Object.assign(modalElement, modalProxy.opts);
|
||||||
|
modalProxy.element = modalElement;
|
||||||
|
|
||||||
|
const onDidDismissHandler = (event: ModalDismissEvent) => {
|
||||||
|
modalElement.removeEventListener(ION_MODAL_DID_DISMISS_EVENT, onDidDismissHandler);
|
||||||
|
if (modalProxy.onDidDismissHandler) {
|
||||||
|
modalProxy.onDidDismissHandler(event.detail.data, event.detail.role);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onWillDismissHandler = (event: ModalDismissEvent) => {
|
||||||
|
modalElement.removeEventListener(ION_MODAL_WILL_DISMISS_EVENT, onWillDismissHandler);
|
||||||
|
if (modalProxy.onWillDismissHandler) {
|
||||||
|
modalProxy.onWillDismissHandler(event.detail.data, event.detail.role);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
modalElement.addEventListener(ION_MODAL_DID_DISMISS_EVENT, onDidDismissHandler);
|
||||||
|
modalElement.addEventListener(ION_MODAL_WILL_DISMISS_EVENT, onWillDismissHandler);
|
||||||
|
|
||||||
|
if (modalProxy.state === PRESENTING) {
|
||||||
|
return modalElement.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(modalProxy: ModalProxyInternal): Promise<any> {
|
||||||
|
modalProxy.state = DISMISSING;
|
||||||
|
if (modalProxy.element) {
|
||||||
|
if (modalProxy.state === DISMISSING) {
|
||||||
|
return modalProxy.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: ModalOptions): Promise<HTMLIonModalElement> {
|
||||||
|
const element = ensureElementInBody('ion-modal-controller') as HTMLIonModalControllerElement;
|
||||||
|
return hydrateElement(element).then(() => {
|
||||||
|
return element.create(opts);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ModalProxy {
|
||||||
|
present(): Promise<void>;
|
||||||
|
dismiss(): Promise<void>;
|
||||||
|
onDidDismiss(callback: (data: any, role: string) => void): void;
|
||||||
|
onWillDismiss(callback: (data: any, role: string) => void): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ModalProxyInternal extends ModalProxy {
|
||||||
|
id: number;
|
||||||
|
opts: ModalOptions;
|
||||||
|
state: number;
|
||||||
|
element: HTMLIonModalElement;
|
||||||
|
onDidDismissHandler?: (data: any, role: string) => void;
|
||||||
|
onWillDismissHandler?: (data: any, role: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PRESENTING = 1;
|
||||||
|
export const DISMISSING = 2;
|
||||||
|
|
||||||
|
const ION_MODAL_DID_DISMISS_EVENT = 'ionModalDidDismiss';
|
||||||
|
const ION_MODAL_WILL_DISMISS_EVENT = 'ionModalWillDismiss';
|
||||||
|
|
1
packages/core/src/components.d.ts
vendored
1
packages/core/src/components.d.ts
vendored
@ -1630,6 +1630,7 @@ declare global {
|
|||||||
enterAnimation?: AnimationBuilder;
|
enterAnimation?: AnimationBuilder;
|
||||||
leaveAnimation?: AnimationBuilder;
|
leaveAnimation?: AnimationBuilder;
|
||||||
animate?: boolean;
|
animate?: boolean;
|
||||||
|
delegate?: FrameworkDelegate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,6 @@ export class ActionSheetController {
|
|||||||
|
|
||||||
// give this action sheet a unique id
|
// give this action sheet a unique id
|
||||||
actionSheet.actionSheetId = `action-sheet-${id}`;
|
actionSheet.actionSheetId = `action-sheet-${id}`;
|
||||||
actionSheet.style.zIndex = (20000 + id).toString();
|
|
||||||
|
|
||||||
// convert the passed in action sheet options into props
|
// convert the passed in action sheet options into props
|
||||||
// that get passed down into the new action sheet
|
// that get passed down into the new action sheet
|
||||||
|
@ -125,6 +125,8 @@ export class ActionSheet {
|
|||||||
}
|
}
|
||||||
this.ionActionSheetWillPresent.emit();
|
this.ionActionSheetWillPresent.emit();
|
||||||
|
|
||||||
|
this.el.style.zIndex = `${20000 + this.actionSheetId}`;
|
||||||
|
|
||||||
// get the user's animation fn if one was provided
|
// get the user's animation fn if one was provided
|
||||||
const animationBuilder = this.enterAnimation || this.config.get('actionSheetEnter', this.mode === 'ios' ? iosEnterAnimation : mdEnterAnimation);
|
const animationBuilder = this.enterAnimation || this.config.get('actionSheetEnter', this.mode === 'ios' ? iosEnterAnimation : mdEnterAnimation);
|
||||||
|
|
||||||
|
@ -23,7 +23,6 @@ export class AlertController {
|
|||||||
|
|
||||||
// give this action sheet a unique id
|
// give this action sheet a unique id
|
||||||
alert.alertId = `alert-${id}`;
|
alert.alertId = `alert-${id}`;
|
||||||
alert.style.zIndex = (20000 + id).toString();
|
|
||||||
|
|
||||||
// convert the passed in action sheet options into props
|
// convert the passed in action sheet options into props
|
||||||
// that get passed down into the new action sheet
|
// that get passed down into the new action sheet
|
||||||
|
@ -131,6 +131,8 @@ export class Alert {
|
|||||||
}
|
}
|
||||||
this.ionAlertWillPresent.emit();
|
this.ionAlertWillPresent.emit();
|
||||||
|
|
||||||
|
this.el.style.zIndex = `${20000 + this.alertId}`;
|
||||||
|
|
||||||
// get the user's animation fn if one was provided
|
// get the user's animation fn if one was provided
|
||||||
const animationBuilder = this.enterAnimation || this.config.get('alertEnter', this.mode === 'ios' ? iosEnterAnimation : mdEnterAnimation);
|
const animationBuilder = this.enterAnimation || this.config.get('alertEnter', this.mode === 'ios' ? iosEnterAnimation : mdEnterAnimation);
|
||||||
|
|
||||||
|
@ -22,7 +22,6 @@ export class LoadingController {
|
|||||||
|
|
||||||
// give this loading a unique id
|
// give this loading a unique id
|
||||||
loading.loadingId = `loading-${id}`;
|
loading.loadingId = `loading-${id}`;
|
||||||
loading.style.zIndex = (20000 + id).toString();
|
|
||||||
|
|
||||||
// convert the passed in loading options into props
|
// convert the passed in loading options into props
|
||||||
// that get passed down into the new loading
|
// that get passed down into the new loading
|
||||||
|
@ -129,6 +129,8 @@ export class Loading {
|
|||||||
|
|
||||||
this.ionLoadingWillPresent.emit();
|
this.ionLoadingWillPresent.emit();
|
||||||
|
|
||||||
|
this.el.style.zIndex = `${20000 + this.loadingId}`;
|
||||||
|
|
||||||
// get the user's animation fn if one was provided
|
// get the user's animation fn if one was provided
|
||||||
const animationBuilder = this.enterAnimation || this.config.get('loadingEnter', this.mode === 'ios' ? iosEnterAnimation : mdEnterAnimation);
|
const animationBuilder = this.enterAnimation || this.config.get('loadingEnter', this.mode === 'ios' ? iosEnterAnimation : mdEnterAnimation);
|
||||||
|
|
||||||
|
@ -20,7 +20,6 @@ export class ModalController {
|
|||||||
|
|
||||||
// give this modal a unique id
|
// give this modal a unique id
|
||||||
modal.modalId = `modal-${id}`;
|
modal.modalId = `modal-${id}`;
|
||||||
modal.style.zIndex = (10000 + id).toString();
|
|
||||||
|
|
||||||
// convert the passed in modal options into props
|
// convert the passed in modal options into props
|
||||||
// that get passed down into the new modal
|
// that get passed down into the new modal
|
||||||
|
@ -4,9 +4,12 @@ import {
|
|||||||
AnimationBuilder,
|
AnimationBuilder,
|
||||||
AnimationController,
|
AnimationController,
|
||||||
Config,
|
Config,
|
||||||
|
FrameworkDelegate,
|
||||||
OverlayDismissEvent,
|
OverlayDismissEvent,
|
||||||
OverlayDismissEventDetail
|
OverlayDismissEventDetail
|
||||||
} from '../../index';
|
} from '../../index';
|
||||||
|
|
||||||
|
import { DomFrameworkDelegate } from '../../utils/dom-framework-delegate';
|
||||||
import { domControllerAsync, playAnimationAsync } from '../../utils/helpers';
|
import { domControllerAsync, playAnimationAsync } from '../../utils/helpers';
|
||||||
import { createThemedClasses } from '../../utils/theme';
|
import { createThemedClasses } from '../../utils/theme';
|
||||||
|
|
||||||
@ -73,12 +76,13 @@ export class Modal {
|
|||||||
@Prop() enterAnimation: AnimationBuilder;
|
@Prop() enterAnimation: AnimationBuilder;
|
||||||
@Prop() leaveAnimation: AnimationBuilder;
|
@Prop() leaveAnimation: AnimationBuilder;
|
||||||
@Prop() animate: boolean;
|
@Prop() animate: boolean;
|
||||||
|
@Prop({ mutable: true }) delegate: FrameworkDelegate;
|
||||||
|
|
||||||
private animation: Animation;
|
private animation: Animation;
|
||||||
|
private usersComponentElement: HTMLElement;
|
||||||
|
|
||||||
@Method()
|
@Method()
|
||||||
present() {
|
async present() {
|
||||||
if (this.animation) {
|
if (this.animation) {
|
||||||
this.animation.destroy();
|
this.animation.destroy();
|
||||||
this.animation = null;
|
this.animation = null;
|
||||||
@ -86,27 +90,39 @@ export class Modal {
|
|||||||
|
|
||||||
this.ionModalWillPresent.emit();
|
this.ionModalWillPresent.emit();
|
||||||
|
|
||||||
|
this.el.style.zIndex = `${20000 + this.modalId}`;
|
||||||
|
|
||||||
// get the user's animation fn if one was provided
|
// get the user's animation fn if one was provided
|
||||||
const animationBuilder = this.enterAnimation || this.config.get('modalEnter', this.mode === 'ios' ? iosEnterAnimation : mdEnterAnimation);
|
const animationBuilder = this.enterAnimation || this.config.get('modalEnter', this.mode === 'ios' ? iosEnterAnimation : mdEnterAnimation);
|
||||||
|
|
||||||
// build the animation and kick it off
|
const userComponentParent = this.el.querySelector(`.${USER_COMPONENT_MODAL_CONTAINER_CLASS}`);
|
||||||
// build the animation and kick it off
|
if (!this.delegate) {
|
||||||
return this.animationCtrl.create(animationBuilder, this.el).then(animation => {
|
this.delegate = new DomFrameworkDelegate();
|
||||||
this.animation = animation;
|
}
|
||||||
|
|
||||||
|
const cssClasses = ['ion-page'];
|
||||||
|
if (this.cssClass && this.cssClass.length) {
|
||||||
|
cssClasses.push(this.cssClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the modal by default to the data being passed
|
||||||
|
this.data = this.data || {};
|
||||||
|
this.data.modal = this.el;
|
||||||
|
const mountingData = await this.delegate.attachViewToDom(userComponentParent, this.component, this.data, cssClasses);
|
||||||
|
this.usersComponentElement = mountingData.element;
|
||||||
|
this.animation = await this.animationCtrl.create(animationBuilder, this.el);
|
||||||
if (!this.animate) {
|
if (!this.animate) {
|
||||||
// if the duration is 0, it won't actually animate I don't think
|
// if the duration is 0, it won't actually animate I don't think
|
||||||
// TODO - validate this
|
// TODO - validate this
|
||||||
this.animation = animation.duration(0);
|
this.animation = this.animation.duration(0);
|
||||||
}
|
}
|
||||||
return playAnimationAsync(animation);
|
await playAnimationAsync(this.animation);
|
||||||
}).then((animation) => {
|
this.animation.destroy();
|
||||||
animation.destroy();
|
|
||||||
this.ionModalDidPresent.emit();
|
this.ionModalDidPresent.emit();
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Method()
|
@Method()
|
||||||
dismiss(data?: any, role?: string) {
|
async dismiss(data?: any, role?: string) {
|
||||||
if (this.animation) {
|
if (this.animation) {
|
||||||
this.animation.destroy();
|
this.animation.destroy();
|
||||||
this.animation = null;
|
this.animation = null;
|
||||||
@ -116,23 +132,35 @@ export class Modal {
|
|||||||
role
|
role
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!this.delegate) {
|
||||||
|
this.delegate = new DomFrameworkDelegate();
|
||||||
|
}
|
||||||
|
|
||||||
// get the user's animation fn if one was provided
|
// get the user's animation fn if one was provided
|
||||||
const animationBuilder = this.leaveAnimation || this.config.get('modalLeave', this.mode === 'ios' ? iosLeaveAnimation : mdLeaveAnimation);
|
const animationBuilder = this.leaveAnimation || this.config.get('modalLeave', this.mode === 'ios' ? iosLeaveAnimation : mdLeaveAnimation);
|
||||||
|
|
||||||
return this.animationCtrl.create(animationBuilder, this.el).then(animation => {
|
this.animation = await this.animationCtrl.create(animationBuilder, this.el);
|
||||||
this.animation = animation;
|
await playAnimationAsync(this.animation);
|
||||||
return playAnimationAsync(animation);
|
this.animation.destroy();
|
||||||
}).then((animation) => {
|
|
||||||
animation.destroy();
|
|
||||||
return domControllerAsync(Context.dom.write, () => {
|
await domControllerAsync(Context.dom.write, () => {});
|
||||||
this.el.parentNode.removeChild(this.el);
|
|
||||||
});
|
// TODO - Figure out how to make DOM controller work with callbacks that return a promise or are async
|
||||||
}).then(() => {
|
const userComponentParent = this.el.querySelector(`.${USER_COMPONENT_MODAL_CONTAINER_CLASS}`);
|
||||||
|
await this.delegate.removeViewFromDom(userComponentParent, this.usersComponentElement);
|
||||||
|
|
||||||
|
this.el.parentElement.removeChild(this.el);
|
||||||
|
|
||||||
this.ionModalDidDismiss.emit({
|
this.ionModalDidDismiss.emit({
|
||||||
data,
|
data,
|
||||||
role
|
role
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
|
@Method()
|
||||||
|
getUserComponentContainer(): HTMLElement {
|
||||||
|
return this.el.querySelector(`.${USER_COMPONENT_MODAL_CONTAINER_CLASS}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Listen('ionDismiss')
|
@Listen('ionDismiss')
|
||||||
@ -188,6 +216,7 @@ export interface ModalOptions {
|
|||||||
enterAnimation?: AnimationBuilder;
|
enterAnimation?: AnimationBuilder;
|
||||||
exitAnimation?: AnimationBuilder;
|
exitAnimation?: AnimationBuilder;
|
||||||
cssClass?: string;
|
cssClass?: string;
|
||||||
|
delegate?: FrameworkDelegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -213,3 +242,5 @@ export {
|
|||||||
mdEnterAnimation as mdModalEnterAnimation,
|
mdEnterAnimation as mdModalEnterAnimation,
|
||||||
mdLeaveAnimation as mdModalLeaveAnimation
|
mdLeaveAnimation as mdModalLeaveAnimation
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const USER_COMPONENT_MODAL_CONTAINER_CLASS = 'modal-wrapper';
|
||||||
|
@ -32,6 +32,11 @@ string
|
|||||||
any
|
any
|
||||||
|
|
||||||
|
|
||||||
|
#### delegate
|
||||||
|
|
||||||
|
any
|
||||||
|
|
||||||
|
|
||||||
#### enableBackdropDismiss
|
#### enableBackdropDismiss
|
||||||
|
|
||||||
boolean
|
boolean
|
||||||
@ -89,6 +94,11 @@ string
|
|||||||
any
|
any
|
||||||
|
|
||||||
|
|
||||||
|
#### delegate
|
||||||
|
|
||||||
|
any
|
||||||
|
|
||||||
|
|
||||||
#### enableBackdropDismiss
|
#### enableBackdropDismiss
|
||||||
|
|
||||||
boolean
|
boolean
|
||||||
@ -144,6 +154,9 @@ boolean
|
|||||||
#### dismiss()
|
#### dismiss()
|
||||||
|
|
||||||
|
|
||||||
|
#### getUserComponentContainer()
|
||||||
|
|
||||||
|
|
||||||
#### present()
|
#### present()
|
||||||
|
|
||||||
|
|
||||||
|
@ -28,10 +28,21 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
async function presentModal() {
|
async function presentModal() {
|
||||||
|
const element = document.createElement('div');
|
||||||
|
element.innerHTML = `
|
||||||
|
<ion-header>
|
||||||
|
<ion-toolbar>
|
||||||
|
<ion-title>Cool Modal</ion-title>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
|
<ion-content padding>
|
||||||
|
<div>Sure, I like dogs. I like caravans moar.</div>
|
||||||
|
</ion-content>
|
||||||
|
`;
|
||||||
const modalController = document.querySelector('ion-modal-controller');
|
const modalController = document.querySelector('ion-modal-controller');
|
||||||
await modalController.componentOnReady();
|
await modalController.componentOnReady();
|
||||||
const modalElement = await modalController.create({
|
const modalElement = await modalController.create({
|
||||||
component: 'page-one'
|
component: element
|
||||||
});
|
});
|
||||||
modalElement.present();
|
modalElement.present();
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ export function buildIOSTransition(rootTransition: Transition, enteringView: Vie
|
|||||||
|
|
||||||
if (enteringView) {
|
if (enteringView) {
|
||||||
const enteringContent = rootTransition.create();
|
const enteringContent = rootTransition.create();
|
||||||
enteringContent.addElement(enteringView.element.querySelectorAll('ion-header > *:not(ion-navbar),ion-footer > *'));
|
enteringContent.addElement(enteringView.element.querySelectorAll('ion-header > *:not(ion-toolbar),ion-footer > *'));
|
||||||
|
|
||||||
rootTransition.add(enteringContent);
|
rootTransition.add(enteringContent);
|
||||||
|
|
||||||
@ -42,30 +42,30 @@ export function buildIOSTransition(rootTransition: Transition, enteringView: Vie
|
|||||||
enteringContent.beforeClearStyles([OPACITY]).fromTo(TRANSLATEX, OFF_RIGHT, CENTER, true);
|
enteringContent.beforeClearStyles([OPACITY]).fromTo(TRANSLATEX, OFF_RIGHT, CENTER, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
const enteringNavbarEle = enteringView.element.querySelector('ion-navbar');
|
const enteringToolBarEle = enteringView.element.querySelector('ion-toolbar');
|
||||||
if (enteringNavbarEle) {
|
if (enteringToolBarEle) {
|
||||||
const enteringNavBar = rootTransition.create();
|
const enteringToolBar = rootTransition.create();
|
||||||
enteringNavBar.addElement(enteringNavbarEle);
|
enteringToolBar.addElement(enteringToolBarEle);
|
||||||
|
|
||||||
rootTransition.add(enteringNavBar);
|
rootTransition.add(enteringToolBar);
|
||||||
|
|
||||||
const enteringTitle = rootTransition.create();
|
const enteringTitle = rootTransition.create();
|
||||||
enteringTitle.addElement(enteringNavbarEle.querySelector('ion-title'));
|
enteringTitle.addElement(enteringToolBarEle.querySelector('ion-title'));
|
||||||
const enteringNavbarItems = rootTransition.create();
|
const enteringToolBarItems = rootTransition.create();
|
||||||
enteringNavbarItems.addElement(enteringNavbarEle.querySelectorAll('ion-buttons,[menuToggle]'));
|
enteringToolBarItems.addElement(enteringToolBarEle.querySelectorAll('ion-buttons,[menuToggle]'));
|
||||||
const enteringNavbarBg = rootTransition.create();
|
const enteringToolBarBg = rootTransition.create();
|
||||||
enteringNavbarBg.addElement(enteringNavbarEle.querySelector('.toolbar-background'));
|
enteringToolBarBg.addElement(enteringToolBarEle.querySelector('.toolbar-background'));
|
||||||
const enteringBackButton = rootTransition.create();
|
const enteringBackButton = rootTransition.create();
|
||||||
enteringBackButton.addElement(enteringNavbarEle.querySelector('.back-button'));
|
enteringBackButton.addElement(enteringToolBarEle.querySelector('.back-button'));
|
||||||
|
|
||||||
enteringNavBar
|
enteringToolBar
|
||||||
.add(enteringTitle)
|
.add(enteringTitle)
|
||||||
.add(enteringNavbarItems)
|
.add(enteringToolBarItems)
|
||||||
.add(enteringNavbarBg)
|
.add(enteringToolBarBg)
|
||||||
.add(enteringBackButton);
|
.add(enteringBackButton);
|
||||||
|
|
||||||
enteringTitle.fromTo(OPACITY, 0.01, 1, true);
|
enteringTitle.fromTo(OPACITY, 0.01, 1, true);
|
||||||
enteringNavbarItems.fromTo(OPACITY, 0.01, 1, true);
|
enteringToolBarItems.fromTo(OPACITY, 0.01, 1, true);
|
||||||
|
|
||||||
if (backDirection) {
|
if (backDirection) {
|
||||||
enteringTitle.fromTo(TRANSLATEX, OFF_LEFT, CENTER, true);
|
enteringTitle.fromTo(TRANSLATEX, OFF_LEFT, CENTER, true);
|
||||||
@ -75,10 +75,10 @@ export function buildIOSTransition(rootTransition: Transition, enteringView: Vie
|
|||||||
enteringBackButton.beforeAddClass(SHOW_BACK_BTN_CSS).fromTo(OPACITY, 0.01, 1, true);
|
enteringBackButton.beforeAddClass(SHOW_BACK_BTN_CSS).fromTo(OPACITY, 0.01, 1, true);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// entering navbar, forward direction
|
// entering toolbar, forward direction
|
||||||
enteringTitle.fromTo(TRANSLATEX, OFF_RIGHT, CENTER, true);
|
enteringTitle.fromTo(TRANSLATEX, OFF_RIGHT, CENTER, true);
|
||||||
|
|
||||||
enteringNavbarBg.beforeClearStyles([OPACITY]).fromTo(TRANSLATEX, OFF_RIGHT, CENTER, true);
|
enteringToolBarBg.beforeClearStyles([OPACITY]).fromTo(TRANSLATEX, OFF_RIGHT, CENTER, true);
|
||||||
|
|
||||||
if (canNavGoBack(enteringView.nav)) {
|
if (canNavGoBack(enteringView.nav)) {
|
||||||
// forward direction, entering page has a back button
|
// forward direction, entering page has a back button
|
||||||
@ -86,10 +86,10 @@ export function buildIOSTransition(rootTransition: Transition, enteringView: Vie
|
|||||||
|
|
||||||
|
|
||||||
const enteringBackBtnText = rootTransition.create();
|
const enteringBackBtnText = rootTransition.create();
|
||||||
enteringBackBtnText.addElement(enteringNavbarEle.querySelector('.back-button-text'));
|
enteringBackBtnText.addElement(enteringToolBarEle.querySelector('.back-button-text'));
|
||||||
|
|
||||||
enteringBackBtnText.fromTo(TRANSLATEX, (isRTL ? '-100px' : '100px'), '0px');
|
enteringBackBtnText.fromTo(TRANSLATEX, (isRTL ? '-100px' : '100px'), '0px');
|
||||||
enteringNavBar.add(enteringBackBtnText);
|
enteringToolBar.add(enteringBackBtnText);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
enteringBackButton.beforeRemoveClass(SHOW_BACK_BTN_CSS);
|
enteringBackButton.beforeRemoveClass(SHOW_BACK_BTN_CSS);
|
||||||
@ -103,7 +103,7 @@ export function buildIOSTransition(rootTransition: Transition, enteringView: Vie
|
|||||||
|
|
||||||
const leavingContent = rootTransition.create();
|
const leavingContent = rootTransition.create();
|
||||||
leavingContent.addElement(leavingView.element);
|
leavingContent.addElement(leavingView.element);
|
||||||
leavingContent.addElement(leavingView.element.querySelectorAll('ion-header > *:not(ion-navbar),ion-footer > *'));
|
leavingContent.addElement(leavingView.element.querySelectorAll('ion-header > *:not(ion-toolbar),ion-footer > *'));
|
||||||
|
|
||||||
rootTransition.add(leavingContent);
|
rootTransition.add(leavingContent);
|
||||||
|
|
||||||
@ -119,59 +119,59 @@ export function buildIOSTransition(rootTransition: Transition, enteringView: Vie
|
|||||||
.afterClearStyles([TRANSFORM, OPACITY]);
|
.afterClearStyles([TRANSFORM, OPACITY]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const leavingNavbarEle = leavingView.element.querySelector('ion-navbar');
|
const leavingToolBarEle = leavingView.element.querySelector('ion-toolbar');
|
||||||
if (leavingNavbarEle) {
|
if (leavingToolBarEle) {
|
||||||
const leavingNavBar = rootTransition.create();
|
const leavingToolBar = rootTransition.create();
|
||||||
leavingNavBar.addElement(leavingNavbarEle);
|
leavingToolBar.addElement(leavingToolBarEle);
|
||||||
|
|
||||||
const leavingTitle = rootTransition.create();
|
const leavingTitle = rootTransition.create();
|
||||||
leavingTitle.addElement(leavingNavbarEle.querySelector('ion-title'));
|
leavingTitle.addElement(leavingToolBarEle.querySelector('ion-title'));
|
||||||
|
|
||||||
const leavingNavbarItems = rootTransition.create();
|
const leavingToolBarItems = rootTransition.create();
|
||||||
leavingNavbarItems.addElement(leavingNavbarEle.querySelectorAll('ion-buttons,[menuToggle]'));
|
leavingToolBarItems.addElement(leavingToolBarEle.querySelectorAll('ion-buttons,[menuToggle]'));
|
||||||
|
|
||||||
const leavingNavbarBg = rootTransition.create();
|
const leavingToolBarBg = rootTransition.create();
|
||||||
leavingNavbarBg.addElement(leavingNavbarEle.querySelector('.toolbar-background'));
|
leavingToolBarBg.addElement(leavingToolBarEle.querySelector('.toolbar-background'));
|
||||||
|
|
||||||
const leavingBackButton = rootTransition.create();
|
const leavingBackButton = rootTransition.create();
|
||||||
leavingBackButton.addElement(leavingNavbarEle.querySelector('.back-button'));
|
leavingBackButton.addElement(leavingToolBarEle.querySelector('.back-button'));
|
||||||
|
|
||||||
leavingNavBar
|
leavingToolBar
|
||||||
.add(leavingTitle)
|
.add(leavingTitle)
|
||||||
.add(leavingNavbarItems)
|
.add(leavingToolBarItems)
|
||||||
.add(leavingBackButton)
|
.add(leavingBackButton)
|
||||||
.add(leavingNavbarBg);
|
.add(leavingToolBarBg);
|
||||||
this.add(leavingNavBar);
|
this.add(leavingToolBar);
|
||||||
|
|
||||||
// fade out leaving navbar items
|
// fade out leaving toolbar items
|
||||||
leavingBackButton.fromTo(OPACITY, 0.99, 0);
|
leavingBackButton.fromTo(OPACITY, 0.99, 0);
|
||||||
leavingTitle.fromTo(OPACITY, 0.99, 0);
|
leavingTitle.fromTo(OPACITY, 0.99, 0);
|
||||||
leavingNavbarItems.fromTo(OPACITY, 0.99, 0);
|
leavingToolBarItems.fromTo(OPACITY, 0.99, 0);
|
||||||
|
|
||||||
if (backDirection) {
|
if (backDirection) {
|
||||||
// leaving navbar, back direction
|
// leaving toolbar, back direction
|
||||||
leavingTitle.fromTo(TRANSLATEX, CENTER, (isRTL ? '-100%' : '100%'));
|
leavingTitle.fromTo(TRANSLATEX, CENTER, (isRTL ? '-100%' : '100%'));
|
||||||
|
|
||||||
// leaving navbar, back direction, and there's no entering navbar
|
// leaving toolbar, back direction, and there's no entering toolbar
|
||||||
// should just slide out, no fading out
|
// should just slide out, no fading out
|
||||||
leavingNavbarBg
|
leavingToolBarBg
|
||||||
.beforeClearStyles([OPACITY])
|
.beforeClearStyles([OPACITY])
|
||||||
.fromTo(TRANSLATEX, CENTER, (isRTL ? '-100%' : '100%'));
|
.fromTo(TRANSLATEX, CENTER, (isRTL ? '-100%' : '100%'));
|
||||||
|
|
||||||
const leavingBackBtnText = rootTransition.create();
|
const leavingBackBtnText = rootTransition.create();
|
||||||
leavingBackBtnText.addElement(leavingNavbarEle.querySelector('.back-button-text'));
|
leavingBackBtnText.addElement(leavingToolBarEle.querySelector('.back-button-text'));
|
||||||
leavingBackBtnText.fromTo(TRANSLATEX, CENTER, (isRTL ? -300 : 300) + 'px');
|
leavingBackBtnText.fromTo(TRANSLATEX, CENTER, (isRTL ? -300 : 300) + 'px');
|
||||||
leavingNavBar.add(leavingBackBtnText);
|
leavingToolBar.add(leavingBackBtnText);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// leaving navbar, forward direction
|
// leaving toolbar, forward direction
|
||||||
leavingTitle
|
leavingTitle
|
||||||
.fromTo(TRANSLATEX, CENTER, OFF_LEFT)
|
.fromTo(TRANSLATEX, CENTER, OFF_LEFT)
|
||||||
.afterClearStyles([TRANSFORM]);
|
.afterClearStyles([TRANSFORM]);
|
||||||
|
|
||||||
leavingBackButton.afterClearStyles([OPACITY]);
|
leavingBackButton.afterClearStyles([OPACITY]);
|
||||||
leavingTitle.afterClearStyles([OPACITY]);
|
leavingTitle.afterClearStyles([OPACITY]);
|
||||||
leavingNavbarItems.afterClearStyles([OPACITY]);
|
leavingToolBarItems.afterClearStyles([OPACITY]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,14 +27,14 @@ export function buildMdTransition(rootTransition: Transition, enteringView: View
|
|||||||
.fromTo('opacity', 0.01, 1, true);
|
.fromTo('opacity', 0.01, 1, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
const enteringNavbarEle = enteringView.element.querySelector('ion-navbar');
|
const enteringToolbarEle = enteringView.element.querySelector('ion-toolbar');
|
||||||
if (enteringNavbarEle) {
|
if (enteringToolbarEle) {
|
||||||
const enteringNavBar = rootTransition.create();
|
const enteringToolBar = rootTransition.create();
|
||||||
enteringNavBar.addElement(enteringNavbarEle);
|
enteringToolBar.addElement(enteringToolbarEle);
|
||||||
rootTransition.add(enteringNavBar);
|
rootTransition.add(enteringToolBar);
|
||||||
|
|
||||||
const enteringBackButton = rootTransition.create();
|
const enteringBackButton = rootTransition.create();
|
||||||
enteringBackButton.addElement(enteringNavbarEle.querySelector('.back-button'));
|
enteringBackButton.addElement(enteringToolbarEle.querySelector('.back-button'));
|
||||||
rootTransition.add(enteringBackButton);
|
rootTransition.add(enteringBackButton);
|
||||||
|
|
||||||
if (canNavGoBack(enteringView.nav)) {
|
if (canNavGoBack(enteringView.nav)) {
|
||||||
|
@ -19,7 +19,6 @@ export class PickerController {
|
|||||||
|
|
||||||
// give this picker a unique id
|
// give this picker a unique id
|
||||||
picker.pickerId = `picker-${id}`;
|
picker.pickerId = `picker-${id}`;
|
||||||
picker.style.zIndex = (20000 + id).toString();
|
|
||||||
|
|
||||||
// convert the passed in picker options into props
|
// convert the passed in picker options into props
|
||||||
// that get passed down into the new picker
|
// that get passed down into the new picker
|
||||||
|
@ -89,6 +89,8 @@ export class Picker {
|
|||||||
|
|
||||||
this.ionPickerWillPresent.emit();
|
this.ionPickerWillPresent.emit();
|
||||||
|
|
||||||
|
this.el.style.zIndex = `${20000 + this.pickerId}`;
|
||||||
|
|
||||||
// get the user's animation fn if one was provided
|
// get the user's animation fn if one was provided
|
||||||
const animationBuilder = this.enterAnimation || this.config.get('pickerEnter', iosEnterAnimation);
|
const animationBuilder = this.enterAnimation || this.config.get('pickerEnter', iosEnterAnimation);
|
||||||
|
|
||||||
|
@ -21,7 +21,6 @@ export class PopoverController {
|
|||||||
|
|
||||||
// give this popover a unique id
|
// give this popover a unique id
|
||||||
popover.popoverId = `popover-${id}`;
|
popover.popoverId = `popover-${id}`;
|
||||||
popover.style.zIndex = (10000 + id).toString();
|
|
||||||
|
|
||||||
// convert the passed in popover options into props
|
// convert the passed in popover options into props
|
||||||
// that get passed down into the new popover
|
// that get passed down into the new popover
|
||||||
|
@ -86,6 +86,8 @@ export class Popover {
|
|||||||
}
|
}
|
||||||
this.ionPopoverWillPresent.emit();
|
this.ionPopoverWillPresent.emit();
|
||||||
|
|
||||||
|
this.el.style.zIndex = `${10000 + this.popoverId}`;
|
||||||
|
|
||||||
// get the user's animation fn if one was provided
|
// get the user's animation fn if one was provided
|
||||||
const animationBuilder = this.enterAnimation || this.config.get('popoverEnter', this.mode === 'ios' ? iosEnterAnimation : mdEnterAnimation);
|
const animationBuilder = this.enterAnimation || this.config.get('popoverEnter', this.mode === 'ios' ? iosEnterAnimation : mdEnterAnimation);
|
||||||
|
|
||||||
@ -249,4 +251,3 @@ export {
|
|||||||
mdEnterAnimation as mdPopoverEnterAnimation,
|
mdEnterAnimation as mdPopoverEnterAnimation,
|
||||||
mdLeaveAnimation as mdPopoverLeaveAnimation
|
mdLeaveAnimation as mdPopoverLeaveAnimation
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -10,7 +10,8 @@ const routes: Routes = [
|
|||||||
{ path: 'actionSheet', loadChildren: 'app/action-sheet/action-sheet.module#ActionSheetModule' },
|
{ path: 'actionSheet', loadChildren: 'app/action-sheet/action-sheet.module#ActionSheetModule' },
|
||||||
{ path: 'toast', loadChildren: 'app/toast/toast.module#ToastModule' },
|
{ path: 'toast', loadChildren: 'app/toast/toast.module#ToastModule' },
|
||||||
{ path: 'loading', loadChildren: 'app/loading/loading.module#LoadingModule' },
|
{ path: 'loading', loadChildren: 'app/loading/loading.module#LoadingModule' },
|
||||||
{ path: 'nav', loadChildren: 'app/nav/nav.module#NavModule' }
|
{ path: 'nav', loadChildren: 'app/nav/nav.module#NavModule' },
|
||||||
|
{ path: 'modal', loadChildren: 'app/modal/modal.module#ModalModule' }
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@ -24,4 +24,7 @@
|
|||||||
<li>
|
<li>
|
||||||
<a href='nav'>Nav Page</a>
|
<a href='nav'>Nav Page</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href='modal'>Modal Page</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'page-one',
|
||||||
|
template: `
|
||||||
|
<ion-header>
|
||||||
|
<ion-toolbar>
|
||||||
|
<ion-title>Page One</ion-title>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
|
<ion-content>
|
||||||
|
Page One
|
||||||
|
<ul>
|
||||||
|
<li>ngOnInit - {{ngOnInitDetection}}</li>
|
||||||
|
</ul>
|
||||||
|
</ion-content>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
export class ModalPageToPresent {
|
||||||
|
|
||||||
|
ngOnInitDetection = 'initial';
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
console.log('page one ngOnInit');
|
||||||
|
setInterval(() => {
|
||||||
|
this.ngOnInitDetection = '' + Date.now();
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
35
packages/demos/angular/src/app/modal/modal-page.component.ts
Normal file
35
packages/demos/angular/src/app/modal/modal-page.component.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
import { ModalController } from '@ionic/angular';
|
||||||
|
import { ModalPageToPresent } from './modal-page-to-present';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-modal-page',
|
||||||
|
template: `
|
||||||
|
<ion-app>
|
||||||
|
<ion-page class="show-page">
|
||||||
|
<ion-header>
|
||||||
|
<ion-toolbar>
|
||||||
|
<ion-title>Test</ion-title>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
|
<ion-content padding>
|
||||||
|
<ion-button (click)="clickMe()">Open Basic Modal</ion-button>
|
||||||
|
</ion-content>
|
||||||
|
</ion-page>
|
||||||
|
</ion-app>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
export class ModalPageComponent {
|
||||||
|
|
||||||
|
constructor(private modalController: ModalController) {
|
||||||
|
}
|
||||||
|
|
||||||
|
clickMe() {
|
||||||
|
const modal = this.modalController.create({
|
||||||
|
component: ModalPageToPresent
|
||||||
|
});
|
||||||
|
return modal.present();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
14
packages/demos/angular/src/app/modal/modal-routing.module.ts
Normal file
14
packages/demos/angular/src/app/modal/modal-routing.module.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { Routes, RouterModule } from '@angular/router';
|
||||||
|
|
||||||
|
import { ModalPageComponent } from './modal-page.component';
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{ path: '', component: ModalPageComponent }
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(routes)],
|
||||||
|
exports: [RouterModule]
|
||||||
|
})
|
||||||
|
export class ModalRoutingModule { }
|
27
packages/demos/angular/src/app/modal/modal.module.ts
Normal file
27
packages/demos/angular/src/app/modal/modal.module.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { IonicAngularModule } from '@ionic/angular';
|
||||||
|
|
||||||
|
import { ModalPageComponent } from './modal-page.component';
|
||||||
|
import { ModalRoutingModule } from './modal-routing.module';
|
||||||
|
|
||||||
|
import { ModalPageToPresent } from './modal-page-to-present';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
IonicAngularModule.forRoot(),
|
||||||
|
ModalRoutingModule
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
ModalPageComponent,
|
||||||
|
ModalPageToPresent
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
],
|
||||||
|
entryComponents: [
|
||||||
|
ModalPageToPresent
|
||||||
|
],
|
||||||
|
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||||
|
})
|
||||||
|
export class ModalModule { }
|
49
packages/demos/react/src/pages/ModalPage.js
Normal file
49
packages/demos/react/src/pages/ModalPage.js
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
|
import PageThree from './PageThree';
|
||||||
|
|
||||||
|
export default class ModalPage extends Component {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.style = {
|
||||||
|
height: '100%'
|
||||||
|
};
|
||||||
|
this.state = {
|
||||||
|
content: 'modal page - ' + 50
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
console.log('componentDidMount');
|
||||||
|
setInterval(() => {
|
||||||
|
this.setState({ content: 'Modal page - ' + Math.random() * 1000});
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
dismiss() {
|
||||||
|
return this.props.modal.dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return [
|
||||||
|
<ion-header ref={(element) => this.element = element}>
|
||||||
|
<ion-toolbar>
|
||||||
|
<ion-title>Modal Page</ion-title>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>,
|
||||||
|
<ion-content>
|
||||||
|
Modal Page
|
||||||
|
<div>
|
||||||
|
<ion-button onClick={() => this.dismiss()}>Dismiss</ion-button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Some random content: {this.state.content}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Props : {this.props.paramOne}
|
||||||
|
</div>
|
||||||
|
</ion-content>
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,9 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
|
import { createModal } from '@ionic/react';
|
||||||
|
|
||||||
import PageTwo from './PageTwo';
|
import PageTwo from './PageTwo';
|
||||||
|
import ModalPage from './ModalPage';
|
||||||
|
|
||||||
export default class PageOne extends Component {
|
export default class PageOne extends Component {
|
||||||
|
|
||||||
@ -35,6 +38,14 @@ export default class PageOne extends Component {
|
|||||||
nav.push(PageTwo, { paramOne: 'Tobey Flenderson'});
|
nav.push(PageTwo, { paramOne: 'Tobey Flenderson'});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
openModal() {
|
||||||
|
return createModal({
|
||||||
|
component: ModalPage
|
||||||
|
}).then((modal) => {
|
||||||
|
return modal.present();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
this.setState({ content: Math.random() * 1000});
|
this.setState({ content: Math.random() * 1000});
|
||||||
@ -54,6 +65,9 @@ export default class PageOne extends Component {
|
|||||||
<div>
|
<div>
|
||||||
<ion-button onClick={() => this.goToPageTwo()}>Go to Page Two</ion-button>
|
<ion-button onClick={() => this.goToPageTwo()}>Go to Page Two</ion-button>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<ion-button onClick={() => this.openModal()}>OpenModal</ion-button>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
Some random content: {this.state.content}
|
Some random content: {this.state.content}
|
||||||
</div>
|
</div>
|
||||||
|
13
packages/react/src/apis/modal.ts
Normal file
13
packages/react/src/apis/modal.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
|
||||||
|
import { ModalOptions } from '@ionic/core';
|
||||||
|
|
||||||
|
import { Delegate } from '../react-framework-delegate';
|
||||||
|
import { getOrAppendElement } from '../utils/helpers';
|
||||||
|
|
||||||
|
export function createModal(opts: ModalOptions): Promise<HTMLIonModalElement> {
|
||||||
|
opts.delegate = Delegate;
|
||||||
|
const element = getOrAppendElement('ion-modal-controller') as HTMLIonModalControllerElement;
|
||||||
|
return (element as any).componentOnReady().then(() => {
|
||||||
|
return element.create(opts);
|
||||||
|
});
|
||||||
|
}
|
@ -1,2 +1,3 @@
|
|||||||
export { Delegate } from './react-framework-delegate';
|
export { Delegate } from './react-framework-delegate';
|
||||||
|
export { createModal } from './apis/modal';
|
||||||
export * from './utils/wc-shim';
|
export * from './utils/wc-shim';
|
10
packages/react/src/utils/helpers.ts
Normal file
10
packages/react/src/utils/helpers.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
export function getOrAppendElement(tagName: string): Element {
|
||||||
|
const element = document.querySelector(tagName);
|
||||||
|
if (element) {
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
const tmp = document.createElement(tagName);
|
||||||
|
document.body.appendChild(tmp);
|
||||||
|
return tmp;
|
||||||
|
}
|
Reference in New Issue
Block a user