Skip to content

Commit

Permalink
Implement Thumbnail component (#1519)
Browse files Browse the repository at this point in the history
Closes #898
  • Loading branch information
wojtekmaj committed Jun 6, 2023
1 parent f2bad6d commit 70dd977
Show file tree
Hide file tree
Showing 11 changed files with 795 additions and 11 deletions.
30 changes: 29 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,7 @@ Loads a document passed using `file` prop.
| inputRef | A prop that behaves like [ref](https://reactjs.org/docs/refs-and-the-dom.html), but it's passed to main `<div>` rendered by `<Document>` component. | n/a | <ul><li>Function:<br />`(ref) => { this.myDocument = ref; }`</li><li>Ref created using `React.createRef`:<br />`this.ref = React.createRef();`<br />…<br />`inputRef={this.ref}`</li><li>Ref created using `React.useRef`:<br />`const ref = React.useRef();`<br />…<br />`inputRef={ref}`</li></ul> |
| loading | What the component should display while loading. | `"Loading PDF…"` | <ul><li>String:<br />`"Please wait!"`</li><li>React element:<br />`<div>Please wait!</div>`</li><li>Function:<br />`this.renderLoader`</li></ul> |
| noData | What the component should display in case of no data. | `"No PDF file specified."` | <ul><li>String:<br />`"Please select a file."`</li><li>React element:<br />`<div>Please select a file.</div>`</li><li>Function:<br />`this.renderNoData`</li></ul> |
| onItemClick | Function called when an outline item has been clicked. Usually, you would like to use this callback to move the user wherever they requested to. | n/a | `({ dest, pageIndex, pageNumber }) => alert('Clicked an item from page ' + pageNumber + '!')` |
| onItemClick | Function called when an outline item or a thumbnail has been clicked. Usually, you would like to use this callback to move the user wherever they requested to. | n/a | `({ dest, pageIndex, pageNumber }) => alert('Clicked an item from page ' + pageNumber + '!')` |
| onLoadError | Function called in case of an error while loading a document. | n/a | `(error) => alert('Error while loading document! ' + error.message)` |
| onLoadProgress | Function called, potentially multiple times, as the loading progresses. | n/a | `({ loaded, total }) => alert('Loading a document: ' + (loaded / total) * 100 + '%');` |
| onLoadSuccess | Function called when the document is successfully loaded. | n/a | `(pdf) => alert('Loaded a file with ' + pdf.numPages + ' pages!')` |
Expand Down Expand Up @@ -478,6 +478,34 @@ Displays an outline (table of contents). Should be placed inside `<Document />`.
| onLoadError | Function called in case of an error while retrieving the outline. | n/a | `(error) => alert('Error while retrieving the outline! ' + error.message)` |
| onLoadSuccess | Function called when the outline is successfully retrieved. | n/a | `(outline) => alert('The outline has been successfully retrieved.')` |

### Thumbnail

Displays a thumbnail of a page. Does not render the annotation layer or the text layer. Does not register itself as a link target, so the user will not be scrolled to a Thumbnail component when clicked on an internal link (e.g. in Table of Contents). When clicked, attempts to navigate to the page clicked (similarly to a link in Outline). Should be placed inside `<Document />`. Alternatively, it can have `pdf` prop passed, which can be obtained from `<Document />`'s `onLoadSuccess` callback function.

#### Props

Props are the same as in `<Page />` component, but certain annotation layer and text layer-related props are not available:

- customTextRenderer
- onGetAnnotationsError
- onGetAnnotationsSuccess
- onGetTextError
- onGetTextSuccess
- onRenderAnnotationLayerError
- onRenderAnnotationLayerSuccess
- onRenderTextLayerError
- onRenderTextLayerSuccess
- renderAnnotationLayer
- renderForms
- renderTextLayer

On top of that, additional props are available:

| Prop name | Description | Default value | Example values |
| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| className | Class name(s) that will be added to rendered element along with the default `react-pdf__Thumbnail`. | n/a | <ul><li>String:<br />`"custom-class-name-1 custom-class-name-2"`</li><li>Array of strings:<br />`["custom-class-name-1", "custom-class-name-2"]`</li></ul> |
| onItemClick | Function called when a thumbnail has been clicked. Usually, you would like to use this callback to move the user wherever they requested to. | n/a | `({ dest, pageIndex, pageNumber }) => alert('Clicked an item from page ' + pageNumber + '!')` |

## Useful links

- [React-PDF Wiki](https://github.com/wojtekmaj/react-pdf/wiki/)
Expand Down
16 changes: 14 additions & 2 deletions src/LinkService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,20 @@ export default class LinkService implements IPDFLinkService {
this.goToDestination(dest);
}

goToPage() {
// Intentionally empty
goToPage(pageNumber: number) {
const pageIndex = pageNumber - 1;

invariant(this.pdfViewer, 'PDF viewer is not initialized.');

invariant(
pageNumber >= 1 && pageNumber <= this.pagesCount,
`"${pageNumber}" is not a valid page number.`,
);

this.pdfViewer.scrollPageIntoView({
pageIndex,
pageNumber,
});
}

addLinkAttributes(link: HTMLAnchorElement, url: string, newWindow: boolean) {
Expand Down
15 changes: 11 additions & 4 deletions src/Page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ import type {
const defaultScale = 1;

export type PageProps = {
_className?: string;
_enableRegisterUnregisterPage?: boolean;
canvasBackground?: string;
canvasRef?: React.Ref<HTMLCanvasElement>;
children?: React.ReactNode;
Expand Down Expand Up @@ -85,12 +87,14 @@ export type PageProps = {
pageIndex?: number;
pageNumber?: number;
pdf?: PDFDocumentProxy | false;
registerPage?: undefined;
renderAnnotationLayer?: boolean;
renderForms?: boolean;
renderMode?: RenderMode;
renderTextLayer?: boolean;
rotate?: number | null;
scale?: number;
unregisterPage?: undefined;
width?: number;
} & EventProps<PageCallback | false | undefined>;

Expand All @@ -104,6 +108,8 @@ export default function Page(props: PageProps) {

const mergedProps = { ...documentContext, ...props };
const {
_className = 'react-pdf__Page',
_enableRegisterUnregisterPage = true,
canvasBackground,
canvasRef,
children,
Expand Down Expand Up @@ -187,13 +193,13 @@ export default function Page(props: PageProps) {
return;
}

if (unregisterPage) {
if (_enableRegisterUnregisterPage && unregisterPage) {
unregisterPage(pageIndex);
}
};
}

useEffect(hook, [pdf, pageIndex, unregisterPage]);
useEffect(hook, [_enableRegisterUnregisterPage, pdf, pageIndex, unregisterPage]);

/**
* Called when a page is loaded successfully
Expand All @@ -208,7 +214,7 @@ export default function Page(props: PageProps) {
onLoadSuccessProps(makePageCallback(page, scale));
}

if (registerPage) {
if (_enableRegisterUnregisterPage && registerPage) {
if (!isProvided(pageIndex) || !pageElement.current) {
// Impossible, but TypeScript doesn't know that
return;
Expand Down Expand Up @@ -283,6 +289,7 @@ export default function Page(props: PageProps) {
// Technically there cannot be page without pageIndex, pageNumber, rotate and scale, but TypeScript doesn't know that
page && isProvided(pageIndex) && pageNumber && isProvided(rotate) && isProvided(scale)
? {
_className,
canvasBackground,
customTextRenderer,
devicePixelRatio,
Expand Down Expand Up @@ -383,7 +390,7 @@ export default function Page(props: PageProps) {

return (
<div
className={clsx('react-pdf__Page', className)}
className={clsx(_className, className)}
data-page-number={pageNumber}
ref={mergeRefs(inputRef, pageElement)}
style={{
Expand Down
3 changes: 2 additions & 1 deletion src/Page/PageCanvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export default function PageCanvas(props: PageCanvasProps) {

const mergedProps = { ...pageContext, ...props };
const {
_className,
canvasBackground,
devicePixelRatio: devicePixelRatioProps,
onRenderError: onRenderErrorProps,
Expand Down Expand Up @@ -164,7 +165,7 @@ export default function PageCanvas(props: PageCanvasProps) {

return (
<canvas
className="react-pdf__Page__canvas"
className={`${_className}__canvas`}
dir="ltr"
ref={mergeRefs(canvasRef, canvasElement)}
style={{
Expand Down
3 changes: 2 additions & 1 deletion src/Page/PageSVG.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export default function PageSVG() {
invariant(pageContext, 'Unable to find Page context.');

const {
_className,
onRenderSuccess: onRenderSuccessProps,
onRenderError: onRenderErrorProps,
page,
Expand Down Expand Up @@ -145,7 +146,7 @@ export default function PageSVG() {

return (
<div
className="react-pdf__Page__svg"
className={`${_className}__svg`}
// Note: This cannot be shortened, as we need this function to be called with each render.
ref={(ref) => drawPageOnContainer(ref)}
style={{
Expand Down
Loading

0 comments on commit 70dd977

Please sign in to comment.