From 1504cb71ae0f8ec76436828e9f7e1994a7b9fa1f Mon Sep 17 00:00:00 2001 From: asrient <44570278+asrient@users.noreply.github.com> Date: Mon, 5 Sep 2022 17:05:38 +0530 Subject: [PATCH] Desktop: Added PDF viewer options (#6800) --- .eslintignore | 18 +++-- .gitignore | 18 +++-- packages/pdf-viewer/Page.tsx | 9 +-- .../{pdfSource.ts => PdfDocument.ts} | 46 +++++++++--- packages/pdf-viewer/VerticalPages.tsx | 20 +++--- .../pdf-viewer/{viewer.css => common.css} | 67 ++---------------- packages/pdf-viewer/hooks/usePdfData.ts | 19 ----- packages/pdf-viewer/hooks/usePdfDocument.ts | 19 +++++ packages/pdf-viewer/hooks/useScaledSize.ts | 15 ++-- packages/pdf-viewer/hooks/useScrollSaver.ts | 29 +++++--- packages/pdf-viewer/main.tsx | 9 ++- packages/pdf-viewer/miniViewer.css | 70 +++++++++++++++++++ packages/pdf-viewer/miniViewer.tsx | 28 +++++--- packages/pdf-viewer/pdfSource.test.ts | 21 ++++-- packages/pdf-viewer/types.ts | 11 +++ packages/pdf-viewer/ui/IconButtons.tsx | 56 +++++++++++++++ 16 files changed, 310 insertions(+), 145 deletions(-) rename packages/pdf-viewer/{pdfSource.ts => PdfDocument.ts} (59%) rename packages/pdf-viewer/{viewer.css => common.css} (52%) delete mode 100644 packages/pdf-viewer/hooks/usePdfData.ts create mode 100644 packages/pdf-viewer/hooks/usePdfDocument.ts create mode 100644 packages/pdf-viewer/miniViewer.css create mode 100644 packages/pdf-viewer/types.ts create mode 100644 packages/pdf-viewer/ui/IconButtons.tsx diff --git a/.eslintignore b/.eslintignore index 1ff95a30c..78059d16f 100644 --- a/.eslintignore +++ b/.eslintignore @@ -2001,6 +2001,9 @@ packages/lib/versionInfo.js.map packages/pdf-viewer/Page.d.ts packages/pdf-viewer/Page.js packages/pdf-viewer/Page.js.map +packages/pdf-viewer/PdfDocument.d.ts +packages/pdf-viewer/PdfDocument.js +packages/pdf-viewer/PdfDocument.js.map packages/pdf-viewer/VerticalPages.d.ts packages/pdf-viewer/VerticalPages.js packages/pdf-viewer/VerticalPages.js.map @@ -2010,9 +2013,9 @@ packages/pdf-viewer/hooks/useIsFocused.js.map packages/pdf-viewer/hooks/useIsVisible.d.ts packages/pdf-viewer/hooks/useIsVisible.js packages/pdf-viewer/hooks/useIsVisible.js.map -packages/pdf-viewer/hooks/usePdfData.d.ts -packages/pdf-viewer/hooks/usePdfData.js -packages/pdf-viewer/hooks/usePdfData.js.map +packages/pdf-viewer/hooks/usePdfDocument.d.ts +packages/pdf-viewer/hooks/usePdfDocument.js +packages/pdf-viewer/hooks/usePdfDocument.js.map packages/pdf-viewer/hooks/useScaledSize.d.ts packages/pdf-viewer/hooks/useScaledSize.js packages/pdf-viewer/hooks/useScaledSize.js.map @@ -2025,12 +2028,15 @@ packages/pdf-viewer/main.js.map packages/pdf-viewer/miniViewer.d.ts packages/pdf-viewer/miniViewer.js packages/pdf-viewer/miniViewer.js.map -packages/pdf-viewer/pdfSource.d.ts -packages/pdf-viewer/pdfSource.js -packages/pdf-viewer/pdfSource.js.map packages/pdf-viewer/pdfSource.test.d.ts packages/pdf-viewer/pdfSource.test.js packages/pdf-viewer/pdfSource.test.js.map +packages/pdf-viewer/types.d.ts +packages/pdf-viewer/types.js +packages/pdf-viewer/types.js.map +packages/pdf-viewer/ui/IconButtons.d.ts +packages/pdf-viewer/ui/IconButtons.js +packages/pdf-viewer/ui/IconButtons.js.map packages/pdf-viewer/ui/ZoomControls.d.ts packages/pdf-viewer/ui/ZoomControls.js packages/pdf-viewer/ui/ZoomControls.js.map diff --git a/.gitignore b/.gitignore index 9b10d0d4a..bf6a0a3dd 100644 --- a/.gitignore +++ b/.gitignore @@ -1989,6 +1989,9 @@ packages/lib/versionInfo.js.map packages/pdf-viewer/Page.d.ts packages/pdf-viewer/Page.js packages/pdf-viewer/Page.js.map +packages/pdf-viewer/PdfDocument.d.ts +packages/pdf-viewer/PdfDocument.js +packages/pdf-viewer/PdfDocument.js.map packages/pdf-viewer/VerticalPages.d.ts packages/pdf-viewer/VerticalPages.js packages/pdf-viewer/VerticalPages.js.map @@ -1998,9 +2001,9 @@ packages/pdf-viewer/hooks/useIsFocused.js.map packages/pdf-viewer/hooks/useIsVisible.d.ts packages/pdf-viewer/hooks/useIsVisible.js packages/pdf-viewer/hooks/useIsVisible.js.map -packages/pdf-viewer/hooks/usePdfData.d.ts -packages/pdf-viewer/hooks/usePdfData.js -packages/pdf-viewer/hooks/usePdfData.js.map +packages/pdf-viewer/hooks/usePdfDocument.d.ts +packages/pdf-viewer/hooks/usePdfDocument.js +packages/pdf-viewer/hooks/usePdfDocument.js.map packages/pdf-viewer/hooks/useScaledSize.d.ts packages/pdf-viewer/hooks/useScaledSize.js packages/pdf-viewer/hooks/useScaledSize.js.map @@ -2013,12 +2016,15 @@ packages/pdf-viewer/main.js.map packages/pdf-viewer/miniViewer.d.ts packages/pdf-viewer/miniViewer.js packages/pdf-viewer/miniViewer.js.map -packages/pdf-viewer/pdfSource.d.ts -packages/pdf-viewer/pdfSource.js -packages/pdf-viewer/pdfSource.js.map packages/pdf-viewer/pdfSource.test.d.ts packages/pdf-viewer/pdfSource.test.js packages/pdf-viewer/pdfSource.test.js.map +packages/pdf-viewer/types.d.ts +packages/pdf-viewer/types.js +packages/pdf-viewer/types.js.map +packages/pdf-viewer/ui/IconButtons.d.ts +packages/pdf-viewer/ui/IconButtons.js +packages/pdf-viewer/ui/IconButtons.js.map packages/pdf-viewer/ui/ZoomControls.d.ts packages/pdf-viewer/ui/ZoomControls.js packages/pdf-viewer/ui/ZoomControls.js.map diff --git a/packages/pdf-viewer/Page.tsx b/packages/pdf-viewer/Page.tsx index 2687c1770..441e9e03b 100644 --- a/packages/pdf-viewer/Page.tsx +++ b/packages/pdf-viewer/Page.tsx @@ -1,7 +1,8 @@ import { useEffect, useRef, useState, MutableRefObject } from 'react'; import * as React from 'react'; import useIsVisible from './hooks/useIsVisible'; -import { PdfData, ScaledSize } from './pdfSource'; +import PdfDocument from './PdfDocument'; +import { ScaledSize } from './types'; import useAsyncEffect, { AsyncEffectEvent } from '@joplin/lib/hooks/useAsyncEffect'; import styled from 'styled-components'; @@ -35,7 +36,7 @@ const PageInfo = styled.div` `; export interface PageProps { - pdf: PdfData; + pdfDocument: PdfDocument; pageNo: number; focusOnLoad: boolean; isAnchored: boolean; @@ -87,9 +88,9 @@ export default function Page(props: PageProps) { }, [page, props.scaledSize, isVisible, props.pageNo]); useAsyncEffect(async (event: AsyncEffectEvent) => { - if (page || !isVisible || !props.pdf) return; + if (page || !isVisible || !props.pdfDocument) return; try { - const _page = await props.pdf.getPage(props.pageNo); + const _page = await props.pdfDocument.getPage(props.pageNo); if (event.cancelled) return; setPage(_page); } catch (error) { diff --git a/packages/pdf-viewer/pdfSource.ts b/packages/pdf-viewer/PdfDocument.ts similarity index 59% rename from packages/pdf-viewer/pdfSource.ts rename to packages/pdf-viewer/PdfDocument.ts index 9dfb7f62e..08ca7c53a 100644 --- a/packages/pdf-viewer/pdfSource.ts +++ b/packages/pdf-viewer/PdfDocument.ts @@ -1,13 +1,7 @@ import * as pdfjsLib from 'pdfjs-dist'; +import { ScaledSize } from './types'; - -export interface ScaledSize { - height: number; - width: number; - scale: number; -} - -export class PdfData { +export default class PdfDocument { public url: string | Uint8Array; private doc: any = null; public pageCount: number = null; @@ -16,8 +10,10 @@ export class PdfData { height: number; width: number; } = null; + private document: HTMLDocument = null; - public constructor() { + public constructor(document: HTMLDocument) { + this.document = document; } public loadDoc = async (url: string | Uint8Array) => { @@ -71,4 +67,36 @@ export class PdfData { scale, }; }; + + public getActivePageNo = (scaledSize: ScaledSize, pageGap: number, scrollTop: number): number => { + const pageHeight = scaledSize.height + pageGap; + const pageNo = Math.floor(scrollTop / pageHeight) + 1; + return Math.min(pageNo, this.pageCount); + }; + + public printPdf = () => { + const frame = this.document.createElement('iframe'); + frame.style.position = 'fixed'; + frame.style.display = 'none'; + frame.style.height = '100%'; + frame.style.width = '100%'; + this.document.body.appendChild(frame); + frame.onload = () => { + frame.contentWindow.onafterprint = () => { + frame.remove(); + }; + frame.focus(); + frame.contentWindow.print(); + }; + frame.src = this.url as string; + }; + + public downloadPdf = async () => { + const url = this.url as string; + const link = this.document.createElement('a'); + link.href = url; + link.download = url; + link.click(); + link.remove(); + }; } diff --git a/packages/pdf-viewer/VerticalPages.tsx b/packages/pdf-viewer/VerticalPages.tsx index 4aa50b6e3..ff4189fde 100644 --- a/packages/pdf-viewer/VerticalPages.tsx +++ b/packages/pdf-viewer/VerticalPages.tsx @@ -1,6 +1,6 @@ import { useEffect, useRef, useState, MutableRefObject } from 'react'; import * as React from 'react'; -import { PdfData } from './pdfSource'; +import PdfDocument from './PdfDocument'; import Page from './Page'; import styled from 'styled-components'; import useScaledSize, { ScaledSizeParams } from './hooks/useScaledSize'; @@ -14,20 +14,21 @@ const PagesHolder = styled.div<{ pageGap: number }>` flex-flow: column; width: fit-content; min-width: 100%; - min-height: 100%; + min-height: fit-content; row-gap: ${(props)=> props.pageGap || 2}px; `; export interface VerticalPagesProps { - pdf: PdfData; + pdfDocument: PdfDocument; isDarkTheme: boolean; anchorPage?: number; rememberScroll?: boolean; pdfId?: string; zoom?: number; container: MutableRefObject; - pageGap?: number; + pageGap: number; showPageNumbers?: boolean; + onActivePageChange: (page: number)=> void; } export default function VerticalPages(props: VerticalPagesProps) { @@ -35,7 +36,7 @@ export default function VerticalPages(props: VerticalPagesProps) { const innerContainerEl = useRef(null); const scaledSize = useScaledSize({ - pdf: props.pdf, + pdfDocument: props.pdfDocument, pdfId: props.pdfId, containerWidth, rememberScroll: props.rememberScroll, @@ -51,6 +52,9 @@ export default function VerticalPages(props: VerticalPagesProps) { scaledSize, pdfId: props.pdfId, rememberScroll: props.rememberScroll, + pdfDocument: props.pdfDocument, + pageGap: props.pageGap, + onActivePageChange: props.onActivePageChange, } as ScrollSaver); useEffect(() => { @@ -81,13 +85,13 @@ export default function VerticalPages(props: VerticalPagesProps) { resizeTimer = null; } }; - }, [props.container, props.pdf]); + }, [props.container, props.pdfDocument]); return ( {scaledSize ? - Array.from(Array(props.pdf.pageCount).keys()).map((i: number) => { + Array.from(Array(props.pdfDocument.pageCount).keys()).map((i: number) => { // setting focusOnLoad only after scaledSize is set so that the container height is set correctly - return ; diff --git a/packages/pdf-viewer/viewer.css b/packages/pdf-viewer/common.css similarity index 52% rename from packages/pdf-viewer/viewer.css rename to packages/pdf-viewer/common.css index 4e90af0f8..cdbcbc407 100644 --- a/packages/pdf-viewer/viewer.css +++ b/packages/pdf-viewer/common.css @@ -19,7 +19,7 @@ } ::-webkit-scrollbar-thumb { - background: var(--grey); + background: rgb(112, 112, 112); border-radius: 5px; } @@ -27,7 +27,7 @@ --white: rgb(255, 255, 255); --light: rgb(219, 219, 219); --grey: rgb(128, 128, 128); - --dark: rgb(1, 0, 34); + --dark: rgb(38, 38, 38); --black: rgb(24, 24, 24); --red: #ff000d; @@ -77,68 +77,11 @@ hr { overflow: visible; } -.mini-app { - display: grid; - grid-template-rows: auto 2rem; - height: 100vh; - width: 100vw; - background-color: var(--bg); - overflow: hidden; - border-radius: 0.4rem; - border: solid thin var(--tertiary); - padding-top: 0.6rem; - padding-right: 0.25rem; - background-color: rgb(240, 241, 245); +.dark-bg{ + background-color: rgb(171, 171, 171); } -.mini-app.focused { - border: solid thin var(--grey); -} - -.mini-app.loading { - display: flex; - justify-content: center; - align-items: center; -} - -[data-theme="dark"] .mini-app { +[data-theme="dark"] .dark-bg { background-color: rgb(54, 54, 54); } -.app-bottom-bar { - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: center; - padding: 0.2rem 0.5rem; - font-size: 0.8rem; - color: var(--grey); -} - -.app-pages { - display: block; - margin: 0px auto; - overflow-x: hidden; - width: 100%; - height: 100%; - padding: 0.5rem; - padding-top: 0px; - padding-bottom: 0px; - overflow-y: hidden; - padding-right: 0.25rem; - position: relative; -} - -.app-pages.focused { - padding-right: 0px; - overflow-y: auto; - overflow-x: auto; -} - -.pdf-info { - display: flex; - flex-direction: row; - justify-content: space-around; - align-items: center; - column-gap: 0.2rem; -} \ No newline at end of file diff --git a/packages/pdf-viewer/hooks/usePdfData.ts b/packages/pdf-viewer/hooks/usePdfData.ts deleted file mode 100644 index 29d4aad18..000000000 --- a/packages/pdf-viewer/hooks/usePdfData.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { useState } from 'react'; -import useAsyncEffect, { AsyncEffectEvent } from '@joplin/lib/hooks/useAsyncEffect'; -import { PdfData } from '../pdfSource'; - - -const usePdfData = (pdfPath: string) => { - const [pdf, setPdf] = useState(null); - - useAsyncEffect(async (event: AsyncEffectEvent) => { - const pdfData = new PdfData(); - await pdfData.loadDoc(pdfPath); - if (event.cancelled) return; - setPdf(pdfData); - }, [pdfPath]); - - return pdf; -}; - -export default usePdfData; diff --git a/packages/pdf-viewer/hooks/usePdfDocument.ts b/packages/pdf-viewer/hooks/usePdfDocument.ts new file mode 100644 index 000000000..e4b0a1ff0 --- /dev/null +++ b/packages/pdf-viewer/hooks/usePdfDocument.ts @@ -0,0 +1,19 @@ +import { useState } from 'react'; +import useAsyncEffect, { AsyncEffectEvent } from '@joplin/lib/hooks/useAsyncEffect'; +import PdfDocument from '../PdfDocument'; + + +const usePdfDocument = (pdfPath: string) => { + const [pdfDocument, setPdfDocument] = useState(null); + + useAsyncEffect(async (event: AsyncEffectEvent) => { + const pdfData = new PdfDocument(document); + await pdfData.loadDoc(pdfPath); + if (event.cancelled) return; + setPdfDocument(pdfData); + }, [pdfPath]); + + return pdfDocument; +}; + +export default usePdfDocument; diff --git a/packages/pdf-viewer/hooks/useScaledSize.ts b/packages/pdf-viewer/hooks/useScaledSize.ts index 5b01afc18..65ac37cb2 100644 --- a/packages/pdf-viewer/hooks/useScaledSize.ts +++ b/packages/pdf-viewer/hooks/useScaledSize.ts @@ -1,9 +1,10 @@ import { useRef, useState, MutableRefObject } from 'react'; import useAsyncEffect, { AsyncEffectEvent } from '@joplin/lib/hooks/useAsyncEffect'; -import { ScaledSize, PdfData } from '../pdfSource'; +import PdfDocument from '../PdfDocument'; +import { ScaledSize } from '../types'; export interface ScaledSizeParams { - pdf: PdfData; + pdfDocument: PdfDocument; pdfId: string; containerWidth: number; rememberScroll: boolean; @@ -14,21 +15,21 @@ export interface ScaledSizeParams { zoom: number; } -const useScaledSize = ({ pdf, pdfId, containerWidth, rememberScroll, anchorPage, container, innerContainerEl, pageGap, zoom }: ScaledSizeParams) => { +const useScaledSize = ({ pdfDocument, pdfId, containerWidth, rememberScroll, anchorPage, container, innerContainerEl, pageGap, zoom }: ScaledSizeParams) => { const [scaledSize, setScaledSize] = useState(null); const currentScaleSize = useRef(scaledSize); useAsyncEffect(async (event: AsyncEffectEvent) => { - if (!pdf || !containerWidth) return; + if (!pdfDocument || !containerWidth) return; // console.log('scaledSize calculation triggered'); const effectiveWidth = Math.min(containerWidth - 20, 900) * (zoom || 1); - const scaledSize_ = await pdf.getScaledSize(null, effectiveWidth); + const scaledSize_ = await pdfDocument.getScaledSize(null, effectiveWidth); if (event.cancelled) return; const oldScaleSize = currentScaleSize.current; const oldScrollTop = container.current.scrollTop; - innerContainerEl.current.style.height = `${(scaledSize_.height + (pageGap || 2)) * pdf.pageCount}px`; + innerContainerEl.current.style.height = `${(scaledSize_.height + pageGap) * pdfDocument.pageCount}px`; // Adjust scroll position after window resize to keep the same page visible if (oldScaleSize && container.current) { @@ -47,7 +48,7 @@ const useScaledSize = ({ pdf, pdfId, containerWidth, rememberScroll, anchorPage, // console.log('scroll set',container.current.scrollTop); } } - }, [pdf, pdfId, rememberScroll, anchorPage, containerWidth, zoom]); + }, [pdfDocument, pdfId, rememberScroll, anchorPage, containerWidth, zoom]); return scaledSize; }; diff --git a/packages/pdf-viewer/hooks/useScrollSaver.ts b/packages/pdf-viewer/hooks/useScrollSaver.ts index c7c630747..496edb5f8 100644 --- a/packages/pdf-viewer/hooks/useScrollSaver.ts +++ b/packages/pdf-viewer/hooks/useScrollSaver.ts @@ -1,15 +1,21 @@ -import { useRef, useEffect, MutableRefObject } from 'react'; -import { ScaledSize } from '../pdfSource'; +import { useRef, useEffect, MutableRefObject, useState } from 'react'; +import PdfDocument from '../PdfDocument'; +import { ScaledSize } from '../types'; export interface ScrollSaver { container: MutableRefObject; scaledSize: ScaledSize; pdfId: string; rememberScroll: boolean; + onActivePageChange: (activePage: number)=> void; + pdfDocument: PdfDocument; + pageGap: number; } -const useScrollSaver = ({ container, scaledSize, pdfId, rememberScroll }: ScrollSaver) => { +const useScrollSaver = ({ container, scaledSize, pdfId, rememberScroll, onActivePageChange, pdfDocument, pageGap }: ScrollSaver) => { const currentScaleSize = useRef(scaledSize); + const [currentActivePage, setCurrentActivePage] = useState(1); + const currentActivePageRef = useRef(currentActivePage); useEffect(() => { let scrollTimer: number = null; @@ -18,9 +24,18 @@ const useScrollSaver = ({ container, scaledSize, pdfId, rememberScroll }: Scroll const saveScroll = () => { if (!currentScaleSize.current) return; const scale = currentScaleSize.current.scale; - const scrollTop = container.current.scrollTop / scale; + const pdfScrollTop = container.current.scrollTop / scale; if (rememberScroll && pdfId) { - sessionStorage.setItem(`pdf.${pdfId}.scrollTop`, `${scrollTop}`); + sessionStorage.setItem(`pdf.${pdfId}.scrollTop`, `${pdfScrollTop}`); + } + if (onActivePageChange && currentScaleSize.current) { + const activePage = pdfDocument.getActivePageNo(currentScaleSize.current, pageGap, container.current.scrollTop); + if (currentActivePageRef.current !== activePage) { + // console.log('Active page changed', activePage, container.current.scrollTop); + currentActivePageRef.current = activePage; + onActivePageChange(activePage); + setCurrentActivePage(activePage); + } } }; @@ -41,13 +56,11 @@ const useScrollSaver = ({ container, scaledSize, pdfId, rememberScroll }: Scroll scrollTimer = null; } }; - }, [container, pdfId, rememberScroll, currentScaleSize]); + }, [container, pdfId, rememberScroll, currentScaleSize, onActivePageChange, pdfDocument, pageGap]); useEffect(() => { currentScaleSize.current = scaledSize; } , [scaledSize]); - - return scaledSize; }; export default useScrollSaver; diff --git a/packages/pdf-viewer/main.tsx b/packages/pdf-viewer/main.tsx index 86ca08374..f97258e46 100644 --- a/packages/pdf-viewer/main.tsx +++ b/packages/pdf-viewer/main.tsx @@ -5,7 +5,7 @@ import { render } from 'react-dom'; import * as pdfjsLib from 'pdfjs-dist'; import MiniViewerApp from './miniViewer'; -require('./viewer.css'); +require('./common.css'); // Setting worker path to worker bundle. pdfjsLib.GlobalWorkerOptions.workerSrc = 'pdf.worker.js'; @@ -13,14 +13,17 @@ pdfjsLib.GlobalWorkerOptions.workerSrc = 'pdf.worker.js'; const url = window.frameElement.getAttribute('x-url'); const type = window.frameElement.getAttribute('x-type'); const appearance = window.frameElement.getAttribute('x-appearance'); -const anchorPage = window.frameElement.getAttribute('x-anchorPage'); +const anchorPage = Number(window.frameElement.getAttribute('x-anchorPage')) || null; const pdfId = window.frameElement.getAttribute('id'); document.documentElement.setAttribute('data-theme', appearance); function App() { if (type === 'mini') { - return ; + return ; } return
Error: Unknown app type "{type}"
; } diff --git a/packages/pdf-viewer/miniViewer.css b/packages/pdf-viewer/miniViewer.css new file mode 100644 index 000000000..9c7aa97c1 --- /dev/null +++ b/packages/pdf-viewer/miniViewer.css @@ -0,0 +1,70 @@ +.mini-app { + display: grid; + grid-template-rows: auto 2rem; + height: 100vh; + width: 100vw; + background-color: var(--bg); + overflow: hidden; + border-radius: 0.4rem; + border: solid thin var(--tertiary); + padding-top: 0.6rem; + padding-right: 0.25rem; +} + +.mini-app.focused { + border: solid thin var(--grey); +} + +.mini-app.loading { + display: flex; + justify-content: center; + align-items: center; +} + +.app-bottom-bar { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + padding: 0.2rem 0.5rem; + font-size: 0.8rem; + color: var(--grey); +} + +.app-pages { + display: block; + margin: 0px auto; + overflow-x: hidden; + width: 100%; + height: 100%; + padding: 0.5rem; + padding-top: 0px; + padding-bottom: 0px; + overflow-y: hidden; + padding-right: 0.25rem; + position: relative; +} + +.app-pages.focused { + padding-right: 0px; + overflow-y: auto; + overflow-x: auto; +} + +.pdf-info { + display: flex; + flex-direction: row; + justify-content: space-around; + align-items: center; + column-gap: 0.2rem; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + user-select: none; +} + +@media screen and (max-width: 380px) { + .can-hide { + display: none; + } +} \ No newline at end of file diff --git a/packages/pdf-viewer/miniViewer.tsx b/packages/pdf-viewer/miniViewer.tsx index 705c79da3..40ebbda63 100644 --- a/packages/pdf-viewer/miniViewer.tsx +++ b/packages/pdf-viewer/miniViewer.tsx @@ -1,8 +1,11 @@ -import React, { useRef, useState } from 'react'; +import React, { useRef, useState, useCallback } from 'react'; import useIsFocused from './hooks/useIsFocused'; -import usePdfData from './hooks/usePdfData'; +import usePdfDocument from './hooks/usePdfDocument'; import VerticalPages from './VerticalPages'; import ZoomControls from './ui/ZoomControls'; +import { DownloadButton, PrintButton } from './ui/IconButtons'; + +require('./miniViewer.css'); export interface MiniViewerAppProps { pdfPath: string; @@ -12,12 +15,17 @@ export interface MiniViewerAppProps { } export default function MiniViewerApp(props: MiniViewerAppProps) { - const pdf = usePdfData(props.pdfPath); + const pdfDocument = usePdfDocument(props.pdfPath); const isFocused = useIsFocused(); const [zoom, setZoom] = useState(1); const containerEl = useRef(null); + const [activePage, setActivePage] = useState(1); - if (!pdf) { + const onActivePageChange = useCallback((page: number) => { + setActivePage(page); + }, []); + + if (!pdfDocument) { return (
Loading pdf..
@@ -28,21 +36,25 @@ export default function MiniViewerApp(props: MiniViewerAppProps) {
+ zoom={zoom} + pageGap={2} + onActivePageChange={onActivePageChange} />
-
{pdf.pageCount} pages
+
{activePage}/{pdfDocument.pageCount} pages
+ +
-
{isFocused ? '' : 'Click to enable scroll'}
+
{isFocused ? '' : 'Click to enable scroll'}
); diff --git a/packages/pdf-viewer/pdfSource.test.ts b/packages/pdf-viewer/pdfSource.test.ts index 2bcd708e7..64e75f6f5 100644 --- a/packages/pdf-viewer/pdfSource.test.ts +++ b/packages/pdf-viewer/pdfSource.test.ts @@ -1,4 +1,4 @@ -import { PdfData } from './pdfSource'; +import PdfDocument from './PdfDocument'; import * as pdfjsLib from 'pdfjs-dist'; import { readFile } from 'fs'; import { resolve } from 'path'; @@ -24,13 +24,13 @@ describe('pdfData', () => { test('Should have correct page count', async () => { const file = await loadFile(pdfFilePath1); - const pdf = new PdfData(); + const pdf = new PdfDocument(document); await pdf.loadDoc(file); expect(pdf.pageCount).toBe(1); }); test('Should throw error on invalid file', async () => { - const pdf = new PdfData(); + const pdf = new PdfDocument(document); await expect(async () => { await pdf.loadDoc(''); }).rejects.toThrowError(); @@ -38,7 +38,7 @@ describe('pdfData', () => { test('Should get correct page size', async () => { const file = await loadFile(pdfFilePath1); - const pdf = new PdfData(); + const pdf = new PdfDocument(document); await pdf.loadDoc(file); const size = await pdf.getPageSize(); expect(size.height).toBeCloseTo(841.91998); @@ -47,10 +47,21 @@ describe('pdfData', () => { test('Should calculate scaled size', async () => { const file = await loadFile(pdfFilePath1); - const pdf = new PdfData(); + const pdf = new PdfDocument(document); await pdf.loadDoc(file); const scaledSize = await pdf.getScaledSize(null, 200); expect(scaledSize.scale).toBeCloseTo(0.336157); }); + test('Should get correct active page', async () => { + const file = await loadFile(pdfFilePath1); + const pdf = new PdfDocument(document); + await pdf.loadDoc(file); + const scaledSize = await pdf.getScaledSize(null, 200); + const activePage = pdf.getActivePageNo(scaledSize, 3, 0); + expect(activePage).toBe(1); + const activePage2 = pdf.getActivePageNo(scaledSize, 4, 8000); + expect(activePage2).toBe(1); + }); + }); diff --git a/packages/pdf-viewer/types.ts b/packages/pdf-viewer/types.ts new file mode 100644 index 000000000..0dfe013d5 --- /dev/null +++ b/packages/pdf-viewer/types.ts @@ -0,0 +1,11 @@ +export interface ScaledSize { + height: number; + width: number; + scale: number; +} + +export interface IconButtonProps { + onClick: ()=> void; + size?: number; + color?: string; +} diff --git a/packages/pdf-viewer/ui/IconButtons.tsx b/packages/pdf-viewer/ui/IconButtons.tsx new file mode 100644 index 000000000..a8fb58a55 --- /dev/null +++ b/packages/pdf-viewer/ui/IconButtons.tsx @@ -0,0 +1,56 @@ +import React from 'react'; +import styled from 'styled-components'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faPrint, faDownload, IconDefinition } from '@fortawesome/free-solid-svg-icons'; +import { IconButtonProps } from '../types'; + + +const ButtonElement = styled.button<{ hoverColor?: string; size?: number; color?: string }>` + padding: 0.2rem 0.7rem; + cursor: pointer; + border: solid thin transparent; + border-radius: 5px; + user-select: none; + display: flex; + justify-content: center; + align-items: center; + font-size: ${props=> props.size || 1}rem; + background: transparent; + color: var(--${props=>props.color || 'grey'}); + &:hover { + background: #7676764d; + ${props=>props.hoverColor && `color: var(--${props.hoverColor})`} + } +`; + +interface BaseButtonProps { + icon: IconDefinition; + onClick: ()=> void; + name: string; + size: number; + color: string; + hoverColor?: string; +} + +function BaseButton({ onClick, icon, name, size, color, hoverColor }: BaseButtonProps) { + return ( + + + + ); +} + +export function DownloadButton({ onClick, size, color }: IconButtonProps) { + return ( + + ); +} + +export function PrintButton({ onClick, size, color }: IconButtonProps) { + return ( + + ); +}