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 {each} from "@marcj/estdlib"; import {DropdownComponent} from "../button"; @Component({ selector: 'dui-emoji-dropdown', template: ` `, 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 each(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]' }) export class EmojiDropdownDirective implements OnChanges, AfterViewInit, OnDestroy { protected dropdown?: ComponentRef; @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(); } } }