- Published on
Making resusable components
- Authors
- Name
- Anders Lind
- @testdrivencoder
How to make reusable components
Drop Down class
$accent-color: cornflowerblue;
$text-color: #444;
$width: 300px;
$font-size: 0.85em;
$background-color: white;
$border-color: #ddd;
.dropdown-collection {
width: $width;
position: relative;
margin-right: 8px;
}
.dropdown-button {
border: none;
border-top: 1px solid $border-color;
position: relative;
justify-content: space-between;
padding: 12px 32px 12px 12px;
background-color: $background-color;
display: flex;
font-size: $font-size;
width: 100%;
cursor: pointer;
align-items: center;
background-image: url("/assets/svg/chevron.svg");
background-size: 10px;
background-position: right 16px bottom 45%;
background-repeat: no-repeat;
&:hover {
outline: 1px solid fade_out($accent-color, 0.5);
}
}
.dropdown-value {
font-weight: 600;
}
.dropdown-list {
display: none;
background: $background-color;
list-style: none;
border: 1px solid $accent-color;
box-shadow: 0px 0px 15px -1px fade_out($accent-color, 0.6);
position: absolute;
bottom: 0;
left: 0;
padding: 0;
z-index: 10;
right: 0;
margin: 0;
transform: translateY(100%);
li {
padding: 4px 16px;
cursor: pointer;
font-weight: 700;
&.selected {
background: darken($background-color, 5%);
}
&:hover {
background: fade_out($accent-color, 0.6);
}
}
&.dropdown-open {
display: block;
}
}
@Component({
selector: 'custom-select',
templateUrl: './select.component.html',
styleUrls: ['./select.component.scss'],
host: {
'(document:keydown)': 'handleKeyboardEvents($event)'
}
})
export class SelectComponent {
@Input() options: any;
@Input() title: string;
@Output() currentValueChange = new EventEmitter();
public currentValue;
public dropdownOpen: boolean = false;
public get dropdownElement(): Element {return this.elem.nativeElement.querySelector('.dropdown-list')}
private currentIndex = -1;
constructor(
private elem: ElementRef
) { }
ngOnInit(): void {
this.currentValue = this.options[0];
}
handleKeyboardEvents($event: KeyboardEvent) {
if (this.dropdownOpen) {
$event.preventDefault();
} else {
return;
}
if ($event.code === 'ArrowUp') {
if (this.currentIndex < 0) {
this.currentIndex = 0;
} else if (this.currentIndex > 0) {
this.currentIndex--;
}
this.elem.nativeElement.querySelectorAll('li').item(this.currentIndex).focus();
} else if ($event.code === 'ArrowDown') {
if (this.currentIndex < 0) {
this.currentIndex = 0;
} else if (this.currentIndex < this.options.length-1) {
this.currentIndex++;
}
this.elem.nativeElement.querySelectorAll('li').item(this.currentIndex).focus();
} else if (($event.code === 'Enter' || $event.code === 'NumpadEnter') && this.currentIndex >= 0) {
this.selectByIndex(this.currentIndex);
} else if ($event.code === 'Escape') {
this.closeDropdown();
}
}
closeDropdown() {
this.dropdownElement.setAttribute('aria-expanded', "false");
this.currentIndex = -1;
this.dropdownOpen = false;
}
selectByIndex(i: number) {
let value = this.options[i];
this.select(value);
}
select(value) {
this.currentValue = value;
this.closeDropdown();
this.currentValueChange.emit(this.currentValue);
}
toggleDropdown() {
this.dropdownOpen = !this.dropdownOpen;
this.dropdownElement.setAttribute('aria-expanded', this.dropdownOpen ? "true" : "false");
}
}
Implementation
<custom-select [title]="'My dropdown'" [options]="dropdownOptions"(currentValueChange)="dropdownValueChanged($event)"></custom-select>
Come and see our updated blog next week.
Thanks, Anders