@epam/ai-dial-react-pdf-highlighter 0.1.0-rc.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 +176 -0
- package/README.md +194 -0
- package/dist/ai-dial-react-pdf-highlighter.cjs.js +1 -0
- package/dist/ai-dial-react-pdf-highlighter.es.js +568 -0
- package/dist/index.css +1 -0
- package/dist/src/components/DocumentPreview/DocumentPreview.d.ts +55 -0
- package/dist/src/components/PageThumbnail/PageThumbnail.d.ts +24 -0
- package/dist/src/components/PdfPreviewLoader/PdfPreviewLoader.d.ts +14 -0
- package/dist/src/components/PdfViewer/PdfViewer.d.ts +35 -0
- package/dist/src/constants/pdf-viewer.constants.d.ts +12 -0
- package/dist/src/constants/storybook.d.ts +2 -0
- package/dist/src/context/documentCacheContext.d.ts +2 -0
- package/dist/src/context/documentCacheProvider.d.ts +15 -0
- package/dist/src/hooks/useDocumentPreview.d.ts +7 -0
- package/dist/src/hooks/useDocumentPreviewCache.d.ts +6 -0
- package/dist/src/index.d.ts +16 -0
- package/dist/src/models/document-cache.models.d.ts +17 -0
- package/dist/src/models/document-preview.models.d.ts +33 -0
- package/dist/src/models/pdf-viewer.models.d.ts +25 -0
- package/dist/src/utils/pdf-viewer.utils.d.ts +2 -0
- package/package.json +111 -0
|
@@ -0,0 +1,568 @@
|
|
|
1
|
+
import { jsx as e, jsxs as v, Fragment as le } from "react/jsx-runtime";
|
|
2
|
+
import { mergeClasses as S, DialEllipsisTooltip as K, DialButton as j, ElementSize as B, DialSelect as oe, SelectSize as se, DialCheckbox as ce, DialLoader as ie } from "@epam/ai-dial-ui-kit";
|
|
3
|
+
import { ZoomMode as Y, PDFHighlightViewer as ue } from "@epam/pdf-highlighter-kit";
|
|
4
|
+
import { useRef as F, useState as E, useEffect as y, useCallback as O, createContext as me, useContext as de, useMemo as J } from "react";
|
|
5
|
+
import { IconChevronDown as fe, IconChevronUp as pe, IconMinus as ve, IconPlus as be, IconAlertTriangle as he } from "@tabler/icons-react";
|
|
6
|
+
const H = "auto", G = "page-fit", X = [
|
|
7
|
+
{ value: H, label: "Auto" },
|
|
8
|
+
{ value: G, label: "Page Fit" },
|
|
9
|
+
{ value: "0.5", label: "50%" },
|
|
10
|
+
{ value: "0.75", label: "75%" },
|
|
11
|
+
{ value: "1", label: "100%" },
|
|
12
|
+
{ value: "1.25", label: "125%" },
|
|
13
|
+
{ value: "1.5", label: "150%" },
|
|
14
|
+
{ value: "2", label: "200%" },
|
|
15
|
+
{ value: "3", label: "300%" }
|
|
16
|
+
], xe = {
|
|
17
|
+
maxWidth: 104,
|
|
18
|
+
format: "image/jpeg",
|
|
19
|
+
quality: 0.85
|
|
20
|
+
}, Q = 15, we = "application/pdf", ye = ".pdf", ge = ({
|
|
21
|
+
pdf: t,
|
|
22
|
+
highlights: a,
|
|
23
|
+
selectedHighlightId: i,
|
|
24
|
+
autoFocusFirstHighlight: n = !1,
|
|
25
|
+
selectedPageNumber: u,
|
|
26
|
+
zoom: s,
|
|
27
|
+
containerClassName: d,
|
|
28
|
+
onTotalPagesChange: f,
|
|
29
|
+
onViewerReady: g,
|
|
30
|
+
selectedPages: m
|
|
31
|
+
}) => {
|
|
32
|
+
const h = F(null), r = F(null), [o, T] = E(!1), [_, b] = E(!1), D = F(f), z = F(g);
|
|
33
|
+
y(() => {
|
|
34
|
+
D.current = f;
|
|
35
|
+
}, [f]), y(() => {
|
|
36
|
+
z.current = g;
|
|
37
|
+
}, [g]);
|
|
38
|
+
const N = O(
|
|
39
|
+
(l) => r.current?.goToHighlight(l),
|
|
40
|
+
[]
|
|
41
|
+
);
|
|
42
|
+
return y(() => {
|
|
43
|
+
if (!h.current) return;
|
|
44
|
+
T(!1), b(!1), r.current && (r.current.destroy(), r.current = null);
|
|
45
|
+
let l = null, p = !0;
|
|
46
|
+
return (async () => {
|
|
47
|
+
try {
|
|
48
|
+
if (l = new ue(), await l.init(h.current, {
|
|
49
|
+
enableTextSelection: !0,
|
|
50
|
+
enableVirtualScrolling: !0,
|
|
51
|
+
bboxOrigin: "top-left"
|
|
52
|
+
}), !p) {
|
|
53
|
+
l.destroy();
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (await l.loadPDF(t, { selectedPages: m }), !p) {
|
|
57
|
+
l.destroy();
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const x = l.getTotalPages?.();
|
|
61
|
+
typeof x == "number" && x > 0 && D.current?.(x), p && (r.current = l, T(!0));
|
|
62
|
+
} catch (x) {
|
|
63
|
+
if (!p)
|
|
64
|
+
return;
|
|
65
|
+
console.error("Error in initViewer:", x);
|
|
66
|
+
}
|
|
67
|
+
})(), () => {
|
|
68
|
+
p = !1, l && l.destroy();
|
|
69
|
+
};
|
|
70
|
+
}, [t, m]), y(() => {
|
|
71
|
+
!o || !r.current || (r.current.loadHighlights(a), n && a && a[0] && N(a[0].id));
|
|
72
|
+
}, [n, N, a, o]), y(() => {
|
|
73
|
+
b(!0);
|
|
74
|
+
}, [a]), y(() => {
|
|
75
|
+
o && _ && i && N(i);
|
|
76
|
+
}, [N, o, _, i]), y(() => {
|
|
77
|
+
if (!(!o || !s))
|
|
78
|
+
if (s === H)
|
|
79
|
+
r.current?.setZoom?.(Y.AUTO);
|
|
80
|
+
else if (s === G)
|
|
81
|
+
r.current?.setZoom?.(Y.PAGE_FIT);
|
|
82
|
+
else {
|
|
83
|
+
const l = parseFloat(s);
|
|
84
|
+
!Number.isNaN(l) && l > 0 && r.current?.setZoom?.(l);
|
|
85
|
+
}
|
|
86
|
+
}, [o, s]), y(() => {
|
|
87
|
+
o && u && r.current?.setPage?.(u);
|
|
88
|
+
}, [o, u]), y(() => {
|
|
89
|
+
if (!o || !r.current) return;
|
|
90
|
+
const l = r.current;
|
|
91
|
+
z.current?.({
|
|
92
|
+
getThumbnailsDataUrl: (p, P) => l.getThumbnailsDataUrl(p, P),
|
|
93
|
+
zoomIn: () => l.zoomIn(),
|
|
94
|
+
zoomOut: () => l.zoomOut(),
|
|
95
|
+
getZoom: () => l.getZoom(),
|
|
96
|
+
navigateToPage: (p) => l.setPage?.(p)
|
|
97
|
+
});
|
|
98
|
+
}, [o]), /* @__PURE__ */ e(
|
|
99
|
+
"div",
|
|
100
|
+
{
|
|
101
|
+
ref: h,
|
|
102
|
+
className: S(
|
|
103
|
+
"grow min-w-0 overflow-auto bg-layer-3",
|
|
104
|
+
d
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
);
|
|
108
|
+
}, C = "h-4 rounded-[120px] bg-gradient-to-r from-layer-4 via-layer-3 to-layer-4 bg-[length:200%_100%] animate-skeleton-wave", Ne = ({
|
|
109
|
+
className: t,
|
|
110
|
+
ariaLabel: a = "pdf preview loading"
|
|
111
|
+
}) => /* @__PURE__ */ e(
|
|
112
|
+
"div",
|
|
113
|
+
{
|
|
114
|
+
role: "status",
|
|
115
|
+
"aria-label": a,
|
|
116
|
+
"aria-live": "polite",
|
|
117
|
+
className: S("h-full w-full bg-layer-3", t),
|
|
118
|
+
children: /* @__PURE__ */ v("div", { className: "flex h-full w-full flex-col gap-10 px-[60px] pt-10", children: [
|
|
119
|
+
/* @__PURE__ */ e("div", { className: "mx-auto h-4 w-[208px] max-w-[40%] rounded-[120px] bg-gradient-to-r from-layer-4 via-layer-3 to-layer-4 bg-[length:200%_100%] animate-skeleton-wave" }),
|
|
120
|
+
/* @__PURE__ */ v("div", { className: "flex flex-col gap-4", children: [
|
|
121
|
+
/* @__PURE__ */ e("div", { className: C }),
|
|
122
|
+
/* @__PURE__ */ e("div", { className: C }),
|
|
123
|
+
/* @__PURE__ */ e("div", { className: C }),
|
|
124
|
+
/* @__PURE__ */ e("div", { className: C }),
|
|
125
|
+
/* @__PURE__ */ e(
|
|
126
|
+
"div",
|
|
127
|
+
{
|
|
128
|
+
className: S(
|
|
129
|
+
C,
|
|
130
|
+
"w-[28%] max-w-[320px]"
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
)
|
|
134
|
+
] }),
|
|
135
|
+
/* @__PURE__ */ v("div", { className: "flex flex-col gap-4", children: [
|
|
136
|
+
/* @__PURE__ */ e("div", { className: C }),
|
|
137
|
+
/* @__PURE__ */ e("div", { className: C }),
|
|
138
|
+
/* @__PURE__ */ e("div", { className: C }),
|
|
139
|
+
/* @__PURE__ */ e("div", { className: C }),
|
|
140
|
+
/* @__PURE__ */ e(
|
|
141
|
+
"div",
|
|
142
|
+
{
|
|
143
|
+
className: S(
|
|
144
|
+
C,
|
|
145
|
+
"w-[58%] max-w-[520px]"
|
|
146
|
+
)
|
|
147
|
+
}
|
|
148
|
+
)
|
|
149
|
+
] })
|
|
150
|
+
] })
|
|
151
|
+
}
|
|
152
|
+
), te = me(null), Ie = () => de(te), Z = X.filter(({ value: t }) => {
|
|
153
|
+
const a = Number.parseFloat(t);
|
|
154
|
+
return !Number.isNaN(a) && a > 0;
|
|
155
|
+
}), Te = [...Z].reverse(), Ce = (t, a, i) => {
|
|
156
|
+
if (Z.length === 0)
|
|
157
|
+
return t;
|
|
158
|
+
const n = Number.parseFloat(t), u = !Number.isNaN(n) && n > 0 ? n : i, s = typeof u == "number" && u > 0 ? u : Number.parseFloat(Z[0].value);
|
|
159
|
+
return a === 1 ? (Z.find(
|
|
160
|
+
({ value: d }) => Number.parseFloat(d) > s
|
|
161
|
+
) ?? Z[Z.length - 1]).value : (Te.find(
|
|
162
|
+
({ value: d }) => Number.parseFloat(d) < s
|
|
163
|
+
) ?? Z[0]).value;
|
|
164
|
+
};
|
|
165
|
+
function Pe(t, a) {
|
|
166
|
+
const i = (t ?? "").toLowerCase();
|
|
167
|
+
return (a ?? "").toLowerCase() === we || i.endsWith(ye);
|
|
168
|
+
}
|
|
169
|
+
const ee = X.map((t) => ({
|
|
170
|
+
value: t.value,
|
|
171
|
+
label: t.label
|
|
172
|
+
})), Oe = ({
|
|
173
|
+
fileUrl: t,
|
|
174
|
+
fileName: a,
|
|
175
|
+
loadFileCb: i,
|
|
176
|
+
highlights: n,
|
|
177
|
+
selectedPageNumber: u,
|
|
178
|
+
onTotalPagesChange: s,
|
|
179
|
+
thumbnailPageNumbers: d,
|
|
180
|
+
onThumbnailsLoaded: f,
|
|
181
|
+
onViewerReady: g,
|
|
182
|
+
showLoaderOverlay: m = !1,
|
|
183
|
+
selectedPages: h
|
|
184
|
+
}) => {
|
|
185
|
+
const r = Ie(), o = F(null), [T, _] = E(!1), [b, D] = E(0), [z, N] = E(!1), [l, p] = E(null), [P, x] = E(null), [I, A] = E(H), k = J(() => {
|
|
186
|
+
let c = a || t;
|
|
187
|
+
if (!a)
|
|
188
|
+
try {
|
|
189
|
+
c = new URL(t).pathname;
|
|
190
|
+
} catch {
|
|
191
|
+
c = t.split("?")[0].split("#")[0];
|
|
192
|
+
}
|
|
193
|
+
return Pe(c.toLowerCase());
|
|
194
|
+
}, [a, t]), M = O(
|
|
195
|
+
(c) => {
|
|
196
|
+
D(
|
|
197
|
+
(b + c + n.length) % n.length
|
|
198
|
+
);
|
|
199
|
+
},
|
|
200
|
+
[b, n.length]
|
|
201
|
+
), R = O(
|
|
202
|
+
(c) => {
|
|
203
|
+
const w = o.current;
|
|
204
|
+
!w?.getZoom || !c || A(Ce(I, c, w.getZoom()));
|
|
205
|
+
},
|
|
206
|
+
[I]
|
|
207
|
+
), $ = J(() => {
|
|
208
|
+
if (!(I === H || I === G || X.some((w) => w.value === I))) {
|
|
209
|
+
const w = parseFloat(I);
|
|
210
|
+
if (!Number.isNaN(w) && w > 0)
|
|
211
|
+
return [
|
|
212
|
+
...ee,
|
|
213
|
+
{ value: I, label: `${Math.round(w * 100)}%` }
|
|
214
|
+
];
|
|
215
|
+
}
|
|
216
|
+
return ee;
|
|
217
|
+
}, [I]);
|
|
218
|
+
y(() => {
|
|
219
|
+
let c = !1;
|
|
220
|
+
if (p(!0), N(!1), x(null), _(!1), o.current = null, !k) {
|
|
221
|
+
N(!0), p(!1);
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
return (async () => {
|
|
225
|
+
try {
|
|
226
|
+
const L = await (r ? r.getFile(t, () => i(t)) : i(t));
|
|
227
|
+
c || x(L);
|
|
228
|
+
} catch {
|
|
229
|
+
c || (N(!0), x(null));
|
|
230
|
+
} finally {
|
|
231
|
+
c || p(!1);
|
|
232
|
+
}
|
|
233
|
+
})(), () => {
|
|
234
|
+
c = !0;
|
|
235
|
+
};
|
|
236
|
+
}, [t, r, i, k]);
|
|
237
|
+
const U = O(
|
|
238
|
+
(c) => {
|
|
239
|
+
o.current = c, _(!0), g?.(c);
|
|
240
|
+
},
|
|
241
|
+
[g]
|
|
242
|
+
);
|
|
243
|
+
return y(() => {
|
|
244
|
+
const c = d ?? [], w = o.current?.getThumbnailsDataUrl;
|
|
245
|
+
if (!c.length || !w || !P || !f || !T)
|
|
246
|
+
return;
|
|
247
|
+
let L = !1;
|
|
248
|
+
return (async () => {
|
|
249
|
+
const q = /* @__PURE__ */ new Map();
|
|
250
|
+
for (let V = 0; V < c.length && !L; V += Q) {
|
|
251
|
+
const re = c.slice(V, V + Q);
|
|
252
|
+
try {
|
|
253
|
+
const W = await w(re, xe);
|
|
254
|
+
if (L) break;
|
|
255
|
+
W.forEach((ae, ne) => q.set(ne, ae)), f(new Map(q));
|
|
256
|
+
} catch (W) {
|
|
257
|
+
console.warn("Thumbnail batch load failed:", W);
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
})(), () => {
|
|
262
|
+
L = !0;
|
|
263
|
+
};
|
|
264
|
+
}, [d, P, f, T]), {
|
|
265
|
+
file: P,
|
|
266
|
+
isLoading: l,
|
|
267
|
+
isError: z,
|
|
268
|
+
isSupportedFile: k,
|
|
269
|
+
activeHighlightIndex: b,
|
|
270
|
+
zoom: I,
|
|
271
|
+
setZoom: A,
|
|
272
|
+
zoomSelectOptions: $,
|
|
273
|
+
viewerApiRef: o,
|
|
274
|
+
showLoader: !z && (!!l || m),
|
|
275
|
+
changeIndex: M,
|
|
276
|
+
handleZoomChange: R,
|
|
277
|
+
handleViewerReady: U
|
|
278
|
+
};
|
|
279
|
+
}, Ae = ({
|
|
280
|
+
fileUrl: t,
|
|
281
|
+
fileName: a,
|
|
282
|
+
errorLabel: i,
|
|
283
|
+
occurrencesLabel: n,
|
|
284
|
+
unsupportedLabel: u,
|
|
285
|
+
highlights: s,
|
|
286
|
+
loadFileCb: d,
|
|
287
|
+
showOccurrences: f = !0,
|
|
288
|
+
selectedPageNumber: g,
|
|
289
|
+
containerClassName: m,
|
|
290
|
+
pdfViewerClassName: h,
|
|
291
|
+
onTotalPagesChange: r,
|
|
292
|
+
title: o,
|
|
293
|
+
thumbnailPageNumbers: T,
|
|
294
|
+
onThumbnailsLoaded: _,
|
|
295
|
+
onViewerReady: b,
|
|
296
|
+
showLoaderOverlay: D = !1,
|
|
297
|
+
selectedPages: z
|
|
298
|
+
}) => {
|
|
299
|
+
const {
|
|
300
|
+
file: N,
|
|
301
|
+
isError: l,
|
|
302
|
+
isSupportedFile: p,
|
|
303
|
+
activeHighlightIndex: P,
|
|
304
|
+
zoom: x,
|
|
305
|
+
setZoom: I,
|
|
306
|
+
zoomSelectOptions: A,
|
|
307
|
+
showLoader: k,
|
|
308
|
+
changeIndex: M,
|
|
309
|
+
handleZoomChange: R,
|
|
310
|
+
handleViewerReady: $
|
|
311
|
+
} = Oe({
|
|
312
|
+
fileUrl: t,
|
|
313
|
+
fileName: a,
|
|
314
|
+
loadFileCb: d,
|
|
315
|
+
highlights: s,
|
|
316
|
+
selectedPageNumber: g,
|
|
317
|
+
onTotalPagesChange: r,
|
|
318
|
+
thumbnailPageNumbers: T,
|
|
319
|
+
onThumbnailsLoaded: _,
|
|
320
|
+
onViewerReady: b,
|
|
321
|
+
showLoaderOverlay: D,
|
|
322
|
+
selectedPages: z
|
|
323
|
+
});
|
|
324
|
+
return /* @__PURE__ */ v(
|
|
325
|
+
"div",
|
|
326
|
+
{
|
|
327
|
+
className: S(
|
|
328
|
+
"flex flex-col self-center gap-4 py-3 px-6 h-full w-full bg-layer-2",
|
|
329
|
+
m
|
|
330
|
+
),
|
|
331
|
+
children: [
|
|
332
|
+
a && /* @__PURE__ */ e(
|
|
333
|
+
K,
|
|
334
|
+
{
|
|
335
|
+
className: "dial-tiny-text text-secondary",
|
|
336
|
+
text: a
|
|
337
|
+
}
|
|
338
|
+
),
|
|
339
|
+
/* @__PURE__ */ v(
|
|
340
|
+
"div",
|
|
341
|
+
{
|
|
342
|
+
className: S(
|
|
343
|
+
"flex w-full justify-between items-center",
|
|
344
|
+
f && o ? "gap-2" : ""
|
|
345
|
+
),
|
|
346
|
+
children: [
|
|
347
|
+
/* @__PURE__ */ e("div", { children: f ? /* @__PURE__ */ v("div", { className: "flex gap-1 py-0.5 px-1.5 min-h-[26px] items-center bg-layer-4 rounded w-fit", children: [
|
|
348
|
+
/* @__PURE__ */ v("span", { className: "dial-small-text", children: [
|
|
349
|
+
n ?? "Occurrences",
|
|
350
|
+
":",
|
|
351
|
+
/* @__PURE__ */ e("span", { className: "pl-0.5 min-w-3 inline-block text-right", children: s.length > 0 ? `${P + 1}/${s.length}` : "0" })
|
|
352
|
+
] }),
|
|
353
|
+
s.length > 0 && /* @__PURE__ */ v(le, { children: [
|
|
354
|
+
/* @__PURE__ */ e(
|
|
355
|
+
j,
|
|
356
|
+
{
|
|
357
|
+
className: "p-0 text-primary !h-6.5",
|
|
358
|
+
size: B.Small,
|
|
359
|
+
iconAfter: /* @__PURE__ */ e(fe, { size: 16 }),
|
|
360
|
+
"aria-label": "Next occurrence",
|
|
361
|
+
onClick: () => M(1)
|
|
362
|
+
}
|
|
363
|
+
),
|
|
364
|
+
/* @__PURE__ */ e(
|
|
365
|
+
j,
|
|
366
|
+
{
|
|
367
|
+
className: "p-0 text-primary !h-6.5",
|
|
368
|
+
size: B.Small,
|
|
369
|
+
iconAfter: /* @__PURE__ */ e(pe, { size: 16 }),
|
|
370
|
+
"aria-label": "Previous occurrence",
|
|
371
|
+
onClick: () => M(-1)
|
|
372
|
+
}
|
|
373
|
+
)
|
|
374
|
+
] })
|
|
375
|
+
] }) : /* @__PURE__ */ e("div", {}) }),
|
|
376
|
+
/* @__PURE__ */ v(
|
|
377
|
+
"div",
|
|
378
|
+
{
|
|
379
|
+
className: S(
|
|
380
|
+
"flex flex-1 items-center gap-4 min-w-0",
|
|
381
|
+
o != null && o !== "" ? "justify-between" : "justify-end"
|
|
382
|
+
),
|
|
383
|
+
children: [
|
|
384
|
+
o != null && o !== "" && /* @__PURE__ */ e(
|
|
385
|
+
K,
|
|
386
|
+
{
|
|
387
|
+
text: o,
|
|
388
|
+
className: "dial-body-semi-text text-primary min-w-0"
|
|
389
|
+
}
|
|
390
|
+
),
|
|
391
|
+
/* @__PURE__ */ v("div", { className: "flex gap-0.5 items-center shrink-0", children: [
|
|
392
|
+
/* @__PURE__ */ e("div", { className: "h-6", children: /* @__PURE__ */ e(
|
|
393
|
+
oe,
|
|
394
|
+
{
|
|
395
|
+
size: se.Sm,
|
|
396
|
+
className: "bg-layer-4 !py-0 h-6 min-h-6 dial-small-text max-h-full border-transparent hover:border-hover rounded",
|
|
397
|
+
options: A,
|
|
398
|
+
value: x,
|
|
399
|
+
onChange: (U) => I(U)
|
|
400
|
+
}
|
|
401
|
+
) }),
|
|
402
|
+
/* @__PURE__ */ e(
|
|
403
|
+
j,
|
|
404
|
+
{
|
|
405
|
+
className: "py-0 flex items-center justify-center rounded text-primary bg-layer-4 border border-transparent hover:border-hover",
|
|
406
|
+
iconAfter: /* @__PURE__ */ e(ve, { size: 16 }),
|
|
407
|
+
"aria-label": "Zoom out",
|
|
408
|
+
size: B.Small,
|
|
409
|
+
onClick: () => R(-1)
|
|
410
|
+
}
|
|
411
|
+
),
|
|
412
|
+
/* @__PURE__ */ e(
|
|
413
|
+
j,
|
|
414
|
+
{
|
|
415
|
+
className: "py-0 flex items-center justify-center rounded text-primary bg-layer-4 border border-transparent hover:border-hover",
|
|
416
|
+
iconAfter: /* @__PURE__ */ e(be, { size: 16 }),
|
|
417
|
+
"aria-label": "Zoom in",
|
|
418
|
+
size: B.Small,
|
|
419
|
+
onClick: () => R(1)
|
|
420
|
+
}
|
|
421
|
+
)
|
|
422
|
+
] })
|
|
423
|
+
]
|
|
424
|
+
}
|
|
425
|
+
)
|
|
426
|
+
]
|
|
427
|
+
}
|
|
428
|
+
),
|
|
429
|
+
/* @__PURE__ */ v("div", { className: "relative flex min-h-0 flex-1", children: [
|
|
430
|
+
N && /* @__PURE__ */ e(
|
|
431
|
+
ge,
|
|
432
|
+
{
|
|
433
|
+
pdf: N,
|
|
434
|
+
highlights: s,
|
|
435
|
+
selectedHighlightId: s[P]?.id,
|
|
436
|
+
selectedPageNumber: g,
|
|
437
|
+
zoom: x,
|
|
438
|
+
onTotalPagesChange: r,
|
|
439
|
+
onViewerReady: $,
|
|
440
|
+
containerClassName: h,
|
|
441
|
+
selectedPages: z
|
|
442
|
+
}
|
|
443
|
+
),
|
|
444
|
+
k && /* @__PURE__ */ e(Ne, { className: "absolute inset-0 z-10" }),
|
|
445
|
+
l && /* @__PURE__ */ e("div", { className: "absolute inset-0 z-20 flex items-center justify-center bg-layer-3", children: /* @__PURE__ */ v("div", { className: "flex flex-col items-center gap-6 p-10", children: [
|
|
446
|
+
/* @__PURE__ */ e(
|
|
447
|
+
he,
|
|
448
|
+
{
|
|
449
|
+
size: 60,
|
|
450
|
+
stroke: 2,
|
|
451
|
+
className: "text-secondary"
|
|
452
|
+
}
|
|
453
|
+
),
|
|
454
|
+
/* @__PURE__ */ e("div", { className: "text-center dial-small-text whitespace-pre-wrap", children: !p && u ? u : i ?? "Failed to load document." })
|
|
455
|
+
] }) })
|
|
456
|
+
] })
|
|
457
|
+
]
|
|
458
|
+
}
|
|
459
|
+
);
|
|
460
|
+
}, _e = 1200 * 1e3, De = 20, Me = ({
|
|
461
|
+
children: t,
|
|
462
|
+
ttlMs: a = _e,
|
|
463
|
+
maxEntries: i = De
|
|
464
|
+
}) => {
|
|
465
|
+
const n = F(/* @__PURE__ */ new Map()), u = O((m, h) => {
|
|
466
|
+
n.current.delete(m), n.current.set(m, h);
|
|
467
|
+
}, []), s = O(() => {
|
|
468
|
+
const m = Date.now();
|
|
469
|
+
for (const [h, r] of n.current.entries())
|
|
470
|
+
r.file && m - r.timestamp > a && n.current.delete(h);
|
|
471
|
+
}, [a]), d = O(() => {
|
|
472
|
+
for (; n.current.size > i; ) {
|
|
473
|
+
const m = n.current.keys().next().value;
|
|
474
|
+
m && n.current.delete(m);
|
|
475
|
+
}
|
|
476
|
+
}, [i]), f = O(() => {
|
|
477
|
+
n.current.clear();
|
|
478
|
+
}, []), g = O(
|
|
479
|
+
async (m, h) => {
|
|
480
|
+
s();
|
|
481
|
+
const r = n.current.get(m);
|
|
482
|
+
if (r) {
|
|
483
|
+
if (u(m, { ...r, timestamp: Date.now() }), r.file)
|
|
484
|
+
return r.file;
|
|
485
|
+
if (r.promise)
|
|
486
|
+
return r.promise;
|
|
487
|
+
}
|
|
488
|
+
const o = Date.now(), T = h().then((b) => {
|
|
489
|
+
const D = { file: b, timestamp: Date.now() };
|
|
490
|
+
return u(m, D), d(), b;
|
|
491
|
+
}).catch((b) => {
|
|
492
|
+
throw n.current.delete(m), b;
|
|
493
|
+
});
|
|
494
|
+
return u(m, { promise: T, timestamp: o }), d(), T;
|
|
495
|
+
},
|
|
496
|
+
[d, s, u]
|
|
497
|
+
);
|
|
498
|
+
return y(() => () => f(), [f]), /* @__PURE__ */ e(te.Provider, { value: { getFile: g, clearCache: f }, children: t });
|
|
499
|
+
}, Re = ({
|
|
500
|
+
pageNum: t,
|
|
501
|
+
onSelectPage: a,
|
|
502
|
+
isSelected: i,
|
|
503
|
+
isLoading: n,
|
|
504
|
+
isMultiselect: u,
|
|
505
|
+
thumbnailUrl: s,
|
|
506
|
+
className: d
|
|
507
|
+
}) => /* @__PURE__ */ v(
|
|
508
|
+
"button",
|
|
509
|
+
{
|
|
510
|
+
type: "button",
|
|
511
|
+
"aria-label": `Page ${t}`,
|
|
512
|
+
"aria-pressed": i,
|
|
513
|
+
onClick: (f) => {
|
|
514
|
+
u && f.target.closest("[data-checkbox-wrapper]") || a(t);
|
|
515
|
+
},
|
|
516
|
+
className: S(
|
|
517
|
+
"flex flex-col gap-2 p-2 h-[186px] w-[120px] items-center justify-center cursor-pointer transition-colors hover:bg-layer-4 rounded-sm",
|
|
518
|
+
i && (u ? "border border-accent-primary bg-accent-primary-alpha" : "border border-primary"),
|
|
519
|
+
d
|
|
520
|
+
),
|
|
521
|
+
children: [
|
|
522
|
+
/* @__PURE__ */ v("div", { className: "relative w-[104px] h-[146px] bg-model-icon rounded-sm overflow-hidden", children: [
|
|
523
|
+
s ? /* @__PURE__ */ e(
|
|
524
|
+
"img",
|
|
525
|
+
{
|
|
526
|
+
src: s,
|
|
527
|
+
alt: `Page ${t} thumbnail`,
|
|
528
|
+
className: "w-full h-full object-cover rounded-sm"
|
|
529
|
+
}
|
|
530
|
+
) : null,
|
|
531
|
+
u && /* @__PURE__ */ e("div", { className: "absolute left-1 top-1 z-10", "data-checkbox-wrapper": !0, children: /* @__PURE__ */ e(
|
|
532
|
+
ce,
|
|
533
|
+
{
|
|
534
|
+
id: `page-thumbnail-${t}`,
|
|
535
|
+
checked: i,
|
|
536
|
+
onChange: () => a(t),
|
|
537
|
+
className: "before:border-primary before:bg-model-icon bg-model-icon"
|
|
538
|
+
}
|
|
539
|
+
) }),
|
|
540
|
+
n && /* @__PURE__ */ e("div", { className: "absolute inset-0 flex items-center justify-center bg-blackout rounded-sm", children: /* @__PURE__ */ e(
|
|
541
|
+
ie,
|
|
542
|
+
{
|
|
543
|
+
size: 24,
|
|
544
|
+
fullWidth: !1,
|
|
545
|
+
ariaLabel: `Loading page ${t}`
|
|
546
|
+
}
|
|
547
|
+
) })
|
|
548
|
+
] }),
|
|
549
|
+
/* @__PURE__ */ e("span", { className: "dial-tiny-text text-primary", children: t })
|
|
550
|
+
]
|
|
551
|
+
}
|
|
552
|
+
);
|
|
553
|
+
export {
|
|
554
|
+
H as AUTO_ZOOM_ID,
|
|
555
|
+
Ae as DocumentPreview,
|
|
556
|
+
Me as DocumentPreviewCacheProvider,
|
|
557
|
+
G as FIT_ZOOM_ID,
|
|
558
|
+
ge as PDFViewer,
|
|
559
|
+
Re as PageThumbnail,
|
|
560
|
+
Ne as PdfPreviewLoader,
|
|
561
|
+
Q as THUMBNAIL_BATCH_SIZE,
|
|
562
|
+
xe as THUMBNAIL_IMAGE_OPTIONS,
|
|
563
|
+
X as ZOOM_OPTIONS,
|
|
564
|
+
Ce as getStepZoomOptionValue,
|
|
565
|
+
Pe as isPdfFile,
|
|
566
|
+
Oe as useDocumentPreview,
|
|
567
|
+
Ie as useDocumentPreviewCache
|
|
568
|
+
};
|
package/dist/index.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.visible{visibility:visible}.static{position:static}.absolute{position:absolute}.relative{position:relative}.inset-0{inset:0}.left-1{left:.25rem}.top-1{top:.25rem}.z-10{z-index:10}.z-20{z-index:20}.mx-auto{margin-left:auto;margin-right:auto}.inline-block{display:inline-block}.flex{display:flex}.h-4{height:1rem}.h-6{height:1.5rem}.h-\[146px\]{height:146px}.h-\[186px\]{height:186px}.h-\[600px\]{height:600px}.h-\[700px\]{height:700px}.h-full{height:100%}.max-h-full{max-height:100%}.min-h-0{min-height:0}.min-h-6{min-height:1.5rem}.min-h-\[26px\]{min-height:26px}.w-\[104px\]{width:104px}.w-\[120px\]{width:120px}.w-\[208px\]{width:208px}.w-\[28\%\]{width:28%}.w-\[500px\]{width:500px}.w-\[58\%\]{width:58%}.w-fit{width:-moz-fit-content;width:fit-content}.w-full{width:100%}.min-w-0{min-width:0}.min-w-3{min-width:.75rem}.max-w-\[320px\]{max-width:320px}.max-w-\[40\%\]{max-width:40%}.max-w-\[520px\]{max-width:520px}.flex-1{flex:1 1 0%}.flex-shrink-0,.shrink-0{flex-shrink:0}.grow{flex-grow:1}@keyframes skeleton-wave{0%{background-position:200% 0}to{background-position:-200% 0}}.animate-skeleton-wave{animation:skeleton-wave 3.5s ease-in-out infinite}.cursor-pointer{cursor:pointer}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-10{gap:2.5rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.self-center{align-self:center}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.whitespace-pre-wrap{white-space:pre-wrap}.rounded{border-radius:4px}.rounded-\[120px\]{border-radius:120px}.rounded-sm{border-radius:.125rem}.border{border-width:1px}.border-accent-primary{border-color:var(--stroke-accent-primary,#7da4ff)}.border-primary{border-color:var(--stroke-primary,#696e7c)}.border-transparent{border-color:transparent}.bg-accent-primary-alpha{background-color:var(--bg-accent-primary-alpha,#7da4ff26)}.bg-blackout{background-color:var(--bg-blackout,#0c101db3)}.bg-layer-1{background-color:var(--bg-layer-1,#0c101d)}.bg-layer-2{background-color:var(--bg-layer-2,#161b2d)}.bg-layer-3{background-color:var(--bg-layer-3,#1d2439)}.bg-layer-4{background-color:var(--bg-layer-4,#242c42)}.bg-model-icon{background-color:var(--bg-model-icon,#fff)}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.from-layer-4{--tw-gradient-from:var(--bg-layer-4,#242c42) var(--tw-gradient-from-position);--tw-gradient-to:hsla(0,0%,100%,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.via-layer-3{--tw-gradient-to:hsla(0,0%,100%,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--bg-layer-3,#1d2439) var(--tw-gradient-via-position),var(--tw-gradient-to)}.to-layer-4{--tw-gradient-to:var(--bg-layer-4,#242c42) var(--tw-gradient-to-position)}.bg-\[length\:200\%_100\%\]{background-size:200% 100%}.object-cover{-o-object-fit:cover;object-fit:cover}.p-0{padding:0}.p-10{padding:2.5rem}.p-2{padding:.5rem}.p-4{padding:1rem}.\!py-0{padding-top:0!important;padding-bottom:0!important}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-\[60px\]{padding-left:60px;padding-right:60px}.py-0{padding-top:0;padding-bottom:0}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.pl-0\.5{padding-left:.125rem}.pt-10{padding-top:2.5rem}.text-center{text-align:center}.text-right{text-align:right}.text-xs{font-size:.75rem;line-height:1rem}.text-primary{color:var(--text-primary,#eef1f7)}.text-secondary{color:var(--text-secondary,#9fa6bd)}.outline{outline-style:solid}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.before\:border-primary:before{content:var(--tw-content);border-color:var(--stroke-primary,#696e7c)}.before\:bg-model-icon:before{content:var(--tw-content);background-color:var(--bg-model-icon,#fff)}.hover\:border-hover:hover{border-color:var(--stroke-hover,#eef1f7)}.hover\:bg-layer-4:hover{background-color:var(--bg-layer-4,#242c42)}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { InputHighlightData } from '@epam/pdf-highlighter-kit';
|
|
2
|
+
import { FC, ReactNode } from 'react';
|
|
3
|
+
import { PdfViewerApi } from '../../models/pdf-viewer.models';
|
|
4
|
+
/**
|
|
5
|
+
* Props for the high-level document preview component.
|
|
6
|
+
*
|
|
7
|
+
* The component handles file loading, optional cache integration, toolbar controls,
|
|
8
|
+
* highlight navigation, and thumbnail generation.
|
|
9
|
+
*/
|
|
10
|
+
export interface DocumentPreviewProps {
|
|
11
|
+
/** URL passed to {@link loadFileCb} to fetch the PDF file. */
|
|
12
|
+
fileUrl: string;
|
|
13
|
+
/** Display name used for PDF type detection. Falls back to {@link fileUrl}. */
|
|
14
|
+
fileName?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Caller-provided file loader.
|
|
17
|
+
* If the cache provider is mounted, this callback is used as the cache miss loader.
|
|
18
|
+
*/
|
|
19
|
+
loadFileCb: (url: string) => Promise<Blob>;
|
|
20
|
+
/** Highlight data rendered by the underlying PDF viewer. */
|
|
21
|
+
highlights: InputHighlightData[];
|
|
22
|
+
/** Custom label shown when file loading fails. */
|
|
23
|
+
errorLabel?: ReactNode;
|
|
24
|
+
/** Label used before the highlights occurrence counter. */
|
|
25
|
+
occurrencesLabel?: ReactNode;
|
|
26
|
+
/** Label shown when the file type is not a supported PDF. */
|
|
27
|
+
unsupportedLabel?: ReactNode;
|
|
28
|
+
/** Show occurrence counter in the toolbar. Default: `true`. */
|
|
29
|
+
showOccurrences?: boolean;
|
|
30
|
+
/** Navigate viewer to this page when provided. */
|
|
31
|
+
selectedPageNumber?: number;
|
|
32
|
+
/** Additional class name for the inner {@link PDFViewer} container. */
|
|
33
|
+
pdfViewerClassName?: string;
|
|
34
|
+
/** Additional class name for the outer wrapper container. */
|
|
35
|
+
containerClassName?: string;
|
|
36
|
+
/** Callback invoked when total page count is known. */
|
|
37
|
+
onTotalPagesChange?: (totalPages: number) => void;
|
|
38
|
+
/** Optional title shown in the toolbar center. */
|
|
39
|
+
title?: string;
|
|
40
|
+
/** Page numbers for which thumbnails should be generated in batches. */
|
|
41
|
+
thumbnailPageNumbers?: number[];
|
|
42
|
+
/** Called incrementally after each thumbnail batch resolves. */
|
|
43
|
+
onThumbnailsLoaded?: (map: Map<number, string>) => void;
|
|
44
|
+
/** Called when the underlying viewer is ready and its imperative API is available. */
|
|
45
|
+
onViewerReady?: (api: PdfViewerApi) => void;
|
|
46
|
+
/** Forces the loading overlay visible while parent-level work is in progress. */
|
|
47
|
+
showLoaderOverlay?: boolean;
|
|
48
|
+
/** Restricts viewer loading to selected pages only. */
|
|
49
|
+
selectedPages?: number[];
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* High-level PDF preview component with toolbar, highlight navigation, zoom controls,
|
|
53
|
+
* loading and error overlays, and optional thumbnail generation.
|
|
54
|
+
*/
|
|
55
|
+
export declare const DocumentPreview: FC<DocumentPreviewProps>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Props for a selectable page thumbnail tile.
|
|
4
|
+
*/
|
|
5
|
+
export interface PageThumbnailProps {
|
|
6
|
+
/** 1-based page number displayed by the tile. */
|
|
7
|
+
pageNum: number;
|
|
8
|
+
/** Called when the tile or its checkbox selects/deselects the page. */
|
|
9
|
+
onSelectPage: (pageNum: number) => void;
|
|
10
|
+
/** Whether this page is currently selected. */
|
|
11
|
+
isSelected: boolean;
|
|
12
|
+
/** Whether thumbnail content is currently loading. */
|
|
13
|
+
isLoading: boolean;
|
|
14
|
+
/** Enables checkbox-driven multi-select mode. */
|
|
15
|
+
isMultiselect?: boolean;
|
|
16
|
+
/** Optional thumbnail image URL for the page. */
|
|
17
|
+
thumbnailUrl?: string | null;
|
|
18
|
+
/** Optional additional class name for the root element. */
|
|
19
|
+
className?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Page thumbnail tile with optional multi-select support and loading overlay.
|
|
23
|
+
*/
|
|
24
|
+
export declare const PageThumbnail: FC<PageThumbnailProps>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Props for the PDF preview loading skeleton overlay.
|
|
4
|
+
*/
|
|
5
|
+
export interface PdfPreviewLoaderProps {
|
|
6
|
+
/** Additional class name for the loader wrapper. */
|
|
7
|
+
className?: string;
|
|
8
|
+
/** Accessible label announced by assistive technologies. */
|
|
9
|
+
ariaLabel?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Skeleton placeholder used while PDF content is loading.
|
|
13
|
+
*/
|
|
14
|
+
export declare const PdfPreviewLoader: FC<PdfPreviewLoaderProps>;
|