Lightweight Angular tooltip component
up vote
6
down vote
favorite
I made a small Popup component to show different error/success...etc. messages.
Template:
<div [@visibilityChanged]="isVisible" *ngIf="shouldShow" [ngClass]="messageType">
<ng-content></ng-content>
</div>
Component:
import {
Component, OnChanges, Input,
Renderer2, AfterViewInit, ElementRef, AfterViewChecked,
HostListener
} from '@angular/core';
import { trigger, state, animate, transition, style } from '@angular/animations';
/**
* This class represents the popup component.
*/
@Component({
moduleId: module.id,
selector: 'sd-popup',
templateUrl: 'popup.component.html',
styleUrls: ['popup.component.css'],
animations: [trigger('visibilityChanged', [
transition(':enter', [
style({ opacity: 0 }),
animate(100, style({ opacity: 1 }))
]),
transition(':leave', [
style({ opacity: 1 }),
animate(100, style({ opacity: 0 }))
])
])
]
})
export class PopUpComponent implements AfterViewInit, AfterViewChecked {
@Input() messageType: string | string;
@Input() errorState: boolean;
@Input() parentEl: Element;
public shouldShow: boolean;
constructor(private popupEl: ElementRef,
private renderer: Renderer2) {
}
ngAfterViewInit(): void {
// Append to body for better calculation
document.body.appendChild(this.popupEl.nativeElement);
//use random delay for load balancing
this.renderer.listen(this.parentEl, 'keydown', () => {
setTimeout(() => {
this.shouldShow = this.errorState;
}, this.getRandomInt(1, 50));
});
}
@HostListener('window:resize')
@HostListener('window:scroll')
ngAfterViewChecked(): void {
// calculate position when view checked,window is resized or scrolling
// consider to use load balancing here as well (random delay might be visible)
this.calculatePosition(this.popupEl.nativeElement, this.parentEl);
}
public calculatePosition(element: any, target: any): void {
const targetOffset = target.getBoundingClientRect();
this.renderer.setStyle(element, 'top', `${(targetOffset.top + document.documentElement.scrollTop) - element.offsetHeight - 5}px`);
this.renderer.setStyle(element, 'left', `${targetOffset.left + (target.offsetWidth / 2 - element.offsetWidth / 2)}px`);
this.renderer.setStyle(element, 'zindex', `${target.zindex + 100}px`);
}
getRandomInt(min: number, max: number) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
}
SCSS:
:host {
position:absolute;
opacity: 0.9;
}
.errorMessage {
padding: 0.375rem 0.75rem;
font-size: 1rem;
line-height: 1.5;
color: #fff;
background-image: none;
background-clip: padding-box;
border-radius: 0.25rem;
}
.warning {
@extend .errorMessage;
background-color: #CD464D;
border: 1px solid #CD464D;
}
.success {
@extend .errorMessage;
background-color: rgb(15, 230, 147);
border: 1px solid rgb(15, 230, 147);
}
div:after, div:before {
top: 100%;
left: 50%;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
}
div:after {
border-color: rgba(136, 183, 213, 0);
border-top-color: inherit;
border-width: 5px;
margin-left: -5px;
}
div:before {
border-color: rgba(194, 225, 245, 0);
border-top-color: inherit;
border-width: 6px;
margin-left: -6px;
}
And finally usage:
<input #passInput type="password" name="password" id="password" class="form-control" formControlName="password"/>
<sd-popup [parentEl]="passInput" [errorState]="password.errors?.required && (password.dirty || password.touched)"
messageType="success">Password is required</sd-popup>
Tell me what do you think about it!
html5 typescript sass angular-2+
bumped to the homepage by Community♦ 11 mins ago
This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.
add a comment |
up vote
6
down vote
favorite
I made a small Popup component to show different error/success...etc. messages.
Template:
<div [@visibilityChanged]="isVisible" *ngIf="shouldShow" [ngClass]="messageType">
<ng-content></ng-content>
</div>
Component:
import {
Component, OnChanges, Input,
Renderer2, AfterViewInit, ElementRef, AfterViewChecked,
HostListener
} from '@angular/core';
import { trigger, state, animate, transition, style } from '@angular/animations';
/**
* This class represents the popup component.
*/
@Component({
moduleId: module.id,
selector: 'sd-popup',
templateUrl: 'popup.component.html',
styleUrls: ['popup.component.css'],
animations: [trigger('visibilityChanged', [
transition(':enter', [
style({ opacity: 0 }),
animate(100, style({ opacity: 1 }))
]),
transition(':leave', [
style({ opacity: 1 }),
animate(100, style({ opacity: 0 }))
])
])
]
})
export class PopUpComponent implements AfterViewInit, AfterViewChecked {
@Input() messageType: string | string;
@Input() errorState: boolean;
@Input() parentEl: Element;
public shouldShow: boolean;
constructor(private popupEl: ElementRef,
private renderer: Renderer2) {
}
ngAfterViewInit(): void {
// Append to body for better calculation
document.body.appendChild(this.popupEl.nativeElement);
//use random delay for load balancing
this.renderer.listen(this.parentEl, 'keydown', () => {
setTimeout(() => {
this.shouldShow = this.errorState;
}, this.getRandomInt(1, 50));
});
}
@HostListener('window:resize')
@HostListener('window:scroll')
ngAfterViewChecked(): void {
// calculate position when view checked,window is resized or scrolling
// consider to use load balancing here as well (random delay might be visible)
this.calculatePosition(this.popupEl.nativeElement, this.parentEl);
}
public calculatePosition(element: any, target: any): void {
const targetOffset = target.getBoundingClientRect();
this.renderer.setStyle(element, 'top', `${(targetOffset.top + document.documentElement.scrollTop) - element.offsetHeight - 5}px`);
this.renderer.setStyle(element, 'left', `${targetOffset.left + (target.offsetWidth / 2 - element.offsetWidth / 2)}px`);
this.renderer.setStyle(element, 'zindex', `${target.zindex + 100}px`);
}
getRandomInt(min: number, max: number) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
}
SCSS:
:host {
position:absolute;
opacity: 0.9;
}
.errorMessage {
padding: 0.375rem 0.75rem;
font-size: 1rem;
line-height: 1.5;
color: #fff;
background-image: none;
background-clip: padding-box;
border-radius: 0.25rem;
}
.warning {
@extend .errorMessage;
background-color: #CD464D;
border: 1px solid #CD464D;
}
.success {
@extend .errorMessage;
background-color: rgb(15, 230, 147);
border: 1px solid rgb(15, 230, 147);
}
div:after, div:before {
top: 100%;
left: 50%;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
}
div:after {
border-color: rgba(136, 183, 213, 0);
border-top-color: inherit;
border-width: 5px;
margin-left: -5px;
}
div:before {
border-color: rgba(194, 225, 245, 0);
border-top-color: inherit;
border-width: 6px;
margin-left: -6px;
}
And finally usage:
<input #passInput type="password" name="password" id="password" class="form-control" formControlName="password"/>
<sd-popup [parentEl]="passInput" [errorState]="password.errors?.required && (password.dirty || password.touched)"
messageType="success">Password is required</sd-popup>
Tell me what do you think about it!
html5 typescript sass angular-2+
bumped to the homepage by Community♦ 11 mins ago
This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.
add a comment |
up vote
6
down vote
favorite
up vote
6
down vote
favorite
I made a small Popup component to show different error/success...etc. messages.
Template:
<div [@visibilityChanged]="isVisible" *ngIf="shouldShow" [ngClass]="messageType">
<ng-content></ng-content>
</div>
Component:
import {
Component, OnChanges, Input,
Renderer2, AfterViewInit, ElementRef, AfterViewChecked,
HostListener
} from '@angular/core';
import { trigger, state, animate, transition, style } from '@angular/animations';
/**
* This class represents the popup component.
*/
@Component({
moduleId: module.id,
selector: 'sd-popup',
templateUrl: 'popup.component.html',
styleUrls: ['popup.component.css'],
animations: [trigger('visibilityChanged', [
transition(':enter', [
style({ opacity: 0 }),
animate(100, style({ opacity: 1 }))
]),
transition(':leave', [
style({ opacity: 1 }),
animate(100, style({ opacity: 0 }))
])
])
]
})
export class PopUpComponent implements AfterViewInit, AfterViewChecked {
@Input() messageType: string | string;
@Input() errorState: boolean;
@Input() parentEl: Element;
public shouldShow: boolean;
constructor(private popupEl: ElementRef,
private renderer: Renderer2) {
}
ngAfterViewInit(): void {
// Append to body for better calculation
document.body.appendChild(this.popupEl.nativeElement);
//use random delay for load balancing
this.renderer.listen(this.parentEl, 'keydown', () => {
setTimeout(() => {
this.shouldShow = this.errorState;
}, this.getRandomInt(1, 50));
});
}
@HostListener('window:resize')
@HostListener('window:scroll')
ngAfterViewChecked(): void {
// calculate position when view checked,window is resized or scrolling
// consider to use load balancing here as well (random delay might be visible)
this.calculatePosition(this.popupEl.nativeElement, this.parentEl);
}
public calculatePosition(element: any, target: any): void {
const targetOffset = target.getBoundingClientRect();
this.renderer.setStyle(element, 'top', `${(targetOffset.top + document.documentElement.scrollTop) - element.offsetHeight - 5}px`);
this.renderer.setStyle(element, 'left', `${targetOffset.left + (target.offsetWidth / 2 - element.offsetWidth / 2)}px`);
this.renderer.setStyle(element, 'zindex', `${target.zindex + 100}px`);
}
getRandomInt(min: number, max: number) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
}
SCSS:
:host {
position:absolute;
opacity: 0.9;
}
.errorMessage {
padding: 0.375rem 0.75rem;
font-size: 1rem;
line-height: 1.5;
color: #fff;
background-image: none;
background-clip: padding-box;
border-radius: 0.25rem;
}
.warning {
@extend .errorMessage;
background-color: #CD464D;
border: 1px solid #CD464D;
}
.success {
@extend .errorMessage;
background-color: rgb(15, 230, 147);
border: 1px solid rgb(15, 230, 147);
}
div:after, div:before {
top: 100%;
left: 50%;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
}
div:after {
border-color: rgba(136, 183, 213, 0);
border-top-color: inherit;
border-width: 5px;
margin-left: -5px;
}
div:before {
border-color: rgba(194, 225, 245, 0);
border-top-color: inherit;
border-width: 6px;
margin-left: -6px;
}
And finally usage:
<input #passInput type="password" name="password" id="password" class="form-control" formControlName="password"/>
<sd-popup [parentEl]="passInput" [errorState]="password.errors?.required && (password.dirty || password.touched)"
messageType="success">Password is required</sd-popup>
Tell me what do you think about it!
html5 typescript sass angular-2+
I made a small Popup component to show different error/success...etc. messages.
Template:
<div [@visibilityChanged]="isVisible" *ngIf="shouldShow" [ngClass]="messageType">
<ng-content></ng-content>
</div>
Component:
import {
Component, OnChanges, Input,
Renderer2, AfterViewInit, ElementRef, AfterViewChecked,
HostListener
} from '@angular/core';
import { trigger, state, animate, transition, style } from '@angular/animations';
/**
* This class represents the popup component.
*/
@Component({
moduleId: module.id,
selector: 'sd-popup',
templateUrl: 'popup.component.html',
styleUrls: ['popup.component.css'],
animations: [trigger('visibilityChanged', [
transition(':enter', [
style({ opacity: 0 }),
animate(100, style({ opacity: 1 }))
]),
transition(':leave', [
style({ opacity: 1 }),
animate(100, style({ opacity: 0 }))
])
])
]
})
export class PopUpComponent implements AfterViewInit, AfterViewChecked {
@Input() messageType: string | string;
@Input() errorState: boolean;
@Input() parentEl: Element;
public shouldShow: boolean;
constructor(private popupEl: ElementRef,
private renderer: Renderer2) {
}
ngAfterViewInit(): void {
// Append to body for better calculation
document.body.appendChild(this.popupEl.nativeElement);
//use random delay for load balancing
this.renderer.listen(this.parentEl, 'keydown', () => {
setTimeout(() => {
this.shouldShow = this.errorState;
}, this.getRandomInt(1, 50));
});
}
@HostListener('window:resize')
@HostListener('window:scroll')
ngAfterViewChecked(): void {
// calculate position when view checked,window is resized or scrolling
// consider to use load balancing here as well (random delay might be visible)
this.calculatePosition(this.popupEl.nativeElement, this.parentEl);
}
public calculatePosition(element: any, target: any): void {
const targetOffset = target.getBoundingClientRect();
this.renderer.setStyle(element, 'top', `${(targetOffset.top + document.documentElement.scrollTop) - element.offsetHeight - 5}px`);
this.renderer.setStyle(element, 'left', `${targetOffset.left + (target.offsetWidth / 2 - element.offsetWidth / 2)}px`);
this.renderer.setStyle(element, 'zindex', `${target.zindex + 100}px`);
}
getRandomInt(min: number, max: number) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
}
SCSS:
:host {
position:absolute;
opacity: 0.9;
}
.errorMessage {
padding: 0.375rem 0.75rem;
font-size: 1rem;
line-height: 1.5;
color: #fff;
background-image: none;
background-clip: padding-box;
border-radius: 0.25rem;
}
.warning {
@extend .errorMessage;
background-color: #CD464D;
border: 1px solid #CD464D;
}
.success {
@extend .errorMessage;
background-color: rgb(15, 230, 147);
border: 1px solid rgb(15, 230, 147);
}
div:after, div:before {
top: 100%;
left: 50%;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
}
div:after {
border-color: rgba(136, 183, 213, 0);
border-top-color: inherit;
border-width: 5px;
margin-left: -5px;
}
div:before {
border-color: rgba(194, 225, 245, 0);
border-top-color: inherit;
border-width: 6px;
margin-left: -6px;
}
And finally usage:
<input #passInput type="password" name="password" id="password" class="form-control" formControlName="password"/>
<sd-popup [parentEl]="passInput" [errorState]="password.errors?.required && (password.dirty || password.touched)"
messageType="success">Password is required</sd-popup>
Tell me what do you think about it!
html5 typescript sass angular-2+
html5 typescript sass angular-2+
edited Oct 3 at 5:10
200_success
127k15148412
127k15148412
asked Dec 3 '17 at 0:13
Embrioka
635
635
bumped to the homepage by Community♦ 11 mins ago
This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.
bumped to the homepage by Community♦ 11 mins ago
This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
up vote
0
down vote
It just looks really good.
I think it stayed this long unanswered because there's not much feedback to give.
I reviewed and liked
- The commenting
- The naming
- The sizes of your functions
- Reading flow
I am not convinced that choosing a random int actually helps the load balancing much, but don't have a better alternative. I would also probably put the random number generator in a helper class, it does not really belong in a tooltip component.
add a comment |
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
0
down vote
It just looks really good.
I think it stayed this long unanswered because there's not much feedback to give.
I reviewed and liked
- The commenting
- The naming
- The sizes of your functions
- Reading flow
I am not convinced that choosing a random int actually helps the load balancing much, but don't have a better alternative. I would also probably put the random number generator in a helper class, it does not really belong in a tooltip component.
add a comment |
up vote
0
down vote
It just looks really good.
I think it stayed this long unanswered because there's not much feedback to give.
I reviewed and liked
- The commenting
- The naming
- The sizes of your functions
- Reading flow
I am not convinced that choosing a random int actually helps the load balancing much, but don't have a better alternative. I would also probably put the random number generator in a helper class, it does not really belong in a tooltip component.
add a comment |
up vote
0
down vote
up vote
0
down vote
It just looks really good.
I think it stayed this long unanswered because there's not much feedback to give.
I reviewed and liked
- The commenting
- The naming
- The sizes of your functions
- Reading flow
I am not convinced that choosing a random int actually helps the load balancing much, but don't have a better alternative. I would also probably put the random number generator in a helper class, it does not really belong in a tooltip component.
It just looks really good.
I think it stayed this long unanswered because there's not much feedback to give.
I reviewed and liked
- The commenting
- The naming
- The sizes of your functions
- Reading flow
I am not convinced that choosing a random int actually helps the load balancing much, but don't have a better alternative. I would also probably put the random number generator in a helper class, it does not really belong in a tooltip component.
answered Oct 2 at 13:33
konijn
26.9k453235
26.9k453235
add a comment |
add a comment |
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f181880%2flightweight-angular-tooltip-component%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown