@fdls/file-viewer 0.2.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 +524 -0
- package/dist/index.css +30 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.ts +309 -0
- package/dist/index.js +3351 -0
- package/dist/index.js.map +1 -0
- package/dist/style.css +2 -0
- package/package.json +100 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 file-viewer contributors
|
|
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,524 @@
|
|
|
1
|
+
[](https://www.npmjs.com/package/file-viewer)
|
|
2
|
+
[](https://opensource.org/licenses/MIT)
|
|
3
|
+
|
|
4
|
+
# file-viewer
|
|
5
|
+
|
|
6
|
+
> React components for in-app file preview: a shell (`FileViewer`), PDF viewer (`react-pdf`), and image viewer with pan/zoom.
|
|
7
|
+
|
|
8
|
+
## Prerequisites
|
|
9
|
+
|
|
10
|
+
This library targets **React 18+** and **Node.js 18+** (recommended for local development).
|
|
11
|
+
|
|
12
|
+
Peer dependencies must be installed in your app (see [Installation](#installation)).
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
node -v && npm -v
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Table of contents
|
|
19
|
+
|
|
20
|
+
- [file-viewer](#file-viewer)
|
|
21
|
+
- [Prerequisites](#prerequisites)
|
|
22
|
+
- [Table of contents](#table-of-contents)
|
|
23
|
+
- [Getting Started](#getting-started)
|
|
24
|
+
- [Installation](#installation)
|
|
25
|
+
- [Usage](#usage)
|
|
26
|
+
- [Quick start](#quick-start)
|
|
27
|
+
- [Global defaults](#global-defaults)
|
|
28
|
+
- [Inline vs modal](#inline-vs-modal)
|
|
29
|
+
- [Styling (Tailwind & CSS)](#styling-tailwind--css)
|
|
30
|
+
- [PDF.js worker (required for PDF)](#pdfjs-worker-required-for-pdf)
|
|
31
|
+
- [React-PDF layer CSS](#react-pdf-layer-css)
|
|
32
|
+
- [Peer dependencies](#peer-dependencies)
|
|
33
|
+
- [Migration from 0.1.x](#migration-from-01x)
|
|
34
|
+
- [Local development (this repo)](#local-development-this-repo)
|
|
35
|
+
- [Serving the playground](#serving-the-playground)
|
|
36
|
+
- [Building the library](#building-the-library)
|
|
37
|
+
- [Building the playground](#building-the-playground)
|
|
38
|
+
- [Publishing to npm (maintainers)](#publishing-to-npm-maintainers)
|
|
39
|
+
- [API](#api)
|
|
40
|
+
- [FileViewer](#fileviewer)
|
|
41
|
+
- [setFileViewerDefaults](#setfileviewerdefaults)
|
|
42
|
+
- [PdfViewer](#pdfviewer)
|
|
43
|
+
- [ImageViewer](#imageviewer)
|
|
44
|
+
- [Translations](#translations)
|
|
45
|
+
- [Customization slots](#customization-slots)
|
|
46
|
+
- [Contributing](#contributing)
|
|
47
|
+
- [Built with](#built-with)
|
|
48
|
+
- [Versioning](#versioning)
|
|
49
|
+
- [License](#license)
|
|
50
|
+
|
|
51
|
+
## Getting Started
|
|
52
|
+
|
|
53
|
+
`file-viewer` is meant to be consumed as an npm package. Clone this repository only if you want to develop or run the playground.
|
|
54
|
+
|
|
55
|
+
```sh
|
|
56
|
+
git clone https://github.com/fidelesdev/file-viewer.git
|
|
57
|
+
cd file-viewer
|
|
58
|
+
npm install
|
|
59
|
+
npm run dev
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Installation
|
|
63
|
+
|
|
64
|
+
Your app must already use **React 18+**. Then install only the library — **npm 7+**, **Yarn 2+**, and **pnpm** install [peer dependencies](https://nodejs.org/en/blog/npm/peer-dependencies) from `file-viewer` automatically (no separate `npm install react-pdf …` in your project).
|
|
65
|
+
|
|
66
|
+
```sh
|
|
67
|
+
npm install file-viewer
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
```sh
|
|
71
|
+
yarn add file-viewer
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
```sh
|
|
75
|
+
pnpm add file-viewer
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Peer dependencies (declared in `file-viewer`)
|
|
79
|
+
|
|
80
|
+
These are listed in this package’s `package.json`; the installer resolves them for you. You do not add them manually unless you use **npm 6** or disable peer auto-install.
|
|
81
|
+
|
|
82
|
+
| Package | Version | Purpose |
|
|
83
|
+
| ------- | ------- | ------- |
|
|
84
|
+
| `react`, `react-dom` | 18 or 19 | UI runtime (from your app) |
|
|
85
|
+
| `react-pdf` | 9.x | PDF rendering |
|
|
86
|
+
| `pdfjs-dist` | **~4.8.69** (same as react-pdf 9) | PDF.js engine — do not use 4.9+ / 4.10+ |
|
|
87
|
+
| `react-to-print` | 3.x | Print in `FileViewer` |
|
|
88
|
+
| `react-zoom-pan-pinch` | 4.x | Image pan/zoom |
|
|
89
|
+
|
|
90
|
+
Dialog, scroll area, tooltips, and toolbar icons are **bundled inside** `file-viewer` (no `@radix-ui/*` or `lucide-react`).
|
|
91
|
+
|
|
92
|
+
### Migration from 0.1.x
|
|
93
|
+
|
|
94
|
+
If you installed `0.1.x`, remove these peers from your app (they are no longer required):
|
|
95
|
+
|
|
96
|
+
- `@radix-ui/react-dialog`
|
|
97
|
+
- `@radix-ui/react-scroll-area`
|
|
98
|
+
- `@radix-ui/react-tooltip`
|
|
99
|
+
- `lucide-react`
|
|
100
|
+
|
|
101
|
+
Then install `file-viewer@^0.2.0` only; peers are declared on the package.
|
|
102
|
+
|
|
103
|
+
## Usage
|
|
104
|
+
|
|
105
|
+
### Quick start
|
|
106
|
+
|
|
107
|
+
Styles load automatically when you import from `file-viewer`. **PDF preview** requires a one-time worker setup in your app entry (see [PDF.js worker](#pdfjs-worker-required-for-pdf)).
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
// main.tsx or app entry — before rendering PDFs
|
|
111
|
+
import {
|
|
112
|
+
configureFileViewerPdfWorker,
|
|
113
|
+
getFileViewerPdfWorkerSrc,
|
|
114
|
+
} from 'file-viewer'
|
|
115
|
+
|
|
116
|
+
configureFileViewerPdfWorker({
|
|
117
|
+
workerSrc: getFileViewerPdfWorkerSrc(),
|
|
118
|
+
})
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
```tsx
|
|
122
|
+
import { FileViewer } from 'file-viewer'
|
|
123
|
+
|
|
124
|
+
export function DocumentPreview() {
|
|
125
|
+
const [open, setOpen] = useState(true)
|
|
126
|
+
|
|
127
|
+
return (
|
|
128
|
+
<FileViewer
|
|
129
|
+
open={open}
|
|
130
|
+
onOpenChange={setOpen}
|
|
131
|
+
name="report.pdf"
|
|
132
|
+
extension="pdf"
|
|
133
|
+
url="/files/report.pdf"
|
|
134
|
+
/>
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Supported preview extensions in `FileViewer`: **pdf**, **jpg**, **jpeg**, **png**. Other types show a fallback message (or `renderUnsupported`).
|
|
140
|
+
|
|
141
|
+
### Global defaults
|
|
142
|
+
|
|
143
|
+
Configure once at app startup; each call merges incrementally.
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
import { setFileViewerDefaults } from 'file-viewer'
|
|
147
|
+
|
|
148
|
+
setFileViewerDefaults({
|
|
149
|
+
language: 'portuguese',
|
|
150
|
+
fileViewer: { mode: 'inline' },
|
|
151
|
+
pdfViewer: { viewMode: 'continuous', zoomDebounceDelay: 500 },
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
setFileViewerDefaults({
|
|
155
|
+
translations: {
|
|
156
|
+
portuguese: { fileViewer: { downloadTooltip: 'Baixar arquivo' } },
|
|
157
|
+
},
|
|
158
|
+
})
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
| API | Description |
|
|
163
|
+
| -------------------------------- | ----------------------------------- |
|
|
164
|
+
| `setFileViewerDefaults(partial)` | Merge into global defaults |
|
|
165
|
+
| `getFileViewerDefaults()` | Read current defaults (debug/tests) |
|
|
166
|
+
| `resetFileViewerDefaults()` | Reset to library built-ins |
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
**Merge priority:** instance props → `setFileViewerDefaults` → built-in defaults.
|
|
170
|
+
|
|
171
|
+
**Never global** (instance only): `open`, `url`, `name`, `extension`, and callbacks such as `onOpenChange`, `onDownload`.
|
|
172
|
+
|
|
173
|
+
### Inline vs modal
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
| `mode` | Behavior |
|
|
177
|
+
| ------------------ | ---------------------------------------------------------- |
|
|
178
|
+
| `inline` (default) | Fills the parent container; close button hidden by default |
|
|
179
|
+
| `modal` | Full-screen dialog overlay with backdrop |
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
In **inline** mode, a header action can open the same file in a built-in full-screen modal (“Visualizar em tela cheia”), unless you pass `onOpenInModal` for custom behavior.
|
|
183
|
+
|
|
184
|
+
### Styling (automatic)
|
|
185
|
+
|
|
186
|
+
The published build runs **Tailwind v4** over this library and ships the result as `dist/style.css`. Importing any export from `file-viewer` pulls that CSS in (Vite, Webpack, Next with `transpilePackages`).
|
|
187
|
+
|
|
188
|
+
**Optional manual import** (same file, if your bundler does not follow CSS from `node_modules`):
|
|
189
|
+
|
|
190
|
+
```ts
|
|
191
|
+
import 'file-viewer/style.css'
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
**Option — Your app already uses Tailwind v4:** you may scan the package instead of relying on the pre-built CSS:
|
|
195
|
+
|
|
196
|
+
```css
|
|
197
|
+
@import "tailwindcss";
|
|
198
|
+
@source "./node_modules/file-viewer/dist";
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
**Customization without Tailwind:** use `styles` (`React.CSSProperties`) on `FileViewer`, `PdfViewer`, `ImageViewer`, or via `setFileViewerDefaults`.
|
|
202
|
+
|
|
203
|
+
**Dynamic `classNames` from props** (e.g. `classNames={{ header: 'text-[#235685]' }}`): Tailwind only emits utilities it finds during the build scan. Classes passed as **runtime strings** are not visible to the compiler unless they also appear as **static** text in files covered by `@source` (your app source), or you add them to a **safelist** / theme. Prefer `styles` for one-off colors, or define fixed utility classes in your own CSS.
|
|
204
|
+
|
|
205
|
+
### PDF.js worker (required for PDF)
|
|
206
|
+
|
|
207
|
+
`file-viewer` does **not** set the PDF.js worker path inside the published bundle (that would point to a non-existent file under `node_modules/file-viewer/dist/`). Call **`configureFileViewerPdfWorker`** once in your app entry, **before** any `FileViewer` / `PdfViewer` renders a PDF.
|
|
208
|
+
|
|
209
|
+
#### Recommended (matches react-pdf API version)
|
|
210
|
+
|
|
211
|
+
Uses `pdfjs.version` from react-pdf (e.g. `4.8.69`), so API and worker stay in sync:
|
|
212
|
+
|
|
213
|
+
```ts
|
|
214
|
+
// src/main.tsx
|
|
215
|
+
import {
|
|
216
|
+
configureFileViewerPdfWorker,
|
|
217
|
+
getFileViewerPdfWorkerSrc,
|
|
218
|
+
} from 'file-viewer'
|
|
219
|
+
|
|
220
|
+
configureFileViewerPdfWorker({
|
|
221
|
+
workerSrc: getFileViewerPdfWorkerSrc(),
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
// or simply (same default):
|
|
225
|
+
// configureFileViewerPdfWorker()
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Requires network (unpkg). Fine for dev; for production you can self-host the same file or pin a local copy.
|
|
229
|
+
|
|
230
|
+
#### Version mismatch (`4.8.69` vs `4.10.x`)
|
|
231
|
+
|
|
232
|
+
If you see *The API version does not match the Worker version*, your app resolved a **newer** `pdfjs-dist` worker (often `4.10.x`) while react-pdf 9 uses **4.8.69** for the API.
|
|
233
|
+
|
|
234
|
+
Fix:
|
|
235
|
+
|
|
236
|
+
```sh
|
|
237
|
+
npm install pdfjs-dist@4.8.69
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Or use `getFileViewerPdfWorkerSrc()` instead of `new URL('pdfjs-dist/...', import.meta.url)`.
|
|
241
|
+
|
|
242
|
+
Optional `package.json` override:
|
|
243
|
+
|
|
244
|
+
```json
|
|
245
|
+
{
|
|
246
|
+
"overrides": {
|
|
247
|
+
"pdfjs-dist": "4.8.69"
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
#### Vite — local worker (offline)
|
|
253
|
+
|
|
254
|
+
Only after pinning `pdfjs-dist@4.8.69`:
|
|
255
|
+
|
|
256
|
+
```ts
|
|
257
|
+
import { configureFileViewerPdfWorker } from 'file-viewer'
|
|
258
|
+
|
|
259
|
+
configureFileViewerPdfWorker({
|
|
260
|
+
workerSrc: new URL(
|
|
261
|
+
'pdfjs-dist/build/pdf.worker.min.mjs',
|
|
262
|
+
import.meta.url,
|
|
263
|
+
).href,
|
|
264
|
+
})
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Or with `?url`:
|
|
268
|
+
|
|
269
|
+
```ts
|
|
270
|
+
import { configureFileViewerPdfWorker } from 'file-viewer'
|
|
271
|
+
import pdfWorker from 'pdfjs-dist/build/pdf.worker.min.mjs?url'
|
|
272
|
+
|
|
273
|
+
configureFileViewerPdfWorker({ workerSrc: pdfWorker })
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
#### Next.js (App Router)
|
|
277
|
+
|
|
278
|
+
In a Client Component loaded early (e.g. `providers.tsx` with `'use client'`):
|
|
279
|
+
|
|
280
|
+
```tsx
|
|
281
|
+
'use client'
|
|
282
|
+
|
|
283
|
+
import { configureFileViewerPdfWorker } from 'file-viewer'
|
|
284
|
+
|
|
285
|
+
import {
|
|
286
|
+
configureFileViewerPdfWorker,
|
|
287
|
+
getFileViewerPdfWorkerSrc,
|
|
288
|
+
} from 'file-viewer'
|
|
289
|
+
|
|
290
|
+
configureFileViewerPdfWorker({
|
|
291
|
+
workerSrc: getFileViewerPdfWorkerSrc(),
|
|
292
|
+
})
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Ensure `pdfjs-dist@4.8.69` is installed and `transpilePackages: ['file-viewer']` is set in `next.config.ts`.
|
|
296
|
+
|
|
297
|
+
#### API
|
|
298
|
+
|
|
299
|
+
| Export | Description |
|
|
300
|
+
| ------ | ----------- |
|
|
301
|
+
| `configureFileViewerPdfWorker({ workerSrc? })` | Sets `pdfjs.GlobalWorkerOptions.workerSrc` |
|
|
302
|
+
| `getFileViewerPdfWorkerSrc()` | CDN URL locked to `pdfjs.version` (recommended) |
|
|
303
|
+
| `getFileViewerPdfWorkerCdnUrl()` | Alias of `getFileViewerPdfWorkerSrc` |
|
|
304
|
+
| `isFileViewerPdfWorkerConfigured()` | `true` after configure was called |
|
|
305
|
+
|
|
306
|
+
See [pdf.js getting started](https://mozilla.github.io/pdf.js/getting_started/) for more context.
|
|
307
|
+
|
|
308
|
+
### React-PDF layer CSS
|
|
309
|
+
|
|
310
|
+
Included in the bundled `style.css` (react-pdf 9 text/annotation layers). No extra imports.
|
|
311
|
+
|
|
312
|
+
### Vite
|
|
313
|
+
|
|
314
|
+
1. Call `configureFileViewerPdfWorker` in `main.tsx` (see [PDF.js worker](#pdfjs-worker-required-for-pdf)).
|
|
315
|
+
2. Use `FileViewer` in your components:
|
|
316
|
+
|
|
317
|
+
```tsx
|
|
318
|
+
import { FileViewer } from 'file-viewer'
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### Next.js (App Router)
|
|
322
|
+
|
|
323
|
+
Add to `next.config.ts`:
|
|
324
|
+
|
|
325
|
+
```ts
|
|
326
|
+
import type { NextConfig } from 'next'
|
|
327
|
+
|
|
328
|
+
const nextConfig: NextConfig = {
|
|
329
|
+
transpilePackages: ['file-viewer'],
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
export default nextConfig
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
Call `configureFileViewerPdfWorker` in a Client Component loaded at startup, then use `FileViewer`. Shell components include `'use client'`.
|
|
336
|
+
|
|
337
|
+
## Local development (this repo)
|
|
338
|
+
|
|
339
|
+
### Serving the playground
|
|
340
|
+
|
|
341
|
+
```sh
|
|
342
|
+
npm run dev
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
Opens the Vite demo (`src/app/App.tsx`) with inline `FileViewer` and sample PDF/image scenarios.
|
|
346
|
+
|
|
347
|
+
### Building the library
|
|
348
|
+
|
|
349
|
+
```sh
|
|
350
|
+
npm run build:lib
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
Outputs ESM + types to `dist/` (`index.js`, `index.d.ts`, `style.css`).
|
|
354
|
+
|
|
355
|
+
Typecheck:
|
|
356
|
+
|
|
357
|
+
```sh
|
|
358
|
+
npx tsc --noEmit
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### Building the playground
|
|
362
|
+
|
|
363
|
+
```sh
|
|
364
|
+
npm run build
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
Production build of the demo into `playground-dist/` (does not replace library `dist/` from `build:lib`).
|
|
368
|
+
|
|
369
|
+
Preview:
|
|
370
|
+
|
|
371
|
+
```sh
|
|
372
|
+
npm run preview
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
## API
|
|
376
|
+
|
|
377
|
+
### FileViewer
|
|
378
|
+
|
|
379
|
+
Shell with header (title, print, download, optional full-screen), and lazy-loaded PDF or image viewer.
|
|
380
|
+
|
|
381
|
+
```tsx
|
|
382
|
+
<FileViewer
|
|
383
|
+
mode="inline"
|
|
384
|
+
open={open}
|
|
385
|
+
onOpenChange={setOpen}
|
|
386
|
+
name="document.pdf"
|
|
387
|
+
extension="pdf"
|
|
388
|
+
url="https://example.com/doc.pdf"
|
|
389
|
+
language="portuguese"
|
|
390
|
+
hideCloseButton
|
|
391
|
+
showOpenInModalButton
|
|
392
|
+
onOpenInModal={() => { /* optional custom handler */ }}
|
|
393
|
+
pdfViewerProps={{ viewMode: 'single' }}
|
|
394
|
+
classNames={{ header: 'my-header' }}
|
|
395
|
+
styles={{ header: { color: '#fff' } }}
|
|
396
|
+
dialogClassNames={{ panel: 'rounded-lg' }}
|
|
397
|
+
/>
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
| Prop | Type | Default | Description |
|
|
402
|
+
| ----------------------------------- | ------------------------------------------ | ---------------------------------- | --------------------------------------------- |
|
|
403
|
+
| `mode` | `'inline' | 'modal'` | `'inline'` | Layout mode |
|
|
404
|
+
| `open` | `boolean` | — | Controlled visibility |
|
|
405
|
+
| `onOpenChange` | `(open: boolean) => void` | — | Open state callback |
|
|
406
|
+
| `name` | `string` | — | Display name |
|
|
407
|
+
| `extension` | `string` | — | File extension (drives viewer choice) |
|
|
408
|
+
| `url` | `string` | — | File URL |
|
|
409
|
+
| `isLoading` | `boolean` | — | Shows loader in viewer area |
|
|
410
|
+
| `language` | `'english' | 'portuguese'` | `'english'` | UI strings |
|
|
411
|
+
| `hideCloseButton` | `boolean` | `true` in inline, `false` in modal | Hide header close control |
|
|
412
|
+
| `showOpenInModalButton` | `boolean` | `true` | Full-screen button (inline only) |
|
|
413
|
+
| `onOpenInModal` | `() => void` | — | Override built-in modal preview |
|
|
414
|
+
| `onDownload` | `() => void` | — | Custom download; default uses `url` |
|
|
415
|
+
| `pdfViewerProps` | `Omit<PdfViewerProps, 'url' | 'language'>` | — | Passed to embedded PDF viewer |
|
|
416
|
+
| `renderUnsupported` | `ReactNode` | — | Custom unsupported-type UI |
|
|
417
|
+
| `classNames` / `styles` | slot maps | — | Per-slot styling |
|
|
418
|
+
| `dialogClassNames` / `dialogStyles` | layer maps | — | Modal layers (`backdrop`, `content`, `panel`) |
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
There is **no** `hideHeader` prop yet; use `classNames.header` with `hidden` or `styles.header` as a workaround.
|
|
422
|
+
|
|
423
|
+
### setFileViewerDefaults
|
|
424
|
+
|
|
425
|
+
```ts
|
|
426
|
+
setFileViewerDefaults({
|
|
427
|
+
language: 'portuguese',
|
|
428
|
+
fileViewer: {
|
|
429
|
+
hideCloseButton: true,
|
|
430
|
+
showOpenInModalButton: true,
|
|
431
|
+
classNames: { header: 'app-viewer-header' },
|
|
432
|
+
pdfViewerProps: { viewMode: 'continuous' },
|
|
433
|
+
},
|
|
434
|
+
pdfViewer: { preloadAhead: 1, zoomDebounceDelay: 500 },
|
|
435
|
+
imageViewer: { classNames: { root: 'image-root' } },
|
|
436
|
+
toolbar: { classNames: { toolbar: 'my-toolbar' } },
|
|
437
|
+
tooltip: { delayDuration: 300 },
|
|
438
|
+
autoHide: { proximityThreshold: 160, timeout: 3000 },
|
|
439
|
+
translations: { /* deep partial per language */ },
|
|
440
|
+
})
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
### PdfViewer
|
|
444
|
+
|
|
445
|
+
Standalone PDF viewer (continuous scroll or single page, zoom, pagination toolbar).
|
|
446
|
+
|
|
447
|
+
```tsx
|
|
448
|
+
import { PdfViewer } from 'file-viewer'
|
|
449
|
+
|
|
450
|
+
<PdfViewer
|
|
451
|
+
url="/doc.pdf"
|
|
452
|
+
viewMode="continuous"
|
|
453
|
+
language="english"
|
|
454
|
+
preloadAhead={1}
|
|
455
|
+
zoomDebounceDelay={500}
|
|
456
|
+
debounceDelay={300}
|
|
457
|
+
classNames={{ page: 'my-page' }}
|
|
458
|
+
/>
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
| Prop | Type | Default | Description |
|
|
463
|
+
| ----------------------------------------------------- | ------------------------- | -------------- | ------------------------------------------------ |
|
|
464
|
+
| `url` | `string` | — | PDF URL |
|
|
465
|
+
| `viewMode` | `'single' | 'continuous'` | `'continuous'` | Page layout |
|
|
466
|
+
| `debounceDelay` | `number` | `300` | Resize debounce (ms) |
|
|
467
|
+
| `zoomDebounceDelay` | `number` | `500` | Zoom canvas re-render debounce (ms) |
|
|
468
|
+
| `preloadAhead` | `number` | `1` | Pages mounted outside viewport (continuous) |
|
|
469
|
+
| `renderPagination` | `function | null` | default UI | Custom pagination; `null` hides |
|
|
470
|
+
| `className` / `pageClassName` / `paginationClassName` | `string` | — | Legacy aliases → `classNames` |
|
|
471
|
+
| `classNames` / `styles` | slot maps | — | `root`, `scrollArea`, `page`, `pagination`, etc. |
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
Page width in both modes is capped at **50rem** (same sizing rules).
|
|
475
|
+
|
|
476
|
+
### ImageViewer
|
|
477
|
+
|
|
478
|
+
```tsx
|
|
479
|
+
import { ImageViewer } from 'file-viewer'
|
|
480
|
+
|
|
481
|
+
<ImageViewer url="/photo.jpg" name="photo.jpg" language="portuguese" />
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
Pan/zoom via `react-zoom-pan-pinch`, floating toolbar with auto-hide.
|
|
485
|
+
|
|
486
|
+
### Translations
|
|
487
|
+
|
|
488
|
+
- Bundled: `english`, `portuguese`
|
|
489
|
+
- `getFileViewerTranslations(language)` — applies global overlay from `setFileViewerDefaults({ translations })`
|
|
490
|
+
- `resolveFormattedMessage` — for `FormattableMessage` entries
|
|
491
|
+
- Exports: `fileViewerTranslationsByLanguage`, `defaultFileViewerTranslations`
|
|
492
|
+
|
|
493
|
+
### Customization slots
|
|
494
|
+
|
|
495
|
+
Shared types: `FileViewerClassNames`, `PdfViewerClassNames`, `ImageViewerClassNames`, `ViewerToolbarClassNames`, `FileViewerTooltipClassNames`, and matching `*Styles` (`SlotStyle` = `CSSProperties`).
|
|
496
|
+
|
|
497
|
+
See `src/features/file-viewer/customization-types.ts` for all keys.
|
|
498
|
+
|
|
499
|
+
## Contributing
|
|
500
|
+
|
|
501
|
+
1. Fork the repository
|
|
502
|
+
2. Create a feature branch: `git checkout -b my-feature`
|
|
503
|
+
3. Commit changes: `git commit -am 'Add feature'`
|
|
504
|
+
4. Push: `git push origin my-feature`
|
|
505
|
+
5. Open a pull request
|
|
506
|
+
|
|
507
|
+
Run `npx tsc --noEmit` and `npm run build:lib` before submitting.
|
|
508
|
+
|
|
509
|
+
## Built with
|
|
510
|
+
|
|
511
|
+
- [React](https://react.dev/)
|
|
512
|
+
- [react-pdf](https://github.com/wojtekmaj/react-pdf) / [pdf.js](https://mozilla.github.io/pdf.js/)
|
|
513
|
+
- [react-zoom-pan-pinch](https://github.com/BetterTyped/react-zoom-pan-pinch)
|
|
514
|
+
- Internal UI primitives (dialog, scroll area, tooltip) and inline SVG icons — no Radix UI or Lucide at runtime
|
|
515
|
+
- [Tailwind CSS](https://tailwindcss.com/) v4 (utilities in components; optional pre-built `file-viewer/style.css`)
|
|
516
|
+
- [Vite](https://vitejs.dev/) + [tsup](https://tsup.egoist.dev/) (playground + library build)
|
|
517
|
+
|
|
518
|
+
## Versioning
|
|
519
|
+
|
|
520
|
+
This project uses [SemVer](https://semver.org/). See `package.json` for the current version.
|
|
521
|
+
|
|
522
|
+
## License
|
|
523
|
+
|
|
524
|
+
[MIT](LICENSE) © file-viewer contributors
|
package/dist/index.css
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/* src/features/file-viewer/pdf-viewer.css */
|
|
2
|
+
.pdf-viewer .textLayer ::-moz-selection {
|
|
3
|
+
background: rgba(0, 0, 255, 0.25);
|
|
4
|
+
background: color-mix(in srgb, AccentColor, transparent 75%);
|
|
5
|
+
color: transparent;
|
|
6
|
+
}
|
|
7
|
+
.pdf-viewer .textLayer ::selection {
|
|
8
|
+
background: rgba(0, 0, 255, 0.25);
|
|
9
|
+
background: color-mix(in srgb, AccentColor, transparent 75%);
|
|
10
|
+
color: transparent;
|
|
11
|
+
-webkit-text-fill-color: transparent;
|
|
12
|
+
}
|
|
13
|
+
.pdf-viewer .textLayer br::-moz-selection,
|
|
14
|
+
.pdf-viewer .textLayer br::selection {
|
|
15
|
+
background: transparent;
|
|
16
|
+
}
|
|
17
|
+
.pdf-viewer [data-layout-syncing=true] .textLayer {
|
|
18
|
+
opacity: 0;
|
|
19
|
+
pointer-events: none;
|
|
20
|
+
}
|
|
21
|
+
.pdf-viewer [data-layout-syncing=true] canvas {
|
|
22
|
+
object-fit: contain;
|
|
23
|
+
}
|
|
24
|
+
.pdf-viewer .pdf-scroll-viewport {
|
|
25
|
+
scrollbar-width: none;
|
|
26
|
+
}
|
|
27
|
+
.pdf-viewer .pdf-scroll-viewport::-webkit-scrollbar {
|
|
28
|
+
display: none;
|
|
29
|
+
}
|
|
30
|
+
/*# sourceMappingURL=index.css.map */
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/features/file-viewer/pdf-viewer.css"],"sourcesContent":["/**\n * PDF viewer overrides (selection + layout sync).\n * Requires react-pdf TextLayer.css to be loaded first.\n */\n\n/* Mozilla-style selection: highlight only, no visible glyph fill */\n.pdf-viewer .textLayer ::-moz-selection {\n background: rgba(0, 0, 255, 0.25);\n background: color-mix(in srgb, AccentColor, transparent 75%);\n color: transparent;\n}\n\n.pdf-viewer .textLayer ::selection {\n background: rgba(0, 0, 255, 0.25);\n background: color-mix(in srgb, AccentColor, transparent 75%);\n color: transparent;\n -webkit-text-fill-color: transparent;\n}\n\n.pdf-viewer .textLayer br::-moz-selection,\n.pdf-viewer .textLayer br::selection {\n background: transparent;\n}\n\n/* Hide misaligned text layer while canvas size catches up (canvas stays visible, scaled via layout) */\n.pdf-viewer [data-layout-syncing='true'] .textLayer {\n opacity: 0;\n pointer-events: none;\n}\n\n/* Keep canvas visible and proportionally scaled while layout catches up */\n.pdf-viewer [data-layout-syncing='true'] canvas {\n object-fit: contain;\n}\n\n/* Hide native scrollbars; custom ScrollAreaScrollbar is used instead */\n.pdf-viewer .pdf-scroll-viewport {\n scrollbar-width: none;\n}\n\n.pdf-viewer .pdf-scroll-viewport::-webkit-scrollbar {\n display: none;\n}\n"],"mappings":";AAMA,CAAC,WAAW,CAAC,UAAU;AACrB,cAAY,KAAK,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE;AAC5B,cAAY,UAAU,GAAG,IAAI,EAAE,WAAW,EAAE,YAAY;AACxD,SAAO;AACT;AAEA,CANC,WAMW,CANC,UAMU;AACrB,cAAY,KAAK,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE;AAC5B,cAAY,UAAU,GAAG,IAAI,EAAE,WAAW,EAAE,YAAY;AACxD,SAAO;AACP,2BAAyB;AAC3B;AAEA,CAbC,WAaW,CAbC,UAaU,EAAE;AACzB,CAdC,WAcW,CAdC,UAcU,EAAE;AACvB,cAAY;AACd;AAGA,CAnBC,WAmBW,CAAC,0BAA4B,CAnB5B;AAoBX,WAAS;AACT,kBAAgB;AAClB;AAGA,CAzBC,WAyBW,CAAC,0BAA4B;AACvC,cAAY;AACd;AAGA,CA9BC,WA8BW,CAAC;AACX,mBAAiB;AACnB;AAEA,CAlCC,WAkCW,CAJC,mBAImB;AAC9B,WAAS;AACX;","names":[]}
|