Skip to content

Commit

Permalink
Merge pull request #2 from criar-art/animation-window
Browse files Browse the repository at this point in the history
Add Animation Support for UpWindow Component
  • Loading branch information
lucasferreiralimax authored Oct 14, 2024
2 parents bdbcbba + 5fe0348 commit 597cc1e
Show file tree
Hide file tree
Showing 7 changed files with 329 additions and 25 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"publish": "cd dist/up-window-angular && npm publish",
"watch": "ng build --watch --configuration development",
"test": "ng test",
"test:module": "ng test up-window-angular",
"postbuild": "node scripts/post-build.js"
},
"private": true,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
<div class="overlay" [ngClass]="{ active: isOpen() }">
<div class="up-window" [ngClass]="class">
<button class="close-window" (click)="closeWindow()">×</button>
<div class="overlay" [class.active]="isOpen()">
<div class="up-window" [ngClass]="getClass()">
<button class="close-window" (click)="closeWindow()">&times;</button>
<div class="up-window-header">
<div>
<h2 class="up-window-title">{{ title }}</h2>
<p class="up-window-subtitle">{{ subtitle }}</p>
</div>
<h3 class="up-window-title">{{ title }}</h3>
<h4 class="up-window-subtitle">{{ subtitle }}</h4>
</div>

<div class="up-window-body">
<ng-content></ng-content>
</div>

<div class="up-window-footer">
<button class="btn btn-cancel" (click)="onCancel()">Cancel</button>
<button class="btn btn-confirm" (click)="onConfirm()">Confirm</button>
Expand Down
153 changes: 151 additions & 2 deletions projects/up-window-angular/src/lib/up-window-angular.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,51 @@ body {
padding: 20px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 1rem;

&.fade {
animation: up-window-fadeIn 0.3s forwards;
}

&.fade-out {
animation: up-window-fadeOut 0.3s forwards;
}

&.slide {
animation: up-window-slideIn 0.3s forwards;
}

&.slide-out {
animation: up-window-slideOut 0.3s forwards;
}

&.slide-up {
animation: up-window-slideUp 0.3s forwards;
}

&.slide-up-out {
animation: up-window-slideUpOut 0.3s forwards;
}

&.slide-down {
animation: up-window-slideDown 0.3s forwards;
}

&.slide-down-out {
animation: up-window-slideDownOut 0.3s forwards;
}

&.scale {
animation: up-window-scaleIn 0.3s forwards;
}

&.scale-out {
animation: up-window-scaleOut 0.3s forwards;
}
}

.up-window-header {
display: flex;
justify-content: space-between;
align-items: center;
flex-direction: column;
}

.up-window-title,
Expand Down Expand Up @@ -109,3 +148,113 @@ body {
background-color: var(--up-window-primary);
color: white;
}

