@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.cjs CHANGED
@@ -395,6 +395,7 @@ function LazyThumbnail({
395
395
  pageNumber,
396
396
  active,
397
397
  width,
398
+ pdf,
398
399
  onClick,
399
400
  ariaLabel
400
401
  }) {
@@ -431,7 +432,15 @@ function LazyThumbnail({
431
432
  active ? "border-primary ring-2 ring-primary/30" : "border-border"
432
433
  ),
433
434
  children: [
434
- visible ? /* @__PURE__ */ jsxRuntime.jsx(reactPdf.Thumbnail, { pageNumber, width, className: "mx-auto" }) : /* @__PURE__ */ jsxRuntime.jsx(
435
+ visible ? /* @__PURE__ */ jsxRuntime.jsx(
436
+ reactPdf.Thumbnail,
437
+ {
438
+ pageNumber,
439
+ width,
440
+ pdf,
441
+ className: "mx-auto"
442
+ }
443
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
435
444
  "div",
436
445
  {
437
446
  className: "mx-auto bg-muted",
@@ -466,14 +475,16 @@ function PdfViewer({
466
475
  const [numPages, setNumPages] = react.useState(0);
467
476
  const [pageNumber, setPageNumber] = react.useState(initialPage);
468
477
  const [zoomIndex, setZoomIndex] = react.useState(2);
469
- const [fitMode, setFitMode] = react.useState("width");
478
+ const [fitMode, setFitMode] = react.useState("page");
470
479
  const [rotation, setRotation] = react.useState(0);
471
480
  const [containerWidth, setContainerWidth] = react.useState(0);
472
481
  const [containerHeight, setContainerHeight] = react.useState(0);
482
+ const [pageNatural, setPageNatural] = react.useState(null);
473
483
  const [isFullscreen, setIsFullscreen] = react.useState(false);
474
484
  const [thumbsOpen, setThumbsOpen] = react.useState(false);
475
485
  const [loadError, setLoadError] = react.useState(false);
476
486
  const [pageDraft, setPageDraft] = react.useState(String(initialPage));
487
+ const [pdfDocument, setPdfDocument] = react.useState(null);
477
488
  const viewportRef = react.useRef(null);
478
489
  const shellRef = react.useRef(null);
479
490
  const pageNumberRef = react.useRef(pageNumber);
@@ -516,37 +527,58 @@ function PdfViewer({
516
527
  setPageNumber(initialPage);
517
528
  setPageDraft(String(initialPage));
518
529
  setZoomIndex(2);
519
- setFitMode("width");
530
+ setFitMode("page");
520
531
  setRotation(0);
521
532
  setNumPages(0);
533
+ setPdfDocument(null);
534
+ setPageNatural(null);
522
535
  setLoadError(false);
523
536
  setThumbsOpen(false);
524
537
  reset();
525
538
  }, [fileUrl, initialPage, reset]);
526
539
  react.useEffect(() => {
527
540
  reset();
541
+ setPageNatural(null);
528
542
  }, [pageNumber, reset]);
543
+ const measureViewport = react.useCallback(() => {
544
+ const el = viewportRef.current;
545
+ if (!el) return;
546
+ const w = el.clientWidth;
547
+ const h = el.clientHeight;
548
+ if (w > 0) setContainerWidth(w);
549
+ if (h > 0) setContainerHeight(h);
550
+ }, []);
529
551
  react.useEffect(() => {
530
552
  if (!viewportRef.current) return;
531
553
  const el = viewportRef.current;
532
554
  const observer = new ResizeObserver((entries) => {
533
555
  const rect = entries[0]?.contentRect;
534
556
  if (!rect) return;
535
- setContainerWidth(rect.width);
536
- setContainerHeight(rect.height);
557
+ if (rect.width > 0) setContainerWidth(rect.width);
558
+ if (rect.height > 0) setContainerHeight(rect.height);
537
559
  });
538
560
  observer.observe(el);
539
- setContainerWidth(el.clientWidth);
540
- setContainerHeight(el.clientHeight);
541
- return () => observer.disconnect();
542
- }, []);
561
+ measureViewport();
562
+ const raf = requestAnimationFrame(() => {
563
+ measureViewport();
564
+ requestAnimationFrame(measureViewport);
565
+ });
566
+ return () => {
567
+ observer.disconnect();
568
+ cancelAnimationFrame(raf);
569
+ };
570
+ }, [measureViewport]);
571
+ react.useEffect(() => {
572
+ measureViewport();
573
+ }, [thumbsOpen, measureViewport]);
543
574
  react.useEffect(() => {
544
575
  function onFullscreenChange() {
545
576
  setIsFullscreen(document.fullscreenElement === shellRef.current);
577
+ requestAnimationFrame(measureViewport);
546
578
  }
547
579
  document.addEventListener("fullscreenchange", onFullscreenChange);
548
580
  return () => document.removeEventListener("fullscreenchange", onFullscreenChange);
549
- }, []);
581
+ }, [measureViewport]);
550
582
  react.useEffect(() => {
551
583
  function onKeyDown(e) {
552
584
  const target = e.target;
@@ -616,14 +648,26 @@ function PdfViewer({
616
648
  if (Number.isFinite(parsed)) goToPage(parsed);
617
649
  else setPageDraft(String(pageNumber));
618
650
  }
619
- const pageWidth = fitMode === "width" ? Math.max(containerWidth - 32, 240) * baseScale : void 0;
620
- const pageHeight = fitMode === "page" ? Math.max(containerHeight - 32, 240) * baseScale : void 0;
651
+ const PAD = 32;
652
+ const availW = Math.max(containerWidth - PAD, 240);
653
+ const availH = Math.max(containerHeight - PAD, 240);
654
+ const rotated = rotation % 180 !== 0 && pageNatural ? { width: pageNatural.height, height: pageNatural.width } : pageNatural;
655
+ let pageWidth;
656
+ if (fitMode === "width" || !rotated) {
657
+ pageWidth = availW * baseScale;
658
+ } else {
659
+ const scaleToFit = Math.min(
660
+ availW / rotated.width,
661
+ availH / rotated.height
662
+ );
663
+ pageWidth = rotated.width * scaleToFit * baseScale;
664
+ }
621
665
  const rawDownload = downloadUrl ?? fileUrl;
622
666
  const downloadHref = `${rawDownload}${rawDownload.includes("?") ? "&" : "?"}download=1`;
623
667
  const { scale: gestureScale, tx, ty } = transform;
624
668
  const transition = snapBack && !isDragging && !prefersReducedMotion() ? `transform ${ZOOM_PAN_SWIPE_SNAP_MS}ms ease-out` : void 0;
625
669
  const pageOf = labels.pageOf?.(pageNumber, numPages) ?? (numPages > 0 ? `${pageNumber} / ${numPages}` : "\u2014");
626
- function renderThumbList(onSelect) {
670
+ function renderThumbList(pdf, onSelect) {
627
671
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: Array.from({ length: numPages }, (_, i) => {
628
672
  const n = i + 1;
629
673
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -632,6 +676,7 @@ function PdfViewer({
632
676
  pageNumber: n,
633
677
  active: n === pageNumber,
634
678
  width: THUMB_WIDTH,
679
+ pdf,
635
680
  onClick: () => onSelect(n),
636
681
  ariaLabel: labels.thumbnailAria?.(n) ?? `P\xE1gina ${n}`
637
682
  },
@@ -779,17 +824,17 @@ function PdfViewer({
779
824
  ] })
780
825
  ] }),
781
826
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex min-h-0 flex-1", children: [
782
- numPages > 0 && /* @__PURE__ */ jsxRuntime.jsx(
827
+ pdfDocument && numPages > 0 && /* @__PURE__ */ jsxRuntime.jsx(
783
828
  "div",
784
829
  {
785
830
  className: cn(
786
831
  "hidden w-40 shrink-0 overflow-y-auto border-r bg-background p-2 lg:block",
787
832
  !thumbsOpen && "lg:hidden"
788
833
  ),
789
- children: renderThumbList(goToPage)
834
+ children: renderThumbList(pdfDocument, goToPage)
790
835
  }
791
836
  ),
792
- thumbsOpen && numPages > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
837
+ thumbsOpen && pdfDocument && numPages > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
793
838
  /* @__PURE__ */ jsxRuntime.jsx(
794
839
  "button",
795
840
  {
@@ -799,7 +844,7 @@ function PdfViewer({
799
844
  onClick: () => setThumbsOpen(false)
800
845
  }
801
846
  ),
802
- /* @__PURE__ */ jsxRuntime.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) => {
847
+ /* @__PURE__ */ jsxRuntime.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) => {
803
848
  goToPage(n);
804
849
  setThumbsOpen(false);
805
850
  }) })
@@ -832,7 +877,7 @@ function PdfViewer({
832
877
  "div",
833
878
  {
834
879
  ref: stageRef,
835
- className: "flex min-h-full touch-none items-start justify-center p-4",
880
+ className: "flex min-h-full min-w-full touch-none p-4",
836
881
  style: {
837
882
  transform: `translate(${tx}px, ${ty}px) scale(${gestureScale})`,
838
883
  transition
@@ -842,18 +887,24 @@ function PdfViewer({
842
887
  onPointerUp: handlers.onPointerUp,
843
888
  onPointerCancel: handlers.onPointerCancel,
844
889
  onTransitionEnd: clearSnapBack,
845
- children: /* @__PURE__ */ jsxRuntime.jsx(
890
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "m-auto", children: /* @__PURE__ */ jsxRuntime.jsx(
846
891
  reactPdf.Document,
847
892
  {
848
893
  file: fileUrl,
849
- onLoadSuccess: ({ numPages: pages }) => {
894
+ onLoadSuccess: (pdf) => {
895
+ const pages = pdf.numPages;
896
+ setPdfDocument(pdf);
850
897
  setNumPages(pages);
851
898
  setLoadError(false);
852
899
  const start = Math.min(Math.max(1, initialPage), pages);
853
900
  setPageNumber(start);
854
901
  setPageDraft(String(start));
902
+ setFitMode(pages === 1 ? "page" : "width");
903
+ },
904
+ onLoadError: () => {
905
+ setPdfDocument(null);
906
+ setLoadError(true);
855
907
  },
856
- onLoadError: () => setLoadError(true),
857
908
  loading: /* @__PURE__ */ jsxRuntime.jsx(LoadingState, { label: labels.loading ?? "Cargando PDF\u2026" }),
858
909
  error: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 flex items-center justify-center p-6 sm:p-8", children: /* @__PURE__ */ jsxRuntime.jsx(
859
910
  ErrorState,
@@ -867,14 +918,19 @@ function PdfViewer({
867
918
  {
868
919
  pageNumber,
869
920
  width: pageWidth,
870
- height: pageHeight,
871
921
  rotate: rotation,
872
922
  renderTextLayer: true,
873
- renderAnnotationLayer: true
923
+ renderAnnotationLayer: true,
924
+ onLoadSuccess: (page) => {
925
+ setPageNatural({
926
+ width: page.originalWidth,
927
+ height: page.originalHeight
928
+ });
929
+ }
874
930
  }
875
931
  )
876
932
  }
877
- )
933
+ ) })
878
934
  }
879
935
  )
880
936
  }
@@ -988,7 +1044,9 @@ function MediaViewerDialog({
988
1044
  {
989
1045
  showCloseButton: false,
990
1046
  className: cn(
991
- "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",
1047
+ // flex-col is required: without it the single child is a row item
1048
+ // sized to max-content (toolbar width) and the viewport collapses.
1049
+ "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",
992
1050
  contentClassName,
993
1051
  className
994
1052
  ),
@@ -1022,31 +1080,43 @@ function PdfViewerDialog({
1022
1080
  labels
1023
1081
  }) {
1024
1082
  const viewerLabels = labels;
1083
+ const closeLabel = labels.close ?? "Cerrar";
1025
1084
  return /* @__PURE__ */ jsxRuntime.jsx(
1026
1085
  MediaViewerDialog,
1027
1086
  {
1028
1087
  open,
1029
1088
  onOpenChange,
1089
+ showCloseButton: false,
1030
1090
  labels: {
1031
- close: labels.close,
1091
+ close: closeLabel,
1032
1092
  title,
1033
1093
  description: labels.dialogDescription ?? title
1034
1094
  },
1035
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-full min-h-0 flex-col pt-12", children: [
1036
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-b px-4 py-2 pr-14", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "truncate text-sm font-medium", children: title }) }),
1037
- /* @__PURE__ */ jsxRuntime.jsx(
1038
- PdfViewer,
1039
- {
1040
- fileUrl,
1041
- fileName: fileName ?? title,
1042
- downloadUrl,
1043
- workerSrc,
1044
- initialPage,
1045
- labels: viewerLabels,
1046
- className: "min-h-0 flex-1"
1047
- }
1048
- )
1049
- ] })
1095
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1096
+ PdfViewer,
1097
+ {
1098
+ fileUrl,
1099
+ fileName: fileName ?? title,
1100
+ downloadUrl,
1101
+ workerSrc,
1102
+ initialPage,
1103
+ labels: viewerLabels,
1104
+ className: "min-h-0 w-full min-w-0 flex-1",
1105
+ closeAction: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
1106
+ /* @__PURE__ */ jsxRuntime.jsx(
1107
+ "button",
1108
+ {
1109
+ type: "button",
1110
+ onClick: () => onOpenChange(false),
1111
+ "aria-label": closeLabel,
1112
+ className: "inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-md border bg-background text-foreground hover:bg-accent",
1113
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-4 w-4" })
1114
+ }
1115
+ ),
1116
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "max-w-[min(40vw,16rem)] truncate text-sm font-medium text-foreground", children: title })
1117
+ ] })
1118
+ }
1119
+ )
1050
1120
  }
1051
1121
  );
1052
1122
  }