@makolabs/ripple 3.4.0 → 3.5.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/dist/elements/pdf-viewer/PdfViewer.svelte +427 -0
- package/dist/elements/pdf-viewer/PdfViewer.svelte.d.ts +4 -0
- package/dist/elements/pdf-viewer/pdf-viewer-types.d.ts +38 -0
- package/dist/elements/pdf-viewer/pdf-viewer-types.js +1 -0
- package/dist/forms/DateRange.svelte +85 -460
- package/dist/forms/calendar/Calendar.svelte +369 -164
- package/dist/forms/calendar/calendar-types.d.ts +15 -5
- package/dist/forms/date-picker/DatePicker.svelte +0 -1
- package/dist/forms/month-picker/MonthPicker.svelte +119 -40
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/package.json +8 -1
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { onMount, onDestroy, tick } from 'svelte';
|
|
3
|
+
import { browser } from '$app/environment';
|
|
4
|
+
import { cn } from '../../helper/cls.js';
|
|
5
|
+
import { buildTestId } from '../../helper/testid.js';
|
|
6
|
+
import Spinner from '../spinner/Spinner.svelte';
|
|
7
|
+
import Button from '../../button/Button.svelte';
|
|
8
|
+
import { Color, Size } from '../../variants.js';
|
|
9
|
+
import type { PDFDocumentLoadingTask, PDFDocumentProxy, RenderTask } from 'pdfjs-dist';
|
|
10
|
+
import type { PdfViewerProps } from './pdf-viewer-types.js';
|
|
11
|
+
|
|
12
|
+
type PdfJsModule = typeof import('pdfjs-dist');
|
|
13
|
+
|
|
14
|
+
let {
|
|
15
|
+
url,
|
|
16
|
+
initialScale = 1.5,
|
|
17
|
+
minScale = 0.5,
|
|
18
|
+
maxScale = 3,
|
|
19
|
+
scaleStep = 0.25,
|
|
20
|
+
showToolbar = true,
|
|
21
|
+
loadingLabel = 'Loading PDF...',
|
|
22
|
+
errorTitle = 'Error loading PDF',
|
|
23
|
+
errorActionLabel = 'Open in new tab',
|
|
24
|
+
class: className = '',
|
|
25
|
+
testId
|
|
26
|
+
}: PdfViewerProps = $props();
|
|
27
|
+
|
|
28
|
+
const clampScale = (value: number) => Math.min(Math.max(value, minScale), maxScale);
|
|
29
|
+
|
|
30
|
+
let containerEl: HTMLDivElement;
|
|
31
|
+
let canvasElements: HTMLCanvasElement[] = $state([]);
|
|
32
|
+
let pdfDoc: PDFDocumentProxy | null = $state(null);
|
|
33
|
+
let pageCount = $state(0);
|
|
34
|
+
let scale = $state(clampScale(initialScale));
|
|
35
|
+
let loading = $state(true);
|
|
36
|
+
let error = $state<string | null>(null);
|
|
37
|
+
let pdfjsLib: PdfJsModule | null = $state(null);
|
|
38
|
+
let currentLoadingTask: PDFDocumentLoadingTask | null = null;
|
|
39
|
+
let previousUrl: string | null = null;
|
|
40
|
+
let loadToken = 0;
|
|
41
|
+
let renderEpoch = 0;
|
|
42
|
+
let renderTasks: (RenderTask | null)[] = [];
|
|
43
|
+
|
|
44
|
+
const wrapperTestId = $derived(buildTestId('pdf-viewer', undefined, testId));
|
|
45
|
+
|
|
46
|
+
onMount(async () => {
|
|
47
|
+
if (!browser) return;
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
const lib = await import('pdfjs-dist');
|
|
51
|
+
const workerModule = await import('pdfjs-dist/build/pdf.worker.min.mjs?url');
|
|
52
|
+
lib.GlobalWorkerOptions.workerSrc = workerModule.default;
|
|
53
|
+
// Assign to reactive state only after workerSrc is configured,
|
|
54
|
+
// so the $effect that calls loadPdf() sees a fully-ready library
|
|
55
|
+
pdfjsLib = lib;
|
|
56
|
+
} catch (err) {
|
|
57
|
+
console.error('Error loading PDF.js:', err);
|
|
58
|
+
error = 'Failed to load PDF library';
|
|
59
|
+
loading = false;
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
onDestroy(() => {
|
|
64
|
+
cleanupPdf();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
$effect(() => {
|
|
68
|
+
if (!pdfjsLib || !browser) return;
|
|
69
|
+
|
|
70
|
+
const isFirstLoad = previousUrl === null;
|
|
71
|
+
const urlChanged = !isFirstLoad && previousUrl !== url;
|
|
72
|
+
|
|
73
|
+
if (!isFirstLoad && !urlChanged) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (urlChanged) {
|
|
78
|
+
cleanupPdf();
|
|
79
|
+
pageCount = 0;
|
|
80
|
+
error = null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
previousUrl = url;
|
|
84
|
+
loadPdf();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
$effect(() => {
|
|
88
|
+
if (pdfDoc && !loading && scale) {
|
|
89
|
+
// Wait for canvas elements to mount after loading completes
|
|
90
|
+
tick().then(() => renderAllPages());
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
function cleanupPdf() {
|
|
95
|
+
loadToken++;
|
|
96
|
+
renderEpoch++;
|
|
97
|
+
|
|
98
|
+
for (const task of renderTasks) {
|
|
99
|
+
if (task) task.cancel();
|
|
100
|
+
}
|
|
101
|
+
renderTasks = [];
|
|
102
|
+
|
|
103
|
+
if (currentLoadingTask) {
|
|
104
|
+
currentLoadingTask.destroy();
|
|
105
|
+
currentLoadingTask = null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (pdfDoc) {
|
|
109
|
+
pdfDoc.destroy();
|
|
110
|
+
pdfDoc = null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
canvasElements = [];
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async function loadPdf() {
|
|
117
|
+
if (!pdfjsLib) return;
|
|
118
|
+
|
|
119
|
+
const token = ++loadToken;
|
|
120
|
+
|
|
121
|
+
loading = true;
|
|
122
|
+
error = null;
|
|
123
|
+
|
|
124
|
+
const loadingTask = (currentLoadingTask = pdfjsLib.getDocument(url));
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
const doc = await loadingTask.promise;
|
|
128
|
+
|
|
129
|
+
if (token !== loadToken) return;
|
|
130
|
+
|
|
131
|
+
currentLoadingTask = null;
|
|
132
|
+
pdfDoc = doc;
|
|
133
|
+
pageCount = pdfDoc.numPages;
|
|
134
|
+
|
|
135
|
+
canvasElements = new Array(pageCount).fill(null);
|
|
136
|
+
|
|
137
|
+
loading = false;
|
|
138
|
+
} catch (err) {
|
|
139
|
+
if (token !== loadToken) return;
|
|
140
|
+
|
|
141
|
+
console.error('Error loading PDF:', err);
|
|
142
|
+
error = err instanceof Error ? err.message : 'Failed to load PDF';
|
|
143
|
+
loading = false;
|
|
144
|
+
currentLoadingTask = null;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async function renderAllPages() {
|
|
149
|
+
if (!pdfDoc) return;
|
|
150
|
+
|
|
151
|
+
const epoch = ++renderEpoch;
|
|
152
|
+
const renderScale = scale;
|
|
153
|
+
|
|
154
|
+
for (const task of renderTasks) {
|
|
155
|
+
if (task) task.cancel();
|
|
156
|
+
}
|
|
157
|
+
renderTasks = [];
|
|
158
|
+
|
|
159
|
+
for (let i = 1; i <= pageCount; i++) {
|
|
160
|
+
void renderPage(i, epoch, renderScale);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async function renderPage(num: number, epoch: number, renderScale: number) {
|
|
165
|
+
const doc = pdfDoc;
|
|
166
|
+
if (!doc) return;
|
|
167
|
+
|
|
168
|
+
const canvas = canvasElements[num - 1];
|
|
169
|
+
if (!canvas) return;
|
|
170
|
+
|
|
171
|
+
try {
|
|
172
|
+
const page = await doc.getPage(num);
|
|
173
|
+
// Bail if a newer pass started, the doc was swapped, or the canvas was remounted
|
|
174
|
+
if (epoch !== renderEpoch || doc !== pdfDoc || canvas !== canvasElements[num - 1]) return;
|
|
175
|
+
|
|
176
|
+
const viewport = page.getViewport({ scale: renderScale });
|
|
177
|
+
|
|
178
|
+
const context = canvas.getContext('2d');
|
|
179
|
+
if (!context) {
|
|
180
|
+
throw new Error('Could not get canvas context');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Account for high-DPI/Retina displays to prevent fuzzy rendering
|
|
184
|
+
const pixelRatio = window.devicePixelRatio || 1;
|
|
185
|
+
|
|
186
|
+
canvas.height = viewport.height * pixelRatio;
|
|
187
|
+
canvas.width = viewport.width * pixelRatio;
|
|
188
|
+
|
|
189
|
+
canvas.style.width = `${viewport.width}px`;
|
|
190
|
+
canvas.style.height = `${viewport.height}px`;
|
|
191
|
+
|
|
192
|
+
context.scale(pixelRatio, pixelRatio);
|
|
193
|
+
|
|
194
|
+
const renderTask = page.render({
|
|
195
|
+
canvas,
|
|
196
|
+
canvasContext: context,
|
|
197
|
+
viewport
|
|
198
|
+
});
|
|
199
|
+
if (epoch === renderEpoch) renderTasks[num - 1] = renderTask;
|
|
200
|
+
|
|
201
|
+
await renderTask.promise;
|
|
202
|
+
if (epoch === renderEpoch) renderTasks[num - 1] = null;
|
|
203
|
+
} catch (err) {
|
|
204
|
+
if (err instanceof Error && err.name === 'RenderingCancelledException') {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
console.error('Error rendering page:', err);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function handleZoomIn() {
|
|
212
|
+
scale = Math.min(scale + scaleStep, maxScale);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function handleZoomOut() {
|
|
216
|
+
scale = Math.max(scale - scaleStep, minScale);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async function handleFitToWidth() {
|
|
220
|
+
if (!pdfDoc || !containerEl) return;
|
|
221
|
+
|
|
222
|
+
const page = await pdfDoc.getPage(1);
|
|
223
|
+
const viewport = page.getViewport({ scale: 1 });
|
|
224
|
+
const containerWidth = containerEl.clientWidth || viewport.width;
|
|
225
|
+
|
|
226
|
+
const computed = (containerWidth - 48) / viewport.width;
|
|
227
|
+
scale = Math.min(Math.max(computed, minScale), maxScale);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function handleKeydown(event: KeyboardEvent) {
|
|
231
|
+
if (loading) return;
|
|
232
|
+
|
|
233
|
+
switch (event.key) {
|
|
234
|
+
case '+':
|
|
235
|
+
case '=':
|
|
236
|
+
event.preventDefault();
|
|
237
|
+
handleZoomIn();
|
|
238
|
+
break;
|
|
239
|
+
case '-':
|
|
240
|
+
event.preventDefault();
|
|
241
|
+
handleZoomOut();
|
|
242
|
+
break;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
</script>
|
|
246
|
+
|
|
247
|
+
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
|
|
248
|
+
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
|
249
|
+
<div
|
|
250
|
+
class={cn(
|
|
251
|
+
'focus-visible:ring-primary-400 flex h-full flex-col focus-visible:ring-2 focus-visible:outline-none',
|
|
252
|
+
className
|
|
253
|
+
)}
|
|
254
|
+
onkeydown={handleKeydown}
|
|
255
|
+
tabindex="0"
|
|
256
|
+
role="region"
|
|
257
|
+
aria-label="PDF viewer"
|
|
258
|
+
data-testid={wrapperTestId}
|
|
259
|
+
>
|
|
260
|
+
{#if showToolbar}
|
|
261
|
+
<div
|
|
262
|
+
class="border-default-200 bg-default-50 flex items-center justify-between border-b px-4 py-2"
|
|
263
|
+
data-testid={buildTestId('pdf-viewer', 'toolbar', testId)}
|
|
264
|
+
>
|
|
265
|
+
<div class="flex items-center gap-2">
|
|
266
|
+
<span class="text-default-700 text-sm" aria-live="polite">
|
|
267
|
+
{#if loading}
|
|
268
|
+
|
|
269
|
+
{:else}
|
|
270
|
+
{pageCount}
|
|
271
|
+
{pageCount === 1 ? 'page' : 'pages'}
|
|
272
|
+
{/if}
|
|
273
|
+
</span>
|
|
274
|
+
</div>
|
|
275
|
+
|
|
276
|
+
<div class="flex items-center gap-1">
|
|
277
|
+
<button
|
|
278
|
+
type="button"
|
|
279
|
+
onclick={handleZoomOut}
|
|
280
|
+
disabled={loading || scale <= minScale}
|
|
281
|
+
class="text-default-700 hover:bg-default-100 focus-visible:ring-primary-400 rounded-md p-1.5 transition-colors focus-visible:ring-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-40"
|
|
282
|
+
aria-label="Zoom out"
|
|
283
|
+
title="Zoom out"
|
|
284
|
+
data-testid={buildTestId('pdf-viewer', 'zoom-out', testId)}
|
|
285
|
+
>
|
|
286
|
+
<svg
|
|
287
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
288
|
+
class="h-5 w-5"
|
|
289
|
+
viewBox="0 0 24 24"
|
|
290
|
+
fill="none"
|
|
291
|
+
stroke="currentColor"
|
|
292
|
+
stroke-width="2"
|
|
293
|
+
stroke-linecap="round"
|
|
294
|
+
stroke-linejoin="round"
|
|
295
|
+
aria-hidden="true"
|
|
296
|
+
>
|
|
297
|
+
<circle cx="11" cy="11" r="8" />
|
|
298
|
+
<line x1="21" y1="21" x2="16.65" y2="16.65" />
|
|
299
|
+
<line x1="8" y1="11" x2="14" y2="11" />
|
|
300
|
+
</svg>
|
|
301
|
+
</button>
|
|
302
|
+
|
|
303
|
+
<span
|
|
304
|
+
class="text-default-700 min-w-[3.5rem] text-center text-sm tabular-nums"
|
|
305
|
+
aria-live="polite"
|
|
306
|
+
data-testid={buildTestId('pdf-viewer', 'scale', testId)}
|
|
307
|
+
>
|
|
308
|
+
{Math.round(scale * 100)}%
|
|
309
|
+
</span>
|
|
310
|
+
|
|
311
|
+
<button
|
|
312
|
+
type="button"
|
|
313
|
+
onclick={handleZoomIn}
|
|
314
|
+
disabled={loading || scale >= maxScale}
|
|
315
|
+
class="text-default-700 hover:bg-default-100 focus-visible:ring-primary-400 rounded-md p-1.5 transition-colors focus-visible:ring-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-40"
|
|
316
|
+
aria-label="Zoom in"
|
|
317
|
+
title="Zoom in"
|
|
318
|
+
data-testid={buildTestId('pdf-viewer', 'zoom-in', testId)}
|
|
319
|
+
>
|
|
320
|
+
<svg
|
|
321
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
322
|
+
class="h-5 w-5"
|
|
323
|
+
viewBox="0 0 24 24"
|
|
324
|
+
fill="none"
|
|
325
|
+
stroke="currentColor"
|
|
326
|
+
stroke-width="2"
|
|
327
|
+
stroke-linecap="round"
|
|
328
|
+
stroke-linejoin="round"
|
|
329
|
+
aria-hidden="true"
|
|
330
|
+
>
|
|
331
|
+
<circle cx="11" cy="11" r="8" />
|
|
332
|
+
<line x1="21" y1="21" x2="16.65" y2="16.65" />
|
|
333
|
+
<line x1="11" y1="8" x2="11" y2="14" />
|
|
334
|
+
<line x1="8" y1="11" x2="14" y2="11" />
|
|
335
|
+
</svg>
|
|
336
|
+
</button>
|
|
337
|
+
|
|
338
|
+
<button
|
|
339
|
+
type="button"
|
|
340
|
+
onclick={handleFitToWidth}
|
|
341
|
+
disabled={loading}
|
|
342
|
+
class="text-default-700 hover:bg-default-100 focus-visible:ring-primary-400 rounded-md p-1.5 transition-colors focus-visible:ring-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-40"
|
|
343
|
+
aria-label="Fit to width"
|
|
344
|
+
title="Fit to width"
|
|
345
|
+
data-testid={buildTestId('pdf-viewer', 'fit-to-width', testId)}
|
|
346
|
+
>
|
|
347
|
+
<svg
|
|
348
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
349
|
+
class="h-5 w-5"
|
|
350
|
+
viewBox="0 0 24 24"
|
|
351
|
+
fill="none"
|
|
352
|
+
stroke="currentColor"
|
|
353
|
+
stroke-width="2"
|
|
354
|
+
stroke-linecap="round"
|
|
355
|
+
stroke-linejoin="round"
|
|
356
|
+
aria-hidden="true"
|
|
357
|
+
>
|
|
358
|
+
<polyline points="15 3 21 3 21 9" />
|
|
359
|
+
<polyline points="9 21 3 21 3 15" />
|
|
360
|
+
<line x1="21" y1="3" x2="14" y2="10" />
|
|
361
|
+
<line x1="3" y1="21" x2="10" y2="14" />
|
|
362
|
+
</svg>
|
|
363
|
+
</button>
|
|
364
|
+
</div>
|
|
365
|
+
</div>
|
|
366
|
+
{/if}
|
|
367
|
+
|
|
368
|
+
<div
|
|
369
|
+
bind:this={containerEl}
|
|
370
|
+
class="bg-default-100 relative flex-1 overflow-auto p-4"
|
|
371
|
+
data-testid={buildTestId('pdf-viewer', 'canvas-area', testId)}
|
|
372
|
+
>
|
|
373
|
+
{#if loading}
|
|
374
|
+
<div class="absolute inset-0 flex items-center justify-center">
|
|
375
|
+
<Spinner size={Size.LG} color={Color.PRIMARY} label={loadingLabel} />
|
|
376
|
+
</div>
|
|
377
|
+
{:else if error}
|
|
378
|
+
<div class="absolute inset-0 flex items-center justify-center">
|
|
379
|
+
<div
|
|
380
|
+
class="ring-default-200 max-w-md rounded-xl bg-white p-6 text-center shadow-sm ring-1"
|
|
381
|
+
data-testid={buildTestId('pdf-viewer', 'error', testId)}
|
|
382
|
+
>
|
|
383
|
+
<div
|
|
384
|
+
class="bg-danger-50 text-danger-600 mx-auto mb-3 flex size-10 items-center justify-center rounded-full"
|
|
385
|
+
aria-hidden="true"
|
|
386
|
+
>
|
|
387
|
+
<svg
|
|
388
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
389
|
+
class="h-5 w-5"
|
|
390
|
+
viewBox="0 0 24 24"
|
|
391
|
+
fill="none"
|
|
392
|
+
stroke="currentColor"
|
|
393
|
+
stroke-width="2"
|
|
394
|
+
stroke-linecap="round"
|
|
395
|
+
stroke-linejoin="round"
|
|
396
|
+
>
|
|
397
|
+
<circle cx="12" cy="12" r="10" />
|
|
398
|
+
<line x1="12" y1="8" x2="12" y2="12" />
|
|
399
|
+
<line x1="12" y1="16" x2="12.01" y2="16" />
|
|
400
|
+
</svg>
|
|
401
|
+
</div>
|
|
402
|
+
<p class="text-default-900 mb-1 text-base font-semibold">{errorTitle}</p>
|
|
403
|
+
<p class="text-default-600 mb-4 text-sm">{error}</p>
|
|
404
|
+
<Button
|
|
405
|
+
color={Color.PRIMARY}
|
|
406
|
+
size={Size.SM}
|
|
407
|
+
href={url}
|
|
408
|
+
target="_blank"
|
|
409
|
+
rel="noopener noreferrer"
|
|
410
|
+
>
|
|
411
|
+
{errorActionLabel}
|
|
412
|
+
</Button>
|
|
413
|
+
</div>
|
|
414
|
+
</div>
|
|
415
|
+
{:else}
|
|
416
|
+
<div class="flex flex-col items-center gap-4">
|
|
417
|
+
{#each { length: pageCount } as _, i (i)}
|
|
418
|
+
<canvas
|
|
419
|
+
bind:this={canvasElements[i]}
|
|
420
|
+
class="ring-default-200 block rounded-sm bg-white shadow-sm ring-1"
|
|
421
|
+
data-testid={buildTestId('pdf-viewer', 'page', testId, i + 1)}
|
|
422
|
+
></canvas>
|
|
423
|
+
{/each}
|
|
424
|
+
</div>
|
|
425
|
+
{/if}
|
|
426
|
+
</div>
|
|
427
|
+
</div>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ClassValue } from 'tailwind-variants';
|
|
2
|
+
/**
|
|
3
|
+
* Props for `<PdfViewer>` — a scrollable, zoomable canvas-based PDF
|
|
4
|
+
* viewer powered by `pdfjs-dist` (loaded dynamically on the client).
|
|
5
|
+
*
|
|
6
|
+
* The component renders every page of the PDF as a stacked column of
|
|
7
|
+
* canvases, with a toolbar for zoom in / zoom out / fit-to-width. PDF.js
|
|
8
|
+
* is imported lazily inside `onMount`, so the viewer is SSR-safe.
|
|
9
|
+
*
|
|
10
|
+
* **Peer dependency:** install `pdfjs-dist` in your application.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```svelte
|
|
14
|
+
* <PdfViewer url="/files/invoice.pdf" class="h-[80vh]" />
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export type PdfViewerProps = {
|
|
18
|
+
/** URL of the PDF document. Changing the URL reloads the viewer. */
|
|
19
|
+
url: string;
|
|
20
|
+
/** Initial zoom scale. @default 1.5 */
|
|
21
|
+
initialScale?: number;
|
|
22
|
+
/** Minimum allowed zoom scale. @default 0.5 */
|
|
23
|
+
minScale?: number;
|
|
24
|
+
/** Maximum allowed zoom scale. @default 3 */
|
|
25
|
+
maxScale?: number;
|
|
26
|
+
/** Step used by the zoom-in / zoom-out buttons. @default 0.25 */
|
|
27
|
+
scaleStep?: number;
|
|
28
|
+
/** Show the zoom / page-count toolbar. @default true */
|
|
29
|
+
showToolbar?: boolean;
|
|
30
|
+
/** Visible label inside the loading spinner. @default 'Loading PDF...' */
|
|
31
|
+
loadingLabel?: string;
|
|
32
|
+
/** Heading shown when the PDF fails to load. @default 'Error loading PDF' */
|
|
33
|
+
errorTitle?: string;
|
|
34
|
+
/** Label of the fallback "open in new tab" link in the error state. @default 'Open in new tab' */
|
|
35
|
+
errorActionLabel?: string;
|
|
36
|
+
class?: ClassValue;
|
|
37
|
+
testId?: string;
|
|
38
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|