From ae378899e2aeaab73149a7fd548bfe676d17d331 Mon Sep 17 00:00:00 2001 From: Kristan Ntavidi Date: Wed, 17 Mar 2021 11:08:59 +0200 Subject: [PATCH] Dataset description and table-of-contents works with tocentries. --- .../dataset-wizard.component.html | 6 +- .../dataset-wizard.component.scss | 3 + .../dataset-wizard.component.ts | 152 ++++++++++- .../form-section/form-section.component.html | 2 +- .../dataset-description.component.html | 36 ++- .../dataset-description.component.ts | 2 +- .../table-of-contents-internal.html | 38 +++ .../table-of-contents-internal.scss | 29 ++ .../table-of-contents-internal.ts | 86 ++++++ .../table-of-contents.html | 27 ++ .../table-of-contents.module.ts | 5 +- .../table-of-contents.scss | 5 + .../table-of-contents.ts | 258 ++++++++++++++---- 13 files changed, 571 insertions(+), 78 deletions(-) create mode 100644 dmp-frontend/src/app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents-internal/table-of-contents-internal.html create mode 100644 dmp-frontend/src/app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents-internal/table-of-contents-internal.scss create mode 100644 dmp-frontend/src/app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents-internal/table-of-contents-internal.ts diff --git a/dmp-frontend/src/app/ui/dataset/dataset-wizard/dataset-wizard.component.html b/dmp-frontend/src/app/ui/dataset/dataset-wizard/dataset-wizard.component.html index 5251bb0f2..be7a809cf 100644 --- a/dmp-frontend/src/app/ui/dataset/dataset-wizard/dataset-wizard.component.html +++ b/dmp-frontend/src/app/ui/dataset/dataset-wizard/dataset-wizard.component.html @@ -54,7 +54,7 @@
0. {{'DMP-EDITOR.STEPPER.MAIN-INFO' | translate}} (done)
- +
@@ -89,14 +89,14 @@ -
+ -
+
diff --git a/dmp-frontend/src/app/ui/misc/dataset-description-form/dataset-description.component.html b/dmp-frontend/src/app/ui/misc/dataset-description-form/dataset-description.component.html index e3c7aad22..198ceb6b8 100644 --- a/dmp-frontend/src/app/ui/misc/dataset-description-form/dataset-description.component.html +++ b/dmp-frontend/src/app/ui/misc/dataset-description-form/dataset-description.component.html @@ -16,22 +16,34 @@
- + -
-

- {{pageEntry.numbering}} - {{pageEntry.label}} -

+ -
-
- -
+
+ + + + +

+ {{pageEntry.numbering}}. {{pageEntry.label |uppercase}} +

