Browse Source

Merge pull request #1835 from abpframework/refactor/modal-component

refactor(theme-shared): add angular animations
pull/1838/head
Yasin Aydın 7 years ago
committed by GitHub
parent
commit
b79aba84fa
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      npm/ng-packs/packages/theme-shared/src/lib/animations/bounce.animations.ts
  2. 64
      npm/ng-packs/packages/theme-shared/src/lib/animations/fade.animations.ts
  3. 25
      npm/ng-packs/packages/theme-shared/src/lib/components/modal/README.md
  4. 14
      npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.animations.ts
  5. 29
      npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.html
  6. 119
      npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts

2
npm/ng-packs/packages/theme-shared/src/lib/animations/bounce.animations.ts

@ -1,4 +1,4 @@
import { animation, animate, style, trigger, transition, state, useAnimation, keyframes } from '@angular/animations';
import { animate, animation, keyframes, style } from '@angular/animations';
export const bounceIn = animation(
[

64
npm/ng-packs/packages/theme-shared/src/lib/animations/fade.animations.ts

@ -13,3 +13,67 @@ export const fadeOut = animation(
],
{ params: { time: '350ms', easing: 'ease' } }
);
export const fadeInDown = animation(
[
style({ opacity: '0', display: '{{ display }}', transform: '{{ transform }} translateY(-20px)' }),
animate('{{ time }} {{ easing }}', style({ opacity: '1', transform: '{{ transform }} translateY(0)' }))
],
{ params: { time: '350ms', easing: 'ease', display: 'block', transform: 'translate(-50%, -50%)' } }
);
export const fadeInUp = animation(
[
style({ opacity: '0', display: '{{ display }}', transform: '{{ transform }} translateY(20px)' }),
animate('{{ time }} {{ easing }}', style({ opacity: '1', transform: '{{ transform }} translateY(0)' }))
],
{ params: { time: '350ms', easing: 'ease', display: 'block', transform: 'translate(-50%, -50%)' } }
);
export const fadeInLeft = animation(
[
style({ opacity: '0', display: '{{ display }}', transform: '{{ transform }} translateX(20px)' }),
animate('{{ time }} {{ easing }}', style({ opacity: '1', transform: '{{ transform }} translateX(0)' }))
],
{ params: { time: '350ms', easing: 'ease', display: 'block', transform: 'translate(-50%, -50%)' } }
);
export const fadeInRight = animation(
[
style({ opacity: '0', display: '{{ display }}', transform: '{{ transform }} translateX(-20px)' }),
animate('{{ time }} {{ easing }}', style({ opacity: '1', transform: '{{ transform }} translateX(0)' }))
],
{ params: { time: '350ms', easing: 'ease', display: 'block', transform: 'translate(-50%, -50%)' } }
);
export const fadeOutDown = animation(
[
style({ opacity: '1', display: '{{ display }}', transform: '{{ transform }} translateY(0)' }),
animate('{{ time }} {{ easing }}', style({ opacity: '0', transform: '{{ transform }} translateY(20px)' }))
],
{ params: { time: '350ms', easing: 'ease', display: 'block', transform: 'translate(-50%, -50%)' } }
);
export const fadeOutUp = animation(
[
style({ opacity: '1', display: '{{ display }}', transform: '{{ transform }} translateY(0)' }),
animate('{{ time }} {{ easing }}', style({ opacity: '0', transform: '{{ transform }} translateY(-20px)' }))
],
{ params: { time: '350ms', easing: 'ease', display: 'block', transform: 'translate(-50%, -50%)' } }
);
export const fadeOutLeft = animation(
[
style({ opacity: '1', display: '{{ display }}', transform: '{{ transform }} translateX(0)' }),
animate('{{ time }} {{ easing }}', style({ opacity: '0', transform: '{{ transform }} translateX(20px)' }))
],
{ params: { time: '350ms', easing: 'ease', display: 'block', transform: 'translate(-50%, -50%)' } }
);
export const fadeOutRight = animation(
[
style({ opacity: '1', display: '{{ display }}', transform: '{{ transform }} translateX(0)' }),
animate('{{ time }} {{ easing }}', style({ opacity: '0', transform: '{{ transform }} translateX(-20px)' }))
],
{ params: { time: '350ms', easing: 'ease', display: 'block', transform: 'translate(-50%, -50%)' } }
);

25
npm/ng-packs/packages/theme-shared/src/lib/components/modal/README.md

@ -1,25 +0,0 @@
<h1> Abp Modal </h1>
Example Usage:
```html
<abp-modal size="md" [(visible)]="isModalShow" [centered]="false">
<ng-template #abpHeader>
<h3>Modal Title</h3>
</ng-template>
<ng-template #abpBody>
<form [formGroup]="form">
<input formControlName="name" />
<input formControlName="surname" />
</form>
</ng-template>
<ng-template #abpFooter>
<button #abpClose type="button" class="btn btn-secondary">Close</button>
<button type="button" class="btn btn-primary" (click)="saveChanges()">Save changes</button>
</ng-template>
</abp-modal>
<button (click)="isModalShow = true">Click</button>
```

14
npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.animations.ts

@ -0,0 +1,14 @@
import { trigger, state, style, transition, useAnimation } from '@angular/animations';
import { fadeInDown, fadeOut, fadeIn } from '../../animations';
export const backdropAnimation = trigger('backdrop', [
state('void', style({ opacity: '0' })),
state('*', style({ opacity: '1' })),
transition('void => *', useAnimation(fadeIn)),
transition('* => void', useAnimation(fadeOut))
]);
export const dialogAnimation = trigger('dialog', [
transition('void => *', useAnimation(fadeInDown)),
transition('* => void', useAnimation(fadeOut))
]);

29
npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.html

@ -1,31 +1,15 @@
<div
*ngIf="showModal"
(click)="close()"
id="abp-modal"
class="modal fade {{ modalClass }} d-block show"
[style.padding-right.px]="'15'"
>
<div
id="abp-modal-container"
class="modal-dialog modal-{{ size }} fade-in-top"
tabindex="-1"
[class.modal-dialog-centered]="centered"
#abpModalContent
>
<div #content id="abp-modal-content" class="modal-content" (click)="$event.stopPropagation()">
<div class="modal {{ modalClass }}" tabindex="-1" role="dialog" *ngIf="visible">
<div class="modal-backdrop" [@backdrop]="visible" (click)="close()"></div>
<div id="abp-modal-dialog" class="modal-dialog modal-{{ size }}" role="document" [@dialog]="visible" #abpModalContent>
<div id="abp-modal-content" class="modal-content">
<div id="abp-modal-header" class="modal-header">
<ng-container *ngTemplateOutlet="abpHeader"></ng-container>
<button id="abp-modal-close-button" type="button" class="close" (click)="close()">
<button id="abp-modal-close-button" type="button" class="close" aria-label="Close" (click)="close()">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div
id="abp-modal-body"
class="modal-body"
[style.height]="height || undefined"
[style.minHeight]="minHeight || undefined"
>
<div id="abp-modal-body" class="modal-body">
<ng-container *ngTemplateOutlet="abpBody"></ng-container>
</div>
<div id="abp-modal-footer" class="modal-footer">
@ -33,6 +17,5 @@
</div>
</div>
</div>
<ng-content></ng-content>
</div>

119
npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts

@ -11,19 +11,19 @@ import {
ViewChild,
ViewChildren
} from '@angular/core';
import { fromEvent, Subject, timer } from 'rxjs';
import { filter, take, takeUntil, debounceTime } from 'rxjs/operators';
import { fromEvent, Subject } from 'rxjs';
import { debounceTime, filter, takeUntil } from 'rxjs/operators';
import { Toaster } from '../../models/toaster';
import { ConfirmationService } from '../../services/confirmation.service';
import { ConfirmationService } from '../../services';
import { ButtonComponent } from '../button/button.component';
import { backdropAnimation, dialogAnimation } from './modal.animations';
export type ModalSize = 'sm' | 'md' | 'lg' | 'xl';
const ANIMATION_TIMEOUT = 200;
@Component({
selector: 'abp-modal',
templateUrl: './modal.component.html'
templateUrl: './modal.component.html',
animations: [backdropAnimation, dialogAnimation]
})
export class ModalComponent implements OnDestroy {
@Input()
@ -32,27 +32,14 @@ export class ModalComponent implements OnDestroy {
}
set visible(value: boolean) {
if (typeof value !== 'boolean') return;
if (!this.modalContent) {
if (value) {
setTimeout(() => {
this.showModal = value;
this.visible = value;
}, 0);
}
return;
}
this._visible = value;
if (value) {
this.setVisible(value);
this.listen();
this.renderer.addClass(document.body, 'modal-open');
this.appear.emit();
} else {
this.closable = false;
this.renderer.addClass(this.modalContent.nativeElement, 'fade-out-top');
setTimeout(() => {
this.setVisible(value);
this.destroy$.next();
}, ANIMATION_TIMEOUT - 10);
this.renderer.removeClass(document.body, 'modal-open');
this.disappear.emit();
}
}
@ -74,13 +61,8 @@ export class ModalComponent implements OnDestroy {
@Input() size: ModalSize = 'lg';
@Input() height: number;
@Input() minHeight: number;
@Output() readonly visibleChange = new EventEmitter<boolean>();
@Output() readonly init = new EventEmitter<void>();
@ContentChild(ButtonComponent, { static: false, read: ButtonComponent })
abpSubmit: ButtonComponent;
@ContentChild('abpHeader', { static: false }) abpHeader: TemplateRef<any>;
@ -91,13 +73,14 @@ export class ModalComponent implements OnDestroy {
@ContentChild('abpClose', { static: false, read: ElementRef })
abpClose: ElementRef<any>;
@ContentChild(ButtonComponent, { static: false, read: ButtonComponent })
abpSubmit: ButtonComponent;
@ViewChild('abpModalContent', { static: false }) modalContent: ElementRef;
@ViewChildren('abp-button') abpButtons;
@Output() readonly visibleChange = new EventEmitter<boolean>();
@Output() readonly init = new EventEmitter<void>();
@Output() readonly appear = new EventEmitter();
@Output() readonly disappear = new EventEmitter();
@ -106,11 +89,7 @@ export class ModalComponent implements OnDestroy {
_busy = false;
showModal = false;
isOpenConfirmation = false;
closable = false;
isConfirmationOpen = false;
destroy$ = new Subject<void>();
@ -120,22 +99,27 @@ export class ModalComponent implements OnDestroy {
this.destroy$.next();
}
setVisible(value: boolean) {
this._visible = value;
this.visibleChange.emit(value);
this.showModal = value;
close() {
if (this.busy) return;
if (value) {
timer(ANIMATION_TIMEOUT + 100)
.pipe(take(1))
.subscribe(_ => (this.closable = true));
const nodes = getFlatNodes(
(this.modalContent.nativeElement.querySelector('#abp-modal-body') as HTMLElement).childNodes
);
this.renderer.addClass(document.body, 'modal-open');
this.appear.emit();
if (hasNgDirty(nodes)) {
if (this.isConfirmationOpen) return;
this.isConfirmationOpen = true;
this.confirmationService
.warn('AbpAccount::AreYouSureYouWantToCancelEditingWarningMessage', 'AbpAccount::AreYouSure')
.subscribe((status: Toaster.Status) => {
this.isConfirmationOpen = false;
if (status === Toaster.Status.confirm) {
this.visible = false;
}
});
} else {
this.closable = false;
this.renderer.removeClass(document.body, 'modal-open');
this.disappear.emit();
this.visible = false;
}
}
@ -144,7 +128,7 @@ export class ModalComponent implements OnDestroy {
.pipe(
takeUntil(this.destroy$),
debounceTime(150),
filter((key: KeyboardEvent) => key && key.code === 'Escape' && this.closable)
filter((key: KeyboardEvent) => key && key.code === 'Escape')
)
.subscribe(_ => {
this.close();
@ -155,40 +139,13 @@ export class ModalComponent implements OnDestroy {
fromEvent(this.abpClose.nativeElement, 'click')
.pipe(
takeUntil(this.destroy$),
filter(() => !!(this.closable && this.modalContent))
filter(() => !!this.modalContent)
)
.subscribe(() => this.close());
}, 0);
this.init.emit();
}
close() {
if (!this.closable || this.busy) return;
const nodes = getFlatNodes(
(this.modalContent.nativeElement.querySelector('#abp-modal-body') as HTMLElement).childNodes
);
if (hasNgDirty(nodes)) {
if (this.isOpenConfirmation) return;
this.isOpenConfirmation = true;
this.confirmationService
.warn('AbpAccount::AreYouSureYouWantToCancelEditingWarningMessage', 'AbpAccount::AreYouSure')
.subscribe((status: Toaster.Status) => {
timer(ANIMATION_TIMEOUT).subscribe(() => {
this.isOpenConfirmation = false;
});
if (status === Toaster.Status.confirm) {
this.visible = false;
}
});
} else {
this.visible = false;
}
}
}
function getFlatNodes(nodes: NodeList): HTMLElement[] {

Loading…
Cancel
Save