import {ChangeDetectorRef, Component, ElementRef, Input, ViewChild} from "@angular/core"; import {FormBuilder, FormControl, UntypedFormArray} from "@angular/forms"; import {ActivatedRoute, Router} from "@angular/router"; import {Subscription} from "rxjs"; import {EnvProperties} from "../../utils/properties/env-properties"; import {properties} from "../../../../environments/environment"; import {StringUtils} from "../../utils/string-utils.class"; import {debounceTime, distinctUntilChanged} from "rxjs/operators"; import {HelperFunctions} from "../../utils/HelperFunctions.class"; import {ISVocabulariesService} from "../../utils/staticAutoComplete/ISVocabularies.service"; import Timeout = NodeJS.Timeout; declare var UIkit; @Component({ selector: 'fos-selection', templateUrl: 'fos-selection.component.html', styleUrls: ['fos-selection.component.less'] }) export class FosSelectionComponent { public properties: EnvProperties = properties; @Input() subjects: string[]; @Input() inModal: boolean = false; @Input() contentHeight: number = 0; @ViewChild("searchElement") searchElement: ElementRef; public loading: boolean; public fos: any[] = []; // public fosOptions: Map; public fosOptions: UntypedFormArray; public activeSection: string; public keywordControl: FormControl; public keyword: string; public viewResults = []; public result = []; private subscriptions: Subscription[] = []; private observer: IntersectionObserver; private timeout: Timeout; @ViewChild('tabs') tabs: ElementRef; public sliderInit: boolean = false; constructor( private vocabulariesService: ISVocabulariesService, private fb: FormBuilder, private cdr: ChangeDetectorRef, private route: ActivatedRoute, private _router: Router ) {} ngOnInit() { this.loading = true; this.vocabulariesService.getFos(properties).subscribe(data => { this.fos = data['fos']; this.convertFosToOptions(); if (typeof document !== 'undefined') { setTimeout(()=> { let slider = UIkit.slider(this.tabs.nativeElement); slider.clsActive = 'uk-slider-active'; slider.updateActiveClasses(); this.sliderInit = true; slider.slides.forEach(item => { item.classList.remove('uk-active'); }); if (this.route.snapshot.fragment) { this.activeSection = this.route.snapshot.fragment; let i = this.fos.findIndex(item => item.id == this.route.snapshot.fragment); slider.show(i); } else { this.activeSection = this.fos[0].id; } if(typeof IntersectionObserver !== "undefined") { this.setObserver(); } this.cdr.detectChanges(); }); } this.subscriptions.push(this.route.fragment.subscribe(fragment => { if(fragment) { this.activeSection = fragment; if(this.tabs) { let slider = UIkit.slider(this.tabs.nativeElement); let i = this.fos.findIndex(item => item.id == fragment); slider.show(i); } } else { this.activeSection = this.fos[0].id; } })); this.keywordControl = this.fb.control(''); this.subscriptions.push(this.keywordControl.valueChanges.pipe(debounceTime(500), distinctUntilChanged()).subscribe(value => { this.keyword = value; this.findMatches(this.keyword); if (typeof IntersectionObserver !== 'undefined') { setTimeout(() => { this.setObserver(); }); } })); this.loading = false; }); } ngOnDestroy() { for (let sub of this.subscriptions) { sub.unsubscribe(); } if(this.observer) { this.observer.disconnect(); } } private setObserver() { if(this.observer) { this.observer.disconnect(); } this.observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if(entry.isIntersecting) { if(this.timeout) { clearTimeout(this.timeout); } this.timeout = setTimeout(() => { if(!this.inModal) { this._router.navigate(['./'], { fragment: entry.target.id, relativeTo: this.route, state: {disableScroll: true} }); } else { this.activeSection = entry.target.id; } }, 200); } }); }, {threshold: 0.1, rootMargin: '-100px'}); this.fos.forEach(fos => { let element = document.getElementById(fos.id); if(element) { this.observer.observe(element); } }); } convertFosToOptions() { this.fosOptions = this.fb.array([]); this.fos.forEach(fos => { this.fosOptions.push(this.fb.group({ id: this.fb.control(fos.id), checked: this.fb.control(false) })); if(fos.children) { fos.children.forEach(child => { this.fosOptions.push(this.fb.group({ id: this.fb.control(fos.id), checked: this.fb.control(false) })); if(child.children) { child.children.forEach(child2 => { this.fosOptions.push(this.fb.group({ id: this.fb.control(child2.id), checked: this.fb.control(this.subjects?.includes(child2.id)) })); }); } }); } }); } findMatches(value: string) { this.viewResults = JSON.parse(JSON.stringify(this.fos)); let matchLevel1: boolean = false; let matchLevel2: boolean = false; // 1st level search if(this.viewResults.length) { this.viewResults = this.viewResults.filter(item => { matchLevel1 = !!item.id.includes(value?.toLowerCase()); // // 2nd level search if(item.children?.length && !matchLevel1) { item.children = item.children.filter(subItem => { matchLevel2 = !!subItem.id.includes(value?.toLowerCase()); // 3rd level search if(subItem.children?.length && !matchLevel2) { subItem.children = subItem.children.filter(subSubItem => subSubItem.id.includes(value?.toLowerCase())); } return subItem.children?.length > 0 || matchLevel2; }); } return item.children?.length > 0; }); } } highlightKeyword(name) { if(name.includes(this.keyword.toLowerCase())) { return name.replace(new RegExp(this.keyword, "gi"), (matchedValue) => `${matchedValue}`); } else { return name; } } public reset() { this.fosOptions.controls.forEach(control => { control.get('checked').setValue(this.subjects?.includes(control.value.id)); }); } public urlEncodeAndQuote(str: string): string { return StringUtils.quote(StringUtils.URIEncode(str)); } public scrollToId(value: string) { HelperFunctions.scrollToId(value); this.activeSection = value; } public getSelectedSubjects() { if(this.fosOptions) { return this.fosOptions.value.filter(sub => sub.checked == true); } return []; } public getControl(id: string) { if(this.fosOptions?.controls) { return this.fosOptions.controls.find(control => control.value.id === id); } return null; } public get hasChanges() { return !!this.fosOptions && this.fosOptions.dirty; } get calculatedHeight(): number { if(this.contentHeight && this.searchElement) { return this.contentHeight - this.searchElement.nativeElement.offsetHeight - 100; } } }