Table of Contents (First Commit) (Issue #180)
This commit is contained in:
parent
3c6bd9685b
commit
4842587e10
|
@ -1,10 +1,8 @@
|
||||||
<div *ngIf="form && this.visibilityRulesService.checkElementVisibility(this.form.get('id').value)"
|
<div *ngIf="form && this.visibilityRulesService.checkElementVisibility(this.form.get('id').value)" [formGroup]="form" class="dynamic-form-composite-field row">
|
||||||
[id]="this.form.get('id').value" [formGroup]="form" class="dynamic-form-composite-field row">
|
|
||||||
|
|
||||||
<div *ngIf="form.get('fields').length === 1 && this.visibilityRulesService.checkElementVisibility(form.get('fields')['controls'][0].get('id').value)"
|
<div *ngIf="form.get('fields').length === 1 && this.visibilityRulesService.checkElementVisibility(form.get('fields')['controls'][0].get('id').value)" class="col-12">
|
||||||
class="col-12">
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<h5 *ngIf="form.get('title').value && !isChild" class="col-auto copositeField">
|
<h5 *ngIf="form.get('title').value && !isChild" class="col-auto copositeField toc-copositeField-header" [id]="this.form.get('id').value">
|
||||||
{{form.get('numbering').value}}
|
{{form.get('numbering').value}}
|
||||||
{{form.get('title').value}}
|
{{form.get('title').value}}
|
||||||
<!-- <a *ngIf="this.markForConsiderationService.exists(compositeField)" (click)="markForConsideration()" style="cursor: pointer">
|
<!-- <a *ngIf="this.markForConsiderationService.exists(compositeField)" (click)="markForConsideration()" style="cursor: pointer">
|
||||||
|
@ -31,7 +29,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="form.get('fields').length > 1" class="col-12">
|
<div *ngIf="form.get('fields').length > 1" class="col-12">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<h5 *ngIf="form.get('title').value && !isChild" class="col-auto copositeField">
|
<h5 *ngIf="form.get('title').value && !isChild" class="col-auto copositeField toc-copositeField-header" [id]="this.form.get('id').value">
|
||||||
{{form.get('numbering').value}}
|
{{form.get('numbering').value}}
|
||||||
{{form.get('title').value}}</h5>
|
{{form.get('title').value}}</h5>
|
||||||
<mat-icon class="col-auto" *ngIf="this.form.get('additionalInformation').value && !isChild"
|
<mat-icon class="col-auto" *ngIf="this.form.get('additionalInformation').value && !isChild"
|
||||||
|
|
|
@ -2,5 +2,5 @@
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #3a3737;
|
color: #3a3737;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
margin-top: 1em;
|
padding-top: 1em;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ export class FormCompositeFieldComponent {
|
||||||
|
|
||||||
@Input() datasetProfileId: String;
|
@Input() datasetProfileId: String;
|
||||||
@Input() form: FormGroup;
|
@Input() form: FormGroup;
|
||||||
@Input() isChild: Boolean =false;
|
@Input() isChild: Boolean = false;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public visibilityRulesService: VisibilityRulesService,
|
public visibilityRulesService: VisibilityRulesService,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<div class="dynamic-form-section row" [id]="this.form.get('id').value">
|
<div class="dynamic-form-section row" [id]="this.form.get('id').value">
|
||||||
<mat-accordion class="col-12">
|
<mat-accordion class="col-12">
|
||||||
<mat-expansion-panel class="row expansion-panel" expanded=true>
|
<mat-expansion-panel class="row expansion-panel toc-section-header" [id]="pathName" [(expanded)]="panelExpanded">
|
||||||
<mat-expansion-panel-header>
|
<mat-expansion-panel-header>
|
||||||
<mat-panel-title>
|
<mat-panel-title>
|
||||||
<h6>{{form.get('numbering').value}} {{form.get('title').value}}</h6>
|
<h6>{{form.get('numbering').value}} {{form.get('title').value}}</h6>
|
||||||
|
@ -60,7 +60,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="form.get('sections')" class="col-12">
|
<div *ngIf="form.get('sections')" class="col-12">
|
||||||
<div *ngFor="let subSectionFormGroup of form.get('sections')['controls']; let j = index;" class="row">
|
<div *ngFor="let subSectionFormGroup of form.get('sections')['controls']; let j = index;" class="row">
|
||||||
<app-form-section class="col-12" [form]="subSectionFormGroup" [path]="path+'.'+(j+1)" [pathName]="pathName+'.sections.'+j"
|
<app-form-section class="col-12" [form]="subSectionFormGroup" [path]="path+'.'+(j+1)" [pathName]="pathName+'.sections.'+j" [linkToScroll]="subsectionLinkToScroll"
|
||||||
[datasetProfileId]="datasetProfileId"></app-form-section>
|
[datasetProfileId]="datasetProfileId"></app-form-section>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,20 +1,24 @@
|
||||||
.dynamic-form-section {
|
.dynamic-form-section {
|
||||||
.expansion-panel {
|
.expansion-panel {
|
||||||
background-color: #eeeeee54;
|
background-color: #eeeeee54;
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
.addOneFieldButton {
|
.addOneFieldButton {
|
||||||
margin-top: -15px;
|
margin-top: -15px;
|
||||||
margin-left: -11px;
|
margin-left: -11px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.styleBorder{
|
.styleBorder {
|
||||||
border: 0.2em solid lightgray;
|
border: 0.2em solid lightgray;
|
||||||
border-radius: 0.5em;
|
border-radius: 0.5em;
|
||||||
margin-bottom: 0.5em;
|
margin-bottom: 0.5em;
|
||||||
}
|
}
|
||||||
.mat-expansion-panel-header-description{
|
.mat-expansion-panel-header-description {
|
||||||
padding-bottom: 18px;
|
padding-bottom: 18px;
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-expansion-panel-header {
|
||||||
|
height: auto !important;
|
||||||
|
min-height: 48px;
|
||||||
|
}
|
||||||
|
|
|
@ -1,23 +1,29 @@
|
||||||
import { AfterViewInit, Component, Input, OnInit } from '@angular/core';
|
import { AfterViewInit, Component, Input, OnInit, OnChanges, SimpleChanges } from '@angular/core';
|
||||||
import { FormGroup, FormArray, AbstractControl } from '@angular/forms';
|
import { FormGroup, FormArray, AbstractControl } from '@angular/forms';
|
||||||
import { FormFocusService } from '../../form-focus/form-focus.service';
|
import { FormFocusService } from '../../form-focus/form-focus.service';
|
||||||
import { VisibilityRulesService } from '../../visibility-rules/visibility-rules.service';
|
import { VisibilityRulesService } from '../../visibility-rules/visibility-rules.service';
|
||||||
import { DatasetDescriptionSectionEditorModel, DatasetDescriptionCompositeFieldEditorModel } from '../../dataset-description-form.model';
|
import { DatasetDescriptionSectionEditorModel, DatasetDescriptionCompositeFieldEditorModel } from '../../dataset-description-form.model';
|
||||||
import { FormCompositeFieldComponent } from '../form-composite-field/form-composite-field.component';
|
import { FormCompositeFieldComponent } from '../form-composite-field/form-composite-field.component';
|
||||||
|
import { LinkToScroll } from '../../tableOfContentsMaterial/table-of-contents';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-form-section',
|
selector: 'app-form-section',
|
||||||
templateUrl: './form-section.component.html',
|
templateUrl: './form-section.component.html',
|
||||||
styleUrls: ['./form-section.component.scss']
|
styleUrls: ['./form-section.component.scss']
|
||||||
})
|
})
|
||||||
export class FormSectionComponent implements OnInit {
|
export class FormSectionComponent implements OnInit, OnChanges {
|
||||||
|
|
||||||
//@Input() section: DatasetDescriptionSectionEditorModel;
|
//@Input() section: DatasetDescriptionSectionEditorModel;
|
||||||
@Input() datasetProfileId: String;
|
@Input() datasetProfileId: String;
|
||||||
@Input() form: FormGroup;
|
@Input() form: FormGroup;
|
||||||
@Input() pathName: string;
|
@Input() pathName: string;
|
||||||
@Input() path: string;
|
@Input() path: string;
|
||||||
|
@Input() linkToScroll: LinkToScroll;
|
||||||
//trackByFn = (index, item) => item ? item['id'] : null;
|
//trackByFn = (index, item) => item ? item['id'] : null;
|
||||||
|
panelExpanded = true;
|
||||||
|
// sub = true;
|
||||||
|
subsectionLinkToScroll: LinkToScroll;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public visibilityRulesService: VisibilityRulesService,
|
public visibilityRulesService: VisibilityRulesService,
|
||||||
private formFocusService: FormFocusService
|
private formFocusService: FormFocusService
|
||||||
|
@ -29,6 +35,21 @@ export class FormSectionComponent implements OnInit {
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges) {
|
||||||
|
|
||||||
|
if (changes['linkToScroll']) {
|
||||||
|
if (changes['linkToScroll'].currentValue && changes['linkToScroll'].currentValue.section) {
|
||||||
|
|
||||||
|
if (this.pathName === changes['linkToScroll'].currentValue.section) {
|
||||||
|
this.panelExpanded = true;
|
||||||
|
} else if (changes['linkToScroll'].currentValue.section.includes(this.pathName)) {
|
||||||
|
this.subsectionLinkToScroll = changes['linkToScroll'].currentValue;
|
||||||
|
this.panelExpanded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ngAfterViewInit() {
|
// ngAfterViewInit() {
|
||||||
// this.visibilityRulesService.triggerVisibilityEvaluation();
|
// this.visibilityRulesService.triggerVisibilityEvaluation();
|
||||||
// }
|
// }
|
||||||
|
|
|
@ -1,21 +1,29 @@
|
||||||
<form *ngIf="form" novalidate [formGroup]="form">
|
<form *ngIf="form" novalidate [formGroup]="form" class="col-12">
|
||||||
<div class="dynamic-form-editor">
|
<app-form-progress-indication class="col-12" *ngIf="form" [formGroup]="form"></app-form-progress-indication>
|
||||||
<app-form-progress-indication class="col-12" *ngIf="form" [formGroup]="form"></app-form-progress-indication>
|
<div class="row">
|
||||||
|
<div class="dynamic-form-editor col-md-8">
|
||||||
<div class="col-12 form-container" id="form-container">
|
<div class="form-container" id="form-container">
|
||||||
<mat-vertical-stepper #stepper [linear]="false">
|
<mat-vertical-stepper #stepper [linear]="false">
|
||||||
<div *ngFor="let pageFormGroup of form.get('pages')['controls']; let z = index;">
|
<div *ngFor="let pageFormGroup of form.get('pages')['controls']; let z = index;">
|
||||||
<div *ngFor="let sectionFormGroup of pageFormGroup.get('sections')['controls']; let i = index;">
|
<div *ngFor="let sectionFormGroup of pageFormGroup.get('sections')['controls']; let i = index;">
|
||||||
<mat-step [stepControl]="sectionFormGroup">
|
<mat-step [stepControl]="sectionFormGroup">
|
||||||
<ng-template matStepLabel>{{pageFormGroup.get('title').value}}</ng-template>
|
<ng-template matStepLabel><span class="toc-page-header" [id]="'page_' + z">{{pageFormGroup.get('title').value}}</span></ng-template>
|
||||||
<!-- <div *ngIf="stepper.selectedIndex == z" class="row"> -->
|
<!-- <div *ngIf="stepper.selectedIndex == z" class="row"> -->
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<app-form-section class="col-12" [form]="sectionFormGroup" [path]="z+1" [pathName]="'pages.'+z+'.sections.'+i" [datasetProfileId]="datasetProfileId"></app-form-section>
|
<app-form-section class="col-12" [form]="sectionFormGroup" [path]="z+1" [pathName]="'pages.'+z+'.sections.'+i" [datasetProfileId]="datasetProfileId" [linkToScroll]="linkToScroll"></app-form-section>
|
||||||
</div>
|
</div>
|
||||||
</mat-step>
|
</mat-step>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</mat-vertical-stepper>
|
||||||
</mat-vertical-stepper>
|
</div>
|
||||||
|
<!-- <div><table-of-contents (stepFound)="onStepFound($event)"></table-of-contents></div> -->
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<table-of-contents (stepFound)="onStepFound($event)" class="w-100"></table-of-contents>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
<!-- <div>
|
||||||
|
<table-of-contents (stepFound)="onStepFound($event)"></table-of-contents>
|
||||||
|
</div> -->
|
||||||
|
|
|
@ -12,6 +12,28 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.toc-and-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
text-align: left;
|
||||||
|
max-width: 940px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
// table-of-contents {
|
||||||
|
// top: 0px;
|
||||||
|
// position: sticky;
|
||||||
|
|
||||||
|
// Reposition on top of content on small screens and remove
|
||||||
|
// sticky positioning
|
||||||
|
// @media (max-width: 720px) {
|
||||||
|
// order: -1;
|
||||||
|
// position: inherit;
|
||||||
|
// width: auto;
|
||||||
|
// padding-left: 0;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
// .ng-sidebar {
|
// .ng-sidebar {
|
||||||
// width: 40%;
|
// width: 40%;
|
||||||
// }
|
// }
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { BaseComponent } from '../../../core/common/base/base.component';
|
import { BaseComponent } from '../../../core/common/base/base.component';
|
||||||
import { Rule } from '../../../core/model/dataset-profile-definition/rule';
|
import { Rule } from '../../../core/model/dataset-profile-definition/rule';
|
||||||
import { FormFocusService } from './form-focus/form-focus.service';
|
import { FormFocusService } from './form-focus/form-focus.service';
|
||||||
|
import { LinkToScroll } from './tableOfContentsMaterial/table-of-contents';
|
||||||
import { VisibilityRulesService } from './visibility-rules/visibility-rules.service';
|
import { VisibilityRulesService } from './visibility-rules/visibility-rules.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -31,6 +32,9 @@ export class DatasetDescriptionFormComponent extends BaseComponent implements On
|
||||||
// pageTrackByFn = (index, item) => item['id'];
|
// pageTrackByFn = (index, item) => item['id'];
|
||||||
@Input() datasetProfileId: String;
|
@Input() datasetProfileId: String;
|
||||||
|
|
||||||
|
linkToScroll: LinkToScroll;
|
||||||
|
|
||||||
|
// uniqueId = Math.random().toString(36).substr(2, 9);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private router: Router,
|
private router: Router,
|
||||||
|
@ -79,6 +83,14 @@ export class DatasetDescriptionFormComponent extends BaseComponent implements On
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onStepFound(linkToScroll: LinkToScroll) {
|
||||||
|
|
||||||
|
if (linkToScroll.page >= 0) {
|
||||||
|
this.stepper.selectedIndex = linkToScroll.page;
|
||||||
|
}
|
||||||
|
this.linkToScroll = linkToScroll;
|
||||||
|
}
|
||||||
|
|
||||||
ngAfterViewInit() {
|
ngAfterViewInit() {
|
||||||
//this.visibilityRulesService.triggerVisibilityEvaluation();
|
//this.visibilityRulesService.triggerVisibilityEvaluation();
|
||||||
// this.route.queryParams
|
// this.route.queryParams
|
||||||
|
|
|
@ -9,12 +9,15 @@ import { FormSectionComponent } from './components/form-section/form-section.com
|
||||||
import { DatasetDescriptionFormComponent } from './dataset-description-form.component';
|
import { DatasetDescriptionFormComponent } from './dataset-description-form.component';
|
||||||
import { FormFocusService } from './form-focus/form-focus.service';
|
import { FormFocusService } from './form-focus/form-focus.service';
|
||||||
import { VisibilityRulesService } from './visibility-rules/visibility-rules.service';
|
import { VisibilityRulesService } from './visibility-rules/visibility-rules.service';
|
||||||
|
import { TableOfContentsModule } from './tableOfContentsMaterial/table-of-contents.module';
|
||||||
|
import { TableOfContents } from './tableOfContentsMaterial/table-of-contents';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
CommonUiModule,
|
CommonUiModule,
|
||||||
CommonFormsModule,
|
CommonFormsModule,
|
||||||
AutoCompleteModule
|
AutoCompleteModule,
|
||||||
|
TableOfContentsModule
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
DatasetDescriptionFormComponent,
|
DatasetDescriptionFormComponent,
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
<div *ngIf="getLinks()?.length" class="docs-toc-container">
|
||||||
|
<div class="docs-toc-heading">Contents</div>
|
||||||
|
<!-- <nav> -->
|
||||||
|
<!-- <a [href]="_rootUrl + '#' + link.id" -->
|
||||||
|
<span *ngFor="let link of getLinks(); let i = index"
|
||||||
|
(click)="goToStep(link)"
|
||||||
|
class="docs-level-{{link.type}} docs-link"
|
||||||
|
[class.docs-active]="link.active">
|
||||||
|
{{link.name}}
|
||||||
|
</span>
|
||||||
|
<!-- </nav> -->
|
||||||
|
</div>
|
|
@ -0,0 +1,12 @@
|
||||||
|
import {CommonModule} from '@angular/common';
|
||||||
|
import {NgModule} from '@angular/core';
|
||||||
|
import {TableOfContents} from './table-of-contents';
|
||||||
|
import {RouterModule} from '@angular/router';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [CommonModule, RouterModule],
|
||||||
|
declarations: [TableOfContents],
|
||||||
|
exports: [TableOfContents],
|
||||||
|
entryComponents: [TableOfContents],
|
||||||
|
})
|
||||||
|
export class TableOfContentsModule { }
|
|
@ -0,0 +1,58 @@
|
||||||
|
:host {
|
||||||
|
font-size: 13px;
|
||||||
|
// Width is container width minus content width
|
||||||
|
width: 19%;
|
||||||
|
height: fit-content;
|
||||||
|
position: -webkit-sticky;
|
||||||
|
position: sticky;
|
||||||
|
// display: inline-flex;
|
||||||
|
align-self: flex-start;
|
||||||
|
top: 0;
|
||||||
|
padding-left: 25px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-toc-container {
|
||||||
|
width: 100%;
|
||||||
|
padding: 5px 0 10px 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-left: solid 4px #0c7489;
|
||||||
|
|
||||||
|
.docs-link {
|
||||||
|
color: rgba(0, 0, 0, 0.54);
|
||||||
|
// color: mat-color($app-blue-theme-foreground, secondary-text);
|
||||||
|
transition: color 100ms;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&.docs-active {
|
||||||
|
color: #0c7489;
|
||||||
|
// color: mat-color($primary, if($is-dark-theme, 200, default));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-toc-heading {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
line-height: 16px;
|
||||||
|
margin: 8px 0 0;
|
||||||
|
position: relative;
|
||||||
|
text-decoration: none;
|
||||||
|
display: block;
|
||||||
|
text-overflow: ellipsis !important;
|
||||||
|
overflow: hidden;
|
||||||
|
color: rgba(0, 0, 0, 0.54);
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-level-mat-expansion-panel {
|
||||||
|
margin-left: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-level-h5 {
|
||||||
|
margin-left: 24px;
|
||||||
|
}
|
|
@ -0,0 +1,221 @@
|
||||||
|
import { DOCUMENT } from '@angular/common';
|
||||||
|
import { AfterViewInit, Component, ElementRef, EventEmitter, Inject, Input, OnDestroy, OnInit, Output } from '@angular/core';
|
||||||
|
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
|
||||||
|
import { fromEvent, Subject } from 'rxjs';
|
||||||
|
import { debounceTime, takeUntil } from 'rxjs/operators';
|
||||||
|
import { link } from 'fs';
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'table-of-contents',
|
||||||
|
styleUrls: ['./table-of-contents.scss'],
|
||||||
|
templateUrl: './table-of-contents.html'
|
||||||
|
})
|
||||||
|
export class TableOfContents implements OnInit, AfterViewInit, OnDestroy {
|
||||||
|
|
||||||
|
@Input() links: Link[] = [];
|
||||||
|
@Input() container: string;
|
||||||
|
@Input() headerSelectors = '.toc-page-header, .toc-section-header, .toc-copositeField-header';
|
||||||
|
|
||||||
|
@Output() stepFound = new EventEmitter<LinkToScroll>();
|
||||||
|
|
||||||
|
_rootUrl = this._router.url.split('#')[0];
|
||||||
|
private _scrollContainer: any;
|
||||||
|
private _destroyed = new Subject();
|
||||||
|
private _urlFragment = '';
|
||||||
|
private selectedLinkId: string;
|
||||||
|
|
||||||
|
constructor(private _router: Router,
|
||||||
|
private _route: ActivatedRoute,
|
||||||
|
private _element: ElementRef,
|
||||||
|
@Inject(DOCUMENT) private _document: Document) {
|
||||||
|
|
||||||
|
this._router.events.pipe(takeUntil(this._destroyed)).subscribe((event) => {
|
||||||
|
if (event instanceof NavigationEnd) {
|
||||||
|
const rootUrl = _router.url.split('#')[0];
|
||||||
|
if (rootUrl !== this._rootUrl) {
|
||||||
|
this.links = this.createLinks();
|
||||||
|
this._rootUrl = rootUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this._route.fragment.pipe(takeUntil(this._destroyed)).subscribe(fragment => {
|
||||||
|
this._urlFragment = fragment;
|
||||||
|
|
||||||
|
const target = document.getElementById(this._urlFragment);
|
||||||
|
if (target) {
|
||||||
|
target.scrollIntoView();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
// On init, the sidenav content element doesn't yet exist, so it's not possible
|
||||||
|
// to subscribe to its scroll event until next tick (when it does exist).
|
||||||
|
Promise.resolve().then(() => {
|
||||||
|
this._scrollContainer = this.container ?
|
||||||
|
this._document.querySelectorAll(this.container)[0] : window;
|
||||||
|
|
||||||
|
if (this._scrollContainer) {
|
||||||
|
fromEvent(this._scrollContainer, 'scroll').pipe(
|
||||||
|
takeUntil(this._destroyed),
|
||||||
|
debounceTime(10))
|
||||||
|
.subscribe(() => this.onScroll());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit() {
|
||||||
|
this.updateScrollPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this._destroyed.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateScrollPosition(): void {
|
||||||
|
this.links = this.createLinks();
|
||||||
|
|
||||||
|
const target = document.getElementById(this._urlFragment);
|
||||||
|
if (target) {
|
||||||
|
target.scrollIntoView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets the scroll offset of the scroll container */
|
||||||
|
private getScrollOffset(): number | void {
|
||||||
|
const { top } = this._element.nativeElement.getBoundingClientRect();
|
||||||
|
if (typeof this._scrollContainer.scrollTop !== 'undefined') {
|
||||||
|
return this._scrollContainer.scrollTop + top;
|
||||||
|
} else if (typeof this._scrollContainer.pageYOffset !== 'undefined') {
|
||||||
|
return this._scrollContainer.pageYOffset + top;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private createLinks(): Link[] {
|
||||||
|
const links: Array<Link> = [];
|
||||||
|
const headers =
|
||||||
|
Array.from(this._document.querySelectorAll(this.headerSelectors)) as HTMLElement[];
|
||||||
|
|
||||||
|
if (headers.length) {
|
||||||
|
for (const header of headers) {
|
||||||
|
// const step = 0;
|
||||||
|
// remove the 'link' icon name from the inner text
|
||||||
|
const name = header.innerText.trim().replace(/^link/, '');
|
||||||
|
const { top } = header.getBoundingClientRect();
|
||||||
|
// links.push({
|
||||||
|
// name,
|
||||||
|
// // step,
|
||||||
|
// type: header.tagName.toLowerCase(),
|
||||||
|
// top: top,
|
||||||
|
// id: header.id,
|
||||||
|
// active: false,
|
||||||
|
// section: section
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return links;
|
||||||
|
}
|
||||||
|
|
||||||
|
private onScroll(): void {
|
||||||
|
// for (let i = 0; i < this.links.length; i++) {
|
||||||
|
// this.links[i].active = this.isLinkActive(this.links[i], this.links[i + 1]);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
// private isLinkActive(currentLink: any, nextLink: any): boolean {
|
||||||
|
// A link is considered active if the page is scrolled passed the anchor without also
|
||||||
|
// being scrolled passed the next link
|
||||||
|
// const scrollOffset = this.getScrollOffset();
|
||||||
|
// return scrollOffset >= currentLink.top && !(nextLink && nextLink.top < scrollOffset);
|
||||||
|
// }
|
||||||
|
|
||||||
|
getLinks() {
|
||||||
|
const links: Array<Link> = [];
|
||||||
|
const headers =
|
||||||
|
Array.from(this._document.querySelectorAll(this.headerSelectors)) as HTMLElement[];
|
||||||
|
|
||||||
|
if (headers.length) {
|
||||||
|
let page;
|
||||||
|
let section;
|
||||||
|
for (const header of headers) {
|
||||||
|
let name;
|
||||||
|
let id;
|
||||||
|
if (header.classList.contains('toc-page-header')) {
|
||||||
|
name = header.innerText.trim().replace(/^link/, '');
|
||||||
|
id = header.id;
|
||||||
|
page = header.id.split('_')[1];
|
||||||
|
section = undefined;
|
||||||
|
} 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;
|
||||||
|
} else if (header.classList.contains('toc-copositeField-header')) {
|
||||||
|
name = (header.childNodes[0]).nodeValue.trim().replace(/^link/, '');
|
||||||
|
id = header.id;
|
||||||
|
// id = header.parentElement.parentElement.parentElement.id;
|
||||||
|
}
|
||||||
|
const { top } = header.getBoundingClientRect();
|
||||||
|
links.push({
|
||||||
|
name,
|
||||||
|
id,
|
||||||
|
type: header.tagName.toLowerCase(),
|
||||||
|
top: top,
|
||||||
|
active: this.selectedLinkId == id ? true : false,
|
||||||
|
page: page,
|
||||||
|
section: section
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.links = links;
|
||||||
|
return links;
|
||||||
|
}
|
||||||
|
|
||||||
|
goToStep(link: Link) {
|
||||||
|
this.selectedLinkId = link.id;
|
||||||
|
this.stepFound.emit({
|
||||||
|
page: link.page,
|
||||||
|
section: link.section
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
const target = document.getElementById(link.id);
|
||||||
|
target.scrollIntoView(true);
|
||||||
|
|
||||||
|
var scrolledY = window.scrollY;
|
||||||
|
if (scrolledY) {
|
||||||
|
window.scroll(0, scrolledY - 70);
|
||||||
|
}
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LinkToScroll {
|
||||||
|
page: number;
|
||||||
|
section: number;
|
||||||
|
}
|
Loading…
Reference in New Issue