@matangot/react-pdf-viewer 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +152 -0
- package/dist/index.d.ts +234 -0
- package/dist/index.js +2148 -0
- package/dist/index.js.map +1 -0
- package/dist/styles.css +1022 -0
- package/package.json +49 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Matan Got
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# @matangot/react-pdf-viewer
|
|
2
|
+
|
|
3
|
+
A fast, customizable React PDF viewer component powered by pdf.js. Works with Next.js, Vite, CRA, and any React environment.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Navigation (prev/next, page jump, keyboard shortcuts)
|
|
8
|
+
- Zoom (in/out, fit-width, fit-page)
|
|
9
|
+
- Search with match highlighting
|
|
10
|
+
- Download with custom filename
|
|
11
|
+
- Print
|
|
12
|
+
- Full screen
|
|
13
|
+
- Rotation
|
|
14
|
+
- Thumbnail sidebar
|
|
15
|
+
- Light/dark/system theme
|
|
16
|
+
- Compound components for full layout control
|
|
17
|
+
- CSS custom properties for easy theming
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install @matangot/react-pdf-viewer react react-dom pdfjs-dist
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Setup pdf.js worker
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { GlobalWorkerOptions } from 'pdfjs-dist';
|
|
29
|
+
|
|
30
|
+
GlobalWorkerOptions.workerSrc = new URL(
|
|
31
|
+
'pdfjs-dist/build/pdf.worker.min.mjs',
|
|
32
|
+
import.meta.url
|
|
33
|
+
).toString();
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Quick Start
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
import { PdfViewer } from '@matangot/react-pdf-viewer';
|
|
40
|
+
import '@matangot/react-pdf-viewer/styles.css';
|
|
41
|
+
|
|
42
|
+
function App() {
|
|
43
|
+
return (
|
|
44
|
+
<PdfViewer
|
|
45
|
+
src="https://example.com/document.pdf"
|
|
46
|
+
defaultPage={1}
|
|
47
|
+
defaultZoom="fit-width"
|
|
48
|
+
theme="system"
|
|
49
|
+
/>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Compound Components
|
|
55
|
+
|
|
56
|
+
For full control over layout and which actions to show:
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
import { PdfViewer } from '@matangot/react-pdf-viewer';
|
|
60
|
+
import '@matangot/react-pdf-viewer/styles.css';
|
|
61
|
+
|
|
62
|
+
function CustomViewer() {
|
|
63
|
+
return (
|
|
64
|
+
<PdfViewer.Root src={pdfUrl} theme="dark">
|
|
65
|
+
<PdfViewer.Toolbar>
|
|
66
|
+
<PdfViewer.Navigation />
|
|
67
|
+
<PdfViewer.Separator />
|
|
68
|
+
<PdfViewer.Zoom />
|
|
69
|
+
<PdfViewer.Separator />
|
|
70
|
+
<PdfViewer.Download fileName="report.pdf" />
|
|
71
|
+
<PdfViewer.Print />
|
|
72
|
+
<PdfViewer.FullScreen />
|
|
73
|
+
</PdfViewer.Toolbar>
|
|
74
|
+
<PdfViewer.Pages />
|
|
75
|
+
</PdfViewer.Root>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Programmatic Access
|
|
81
|
+
|
|
82
|
+
```tsx
|
|
83
|
+
import { usePdfViewer } from '@matangot/react-pdf-viewer';
|
|
84
|
+
|
|
85
|
+
function CustomControls() {
|
|
86
|
+
const { currentPage, totalPages, zoomIn, goToPage } = usePdfViewer();
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<div>
|
|
90
|
+
<span>Page {currentPage} of {totalPages}</span>
|
|
91
|
+
<button onClick={zoomIn}>Zoom In</button>
|
|
92
|
+
<button onClick={() => goToPage(1)}>Go to First Page</button>
|
|
93
|
+
</div>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Props
|
|
99
|
+
|
|
100
|
+
### `<PdfViewer>` / `<PdfViewer.Root>`
|
|
101
|
+
|
|
102
|
+
| Prop | Type | Default | Description |
|
|
103
|
+
|------|------|---------|-------------|
|
|
104
|
+
| `src` | `string \| File \| ArrayBuffer \| Uint8Array` | — | PDF source (required) |
|
|
105
|
+
| `defaultPage` | `number` | `1` | Initial page |
|
|
106
|
+
| `defaultZoom` | `number \| 'fit-width' \| 'fit-page'` | `1` | Initial zoom |
|
|
107
|
+
| `theme` | `'light' \| 'dark' \| 'system'` | `'system'` | Color theme |
|
|
108
|
+
| `onPageChange` | `(page: number) => void` | — | Page change callback |
|
|
109
|
+
| `onDocumentLoad` | `(info: DocumentInfo) => void` | — | Document load callback |
|
|
110
|
+
| `className` | `string` | — | Additional CSS class |
|
|
111
|
+
|
|
112
|
+
### `<PdfViewer.Download>`
|
|
113
|
+
|
|
114
|
+
| Prop | Type | Description |
|
|
115
|
+
|------|------|-------------|
|
|
116
|
+
| `fileName` | `string` | Override downloaded file name |
|
|
117
|
+
|
|
118
|
+
## Theming
|
|
119
|
+
|
|
120
|
+
Override CSS custom properties to customize the look:
|
|
121
|
+
|
|
122
|
+
```css
|
|
123
|
+
.pdf-viewer {
|
|
124
|
+
--pdf-toolbar-bg: #1a1a2e;
|
|
125
|
+
--pdf-toolbar-color: #ffffff;
|
|
126
|
+
--pdf-toolbar-height: 48px;
|
|
127
|
+
--pdf-sidebar-width: 200px;
|
|
128
|
+
--pdf-page-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
129
|
+
--pdf-page-gap: 16px;
|
|
130
|
+
--pdf-search-highlight: rgba(255, 230, 0, 0.4);
|
|
131
|
+
--pdf-font-family: system-ui, sans-serif;
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
For a completely custom look, skip importing `styles.css` and style from scratch using the BEM class names.
|
|
136
|
+
|
|
137
|
+
## Keyboard Shortcuts
|
|
138
|
+
|
|
139
|
+
| Shortcut | Action |
|
|
140
|
+
|----------|--------|
|
|
141
|
+
| `Arrow Left` / `Page Up` | Previous page |
|
|
142
|
+
| `Arrow Right` / `Page Down` | Next page |
|
|
143
|
+
| `Ctrl/Cmd + =` | Zoom in |
|
|
144
|
+
| `Ctrl/Cmd + -` | Zoom out |
|
|
145
|
+
| `Ctrl/Cmd + 0` | Reset zoom |
|
|
146
|
+
| `Ctrl/Cmd + F` | Focus search |
|
|
147
|
+
| `Enter` | Next search match |
|
|
148
|
+
| `Shift + Enter` | Previous search match |
|
|
149
|
+
|
|
150
|
+
## License
|
|
151
|
+
|
|
152
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import React, { ReactNode } from 'react';
|
|
3
|
+
import * as pdfjs_dist from 'pdfjs-dist';
|
|
4
|
+
|
|
5
|
+
declare function DocumentPropertiesModal(): react_jsx_runtime.JSX.Element | null;
|
|
6
|
+
|
|
7
|
+
interface DropdownMenuProps {
|
|
8
|
+
trigger: ReactNode;
|
|
9
|
+
children: ReactNode;
|
|
10
|
+
className?: string;
|
|
11
|
+
align?: 'left' | 'right' | 'center';
|
|
12
|
+
title?: string;
|
|
13
|
+
triggerClassName?: string;
|
|
14
|
+
}
|
|
15
|
+
declare function DropdownMenu({ trigger, children, className, align, title, triggerClassName }: DropdownMenuProps): react_jsx_runtime.JSX.Element;
|
|
16
|
+
interface DropdownMenuItemProps {
|
|
17
|
+
icon?: ReactNode;
|
|
18
|
+
label: string;
|
|
19
|
+
onClick: () => void;
|
|
20
|
+
disabled?: boolean;
|
|
21
|
+
active?: boolean;
|
|
22
|
+
}
|
|
23
|
+
declare function DropdownMenuItem({ icon, label, onClick, disabled, active }: DropdownMenuItemProps): react_jsx_runtime.JSX.Element;
|
|
24
|
+
declare function DropdownMenuSeparator(): react_jsx_runtime.JSX.Element;
|
|
25
|
+
|
|
26
|
+
interface MoreMenuProps {
|
|
27
|
+
className?: string;
|
|
28
|
+
}
|
|
29
|
+
declare function MoreMenu({ className }: MoreMenuProps): react_jsx_runtime.JSX.Element;
|
|
30
|
+
|
|
31
|
+
interface CursorModeToggleProps {
|
|
32
|
+
className?: string;
|
|
33
|
+
}
|
|
34
|
+
declare function CursorModeToggle({ className }: CursorModeToggleProps): react_jsx_runtime.JSX.Element;
|
|
35
|
+
|
|
36
|
+
interface PageProps {
|
|
37
|
+
pageNumber: number;
|
|
38
|
+
className?: string;
|
|
39
|
+
}
|
|
40
|
+
declare function Page({ pageNumber, className }: PageProps): react_jsx_runtime.JSX.Element;
|
|
41
|
+
|
|
42
|
+
interface PagesProps {
|
|
43
|
+
className?: string;
|
|
44
|
+
}
|
|
45
|
+
declare function Pages({ className }: PagesProps): react_jsx_runtime.JSX.Element;
|
|
46
|
+
|
|
47
|
+
interface ThumbnailSidebarProps {
|
|
48
|
+
className?: string;
|
|
49
|
+
}
|
|
50
|
+
declare function ThumbnailSidebar({ className }: ThumbnailSidebarProps): react_jsx_runtime.JSX.Element | null;
|
|
51
|
+
|
|
52
|
+
interface ThumbnailToggleProps {
|
|
53
|
+
className?: string;
|
|
54
|
+
}
|
|
55
|
+
declare function ThumbnailToggle({ className }: ThumbnailToggleProps): react_jsx_runtime.JSX.Element;
|
|
56
|
+
|
|
57
|
+
interface FullScreenProps {
|
|
58
|
+
className?: string;
|
|
59
|
+
}
|
|
60
|
+
declare function FullScreen({ className }: FullScreenProps): react_jsx_runtime.JSX.Element;
|
|
61
|
+
|
|
62
|
+
interface PrintProps {
|
|
63
|
+
className?: string;
|
|
64
|
+
}
|
|
65
|
+
declare function Print({ className }: PrintProps): react_jsx_runtime.JSX.Element;
|
|
66
|
+
|
|
67
|
+
interface DownloadProps {
|
|
68
|
+
fileName?: string;
|
|
69
|
+
className?: string;
|
|
70
|
+
}
|
|
71
|
+
declare function Download({ fileName, className }: DownloadProps): react_jsx_runtime.JSX.Element;
|
|
72
|
+
|
|
73
|
+
interface RotateProps {
|
|
74
|
+
className?: string;
|
|
75
|
+
}
|
|
76
|
+
declare function Rotate({ className }: RotateProps): react_jsx_runtime.JSX.Element;
|
|
77
|
+
|
|
78
|
+
interface SeparatorProps {
|
|
79
|
+
className?: string;
|
|
80
|
+
}
|
|
81
|
+
declare function Separator({ className }: SeparatorProps): react_jsx_runtime.JSX.Element;
|
|
82
|
+
|
|
83
|
+
interface SearchProps {
|
|
84
|
+
className?: string;
|
|
85
|
+
}
|
|
86
|
+
declare function Search({ className }: SearchProps): react_jsx_runtime.JSX.Element;
|
|
87
|
+
|
|
88
|
+
interface ZoomProps {
|
|
89
|
+
className?: string;
|
|
90
|
+
}
|
|
91
|
+
declare function Zoom({ className }: ZoomProps): react_jsx_runtime.JSX.Element;
|
|
92
|
+
|
|
93
|
+
interface NavigationProps {
|
|
94
|
+
className?: string;
|
|
95
|
+
}
|
|
96
|
+
declare function Navigation({ className }: NavigationProps): react_jsx_runtime.JSX.Element;
|
|
97
|
+
|
|
98
|
+
interface ToolbarProps {
|
|
99
|
+
className?: string;
|
|
100
|
+
children: ReactNode;
|
|
101
|
+
}
|
|
102
|
+
declare function Toolbar({ className, children }: ToolbarProps): react_jsx_runtime.JSX.Element;
|
|
103
|
+
|
|
104
|
+
type PdfSource = string | File | ArrayBuffer | Uint8Array;
|
|
105
|
+
type ZoomMode = 'fit-width' | 'fit-page';
|
|
106
|
+
type ZoomValue = number | ZoomMode;
|
|
107
|
+
type Theme = 'light' | 'dark' | 'system';
|
|
108
|
+
type CursorMode = 'select' | 'hand';
|
|
109
|
+
type ViewMode = 'single' | 'dual';
|
|
110
|
+
type ScrollMode = 'page' | 'vertical' | 'horizontal';
|
|
111
|
+
/** @deprecated Use ViewMode and ScrollMode instead */
|
|
112
|
+
type LayoutMode = 'single' | 'dual' | 'horizontal';
|
|
113
|
+
interface DocumentInfo {
|
|
114
|
+
numPages: number;
|
|
115
|
+
title?: string;
|
|
116
|
+
author?: string;
|
|
117
|
+
}
|
|
118
|
+
interface DocumentProperties {
|
|
119
|
+
fileName: string;
|
|
120
|
+
fileSize: string;
|
|
121
|
+
title: string;
|
|
122
|
+
author: string;
|
|
123
|
+
subject: string;
|
|
124
|
+
creator: string;
|
|
125
|
+
producer: string;
|
|
126
|
+
creationDate: string;
|
|
127
|
+
modificationDate: string;
|
|
128
|
+
pageCount: number;
|
|
129
|
+
pageSize: string;
|
|
130
|
+
}
|
|
131
|
+
interface SearchMatch {
|
|
132
|
+
pageIndex: number;
|
|
133
|
+
matchIndex: number;
|
|
134
|
+
}
|
|
135
|
+
interface PdfViewerState {
|
|
136
|
+
document: pdfjs_dist.PDFDocumentProxy | null;
|
|
137
|
+
isLoading: boolean;
|
|
138
|
+
error: Error | null;
|
|
139
|
+
currentPage: number;
|
|
140
|
+
totalPages: number;
|
|
141
|
+
zoomLevel: number;
|
|
142
|
+
zoomMode: ZoomMode | null;
|
|
143
|
+
rotation: number;
|
|
144
|
+
isThumbnailsOpen: boolean;
|
|
145
|
+
searchQuery: string;
|
|
146
|
+
searchMatches: SearchMatch[];
|
|
147
|
+
currentMatchIndex: number;
|
|
148
|
+
cursorMode: CursorMode;
|
|
149
|
+
viewMode: ViewMode;
|
|
150
|
+
scrollMode: ScrollMode;
|
|
151
|
+
layoutMode: LayoutMode;
|
|
152
|
+
isDocPropertiesOpen: boolean;
|
|
153
|
+
docProperties: DocumentProperties | null;
|
|
154
|
+
isPrinting: boolean;
|
|
155
|
+
}
|
|
156
|
+
interface PdfViewerActions {
|
|
157
|
+
goToPage: (page: number) => void;
|
|
158
|
+
nextPage: () => void;
|
|
159
|
+
prevPage: () => void;
|
|
160
|
+
zoomIn: () => void;
|
|
161
|
+
zoomOut: () => void;
|
|
162
|
+
zoomTo: (value: ZoomValue) => void;
|
|
163
|
+
/** @internal Set zoom level without clearing zoomMode (used by fit-zoom computation) */
|
|
164
|
+
_setZoomLevel: (level: number) => void;
|
|
165
|
+
/** @internal Set current page without scrolling (used by scroll tracking) */
|
|
166
|
+
_setCurrentPage: (page: number) => void;
|
|
167
|
+
rotate: (degrees?: number) => void;
|
|
168
|
+
toggleThumbnails: () => void;
|
|
169
|
+
search: (query: string) => void;
|
|
170
|
+
nextMatch: () => void;
|
|
171
|
+
prevMatch: () => void;
|
|
172
|
+
clearSearch: () => void;
|
|
173
|
+
download: (fileName?: string) => void;
|
|
174
|
+
print: () => void;
|
|
175
|
+
toggleFullScreen: () => void;
|
|
176
|
+
setCursorMode: (mode: CursorMode) => void;
|
|
177
|
+
toggleCursorMode: () => void;
|
|
178
|
+
setViewMode: (mode: ViewMode) => void;
|
|
179
|
+
setScrollMode: (mode: ScrollMode) => void;
|
|
180
|
+
setLayoutMode: (mode: LayoutMode) => void;
|
|
181
|
+
goToFirstPage: () => void;
|
|
182
|
+
goToLastPage: () => void;
|
|
183
|
+
openDocProperties: () => void;
|
|
184
|
+
closeDocProperties: () => void;
|
|
185
|
+
/** @internal Ref to the pages container element for fit-zoom calculations */
|
|
186
|
+
containerRef: React.MutableRefObject<HTMLElement | null>;
|
|
187
|
+
/** @internal Ref to scroll-to-page function set by Pages component */
|
|
188
|
+
scrollToPageRef: React.MutableRefObject<((page: number) => void) | null>;
|
|
189
|
+
}
|
|
190
|
+
type PdfViewerContextValue = PdfViewerState & PdfViewerActions;
|
|
191
|
+
interface PdfViewerRootProps {
|
|
192
|
+
src: PdfSource;
|
|
193
|
+
defaultPage?: number;
|
|
194
|
+
defaultZoom?: ZoomValue;
|
|
195
|
+
defaultCursorMode?: CursorMode;
|
|
196
|
+
defaultSidebarOpen?: boolean;
|
|
197
|
+
theme?: Theme;
|
|
198
|
+
onPageChange?: (page: number) => void;
|
|
199
|
+
onDocumentLoad?: (info: DocumentInfo) => void;
|
|
200
|
+
className?: string;
|
|
201
|
+
children: React.ReactNode;
|
|
202
|
+
}
|
|
203
|
+
interface PdfViewerProps extends Omit<PdfViewerRootProps, 'children'> {
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
declare function Root({ src, defaultPage, defaultZoom, defaultCursorMode, defaultSidebarOpen, theme, onPageChange, onDocumentLoad, className, children, }: PdfViewerRootProps): react_jsx_runtime.JSX.Element;
|
|
207
|
+
|
|
208
|
+
declare function PdfViewer(props: PdfViewerProps): react_jsx_runtime.JSX.Element;
|
|
209
|
+
declare namespace PdfViewer {
|
|
210
|
+
var Root: typeof Root;
|
|
211
|
+
var Toolbar: typeof Toolbar;
|
|
212
|
+
var Navigation: typeof Navigation;
|
|
213
|
+
var Zoom: typeof Zoom;
|
|
214
|
+
var Search: typeof Search;
|
|
215
|
+
var Separator: typeof Separator;
|
|
216
|
+
var Rotate: typeof Rotate;
|
|
217
|
+
var Download: typeof Download;
|
|
218
|
+
var Print: typeof Print;
|
|
219
|
+
var FullScreen: typeof FullScreen;
|
|
220
|
+
var ThumbnailToggle: typeof ThumbnailToggle;
|
|
221
|
+
var ThumbnailSidebar: typeof ThumbnailSidebar;
|
|
222
|
+
var Pages: typeof Pages;
|
|
223
|
+
var Page: typeof Page;
|
|
224
|
+
var CursorModeToggle: typeof CursorModeToggle;
|
|
225
|
+
var MoreMenu: typeof MoreMenu;
|
|
226
|
+
var DropdownMenu: typeof DropdownMenu;
|
|
227
|
+
var DropdownMenuItem: typeof DropdownMenuItem;
|
|
228
|
+
var DropdownMenuSeparator: typeof DropdownMenuSeparator;
|
|
229
|
+
var DocumentPropertiesModal: typeof DocumentPropertiesModal;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
declare function usePdfViewer(): PdfViewerContextValue;
|
|
233
|
+
|
|
234
|
+
export { type CursorMode, CursorModeToggle, type DocumentInfo, type DocumentProperties, DocumentPropertiesModal, Download, DropdownMenu, DropdownMenuItem, DropdownMenuSeparator, FullScreen, type LayoutMode, MoreMenu, Navigation, Page, Pages, type PdfSource, PdfViewer, type PdfViewerContextValue, type PdfViewerProps, type PdfViewerRootProps, Print, Root, Rotate, type ScrollMode, Search, Separator, type Theme, ThumbnailSidebar, ThumbnailToggle, Toolbar, type ViewMode, Zoom, type ZoomMode, type ZoomValue, usePdfViewer };
|