diff --git a/packages/angular/common/src/index.ts b/packages/angular/common/src/index.ts index f3439baeb5..2c689cc6d6 100644 --- a/packages/angular/common/src/index.ts +++ b/packages/angular/common/src/index.ts @@ -20,6 +20,9 @@ export { bindLifecycleEvents, AngularDelegate } from './providers/angular-delega export type { IonicWindow } from './types/interfaces'; export { NavParams } from './directives/navigation/nav-params'; + +export { IonModal } from './overlays/modal'; + export { IonRouterOutlet, INPUT_BINDER, RoutedComponentInputBinder } from './directives/navigation/router-outlet'; export type { StackEvent } from './directives/navigation/stack-utils'; diff --git a/packages/angular/common/src/overlays/modal.ts b/packages/angular/common/src/overlays/modal.ts new file mode 100644 index 0000000000..0ad1fd49c0 --- /dev/null +++ b/packages/angular/common/src/overlays/modal.ts @@ -0,0 +1,133 @@ +import { + ChangeDetectorRef, + ContentChild, + Directive, + ElementRef, + EventEmitter, + NgZone, + TemplateRef, +} from '@angular/core'; +import { Components, ModalBreakpointChangeEventDetail } from '@ionic/core'; + +import { ProxyCmp, proxyOutputs } from '../utils/proxy'; + +export declare interface IonModal extends Components.IonModal { + /** + * Emitted after the modal has presented. + **/ + ionModalDidPresent: EventEmitter; + /** + * Emitted before the modal has presented. + */ + ionModalWillPresent: EventEmitter; + /** + * Emitted before the modal has dismissed. + */ + ionModalWillDismiss: EventEmitter; + /** + * Emitted after the modal has dismissed. + */ + ionModalDidDismiss: EventEmitter; + /** + * Emitted after the modal breakpoint has changed. + */ + ionBreakpointDidChange: EventEmitter>; + /** + * Emitted after the modal has presented. Shorthand for ionModalDidPresent. + */ + didPresent: EventEmitter; + /** + * Emitted before the modal has presented. Shorthand for ionModalWillPresent. + */ + willPresent: EventEmitter; + /** + * Emitted before the modal has dismissed. Shorthand for ionModalWillDismiss. + */ + willDismiss: EventEmitter; + /** + * Emitted after the modal has dismissed. Shorthand for ionModalDidDismiss. + */ + didDismiss: EventEmitter; +} + +const MODAL_INPUTS = [ + 'animated', + 'keepContentsMounted', + 'backdropBreakpoint', + 'backdropDismiss', + 'breakpoints', + 'canDismiss', + 'cssClass', + 'enterAnimation', + 'event', + 'handle', + 'handleBehavior', + 'initialBreakpoint', + 'isOpen', + 'keyboardClose', + 'leaveAnimation', + 'mode', + 'presentingElement', + 'showBackdrop', + 'translucent', + 'trigger', +]; + +const MODAL_METHODS = [ + 'present', + 'dismiss', + 'onDidDismiss', + 'onWillDismiss', + 'setCurrentBreakpoint', + 'getCurrentBreakpoint', +]; + +@ProxyCmp({ + inputs: MODAL_INPUTS, + methods: MODAL_METHODS, +}) +/** + * @Component extends from @Directive + * so by defining the inputs here we + * do not need to re-define them for the + * lazy loaded popover. + */ +@Directive({ + selector: 'ion-modal', + // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property + inputs: MODAL_INPUTS, +}) + +// eslint-disable-next-line @angular-eslint/directive-class-suffix +export class IonModal { + // TODO(FW-2827): type + @ContentChild(TemplateRef, { static: false }) template: TemplateRef; + + isCmpOpen = false; + + protected el: HTMLElement; + + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { + this.el = r.nativeElement; + + this.el.addEventListener('ionMount', () => { + this.isCmpOpen = true; + c.detectChanges(); + }); + this.el.addEventListener('didDismiss', () => { + this.isCmpOpen = false; + c.detectChanges(); + }); + proxyOutputs(this, this.el, [ + 'ionModalDidPresent', + 'ionModalWillPresent', + 'ionModalWillDismiss', + 'ionModalDidDismiss', + 'ionBreakpointDidChange', + 'didPresent', + 'willPresent', + 'willDismiss', + 'didDismiss', + ]); + } +} diff --git a/packages/angular/src/directives/overlays/modal.ts b/packages/angular/src/directives/overlays/modal.ts index 645a542c57..9f9a22d9d6 100644 --- a/packages/angular/src/directives/overlays/modal.ts +++ b/packages/angular/src/directives/overlays/modal.ts @@ -1,139 +1,11 @@ -/* eslint-disable */ -/* tslint:disable */ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ContentChild, - ElementRef, - EventEmitter, - NgZone, - TemplateRef, -} from '@angular/core'; -import { ProxyCmp, proxyOutputs } from '../angular-component-lib/utils'; -import { Components, ModalBreakpointChangeEventDetail } from '@ionic/core'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { IonModal as IonModalBase } from '@ionic/angular/common'; -export declare interface IonModal extends Components.IonModal { - /** - * Emitted after the modal has presented. - **/ - ionModalDidPresent: EventEmitter; - /** - * Emitted before the modal has presented. - */ - ionModalWillPresent: EventEmitter; - /** - * Emitted before the modal has dismissed. - */ - ionModalWillDismiss: EventEmitter; - /** - * Emitted after the modal has dismissed. - */ - ionModalDidDismiss: EventEmitter; - /** - * Emitted after the modal breakpoint has changed. - */ - ionBreakpointDidChange: EventEmitter>; - /** - * Emitted after the modal has presented. Shorthand for ionModalWillDismiss. - */ - didPresent: EventEmitter; - /** - * Emitted before the modal has presented. Shorthand for ionModalWillPresent. - */ - willPresent: EventEmitter; - /** - * Emitted before the modal has dismissed. Shorthand for ionModalWillDismiss. - */ - willDismiss: EventEmitter; - /** - * Emitted after the modal has dismissed. Shorthand for ionModalDidDismiss. - */ - didDismiss: EventEmitter; -} -@ProxyCmp({ - inputs: [ - 'animated', - 'keepContentsMounted', - 'backdropBreakpoint', - 'backdropDismiss', - 'breakpoints', - 'canDismiss', - 'cssClass', - 'enterAnimation', - 'event', - 'handle', - 'handleBehavior', - 'initialBreakpoint', - 'isOpen', - 'keyboardClose', - 'leaveAnimation', - 'mode', - 'presentingElement', - 'showBackdrop', - 'translucent', - 'trigger', - ], - methods: ['present', 'dismiss', 'onDidDismiss', 'onWillDismiss', 'setCurrentBreakpoint', 'getCurrentBreakpoint'], -}) @Component({ selector: 'ion-modal', changeDetection: ChangeDetectionStrategy.OnPush, template: `
`, - inputs: [ - 'animated', - 'keepContentsMounted', - 'backdropBreakpoint', - 'backdropDismiss', - 'breakpoints', - 'canDismiss', - 'cssClass', - 'enterAnimation', - 'event', - 'handle', - 'handleBehavior', - 'initialBreakpoint', - 'isOpen', - 'keyboardClose', - 'leaveAnimation', - 'mode', - 'presentingElement', - 'showBackdrop', - 'translucent', - 'trigger', - ], }) -export class IonModal { - // TODO(FW-2827): type - @ContentChild(TemplateRef, { static: false }) template: TemplateRef; - - isCmpOpen: boolean = false; - - protected el: HTMLElement; - - constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { - this.el = r.nativeElement; - - this.el.addEventListener('ionMount', () => { - this.isCmpOpen = true; - c.detectChanges(); - }); - this.el.addEventListener('didDismiss', () => { - this.isCmpOpen = false; - c.detectChanges(); - }); - proxyOutputs(this, this.el, [ - 'ionModalDidPresent', - 'ionModalWillPresent', - 'ionModalWillDismiss', - 'ionModalDidDismiss', - 'ionBreakpointDidChange', - 'didPresent', - 'willPresent', - 'willDismiss', - 'didDismiss', - ]); - } -} +export class IonModal extends IonModalBase {} diff --git a/packages/angular/standalone/src/index.ts b/packages/angular/standalone/src/index.ts index 9dcb40d1f9..b8ddcc657e 100644 --- a/packages/angular/standalone/src/index.ts +++ b/packages/angular/standalone/src/index.ts @@ -1,2 +1,3 @@ export { IonBackButton } from './navigation/back-button'; +export { IonModal } from './overlays/modal'; export { IonRouterOutlet } from './navigation/router-outlet'; diff --git a/packages/angular/standalone/src/overlays/modal.ts b/packages/angular/standalone/src/overlays/modal.ts new file mode 100644 index 0000000000..6e132b2fb4 --- /dev/null +++ b/packages/angular/standalone/src/overlays/modal.ts @@ -0,0 +1,18 @@ +import { CommonModule } from '@angular/common'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { IonModal as IonModalBase, ProxyCmp } from '@ionic/angular/common'; +import { defineCustomElement } from '@ionic/core/components/ion-modal.js'; + +@ProxyCmp({ + defineCustomElementFn: defineCustomElement, +}) +@Component({ + selector: 'ion-modal', + changeDetection: ChangeDetectionStrategy.OnPush, + template: `
+ +
`, + standalone: true, + imports: [CommonModule], +}) +export class IonModal extends IonModalBase {} diff --git a/packages/angular/test/base/e2e/src/standalone/modal.spec.ts b/packages/angular/test/base/e2e/src/standalone/modal.spec.ts new file mode 100644 index 0000000000..ffa4df9dac --- /dev/null +++ b/packages/angular/test/base/e2e/src/standalone/modal.spec.ts @@ -0,0 +1,12 @@ +describe('Modals: Inline', () => { + beforeEach(() => { + cy.visit('/standalone/modal'); + }); + + it('should render modal', () => { + cy.get('button#open-modal').click(); + + cy.get('ion-modal').should('be.visible'); + cy.get('ion-modal #modal-content').should('exist'); + }); +}); diff --git a/packages/angular/test/base/src/app/standalone/app-standalone/app.routes.ts b/packages/angular/test/base/src/app/standalone/app-standalone/app.routes.ts index a045a0a69a..c7233f2a3e 100644 --- a/packages/angular/test/base/src/app/standalone/app-standalone/app.routes.ts +++ b/packages/angular/test/base/src/app/standalone/app-standalone/app.routes.ts @@ -7,6 +7,7 @@ export const routes: Routes = [ component: AppComponent, children: [ { path: 'test', loadComponent: () => import('../test/test.component').then(m => m.TestComponent) }, + { path: 'modal', loadComponent: () => import('../modal/modal.component').then(m => m.ModalComponent) }, { path: 'router-outlet', loadComponent: () => import('../router-outlet/router-outlet.component').then(m => m.RouterOutletComponent) }, { path: 'back-button', loadComponent: () => import('../back-button/back-button.component').then(m => m.BackButtonComponent) }, ] diff --git a/packages/angular/test/base/src/app/standalone/modal/modal.component.html b/packages/angular/test/base/src/app/standalone/modal/modal.component.html new file mode 100644 index 0000000000..af65317ff2 --- /dev/null +++ b/packages/angular/test/base/src/app/standalone/modal/modal.component.html @@ -0,0 +1,7 @@ + + + + + + + diff --git a/packages/angular/test/base/src/app/standalone/modal/modal.component.ts b/packages/angular/test/base/src/app/standalone/modal/modal.component.ts new file mode 100644 index 0000000000..1dedd57538 --- /dev/null +++ b/packages/angular/test/base/src/app/standalone/modal/modal.component.ts @@ -0,0 +1,11 @@ +import { Component } from '@angular/core'; +import { IonModal } from '@ionic/angular/standalone'; + +@Component({ + selector: 'app-test', + templateUrl: './modal.component.html', + standalone: true, + imports: [IonModal] +}) +export class ModalComponent { +}