UNPKG

@deepkit/desktop-ui

Version:

Library for desktop UI widgets in Angular 10+

249 lines (211 loc) 8.24 kB
/* * Deepkit Framework * Copyright (C) 2021 Deepkit UG, Marc J. Schmidt * * This program is free software: you can redistribute it and/or modify * it under the terms of the MIT License. * * You should have received a copy of the MIT License along with this program. */ import { AfterViewInit, ChangeDetectorRef, Component, ComponentFactoryResolver, ComponentRef, Directive, ElementRef, EventEmitter, HostListener, Input, OnChanges, OnDestroy, Output, SimpleChanges, ViewChild, ViewContainerRef, } from '@angular/core'; import * as emojis from './emojis'; import { EmojiCategory } from './emojis'; import { DropdownComponent } from '../button'; @Component({ selector: 'dui-emoji-dropdown', standalone: false, template: ` <dui-dropdown overlay [center]="true" (shown)="searchInput.focusInput()" #dropdown [height]="360" [width]="320" [minWidth]="320"> <div class="dropdown"> <dui-input round style="width: calc(100% - 13px)" clearer focus #searchInput icon="search" placeholder="Search" [(ngModel)]="search"></dui-input> <div *ngIf="search"> <div class="emojis"> <div class="emoji" *ngFor="let name of find(search)" [class.selected]="name === emoji" [attr.id]="'emoji_' + name" (click)="choose(name)"> <div class="emoji-image" [style.backgroundPosition]="emojis.emojis[name] ? (-((emojis.emojis[name].x * 34) + 1) + 'px ' + -((emojis.emojis[name].y * 34) + 1) + 'px') : ''"></div> </div> </div> </div> <ng-container *ngIf="!search"> <div *ngIf="getLast(lastEmojis) as lastEmojis"> <div class="category-title"> Frequently used </div> <div class="emojis"> <div class="emoji" *ngFor="let name of lastEmojis" [attr.id]="'emoji_' + name" [class.selected]="name === emoji" (click)="choose(name)"> <div class="emoji-image" [style.backgroundPosition]="emojis.emojis[name] ? (-((emojis.emojis[name].x * 34) + 1) + 'px ' + -((emojis.emojis[name].y * 34) + 1) + 'px') : ''"></div> </div> </div> </div> <div *ngFor="let categoryName of categories"> <ng-container *ngIf="getCategory(categoryName) as category"> <div class="category-title"> {{category.name}} </div> <div class="emojis"> <div class="emoji" *ngFor="let name of category.emojis" [attr.id]="'emoji_' + name" [class.selected]="name === emoji" (click)="choose(name)"> <div class="emoji-image" [style.backgroundPosition]="emojis.emojis[name] ? (-((emojis.emojis[name].x * 34) + 1) + 'px ' + -((emojis.emojis[name].y * 34) + 1) + 'px') : ''"></div> </div> </div> </ng-container> </div> </ng-container> </div> </dui-dropdown> `, styleUrls: ['./emoji-dropdown.component.scss'], }) export class EmojiDropdownComponent implements AfterViewInit { @Input() blacklist: string[] = []; @Input() lastEmojis: string[] = []; @Output() lastEmojisChange = new EventEmitter(); @Input() emoji: string = ''; @Output() emojiChange = new EventEmitter(); @ViewChild(DropdownComponent, { static: true }) dropdown!: DropdownComponent; emojis = emojis; search: string = ''; categories: string[] = [ 'Smileys & People', 'Animals & Nature', 'Food & Drink', 'Activities', 'Travel & Places', 'Objects', 'Symbols', 'Flags', ]; constructor(protected element: ElementRef, protected cd: ChangeDetectorRef) { } getLast(emojis: string[]): string[] | undefined { if (!emojis.length) return; return [...new Set(emojis.map(v => { if (v[0] === ':') return v.substring(1, v.length - 1); return v[0]; }))]; } ngAfterViewInit() { this.element.nativeElement.remove(); } public open(target: ElementRef) { if (this.search) { this.search = ''; this.cd.detectChanges(); } this.dropdown.toggle(target); if (this.emoji) { const element = document.getElementById('emoji_' + this.emoji); if (element) { element.scrollIntoView({ behavior: 'smooth', block: 'center' }); } } } find(search: string): string[] { search = search.toLowerCase(); const result: string[] = []; for (const emoji of Object.values(emojis.emojis)) { if (-1 !== emoji.name.toLowerCase().indexOf(search)) { result.push(emoji.shortName); } } return result; } choose(name: string) { name = ':' + name + ':'; this.emoji = name; this.emojiChange.emit(name); if (-1 === this.lastEmojis.indexOf(name)) { this.lastEmojis.push(name); } else { const index = this.lastEmojis.indexOf(name); if (index > 0) { this.lastEmojis.splice(index, 1); this.lastEmojis.splice(index - 1, 0, name); } } this.lastEmojis = this.lastEmojis.slice(0); this.dropdown.close(); this.cd.detectChanges(); } getCategory(name: string): EmojiCategory { for (const category of emojis.categories) { if (category.name === name) return category; } throw new Error(`No category for name ${name} found`); } } @Directive({ selector: '[duiEmojiDropdown]', standalone: false, }) export class EmojiDropdownDirective implements OnChanges, AfterViewInit, OnDestroy { protected dropdown?: ComponentRef<EmojiDropdownComponent>; @Input() lastEmojis: string[] = []; @Output() lastEmojisChange = new EventEmitter(); @Input() blacklist: string[] = []; @Input() emoji: string = ''; @Output() emojiChange = new EventEmitter(); constructor( protected elementRef: ElementRef, protected view: ViewContainerRef, protected resolver: ComponentFactoryResolver, ) { } ngAfterViewInit() { const factory = this.resolver.resolveComponentFactory(EmojiDropdownComponent); this.dropdown = this.view.createComponent(factory); this.dropdown.instance.emojiChange = this.emojiChange; this.dropdown.instance.lastEmojisChange = this.lastEmojisChange; this.dropdown.instance.blacklist = this.blacklist; this.dropdown.instance.lastEmojis = this.lastEmojis; this.dropdown.instance.emoji = this.emoji; this.dropdown.changeDetectorRef.detectChanges(); } ngOnChanges(changes: SimpleChanges): void { if (this.dropdown) { this.dropdown.instance.blacklist = this.blacklist; this.dropdown.instance.emoji = this.emoji; this.dropdown.instance.lastEmojis = this.lastEmojis; this.dropdown.changeDetectorRef.detectChanges(); } } @HostListener('click') onClick() { if (this.dropdown) { this.dropdown.instance.open(this.elementRef); } } ngOnDestroy() { if (this.dropdown) { this.dropdown.destroy(); } } }