changed description editor table of contents
This commit is contained in:
parent
8a8aa40ac9
commit
8940394aa5
|
@ -107,7 +107,7 @@
|
||||||
<div class="row toc-pane-container" #boundary>
|
<div class="row toc-pane-container" #boundary>
|
||||||
<div #spacer></div>
|
<div #spacer></div>
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<app-table-of-contents [visibilityRulesService]="visibilityRulesService" [selectedFieldsetId]="fieldsetIdWithFocus" #table0fContents [showErrors]="showtocentriesErrors" [hasFocus]="step > 0" [formGroup]="formGroup.get('properties')" [descriptionTemplate]="item.descriptionTemplate" *ngIf="formGroup" [links]="links" [boundary]="boundary" [spacer]="spacer" [isActive]="step !== 0" stickyThing (stepFound)="onStepFound($event)" (currentLinks)="getLinks($event)" (entrySelected)="changeStep($event.entry, $event.execute)" [pageToFieldSetMap]="pageToFieldSetMap"></app-table-of-contents>
|
<app-table-of-contents [visibilityRulesService]="visibilityRulesService" #table0fContents [showErrors]="showTocEntriesErrors" [hasFocus]="step > 0" [formGroup]="formGroup.get('properties')" [descriptionTemplate]="item.descriptionTemplate" *ngIf="formGroup" [links]="links" [boundary]="boundary" [spacer]="spacer" [isActive]="step !== 0" stickyThing (entrySelected)="changeStep($event.entry, $event.execute)" [pageToFieldSetMap]="pageToFieldSetMap"></app-table-of-contents>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -163,7 +163,6 @@
|
||||||
[validationErrorModel]="editorModel.validationErrorModel"
|
[validationErrorModel]="editorModel.validationErrorModel"
|
||||||
[isNew]="isNew || isCopy"
|
[isNew]="isNew || isCopy"
|
||||||
[canReview]="canReview"
|
[canReview]="canReview"
|
||||||
(fieldsetFocusChange)="fieldsetIdWithFocus = $event"
|
|
||||||
></app-description-form>
|
></app-description-form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -46,7 +46,7 @@ import { DescriptionEditorService } from './description-editor.service';
|
||||||
import { PrefillDescriptionDialogComponent } from './prefill-description/prefill-description.component';
|
import { PrefillDescriptionDialogComponent } from './prefill-description/prefill-description.component';
|
||||||
import { ToCEntry } from './table-of-contents/models/toc-entry';
|
import { ToCEntry } from './table-of-contents/models/toc-entry';
|
||||||
import { ToCEntryType } from './table-of-contents/models/toc-entry-type.enum';
|
import { ToCEntryType } from './table-of-contents/models/toc-entry-type.enum';
|
||||||
import { TableOfContentsValidationService } from './table-of-contents/services/table-of-contents-validation-service';
|
import { TableOfContentsService } from './table-of-contents/services/table-of-contents-service';
|
||||||
import { TableOfContentsComponent } from './table-of-contents/table-of-contents.component';
|
import { TableOfContentsComponent } from './table-of-contents/table-of-contents.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -63,13 +63,13 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
|
||||||
canEdit = false;
|
canEdit = false;
|
||||||
canReview = false;
|
canReview = false;
|
||||||
item: Description;
|
item: Description;
|
||||||
fieldsetIdWithFocus: string;
|
|
||||||
fileTransformerEntityTypeEnum = FileTransformerEntityType;
|
fileTransformerEntityTypeEnum = FileTransformerEntityType;
|
||||||
|
|
||||||
viewOnly = false;
|
viewOnly = false;
|
||||||
lockStatus: Boolean;
|
lockStatus: Boolean;
|
||||||
descriptionIsOnceSaved = false;
|
descriptionIsOnceSaved = false;
|
||||||
isFinalized = false;
|
isFinalized = false;
|
||||||
|
showTocEntriesErrors = false;
|
||||||
@ViewChild('table0fContents') table0fContents: TableOfContentsComponent;
|
@ViewChild('table0fContents') table0fContents: TableOfContentsComponent;
|
||||||
step: number = 0;
|
step: number = 0;
|
||||||
|
|
||||||
|
@ -100,11 +100,10 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
|
||||||
private descriptionTemplateService: DescriptionTemplateService,
|
private descriptionTemplateService: DescriptionTemplateService,
|
||||||
public visibilityRulesService: VisibilityRulesService,
|
public visibilityRulesService: VisibilityRulesService,
|
||||||
public fileTransformerService: FileTransformerService,
|
public fileTransformerService: FileTransformerService,
|
||||||
public tocValidationService: TableOfContentsValidationService,
|
|
||||||
public titleService: Title,
|
public titleService: Title,
|
||||||
private analyticsService: AnalyticsService,
|
private analyticsService: AnalyticsService,
|
||||||
private breadcrumbService: BreadcrumbService,
|
private changeDetectorRef: ChangeDetectorRef,
|
||||||
private changeDetectorRef: ChangeDetectorRef
|
private tableOfContentsService: TableOfContentsService
|
||||||
) {
|
) {
|
||||||
const descriptionLabel: string = route.snapshot.data['entity']?.label;
|
const descriptionLabel: string = route.snapshot.data['entity']?.label;
|
||||||
if (descriptionLabel) {
|
if (descriptionLabel) {
|
||||||
|
@ -270,7 +269,6 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
|
||||||
this.getItem(this.editorModel.id, (data: Description) => {
|
this.getItem(this.editorModel.id, (data: Description) => {
|
||||||
this.prepareForm(data)
|
this.prepareForm(data)
|
||||||
});
|
});
|
||||||
this.tocValidationService.validateForm();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshOnNavigateToData(id?: Guid): void {
|
refreshOnNavigateToData(id?: Guid): void {
|
||||||
|
@ -551,9 +549,13 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
|
||||||
public changeStep(selected: ToCEntry = null, execute: boolean = true) {
|
public changeStep(selected: ToCEntry = null, execute: boolean = true) {
|
||||||
if (execute) {
|
if (execute) {
|
||||||
if (selected) {
|
if (selected) {
|
||||||
let fieldSet = this.getFirstFieldSet(selected);
|
if (this.step == 0) {
|
||||||
let index = this.visibleFieldSets.findIndex(entry => entry.id === fieldSet.id);
|
this.step = 1;
|
||||||
this.step = index + (selected.type === ToCEntryType.FieldSet ? 1 : 0.5);
|
}
|
||||||
|
const element = document.getElementById(selected.id);
|
||||||
|
if (element) {
|
||||||
|
element.scrollIntoView({ behavior: 'smooth' });
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.step = 0;
|
this.step = 0;
|
||||||
this.resetScroll();
|
this.resetScroll();
|
||||||
|
@ -561,54 +563,44 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getFirstFieldSet(entry: ToCEntry): ToCEntry {
|
|
||||||
if (entry.type === ToCEntryType.FieldSet && !this.table0fContents.internalTable.hiddenEntries.find(hiddenEntry => hiddenEntry === entry.id.toString())) {
|
|
||||||
return entry;
|
|
||||||
} else {
|
|
||||||
let subEntries = entry.subEntries.filter(subEntry => !this.table0fContents.internalTable.hiddenEntries.find(hiddenEntry => hiddenEntry === subEntry.id.toString()));
|
|
||||||
if (subEntries.length > 0) {
|
|
||||||
return this.getFirstFieldSet(subEntries[0]);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get maxStep() {
|
get maxStep() {
|
||||||
return this.visibleFieldSets.length;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public nextStep() {
|
public nextStep() {
|
||||||
if (this.step < this.maxStep) {//view is changing
|
this.tableOfContentsService.nextClicked();
|
||||||
this.step = Math.floor(this.step + 1);
|
// if (this.step < this.maxStep) {//view is changing
|
||||||
let entry = this.visibleFieldSets[this.step - 1];
|
// this.step = Math.floor(this.step + 1);
|
||||||
const targetElement = document.getElementById(entry.id);
|
// let entry = this.visibleFieldSets[this.step - 1];
|
||||||
if (targetElement) {
|
// const targetElement = document.getElementById(entry.id);
|
||||||
this.table0fContents.onToCentrySelected(entry, false);
|
// if (targetElement) {
|
||||||
this.scroll(entry);
|
// this.table0fContents.onToCentrySelected(entry, false);
|
||||||
} else {
|
// this.scroll(entry);
|
||||||
this.nextStep();
|
// } else {
|
||||||
}
|
// this.nextStep();
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
public previousStep() {
|
public previousStep() {
|
||||||
if (this.step > 0) {
|
this.tableOfContentsService.nextClicked();
|
||||||
this.step = Math.ceil(this.step - 1);
|
|
||||||
if (this.step >= 1) {
|
// if (this.step > 0) {
|
||||||
let entry = this.visibleFieldSets[this.step - 1];
|
// this.step = Math.ceil(this.step - 1);
|
||||||
const targetElement = document.getElementById(entry.id);
|
// if (this.step >= 1) {
|
||||||
if (targetElement) {
|
// let entry = this.visibleFieldSets[this.step - 1];
|
||||||
this.table0fContents.onToCentrySelected(entry, false);
|
// const targetElement = document.getElementById(entry.id);
|
||||||
this.scroll(entry);
|
// if (targetElement) {
|
||||||
} else {
|
// this.table0fContents.onToCentrySelected(entry, false);
|
||||||
this.previousStep();
|
// this.scroll(entry);
|
||||||
}
|
// } else {
|
||||||
} else {
|
// this.previousStep();
|
||||||
this.table0fContents.onToCentrySelected(null, false);
|
// }
|
||||||
this.resetScroll();
|
// } else {
|
||||||
}
|
// this.table0fContents.onToCentrySelected(null, false);
|
||||||
}
|
// this.resetScroll();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
private scroll(entry: ToCEntry) {
|
private scroll(entry: ToCEntry) {
|
||||||
|
@ -619,19 +611,6 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
|
||||||
document.getElementById('description-editor-form').scrollTop = 0;
|
document.getElementById('description-editor-form').scrollTop = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
get visibleFieldSets(): ToCEntry[] {
|
|
||||||
let fieldSets = [];
|
|
||||||
let arrays = this.table0fContents ? this.table0fContents.tocentries.
|
|
||||||
filter(entry => !this.table0fContents.internalTable.hiddenEntries.find(hiddenEntry => hiddenEntry === entry.id.toString())).map(entry => {
|
|
||||||
return this.getEntryVisibleFieldSets(entry);
|
|
||||||
})
|
|
||||||
: [];
|
|
||||||
arrays.forEach(array => {
|
|
||||||
fieldSets = fieldSets.concat(array);
|
|
||||||
});
|
|
||||||
return fieldSets;
|
|
||||||
}
|
|
||||||
|
|
||||||
get countErrorsOfBaseInfoPage(): number {
|
get countErrorsOfBaseInfoPage(): number {
|
||||||
if (this.formGroup == null) return 0;
|
if (this.formGroup == null) return 0;
|
||||||
|
|
||||||
|
@ -645,18 +624,6 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
|
||||||
return errorsCount;
|
return errorsCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
getEntryVisibleFieldSets(entry: ToCEntry): ToCEntry[] {
|
|
||||||
let fieldSets = [];
|
|
||||||
if (entry.type === ToCEntryType.FieldSet && !this.table0fContents.internalTable.hiddenEntries.find(hiddenEntry => hiddenEntry === entry.id.toString())) {
|
|
||||||
fieldSets.push(entry);
|
|
||||||
} else if (entry.type !== ToCEntryType.FieldSet) {
|
|
||||||
entry.subEntries.forEach(subEntry => {
|
|
||||||
fieldSets = fieldSets.concat(this.getEntryVisibleFieldSets(subEntry));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return fieldSets;
|
|
||||||
}
|
|
||||||
|
|
||||||
registerFormListeners() {
|
registerFormListeners() {
|
||||||
|
|
||||||
this.formGroup.get('descriptionTemplateId').valueChanges
|
this.formGroup.get('descriptionTemplateId').valueChanges
|
||||||
|
@ -667,10 +634,6 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
|
||||||
this.descriptionTemplateValueChanged(descriptionTemplateId);
|
this.descriptionTemplateValueChanged(descriptionTemplateId);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.formGroup.get('properties').valueChanges
|
|
||||||
.pipe(takeUntil(this._destroyed))
|
|
||||||
.subscribe(next => this.tocValidationService.validateForm());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
descriptionTemplateValueChanged(descriptionTemplateId: Guid) {
|
descriptionTemplateValueChanged(descriptionTemplateId: Guid) {
|
||||||
|
@ -757,7 +720,7 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
|
||||||
this.formService.touchAllFormFields(this.formGroup);
|
this.formService.touchAllFormFields(this.formGroup);
|
||||||
this.formService.validateAllFormFields(this.formGroup);
|
this.formService.validateAllFormFields(this.formGroup);
|
||||||
|
|
||||||
this.tocValidationService.validateForm();
|
this.showTocEntriesErrors = true;
|
||||||
if (!this.isFormValid()) {
|
if (!this.isFormValid()) {
|
||||||
this.dialog.open(FormValidationErrorsDialogComponent, {
|
this.dialog.open(FormValidationErrorsDialogComponent, {
|
||||||
data: {
|
data: {
|
||||||
|
|
|
@ -28,7 +28,6 @@ export class DescriptionFormComponent extends BaseComponent implements OnInit, O
|
||||||
@Input() validationErrorModel: ValidationErrorModel;
|
@Input() validationErrorModel: ValidationErrorModel;
|
||||||
|
|
||||||
@Output() formChanged: EventEmitter<any> = new EventEmitter();
|
@Output() formChanged: EventEmitter<any> = new EventEmitter();
|
||||||
@Output() fieldsetFocusChange: EventEmitter<string> = new EventEmitter<string>();
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public descriptionFormAnnotationService: DescriptionFormAnnotationService,
|
public descriptionFormAnnotationService: DescriptionFormAnnotationService,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Guid } from "@common/types/guid";
|
import { AbstractControl } from "@angular/forms";
|
||||||
import { ToCEntryType } from "./toc-entry-type.enum";
|
import { ToCEntryType } from "./toc-entry-type.enum";
|
||||||
|
|
||||||
export interface ToCEntry {
|
export interface ToCEntry {
|
||||||
|
@ -10,5 +10,12 @@ export interface ToCEntry {
|
||||||
// form: AbstractControl;
|
// form: AbstractControl;
|
||||||
numbering: string;
|
numbering: string;
|
||||||
ordinal: number;
|
ordinal: number;
|
||||||
hidden?: boolean
|
hidden?: boolean;
|
||||||
|
visibilityRuleKey: string;
|
||||||
|
validityAbstractControl: AbstractControl;
|
||||||
|
|
||||||
|
isLastEntry?: boolean;
|
||||||
|
isFirstEntry?: boolean;
|
||||||
|
previousEntry?: ToCEntry;
|
||||||
|
NextEntry?: ToCEntry;
|
||||||
}
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { EventEmitter, Injectable } from "@angular/core";
|
||||||
|
import { Observable } from "rxjs";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class TableOfContentsService {
|
||||||
|
|
||||||
|
private _nextClickedEventEmmiter: EventEmitter<any> = new EventEmitter<any>();
|
||||||
|
private _previousClickedEventEmmiter: EventEmitter<any> = new EventEmitter<any>();
|
||||||
|
|
||||||
|
getNextClickedEventObservable(): Observable<any> {
|
||||||
|
return this._nextClickedEventEmmiter.asObservable();
|
||||||
|
}
|
||||||
|
|
||||||
|
getPreviousEventObservable(): Observable<any> {
|
||||||
|
return this._previousClickedEventEmmiter.asObservable();
|
||||||
|
}
|
||||||
|
|
||||||
|
nextClicked(): void {
|
||||||
|
this._nextClickedEventEmmiter.emit();
|
||||||
|
}
|
||||||
|
|
||||||
|
previousClicked(): void {
|
||||||
|
this._previousClickedEventEmmiter.emit();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,13 +0,0 @@
|
||||||
import { EventEmitter, Injectable } from "@angular/core";
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class TableOfContentsValidationService {
|
|
||||||
private _validateFormEvent: EventEmitter<any> = new EventEmitter<any>();
|
|
||||||
get validateFormEvent(): EventEmitter<any> {
|
|
||||||
return this._validateFormEvent;
|
|
||||||
}
|
|
||||||
|
|
||||||
validateForm(): void {
|
|
||||||
this._validateFormEvent.emit();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,56 +1,31 @@
|
||||||
<div *ngFor="let entry of tocentries; index as idx">
|
<div *ngFor="let entry of tocentries; index as idx">
|
||||||
<!-- check if is visible -->
|
<!-- check if is visible -->
|
||||||
<!-- <ng-container *ngIf="!hiddenEntries.includes(entry.id)"> -->
|
<ng-container *ngIf="visibilityRulesService.isVisibleMap[entry.visibilityRuleKey] ?? true">
|
||||||
<ng-container *ngIf="!hiddenEntries.includes(entry.id)">
|
|
||||||
|
|
||||||
<!-- Is fieldset and has no visible inputs -->
|
|
||||||
<!-- <ng-container *ngIf="!(entry.type === tocEntryTypeEnum.FieldSet && !visibilityRulesService.scanIfChildsOfCompositeFieldHasVisibleItems(entry.form))"> TODO: add this bellow -->
|
|
||||||
<!-- <ng-container *ngIf="!(entry.type === tocEntryTypeEnum.FieldSet)"> -->
|
|
||||||
<ng-container >
|
<ng-container >
|
||||||
|
|
||||||
<span class="table-entry"
|
<span class="table-entry"
|
||||||
(click)="toggleExpand(idx);navigateToFieldSet(entry, $event); onEntrySelected(entry);"
|
(click)="toggleExpand(idx); onEntrySelected(entry);"
|
||||||
[ngStyle]="calculateStyle(entry)"
|
[ngStyle]="calculateStyle(entry)"
|
||||||
[ngClass]="calculateClass(entry)"
|
[ngClass]="calculateClass(entry)"
|
||||||
>
|
>
|
||||||
<span [class.text-danger]="showErrors && propertiesFormGroup.get('entry.id')?.invalid && ( entry.type === tocEntryTypeEnum.FieldSet || (entry.type !== tocEntryTypeEnum.FieldSet && !expandChildren[idx])) && invalidChildsVisible(entry) ">
|
<span [class.text-danger]="!isTocEntryValid(entry) && showErrors">
|
||||||
{{entry.numbering}}. {{entry.label}}
|
{{entry.numbering}}. {{entry.label}}
|
||||||
</span>
|
</span>
|
||||||
<!-- <mat-icon style="transform: translateY(3px);" class="text-danger"
|
|
||||||
*ngIf="showErrors && entry.form.invalid && entry.type !== tocEntryTypeEnum.FieldSet && !expandChildren[idx]">
|
|
||||||
priority_high
|
|
||||||
</mat-icon> -->
|
|
||||||
<!-- <ng-container *ngIf="entry.subEntries && entry.subEntries.length && !expandChildren[idx]">
|
|
||||||
<small>
|
|
||||||
({{entry.subEntries.length}})
|
|
||||||
</small>
|
|
||||||
|
|
||||||
</ng-container> -->
|
|
||||||
</span>
|
</span>
|
||||||
<!-- <div class="table-entry-container">
|
|
||||||
</div> -->
|
|
||||||
<div class="internal-table">
|
<div class="internal-table">
|
||||||
<table-of-contents-internal
|
<table-of-contents-internal
|
||||||
[tocentries]="entry.subEntries"
|
[tocentries]="entry.subEntries"
|
||||||
*ngIf="entry.subEntries && entry.subEntries.length && expandChildren[idx]"
|
*ngIf="entry.subEntries && entry.subEntries.length && expandChildren[idx]"
|
||||||
(entrySelected)="onEntrySelected($event)"
|
(entrySelected)="onEntrySelected($event)"
|
||||||
[selected]="selected"
|
[selected]="selected"
|
||||||
[showErrors]="showErrors"
|
|
||||||
[hiddenEntries]="hiddenEntries"
|
|
||||||
[visibilityRulesService]="visibilityRulesService"
|
[visibilityRulesService]="visibilityRulesService"
|
||||||
[propertiesFormGroup]="propertiesFormGroup"
|
[propertiesFormGroup]="propertiesFormGroup"
|
||||||
[parentId]="entry.id"
|
[showErrors]="showErrors"
|
||||||
[parentMap]="updatedMap"
|
|
||||||
(scrollFinished)="onScrollFinished($event)"
|
|
||||||
(scrollStarted)="onScrollStarted($event)"
|
|
||||||
>
|
>
|
||||||
|
|
||||||
</table-of-contents-internal>
|
</table-of-contents-internal>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
</div>
|
</div>
|
|
@ -1,50 +1,35 @@
|
||||||
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, QueryList, SimpleChanges, ViewChildren } from '@angular/core';
|
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, SimpleChanges } from '@angular/core';
|
||||||
import { UntypedFormGroup } from '@angular/forms';
|
import { UntypedFormGroup } from '@angular/forms';
|
||||||
import { VisibilityRulesService } from '@app/ui/description/editor/description-form/visibility-rules/visibility-rules.service';
|
import { VisibilityRulesService } from '@app/ui/description/editor/description-form/visibility-rules/visibility-rules.service';
|
||||||
import { Guid } from '@common/types/guid';
|
import { BaseComponent } from '@common/base/base.component';
|
||||||
import { ToCEntry } from '../models/toc-entry';
|
import { ToCEntry } from '../models/toc-entry';
|
||||||
import { ToCEntryType } from '../models/toc-entry-type.enum';
|
import { ToCEntryType } from '../models/toc-entry-type.enum';
|
||||||
import { DescriptionFieldIndicator } from '../../description-editor.model';
|
import { TableOfContentsComponent } from '../table-of-contents.component';
|
||||||
import { Observable, Subscription, map } from 'rxjs';
|
|
||||||
import { TableOfContentsValidationService } from '../services/table-of-contents-validation-service';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'table-of-contents-internal',
|
selector: 'table-of-contents-internal',
|
||||||
styleUrls: ['./table-of-contents-internal.scss'],
|
styleUrls: ['./table-of-contents-internal.scss'],
|
||||||
templateUrl: './table-of-contents-internal.html'
|
templateUrl: './table-of-contents-internal.html'
|
||||||
})
|
})
|
||||||
export class TableOfContentsInternal implements OnInit, OnDestroy {
|
export class TableOfContentsInternal extends BaseComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
@Input() tocentries: ToCEntry[] = null;
|
@Input() tocentries: ToCEntry[] = null;
|
||||||
@Input() selected: ToCEntry = null;
|
@Input() selected: ToCEntry = null;
|
||||||
// @Input() visibilityRules:Rule[] = [];
|
|
||||||
@Output() entrySelected = new EventEmitter<ToCEntry>();
|
@Output() entrySelected = new EventEmitter<ToCEntry>();
|
||||||
@Output() scrollStarted = new EventEmitter<ToCEntry>();
|
|
||||||
@Output() scrollFinished = new EventEmitter<ToCEntry>();
|
|
||||||
@Input() propertiesFormGroup: UntypedFormGroup;
|
@Input() propertiesFormGroup: UntypedFormGroup;
|
||||||
|
|
||||||
expandChildren: boolean[];
|
expandChildren: boolean[];
|
||||||
tocEntryTypeEnum = ToCEntryType;
|
|
||||||
@Input() showErrors: boolean = false;
|
@Input() showErrors: boolean = false;
|
||||||
@Input() hiddenEntries: string[] = [];
|
|
||||||
@Input() visibilityRulesService: VisibilityRulesService;
|
@Input() visibilityRulesService: VisibilityRulesService;
|
||||||
@ViewChildren(TableOfContentsInternal) internalTables: QueryList<TableOfContentsInternal>;
|
|
||||||
|
|
||||||
@Input() parentId: string;
|
constructor() { super(); }
|
||||||
@Input() parentMap: Map<string, DescriptionFieldIndicator[]> = new Map<string, DescriptionFieldIndicator[]>();
|
|
||||||
@Input() updatedMap: Map<string, DescriptionFieldIndicator[]> = new Map<string, DescriptionFieldIndicator[]>();
|
|
||||||
tocEntriesStateSubscriptions: Subscription[] = [];
|
|
||||||
tocEntriesStateMap: Map<string, boolean> = new Map<string, boolean>();
|
|
||||||
|
|
||||||
constructor(private tocValidationService: TableOfContentsValidationService) { }
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
// console.log('component created' + JSON.stringify(this.tocentries));
|
|
||||||
if (this.tocentries) {
|
if (this.tocentries) {
|
||||||
this.expandChildren = this.tocentries.map(() => false);
|
this.expandChildren = this.tocentries.map(() => false);
|
||||||
if (this.selected) {
|
if (this.selected) {
|
||||||
for (let i = 0; i < this.tocentries.length; i++) {
|
for (let i = 0; i < this.tocentries.length; i++) {
|
||||||
if (this._findTocEntryById(this.selected.id, this.tocentries[i].subEntries)) {
|
if (TableOfContentsComponent._findTocEntryById(this.selected.id, this.tocentries[i].subEntries)) {
|
||||||
if (this.expandChildren) {
|
if (this.expandChildren) {
|
||||||
this.expandChildren[i] = true;
|
this.expandChildren[i] = true;
|
||||||
}
|
}
|
||||||
|
@ -52,17 +37,13 @@ export class TableOfContentsInternal implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.parentMap) {
|
|
||||||
this.refreshErrorIndicators();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges) {
|
ngOnChanges(changes: SimpleChanges) {
|
||||||
if (changes.selected && this.selected) {
|
if (changes.selected && this.selected) {
|
||||||
for (let i = 0; i < this.tocentries.length; i++) {
|
for (let i = 0; i < this.tocentries.length; i++) {
|
||||||
if (this._findTocEntryById(this.selected.id, this.tocentries[i].subEntries)) {
|
if (TableOfContentsComponent._findTocEntryById(this.selected.id, this.tocentries[i].subEntries)) {
|
||||||
if (this.expandChildren) {
|
if (this.expandChildren) {
|
||||||
this.expandChildren[i] = true;
|
this.expandChildren[i] = true;
|
||||||
}
|
}
|
||||||
|
@ -70,127 +51,24 @@ export class TableOfContentsInternal implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changes.parentMap && this.parentMap) {
|
|
||||||
this.refreshErrorIndicators();
|
|
||||||
}
|
|
||||||
// if (!this.isActive && this.links && this.links.length > 0) {
|
|
||||||
// this.links.forEach(link => {
|
|
||||||
// link.selected = false;
|
|
||||||
// })
|
|
||||||
// this.links[0].selected = true;
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
this.tocEntriesStateSubscriptions.forEach((errorSubscription: Subscription) => {
|
super.ngOnDestroy();
|
||||||
errorSubscription.unsubscribe();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
refreshErrorIndicators(): void {
|
|
||||||
this.updatedMap = this.updateMap(this.tocentries, this.parentMap);
|
|
||||||
for (let entry of this.tocentries) {
|
|
||||||
this.tocEntriesStateMap.set(entry.id, this.hasErrors(entry.id));
|
|
||||||
|
|
||||||
this.tocEntriesStateSubscriptions.push(
|
|
||||||
this.tocValidationService.validateFormEvent
|
|
||||||
.pipe(map(() => this.hasErrors(entry.id)))
|
|
||||||
.subscribe(next => {
|
|
||||||
this.tocEntriesStateMap.set(entry.id, next);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updateMap(entries: ToCEntry[], parentMap:Map<string, DescriptionFieldIndicator[]>): Map<string, DescriptionFieldIndicator[]> {
|
|
||||||
if (this.parentId == null) return parentMap;
|
|
||||||
|
|
||||||
let updatedMap = new Map<string, DescriptionFieldIndicator[]>();
|
|
||||||
|
|
||||||
parentMap.forEach((fields: DescriptionFieldIndicator[], parentId: string) => {
|
|
||||||
if (this.parentId === parentId) {
|
|
||||||
for (let entry of entries) {
|
|
||||||
let entryFields = fields.filter((field: DescriptionFieldIndicator) => field.sectionIds.includes(entry.id) || field.fieldSetId === entry.id || field.fieldId === entry.id )
|
|
||||||
|
|
||||||
updatedMap.set(entry.id, entryFields);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return updatedMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
hasErrors(entryId: string): boolean {
|
|
||||||
if (this.updatedMap.size == 0) return false;
|
|
||||||
|
|
||||||
const fields: DescriptionFieldIndicator[] = this.updatedMap.get(entryId);
|
|
||||||
|
|
||||||
for (let field of fields) {
|
|
||||||
let formFieldName: string = `fieldSets.${field.fieldSetId}.items.0.fields.${field.fieldId}.${field.type}`;
|
|
||||||
if (this.isFormFieldValid(formFieldName) === false) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
isFormFieldValid(formFieldName: string):boolean {
|
|
||||||
const formField = this.propertiesFormGroup?.get(formFieldName);
|
|
||||||
if (formField == null) return true;
|
|
||||||
|
|
||||||
if (formField.dirty === false
|
|
||||||
&& formField.touched === false) return true;
|
|
||||||
|
|
||||||
return formField.valid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleExpand(index) {
|
toggleExpand(index) {
|
||||||
this.expandChildren[index] = !this.expandChildren[index];
|
this.expandChildren[index] = !this.expandChildren[index];
|
||||||
// console.log(this.expandChildren);
|
|
||||||
}
|
|
||||||
|
|
||||||
navigateToFieldSet(entry: ToCEntry, event) {
|
|
||||||
if (entry.type === ToCEntryType.FieldSet) {
|
|
||||||
this.onScrollStarted(entry);
|
|
||||||
|
|
||||||
const fieldSetId = entry.id;
|
|
||||||
const element = document.getElementById(fieldSetId);
|
|
||||||
if (element) {
|
|
||||||
element.click();//open mat expansion panel
|
|
||||||
|
|
||||||
//scroll asyn in 200 ms so the expansion panel is expanded and the element coordinates are updated
|
|
||||||
setTimeout(() => {
|
|
||||||
const element = document.getElementById(fieldSetId);
|
|
||||||
if (element) {
|
|
||||||
element.scrollIntoView({ behavior: 'smooth' });
|
|
||||||
// element.focus({preventScroll: true});
|
|
||||||
setTimeout(() => {
|
|
||||||
this.scrollFinished.emit(entry);
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
}, 300);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onEntrySelected(entry: ToCEntry) {
|
onEntrySelected(entry: ToCEntry) {
|
||||||
this.entrySelected.emit(entry);
|
this.entrySelected.emit(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
onScrollStarted(entry: ToCEntry) {
|
|
||||||
this.scrollStarted.emit(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
onScrollFinished(entry: ToCEntry) {
|
|
||||||
this.scrollFinished.emit(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
calculateStyle(entry: ToCEntry) {
|
calculateStyle(entry: ToCEntry) {
|
||||||
const style = {};
|
const style = {};
|
||||||
style['font-size'] = entry.type === this.tocEntryTypeEnum.FieldSet ? '.9em' : '1em';
|
style['font-size'] = entry.type === ToCEntryType.FieldSet ? '.9em' : '1em';
|
||||||
return style;
|
return style;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,64 +79,24 @@ export class TableOfContentsInternal implements OnInit, OnDestroy {
|
||||||
myClass['selected'] = true;
|
myClass['selected'] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry.type != this.tocEntryTypeEnum.FieldSet) {
|
if (entry.type != ToCEntryType.FieldSet) {
|
||||||
myClass['section'] = true;
|
myClass['section'] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.tocEntriesStateMap?.get(entry.id) === true) {
|
|
||||||
myClass['text-danger'] = true;
|
|
||||||
}
|
|
||||||
return myClass;
|
return myClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _findTocEntryById(id: string, tocentries: ToCEntry[]): ToCEntry {
|
isTocEntryValid(entry: ToCEntry): boolean {
|
||||||
if (!tocentries || !tocentries.length) {
|
if (entry == null) {
|
||||||
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;
|
|
||||||
}
|
|
||||||
public invalidChildsVisible(entry: ToCEntry): boolean {
|
|
||||||
if (!entry || !this.visibilityRulesService) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry.type != this.tocEntryTypeEnum.FieldSet) {
|
|
||||||
return entry.subEntries.some(_ => this.invalidChildsVisible(_));
|
|
||||||
}
|
|
||||||
if (entry.type === this.tocEntryTypeEnum.FieldSet) {
|
|
||||||
const id = entry.id;
|
|
||||||
if (!(this.visibilityRulesService.isVisibleMap[id.toString()])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// const fieldsArray = entry.form.get('fields') as UntypedFormArray;
|
|
||||||
const hasError = !entry.subEntries.every(field => {//every invalid field should be invisible
|
|
||||||
//TODO: check this
|
|
||||||
// if (field.invalid) {
|
|
||||||
// const id = field.id;
|
|
||||||
// const isVisible = this.visibilityRulesService.isVisibleMap[id);
|
|
||||||
// if (isVisible) {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
return true;
|
return true;
|
||||||
});
|
|
||||||
return hasError;
|
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
|
let currentValidity = entry.validityAbstractControl?.valid ?? true;
|
||||||
|
if (!currentValidity) return currentValidity;
|
||||||
|
entry.subEntries?.forEach(subEntry => {
|
||||||
|
currentValidity = currentValidity && this.isTocEntryValid(subEntry);
|
||||||
|
if (!currentValidity) return currentValidity;
|
||||||
|
});
|
||||||
|
|
||||||
|
return currentValidity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,3 @@
|
||||||
<div *ngIf="links?.length" class="row docs-toc-container">
|
|
||||||
<div class="col-12 scroll-container">
|
|
||||||
<span *ngFor="let link of links; let i = index" (click)="toggle(link); goToStep(link)" class="docs-level-{{link.type}} docs-link mt-0" [class.docs-active]="link.active">
|
|
||||||
<span *ngIf="link.show" class="link-name"><span [class.selected]="link.selected && isActive">{{link.name}}</span></span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div *ngIf="tocentries" class="row docs-toc-container">
|
<div *ngIf="tocentries" class="row docs-toc-container">
|
||||||
|
|
||||||
<div class="scroll-container col-12 internal-table">
|
<div class="scroll-container col-12 internal-table">
|
||||||
|
@ -13,12 +5,8 @@
|
||||||
[showErrors]="showErrors"
|
[showErrors]="showErrors"
|
||||||
(entrySelected)="onToCentrySelected($event)"
|
(entrySelected)="onToCentrySelected($event)"
|
||||||
[selected]="tocentrySelected"
|
[selected]="tocentrySelected"
|
||||||
[hiddenEntries]="hiddenEntries"
|
|
||||||
[visibilityRulesService]="visibilityRulesService"
|
[visibilityRulesService]="visibilityRulesService"
|
||||||
[propertiesFormGroup]="formGroup"
|
[propertiesFormGroup]="formGroup"
|
||||||
[parentMap]="pageToFieldSetMap"
|
|
||||||
(scrollFinished)="onScrollFinished($event)"
|
|
||||||
(scrollStarted)="onScrollStarted($event)"
|
|
||||||
>
|
>
|
||||||
|
|
||||||
</table-of-contents-internal>
|
</table-of-contents-internal>
|
||||||
|
|
|
@ -1,32 +1,12 @@
|
||||||
import { DOCUMENT } from '@angular/common';
|
import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core';
|
||||||
import { Component, EventEmitter, Inject, Input, OnChanges, OnInit, Output, SimpleChanges, ViewChild } from '@angular/core';
|
|
||||||
import { UntypedFormGroup } from '@angular/forms';
|
import { UntypedFormGroup } from '@angular/forms';
|
||||||
import { DescriptionTemplate, DescriptionTemplateField, DescriptionTemplateFieldSet, DescriptionTemplateSection } from '@app/core/model/description-template/description-template';
|
import { DescriptionTemplate, DescriptionTemplateFieldSet, DescriptionTemplateSection } from '@app/core/model/description-template/description-template';
|
||||||
import { VisibilityRulesService } from '@app/ui/description/editor/description-form/visibility-rules/visibility-rules.service';
|
import { VisibilityRulesService } from '@app/ui/description/editor/description-form/visibility-rules/visibility-rules.service';
|
||||||
import { BaseComponent } from '@common/base/base.component';
|
import { BaseComponent } from '@common/base/base.component';
|
||||||
import { Subject, Subscription, interval } from 'rxjs';
|
import { Observable, debounceTime, distinctUntilChanged, filter, mergeMap, take, takeUntil } from 'rxjs';
|
||||||
import { debounceTime, distinctUntilChanged, takeUntil } from 'rxjs/operators';
|
|
||||||
import { ToCEntry } from './models/toc-entry';
|
import { ToCEntry } from './models/toc-entry';
|
||||||
import { ToCEntryType } from './models/toc-entry-type.enum';
|
import { ToCEntryType } from './models/toc-entry-type.enum';
|
||||||
import { TableOfContentsInternal } from './table-of-contents-internal/table-of-contents-internal';
|
import { TableOfContentsService } from './services/table-of-contents-service';
|
||||||
import { DescriptionFieldIndicator } from '../description-editor.model';
|
|
||||||
|
|
||||||
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({
|
@Component({
|
||||||
selector: 'app-table-of-contents',
|
selector: 'app-table-of-contents',
|
||||||
|
@ -35,30 +15,18 @@ export interface Link {
|
||||||
})
|
})
|
||||||
export class TableOfContentsComponent extends BaseComponent implements OnInit, OnChanges {
|
export class TableOfContentsComponent extends BaseComponent implements OnInit, OnChanges {
|
||||||
|
|
||||||
@ViewChild('internalTable') internalTable: TableOfContentsInternal;
|
|
||||||
|
|
||||||
|
|
||||||
@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[]>();
|
|
||||||
@Output() entrySelected = new EventEmitter<any>();
|
@Output() entrySelected = new EventEmitter<any>();
|
||||||
subscription: Subscription;
|
@Input() showErrors: boolean = false;
|
||||||
linksSubject: Subject<HTMLElement[]> = new Subject<HTMLElement[]>();
|
@Input() formGroup: UntypedFormGroup;
|
||||||
|
@Input() descriptionTemplate: DescriptionTemplate;
|
||||||
@Input() isActive: boolean;
|
@Input() hasFocus: boolean = false;
|
||||||
|
@Input() visibilityRulesService: VisibilityRulesService;
|
||||||
|
|
||||||
tocentries: ToCEntry[] = null;
|
tocentries: ToCEntry[] = null;
|
||||||
@Input() showErrors: boolean = false;
|
|
||||||
@Input() selectedFieldsetId: string;
|
private _intersectionObserver: IntersectionObserver;
|
||||||
|
|
||||||
private _tocentrySelected: ToCEntry = null;
|
private _tocentrySelected: ToCEntry = null;
|
||||||
private isSelecting: boolean = false;
|
|
||||||
private isScrolling: boolean = false;
|
|
||||||
private _intersectionObserver: IntersectionObserver;
|
|
||||||
private _actOnObservation: boolean = true;
|
|
||||||
public hiddenEntries: string[] = [];
|
|
||||||
get tocentrySelected() {
|
get tocentrySelected() {
|
||||||
return this.hasFocus ? this._tocentrySelected : null;
|
return this.hasFocus ? this._tocentrySelected : null;
|
||||||
}
|
}
|
||||||
|
@ -66,17 +34,9 @@ export class TableOfContentsComponent extends BaseComponent implements OnInit, O
|
||||||
this._tocentrySelected = value;
|
this._tocentrySelected = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Input() formGroup: UntypedFormGroup;
|
|
||||||
@Input() descriptionTemplate: DescriptionTemplate;
|
|
||||||
@Input() hasFocus: boolean = false;
|
|
||||||
@Input() visibilityRulesService: VisibilityRulesService;
|
|
||||||
show: boolean = false;
|
|
||||||
|
|
||||||
@Input() pageToFieldSetMap: Map<string, DescriptionFieldIndicator[]>;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DOCUMENT) private _document: Document,
|
private tableOfContentsService: TableOfContentsService
|
||||||
// public visibilityRulesService: VisibilityRulesService
|
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
@ -84,166 +44,45 @@ export class TableOfContentsComponent extends BaseComponent implements OnInit, O
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
|
||||||
this.visibilityRulesService.getRulesChangedObservable().pipe(takeUntil(this._destroyed)).subscribe(data => {
|
|
||||||
this.hiddenEntries = this._findHiddenEntries(this.tocentries);
|
|
||||||
});
|
|
||||||
if (this.descriptionTemplate) {
|
if (this.descriptionTemplate) {
|
||||||
this.tocentries = this.getTocEntries(this.descriptionTemplate);
|
this.tocentries = this.getTocEntries(this.descriptionTemplate);
|
||||||
this.hiddenEntries = this._findHiddenEntries(this.tocentries);
|
}
|
||||||
|
|
||||||
} else {
|
this.tableOfContentsService.getNextClickedEventObservable().pipe(takeUntil(this._destroyed)).subscribe(x => {
|
||||||
|
//TODO: implement function to find next element
|
||||||
//emit value every 500ms
|
// call onToCentrySelected()
|
||||||
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.tableOfContentsService.getPreviousEventObservable().pipe(takeUntil(this._destroyed)).subscribe(x => {
|
||||||
// this.linksSubject.asObservable()
|
//TODO: implement function to find previous element
|
||||||
// .pipe(distinctUntilChanged((p: HTMLElement[], q: HTMLElement[]) => JSON.stringify(p) == JSON.stringify(q)))
|
// call onToCentrySelected()
|
||||||
// .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[1].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;
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private _findHiddenEntries(tocentries: ToCEntry[]): string[] {
|
|
||||||
if (!tocentries) return [];
|
|
||||||
|
|
||||||
const invisibleEntries: string[] = []
|
|
||||||
tocentries.forEach(entry => {
|
|
||||||
if (entry.type === ToCEntryType.FieldSet) {
|
|
||||||
const isVisible = this.visibilityRulesService.isVisibleMap[entry.id.toString()];
|
|
||||||
if (!isVisible) {
|
|
||||||
invisibleEntries.push(entry.id.toString());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const hiddenEntries = this._findHiddenEntries(entry.subEntries);
|
|
||||||
|
|
||||||
if (entry.subEntries && (entry.subEntries.every(e => hiddenEntries.includes(e.id.toString())))) {
|
|
||||||
//all children all hidden then hide parent node;
|
|
||||||
invisibleEntries.push(entry.id.toString());
|
|
||||||
} else {
|
|
||||||
invisibleEntries.push(...hiddenEntries);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return invisibleEntries;
|
|
||||||
}
|
|
||||||
ngOnChanges(changes: SimpleChanges) {
|
ngOnChanges(changes: SimpleChanges) {
|
||||||
|
|
||||||
if (this.selectedFieldsetId) {
|
|
||||||
this.onToCentrySelected(this._findTocEntryById(this.selectedFieldsetId, this.tocentries), false);
|
|
||||||
this._actOnObservation = false;
|
|
||||||
setTimeout(() => {
|
|
||||||
this._actOnObservation = true;
|
|
||||||
}, 1000);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (changes['hasFocus'] && changes.hasFocus.currentValue) {
|
if (changes['hasFocus'] && changes.hasFocus.currentValue) {
|
||||||
this._resetObserver();
|
this._resetObserver();
|
||||||
|
|
||||||
}
|
}
|
||||||
if (changes['descriptionTemplate'] && changes.descriptionTemplate != null) {
|
if (changes['descriptionTemplate'] && changes.descriptionTemplate != null) {
|
||||||
this.tocentries = this.getTocEntries(this.descriptionTemplate);
|
this.tocentries = this.getTocEntries(this.descriptionTemplate);
|
||||||
this.hiddenEntries = this._findHiddenEntries(this.tocentries);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private _resetObserver() {
|
private _resetObserver() {
|
||||||
if (this._intersectionObserver) {//clean up
|
if (this._intersectionObserver) {//clean up
|
||||||
this._intersectionObserver.disconnect();
|
this._intersectionObserver.disconnect();
|
||||||
this._intersectionObserver = null;
|
this._intersectionObserver = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
new Observable(observer => {
|
||||||
const options = {
|
const options = {
|
||||||
root: null,
|
root: null,
|
||||||
rootMargin: '20% 0px -30% 0px',
|
rootMargin: '20% 0px -30% 0px',
|
||||||
threshold: 0
|
threshold: [0, 0.2, 0.5, 1]
|
||||||
}
|
}
|
||||||
|
this._intersectionObserver = new IntersectionObserver(entries => {
|
||||||
this._intersectionObserver = new IntersectionObserver((entries, observer) => {
|
observer.next(entries);
|
||||||
if (!this._actOnObservation) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
entries.forEach(ie => {
|
|
||||||
if (ie.isIntersecting) {
|
|
||||||
try {
|
|
||||||
if(!this.isScrolling) {
|
|
||||||
const target_id = ie.target.id;
|
|
||||||
if (this.visibilityRulesService.isVisibleMap[target_id] ?? true) {
|
|
||||||
this.onToCentrySelected(this._findTocEntryById(target_id, this.tocentries));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
}
|
|
||||||
|
|
||||||
// setTimeout(() => {
|
|
||||||
// try {
|
|
||||||
// if(!this.isSelecting) {
|
|
||||||
// const target_id = ie.target.id;
|
|
||||||
// if (this.visibilityRulesService.isVisibleMap[target_id] ?? true) {
|
|
||||||
// this.onToCentrySelected(this._findTocEntryById(target_id, this.tocentries));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// } catch {
|
|
||||||
// }
|
|
||||||
// }, 1000)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}, options);
|
}, options);
|
||||||
|
|
||||||
const fieldsetsEtries = this._getAllFieldSets(this.tocentries);
|
const fieldsetsEtries = this._getAllFieldSets(this.tocentries);
|
||||||
|
@ -258,48 +97,24 @@ export class TableOfContentsComponent extends BaseComponent implements OnInit, O
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
goToStep(link: Link) {
|
return () => { this._intersectionObserver.disconnect(); };
|
||||||
this.stepFound.emit({
|
}).pipe(
|
||||||
page: link.page,
|
takeUntil(this._destroyed),
|
||||||
section: link.section
|
mergeMap((entries: IntersectionObserverEntry[]) => entries),
|
||||||
|
filter(entry => entry.isIntersecting),
|
||||||
|
debounceTime(200),
|
||||||
|
distinctUntilChanged(),
|
||||||
|
).subscribe(x => {
|
||||||
|
if (x.isIntersecting) {
|
||||||
|
const target_id = x.target.id;
|
||||||
|
console.log(target_id);
|
||||||
|
if (this.visibilityRulesService.isVisibleMap[target_id] ?? true) {
|
||||||
|
this.tocentrySelected = TableOfContentsComponent._findTocEntryById(target_id, this.tocentries);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
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 _buildRecursivelySection(item: DescriptionTemplateSection): ToCEntry {
|
private _buildRecursivelySection(item: DescriptionTemplateSection): ToCEntry {
|
||||||
if (!item) return null;
|
if (!item) return null;
|
||||||
|
@ -328,18 +143,14 @@ export class TableOfContentsComponent extends BaseComponent implements OnInit, O
|
||||||
subEntries: tempResult,
|
subEntries: tempResult,
|
||||||
subEntriesType: sections && sections.length ? ToCEntryType.Section : ToCEntryType.FieldSet,
|
subEntriesType: sections && sections.length ? ToCEntryType.Section : ToCEntryType.FieldSet,
|
||||||
type: ToCEntryType.Section,
|
type: ToCEntryType.Section,
|
||||||
ordinal: item.ordinal
|
ordinal: item.ordinal,
|
||||||
|
visibilityRuleKey: item.id,
|
||||||
|
validityAbstractControl: this.formGroup.get('fieldSets').get(item.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _buildRecursivelyFieldSet(item: DescriptionTemplateFieldSet): ToCEntry {
|
private _buildRecursivelyFieldSet(item: DescriptionTemplateFieldSet): ToCEntry {
|
||||||
if (!item) return null;
|
if (!item) return null;
|
||||||
const tempResult: ToCEntry[] = [];
|
|
||||||
if (item && item.fields.length > 0) {
|
|
||||||
item.fields.forEach(field => {
|
|
||||||
tempResult.push(this._buildRecursivelyField(field));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// form: form,
|
// form: form,
|
||||||
|
@ -349,22 +160,9 @@ export class TableOfContentsComponent extends BaseComponent implements OnInit, O
|
||||||
subEntries: null,
|
subEntries: null,
|
||||||
subEntriesType: ToCEntryType.Field,
|
subEntriesType: ToCEntryType.Field,
|
||||||
type: ToCEntryType.FieldSet,
|
type: ToCEntryType.FieldSet,
|
||||||
ordinal: item.ordinal
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private _buildRecursivelyField(item: DescriptionTemplateField): ToCEntry {
|
|
||||||
if (!item) return null;
|
|
||||||
return {
|
|
||||||
// form: form,
|
|
||||||
id: item.id,
|
|
||||||
label: null,
|
|
||||||
numbering: 's',
|
|
||||||
subEntries: null,
|
|
||||||
subEntriesType: null,
|
|
||||||
type: ToCEntryType.Field,
|
|
||||||
ordinal: item.ordinal,
|
ordinal: item.ordinal,
|
||||||
hidden: false
|
visibilityRuleKey: item.id,
|
||||||
|
validityAbstractControl: this.formGroup.get('fieldSets').get(item.id)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -404,15 +202,15 @@ export class TableOfContentsComponent extends BaseComponent implements OnInit, O
|
||||||
//build parent pages
|
//build parent pages
|
||||||
descriptionTemplate.definition.pages.forEach((pageElement, i) => {
|
descriptionTemplate.definition.pages.forEach((pageElement, i) => {
|
||||||
const tocEntry: ToCEntry = {
|
const tocEntry: ToCEntry = {
|
||||||
// id: i + 'id',
|
|
||||||
id: pageElement.id,
|
id: pageElement.id,
|
||||||
label: pageElement.title,
|
label: pageElement.title,
|
||||||
type: ToCEntryType.Page,
|
type: ToCEntryType.Page,
|
||||||
// form: pageElement,
|
|
||||||
numbering: (i + 1).toString(),
|
numbering: (i + 1).toString(),
|
||||||
subEntriesType: ToCEntryType.Section,
|
subEntriesType: ToCEntryType.Section,
|
||||||
subEntries: [],
|
subEntries: [],
|
||||||
ordinal: pageElement.ordinal
|
ordinal: pageElement.ordinal,
|
||||||
|
visibilityRuleKey: pageElement.id,
|
||||||
|
validityAbstractControl: null
|
||||||
};
|
};
|
||||||
|
|
||||||
const sections = descriptionTemplate.definition.pages.find(x => x.id == pageElement.id)?.sections;
|
const sections = descriptionTemplate.definition.pages.find(x => x.id == pageElement.id)?.sections;
|
||||||
|
@ -432,45 +230,10 @@ export class TableOfContentsComponent extends BaseComponent implements OnInit, O
|
||||||
}
|
}
|
||||||
|
|
||||||
onToCentrySelected(entry: ToCEntry = null, execute: boolean = true) {
|
onToCentrySelected(entry: ToCEntry = null, execute: boolean = true) {
|
||||||
if (!this.isSelecting) {
|
|
||||||
this.isSelecting = true;
|
|
||||||
this.tocentrySelected = entry;
|
this.tocentrySelected = entry;
|
||||||
this.entrySelected.emit({ entry: entry, execute: execute });
|
this.entrySelected.emit({ entry: entry, execute: execute });
|
||||||
setTimeout(() => {
|
|
||||||
this.isSelecting = false;
|
|
||||||
}, 600);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onScrollStarted(entry: ToCEntry) {
|
|
||||||
this.isScrolling = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
onScrollFinished(entry: ToCEntry) {
|
|
||||||
this.isScrolling = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Get all filedsets in a tocentry array;
|
* Get all filedsets in a tocentry array;
|
||||||
* @param entries Tocentries to search in
|
* @param entries Tocentries to search in
|
||||||
|
@ -492,15 +255,27 @@ export class TableOfContentsComponent extends BaseComponent implements OnInit, O
|
||||||
return fieldsets;
|
return fieldsets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static _findTocEntryById(id: string, tocentries: ToCEntry[]): ToCEntry {
|
||||||
public hasVisibleInvalidFields(): boolean {
|
if (!tocentries || !tocentries.length) {
|
||||||
if (!this.internalTable || !this.tocentries) {
|
return null;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return this.tocentries.some(e => this.internalTable.invalidChildsVisible(e));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected readonly console = console;
|
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 {
|
export interface LinkToScroll {
|
||||||
|
|
|
@ -5,12 +5,12 @@ import { TableOfContentsInternal } from './table-of-contents-internal/table-of-c
|
||||||
import { MatIconModule } from '@angular/material/icon';
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
import { VisibilityRulesService } from '@app/ui/description/editor/description-form/visibility-rules/visibility-rules.service';
|
import { VisibilityRulesService } from '@app/ui/description/editor/description-form/visibility-rules/visibility-rules.service';
|
||||||
import { TableOfContentsComponent } from './table-of-contents.component';
|
import { TableOfContentsComponent } from './table-of-contents.component';
|
||||||
import { TableOfContentsValidationService } from './services/table-of-contents-validation-service';
|
import { TableOfContentsService } from './services/table-of-contents-service';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [CommonModule, RouterModule, MatIconModule],
|
imports: [CommonModule, RouterModule, MatIconModule],
|
||||||
declarations: [TableOfContentsComponent, TableOfContentsInternal],
|
declarations: [TableOfContentsComponent, TableOfContentsInternal],
|
||||||
exports: [TableOfContentsComponent],
|
exports: [TableOfContentsComponent],
|
||||||
providers: [VisibilityRulesService, TableOfContentsValidationService]
|
providers: [VisibilityRulesService, TableOfContentsService]
|
||||||
})
|
})
|
||||||
export class TableOfContentsModule { }
|
export class TableOfContentsModule { }
|
||||||
|
|
Loading…
Reference in New Issue