Skip to content

Commit

Permalink
feat(design)!: allow modal to be closed with ESC key (#2812)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: Previously, the `DaffModalService` required a `DaffModal` as an arg to `close` (the return type of `open`). This API was probably larger than it should have been, so I trimmed it down. Most consumers won't notice a return type change, but if you do, we can reconsider this change.
  • Loading branch information
xelaint committed May 29, 2024
1 parent 075859b commit e121d40
Show file tree
Hide file tree
Showing 8 changed files with 58 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
import { DaffCartActionTypes } from '@daffodil/cart/state';
import {
DaffModalService,
DaffModal,
DaffModalComponent,
} from '@daffodil/design/modal';

import {
Expand All @@ -25,7 +25,7 @@ import { AddToCartNotificationComponent } from '../components/add-to-cart-notifi

@Injectable()
export class AddToCartNotificationEffects {
private notification: DaffModal;
private notification: DaffModalComponent;

constructor(
private actions$: Actions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ import {
Component,
} from '@angular/core';

import { DaffModalService } from '@daffodil/design/modal';
import {
DaffModalComponent,
DaffModalService,
} from '@daffodil/design/modal';

import { BasicModalContentComponent } from './modal-content.component';

Expand All @@ -14,7 +17,7 @@ import { BasicModalContentComponent } from './modal-content.component';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class BasicModalComponent {
modal: any;
modal: DaffModalComponent;

constructor(private modalService: DaffModalService) {}

Expand Down
4 changes: 4 additions & 0 deletions libs/design/modal/src/modal.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { DaffModalActionsComponent } from './modal-actions/modal-actions.compone
import { DaffModalContentComponent } from './modal-content/modal-content.component';
import { DaffModalHeaderComponent } from './modal-header/modal-header.component';
import { DaffModalTitleDirective } from './modal-title/modal-title.directive';
import { DaffModalService } from './service/modal.service';

@NgModule({
imports: [
Expand All @@ -28,5 +29,8 @@ import { DaffModalTitleDirective } from './modal-title/modal-title.directive';
DaffModalContentComponent,
DaffModalActionsComponent,
],
providers: [
DaffModalService,
],
})
export class DaffModalModule { }
4 changes: 4 additions & 0 deletions libs/design/modal/src/modal/modal.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { By } from '@angular/platform-browser';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';

import { DaffModalComponent } from './modal.component';
import { DaffModalService } from '../service/modal.service';

@Component({ template: `
<div class="daff-modal-wrapper">
Expand All @@ -34,6 +35,9 @@ describe('@daffodil/design/modal | DaffModalComponent', () => {
WrapperComponent,
DaffModalComponent,
],
providers: [
DaffModalService,
],
})
.compileComponents();
}));
Expand Down
10 changes: 8 additions & 2 deletions libs/design/modal/src/modal/modal.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ import {
ElementRef,
AfterContentInit,
AfterViewInit,
Self,
} from '@angular/core';

import { daffFocusableElementsSelector } from '@daffodil/design';

import { daffFadeAnimations } from '../animations/modal-animation';
import { getAnimationState } from '../animations/modal-animation-state';
import { DaffModalService } from '../service/modal.service';

@Component({
selector: 'daff-modal',
Expand Down Expand Up @@ -61,13 +63,17 @@ export class DaffModalComponent implements AfterContentInit, AfterViewInit {
>();

/**
* Event fired when the backdrop is clicked. This is often used to close the modal.
* @docs-private
*/
hide: EventEmitter<void> = new EventEmitter<void>();
@HostListener('keydown.escape')
onEscape() {
this.modalService.close(this);
}

private _focusTrap: ConfigurableFocusTrap;

constructor(
private modalService: DaffModalService,
private _focusTrapFactory: ConfigurableFocusTrapFactory,
private _elementRef: ElementRef<HTMLElement>,
) {
Expand Down
46 changes: 27 additions & 19 deletions libs/design/modal/src/service/modal.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,16 @@ import {
Injectable,
Type,
ComponentRef,
Injector,
} from '@angular/core';

import { DaffModal } from '../modal/modal';
import { DaffModalConfiguration } from '../modal/modal-config';
import { DaffModalComponent } from '../modal/modal.component';
import { DaffModalModule } from '../modal.module';

@Injectable({
providedIn: DaffModalModule,
})
@Injectable()
export class DaffModalService {
private _modals: DaffModal[] = [];
private _modals: Map<DaffModalComponent, DaffModal> = new Map();

constructor(private overlay: Overlay) {}

Expand All @@ -29,7 +27,16 @@ export class DaffModalService {
private _attachModal(
overlayRef: OverlayRef,
): ComponentRef<DaffModalComponent> {
const modal = overlayRef.attach(new ComponentPortal(DaffModalComponent));
const modal = overlayRef.attach(
new ComponentPortal(
DaffModalComponent,
undefined,
Injector.create({ providers: [{
provide: DaffModalService,
useValue: this,
}]}),
),
);
modal.instance.open = true;
return modal;
}
Expand All @@ -51,22 +58,21 @@ export class DaffModalService {
}

private _removeModal(modal: DaffModal) {
const index = this._modals.indexOf(modal);
if (index === -1) {
throw new Error(
if (!this._modals.has(modal.modal.instance)) {
throw new Error(
'The Modal that you are trying to remove does not exist.',
);
}
}

modal.overlay.dispose();
this._modals.delete(modal.modal.instance);

this._modals = this._modals.filter(m => m !== modal);
modal.overlay.dispose();
}

open(
component: Type<any>,
configuration?: Partial<DaffModalConfiguration>,
): DaffModal {
): DaffModalComponent {
const config = { ...this.defaultConfiguration, ...configuration };
const _ref = this._createOverlayRef();
const _modal = this._attachModal(_ref);
Expand All @@ -77,22 +83,24 @@ export class DaffModalService {
overlay: _ref,
};

this._modals.push(modal);
this._modals.set(modal.modal.instance, modal);

_ref
.backdropClick()
.subscribe(() =>
config.onBackdropClicked
? config.onBackdropClicked()
: this.close(modal),
: this.close(modal.modal.instance),
);
return modal;
return modal.modal.instance;
}

close(modal: DaffModal): void {
modal.modal.instance.open = false;
close(component: DaffModalComponent): void {
component.open = false;
const modal = this._modals.get(component);

modal.overlay.detachBackdrop();
modal.modal.instance.closedAnimationCompleted.subscribe(
component.closedAnimationCompleted.subscribe(
(e: AnimationEvent) => this._removeModal(modal),
);
}
Expand Down
4 changes: 4 additions & 0 deletions libs/design/modal/src/specs/animations.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { By } from '@angular/platform-browser';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';

import { DaffModalComponent } from '../modal/modal.component';
import { DaffModalService } from '../service/modal.service';

@Component({ template: `
<div class="daff-modal-wrapper">
Expand All @@ -36,6 +37,9 @@ describe('@daffodil/design/modal | DaffModalComponent', () => {
WrapperComponent,
DaffModalComponent,
],
providers: [
DaffModalService,
],
})
.compileComponents();
}));
Expand Down
4 changes: 4 additions & 0 deletions libs/design/modal/src/specs/dynamic-content.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { By } from '@angular/platform-browser';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';

import { DaffModalComponent } from '../modal/modal.component';
import { DaffModalService } from '../service/modal.service';

@Component({ template: `<daff-modal></daff-modal>` })
class WrapperComponent {}
Expand Down Expand Up @@ -45,6 +46,9 @@ describe('@daffodil/design/modal | DaffModalComponent', () => {
WrapperComponent,
DaffModalComponent,
],
providers: [
DaffModalService,
],
})
.compileComponents();
}));
Expand Down

0 comments on commit e121d40

Please sign in to comment.