diff --git a/UI/Web/src/app/book-reader/_components/book-reader/book-reader.component.html b/UI/Web/src/app/book-reader/_components/book-reader/book-reader.component.html index efd4ffcda..24b1d9d16 100644 --- a/UI/Web/src/app/book-reader/_components/book-reader/book-reader.component.html +++ b/UI/Web/src/app/book-reader/_components/book-reader/book-reader.component.html @@ -101,20 +101,24 @@
-
-
+ tabindex="-1" + [ngStyle]="{height: PageHeightForPagination}">
+
-
+
+ [innerHtml]="page" *ngIf="page !== undefined" (wheel)="onWheel($event)">
diff --git a/UI/Web/src/app/book-reader/_components/book-reader/book-reader.component.scss b/UI/Web/src/app/book-reader/_components/book-reader/book-reader.component.scss index c9e0ca83f..e5fbcece0 100644 --- a/UI/Web/src/app/book-reader/_components/book-reader/book-reader.component.scss +++ b/UI/Web/src/app/book-reader/_components/book-reader/book-reader.component.scss @@ -199,6 +199,9 @@ $action-bar-height: 38px; overflow: auto; } + &.pointer { + cursor: pointer; + } } .book-content { @@ -339,8 +342,8 @@ $action-bar-height: 38px; top: 0px; } - &:hover { - cursor: pointer; + &.no-pointer-events { + pointer-events: none; } } @@ -360,8 +363,9 @@ $action-bar-height: 38px; &.immersive { top: 0px; } - &:hover { - cursor: pointer; + + &.no-pointer-events { + pointer-events: none; } } @@ -381,8 +385,8 @@ $action-bar-height: 38px; top: 0px; } - &:hover { - cursor: pointer; + &.no-pointer-events { + pointer-events: none; } } diff --git a/UI/Web/src/app/book-reader/_components/book-reader/book-reader.component.ts b/UI/Web/src/app/book-reader/_components/book-reader/book-reader.component.ts index 99b5a2880..a106620b4 100644 --- a/UI/Web/src/app/book-reader/_components/book-reader/book-reader.component.ts +++ b/UI/Web/src/app/book-reader/_components/book-reader/book-reader.component.ts @@ -17,7 +17,7 @@ import { DOCUMENT, Location, NgTemplateOutlet, NgIf, NgStyle, NgClass } from '@a import { ActivatedRoute, Router } from '@angular/router'; import { ToastrService } from 'ngx-toastr'; import { forkJoin, fromEvent, of } from 'rxjs'; -import { catchError, debounceTime, take } from 'rxjs/operators'; +import { catchError, debounceTime, distinctUntilChanged, map, take } from 'rxjs/operators'; import { Chapter } from 'src/app/_models/chapter'; import { AccountService } from 'src/app/_services/account.service'; import { NavService } from 'src/app/_services/nav.service'; @@ -292,6 +292,19 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy { writingStyle: WritingStyle = WritingStyle.Horizontal; + /** + * Controls whether the cursor is a pointer when tap to paginate is on and the cursor is over + * either of the pagination areas + */ + cursorIsPointer: boolean = false; + + /** + * Used to keep track of the last time a paginator area was clicked to prevent the default + * browser behavior of selecting words or lines when double- or triple-clicking, respectively, + * triggered by repeated click pagination (when enabled). + */ + lastPaginatorClickTime: number = 0; + /** * Used to refresh the Personal PoC */ @@ -497,8 +510,14 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy { this.handleScrollEvent(); }); - // TODO: In order to allow people to highlight content under pagination overlays, apply pointer-events: none while - // a highlight operation is in effect + fromEvent(this.bookContainerElemRef.nativeElement, 'mousemove') + .pipe( + takeUntilDestroyed(this.destroyRef), + map(this.isCursorOverPaginationArea.bind(this)), + distinctUntilChanged()) + .subscribe((isPointer) => { + this.changeCursor(isPointer); + }); } handleScrollEvent() { @@ -823,6 +842,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy { const links = this.readingSectionElemRef.nativeElement.querySelectorAll('a'); links.forEach((link: any) => { link.addEventListener('click', (e: any) => { + e.stopPropagation(); let targetElem = e.target; if (e.target.nodeName !== 'A' && e.target.parentNode.nodeName === 'A') { // Certain combos like text can cause the target to be the sup tag and not the anchor @@ -1568,7 +1588,49 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy { return side === 'right' ? 'highlight-2' : 'highlight'; } + private isCursorOverLeftPaginationArea(event: MouseEvent): boolean { + const leftPaginationAreaEnd = window.innerWidth * 0.2; + return event.clientX <= leftPaginationAreaEnd; + } + private isCursorOverRightPaginationArea(event: MouseEvent): boolean { + const rightPaginationAreaStart = event.clientX >= window.innerWidth * 0.8; + return rightPaginationAreaStart; + } + + private isCursorOverPaginationArea(event: MouseEvent): boolean { + if (this.isLoading || !this.clickToPaginate) { + return false; + } + + return this.isCursorOverLeftPaginationArea(event) || this.isCursorOverRightPaginationArea(event); + } + + private changeCursor(isPointer: boolean) { + this.cursorIsPointer = isPointer; + this.cdRef.markForCheck(); + } + + handleReaderClick(event: MouseEvent) { + if (!this.clickToPaginate) { + this.toggleMenu(event); + return; + } + + const isHighlighting = window.getSelection()?.toString() != ''; + + if (isHighlighting) { + return; + } + + if (this.isCursorOverLeftPaginationArea(event)) { + this.movePage(this.readingDirection === ReadingDirection.LeftToRight ? PAGING_DIRECTION.BACKWARDS : PAGING_DIRECTION.FORWARD); + } else if (this.isCursorOverRightPaginationArea(event)) { + this.movePage(this.readingDirection === ReadingDirection.LeftToRight ? PAGING_DIRECTION.FORWARD : PAGING_DIRECTION.BACKWARDS) + } else { + this.toggleMenu(event); + } + } toggleMenu(event: MouseEvent) { const targetElement = (event.target as Element); @@ -1581,8 +1643,8 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy { } if ( - Math.abs(this.mousePosition.x - event.screenX) <= mouseOffset && - Math.abs(this.mousePosition.y - event.screenY) <= mouseOffset + Math.abs(this.mousePosition.x - event.clientX) <= mouseOffset && + Math.abs(this.mousePosition.y - event.clientY) <= mouseOffset ) { this.actionBarVisible = !this.actionBarVisible; this.cdRef.markForCheck(); @@ -1590,8 +1652,25 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy { } mouseDown($event: MouseEvent) { - this.mousePosition.x = $event.screenX; - this.mousePosition.y = $event.screenY; + this.mousePosition.x = $event.clientX; + this.mousePosition.y = $event.clientY; + + if (!this.clickToPaginate || !this.isCursorOverPaginationArea($event)) { + return + } + + // This value is completely arbitrary and should cover most + // double-click speeds + const halfASecond = 500; + const now = Date.now(); + + // This is to prevent selecting text when clicking repeatedly to switch pages, + // while also allowing selections to begin in a pagination area + if (now - this.lastPaginatorClickTime < halfASecond) { + $event.preventDefault(); + } + + this.lastPaginatorClickTime = now; } refreshPersonalToC() { diff --git a/openapi.json b/openapi.json index 386732a08..98d40bc46 100644 --- a/openapi.json +++ b/openapi.json @@ -18246,4 +18246,4 @@ "description": "Responsible for all things Want To Read" } ] -} \ No newline at end of file +}