-
Notifications
You must be signed in to change notification settings - Fork 297
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Development: Decompose PDF Preview components (#9592)
- Loading branch information
Showing
13 changed files
with
1,343 additions
and
1,263 deletions.
There are no files selected for viewing
18 changes: 18 additions & 0 deletions
18
...ecture/pdf-preview/pdf-preview-enlarged-canvas/pdf-preview-enlarged-canvas.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<div #enlargedContainer class="enlarged-container" (click)="closeIfOutside($event)"> | ||
@if (isEnlargedCanvasLoading()) { | ||
<div class="d-flex justify-content-center position-absolute"> | ||
<div class="spinner-border" role="status"> | ||
<span class="sr-only" jhiTranslate="loading"></span> | ||
</div> | ||
</div> | ||
} | ||
<canvas #enlargedCanvas></canvas> | ||
<button class="btn btn-close" (click)="closeEnlargedView($event)"></button> | ||
@if (currentPage() !== 1) { | ||
<button class="btn btn-secondary nav-button left" (click)="handleNavigation('prev', $event)" aria-label="Previous page">←</button> | ||
} | ||
@if (currentPage() !== totalPages()) { | ||
<button class="btn btn-secondary nav-button right" (click)="handleNavigation('next', $event)" aria-label="Next page">→</button> | ||
} | ||
<div class="page-number-display">{{ currentPage() }}</div> | ||
</div> |
62 changes: 62 additions & 0 deletions
62
...ecture/pdf-preview/pdf-preview-enlarged-canvas/pdf-preview-enlarged-canvas.component.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
.enlarged-container { | ||
position: absolute; | ||
width: 100%; | ||
height: 100%; | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
background-color: var(--pdf-preview-enlarged-container-overlay); | ||
z-index: 5; | ||
|
||
.btn-close { | ||
position: absolute; | ||
top: 10px; | ||
right: 10px; | ||
cursor: pointer; | ||
color: var(--bs-body-color); | ||
} | ||
} | ||
|
||
.nav-button { | ||
position: absolute; | ||
transform: translateY(-50%); | ||
cursor: pointer; | ||
border-radius: 50%; | ||
width: 30px; | ||
height: 30px; | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
font-size: 20px; | ||
z-index: 3; | ||
} | ||
|
||
.nav-button.left { | ||
left: calc(5% + 10px); | ||
|
||
@media (max-width: 1200px) { | ||
left: 10px; | ||
} | ||
} | ||
|
||
.nav-button.right { | ||
right: calc(5% + 10px); | ||
|
||
@media (max-width: 1200px) { | ||
right: 10px; | ||
} | ||
} | ||
|
||
.page-number-display { | ||
position: absolute; | ||
bottom: 10px; | ||
right: calc(5% + 10px); | ||
font-size: 18px; | ||
color: var(--bs-body-color); | ||
z-index: 2; | ||
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); | ||
|
||
@media (max-width: 1200px) { | ||
right: 10px; | ||
} | ||
} |
225 changes: 225 additions & 0 deletions
225
.../lecture/pdf-preview/pdf-preview-enlarged-canvas/pdf-preview-enlarged-canvas.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,225 @@ | ||
import { Component, ElementRef, HostListener, effect, input, output, signal, viewChild } from '@angular/core'; | ||
import { ArtemisSharedModule } from 'app/shared/shared.module'; | ||
|
||
type NavigationDirection = 'next' | 'prev'; | ||
|
||
@Component({ | ||
selector: 'jhi-pdf-preview-enlarged-canvas-component', | ||
templateUrl: './pdf-preview-enlarged-canvas.component.html', | ||
styleUrls: ['./pdf-preview-enlarged-canvas.component.scss'], | ||
standalone: true, | ||
imports: [ArtemisSharedModule], | ||
}) | ||
export class PdfPreviewEnlargedCanvasComponent { | ||
enlargedContainer = viewChild.required<ElementRef<HTMLDivElement>>('enlargedContainer'); | ||
enlargedCanvas = viewChild.required<ElementRef<HTMLCanvasElement>>('enlargedCanvas'); | ||
|
||
readonly DEFAULT_ENLARGED_SLIDE_HEIGHT = 800; | ||
|
||
// Inputs | ||
pdfContainer = input.required<HTMLDivElement>(); | ||
originalCanvas = input<HTMLCanvasElement>(); | ||
totalPages = input<number>(0); | ||
|
||
// Signals | ||
currentPage = signal<number>(1); | ||
isEnlargedCanvasLoading = signal<boolean>(false); | ||
|
||
//Outputs | ||
isEnlargedViewOutput = output<boolean>(); | ||
|
||
constructor() { | ||
effect( | ||
() => { | ||
this.enlargedContainer().nativeElement.style.top = `${this.pdfContainer().scrollTop}px`; | ||
this.displayEnlargedCanvas(this.originalCanvas()!); | ||
}, | ||
{ allowSignalWrites: true }, | ||
); | ||
} | ||
|
||
/** | ||
* Handles navigation within the PDF viewer using keyboard arrow keys. | ||
* @param event - The keyboard event captured for navigation. | ||
*/ | ||
@HostListener('document:keydown', ['$event']) | ||
handleKeyboardEvents(event: KeyboardEvent) { | ||
if (event.key === 'ArrowRight' && this.currentPage() < this.totalPages()) { | ||
this.navigatePages('next'); | ||
} else if (event.key === 'ArrowLeft' && this.currentPage() > 1) { | ||
this.navigatePages('prev'); | ||
} | ||
} | ||
|
||
/** | ||
* Adjusts the canvas size based on the window resize event to ensure proper display. | ||
*/ | ||
@HostListener('window:resize') | ||
resizeCanvasBasedOnContainer() { | ||
this.adjustCanvasSize(); | ||
} | ||
|
||
/** | ||
* Dynamically updates the canvas size within an enlarged view based on the viewport. | ||
*/ | ||
adjustCanvasSize = () => { | ||
const canvasElements = this.pdfContainer().querySelectorAll('.pdf-canvas-container canvas'); | ||
if (this.currentPage() - 1 < canvasElements.length) { | ||
const canvas = canvasElements[this.currentPage() - 1] as HTMLCanvasElement; | ||
this.updateEnlargedCanvas(canvas); | ||
} | ||
}; | ||
|
||
displayEnlargedCanvas(originalCanvas: HTMLCanvasElement) { | ||
this.isEnlargedCanvasLoading.set(true); | ||
this.currentPage.set(Number(originalCanvas.id)); | ||
this.toggleBodyScroll(true); | ||
setTimeout(() => { | ||
this.updateEnlargedCanvas(originalCanvas); | ||
}, 500); | ||
} | ||
|
||
/** | ||
* Updates the enlarged canvas dimensions to optimize PDF page display within the current viewport. | ||
* This method dynamically adjusts the size, position, and scale of the canvas to maintain the aspect ratio, | ||
* ensuring the content is centered and displayed appropriately within the available space. | ||
* It is called within an animation frame to synchronize updates with the browser's render cycle for smooth visuals. | ||
* | ||
* @param originalCanvas - The source canvas element used to extract image data for resizing and redrawing. | ||
*/ | ||
updateEnlargedCanvas(originalCanvas: HTMLCanvasElement) { | ||
requestAnimationFrame(() => { | ||
const isVertical = originalCanvas.height > originalCanvas.width; | ||
this.adjustPdfContainerSize(isVertical); | ||
|
||
const scaleFactor = this.calculateScaleFactor(originalCanvas); | ||
this.resizeCanvas(originalCanvas, scaleFactor); | ||
this.redrawCanvas(originalCanvas); | ||
this.isEnlargedCanvasLoading.set(false); | ||
}); | ||
} | ||
|
||
/** | ||
* Calculates the scaling factor to adjust the canvas size based on the dimensions of the container. | ||
* This method ensures that the canvas is scaled to fit within the container without altering the aspect ratio. | ||
* | ||
* @param originalCanvas - The original canvas element representing the PDF page. | ||
* @returns The scaling factor used to resize the original canvas to fit within the container dimensions. | ||
*/ | ||
calculateScaleFactor(originalCanvas: HTMLCanvasElement): number { | ||
const containerWidth = this.pdfContainer().clientWidth; | ||
const containerHeight = this.pdfContainer().clientHeight; | ||
|
||
let scaleX, scaleY; | ||
|
||
if (originalCanvas.height > originalCanvas.width) { | ||
// Vertical slide | ||
const fixedHeight = this.DEFAULT_ENLARGED_SLIDE_HEIGHT; | ||
scaleY = fixedHeight / originalCanvas.height; | ||
scaleX = containerWidth / originalCanvas.width; | ||
} else { | ||
// Horizontal slide | ||
scaleX = containerWidth / originalCanvas.width; | ||
scaleY = containerHeight / originalCanvas.height; | ||
} | ||
|
||
return Math.min(scaleX, scaleY); | ||
} | ||
|
||
/** | ||
* Resizes the canvas according to the computed scale factor. | ||
* This method updates the dimensions of the enlarged canvas element to ensure that the entire PDF page | ||
* is visible and properly scaled within the viewer. | ||
* | ||
* @param originalCanvas - The canvas element from which the image is scaled. | ||
* @param scaleFactor - The factor by which the canvas is resized. | ||
*/ | ||
resizeCanvas(originalCanvas: HTMLCanvasElement, scaleFactor: number): void { | ||
const enlargedCanvas = this.enlargedCanvas().nativeElement; | ||
enlargedCanvas.width = originalCanvas.width * scaleFactor; | ||
enlargedCanvas.height = originalCanvas.height * scaleFactor; | ||
} | ||
|
||
/** | ||
* Redraws the original canvas content onto the enlarged canvas at the updated scale. | ||
* This method ensures that the image is rendered clearly and correctly positioned on the enlarged canvas. | ||
* | ||
* @param originalCanvas - The original canvas containing the image to be redrawn. | ||
*/ | ||
redrawCanvas(originalCanvas: HTMLCanvasElement): void { | ||
const enlargedCanvas = this.enlargedCanvas().nativeElement; | ||
const context = enlargedCanvas.getContext('2d'); | ||
context!.clearRect(0, 0, enlargedCanvas.width, enlargedCanvas.height); | ||
context!.drawImage(originalCanvas, 0, 0, enlargedCanvas.width, enlargedCanvas.height); | ||
} | ||
|
||
/** | ||
* Adjusts the size of the PDF container based on whether the enlarged view is active or not. | ||
* If the enlarged view is active, the container's size is reduced to focus on the enlarged content. | ||
* If the enlarged view is closed, the container returns to its original size. | ||
* | ||
* @param isVertical A boolean flag indicating whether to enlarge or reset the container size. | ||
*/ | ||
adjustPdfContainerSize(isVertical: boolean): void { | ||
const pdfContainer = this.pdfContainer(); | ||
if (isVertical) { | ||
pdfContainer.style.height = `${this.DEFAULT_ENLARGED_SLIDE_HEIGHT}px`; | ||
} else { | ||
pdfContainer.style.height = '60vh'; | ||
} | ||
} | ||
|
||
/** | ||
* Toggles the ability to scroll through the PDF container. | ||
* @param disable A boolean flag indicating whether scrolling should be disabled (`true`) or enabled (`false`). | ||
*/ | ||
toggleBodyScroll(disable: boolean): void { | ||
this.pdfContainer().style.overflow = disable ? 'hidden' : 'auto'; | ||
} | ||
|
||
/** | ||
* Closes the enlarged view of the PDF and re-enables scrolling in the PDF container. | ||
*/ | ||
closeEnlargedView(event: MouseEvent) { | ||
this.isEnlargedViewOutput.emit(false); | ||
this.adjustPdfContainerSize(false); | ||
this.toggleBodyScroll(false); | ||
event.stopPropagation(); | ||
} | ||
|
||
/** | ||
* Closes the enlarged view if a click event occurs outside the actual canvas area but within the enlarged container. | ||
* @param event The mouse event captured, used to determine the location of the click. | ||
*/ | ||
closeIfOutside(event: MouseEvent): void { | ||
const target = event.target as HTMLElement; | ||
const enlargedCanvas = this.enlargedCanvas().nativeElement; | ||
|
||
if (target.classList.contains('enlarged-container') && target !== enlargedCanvas) { | ||
this.closeEnlargedView(event); | ||
} | ||
} | ||
|
||
/** | ||
* Handles navigation between PDF pages and stops event propagation to prevent unwanted side effects. | ||
* @param direction The direction to navigate. | ||
* @param event The MouseEvent to be stopped. | ||
*/ | ||
handleNavigation(direction: NavigationDirection, event: MouseEvent): void { | ||
event.stopPropagation(); | ||
this.navigatePages(direction); | ||
} | ||
|
||
/** | ||
* Navigates to a specific page in the PDF based on the direction relative to the current page. | ||
* @param direction The navigation direction (next or previous). | ||
*/ | ||
navigatePages(direction: NavigationDirection) { | ||
const nextPageIndex = direction === 'next' ? this.currentPage() + 1 : this.currentPage() - 1; | ||
if (nextPageIndex > 0 && nextPageIndex <= this.totalPages()) { | ||
this.currentPage.set(nextPageIndex); | ||
const canvas = this.pdfContainer().querySelectorAll('.pdf-canvas-container canvas')[this.currentPage() - 1] as HTMLCanvasElement; | ||
this.updateEnlargedCanvas(canvas); | ||
} | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
.../lecture/pdf-preview/pdf-preview-thumbnail-grid/pdf-preview-thumbnail-grid.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<div class="pdf-container" #pdfContainer> | ||
@if (isEnlargedView()) { | ||
<jhi-pdf-preview-enlarged-canvas-component | ||
class="enlarged-canvas" | ||
[pdfContainer]="pdfContainer" | ||
[totalPages]="totalPages()" | ||
[originalCanvas]="originalCanvas()" | ||
(isEnlargedViewOutput)="isEnlargedView.set($event)" | ||
/> | ||
} | ||
</div> |
26 changes: 26 additions & 0 deletions
26
.../lecture/pdf-preview/pdf-preview-thumbnail-grid/pdf-preview-thumbnail-grid.component.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
.pdf-container { | ||
position: relative; | ||
display: grid; | ||
grid-template-columns: repeat(auto-fill, minmax(270px, 1fr)); | ||
gap: 10px; | ||
height: 60vh; | ||
overflow-y: auto; | ||
border: 1px solid var(--border-color); | ||
padding: 10px; | ||
margin: 10px 0; | ||
width: 100%; | ||
box-shadow: 0 2px 5px var(--pdf-preview-pdf-container-shadow); | ||
z-index: 0; | ||
|
||
@media (max-width: 800px) { | ||
grid-template-columns: repeat(auto-fit, minmax(270px, 1fr)); | ||
} | ||
|
||
@media (max-width: 500px) { | ||
grid-template-columns: 1fr; | ||
} | ||
} | ||
|
||
.enlarged-canvas { | ||
display: contents; | ||
} |
Oops, something went wrong.