+
+
+ +
+
+ +
+
+
-
+
diff --git a/dmp-frontend/src/app/ui/misc/dataset-description-form/dataset-description.component.ts b/dmp-frontend/src/app/ui/misc/dataset-description-form/dataset-description.component.ts index f1b19ef16..eb1071433 100644 --- a/dmp-frontend/src/app/ui/misc/dataset-description-form/dataset-description.component.ts +++ b/dmp-frontend/src/app/ui/misc/dataset-description-form/dataset-description.component.ts @@ -44,7 +44,7 @@ export class DatasetDescriptionComponent extends BaseComponent implements OnInit // this.formChanged.emit(val); // }); // } - + this.tocentries = this.getTocEntries(); } diff --git a/dmp-frontend/src/app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents-internal/table-of-contents-internal.html b/dmp-frontend/src/app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents-internal/table-of-contents-internal.html new file mode 100644 index 000000000..d6168cd64 --- /dev/null +++ b/dmp-frontend/src/app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents-internal/table-of-contents-internal.html @@ -0,0 +1,38 @@ +
+ + + + + + + + {{entry.numbering}}. {{entry.label}} + + + +
+ + + +
+
+ + + +
+ +
\ No newline at end of file diff --git a/dmp-frontend/src/app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents-internal/table-of-contents-internal.scss b/dmp-frontend/src/app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents-internal/table-of-contents-internal.scss new file mode 100644 index 000000000..6a9aa908d --- /dev/null +++ b/dmp-frontend/src/app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents-internal/table-of-contents-internal.scss @@ -0,0 +1,29 @@ + +.internal-table{ + margin-left: 1.2em; + // width: inherit; +} +// .table-entry-container{ +// // overflow: hidden; +// // width: inherit; +// } +.table-entry{ + cursor: pointer; + // display: block; + text-overflow: ellipsis; + white-space: nowrap; + // overflow: hidden; + + color: rgba(0, 0, 0, 0.54); + transition: color 100ms; +} + +.table-entry:hover{ + background-color: #ececec; + border-radius: 6px; +} +.selected { + color: #212121 !important; + font-weight: 700 !important; + opacity: 1 !important; +} \ No newline at end of file diff --git a/dmp-frontend/src/app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents-internal/table-of-contents-internal.ts b/dmp-frontend/src/app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents-internal/table-of-contents-internal.ts new file mode 100644 index 000000000..63070df1f --- /dev/null +++ b/dmp-frontend/src/app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents-internal/table-of-contents-internal.ts @@ -0,0 +1,86 @@ +import { DOCUMENT } from '@angular/common'; +import { Component, EventEmitter, Inject, OnInit, Output, Input } 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 { VisibilityRulesService } from '../../visibility-rules/visibility-rules.service'; +import { Rule } from '@app/core/model/dataset-profile-definition/rule'; + +@Component({ + selector: 'table-of-contents-internal', + styleUrls: ['./table-of-contents-internal.scss'], + templateUrl: './table-of-contents-internal.html' +}) +export class TableOfContentsInternal implements OnInit { + + @Input() tocentries: ToCEntry[] = null; + @Input() selected: ToCEntry = null; + // @Input() visibilityRules:Rule[] = []; + @Output() entrySelected = new EventEmitter(); + + expandChildren:boolean[]; + tocEntryTypeEnum = ToCEntryType; + + + constructor(public visibilityRulesService: VisibilityRulesService){ + + + } + ngOnInit(): void { + // console.log('component created'); + if(this.tocentries){ + this.expandChildren = this.tocentries.map(()=>false); + } + } + + ngOnChanges(changes: SimpleChanges) { + // if (!this.isActive && this.links && this.links.length > 0) { + // this.links.forEach(link => { + // link.selected = false; + // }) + // this.links[0].selected = true; + // } + } + + toggleExpand(index){ + this.expandChildren[index] = !this.expandChildren[index]; + // console.log(this.expandChildren); + } + + navigateToFieldSet(entry:ToCEntry, event){ + if(entry.type === ToCEntryType.FieldSet){ + + const fieldSetId = entry.id; + const element = document.getElementById(fieldSetId); + if(element){ + element.scrollIntoView({behavior:'smooth'}); + // event.stopPropagation(); + } + + } + } + + + onEntrySelected(entry:ToCEntry){ + this.entrySelected.emit(entry); + } + + + calculateStyle(entry: ToCEntry){ + const style = {}; + style['font-size'] = entry.type ===this.tocEntryTypeEnum.FieldSet? '.9em': '1em'; + return style; + } + + calculateClass(entry:ToCEntry){ + + if(this.selected && entry.id === this.selected.id){ + return{'selected': true}; + } + + return {}; + } +} diff --git a/dmp-frontend/src/app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents.html b/dmp-frontend/src/app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents.html index b91593ca0..629f03eed 100644 --- a/dmp-frontend/src/app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents.html +++ b/dmp-frontend/src/app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents.html @@ -9,3 +9,30 @@
+ + + +
+ +
+ + + +
+
\ No newline at end of file diff --git a/dmp-frontend/src/app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents.module.ts b/dmp-frontend/src/app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents.module.ts index b28ec7804..bf99a7809 100644 --- a/dmp-frontend/src/app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents.module.ts +++ b/dmp-frontend/src/app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents.module.ts @@ -2,11 +2,14 @@ import {CommonModule} from '@angular/common'; import {NgModule} from '@angular/core'; import {TableOfContents} from './table-of-contents'; import {RouterModule} from '@angular/router'; +import { TableOfContentsInternal } from './table-of-contents-internal/table-of-contents-internal'; +import { VisibilityRulesService } from '../visibility-rules/visibility-rules.service'; @NgModule({ imports: [CommonModule, RouterModule], - declarations: [TableOfContents], + declarations: [TableOfContents, TableOfContentsInternal], exports: [TableOfContents], entryComponents: [TableOfContents], + providers:[VisibilityRulesService] }) export class TableOfContentsModule { } diff --git a/dmp-frontend/src/app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents.scss b/dmp-frontend/src/app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents.scss index b346518fa..9a0092598 100644 --- a/dmp-frontend/src/app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents.scss +++ b/dmp-frontend/src/app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents.scss @@ -68,3 +68,8 @@ span { .docs-level-h5 { margin-left: 24px; } + +// .internal-table-outer{ +// padding-left: 1.1em; +// width: 100%; +// } \ No newline at end of file diff --git a/dmp-frontend/src/app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents.ts b/dmp-frontend/src/app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents.ts index 917a80f55..608d9c5b0 100644 --- a/dmp-frontend/src/app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents.ts +++ b/dmp-frontend/src/app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents.ts @@ -5,6 +5,10 @@ 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*/ @@ -39,72 +43,99 @@ export class TableOfContents extends BaseComponent implements OnInit { linksSubject: Subject = new Subject(); @Input() isActive: boolean; + + tocentries: ToCEntry[] = null; + // visibilityRules:Rule[] = []; + @Input() visibilityRules:Rule[] = []; + + 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) { + @Inject(DOCUMENT) private _document: Document, + public visibilityRulesService: VisibilityRulesService + ) { super(); } ngOnInit(): void { - //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.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 = []; + 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 = []; - 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; + 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 + }); } - 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; - }) + this.links = links; + // Initialize selected for button next on dataset wizard component editor + this.links.length > 0 ? this.links[0].selected = true : null; + }) + } } } @@ -158,6 +189,125 @@ export class TableOfContents extends BaseComponent implements OnInit { // 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 &§ions.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 &§ions.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); + } + + } export interface LinkToScroll {