@zerohive/hive-viewer 0.2.4 → 0.2.6
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 +182 -223
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +183 -225
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +183 -225
- package/dist/styles.css +66 -2
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -10,8 +10,7 @@ import {
|
|
|
10
10
|
useEffect,
|
|
11
11
|
useImperativeHandle,
|
|
12
12
|
useMemo,
|
|
13
|
-
useRef
|
|
14
|
-
useState
|
|
13
|
+
useRef
|
|
15
14
|
} from "react";
|
|
16
15
|
|
|
17
16
|
// src/utils/sanitize.ts
|
|
@@ -26,222 +25,122 @@ function sanitizeHtml(html) {
|
|
|
26
25
|
// src/editors/RichTextEditor.tsx
|
|
27
26
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
28
27
|
var PAGE_H = 1122;
|
|
29
|
-
var RichTextEditor = forwardRef(
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
if (props.fileType === "docx") {
|
|
50
|
-
const res = await mammoth.convertToHtml({
|
|
51
|
-
arrayBuffer: props.arrayBuffer
|
|
52
|
-
});
|
|
53
|
-
if (!cancelled) {
|
|
54
|
-
setHtml(sanitizeHtml(res.value || "<p><br/></p>"));
|
|
28
|
+
var RichTextEditor = forwardRef(
|
|
29
|
+
(props, ref) => {
|
|
30
|
+
const readOnly = props.mode === "view";
|
|
31
|
+
const md = useMemo(
|
|
32
|
+
() => new MarkdownIt({ html: false, linkify: true, breaks: true }),
|
|
33
|
+
[]
|
|
34
|
+
);
|
|
35
|
+
const scrollerRef = useRef(null);
|
|
36
|
+
const editorRef = useRef(null);
|
|
37
|
+
const captureRef = useRef(null);
|
|
38
|
+
const initialized = useRef(false);
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
if (initialized.current) return;
|
|
41
|
+
(async () => {
|
|
42
|
+
if (props.mode === "create") {
|
|
43
|
+
editorRef.current.innerHTML = "<p><br/></p>";
|
|
44
|
+
initialized.current = true;
|
|
45
|
+
return;
|
|
55
46
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
47
|
+
if (!props.arrayBuffer) return;
|
|
48
|
+
if (props.fileType === "docx") {
|
|
49
|
+
const res = await mammoth.convertToHtml({
|
|
50
|
+
arrayBuffer: props.arrayBuffer
|
|
51
|
+
});
|
|
52
|
+
editorRef.current.innerHTML = sanitizeHtml(
|
|
53
|
+
res.value || "<p><br/></p>"
|
|
54
|
+
);
|
|
60
55
|
} else {
|
|
61
|
-
|
|
56
|
+
const text = new TextDecoder().decode(props.arrayBuffer);
|
|
57
|
+
editorRef.current.innerHTML = props.fileType === "md" ? sanitizeHtml(md.render(text)) : `<pre>${escapeHtml(text)}</pre>`;
|
|
62
58
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}, [html, props.headerFooterEnabled]);
|
|
80
|
-
function exec(cmd) {
|
|
81
|
-
if (readOnly) {
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
document.execCommand(cmd);
|
|
85
|
-
}
|
|
86
|
-
function onClick(e) {
|
|
87
|
-
if (!props.armedSignatureUrl) {
|
|
88
|
-
return;
|
|
59
|
+
initialized.current = true;
|
|
60
|
+
})();
|
|
61
|
+
}, [props.arrayBuffer, props.fileType, props.mode, md]);
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
const el = scrollerRef.current;
|
|
64
|
+
if (!el) return;
|
|
65
|
+
const recompute = () => props.onPageCount(Math.max(1, Math.ceil(el.scrollHeight / PAGE_H)));
|
|
66
|
+
recompute();
|
|
67
|
+
const ro = new ResizeObserver(recompute);
|
|
68
|
+
ro.observe(el);
|
|
69
|
+
return () => ro.disconnect();
|
|
70
|
+
}, [props.headerFooterEnabled]);
|
|
71
|
+
function exec(cmd) {
|
|
72
|
+
if (readOnly) return;
|
|
73
|
+
document.execCommand(cmd);
|
|
74
|
+
editorRef.current?.focus();
|
|
89
75
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const scroller = scrollerRef.current;
|
|
104
|
-
const capture = captureRef.current;
|
|
105
|
-
if (!scroller || !capture) {
|
|
106
|
-
return void 0;
|
|
107
|
-
}
|
|
108
|
-
const old = scroller.scrollTop;
|
|
109
|
-
scroller.scrollTop = index * PAGE_H;
|
|
110
|
-
await new Promise((r) => requestAnimationFrame(() => r(null)));
|
|
111
|
-
try {
|
|
112
|
-
const canvas = await html2canvas(capture, {
|
|
113
|
-
backgroundColor: null,
|
|
114
|
-
scale: 0.25,
|
|
115
|
-
useCORS: true
|
|
116
|
-
});
|
|
117
|
-
return canvas.toDataURL("image/png");
|
|
118
|
-
} catch {
|
|
119
|
-
return void 0;
|
|
120
|
-
} finally {
|
|
121
|
-
scroller.scrollTop = old;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
async function save(exportPdf) {
|
|
125
|
-
const inner = editorRef.current?.innerHTML ?? html;
|
|
126
|
-
const stitched = `<!doctype html><html><head><meta charset="utf-8" /></head><body>${inner}</body></html>`;
|
|
127
|
-
if (exportPdf) {
|
|
128
|
-
const b642 = btoa(unescape(encodeURIComponent(stitched)));
|
|
129
|
-
props.onSave(b642, {
|
|
130
|
-
fileName: replaceExt(props.fileName, "html"),
|
|
131
|
-
fileType: "txt",
|
|
132
|
-
exportedAsPdf: true,
|
|
133
|
-
annotations: { signaturePlacements: props.signaturePlacements }
|
|
76
|
+
function onClickPage(e) {
|
|
77
|
+
if (!props.armedSignatureUrl) return;
|
|
78
|
+
const scroller = scrollerRef.current;
|
|
79
|
+
if (!scroller) return;
|
|
80
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
81
|
+
const absY = scroller.scrollTop + (e.clientY - rect.top);
|
|
82
|
+
const page = Math.floor(absY / PAGE_H) + 1;
|
|
83
|
+
props.onPlaceSignature({
|
|
84
|
+
page,
|
|
85
|
+
x: (e.clientX - rect.left) / rect.width,
|
|
86
|
+
y: absY % PAGE_H / PAGE_H,
|
|
87
|
+
w: 0.25,
|
|
88
|
+
h: 0.1
|
|
134
89
|
});
|
|
135
|
-
return;
|
|
136
90
|
}
|
|
137
|
-
|
|
91
|
+
async function requestThumbnail(index) {
|
|
92
|
+
const scroller = scrollerRef.current;
|
|
93
|
+
const capture = captureRef.current;
|
|
94
|
+
if (!scroller || !capture) return;
|
|
95
|
+
const old = scroller.scrollTop;
|
|
96
|
+
scroller.scrollTop = index * PAGE_H;
|
|
97
|
+
await new Promise((r) => requestAnimationFrame(r));
|
|
138
98
|
try {
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
body: JSON.stringify({
|
|
143
|
-
html: stitched,
|
|
144
|
-
fileName: replaceExt(props.fileName, "docx")
|
|
145
|
-
})
|
|
99
|
+
const canvas = await html2canvas(capture, {
|
|
100
|
+
scale: 0.25,
|
|
101
|
+
useCORS: true
|
|
146
102
|
});
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
const blob = await response.blob();
|
|
151
|
-
const url = window.URL.createObjectURL(blob);
|
|
152
|
-
const a = document.createElement("a");
|
|
153
|
-
a.href = url;
|
|
154
|
-
a.download = replaceExt(props.fileName, "docx");
|
|
155
|
-
document.body.appendChild(a);
|
|
156
|
-
a.click();
|
|
157
|
-
setTimeout(() => {
|
|
158
|
-
window.URL.revokeObjectURL(url);
|
|
159
|
-
a.remove();
|
|
160
|
-
}, 100);
|
|
161
|
-
} catch (err) {
|
|
162
|
-
alert(
|
|
163
|
-
"DOCX export failed: " + (err instanceof Error ? err.message : String(err))
|
|
164
|
-
);
|
|
103
|
+
return canvas.toDataURL("image/png");
|
|
104
|
+
} finally {
|
|
105
|
+
scroller.scrollTop = old;
|
|
165
106
|
}
|
|
166
|
-
return;
|
|
167
107
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
className: "hv-btn",
|
|
192
|
-
onClick: () => exec("italic"),
|
|
193
|
-
disabled: readOnly,
|
|
194
|
-
children: "I"
|
|
195
|
-
}
|
|
196
|
-
),
|
|
197
|
-
/* @__PURE__ */ jsx(
|
|
198
|
-
"button",
|
|
199
|
-
{
|
|
200
|
-
className: "hv-btn",
|
|
201
|
-
onClick: () => exec("underline"),
|
|
202
|
-
disabled: readOnly,
|
|
203
|
-
children: "U"
|
|
204
|
-
}
|
|
205
|
-
),
|
|
206
|
-
props.armedSignatureUrl ? /* @__PURE__ */ jsx("div", { className: "hv-hint", children: "Click to place signature" }) : null
|
|
207
|
-
] }),
|
|
208
|
-
/* @__PURE__ */ jsx("div", { className: "hv-scroll", ref: scrollerRef, onClick, children: /* @__PURE__ */ jsxs("div", { className: "hv-pageStage", ref: captureRef, children: [
|
|
209
|
-
props.headerFooterEnabled && props.headerComponent ? /* @__PURE__ */ jsx("div", { className: "hv-letterhead", children: props.headerComponent }) : null,
|
|
210
|
-
/* @__PURE__ */ jsx(
|
|
108
|
+
async function save(exportPdf) {
|
|
109
|
+
const html = editorRef.current?.innerHTML ?? "";
|
|
110
|
+
const stitched = `<!doctype html><html><body>${html}</body></html>`;
|
|
111
|
+
const b64 = btoa(unescape(encodeURIComponent(stitched)));
|
|
112
|
+
props.onSave(b64, {
|
|
113
|
+
fileName: props.fileName,
|
|
114
|
+
fileType: props.fileType,
|
|
115
|
+
exportedAsPdf: exportPdf,
|
|
116
|
+
annotations: { signaturePlacements: props.signaturePlacements }
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
useImperativeHandle(ref, () => ({
|
|
120
|
+
save,
|
|
121
|
+
requestThumbnail
|
|
122
|
+
}));
|
|
123
|
+
return /* @__PURE__ */ jsxs("div", { className: "hv-root", children: [
|
|
124
|
+
/* @__PURE__ */ jsxs("div", { className: "hv-toolbar", children: [
|
|
125
|
+
/* @__PURE__ */ jsx("button", { onClick: () => exec("bold"), disabled: readOnly, children: "B" }),
|
|
126
|
+
/* @__PURE__ */ jsx("button", { onClick: () => exec("italic"), disabled: readOnly, children: "I" }),
|
|
127
|
+
/* @__PURE__ */ jsx("button", { onClick: () => exec("underline"), disabled: readOnly, children: "U" }),
|
|
128
|
+
props.armedSignatureUrl && /* @__PURE__ */ jsx("span", { className: "hv-hint", children: "Click page to place signature" })
|
|
129
|
+
] }),
|
|
130
|
+
/* @__PURE__ */ jsx("div", { className: "hv-scroll", ref: scrollerRef, onClick: onClickPage, children: /* @__PURE__ */ jsx("div", { className: "hv-pageStage", ref: captureRef, children: /* @__PURE__ */ jsx(
|
|
211
131
|
"div",
|
|
212
132
|
{
|
|
213
133
|
ref: editorRef,
|
|
214
|
-
className: readOnly ? "
|
|
134
|
+
className: `hv-editor ${readOnly ? "ro" : ""}`,
|
|
215
135
|
contentEditable: !readOnly,
|
|
216
|
-
suppressContentEditableWarning: true
|
|
217
|
-
onInput: () => setHtml(editorRef.current?.innerHTML ?? ""),
|
|
218
|
-
dangerouslySetInnerHTML: { __html: html }
|
|
136
|
+
suppressContentEditableWarning: true
|
|
219
137
|
}
|
|
220
|
-
)
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
"img",
|
|
225
|
-
{
|
|
226
|
-
src: s.signatureImageUrl,
|
|
227
|
-
alt: "",
|
|
228
|
-
className: "hv-sign-img"
|
|
229
|
-
}
|
|
230
|
-
),
|
|
231
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
232
|
-
/* @__PURE__ */ jsx("div", { className: "hv-sign-name", children: s.signedBy }),
|
|
233
|
-
/* @__PURE__ */ jsx("div", { className: "hv-sign-date", children: new Date(s.dateSigned).toLocaleString() })
|
|
234
|
-
] })
|
|
235
|
-
] }, i)) }) : null
|
|
236
|
-
] }) })
|
|
237
|
-
] });
|
|
238
|
-
});
|
|
239
|
-
function replaceExt(name, ext) {
|
|
240
|
-
const base = name.includes(".") ? name.slice(0, name.lastIndexOf(".")) : name;
|
|
241
|
-
return `${base}.${ext}`;
|
|
242
|
-
}
|
|
138
|
+
) }) })
|
|
139
|
+
] });
|
|
140
|
+
}
|
|
141
|
+
);
|
|
243
142
|
function escapeHtml(s) {
|
|
244
|
-
return s.replace(/&/g, "&").replace(/</g, "<")
|
|
143
|
+
return s.replace(/&/g, "&").replace(/</g, "<");
|
|
245
144
|
}
|
|
246
145
|
|
|
247
146
|
// src/editors/SpreadsheetEditor.tsx
|
|
@@ -941,12 +840,12 @@ function Toolbar(props) {
|
|
|
941
840
|
role: "toolbar",
|
|
942
841
|
"aria-label": t("a11y.toolbar", "Document toolbar"),
|
|
943
842
|
children: [
|
|
944
|
-
/* @__PURE__ */ jsxs8("div", { className: "hv-toolbar__left
|
|
843
|
+
/* @__PURE__ */ jsxs8("div", { className: "hv-toolbar__left space-x-1", children: [
|
|
945
844
|
/* @__PURE__ */ jsx8(
|
|
946
845
|
"button",
|
|
947
846
|
{
|
|
948
847
|
type: "button",
|
|
949
|
-
className: "hv-btn",
|
|
848
|
+
className: "hv-btn text-sm",
|
|
950
849
|
onClick: props.onToggleThumbnails,
|
|
951
850
|
"aria-pressed": props.showThumbnails,
|
|
952
851
|
children: t("toolbar.thumbs", "Thumbnails")
|
|
@@ -956,7 +855,7 @@ function Toolbar(props) {
|
|
|
956
855
|
"button",
|
|
957
856
|
{
|
|
958
857
|
type: "button",
|
|
959
|
-
className: "hv-btn",
|
|
858
|
+
className: "hv-btn text-sm",
|
|
960
859
|
onClick: props.onToggleSignatures,
|
|
961
860
|
"aria-pressed": props.showSignatures,
|
|
962
861
|
children: t("toolbar.signatures", "Signatures")
|
|
@@ -967,7 +866,7 @@ function Toolbar(props) {
|
|
|
967
866
|
"button",
|
|
968
867
|
{
|
|
969
868
|
type: "button",
|
|
970
|
-
className: props.layout === "single" ? "hv-btn hv-btn--active" : "hv-btn",
|
|
869
|
+
className: props.layout === "single" ? "hv-btn hv-btn--active text-sm" : "hv-btn text-sm",
|
|
971
870
|
onClick: () => props.onChangeLayout("single"),
|
|
972
871
|
children: t("toolbar.layout.single", "Single")
|
|
973
872
|
}
|
|
@@ -976,7 +875,7 @@ function Toolbar(props) {
|
|
|
976
875
|
"button",
|
|
977
876
|
{
|
|
978
877
|
type: "button",
|
|
979
|
-
className: props.layout === "side-by-side" ? "hv-btn hv-btn--active" : "hv-btn",
|
|
878
|
+
className: props.layout === "side-by-side" ? "hv-btn hv-btn--active text-sm" : "hv-btn text-sm",
|
|
980
879
|
onClick: () => props.onChangeLayout("side-by-side"),
|
|
981
880
|
children: t("toolbar.layout.two", "Two")
|
|
982
881
|
}
|
|
@@ -998,18 +897,26 @@ function Toolbar(props) {
|
|
|
998
897
|
"button",
|
|
999
898
|
{
|
|
1000
899
|
type: "button",
|
|
1001
|
-
className: "hv-btn hv-btn--primary",
|
|
900
|
+
className: "hv-btn hv-btn--primary text-sm",
|
|
1002
901
|
onClick: props.onSign,
|
|
1003
902
|
disabled: props.signingDisabled,
|
|
1004
903
|
children: t("toolbar.sign", "Sign Document")
|
|
1005
904
|
}
|
|
1006
905
|
),
|
|
1007
|
-
props.canExportPdf && /* @__PURE__ */ jsx8(
|
|
906
|
+
props.canExportPdf && /* @__PURE__ */ jsx8(
|
|
907
|
+
"button",
|
|
908
|
+
{
|
|
909
|
+
type: "button",
|
|
910
|
+
className: "hv-btn text-sm",
|
|
911
|
+
onClick: props.onExportPdf,
|
|
912
|
+
children: t("toolbar.exportPdf", "Export as PDF")
|
|
913
|
+
}
|
|
914
|
+
),
|
|
1008
915
|
props.canSave && /* @__PURE__ */ jsx8(
|
|
1009
916
|
"button",
|
|
1010
917
|
{
|
|
1011
918
|
type: "button",
|
|
1012
|
-
className: "hv-btn hv-btn--primary",
|
|
919
|
+
className: "hv-btn hv-btn--primary text-sm",
|
|
1013
920
|
onClick: props.onSave,
|
|
1014
921
|
children: t("toolbar.save", "Save")
|
|
1015
922
|
}
|
|
@@ -1025,8 +932,13 @@ import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
|
1025
932
|
function DocumentViewer(props) {
|
|
1026
933
|
const mode = props.mode ?? "view";
|
|
1027
934
|
const theme = props.theme ?? "light";
|
|
1028
|
-
const locale = useMemo6(
|
|
1029
|
-
|
|
935
|
+
const locale = useMemo6(
|
|
936
|
+
() => ({ ...defaultLocale, ...props.locale ?? {} }),
|
|
937
|
+
[props.locale]
|
|
938
|
+
);
|
|
939
|
+
const [layout, setLayout] = useState6(
|
|
940
|
+
props.defaultLayout ?? "single"
|
|
941
|
+
);
|
|
1030
942
|
const [showThumbnails, setShowThumbnails] = useState6(true);
|
|
1031
943
|
const [showSignatures, setShowSignatures] = useState6(true);
|
|
1032
944
|
const [headerFooterEnabled, setHeaderFooterEnabled] = useState6(true);
|
|
@@ -1037,10 +949,17 @@ function DocumentViewer(props) {
|
|
|
1037
949
|
const [pageCount, setPageCount] = useState6(1);
|
|
1038
950
|
const [currentPage, setCurrentPage] = useState6(1);
|
|
1039
951
|
const [thumbs, setThumbs] = useState6([]);
|
|
1040
|
-
const [localSignatures, setLocalSignatures] = useState6(
|
|
1041
|
-
|
|
952
|
+
const [localSignatures, setLocalSignatures] = useState6(
|
|
953
|
+
props.signatures ?? []
|
|
954
|
+
);
|
|
955
|
+
useEffect6(
|
|
956
|
+
() => setLocalSignatures(props.signatures ?? []),
|
|
957
|
+
[props.signatures]
|
|
958
|
+
);
|
|
1042
959
|
const [sigPlacements, setSigPlacements] = useState6([]);
|
|
1043
|
-
const [armedSignatureUrl, setArmedSignatureUrl] = useState6(
|
|
960
|
+
const [armedSignatureUrl, setArmedSignatureUrl] = useState6(
|
|
961
|
+
null
|
|
962
|
+
);
|
|
1044
963
|
const editorRef = useRef3(null);
|
|
1045
964
|
useEffect6(() => {
|
|
1046
965
|
let cancelled = false;
|
|
@@ -1054,7 +973,10 @@ function DocumentViewer(props) {
|
|
|
1054
973
|
setArmedSignatureUrl(null);
|
|
1055
974
|
if (mode === "create") {
|
|
1056
975
|
const ft = props.fileType ?? "docx";
|
|
1057
|
-
setResolved({
|
|
976
|
+
setResolved({
|
|
977
|
+
fileType: ft,
|
|
978
|
+
fileName: props.fileName ?? `Untitled.${ft}`
|
|
979
|
+
});
|
|
1058
980
|
return;
|
|
1059
981
|
}
|
|
1060
982
|
try {
|
|
@@ -1068,7 +990,12 @@ function DocumentViewer(props) {
|
|
|
1068
990
|
if (cancelled) {
|
|
1069
991
|
return;
|
|
1070
992
|
}
|
|
1071
|
-
setResolved({
|
|
993
|
+
setResolved({
|
|
994
|
+
fileType: res.fileType,
|
|
995
|
+
fileName: res.fileName,
|
|
996
|
+
url: res.url,
|
|
997
|
+
arrayBuffer: res.arrayBuffer
|
|
998
|
+
});
|
|
1072
999
|
} catch (e) {
|
|
1073
1000
|
if (cancelled) {
|
|
1074
1001
|
return;
|
|
@@ -1079,7 +1006,14 @@ function DocumentViewer(props) {
|
|
|
1079
1006
|
return () => {
|
|
1080
1007
|
cancelled = true;
|
|
1081
1008
|
};
|
|
1082
|
-
}, [
|
|
1009
|
+
}, [
|
|
1010
|
+
mode,
|
|
1011
|
+
props.fileUrl,
|
|
1012
|
+
props.base64,
|
|
1013
|
+
props.blob,
|
|
1014
|
+
props.fileName,
|
|
1015
|
+
props.fileType
|
|
1016
|
+
]);
|
|
1083
1017
|
const thumbnails = useMemo6(() => {
|
|
1084
1018
|
const n = Math.max(1, pageCount);
|
|
1085
1019
|
return Array.from({ length: n }, (_, i) => ({
|
|
@@ -1105,7 +1039,10 @@ function DocumentViewer(props) {
|
|
|
1105
1039
|
if (!armedSignatureUrl) {
|
|
1106
1040
|
return;
|
|
1107
1041
|
}
|
|
1108
|
-
setSigPlacements((prev) => [
|
|
1042
|
+
setSigPlacements((prev) => [
|
|
1043
|
+
...prev,
|
|
1044
|
+
{ ...p, signatureImageUrl: armedSignatureUrl }
|
|
1045
|
+
]);
|
|
1109
1046
|
setArmedSignatureUrl(null);
|
|
1110
1047
|
}
|
|
1111
1048
|
async function handleSave(exportPdf) {
|
|
@@ -1117,7 +1054,11 @@ function DocumentViewer(props) {
|
|
|
1117
1054
|
return;
|
|
1118
1055
|
}
|
|
1119
1056
|
const b64 = arrayBufferToBase642(resolved.arrayBuffer);
|
|
1120
|
-
props.onSave?.(b64, {
|
|
1057
|
+
props.onSave?.(b64, {
|
|
1058
|
+
fileName: resolved.fileName,
|
|
1059
|
+
fileType: resolved.fileType,
|
|
1060
|
+
annotations: { sigPlacements }
|
|
1061
|
+
});
|
|
1121
1062
|
}
|
|
1122
1063
|
const canSave = mode === "edit" || mode === "create";
|
|
1123
1064
|
const canExportPdf = (mode === "edit" || mode === "create") && (resolved?.fileType === "docx" || resolved?.fileType === "md" || resolved?.fileType === "txt" || resolved?.fileType === "xlsx");
|
|
@@ -1174,10 +1115,16 @@ function DocumentViewer(props) {
|
|
|
1174
1115
|
onCurrentPageChange: setCurrentPage,
|
|
1175
1116
|
onPageCount: (n) => {
|
|
1176
1117
|
setPageCount(n);
|
|
1177
|
-
setThumbs(
|
|
1118
|
+
setThumbs(
|
|
1119
|
+
(prev) => prev.length === n ? prev : Array.from({ length: n }, (_, i) => prev[i])
|
|
1120
|
+
);
|
|
1178
1121
|
},
|
|
1179
1122
|
onThumbs: (t) => setThumbs(t),
|
|
1180
|
-
signatureStamp: armedSignatureUrl ? {
|
|
1123
|
+
signatureStamp: armedSignatureUrl ? {
|
|
1124
|
+
imageUrl: armedSignatureUrl,
|
|
1125
|
+
armed: true,
|
|
1126
|
+
onPlaced: placeSignature
|
|
1127
|
+
} : void 0
|
|
1181
1128
|
}
|
|
1182
1129
|
) : null,
|
|
1183
1130
|
resolved.fileType === "docx" || resolved.fileType === "md" || resolved.fileType === "txt" ? /* @__PURE__ */ jsx9(
|
|
@@ -1196,7 +1143,9 @@ function DocumentViewer(props) {
|
|
|
1196
1143
|
signaturePlacements: sigPlacements,
|
|
1197
1144
|
onPageCount: (n) => {
|
|
1198
1145
|
setPageCount(n);
|
|
1199
|
-
setThumbs(
|
|
1146
|
+
setThumbs(
|
|
1147
|
+
(prev) => prev.length === n ? prev : Array.from({ length: n }, (_, i) => prev[i])
|
|
1148
|
+
);
|
|
1200
1149
|
},
|
|
1201
1150
|
onSave: (b64, meta) => props.onSave?.(b64, meta),
|
|
1202
1151
|
armedSignatureUrl,
|
|
@@ -1223,12 +1172,21 @@ function DocumentViewer(props) {
|
|
|
1223
1172
|
onCurrentPageChange: setCurrentPage,
|
|
1224
1173
|
onSlideCount: (n) => {
|
|
1225
1174
|
setPageCount(n);
|
|
1226
|
-
setThumbs(
|
|
1175
|
+
setThumbs(
|
|
1176
|
+
(prev) => prev.length === n ? prev : Array.from({ length: n }, (_, i) => prev[i])
|
|
1177
|
+
);
|
|
1227
1178
|
},
|
|
1228
1179
|
onThumbs: (t) => setThumbs(t)
|
|
1229
1180
|
}
|
|
1230
1181
|
) : null,
|
|
1231
|
-
resolved.fileType === "png" || resolved.fileType === "jpg" || resolved.fileType === "svg" ? /* @__PURE__ */ jsx9(
|
|
1182
|
+
resolved.fileType === "png" || resolved.fileType === "jpg" || resolved.fileType === "svg" ? /* @__PURE__ */ jsx9(
|
|
1183
|
+
ImageRenderer,
|
|
1184
|
+
{
|
|
1185
|
+
arrayBuffer: resolved.arrayBuffer,
|
|
1186
|
+
fileType: resolved.fileType,
|
|
1187
|
+
fileName: resolved.fileName
|
|
1188
|
+
}
|
|
1189
|
+
) : null
|
|
1232
1190
|
] }),
|
|
1233
1191
|
mode !== "create" && localSignatures.length ? /* @__PURE__ */ jsx9(
|
|
1234
1192
|
SignaturePanel,
|
package/dist/styles.css
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* Focus ring for accessibility */
|
|
2
2
|
.hv-btn:focus, .hv-thumb:focus, .hv-modal-close:focus {
|
|
3
|
-
outline:
|
|
3
|
+
outline: 1px solid #6366f1;
|
|
4
4
|
outline-offset: 2px;
|
|
5
5
|
z-index: 2;
|
|
6
6
|
}
|
|
@@ -134,7 +134,7 @@
|
|
|
134
134
|
|
|
135
135
|
/* Thumbnails Sidebar Modern Redesign */
|
|
136
136
|
.hv-thumbs {
|
|
137
|
-
width:
|
|
137
|
+
width: 200px;
|
|
138
138
|
flex: 0 0 auto;
|
|
139
139
|
background: var(--hv-panel);
|
|
140
140
|
border-right: 1px solid var(--hv-border);
|
|
@@ -477,3 +477,67 @@
|
|
|
477
477
|
.hv-grid td,.hv-grid th{border:1px solid rgba(0,0,0,.12);padding:6px 8px;min-width:90px}
|
|
478
478
|
.hv-grid th{background:#f1f5f9;font-weight:600;text-align:left}
|
|
479
479
|
.hv-slide{background:#fff;border-radius:14px;box-shadow:0 10px 30px rgba(0,0,0,.25);padding:18px;max-width:480px;min-height:270px}
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
.hv-root {
|
|
483
|
+
height: 100%;
|
|
484
|
+
display: flex;
|
|
485
|
+
flex-direction: column;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
.hv-toolbar {
|
|
489
|
+
position: sticky;
|
|
490
|
+
top: 0;
|
|
491
|
+
z-index: 20;
|
|
492
|
+
display: flex;
|
|
493
|
+
gap: 8px;
|
|
494
|
+
padding: 8px 12px;
|
|
495
|
+
background: #ffffff;
|
|
496
|
+
border-bottom: 1px solid #e5e7eb;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
.hv-toolbar button {
|
|
500
|
+
padding: 6px 10px;
|
|
501
|
+
border-radius: 6px;
|
|
502
|
+
border: 1px solid #d1d5db;
|
|
503
|
+
background: #fff;
|
|
504
|
+
cursor: pointer;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
.hv-toolbar button:disabled {
|
|
508
|
+
opacity: 0.5;
|
|
509
|
+
cursor: not-allowed;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
.hv-hint {
|
|
513
|
+
margin-left: auto;
|
|
514
|
+
font-size: 13px;
|
|
515
|
+
color: #2563eb;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
.hv-scroll {
|
|
519
|
+
flex: 1;
|
|
520
|
+
overflow: auto;
|
|
521
|
+
padding: 32px 0;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
.hv-pageStage {
|
|
525
|
+
width: 794px;
|
|
526
|
+
margin: 0 auto;
|
|
527
|
+
background: white;
|
|
528
|
+
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.08);
|
|
529
|
+
padding: 72px;
|
|
530
|
+
min-height: 1122px;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
.hv-editor {
|
|
534
|
+
outline: none;
|
|
535
|
+
min-height: 900px;
|
|
536
|
+
font-family: 'Inter', system-ui, sans-serif;
|
|
537
|
+
font-size: 15px;
|
|
538
|
+
line-height: 1.7;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
.hv-editor.ro {
|
|
542
|
+
cursor: default;
|
|
543
|
+
}
|