@keyframes up-window-fadeIn {
from {
opacity: 0;
transform: translateY(-20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}

@keyframes up-window-fadeOut {
from {
opacity: 1;
transform: translateY(0);
}
to {
opacity: 0;
transform: translateY(-20px);
}
}

@keyframes up-window-slideIn {
from {
opacity: 0;
transform: translateX(-100%);
}
to {
opacity: 1;
transform: translateX(0);
}
}

@keyframes up-window-slideOut {
from {
opacity: 1;
transform: translateX(0);
}
to {
opacity: 0;
transform: translateX(-100%);
}
}

@keyframes up-window-slideUp {
from {
opacity: 0;
transform: translateY(100%);
}
to {
opacity: 1;
transform: translateY(0);
}
}

@keyframes up-window-slideDown {
from {
opacity: 0;
transform: translateY(-100%);
}
to {
opacity: 1;
transform: translateY(0);
}
}

@keyframes up-window-scaleIn {
from {
opacity: 0;
transform: scale(0.8);
}
to {
opacity: 1;
transform: scale(1);
}
}

@keyframes up-window-scaleOut {
from {
opacity: 1;
transform: scale(1);
}
to {
opacity: 0;
transform: scale(0.8);
}
}

@keyframes up-window-slideUpOut {
from {
opacity: 1;
transform: translateY(0);
}
to {
opacity: 0;
transform: translateY(100%);
}
}

@keyframes up-window-slideDownOut {
from {
opacity: 1;
transform: translateY(0);
}
to {
opacity: 0;
transform: translateY(-100%);
}
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,91 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { UpWindowAngularComponent } from './up-window-angular.component';
import { By } from '@angular/platform-browser';

describe('UpWindowAngularComponent', () => {
let component: UpWindowAngularComponent;
let fixture: ComponentFixture<UpWindowAngularComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [UpWindowAngularComponent],
declarations: [UpWindowAngularComponent],
}).compileComponents();

fixture = TestBed.createComponent(UpWindowAngularComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
it('should create the component', () => {
expect(component).toBeTruthy();
});

it('should render title and subtitle', () => {
component.title = 'Test Title';
component.subtitle = 'Test Subtitle';
fixture.detectChanges();

const titleElement = fixture.debugElement.query(By.css('.up-window-title')).nativeElement;
const subtitleElement = fixture.debugElement.query(By.css('.up-window-subtitle')).nativeElement;

expect(titleElement.textContent).toContain('Test Title');
expect(subtitleElement.textContent).toContain('Test Subtitle');
});

it('should apply the correct animation class when opening and closing the window', () => {
component.animation = 'slide';
component.isOpen.set(true);
fixture.detectChanges();

let windowElement = fixture.debugElement.query(By.css('.up-window'));
expect(windowElement.classes['slide']).toBeTruthy();

component.closeWindow();
fixture.detectChanges();

setTimeout(() => {
windowElement = fixture.debugElement.query(By.css('.up-window'));
expect(windowElement.classes['slide-out']).toBeTruthy();
expect(component.isOpen()).toBeFalse();
}, 300);
});

it('should trigger onConfirm and onCancel when buttons are clicked', () => {
spyOn(component, 'onConfirm').and.callThrough();
spyOn(component, 'onCancel').and.callThrough();

const confirmButton = fixture.debugElement.query(By.css('.btn-confirm')).nativeElement;
const cancelButton = fixture.debugElement.query(By.css('.btn-cancel')).nativeElement;

confirmButton.click();
expect(component.onConfirm).toHaveBeenCalled();

cancelButton.click();
expect(component.onCancel).toHaveBeenCalled();
});

it('should apply custom class from @Input', () => {
component.class = 'custom-class';
fixture.detectChanges();

const windowElement = fixture.debugElement.query(By.css('.up-window'));
expect(windowElement.classes['custom-class']).toBeTruthy();
});

it('should set isOpen to true when openWindow is called', () => {
component.openWindow();
expect(component.isOpen()).toBeTrue();
});

it('should set isOpen to false after closeWindow is called', () => {
component.isOpen.set(true);
fixture.detectChanges();

component.closeWindow();
fixture.detectChanges();

setTimeout(() => {
expect(component.isOpen()).toBeFalse();
}, 300);
});
});
18 changes: 16 additions & 2 deletions projects/up-window-angular/src/lib/up-window-angular.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ export class UpWindowAngularComponent implements OnInit {
@Input() subtitle: string = 'Default Subtitle';
@Input() size: string = 'medium';
@Input() class: string | undefined;

@Input() isOpen: WritableSignal<boolean> = signal(false);
@Input() animation: string = 'fade';
closingAnimation: boolean = false;

ngOnInit(): void {}

Expand All @@ -28,7 +29,20 @@ export class UpWindowAngularComponent implements OnInit {
}

closeWindow() {
this.isOpen.set(false);
this.closingAnimation = true;

setTimeout(() => {
this.isOpen.set(false);
this.closingAnimation = false;
}, 300);
}

getClass() {
return {
...(this.class ? { [this.class]: true } : {}),
[this.animation]: !this.closingAnimation,
[`${this.animation}-out`]: this.closingAnimation,
};
}

onConfirm() {
Expand Down
62 changes: 55 additions & 7 deletions src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,64 @@
</button>
</div>
<hr />
<button class="button-modal-example" type="button" (click)="openModalExample()">
Open Modal Example
<button class="button-modal-example" type="button" (click)="openWindowExample('fade')">
Open Fade Example
</button>
<up-window-angular
[isOpen]="isModalOpenExample"
title="Modal Title"
subtitle="This is the subtitle of the modal."
size="medium"
[isOpen]="isWindowOpenFade"
title="Fade Window"
subtitle="This window uses a fade animation."
animation="fade"
>
Modal Example content!
Fade Example content!
</up-window-angular>

<button class="button-modal-example" type="button" (click)="openWindowExample('slide')">
Open Slide Example
</button>
<up-window-angular
[isOpen]="isWindowOpenSlide"
title="Slide Window"
subtitle="This window uses a slide animation."
animation="slide"
>
Slide Example content!
</up-window-angular>

<button class="button-modal-example" type="button" (click)="openWindowExample('slide-up')">
Open Slide Up Example
</button>
<up-window-angular
[isOpen]="isWindowOpenSlideUp"
title="Slide Up Window"
subtitle="This window uses a slide-up animation."
animation="slide-up"
>
Slide Up Example content!
</up-window-angular>

<button class="button-modal-example" type="button" (click)="openWindowExample('slide-down')">
Open Slide Down Example
</button>
<up-window-angular
[isOpen]="isWindowOpenSlideDown"
title="Slide Down Window"
subtitle="This window uses a slide-down animation."
animation="slide-down"
>
Slide Down Example content!
</up-window-angular>

<button class="button-modal-example" type="button" (click)="openWindowExample('scale')">
Open Scale Example
</button>
<up-window-angular
[isOpen]="isWindowOpenScale"
title="Scale Window"
subtitle="This window uses a scale animation."
animation="scale"
>
Scale Example content!
</up-window-angular>
</div>
</main>
Expand Down
Loading

0 comments on commit 597cc1e

Please sign in to comment.