You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
argos/dmp-frontend/src/app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents.ts

353 lines
9.6 KiB
TypeScript

import { DOCUMENT } from '@angular/common';
import { Component, EventEmitter, Inject, OnInit, Output, Input, OnChanges } from '@angular/core';
import { BaseComponent } from '@common/base/base.component';
import { interval, Subject, Subscription } from 'rxjs';
import { distinctUntilChanged } from 'rxjs/operators';
import { type } from 'os';
import { SimpleChanges } from '@angular/core';
import { ToCEntry, ToCEntryType } from '../dataset-description.component';
import { FormArray, FormGroup } from '@angular/forms';
import { VisibilityRulesService } from '../visibility-rules/visibility-rules.service';
import { Rule } from '@app/core/model/dataset-profile-definition/rule';
export interface Link {
/* id of the section*/
id: string;
/* header type h3/h4 */
type: string;
/* If the anchor is in view of the page */
active: boolean;
/* name of the anchor */
name: string;
/* top offset px of the anchor */
top: number;
page: number;
section: number;
show: boolean;
selected: boolean;
}
@Component({
selector: 'table-of-contents',
styleUrls: ['./table-of-contents.scss'],
templateUrl: './table-of-contents.html'
})
export class TableOfContents extends BaseComponent implements OnInit, OnChanges {
@Input() links: Link[];
container: string;
headerSelectors = '.toc-page-header, .toc-section-header, .toc-compositeField-header';
@Output() stepFound = new EventEmitter<LinkToScroll>();
@Output() currentLinks = new EventEmitter<Link[]>();
subscription: Subscription;
linksSubject: Subject<HTMLElement[]> = new Subject<HTMLElement[]>();
@Input() isActive: boolean;
tocentries: ToCEntry[] = null;
@Input() TOCENTRY_ID_PREFIX = '';
// visibilityRules:Rule[] = [];
@Input() visibilityRules:Rule[] = [];
@Input() showErrors: boolean = false;
@Input() selectedFieldsetId:string;
private _tocentrySelected:ToCEntry = null;
get tocentrySelected(){
return this.hasFocus?this._tocentrySelected: null;
}
set tocentrySelected(value){
this._tocentrySelected = value;
}
@Input() formGroup: FormGroup;
@Input() hasFocus: boolean = false;
show: boolean = false;
constructor(
@Inject(DOCUMENT) private _document: Document,
public visibilityRulesService: VisibilityRulesService
) {
super();
}
ngOnInit(): void {
if(this.formGroup){
this.tocentries = this.getTocEntries(this.formGroup.get('datasetProfileDefinition'));
const fg = this.formGroup.get('datasetProfileDefinition');
this.visibilityRulesService.buildVisibilityRules(this.visibilityRules, fg);
}else{
//emit value every 500ms
const source = interval(500);
this.subscription = source.subscribe(val => {
const headers = Array.from(this._document.querySelectorAll(this.headerSelectors)) as HTMLElement[];
this.linksSubject.next(headers);
});
if (!this.links || this.links.length === 0) {
this.linksSubject.asObservable()
.pipe(distinctUntilChanged((p: HTMLElement[], q: HTMLElement[]) => JSON.stringify(p) == JSON.stringify(q)))
.subscribe(headers => {
const links: Array<Link> = [];
if (headers.length) {
let page;
let section;
let show
for (const header of headers) {
let name;
let id;
if (header.classList.contains('toc-page-header')) { // deprecated after removing stepper
name = header.innerText.trim().replace(/^link/, '');
id = header.id;
page = header.id.split('_')[1];
section = undefined;
show = true;
} else if (header.classList.contains('toc-section-header')) {
name = header.childNodes[0].childNodes[0].childNodes[0].childNodes[0].childNodes[0].nodeValue.trim().replace(/^link/, '');
id = header.id;
page = header.id.split('.')[1];
section = header.id;
if (header.id.split('.')[4]) { show = false; }
else { show = true; }
} else if (header.classList.contains('toc-compositeField-header')) {
name = (header.childNodes[0]).nodeValue.trim().replace(/^link/, '');
id = header.id;
// id = header.parentElement.parentElement.parentElement.id;
show = false;
}
const { top } = header.getBoundingClientRect();
links.push({
name,
id,
type: header.tagName.toLowerCase(),
top: top,
active: false,
page: page,
section: section,
show: show,
selected: false
});
}
}
this.links = links;
// Initialize selected for button next on dataset wizard component editor
this.links.length > 0 ? this.links[0].selected = true : null;
})
}
}
}
ngOnChanges(changes: SimpleChanges) {
if(this.selectedFieldsetId){
this.tocentrySelected = this._findTocEntryById(this.selectedFieldsetId,this.tocentries);
}
// if (!this.isActive && this.links && this.links.length > 0) {
// this.links.forEach(link => {
// link.selected = false;
// })
// this.links[0].selected = true;
// }
}
goToStep(link: Link) {
this.stepFound.emit({
page: link.page,
section: link.section
});
this.currentLinks.emit(this.links);
setTimeout(() => {
const target = document.getElementById(link.id);
target.scrollIntoView(true);
var scrolledY = window.scrollY;
if (scrolledY) {
window.scroll(0, scrolledY - 70);
}
}, 500);
}
toggle(headerLink: Link) {
const headerPage = +headerLink.name.split(" ", 1);
let innerPage;
for (const link of this.links) {
link.selected = false;
if (link.type === 'mat-expansion-panel') {
innerPage = +link.name.split(".", 1)[0];
if (isNaN(innerPage)) { innerPage = +link.name.split(" ", 1) }
} else if (link.type === 'h5') {
innerPage = +link.name.split(".", 1)[0];
}
if (headerPage === innerPage && (link.type !== 'mat-expansion-panel' || (link.type === 'mat-expansion-panel' && link.id.split(".")[4]))) {
link.show = !link.show;
}
}
headerLink.selected = true;
}
// getIndex(link: Link): number {
// return +link.id.split("_", 2)[1];
// }
private _buildRecursively(form: FormGroup,whatAmI:ToCEntryType):ToCEntry{
if(!form) return null;
switch(whatAmI){
case ToCEntryType.Section:
const sections = form.get('sections') as FormArray;
const fieldsets = form.get('compositeFields') as FormArray;
const tempResult:ToCEntry[] = [];
if(sections &&sections.length){
sections.controls.forEach(section=>{
tempResult.push(this._buildRecursively(section as FormGroup, ToCEntryType.Section));
});
}else if(fieldsets && fieldsets.length){
fieldsets.controls.forEach(fieldset=>{
tempResult.push(this._buildRecursively(fieldset as FormGroup, ToCEntryType.FieldSet));
});
}
return {
form: form,
id: form.get('id').value,
label: form.get('title').value,
numbering: '',
subEntries:tempResult,
subEntriesType: sections &&sections.length? ToCEntryType.Section: ToCEntryType.FieldSet,
type: ToCEntryType.Section,
ordinal: form.get('ordinal').value
}
case ToCEntryType.FieldSet:
return {
form: form,
label:form.get('title').value,
id: form.get('id').value,
numbering:'s',
subEntries:[],
subEntriesType: ToCEntryType.Field,
type: ToCEntryType.FieldSet,
ordinal: form.get('ordinal').value
}
}
}
private _sortByOrdinal(tocentries: ToCEntry[]){
if(!tocentries || !tocentries.length) return;
tocentries.sort(this._customCompare);
tocentries.forEach(entry=>{
this._sortByOrdinal(entry.subEntries);
});
}
private _customCompare(a,b){
return a.ordinal - b.ordinal;
}
private _calculateNumbering(tocentries: ToCEntry[], depth:number[] = []){
if(!tocentries || !tocentries.length){
return;
}
let prefixNumbering = depth.length? depth.join('.'): '';
if(depth.length) prefixNumbering = prefixNumbering+".";
tocentries.forEach((entry,i)=>{
entry.numbering = prefixNumbering + (i+1);
this._calculateNumbering(entry.subEntries, [...depth, i+1])
});
}
getTocEntries(form): ToCEntry[] {
if (form == null) { return []; }
const result: ToCEntry[] = [];
//build parent pages
(form.get('pages') as FormArray).controls.forEach((pageElement, i) => {
result.push({
id: i+'id',
label: pageElement.get('title').value,
type: ToCEntryType.Page,
form: pageElement,
numbering: (i + 1).toString(),
subEntriesType: ToCEntryType.Section,
subEntries:[],
ordinal: pageElement.get('ordinal').value
} as ToCEntry)
});
result.forEach((entry,i)=>{
const sections = entry.form.get('sections') as FormArray;
sections.controls.forEach(section=>{
const tempResults = this._buildRecursively(section as FormGroup,ToCEntryType.Section);
entry.subEntries.push(tempResults);
});
});
this._sortByOrdinal(result);
//calculate numbering
this._calculateNumbering(result);
return result;
}
onToCentrySelected(entry: ToCEntry){
this.tocentrySelected = entry;
// console.log('entry selected', entry);
}
public seekToFirstElement(){//only on tocentry mode
if(this.tocentries && this.tocentries.length){
this.tocentrySelected = this.tocentries[0];
}
}
private _findTocEntryById(id: string, tocentries: ToCEntry[]): ToCEntry{
if(!tocentries || !tocentries.length){
return null;
}
let tocEntryFound = tocentries.find(entry=>entry.id === id);
if(tocEntryFound){
return tocEntryFound;
}
for(let entry of tocentries){
const result = this._findTocEntryById(id, entry.subEntries);
if(result){
tocEntryFound = result;
break;
}
}
return tocEntryFound? tocEntryFound: null;
}
}
export interface LinkToScroll {
page: number;
section: number;
}