@sesionario/blocks 1.31.1 → 1.32.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/pdf.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { useState, useRef, useCallback, useEffect } from 'react';
2
2
  import { pdfjs, Document, Page, Thumbnail } from 'react-pdf';
3
- import { PanelLeftClose, PanelLeft, ChevronLeft, ChevronRight, ZoomOut, ZoomIn, RotateCw, Minimize2, Maximize2, Download, AlertTriangle, Loader2, X, XIcon } from 'lucide-react';
3
+ import { PanelLeftClose, PanelLeft, ChevronLeft, ChevronRight, ZoomOut, ZoomIn, RotateCw, Minimize2, Maximize2, Download, X, AlertTriangle, Loader2, XIcon } from 'lucide-react';
4
4
  import { clsx } from 'clsx';
5
5
  import { twMerge } from 'tailwind-merge';
6
6
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
@@ -373,6 +373,7 @@ function LazyThumbnail({
373
373
  pageNumber,
374
374
  active,
375
375
  width,
376
+ pdf,
376
377
  onClick,
377
378
  ariaLabel
378
379
  }) {
@@ -409,7 +410,15 @@ function LazyThumbnail({
409
410
  active ? "border-primary ring-2 ring-primary/30" : "border-border"
410
411
  ),
411
412
  children: [
412
- visible ? /* @__PURE__ */ jsx(Thumbnail, { pageNumber, width, className: "mx-auto" }) : /* @__PURE__ */ jsx(
413
+ visible ? /* @__PURE__ */ jsx(
414
+ Thumbnail,
415
+ {
416
+ pageNumber,
417
+ width,
418
+ pdf,
419
+ className: "mx-auto"
420
+ }
421
+ ) : /* @__PURE__ */ jsx(
413
422
  "div",
414
423
  {
415
424
  className: "mx-auto bg-muted",
@@ -444,14 +453,16 @@ function PdfViewer({
444
453
  const [numPages, setNumPages] = useState(0);
445
454
  const [pageNumber, setPageNumber] = useState(initialPage);
446
455
  const [zoomIndex, setZoomIndex] = useState(2);
447
- const [fitMode, setFitMode] = useState("width");
456
+ const [fitMode, setFitMode] = useState("page");
448
457
  const [rotation, setRotation] = useState(0);
449
458
  const [containerWidth, setContainerWidth] = useState(0);
450
459
  const [containerHeight, setContainerHeight] = useState(0);
460
+ const [pageNatural, setPageNatural] = useState(null);
451
461
  const [isFullscreen, setIsFullscreen] = useState(false);
452
462
  const [thumbsOpen, setThumbsOpen] = useState(false);
453
463
  const [loadError, setLoadError] = useState(false);
454
464
  const [pageDraft, setPageDraft] = useState(String(initialPage));
465
+ const [pdfDocument, setPdfDocument] = useState(null);
455
466
  const viewportRef = useRef(null);
456
467
  const shellRef = useRef(null);
457
468
  const pageNumberRef = useRef(pageNumber);
@@ -494,37 +505,58 @@ function PdfViewer({
494
505
  setPageNumber(initialPage);
495
506
  setPageDraft(String(initialPage));
496
507
  setZoomIndex(2);
497
- setFitMode("width");
508
+ setFitMode("page");
498
509
  setRotation(0);
499
510
  setNumPages(0);
511
+ setPdfDocument(null);
512
+ setPageNatural(null);
500
513
  setLoadError(false);
501
514
  setThumbsOpen(false);
502
515
  reset();
503
516
  }, [fileUrl, initialPage, reset]);
504
517
  useEffect(() => {
505
518
  reset();
519
+ setPageNatural(null);
506
520
  }, [pageNumber, reset]);
521
+ const measureViewport = useCallback(() => {
522
+ const el = viewportRef.current;
523
+ if (!el) return;
524
+ const w = el.clientWidth;
525
+ const h = el.clientHeight;
526
+ if (w > 0) setContainerWidth(w);
527
+ if (h > 0) setContainerHeight(h);
528
+ }, []);
507
529
  useEffect(() => {
508
530
  if (!viewportRef.current) return;
509
531
  const el = viewportRef.current;
510
532
  const observer = new ResizeObserver((entries) => {
511
533
  const rect = entries[0]?.contentRect;
512
534
  if (!rect) return;
513
- setContainerWidth(rect.width);
514
- setContainerHeight(rect.height);
535
+ if (rect.width > 0) setContainerWidth(rect.width);
536
+ if (rect.height > 0) setContainerHeight(rect.height);
515
537
  });
516
538
  observer.observe(el);
517
- setContainerWidth(el.clientWidth);
518
- setContainerHeight(el.clientHeight);
519
- return () => observer.disconnect();
520
- }, []);
539
+ measureViewport();
540
+ const raf = requestAnimationFrame(() => {
541
+ measureViewport();
542
+ requestAnimationFrame(measureViewport);
543
+ });
544
+ return () => {
545
+ observer.disconnect();
546
+ cancelAnimationFrame(raf);
547
+ };
548
+ }, [measureViewport]);
549
+ useEffect(() => {
550
+ measureViewport();
551
+ }, [thumbsOpen, measureViewport]);
521
552
  useEffect(() => {
522
553
  function onFullscreenChange() {
523
554
  setIsFullscreen(document.fullscreenElement === shellRef.current);
555
+ requestAnimationFrame(measureViewport);
524
556
  }
525
557
  document.addEventListener("fullscreenchange", onFullscreenChange);
526
558
  return () => document.removeEventListener("fullscreenchange", onFullscreenChange);
527
- }, []);
559
+ }, [measureViewport]);
528
560
  useEffect(() => {
529
561
  function onKeyDown(e) {
530
562
  const target = e.target;
@@ -594,14 +626,26 @@ function PdfViewer({
594
626
  if (Number.isFinite(parsed)) goToPage(parsed);
595
627
  else setPageDraft(String(pageNumber));
596
628
  }
597
- const pageWidth = fitMode === "width" ? Math.max(containerWidth - 32, 240) * baseScale : void 0;
598
- const pageHeight = fitMode === "page" ? Math.max(containerHeight - 32, 240) * baseScale : void 0;
629
+ const PAD = 32;
630
+ const availW = Math.max(containerWidth - PAD, 240);
631
+ const availH = Math.max(containerHeight - PAD, 240);
632
+ const rotated = rotation % 180 !== 0 && pageNatural ? { width: pageNatural.height, height: pageNatural.width } : pageNatural;
633
+ let pageWidth;
634
+ if (fitMode === "width" || !rotated) {
635
+ pageWidth = availW * baseScale;
636
+ } else {
637
+ const scaleToFit = Math.min(
638
+ availW / rotated.width,
639
+ availH / rotated.height
640
+ );
641
+ pageWidth = rotated.width * scaleToFit * baseScale;
642
+ }
599
643
  const rawDownload = downloadUrl ?? fileUrl;
600
644
  const downloadHref = `${rawDownload}${rawDownload.includes("?") ? "&" : "?"}download=1`;
601
645
  const { scale: gestureScale, tx, ty } = transform;
602
646
  const transition = snapBack && !isDragging && !prefersReducedMotion() ? `transform ${ZOOM_PAN_SWIPE_SNAP_MS}ms ease-out` : void 0;
603
647
  const pageOf = labels.pageOf?.(pageNumber, numPages) ?? (numPages > 0 ? `${pageNumber} / ${numPages}` : "\u2014");
604
- function renderThumbList(onSelect) {
648
+ function renderThumbList(pdf, onSelect) {
605
649
  return /* @__PURE__ */ jsx("div", { className: "space-y-2", children: Array.from({ length: numPages }, (_, i) => {
606
650
  const n = i + 1;
607
651
  return /* @__PURE__ */ jsx(
@@ -610,6 +654,7 @@ function PdfViewer({
610
654
  pageNumber: n,
611
655
  active: n === pageNumber,
612
656
  width: THUMB_WIDTH,
657
+ pdf,
613
658
  onClick: () => onSelect(n),
614
659
  ariaLabel: labels.thumbnailAria?.(n) ?? `P\xE1gina ${n}`
615
660
  },
@@ -757,17 +802,17 @@ function PdfViewer({
757
802
  ] })
758
803
  ] }),
759
804
  /* @__PURE__ */ jsxs("div", { className: "relative flex min-h-0 flex-1", children: [
760
- numPages > 0 && /* @__PURE__ */ jsx(
805
+ pdfDocument && numPages > 0 && /* @__PURE__ */ jsx(
761
806
  "div",
762
807
  {
763
808
  className: cn(
764
809
  "hidden w-40 shrink-0 overflow-y-auto border-r bg-background p-2 lg:block",
765
810
  !thumbsOpen && "lg:hidden"
766
811
  ),
767
- children: renderThumbList(goToPage)
812
+ children: renderThumbList(pdfDocument, goToPage)
768
813
  }
769
814
  ),
770
- thumbsOpen && numPages > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
815
+ thumbsOpen && pdfDocument && numPages > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
771
816
  /* @__PURE__ */ jsx(
772
817
  "button",
773
818
  {
@@ -777,7 +822,7 @@ function PdfViewer({
777
822
  onClick: () => setThumbsOpen(false)
778
823
  }
779
824
  ),
780
- /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 left-0 z-30 w-40 overflow-y-auto border-r bg-background p-2 shadow-lg lg:hidden", children: renderThumbList((n) => {
825
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 left-0 z-30 w-40 overflow-y-auto border-r bg-background p-2 shadow-lg lg:hidden", children: renderThumbList(pdfDocument, (n) => {
781
826
  goToPage(n);
782
827
  setThumbsOpen(false);
783
828
  }) })
@@ -810,7 +855,7 @@ function PdfViewer({
810
855
  "div",
811
856
  {
812
857
  ref: stageRef,
813
- className: "flex min-h-full touch-none items-start justify-center p-4",
858
+ className: "flex min-h-full min-w-full touch-none p-4",
814
859
  style: {
815
860
  transform: `translate(${tx}px, ${ty}px) scale(${gestureScale})`,
816
861
  transition
@@ -820,18 +865,24 @@ function PdfViewer({
820
865
  onPointerUp: handlers.onPointerUp,
821
866
  onPointerCancel: handlers.onPointerCancel,
822
867
  onTransitionEnd: clearSnapBack,
823
- children: /* @__PURE__ */ jsx(
868
+ children: /* @__PURE__ */ jsx("div", { className: "m-auto", children: /* @__PURE__ */ jsx(
824
869
  Document,
825
870
  {
826
871
  file: fileUrl,
827
- onLoadSuccess: ({ numPages: pages }) => {
872
+ onLoadSuccess: (pdf) => {
873
+ const pages = pdf.numPages;
874
+ setPdfDocument(pdf);
828
875
  setNumPages(pages);
829
876
  setLoadError(false);
830
877
  const start = Math.min(Math.max(1, initialPage), pages);
831
878
  setPageNumber(start);
832
879
  setPageDraft(String(start));
880
+ setFitMode(pages === 1 ? "page" : "width");
881
+ },
882
+ onLoadError: () => {
883
+ setPdfDocument(null);
884
+ setLoadError(true);
833
885
  },
834
- onLoadError: () => setLoadError(true),
835
886
  loading: /* @__PURE__ */ jsx(LoadingState, { label: labels.loading ?? "Cargando PDF\u2026" }),
836
887
  error: /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center p-6 sm:p-8", children: /* @__PURE__ */ jsx(
837
888
  ErrorState,
@@ -845,14 +896,19 @@ function PdfViewer({
845
896
  {
846
897
  pageNumber,
847
898
  width: pageWidth,
848
- height: pageHeight,
849
899
  rotate: rotation,
850
900
  renderTextLayer: true,
851
- renderAnnotationLayer: true
901
+ renderAnnotationLayer: true,
902
+ onLoadSuccess: (page) => {
903
+ setPageNatural({
904
+ width: page.originalWidth,
905
+ height: page.originalHeight
906
+ });
907
+ }
852
908
  }
853
909
  )
854
910
  }
855
- )
911
+ ) })
856
912
  }
857
913
  )
858
914
  }
@@ -966,7 +1022,9 @@ function MediaViewerDialog({
966
1022
  {
967
1023
  showCloseButton: false,
968
1024
  className: cn(
969
- "inset-0 flex h-dvh max-h-dvh w-screen max-w-none translate-x-0 translate-y-0 top-0 left-0 gap-0 overflow-hidden rounded-none border-0 p-0 shadow-none sm:max-w-none",
1025
+ // flex-col is required: without it the single child is a row item
1026
+ // sized to max-content (toolbar width) and the viewport collapses.
1027
+ "inset-0 flex flex-col h-dvh max-h-dvh w-screen max-w-none translate-x-0 translate-y-0 top-0 left-0 gap-0 overflow-hidden rounded-none border-0 p-0 shadow-none sm:max-w-none",
970
1028
  contentClassName,
971
1029
  className
972
1030
  ),
@@ -1000,31 +1058,43 @@ function PdfViewerDialog({
1000
1058
  labels
1001
1059
  }) {
1002
1060
  const viewerLabels = labels;
1061
+ const closeLabel = labels.close ?? "Cerrar";
1003
1062
  return /* @__PURE__ */ jsx(
1004
1063
  MediaViewerDialog,
1005
1064
  {
1006
1065
  open,
1007
1066
  onOpenChange,
1067
+ showCloseButton: false,
1008
1068
  labels: {
1009
- close: labels.close,
1069
+ close: closeLabel,
1010
1070
  title,
1011
1071
  description: labels.dialogDescription ?? title
1012
1072
  },
1013
- children: /* @__PURE__ */ jsxs("div", { className: "flex h-full min-h-0 flex-col pt-12", children: [
1014
- /* @__PURE__ */ jsx("div", { className: "border-b px-4 py-2 pr-14", children: /* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium", children: title }) }),
1015
- /* @__PURE__ */ jsx(
1016
- PdfViewer,
1017
- {
1018
- fileUrl,
1019
- fileName: fileName ?? title,
1020
- downloadUrl,
1021
- workerSrc,
1022
- initialPage,
1023
- labels: viewerLabels,
1024
- className: "min-h-0 flex-1"
1025
- }
1026
- )
1027
- ] })
1073
+ children: /* @__PURE__ */ jsx(
1074
+ PdfViewer,
1075
+ {
1076
+ fileUrl,
1077
+ fileName: fileName ?? title,
1078
+ downloadUrl,
1079
+ workerSrc,
1080
+ initialPage,
1081
+ labels: viewerLabels,
1082
+ className: "min-h-0 w-full min-w-0 flex-1",
1083
+ closeAction: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1084
+ /* @__PURE__ */ jsx(
1085
+ "button",
1086
+ {
1087
+ type: "button",
1088
+ onClick: () => onOpenChange(false),
1089
+ "aria-label": closeLabel,
1090
+ className: "inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-md border bg-background text-foreground hover:bg-accent",
1091
+ children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
1092
+ }
1093
+ ),
1094
+ /* @__PURE__ */ jsx("span", { className: "max-w-[min(40vw,16rem)] truncate text-sm font-medium text-foreground", children: title })
1095
+ ] })
1096
+ }
1097
+ )
1028
1098
  }
1029
1099
  );
1030
1100
  }