@zerohive/hive-viewer 0.2.3 → 0.2.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.js CHANGED
@@ -569,12 +569,12 @@ function PdfRenderer(props) {
569
569
  props.onThumbs(thumbs);
570
570
  }, [thumbs]);
571
571
  const pagesToShow = useMemo4(() => {
572
- if (props.layout === "side-by-side") {
573
- const left = props.currentPage;
574
- const right = Math.min(pageCount || left + 1, left + 1);
575
- return [left, right];
572
+ if (props.layout === "side-by-side" && pageCount > 1) {
573
+ const left = Math.max(1, Math.min(props.currentPage, pageCount));
574
+ const right = Math.max(1, Math.min(left + 1, pageCount));
575
+ return left === right ? [left] : [left, right];
576
576
  }
577
- return [props.currentPage];
577
+ return [Math.max(1, Math.min(props.currentPage, pageCount))];
578
578
  }, [props.currentPage, props.layout, pageCount]);
579
579
  useEffect4(() => {
580
580
  if (!doc) {
@@ -750,12 +750,13 @@ function PptxRenderer(props) {
750
750
  props.onThumbs(thumbs);
751
751
  }, [thumbs]);
752
752
  const pagesToShow = useMemo5(() => {
753
- if (props.layout === "side-by-side")
754
- return [
755
- props.currentPage,
756
- Math.min(slides.length || props.currentPage + 1, props.currentPage + 1)
757
- ];
758
- return [props.currentPage];
753
+ const total = slides.length;
754
+ if (props.layout === "side-by-side" && total > 1) {
755
+ const left = Math.max(1, Math.min(props.currentPage, total));
756
+ const right = Math.max(1, Math.min(left + 1, total));
757
+ return left === right ? [left] : [left, right];
758
+ }
759
+ return [Math.max(1, Math.min(props.currentPage, total))];
759
760
  }, [props.currentPage, props.layout, slides.length]);
760
761
  return /* @__PURE__ */ jsxs5("div", { className: "hv-doc", children: [
761
762
  loading && /* @__PURE__ */ jsx5("div", { className: "hv-loading", children: "Loading PPTX\u2026" }),
@@ -814,87 +815,209 @@ var defaultLocale = {
814
815
  };
815
816
 
816
817
  // src/components/SignaturePanel.tsx
818
+ import React6 from "react";
817
819
  import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
818
820
  function SignaturePanel(props) {
819
821
  const title = props.locale["signatures.title"] ?? "Signatures";
820
- return /* @__PURE__ */ jsxs6("aside", { className: props.collapsed ? "hv-side hv-side--collapsed" : "hv-side", "aria-label": title, children: [
821
- /* @__PURE__ */ jsxs6("div", { className: "hv-sidebar-header", children: [
822
- /* @__PURE__ */ jsx6("button", { type: "button", className: "hv-icon", onClick: props.onToggle, "aria-label": props.locale["toolbar.signatures"] ?? "Signatures", children: "\u270D" }),
823
- /* @__PURE__ */ jsx6("div", { className: "hv-sidebar-title", children: title })
824
- ] }),
825
- /* @__PURE__ */ jsx6("div", { className: "hv-sidebar-body", children: props.signatures.map((s, idx) => /* @__PURE__ */ jsxs6("div", { className: "hv-signature-card", children: [
826
- /* @__PURE__ */ jsx6("img", { src: s.signatureImageUrl, alt: `Signature by ${s.signedBy}`, className: "hv-signature-img" }),
827
- /* @__PURE__ */ jsxs6("div", { className: "hv-signature-meta", children: [
828
- /* @__PURE__ */ jsx6("div", { className: "hv-signature-name", children: s.signedBy }),
829
- /* @__PURE__ */ jsx6("div", { className: "hv-signature-date", children: new Date(s.dateSigned).toLocaleString() }),
830
- s.comment ? /* @__PURE__ */ jsx6("div", { className: "hv-signature-comment", children: s.comment }) : null
831
- ] })
832
- ] }, `${s.signedBy}-${s.dateSigned}-${idx}`)) })
833
- ] });
822
+ const deduped = React6.useMemo(() => {
823
+ const seen = /* @__PURE__ */ new Set();
824
+ return props.signatures.filter((s) => {
825
+ const key = `${s.signedBy}|${s.dateSigned}|${s.signatureImageUrl}`;
826
+ if (seen.has(key)) return false;
827
+ seen.add(key);
828
+ return true;
829
+ });
830
+ }, [props.signatures]);
831
+ return /* @__PURE__ */ jsxs6(
832
+ "aside",
833
+ {
834
+ className: props.collapsed ? "hv-side hv-side--collapsed" : "hv-side",
835
+ "aria-label": title,
836
+ children: [
837
+ /* @__PURE__ */ jsxs6("div", { className: "hv-sidebar-header", children: [
838
+ /* @__PURE__ */ jsx6(
839
+ "button",
840
+ {
841
+ type: "button",
842
+ className: "hv-icon",
843
+ onClick: props.onToggle,
844
+ "aria-label": props.locale["toolbar.signatures"] ?? "Signatures",
845
+ children: /* @__PURE__ */ jsx6("span", { "aria-hidden": true, children: "\u270D" })
846
+ }
847
+ ),
848
+ /* @__PURE__ */ jsx6("div", { className: "hv-sidebar-title", children: title })
849
+ ] }),
850
+ /* @__PURE__ */ jsxs6("div", { className: "hv-sidebar-body", children: [
851
+ deduped.length === 0 && /* @__PURE__ */ jsx6("div", { className: "hv-signature-empty", "aria-live": "polite", children: props.locale["signatures.empty"] ?? "No signatures yet." }),
852
+ deduped.map((s, idx) => /* @__PURE__ */ jsxs6(
853
+ "div",
854
+ {
855
+ className: "hv-signature-card",
856
+ tabIndex: 0,
857
+ "aria-label": `Signature by ${s.signedBy}`,
858
+ children: [
859
+ /* @__PURE__ */ jsx6(
860
+ "img",
861
+ {
862
+ src: s.signatureImageUrl,
863
+ alt: props.locale["signatures.imgAlt"] ? props.locale["signatures.imgAlt"].replace(
864
+ "{name}",
865
+ s.signedBy
866
+ ) : `Signature by ${s.signedBy}`,
867
+ className: "hv-signature-img"
868
+ }
869
+ ),
870
+ /* @__PURE__ */ jsxs6("div", { className: "hv-signature-meta", children: [
871
+ /* @__PURE__ */ jsx6("div", { className: "hv-signature-name", children: s.signedBy }),
872
+ /* @__PURE__ */ jsx6("div", { className: "hv-signature-date", children: new Date(s.dateSigned).toLocaleString() }),
873
+ s.comment ? /* @__PURE__ */ jsx6("div", { className: "hv-signature-comment", children: s.comment }) : null
874
+ ] })
875
+ ]
876
+ },
877
+ `${s.signedBy}-${s.dateSigned}-${s.signatureImageUrl}`
878
+ ))
879
+ ] })
880
+ ]
881
+ }
882
+ );
834
883
  }
835
884
 
836
885
  // src/components/ThumbnailsSidebar.tsx
837
886
  import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
838
887
  function ThumbnailsSidebar(props) {
839
888
  const t = props.locale["thumbnails.title"] ?? "Thumbnails";
840
- return /* @__PURE__ */ jsxs7("aside", { className: props.collapsed ? "hv-thumbs hv-thumbs--collapsed" : "hv-thumbs", "aria-label": t, children: [
841
- /* @__PURE__ */ jsxs7("div", { className: "hv-thumbs__header", children: [
842
- /* @__PURE__ */ jsx7(
843
- "button",
844
- {
845
- type: "button",
846
- className: "hv-icon",
847
- onClick: props.onToggle,
848
- "aria-label": props.collapsed ? props.locale["thumbnails.open"] ?? "Open thumbnails" : props.locale["thumbnails.close"] ?? "Close thumbnails",
849
- children: props.collapsed ? "\u25B8" : "\u25BE"
850
- }
851
- ),
852
- !props.collapsed ? /* @__PURE__ */ jsx7("div", { className: "hv-thumbs__title", children: t }) : null
853
- ] }),
854
- !props.collapsed ? /* @__PURE__ */ jsx7("div", { className: "hv-thumbs__list", role: "list", children: props.thumbnails.map((th, idx) => {
855
- const p = idx + 1;
856
- const active = p === props.currentPage;
857
- return /* @__PURE__ */ jsxs7(
858
- "button",
859
- {
860
- type: "button",
861
- role: "listitem",
862
- className: active ? "hv-thumb hv-thumb--active" : "hv-thumb",
863
- onClick: () => props.onSelectPage(p),
864
- "aria-current": active ? "page" : void 0,
865
- children: [
866
- /* @__PURE__ */ jsx7("div", { className: "hv-thumb__img", "aria-hidden": true, children: th.dataUrl ? /* @__PURE__ */ jsx7("img", { src: th.dataUrl, alt: "" }) : /* @__PURE__ */ jsx7("div", { className: "hv-thumb__placeholder" }) }),
867
- /* @__PURE__ */ jsx7("div", { className: "hv-thumb__label", children: th.label })
868
- ]
869
- },
870
- th.id
871
- );
872
- }) }) : null
873
- ] });
889
+ return /* @__PURE__ */ jsxs7(
890
+ "aside",
891
+ {
892
+ className: props.collapsed ? "hv-thumbs hv-thumbs--collapsed" : "hv-thumbs",
893
+ "aria-label": t,
894
+ children: [
895
+ /* @__PURE__ */ jsxs7("div", { className: "hv-thumbs-header", children: [
896
+ /* @__PURE__ */ jsx7(
897
+ "button",
898
+ {
899
+ type: "button",
900
+ className: "hv-thumbs-toggle",
901
+ onClick: props.onToggle,
902
+ "aria-label": props.collapsed ? props.locale["thumbnails.open"] ?? "Open thumbnails" : props.locale["thumbnails.close"] ?? "Close thumbnails",
903
+ children: /* @__PURE__ */ jsx7("span", { className: "hv-thumbs-toggle-icon", children: props.collapsed ? "\u25B8" : "\u25BE" })
904
+ }
905
+ ),
906
+ !props.collapsed && /* @__PURE__ */ jsx7("div", { className: "hv-thumbs-title", children: t })
907
+ ] }),
908
+ !props.collapsed && /* @__PURE__ */ jsx7("div", { className: "hv-thumbs-list", role: "list", children: props.thumbnails.map((th, idx) => {
909
+ const p = idx + 1;
910
+ const active = p === props.currentPage;
911
+ return /* @__PURE__ */ jsxs7(
912
+ "button",
913
+ {
914
+ type: "button",
915
+ role: "listitem",
916
+ className: active ? "hv-thumb hv-thumb--active" : "hv-thumb",
917
+ onClick: () => props.onSelectPage(p),
918
+ "aria-current": active ? "page" : void 0,
919
+ tabIndex: 0,
920
+ children: [
921
+ /* @__PURE__ */ jsx7("div", { className: "hv-thumb-img", "aria-hidden": true, children: th.dataUrl ? /* @__PURE__ */ jsx7("img", { src: th.dataUrl, alt: "" }) : /* @__PURE__ */ jsx7("div", { className: "hv-thumb-placeholder" }) }),
922
+ /* @__PURE__ */ jsx7("div", { className: "hv-thumb-label", children: th.label })
923
+ ]
924
+ },
925
+ th.id
926
+ );
927
+ }) })
928
+ ]
929
+ }
930
+ );
874
931
  }
875
932
 
876
933
  // src/components/Toolbar.tsx
877
934
  import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
878
935
  function Toolbar(props) {
879
936
  const t = (k, fallback) => props.locale[k] ?? fallback;
880
- return /* @__PURE__ */ jsxs8("div", { className: "hv-toolbar", role: "toolbar", "aria-label": t("a11y.toolbar", "Document toolbar"), children: [
881
- /* @__PURE__ */ jsxs8("div", { className: "hv-toolbar__left", children: [
882
- /* @__PURE__ */ jsx8("button", { type: "button", className: "hv-btn", onClick: props.onToggleThumbnails, "aria-pressed": props.showThumbnails, children: t("toolbar.thumbs", "Thumbnails") }),
883
- props.mode !== "create" && /* @__PURE__ */ jsx8("button", { type: "button", className: "hv-btn", onClick: props.onToggleSignatures, "aria-pressed": props.showSignatures, children: t("toolbar.signatures", "Signatures") }),
884
- /* @__PURE__ */ jsx8("span", { className: "hv-sep" }),
885
- /* @__PURE__ */ jsx8("button", { type: "button", className: props.layout === "single" ? "hv-btn hv-btn--active" : "hv-btn", onClick: () => props.onChangeLayout("single"), children: t("toolbar.layout.single", "Single") }),
886
- /* @__PURE__ */ jsx8("button", { type: "button", className: props.layout === "side-by-side" ? "hv-btn hv-btn--active" : "hv-btn", onClick: () => props.onChangeLayout("side-by-side"), children: t("toolbar.layout.two", "Two") })
887
- ] }),
888
- /* @__PURE__ */ jsxs8("div", { className: "hv-toolbar__right", children: [
889
- props.showHeaderFooterToggle && /* @__PURE__ */ jsxs8("label", { className: "hv-toggle", children: [
890
- /* @__PURE__ */ jsx8("input", { type: "checkbox", checked: props.headerFooterEnabled, onChange: props.onToggleHeaderFooter }),
891
- /* @__PURE__ */ jsx8("span", { children: t("toolbar.letterhead", "Letterhead") })
892
- ] }),
893
- props.allowSigning && /* @__PURE__ */ jsx8("button", { type: "button", className: "hv-btn hv-btn--primary", onClick: props.onSign, disabled: props.signingDisabled, children: t("toolbar.sign", "Sign Document") }),
894
- props.canExportPdf && /* @__PURE__ */ jsx8("button", { type: "button", className: "hv-btn", onClick: props.onExportPdf, children: t("toolbar.exportPdf", "Export as PDF") }),
895
- props.canSave && /* @__PURE__ */ jsx8("button", { type: "button", className: "hv-btn hv-btn--primary", onClick: props.onSave, children: t("toolbar.save", "Save") })
896
- ] })
897
- ] });
937
+ return /* @__PURE__ */ jsxs8(
938
+ "div",
939
+ {
940
+ className: "hv-toolbar",
941
+ role: "toolbar",
942
+ "aria-label": t("a11y.toolbar", "Document toolbar"),
943
+ children: [
944
+ /* @__PURE__ */ jsxs8("div", { className: "hv-toolbar__left gap-2", children: [
945
+ /* @__PURE__ */ jsx8(
946
+ "button",
947
+ {
948
+ type: "button",
949
+ className: "hv-btn",
950
+ onClick: props.onToggleThumbnails,
951
+ "aria-pressed": props.showThumbnails,
952
+ children: t("toolbar.thumbs", "Thumbnails")
953
+ }
954
+ ),
955
+ props.mode !== "create" && /* @__PURE__ */ jsx8(
956
+ "button",
957
+ {
958
+ type: "button",
959
+ className: "hv-btn",
960
+ onClick: props.onToggleSignatures,
961
+ "aria-pressed": props.showSignatures,
962
+ children: t("toolbar.signatures", "Signatures")
963
+ }
964
+ ),
965
+ /* @__PURE__ */ jsx8("span", { className: "hv-sep" }),
966
+ /* @__PURE__ */ jsx8(
967
+ "button",
968
+ {
969
+ type: "button",
970
+ className: props.layout === "single" ? "hv-btn hv-btn--active" : "hv-btn",
971
+ onClick: () => props.onChangeLayout("single"),
972
+ children: t("toolbar.layout.single", "Single")
973
+ }
974
+ ),
975
+ /* @__PURE__ */ jsx8(
976
+ "button",
977
+ {
978
+ type: "button",
979
+ className: props.layout === "side-by-side" ? "hv-btn hv-btn--active" : "hv-btn",
980
+ onClick: () => props.onChangeLayout("side-by-side"),
981
+ children: t("toolbar.layout.two", "Two")
982
+ }
983
+ )
984
+ ] }),
985
+ /* @__PURE__ */ jsxs8("div", { className: "hv-toolbar__right", children: [
986
+ props.showHeaderFooterToggle && /* @__PURE__ */ jsxs8("label", { className: "hv-toggle", children: [
987
+ /* @__PURE__ */ jsx8(
988
+ "input",
989
+ {
990
+ type: "checkbox",
991
+ checked: props.headerFooterEnabled,
992
+ onChange: props.onToggleHeaderFooter
993
+ }
994
+ ),
995
+ /* @__PURE__ */ jsx8("span", { children: t("toolbar.letterhead", "Letterhead") })
996
+ ] }),
997
+ props.allowSigning && /* @__PURE__ */ jsx8(
998
+ "button",
999
+ {
1000
+ type: "button",
1001
+ className: "hv-btn hv-btn--primary",
1002
+ onClick: props.onSign,
1003
+ disabled: props.signingDisabled,
1004
+ children: t("toolbar.sign", "Sign Document")
1005
+ }
1006
+ ),
1007
+ props.canExportPdf && /* @__PURE__ */ jsx8("button", { type: "button", className: "hv-btn", onClick: props.onExportPdf, children: t("toolbar.exportPdf", "Export as PDF") }),
1008
+ props.canSave && /* @__PURE__ */ jsx8(
1009
+ "button",
1010
+ {
1011
+ type: "button",
1012
+ className: "hv-btn hv-btn--primary",
1013
+ onClick: props.onSave,
1014
+ children: t("toolbar.save", "Save")
1015
+ }
1016
+ )
1017
+ ] })
1018
+ ]
1019
+ }
1020
+ );
898
1021
  }
899
1022
 
900
1023
  // src/components/DocumentViewer.tsx