text-panel.component.ts 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. import { Component, ElementRef, Input, OnDestroy, OnInit, Output, ViewChild } from '@angular/core';
  2. import { BehaviorSubject, combineLatest, Observable, Subject, Subscription } from 'rxjs';
  3. import { delay, distinctUntilChanged, filter, map, shareReplay, take } from 'rxjs/operators';
  4. import { AppConfig, EditionLevel, EditionLevelType, TextFlow } from '../../app.config';
  5. import { EntitiesSelectItem } from '../../components/entities-select/entities-select.component';
  6. import { LemsSelectItem } from '../../components/lems-select/lems-select.component';
  7. import { IperlemsSelectItem } from '../../components/iperlems-select/iperlems-select.component';
  8. import { Page } from '../../models/evt-models';
  9. import { EVTModelService } from '../../services/evt-model.service';
  10. import { EVTStatusService } from '../../services/evt-status.service';
  11. import { EvtIconInfo } from '../../ui-components/icon/icon.component';
  12. @Component({
  13. selector: 'evt-text-panel',
  14. templateUrl: './text-panel.component.html',
  15. styleUrls: ['./text-panel.component.scss'],
  16. })
  17. export class TextPanelComponent implements OnInit, OnDestroy {
  18. @ViewChild('mainContent') mainContent: ElementRef;
  19. @Input() hideEditionLevelSelector: boolean;
  20. @Input() pageID: string;
  21. public currentPage$ = new BehaviorSubject<Page>(undefined);
  22. public currentPageId$ = this.currentPage$.pipe(
  23. map(p => p?.id),
  24. );
  25. @Output() pageChange: Observable<Page> = this.currentPage$.pipe(
  26. filter(p => !!p),
  27. distinctUntilChanged(),
  28. );
  29. @Input() editionLevelID: EditionLevelType;
  30. public currentEdLevel$ = new BehaviorSubject<EditionLevel>(undefined);
  31. public currentEdLevelId$ = this.currentEdLevel$.pipe(
  32. map(e => e?.id),
  33. );
  34. @Output() editionLevelChange: Observable<EditionLevel> = this.currentEdLevel$.pipe(
  35. filter(e => !!e),
  36. distinctUntilChanged(),
  37. );
  38. public currentStatus$ = combineLatest([
  39. this.evtModelService.pages$,
  40. this.currentPage$,
  41. this.currentEdLevel$,
  42. this.evtStatus.currentViewMode$,
  43. ]).pipe(
  44. delay(0),
  45. filter(([pages, currentPage, editionLevel, currentViewMode]) => !!pages && !!currentPage && !!editionLevel && !!currentViewMode),
  46. map(([pages, currentPage, editionLevel, currentViewMode]) => ({ pages, currentPage, editionLevel, currentViewMode })),
  47. distinctUntilChanged((a, b) => JSON.stringify(a) === JSON.stringify(b)),
  48. shareReplay(1),
  49. );
  50. public itemsToHighlight$ = new Subject<EntitiesSelectItem[]>();
  51. public itemsLemsToHighlight$ = new Subject<LemsSelectItem[]>();
  52. public itemsIperlemsToHighlight$ = new Subject<IperlemsSelectItem[]>();
  53. public secondaryContent = '';
  54. private showSecondaryContent = false;
  55. public selectedPage;
  56. public textFlow: TextFlow = AppConfig.evtSettings.edition.defaultTextFlow || 'prose';
  57. public enableProseVersesToggler = AppConfig.evtSettings.edition.proseVersesToggler;
  58. public get proseVersesTogglerIcon(): EvtIconInfo {
  59. return { icon: this.textFlow === 'prose' ? 'align-left' : 'align-justify', iconSet: 'fas' };
  60. }
  61. public isMultiplePageFlow$ = this.currentStatus$.pipe(
  62. map((x) => x.editionLevel.id === 'critical' && x.currentViewMode.id !== 'imageText'),
  63. shareReplay(1),
  64. );
  65. private updatingPageFromScroll = false;
  66. private subscriptions: Subscription[] = [];
  67. constructor(
  68. public evtModelService: EVTModelService,
  69. public evtStatus: EVTStatusService,
  70. ) {
  71. }
  72. ngOnInit() {
  73. if (this.editionLevelID === 'critical') {
  74. this.textFlow = AppConfig.evtSettings.edition.defaultTextFlow || 'verses';
  75. }
  76. if (!this.enableProseVersesToggler) {
  77. this.textFlow = undefined;
  78. }
  79. this.subscriptions.push(
  80. this.currentStatus$.pipe(
  81. map(currentStatus => currentStatus.currentPage),
  82. filter(page => !!page),
  83. delay(0), // Needed to have the HTML pb el available
  84. ).subscribe((page) => this._scrollToPage(page.id)),
  85. );
  86. }
  87. getSecondaryContent(): string {
  88. return this.secondaryContent;
  89. }
  90. isSecondaryContentOpened(): boolean {
  91. return this.showSecondaryContent;
  92. }
  93. toggleSecondaryContent(newContent: string) {
  94. if (this.secondaryContent !== newContent) {
  95. this.showSecondaryContent = true;
  96. this.secondaryContent = newContent;
  97. }
  98. else {
  99. this.showSecondaryContent = false;
  100. this.secondaryContent = '';
  101. }
  102. }
  103. toggleProseVerses() {
  104. this.textFlow = this.textFlow === 'prose' ? 'verses' : 'prose';
  105. }
  106. ngOnDestroy() {
  107. this.subscriptions.forEach(subscription => subscription.unsubscribe());
  108. }
  109. updatePage() {
  110. if (this.mainContent && this.editionLevelID === 'critical') {
  111. const mainContentEl: HTMLElement = this.mainContent.nativeElement;
  112. const pbs = mainContentEl.querySelectorAll('evt-page');
  113. let pbCount = 0;
  114. let pbVisible = false;
  115. let pbId = '';
  116. const docViewTop = mainContentEl.scrollTop;
  117. const docViewBottom = docViewTop + mainContentEl.parentElement.clientHeight;
  118. while (pbCount < pbs.length && !pbVisible) {
  119. pbId = pbs[pbCount].getAttribute('data-id');
  120. const pbElem = mainContentEl.querySelector<HTMLElement>(`evt-page[data-id="${pbId}"]`);
  121. const pbRect = pbElem.getBoundingClientRect();
  122. if (pbRect.top && (pbRect.top <= docViewBottom) && (pbRect.top >= docViewTop)) {
  123. pbVisible = true;
  124. } else {
  125. pbCount++;
  126. }
  127. }
  128. combineLatest([this.evtModelService.pages$, this.currentPageId$])
  129. .pipe(take(1)).subscribe(([pages, currentPageId]) => {
  130. if (pbVisible && currentPageId !== pbId) {
  131. this.updatingPageFromScroll = true;
  132. this.evtStatus.updatePage$.next(pages.find(p => p.id === pbId));
  133. }
  134. });
  135. }
  136. }
  137. private _scrollToPage(pageId: string) {
  138. if (this.updatingPageFromScroll) {
  139. this.updatingPageFromScroll = false;
  140. } else if (this.mainContent) {
  141. const mainContentEl: HTMLElement = this.mainContent.nativeElement;
  142. const pageEl = mainContentEl.querySelector<HTMLElement>(`[data-id="${pageId}"]`);
  143. if (pageEl) {
  144. pageEl.scrollIntoView();
  145. } else {
  146. mainContentEl.parentElement.scrollTop = 0;
  147. }
  148. }
  149. }
  150. }