import {
ChangeDetectorRef,
Component,
ContentChildren,
ElementRef,
forwardRef,
HostBinding,
HostListener,
Injectable,
Injector,
Input,
OnChanges,
OnDestroy,
Optional,
QueryList,
SimpleChanges,
SkipSelf
} from '@angular/core';
import {NavigationEnd, Router, UrlTree} from '@angular/router';
import {ngValueAccessor, ValueAccessorBase} from "../../core/form";
import {Subscription} from "rxjs";
@Component({
selector: 'dui-list-title',
template: `
`,
styleUrls: ['./list-title.component.scss']
})
export class ListTitleComponent {
constructor() {
}
}
@Component({
selector: 'dui-list',
template: `
`,
styleUrls: ['./list.component.scss'],
host: {
'[class.white]': 'white !== false',
'[class.overlay-scrollbar-small]': 'true',
'[class.focusable]': 'focusable',
'[class.delimiter-line]': 'delimiterLine !== false',
},
providers: [ngValueAccessor(ListComponent)]
})
@Injectable()
export class ListComponent extends ValueAccessorBase {
@Input() white: boolean | '' = false;
@Input() focusable: boolean = true;
@Input() delimiterLine: boolean | '' = false;
@HostBinding('tabindex') tabIndex: number = 1;
@ContentChildren(forwardRef(() => ListItemComponent), {descendants: true}) list!: QueryList;
constructor(
protected injector: Injector,
public readonly cd: ChangeDetectorRef,
@SkipSelf() public readonly cdParent: ChangeDetectorRef,
) {
super(injector, cd, cdParent);
}
@HostListener('keydown', ['$event'])
public async onKeyDown(event: KeyboardEvent) {
if (event.key === 'ArrowDown') {
event.preventDefault();
const selectedItem = this.getSelectedItem();
if (selectedItem) {
const position = this.list.toArray().indexOf(selectedItem);
if (this.list.toArray()[position + 1]) {
await this.list.toArray()[position + 1].select();
}
}
}
if (event.key === 'ArrowUp') {
event.preventDefault();
const selectedItem = this.getSelectedItem();
if (selectedItem) {
const position = this.list.toArray().indexOf(selectedItem);
if (this.list.toArray()[position - 1]) {
await this.list.toArray()[position - 1].select();
}
}
}
}
public getSelectedItem(): ListItemComponent | undefined {
for (const item of this.list.toArray()) {
if (item.isSelected()) {
return item;
}
}
return;
}
}
@Component({
selector: 'dui-list-item',
template: `
`,
host: {
'[class.selected]': 'isSelected()',
},
styleUrls: ['./list-item.component.scss']
})
export class ListItemComponent implements OnChanges, OnDestroy {
@Input() value: any;
@Input() routerLink?: string | UrlTree | any[];
protected routerSub?: Subscription;
constructor(
public list: ListComponent,
public element: ElementRef,
@SkipSelf() public cd: ChangeDetectorRef,
@Optional() public router?: Router,
) {
this.element.nativeElement.removeAttribute('tabindex');
this.list.registerOnChange(() => {
this.cd.detectChanges();
});
if (this.router) {
this.routerSub = this.router.events.subscribe((event) => {
if (event instanceof NavigationEnd) {
this.cd.detectChanges();
}
})
}
}
ngOnDestroy(): void {
if (this.routerSub) {
this.routerSub.unsubscribe();
}
}
ngOnChanges(changes: SimpleChanges): void {
this.cd.detectChanges();
}
public async select() {
if (this.routerLink && this.router) {
if ('string' === typeof this.routerLink) {
await this.router.navigateByUrl(this.routerLink);
} else if (Array.isArray(this.routerLink)) {
await this.router.navigate(this.routerLink);
} else {
await this.router.navigateByUrl(this.router.serializeUrl(this.routerLink!));
}
} else {
this.list.innerValue = this.value;
}
}
public isSelected(): boolean {
if (this.value !== undefined) {
return this.list.innerValue === this.value;
}
if (this.routerLink && this.router) {
if ('string' === typeof this.routerLink) {
return this.router.isActive(this.routerLink, false);
} else if (Array.isArray(this.routerLink)) {
return this.router.isActive(this.router.createUrlTree(this.routerLink), false);
} else {
return this.router.isActive(this.routerLink!, false);
}
}
return false;
}
@HostListener('mousedown')
public onClick() {
this.list.innerValue = this.value;
}
}