244 lines
7.4 KiB
TypeScript
244 lines
7.4 KiB
TypeScript
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<string, boolean>;
|
|
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;
|
|
}
|
|
|
|
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 document !== '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) => `<mark class="highlighted">${matchedValue}</mark>`);
|
|
} 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;
|
|
}
|
|
}
|
|
}
|