Skip to content

Commit 73f0ca0

Browse files
committed
refactor(modal): migrate to native CSS animations
1 parent 3ffc0b7 commit 73f0ca0

File tree

2 files changed

+33
-54
lines changed

2 files changed

+33
-54
lines changed

projects/coreui-angular/src/lib/modal/modal/modal.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1+
@let isVisible = visible();
12
<c-modal-dialog
23
[alignment]="alignment()"
34
[fullscreen]="fullscreen()"
45
[scrollable]="scrollable()"
56
[size]="size()">
67
<c-modal-content>
7-
@let isVisible = visible();
8-
<div [cdkTrapFocus]="isVisible" [cdkTrapFocusAutoCapture]="isVisible" style="display: contents;" #modalContentRef>
8+
<div #modalContentRef [cdkTrapFocusAutoCapture]="isVisible" [cdkTrapFocus]="isVisible" style="display: contents;">
99
<ng-content />
1010
</div>
1111
</c-modal-content>

projects/coreui-angular/src/lib/modal/modal/modal.component.ts

Lines changed: 31 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { animate, AnimationEvent, state, style, transition, trigger } from '@angular/animations';
21
import { A11yModule, FocusMonitor } from '@angular/cdk/a11y';
32
import { BooleanInput } from '@angular/cdk/coercion';
43
import {
@@ -30,37 +29,18 @@ import { ModalDialogComponent } from '../modal-dialog/modal-dialog.component';
3029

3130
@Component({
3231
selector: 'c-modal',
33-
animations: [
34-
trigger('showHide', [
35-
state(
36-
'visible',
37-
style({
38-
// display: 'block'
39-
})
40-
),
41-
state(
42-
'hidden',
43-
style({
44-
// display: 'none'
45-
})
46-
),
47-
transition('visible <=> *', [animate('150ms')])
48-
])
49-
],
5032
templateUrl: './modal.component.html',
5133
exportAs: 'cModal',
5234
imports: [ModalDialogComponent, ModalContentComponent, A11yModule],
5335
host: {
5436
class: 'modal',
5537
'[class]': 'hostClasses()',
56-
'[attr.role]': 'role()',
57-
'[attr.inert]': 'ariaHidden',
38+
'[attr.role]': 'visible() ? role() : null',
39+
'[attr.inert]': 'ariaHidden()',
5840
'[attr.id]': 'id',
5941
'[attr.aria-modal]': 'ariaModal()',
42+
'[attr.aria-hidden]': 'ariaHidden()',
6043
'[attr.tabindex]': '-1',
61-
'[@showHide]': 'animateTrigger()',
62-
'(@showHide.start)': 'animateStart($event)',
63-
'(@showHide.done)': 'animateDone($event)',
6444
'(mousedown)': 'onMouseDownHandler($event)',
6545
'(click)': 'onClickHandler($event)',
6646
'(document:keyup)': 'onKeyUpHandler($event)'
@@ -163,6 +143,7 @@ export class ModalComponent implements OnInit, OnDestroy, AfterViewInit {
163143
readonly #visibleInputEffect = effect(() => {
164144
const visible = this.visible();
165145
untracked(() => {
146+
this.animateStart();
166147
this.setBodyStyles(visible);
167148
this.setBackdrop(this.backdrop() !== false && visible);
168149
this.visibleChange?.emit(visible);
@@ -209,6 +190,28 @@ export class ModalComponent implements OnInit, OnDestroy, AfterViewInit {
209190
// @ViewChild('modalContentRef', { read: ElementRef }) modalContentRef!: ElementRef;
210191
// readonly modalContentRef = viewChild(ModalContentComponent, { read: ElementRef });
211192
readonly modalContentRef = viewChild('modalContentRef', { read: ElementRef });
193+
readonly modalDialogRef = viewChild.required(ModalDialogComponent, { read: ElementRef });
194+
195+
readonly #modalDialogEffect = effect((OnCleanup) => {
196+
const modalDialogElement = this.modalDialogRef().nativeElement;
197+
198+
const removeEventListeners = () => {
199+
modalDialogElement?.removeEventListener('transitionend', this.#handleTransitionEnd);
200+
};
201+
202+
OnCleanup(removeEventListeners);
203+
204+
modalDialogElement?.addEventListener('transitionend', this.#handleTransitionEnd);
205+
});
206+
207+
readonly #handleTransitionEnd = (event: TransitionEvent) => {
208+
const modalDialogElement = this.modalDialogRef().nativeElement;
209+
if (event.target === modalDialogElement && event.propertyName === 'transform') {
210+
if (!this.visible()) {
211+
this.#renderer.setStyle(this.#hostElement.nativeElement, 'display', 'none');
212+
}
213+
}
214+
};
212215

213216
#activeBackdrop!: any;
214217

@@ -218,30 +221,16 @@ export class ModalComponent implements OnInit, OnDestroy, AfterViewInit {
218221
return {
219222
modal: true,
220223
fade: this.transition(),
221-
show: this.show
224+
show: this.visible()
222225
} as Record<string, boolean>;
223226
});
224227

225-
get ariaHidden(): boolean | null {
228+
readonly ariaHidden = computed(() => {
226229
return this.visible() ? null : true;
227-
}
228-
229-
readonly animateTrigger = computed(() => {
230-
return this.visible() ? 'visible' : 'hidden';
231230
});
232231

233-
get show(): boolean {
234-
return this.visible() && this.#show();
235-
}
236-
237-
set show(value: boolean) {
238-
this.#show.set(value);
239-
}
240-
241-
readonly #show = signal(true);
242-
243-
animateStart(event: AnimationEvent) {
244-
if (event.toState === 'visible') {
232+
animateStart() {
233+
if (this.visible()) {
245234
this.#backdropService.hideScrollbar();
246235
this.#renderer.setStyle(this.#hostElement.nativeElement, 'display', 'block');
247236
} else {
@@ -251,16 +240,6 @@ export class ModalComponent implements OnInit, OnDestroy, AfterViewInit {
251240
}
252241
}
253242

254-
animateDone(event: AnimationEvent) {
255-
setTimeout(() => {
256-
if (event.toState === 'hidden') {
257-
this.#renderer.setStyle(this.#hostElement.nativeElement, 'display', 'none');
258-
this.#backdropService.resetScrollbar();
259-
}
260-
});
261-
this.show = this.visible();
262-
}
263-
264243
onKeyUpHandler(event: KeyboardEvent): void {
265244
if (event.key === 'Escape' && this.keyboard() && this.visible()) {
266245
if (this.backdrop() === 'static') {
@@ -332,7 +311,7 @@ export class ModalComponent implements OnInit, OnDestroy, AfterViewInit {
332311

333312
private setBodyStyles(open: boolean): void {
334313
if (open) {
335-
if (this.backdrop() === true || this.backdrop() === 'static') {
314+
if (!!this.backdrop()) {
336315
this.#renderer.addClass(this.#document.body, 'modal-open');
337316
}
338317
} else {

0 commit comments

Comments
 (0)