@algenium/blocks 1.7.0-rc.2 → 1.7.0-rc.4

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/index.cjs CHANGED
@@ -8131,6 +8131,746 @@ function USAddressInput({
8131
8131
  ] })
8132
8132
  ] });
8133
8133
  }
8134
+ function defaultPageOf(page, total) {
8135
+ return `${page} / ${total}`;
8136
+ }
8137
+ var DEFAULT_WORKER_MSG = "PdfViewer requires a non-empty workerSrc pointing at pdf.worker matching your project's pdfjs-dist version. Example: `new URL('pdfjs-dist/build/pdf.worker.min.mjs', import.meta.url)`.";
8138
+ function buildGetDocumentArgs(src, documentOptions) {
8139
+ const rest = documentOptions ?? {};
8140
+ if (typeof src === "string") {
8141
+ return { url: src, ...rest };
8142
+ }
8143
+ return { data: src, ...rest };
8144
+ }
8145
+ function PdfPageCanvas({
8146
+ page,
8147
+ layout,
8148
+ scale,
8149
+ visible,
8150
+ pageClassName
8151
+ }) {
8152
+ const canvasRef = React2.useRef(null);
8153
+ const taskRef = React2.useRef(null);
8154
+ React2.useEffect(() => {
8155
+ if (!visible || !canvasRef.current) {
8156
+ taskRef.current?.cancel();
8157
+ taskRef.current = null;
8158
+ const c = canvasRef.current;
8159
+ if (c) {
8160
+ const ctx2 = c.getContext("2d");
8161
+ if (ctx2) {
8162
+ ctx2.setTransform(1, 0, 0, 1, 0, 0);
8163
+ ctx2.clearRect(0, 0, c.width, c.height);
8164
+ }
8165
+ }
8166
+ return;
8167
+ }
8168
+ const canvas = canvasRef.current;
8169
+ const ctx = canvas.getContext("2d");
8170
+ if (!ctx) return;
8171
+ const viewport = page.getViewport({ scale });
8172
+ canvas.width = viewport.width;
8173
+ canvas.height = viewport.height;
8174
+ const task = page.render({
8175
+ canvasContext: ctx,
8176
+ viewport,
8177
+ canvas
8178
+ });
8179
+ taskRef.current = task;
8180
+ task.promise.catch(() => {
8181
+ });
8182
+ return () => {
8183
+ task.cancel();
8184
+ if (taskRef.current === task) {
8185
+ taskRef.current = null;
8186
+ }
8187
+ };
8188
+ }, [page, visible, scale]);
8189
+ const w = layout.widthPt * scale;
8190
+ const h = layout.heightPt * scale;
8191
+ return /* @__PURE__ */ jsxRuntime.jsx(
8192
+ "div",
8193
+ {
8194
+ className: cn("relative flex justify-center bg-muted/40", pageClassName),
8195
+ style: { width: w, height: h },
8196
+ "data-page": layout.pageNumber,
8197
+ children: visible ? /* @__PURE__ */ jsxRuntime.jsx(
8198
+ "canvas",
8199
+ {
8200
+ ref: canvasRef,
8201
+ className: "block max-h-full shadow-sm",
8202
+ "aria-hidden": "true"
8203
+ }
8204
+ ) : null
8205
+ }
8206
+ );
8207
+ }
8208
+ var PAGE_INPUT_CLASS = "h-8 w-14 rounded-md border px-2 text-sm tabular-nums shadow-sm border-neutral-300 bg-white text-neutral-950 caret-neutral-950 selection:bg-neutral-200 selection:text-neutral-950 dark:border-neutral-600 dark:bg-neutral-950 dark:text-neutral-50 dark:caret-neutral-50 dark:selection:bg-neutral-700 dark:selection:text-neutral-50";
8209
+ function touchPairDistance(touches) {
8210
+ if (touches.length < 2) return 0;
8211
+ const a = touches.item(0);
8212
+ const b = touches.item(1);
8213
+ return Math.hypot(a.clientX - b.clientX, a.clientY - b.clientY);
8214
+ }
8215
+ function PdfThumbnailStrip({
8216
+ page,
8217
+ pageNumber,
8218
+ active,
8219
+ onPick,
8220
+ label
8221
+ }) {
8222
+ const canvasRef = React2.useRef(null);
8223
+ React2.useEffect(() => {
8224
+ const canvas = canvasRef.current;
8225
+ if (!canvas) return;
8226
+ const viewport = page.getViewport({ scale: 1 });
8227
+ const thumbMax = 112;
8228
+ const s = thumbMax / Math.max(viewport.width, viewport.height);
8229
+ const vp = page.getViewport({ scale: s });
8230
+ const ctx = canvas.getContext("2d");
8231
+ if (!ctx) return;
8232
+ canvas.width = vp.width;
8233
+ canvas.height = vp.height;
8234
+ const task = page.render({
8235
+ canvasContext: ctx,
8236
+ viewport: vp,
8237
+ canvas
8238
+ });
8239
+ task.promise.catch(() => {
8240
+ });
8241
+ return () => {
8242
+ task.cancel();
8243
+ };
8244
+ }, [page]);
8245
+ return /* @__PURE__ */ jsxRuntime.jsxs(
8246
+ "button",
8247
+ {
8248
+ type: "button",
8249
+ onClick: () => onPick(pageNumber),
8250
+ className: cn(
8251
+ "flex w-full flex-col items-center gap-1 rounded-md p-1 transition-colors hover:bg-muted/80",
8252
+ active ? "bg-muted ring-2 ring-ring" : ""
8253
+ ),
8254
+ "aria-label": label,
8255
+ "aria-current": active ? "page" : void 0,
8256
+ children: [
8257
+ /* @__PURE__ */ jsxRuntime.jsx(
8258
+ "canvas",
8259
+ {
8260
+ ref: canvasRef,
8261
+ className: "block max-w-full rounded-sm shadow-sm",
8262
+ "aria-hidden": true
8263
+ }
8264
+ ),
8265
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground text-[10px] tabular-nums", children: pageNumber })
8266
+ ]
8267
+ }
8268
+ );
8269
+ }
8270
+ function PdfViewer({
8271
+ src,
8272
+ workerSrc,
8273
+ documentOptions,
8274
+ initialPage = 1,
8275
+ initialScale = 1,
8276
+ minScale = 0.5,
8277
+ maxScale = 4,
8278
+ scaleStep = 0.2,
8279
+ enableKeyboardShortcuts = true,
8280
+ enablePinchZoom = true,
8281
+ enableThumbnailSidebar = true,
8282
+ thumbnailSidebarDefaultOpen = true,
8283
+ labels,
8284
+ onLoad,
8285
+ onError,
8286
+ onPageChange,
8287
+ onScaleChange,
8288
+ className,
8289
+ toolbarClassName,
8290
+ pageClassName,
8291
+ toolbarEndSlot
8292
+ }) {
8293
+ const mergedLabels = labels ?? {};
8294
+ const toolbarLabel = mergedLabels.toolbar ?? "PDF controls";
8295
+ const loadingLabel = mergedLabels.loading ?? "Loading PDF\u2026";
8296
+ const workerErrorMessage = mergedLabels.workerMissing ?? DEFAULT_WORKER_MSG;
8297
+ const errorLabelFallback = mergedLabels.error ?? "Failed to load PDF";
8298
+ const toolbarId = React2.useId();
8299
+ const pageInputId = `${toolbarId}-page`;
8300
+ const scrollRef = React2.useRef(null);
8301
+ const observerRef = React2.useRef(null);
8302
+ const lastReportedPage = React2.useRef(0);
8303
+ const loadedPdfRef = React2.useRef(null);
8304
+ const [loadError, setLoadError] = React2.useState(null);
8305
+ const [busy, setBusy] = React2.useState(false);
8306
+ const [layouts, setLayouts] = React2.useState([]);
8307
+ const [pagesMap, setPagesMap] = React2.useState(
8308
+ null
8309
+ );
8310
+ const [scale, setScale] = React2.useState(initialScale);
8311
+ const [ratios, setRatios] = React2.useState({});
8312
+ const [pageInput, setPageInput] = React2.useState(String(initialPage));
8313
+ const [thumbnailSidebarOpen, setThumbnailSidebarOpen] = React2.useState(
8314
+ thumbnailSidebarDefaultOpen
8315
+ );
8316
+ const baselineScaleRef = React2.useRef(initialScale);
8317
+ React2.useEffect(() => {
8318
+ baselineScaleRef.current = initialScale;
8319
+ }, [initialScale]);
8320
+ const pageOfFormatter = mergedLabels.pageOf ?? defaultPageOf;
8321
+ const resolvedGoToLabel = mergedLabels.goToPage ?? "Go to page";
8322
+ const zoomInLabel = mergedLabels.zoomIn ?? "Zoom in";
8323
+ const zoomOutLabel = mergedLabels.zoomOut ?? "Zoom out";
8324
+ const resetZoomLabel = mergedLabels.resetZoom ?? "Reset zoom";
8325
+ const fitWidthLabel = mergedLabels.fitToWidth ?? "Fit to width";
8326
+ const thumbnailSidebarLabel = mergedLabels.thumbnailSidebar ?? "Page thumbnails";
8327
+ const toggleThumbLabel = mergedLabels.toggleThumbnailSidebar ?? "Toggle page thumbnails";
8328
+ const pageThumbLabel = mergedLabels.pageThumbnail ?? ((n) => `Page ${n}, show in document`);
8329
+ const pageCount = layouts.length;
8330
+ const currentVisiblePage = React2.useMemo(() => {
8331
+ let bestPage = 1;
8332
+ let bestRatio = -1;
8333
+ for (const layout of layouts) {
8334
+ const r2 = ratios[layout.pageNumber] ?? 0;
8335
+ if (r2 > bestRatio) {
8336
+ bestRatio = r2;
8337
+ bestPage = layout.pageNumber;
8338
+ }
8339
+ }
8340
+ return bestRatio >= 0.01 ? bestPage : 1;
8341
+ }, [layouts, ratios]);
8342
+ const clampScaleValue = React2.useCallback(
8343
+ (v) => Math.min(maxScale, Math.max(minScale, v)),
8344
+ [minScale, maxScale]
8345
+ );
8346
+ const zoomInAct = React2.useCallback(() => {
8347
+ setScale((prev) => {
8348
+ const c = clampScaleValue(prev + scaleStep);
8349
+ onScaleChange?.(c);
8350
+ return c;
8351
+ });
8352
+ }, [clampScaleValue, onScaleChange, scaleStep]);
8353
+ const zoomOutAct = React2.useCallback(() => {
8354
+ setScale((prev) => {
8355
+ const c = clampScaleValue(prev - scaleStep);
8356
+ onScaleChange?.(c);
8357
+ return c;
8358
+ });
8359
+ }, [clampScaleValue, onScaleChange, scaleStep]);
8360
+ const resetZoomAct = React2.useCallback(() => {
8361
+ const c = clampScaleValue(baselineScaleRef.current);
8362
+ setScale(c);
8363
+ onScaleChange?.(c);
8364
+ }, [clampScaleValue, onScaleChange]);
8365
+ const fitToWidthAct = React2.useCallback(() => {
8366
+ const root = scrollRef.current;
8367
+ if (!root || layouts.length === 0) return;
8368
+ const avail = Math.max(root.clientWidth - 32, 120);
8369
+ const maxW = Math.max(...layouts.map((l) => l.widthPt), 1);
8370
+ const next = clampScaleValue(avail / maxW);
8371
+ setScale(next);
8372
+ onScaleChange?.(next);
8373
+ }, [clampScaleValue, layouts, onScaleChange]);
8374
+ React2.useEffect(() => {
8375
+ setPageInput(String(currentVisiblePage));
8376
+ if (lastReportedPage.current !== currentVisiblePage) {
8377
+ lastReportedPage.current = currentVisiblePage;
8378
+ onPageChange?.(currentVisiblePage);
8379
+ }
8380
+ }, [currentVisiblePage, onPageChange]);
8381
+ const scrollToPage = React2.useCallback((pageNum) => {
8382
+ const root = scrollRef.current;
8383
+ if (!root || pageNum < 1) return;
8384
+ const el = root.querySelector(`[data-page="${pageNum}"]`);
8385
+ if (el instanceof HTMLElement) {
8386
+ el.scrollIntoView({ behavior: "smooth", block: "start" });
8387
+ }
8388
+ }, []);
8389
+ React2.useEffect(() => {
8390
+ if (!busy && pagesMap && pageCount > 0) {
8391
+ const p = Math.min(Math.max(1, initialPage), pageCount);
8392
+ requestAnimationFrame(() => scrollToPage(p));
8393
+ }
8394
+ }, [busy, pagesMap, pageCount, initialPage, scrollToPage]);
8395
+ const loadKey = React2.useMemo(() => {
8396
+ if (typeof src === "string") return src;
8397
+ if (src instanceof ArrayBuffer) return `ab:${src.byteLength}`;
8398
+ return `ua:${src.byteLength}:${src.byteOffset}:${src.buffer.byteLength}`;
8399
+ }, [src]);
8400
+ React2.useEffect(() => {
8401
+ let cancelled = false;
8402
+ let activeDoc = null;
8403
+ setLoadError(null);
8404
+ if (loadedPdfRef.current) {
8405
+ void loadedPdfRef.current.destroy();
8406
+ loadedPdfRef.current = null;
8407
+ }
8408
+ setLayouts([]);
8409
+ setPagesMap(null);
8410
+ setRatios({});
8411
+ const trimmedWorker = workerSrc?.trim?.() ?? "";
8412
+ if (!trimmedWorker) {
8413
+ setLoadError(workerErrorMessage);
8414
+ onError?.(new Error(workerErrorMessage));
8415
+ return () => {
8416
+ cancelled = true;
8417
+ };
8418
+ }
8419
+ void (async () => {
8420
+ try {
8421
+ setBusy(true);
8422
+ const pdfjs = await import('pdfjs-dist');
8423
+ if (cancelled) return;
8424
+ pdfjs.GlobalWorkerOptions.workerSrc = trimmedWorker;
8425
+ const loadingTask = pdfjs.getDocument(
8426
+ buildGetDocumentArgs(src, documentOptions)
8427
+ );
8428
+ activeDoc = await loadingTask.promise;
8429
+ if (cancelled) {
8430
+ void activeDoc.destroy();
8431
+ activeDoc = null;
8432
+ return;
8433
+ }
8434
+ const layoutsLocal = [];
8435
+ const mapLocal = /* @__PURE__ */ new Map();
8436
+ for (let p = 1; p <= activeDoc.numPages; p++) {
8437
+ const page = await activeDoc.getPage(p);
8438
+ mapLocal.set(p, page);
8439
+ const viewport = page.getViewport({ scale: 1 });
8440
+ layoutsLocal.push({
8441
+ pageNumber: p,
8442
+ widthPt: viewport.width,
8443
+ heightPt: viewport.height
8444
+ });
8445
+ }
8446
+ if (cancelled) {
8447
+ void activeDoc.destroy();
8448
+ activeDoc = null;
8449
+ return;
8450
+ }
8451
+ onLoad?.(activeDoc);
8452
+ loadedPdfRef.current = activeDoc;
8453
+ setLayouts(layoutsLocal);
8454
+ setPagesMap(mapLocal);
8455
+ activeDoc = null;
8456
+ } catch (e) {
8457
+ const err = e instanceof Error ? e : new Error(String(e));
8458
+ if (!cancelled) {
8459
+ setLoadError(errorLabelFallback);
8460
+ onError?.(err);
8461
+ }
8462
+ if (activeDoc) {
8463
+ void activeDoc.destroy();
8464
+ activeDoc = null;
8465
+ }
8466
+ } finally {
8467
+ if (!cancelled) setBusy(false);
8468
+ }
8469
+ })();
8470
+ return () => {
8471
+ cancelled = true;
8472
+ if (observerRef.current) {
8473
+ observerRef.current.disconnect();
8474
+ observerRef.current = null;
8475
+ }
8476
+ if (activeDoc) {
8477
+ void activeDoc.destroy();
8478
+ }
8479
+ if (loadedPdfRef.current) {
8480
+ void loadedPdfRef.current.destroy();
8481
+ loadedPdfRef.current = null;
8482
+ }
8483
+ };
8484
+ }, [workerSrc, loadKey, documentOptions]);
8485
+ React2.useEffect(() => {
8486
+ setScale(clampScaleValue(initialScale));
8487
+ }, [initialScale, clampScaleValue]);
8488
+ React2.useEffect(() => {
8489
+ const el = scrollRef.current;
8490
+ if (!el || !enablePinchZoom) return;
8491
+ let pinchDist = 0;
8492
+ const onWheel = (e) => {
8493
+ if (!e.ctrlKey) return;
8494
+ e.preventDefault();
8495
+ const delta = -e.deltaY;
8496
+ const factor = Math.exp(delta * 2e-3);
8497
+ setScale((prev) => {
8498
+ const c = clampScaleValue(prev * factor);
8499
+ onScaleChange?.(c);
8500
+ return c;
8501
+ });
8502
+ };
8503
+ const onTouchStart = (e) => {
8504
+ if (e.touches.length === 2) {
8505
+ pinchDist = touchPairDistance(e.touches);
8506
+ }
8507
+ };
8508
+ const onTouchMove = (e) => {
8509
+ if (e.touches.length !== 2 || pinchDist <= 0) return;
8510
+ e.preventDefault();
8511
+ const d = touchPairDistance(e.touches);
8512
+ const ratio = d / pinchDist;
8513
+ pinchDist = d;
8514
+ setScale((prev) => {
8515
+ const c = clampScaleValue(prev * ratio);
8516
+ onScaleChange?.(c);
8517
+ return c;
8518
+ });
8519
+ };
8520
+ const endPinch = (e) => {
8521
+ if (e.touches.length < 2) pinchDist = 0;
8522
+ };
8523
+ el.addEventListener("wheel", onWheel, { passive: false });
8524
+ el.addEventListener("touchstart", onTouchStart, { passive: true });
8525
+ el.addEventListener("touchmove", onTouchMove, { passive: false });
8526
+ el.addEventListener("touchend", endPinch);
8527
+ el.addEventListener("touchcancel", endPinch);
8528
+ return () => {
8529
+ el.removeEventListener("wheel", onWheel);
8530
+ el.removeEventListener("touchstart", onTouchStart);
8531
+ el.removeEventListener("touchmove", onTouchMove);
8532
+ el.removeEventListener("touchend", endPinch);
8533
+ el.removeEventListener("touchcancel", endPinch);
8534
+ };
8535
+ }, [enablePinchZoom, clampScaleValue, onScaleChange]);
8536
+ React2.useEffect(() => {
8537
+ if (layouts.length === 0) return;
8538
+ const frame = requestAnimationFrame(() => {
8539
+ const root = scrollRef.current;
8540
+ if (!root) return;
8541
+ if (observerRef.current) {
8542
+ observerRef.current.disconnect();
8543
+ }
8544
+ const io = new IntersectionObserver(
8545
+ (entries) => {
8546
+ setRatios((prev) => {
8547
+ const next = { ...prev };
8548
+ for (const e of entries) {
8549
+ const tgt = e.target;
8550
+ const p = Number(tgt.dataset.page);
8551
+ if (Number.isFinite(p)) next[p] = e.intersectionRatio;
8552
+ }
8553
+ return next;
8554
+ });
8555
+ },
8556
+ {
8557
+ root,
8558
+ rootMargin: "200px 0px",
8559
+ threshold: [0, 0.05, 0.25, 0.5, 0.75, 1]
8560
+ }
8561
+ );
8562
+ observerRef.current = io;
8563
+ root.querySelectorAll("[data-page]").forEach((el) => io.observe(el));
8564
+ });
8565
+ return () => {
8566
+ cancelAnimationFrame(frame);
8567
+ if (observerRef.current) {
8568
+ observerRef.current.disconnect();
8569
+ observerRef.current = null;
8570
+ }
8571
+ };
8572
+ }, [layouts]);
8573
+ const handleSubmitPage = React2.useCallback(() => {
8574
+ const n = Number.parseInt(pageInput, 10);
8575
+ if (!Number.isFinite(n) || pageCount === 0) return;
8576
+ const clamped = Math.min(Math.max(1, n), pageCount);
8577
+ scrollToPage(clamped);
8578
+ setPageInput(String(clamped));
8579
+ }, [pageInput, pageCount, scrollToPage]);
8580
+ const handleKeyDown = React2.useCallback(
8581
+ (e) => {
8582
+ if (!enableKeyboardShortcuts) return;
8583
+ if (e.target !== e.currentTarget && e.target instanceof HTMLInputElement) {
8584
+ return;
8585
+ }
8586
+ let handled = false;
8587
+ switch (e.key) {
8588
+ case "PageDown": {
8589
+ scrollToPage(Math.min(pageCount || 1, currentVisiblePage + 1));
8590
+ handled = true;
8591
+ break;
8592
+ }
8593
+ case "PageUp": {
8594
+ scrollToPage(Math.max(1, currentVisiblePage - 1));
8595
+ handled = true;
8596
+ break;
8597
+ }
8598
+ case "Home": {
8599
+ scrollToPage(1);
8600
+ handled = true;
8601
+ break;
8602
+ }
8603
+ case "End": {
8604
+ if (pageCount) scrollToPage(pageCount);
8605
+ handled = true;
8606
+ break;
8607
+ }
8608
+ case "+":
8609
+ case "=": {
8610
+ zoomInAct();
8611
+ handled = true;
8612
+ break;
8613
+ }
8614
+ case "-":
8615
+ case "_": {
8616
+ zoomOutAct();
8617
+ handled = true;
8618
+ break;
8619
+ }
8620
+ case "0": {
8621
+ resetZoomAct();
8622
+ handled = true;
8623
+ break;
8624
+ }
8625
+ }
8626
+ if (handled) {
8627
+ e.preventDefault();
8628
+ e.stopPropagation();
8629
+ }
8630
+ },
8631
+ [
8632
+ enableKeyboardShortcuts,
8633
+ pageCount,
8634
+ currentVisiblePage,
8635
+ scrollToPage,
8636
+ zoomInAct,
8637
+ zoomOutAct,
8638
+ resetZoomAct
8639
+ ]
8640
+ );
8641
+ if (loadError) {
8642
+ return /* @__PURE__ */ jsxRuntime.jsx(
8643
+ "div",
8644
+ {
8645
+ className: cn("rounded-md border border-destructive/50 p-4", className),
8646
+ role: "alert",
8647
+ children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-destructive text-sm", children: loadError })
8648
+ }
8649
+ );
8650
+ }
8651
+ return /* @__PURE__ */ jsxRuntime.jsxs(
8652
+ "div",
8653
+ {
8654
+ className: cn(
8655
+ "flex min-h-0 flex-col overflow-hidden rounded-md border",
8656
+ className
8657
+ ),
8658
+ children: [
8659
+ /* @__PURE__ */ jsxRuntime.jsxs(
8660
+ "div",
8661
+ {
8662
+ id: toolbarId,
8663
+ role: "toolbar",
8664
+ "aria-label": toolbarLabel,
8665
+ className: cn(
8666
+ "sticky top-0 z-10 flex shrink-0 flex-wrap items-center gap-2 border-b bg-background/95 px-2 py-2 backdrop-blur supports-[backdrop-filter]:bg-background/80",
8667
+ toolbarClassName
8668
+ ),
8669
+ children: [
8670
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground text-xs tabular-nums sm:text-sm", children: busy || pagesMap === null ? "\u2014" : pageOfFormatter(currentVisiblePage, pageCount) }),
8671
+ /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: pageInputId, className: "sr-only", children: resolvedGoToLabel }),
8672
+ /* @__PURE__ */ jsxRuntime.jsx(
8673
+ "input",
8674
+ {
8675
+ id: pageInputId,
8676
+ type: "number",
8677
+ min: 1,
8678
+ max: Math.max(pageCount, 1),
8679
+ disabled: busy || pageCount === 0,
8680
+ className: PAGE_INPUT_CLASS,
8681
+ "aria-label": resolvedGoToLabel,
8682
+ value: pageInput,
8683
+ onChange: (ev) => setPageInput(ev.target.value),
8684
+ onKeyDown: (ev) => {
8685
+ if (ev.key === "Enter") {
8686
+ ev.preventDefault();
8687
+ handleSubmitPage();
8688
+ }
8689
+ }
8690
+ }
8691
+ ),
8692
+ /* @__PURE__ */ jsxRuntime.jsx(
8693
+ Button,
8694
+ {
8695
+ type: "button",
8696
+ variant: "outline",
8697
+ size: "icon-sm",
8698
+ onClick: zoomOutAct,
8699
+ disabled: busy || scale <= minScale,
8700
+ "aria-label": zoomOutLabel,
8701
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ZoomOut, { "aria-hidden": true })
8702
+ }
8703
+ ),
8704
+ /* @__PURE__ */ jsxRuntime.jsx(
8705
+ Button,
8706
+ {
8707
+ type: "button",
8708
+ variant: "outline",
8709
+ size: "icon-sm",
8710
+ onClick: zoomInAct,
8711
+ disabled: busy || scale >= maxScale,
8712
+ "aria-label": zoomInLabel,
8713
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ZoomIn, { "aria-hidden": true })
8714
+ }
8715
+ ),
8716
+ /* @__PURE__ */ jsxRuntime.jsx(
8717
+ Button,
8718
+ {
8719
+ type: "button",
8720
+ variant: "outline",
8721
+ size: "icon-sm",
8722
+ onClick: resetZoomAct,
8723
+ disabled: busy,
8724
+ "aria-label": resetZoomLabel,
8725
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RotateCcw, { "aria-hidden": true })
8726
+ }
8727
+ ),
8728
+ /* @__PURE__ */ jsxRuntime.jsx(
8729
+ Button,
8730
+ {
8731
+ type: "button",
8732
+ variant: "outline",
8733
+ size: "icon-sm",
8734
+ onClick: fitToWidthAct,
8735
+ disabled: busy || pageCount === 0,
8736
+ "aria-label": fitWidthLabel,
8737
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Maximize2, { "aria-hidden": true })
8738
+ }
8739
+ ),
8740
+ toolbarEndSlot
8741
+ ]
8742
+ }
8743
+ ),
8744
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-h-0 flex-1 overflow-hidden", children: [
8745
+ enableThumbnailSidebar && !busy && pagesMap !== null && pageCount > 0 && thumbnailSidebarOpen ? /* @__PURE__ */ jsxRuntime.jsx(
8746
+ "nav",
8747
+ {
8748
+ className: "bg-muted/30 flex w-[132px] shrink-0 flex-col overflow-y-auto border-r py-2 pl-2 pr-1",
8749
+ "aria-label": thumbnailSidebarLabel,
8750
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-1", children: layouts.map((layout) => {
8751
+ const pg = pagesMap.get(layout.pageNumber);
8752
+ return pg ? /* @__PURE__ */ jsxRuntime.jsx(
8753
+ PdfThumbnailStrip,
8754
+ {
8755
+ page: pg,
8756
+ pageNumber: layout.pageNumber,
8757
+ active: layout.pageNumber === currentVisiblePage,
8758
+ onPick: scrollToPage,
8759
+ label: pageThumbLabel(layout.pageNumber)
8760
+ },
8761
+ `thumb-${layout.pageNumber}`
8762
+ ) : null;
8763
+ }) })
8764
+ }
8765
+ ) : null,
8766
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-h-0 min-w-0 flex-1", children: [
8767
+ enableThumbnailSidebar && !busy && pagesMap !== null && pageCount > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
8768
+ "button",
8769
+ {
8770
+ type: "button",
8771
+ className: "text-muted-foreground hover:bg-muted/60 flex w-9 shrink-0 flex-col items-center justify-center border-r bg-transparent transition-colors",
8772
+ onClick: () => setThumbnailSidebarOpen((v) => !v),
8773
+ "aria-label": toggleThumbLabel,
8774
+ "aria-expanded": thumbnailSidebarOpen,
8775
+ children: thumbnailSidebarOpen ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { className: "size-4", "aria-hidden": true }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "size-4", "aria-hidden": true })
8776
+ }
8777
+ ) : null,
8778
+ /* @__PURE__ */ jsxRuntime.jsx(
8779
+ "div",
8780
+ {
8781
+ ref: scrollRef,
8782
+ tabIndex: 0,
8783
+ onKeyDown: handleKeyDown,
8784
+ className: "relative min-h-[240px] min-w-0 flex-1 overflow-auto outline-none focus-visible:ring-2 focus-visible:ring-ring",
8785
+ "aria-busy": busy,
8786
+ "aria-label": busy ? loadingLabel : "PDF pages",
8787
+ children: busy || pagesMap === null ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground p-6 text-sm", children: loadingLabel }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col items-center gap-4 pb-8 pt-2", children: layouts.map((layout) => {
8788
+ const pg = pagesMap.get(layout.pageNumber);
8789
+ const visible = (ratios[layout.pageNumber] ?? 0) >= 1e-3;
8790
+ return pg ? /* @__PURE__ */ jsxRuntime.jsx(
8791
+ PdfPageCanvas,
8792
+ {
8793
+ page: pg,
8794
+ layout,
8795
+ scale,
8796
+ visible,
8797
+ pageClassName
8798
+ },
8799
+ layout.pageNumber
8800
+ ) : null;
8801
+ }) })
8802
+ }
8803
+ )
8804
+ ] })
8805
+ ] })
8806
+ ]
8807
+ }
8808
+ );
8809
+ }
8810
+ var fullscreenDialogContentClass = "!max-w-none !w-screen !h-screen !p-0 !border-0 !bg-black/95 !rounded-none !top-0 !left-0 !translate-x-0 !translate-y-0 !gap-0 !shadow-none !flex !flex-col data-[state=open]:!zoom-in-100 data-[state=closed]:!zoom-out-100";
8811
+ function PdfViewerDialog({
8812
+ open,
8813
+ onOpenChange,
8814
+ src,
8815
+ workerSrc,
8816
+ documentOptions,
8817
+ trigger,
8818
+ title,
8819
+ labels,
8820
+ forwardedProps
8821
+ }) {
8822
+ const closeLabel = labels?.close ?? "Close";
8823
+ const dialogTitle = labels?.dialogTitle ?? title ?? "PDF document";
8824
+ const dialogDescription = labels?.dialogDescription ?? "Document viewer with zoom and page navigation.";
8825
+ const {
8826
+ className: forwardedClassName,
8827
+ toolbarClassName: forwardedToolbarClassName,
8828
+ ...restForwarded
8829
+ } = forwardedProps ?? {};
8830
+ return /* @__PURE__ */ jsxRuntime.jsxs(Dialog, { open, onOpenChange, children: [
8831
+ trigger ? /* @__PURE__ */ jsxRuntime.jsx(DialogTrigger, { asChild: true, children: trigger }) : null,
8832
+ /* @__PURE__ */ jsxRuntime.jsxs(
8833
+ DialogContent,
8834
+ {
8835
+ className: fullscreenDialogContentClass,
8836
+ showCloseButton: false,
8837
+ children: [
8838
+ /* @__PURE__ */ jsxRuntime.jsx(DialogTitle, { className: "sr-only", children: dialogTitle }),
8839
+ /* @__PURE__ */ jsxRuntime.jsx(DialogDescription, { className: "sr-only", children: dialogDescription }),
8840
+ /* @__PURE__ */ jsxRuntime.jsx(
8841
+ PdfViewer,
8842
+ {
8843
+ ...restForwarded,
8844
+ src,
8845
+ workerSrc,
8846
+ documentOptions,
8847
+ className: cn(
8848
+ "min-h-0 flex-1 rounded-none border-0 bg-transparent text-white",
8849
+ forwardedClassName
8850
+ ),
8851
+ toolbarClassName: cn(
8852
+ "border-white/10 bg-black/80 text-white backdrop-blur",
8853
+ forwardedToolbarClassName
8854
+ ),
8855
+ toolbarEndSlot: /* @__PURE__ */ jsxRuntime.jsx(
8856
+ Button,
8857
+ {
8858
+ type: "button",
8859
+ variant: "ghost",
8860
+ size: "icon-sm",
8861
+ className: "ml-auto text-white hover:bg-white/10 hover:text-white",
8862
+ onClick: () => onOpenChange(false),
8863
+ "aria-label": closeLabel,
8864
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "size-5", "aria-hidden": true })
8865
+ }
8866
+ )
8867
+ }
8868
+ )
8869
+ ]
8870
+ }
8871
+ )
8872
+ ] });
8873
+ }
8134
8874
 
8135
8875
  exports.AvatarEditor = AvatarEditor;
8136
8876
  exports.AvatarEditorDialog = AvatarEditorDialog;
@@ -8192,6 +8932,8 @@ exports.LanguageSwitcher = LanguageSwitcher;
8192
8932
  exports.MiniCalendar = MiniCalendar;
8193
8933
  exports.NotificationsContext = NotificationsContext;
8194
8934
  exports.NotificationsWidget = NotificationsWidget;
8935
+ exports.PdfViewer = PdfViewer;
8936
+ exports.PdfViewerDialog = PdfViewerDialog;
8195
8937
  exports.Popover = Popover;
8196
8938
  exports.PopoverAnchor = PopoverAnchor;
8197
8939
  exports.PopoverContent = PopoverContent;