import { AfterViewInit, ChangeDetectorRef, Component, ContentChild, ContentChildren, Directive, ElementRef, HostBinding, HostListener, Injector, Input, OnChanges, OnDestroy, QueryList, SimpleChanges, SkipSelf, TemplateRef, ViewChild } from "@angular/core"; import {Subscription} from "rxjs"; import {ngValueAccessor, ValueAccessorBase} from "../../core/form"; import {Overlay} from "@angular/cdk/overlay"; import {DropdownComponent} from "../button/dropdown.component"; import {ButtonComponent} from "../button/button.component"; /** * Necessary directive to get a dynamic rendered option. * * ```html * * * {{item.fieldName | date}} * * * ``` */ @Directive({ selector: '[dynamicOption]', }) export class DynamicOptionDirective { constructor( public template: TemplateRef ) { } } @Component({ selector: 'dui-option', template: ` ` }) export class OptionDirective { @Input() value: any; @ContentChild(DynamicOptionDirective, {static: false}) dynamic?: DynamicOptionDirective; constructor(public readonly element: ElementRef) { } } class NotSelected {} @Component({ selector: 'dui-select', template: `
{{placeholder}}
`, styleUrls: ['./selectbox.component.scss'], host: { '[attr.tabIndex]': '1', '[class.split]': '!!button', '[class.textured]': 'textured !== false', '[class.small]': 'small !== false', }, providers: [ngValueAccessor(SelectboxComponent)] }) export class SelectboxComponent extends ValueAccessorBase implements AfterViewInit, OnDestroy, OnChanges { @Input() placeholder: string = ''; /** * Different textured styled. */ @Input() textured: boolean | '' = false; /** * Smaller text and height. */ @Input() small: boolean | '' = false; @ContentChild(ButtonComponent, {static: false}) button?: ButtonComponent; @ContentChildren(OptionDirective, {descendants: true}) options?: QueryList; @ViewChild('dropdown', {static: false}) dropdown!: DropdownComponent; public label: string = ''; public optionsValueMap = new Map(); protected changeSubscription?: Subscription; constructor( protected overlay: Overlay, protected injector: Injector, public element: ElementRef, public readonly cd: ChangeDetectorRef, @SkipSelf() public readonly cdParent: ChangeDetectorRef, ) { super(injector, cd, cdParent); this.innerValue = new NotSelected; } ngAfterViewInit(): void { if (this.options) { this.changeSubscription = this.options.changes.subscribe(() => this.updateMap()); setTimeout(() => { this.updateMap(); }); } } public select(value: T) { this.innerValue = value; this.touch(); this.dropdown.close(); } @HostListener('mousedown') public onClick() { if (this.disabled) return; if (this.button) return; this.dropdown.toggle(); } ngOnChanges(changes: SimpleChanges) { } async writeValue(value?: T | NotSelected): Promise { super.writeValue(value); } @HostBinding('class.selected') get isSelected(): boolean { return !(this.innerValue instanceof NotSelected); } protected updateMap() { this.optionsValueMap.clear(); if (!this.options) return; for (const option of this.options.toArray()) { this.optionsValueMap.set(option.value, option); } this.cd.detectChanges(); } ngOnDestroy(): void { if (this.changeSubscription) { this.changeSubscription.unsubscribe(); } } }