@embedpdf/viewer-chrome 3.0.0-next.13 → 3.0.0-next.15
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/README.md +175 -132
- package/dist/annotation-fonts-BqoLZ4Mp.js +80 -0
- package/dist/annotation-fonts-BqoLZ4Mp.js.map +1 -0
- package/dist/annotation-fonts-Dt0IQXhz.cjs +91 -0
- package/dist/annotation-fonts-Dt0IQXhz.cjs.map +1 -0
- package/dist/annotation-style--rPIZsuP.js +879 -0
- package/dist/annotation-style--rPIZsuP.js.map +1 -0
- package/dist/annotation-style-Bzvvp8-N.cjs +879 -0
- package/dist/annotation-style-Bzvvp8-N.cjs.map +1 -0
- package/dist/ar-D9XKAmDx.js +567 -0
- package/dist/ar-D9XKAmDx.js.map +1 -0
- package/dist/ar-DnEj5vbP.cjs +567 -0
- package/dist/ar-DnEj5vbP.cjs.map +1 -0
- package/dist/chrome.cjs +378 -221
- package/dist/chrome.cjs.map +1 -1
- package/dist/chrome.d.cts +5 -34
- package/dist/chrome.d.ts +5 -34
- package/dist/chrome.js +377 -218
- package/dist/chrome.js.map +1 -1
- package/dist/commands-BmgxhJ8B.js +953 -0
- package/dist/commands-BmgxhJ8B.js.map +1 -0
- package/dist/commands-CYt4SFUX.cjs +1060 -0
- package/dist/commands-CYt4SFUX.cjs.map +1 -0
- package/dist/commands.cjs +12 -595
- package/dist/commands.d.cts +29 -4
- package/dist/commands.d.ts +29 -4
- package/dist/commands.js +2 -593
- package/dist/comments-panel-MUVgLCBI.js +514 -0
- package/dist/comments-panel-MUVgLCBI.js.map +1 -0
- package/dist/comments-panel-pEcQg4Vs.cjs +514 -0
- package/dist/comments-panel-pEcQg4Vs.cjs.map +1 -0
- package/dist/de-Bdr4aWzm.js +566 -0
- package/dist/de-Bdr4aWzm.js.map +1 -0
- package/dist/de-Dv6zvbnd.cjs +566 -0
- package/dist/de-Dv6zvbnd.cjs.map +1 -0
- package/dist/dialog-B5GBisKv.js +55 -0
- package/dist/dialog-B5GBisKv.js.map +1 -0
- package/dist/dialog-t8F13BL_.cjs +60 -0
- package/dist/dialog-t8F13BL_.cjs.map +1 -0
- package/dist/es-BP09DR9w.cjs +566 -0
- package/dist/es-BP09DR9w.cjs.map +1 -0
- package/dist/es-BSXz5tBv.js +566 -0
- package/dist/es-BSXz5tBv.js.map +1 -0
- package/dist/form-design-panel-B5MXTZ96.cjs +418 -0
- package/dist/form-design-panel-B5MXTZ96.cjs.map +1 -0
- package/dist/form-design-panel-vWWU9uB-.js +418 -0
- package/dist/form-design-panel-vWWU9uB-.js.map +1 -0
- package/dist/fr-BBQQn7tJ.cjs +566 -0
- package/dist/fr-BBQQn7tJ.cjs.map +1 -0
- package/dist/fr-Ckn3K9v1.js +566 -0
- package/dist/fr-Ckn3K9v1.js.map +1 -0
- package/dist/he-DRsG7bH7.cjs +567 -0
- package/dist/he-DRsG7bH7.cjs.map +1 -0
- package/dist/he-wdGU5PkU.js +567 -0
- package/dist/he-wdGU5PkU.js.map +1 -0
- package/dist/icons-BrK5iVL8.js +856 -0
- package/dist/icons-BrK5iVL8.js.map +1 -0
- package/dist/icons-x0Tc05bs.cjs +1010 -0
- package/dist/icons-x0Tc05bs.cjs.map +1 -0
- package/dist/index.cjs +3162 -5106
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +636 -143
- package/dist/index.d.ts +636 -143
- package/dist/index.js +3083 -4975
- package/dist/index.js.map +1 -1
- package/dist/it-BuP4o0xQ.cjs +566 -0
- package/dist/it-BuP4o0xQ.cjs.map +1 -0
- package/dist/it-Us-VmvIu.js +566 -0
- package/dist/it-Us-VmvIu.js.map +1 -0
- package/dist/ja-Cizj0-5O.js +566 -0
- package/dist/ja-Cizj0-5O.js.map +1 -0
- package/dist/ja-DovEd6ts.cjs +566 -0
- package/dist/ja-DovEd6ts.cjs.map +1 -0
- package/dist/ko-B-qJEol2.cjs +566 -0
- package/dist/ko-B-qJEol2.cjs.map +1 -0
- package/dist/ko-DlrXliE1.js +566 -0
- package/dist/ko-DlrXliE1.js.map +1 -0
- package/dist/labels-BiM9wp9K.cjs +21 -0
- package/dist/labels-BiM9wp9K.cjs.map +1 -0
- package/dist/labels-C6_i87Oj.js +16 -0
- package/dist/labels-C6_i87Oj.js.map +1 -0
- package/dist/link-editor-CSjqPnj1.cjs +125 -0
- package/dist/link-editor-CSjqPnj1.cjs.map +1 -0
- package/dist/link-editor-SxM1nnJU.js +125 -0
- package/dist/link-editor-SxM1nnJU.js.map +1 -0
- package/dist/measurement-Bs61XWbJ.js +325 -0
- package/dist/measurement-Bs61XWbJ.js.map +1 -0
- package/dist/measurement-CcCsHPOX.cjs +326 -0
- package/dist/measurement-CcCsHPOX.cjs.map +1 -0
- package/dist/nl-0aF7M_0h.js +566 -0
- package/dist/nl-0aF7M_0h.js.map +1 -0
- package/dist/nl-D3KZDDiJ.cjs +566 -0
- package/dist/nl-D3KZDDiJ.cjs.map +1 -0
- package/dist/pl-BW--AaCI.js +566 -0
- package/dist/pl-BW--AaCI.js.map +1 -0
- package/dist/pl-DSFhAUqx.cjs +566 -0
- package/dist/pl-DSFhAUqx.cjs.map +1 -0
- package/dist/pt-C5S-8MC5.cjs +566 -0
- package/dist/pt-C5S-8MC5.cjs.map +1 -0
- package/dist/pt-DIhpU5pT.js +566 -0
- package/dist/pt-DIhpU5pT.js.map +1 -0
- package/dist/redact-confirm-B6j36TPw.js +83 -0
- package/dist/redact-confirm-B6j36TPw.js.map +1 -0
- package/dist/redact-confirm-CX_SY2ZN.cjs +83 -0
- package/dist/redact-confirm-CX_SY2ZN.cjs.map +1 -0
- package/dist/redaction-panel-BmoIl-Ll.cjs +96 -0
- package/dist/redaction-panel-BmoIl-Ll.cjs.map +1 -0
- package/dist/redaction-panel-IJDshSJI.js +96 -0
- package/dist/redaction-panel-IJDshSJI.js.map +1 -0
- package/dist/search-panel-HfIhJm1M.js +249 -0
- package/dist/search-panel-HfIhJm1M.js.map +1 -0
- package/dist/search-panel-s9IoiOtv.cjs +249 -0
- package/dist/search-panel-s9IoiOtv.cjs.map +1 -0
- package/dist/sheet-BDsa_Sup.cjs +442 -0
- package/dist/sheet-BDsa_Sup.cjs.map +1 -0
- package/dist/sheet-R6XDgyEf.js +365 -0
- package/dist/sheet-R6XDgyEf.js.map +1 -0
- package/dist/sign-dialog-CgnR7akj.cjs +155 -0
- package/dist/sign-dialog-CgnR7akj.cjs.map +1 -0
- package/dist/sign-dialog-GDqCoQhA.js +155 -0
- package/dist/sign-dialog-GDqCoQhA.js.map +1 -0
- package/dist/signature-inspector-DyO313Qf.cjs +166 -0
- package/dist/signature-inspector-DyO313Qf.cjs.map +1 -0
- package/dist/signature-inspector-okFz8do6.js +166 -0
- package/dist/signature-inspector-okFz8do6.js.map +1 -0
- package/dist/signature-maker-BhCmYyHD.js +393 -0
- package/dist/signature-maker-BhCmYyHD.js.map +1 -0
- package/dist/signature-maker-BoYa8vgA.cjs +393 -0
- package/dist/signature-maker-BoYa8vgA.cjs.map +1 -0
- package/dist/signatures-panel-B56PC6d9.cjs +300 -0
- package/dist/signatures-panel-B56PC6d9.cjs.map +1 -0
- package/dist/signatures-panel-CgnFfhFm.js +300 -0
- package/dist/signatures-panel-CgnFfhFm.js.map +1 -0
- package/dist/stamp-store-BHvRs-Do.cjs +147 -0
- package/dist/stamp-store-BHvRs-Do.cjs.map +1 -0
- package/dist/stamp-store-WuF-L3Jj.js +112 -0
- package/dist/stamp-store-WuF-L3Jj.js.map +1 -0
- package/dist/stamps-panel-B9NYGayn.cjs +231 -0
- package/dist/stamps-panel-B9NYGayn.cjs.map +1 -0
- package/dist/stamps-panel-CDO0FPyK.js +231 -0
- package/dist/stamps-panel-CDO0FPyK.js.map +1 -0
- package/dist/toolbar-CJu2wKTU.cjs +1659 -0
- package/dist/toolbar-CJu2wKTU.cjs.map +1 -0
- package/dist/toolbar-z9od3tKl.js +1564 -0
- package/dist/toolbar-z9od3tKl.js.map +1 -0
- package/dist/zh-cn-3tmMeHqv.cjs +566 -0
- package/dist/zh-cn-3tmMeHqv.cjs.map +1 -0
- package/dist/zh-cn-CxkUUBct.js +566 -0
- package/dist/zh-cn-CxkUUBct.js.map +1 -0
- package/package.json +6 -4
- package/src/styles.css +153 -89
- package/dist/commands.cjs.map +0 -1
- package/dist/commands.js.map +0 -1
- package/dist/es-DYwzfxyt.cjs +0 -293
- package/dist/es-DYwzfxyt.cjs.map +0 -1
- package/dist/es-sN6uqBgD.js +0 -293
- package/dist/es-sN6uqBgD.js.map +0 -1
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
const require_icons = require("./icons-x0Tc05bs.cjs");
|
|
2
|
+
const require_stamp_store = require("./stamp-store-BHvRs-Do.cjs");
|
|
3
|
+
let react = require("react");
|
|
4
|
+
let _embedpdf_react_runtime = require("@embedpdf/react/runtime");
|
|
5
|
+
let _embedpdf_react_interaction = require("@embedpdf/react/interaction");
|
|
6
|
+
let _embedpdf_react_stamp = require("@embedpdf/react/stamp");
|
|
7
|
+
let _embedpdf_react_signature = require("@embedpdf/react/signature");
|
|
8
|
+
let _embedpdf_react_i18n = require("@embedpdf/react/i18n");
|
|
9
|
+
let _embedpdf_react_shell = require("@embedpdf/react/shell");
|
|
10
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
11
|
+
//#region src/ui/signatures-panel.tsx
|
|
12
|
+
/**
|
|
13
|
+
* The signatures sidebar (right panel), a signature sheet like Preview's.
|
|
14
|
+
* One row per person: a stamp library of kind `signatures` holding a
|
|
15
|
+
* full signature and, optionally, initials. Nothing is stored beyond the
|
|
16
|
+
* file; rename/export/delete are the library verbs.
|
|
17
|
+
*
|
|
18
|
+
* pick a mark → with a target field ("sign here" was clicked, or a
|
|
19
|
+
* signature widget selected) the mark goes into that field by the
|
|
20
|
+
* plugin's mode; otherwise it arms — hover a page for the ghost, click a
|
|
21
|
+
* signature field to sign it, click anywhere else to drop it as a stamp.
|
|
22
|
+
*
|
|
23
|
+
* Below the people: the signature fields of this document, signed or not,
|
|
24
|
+
* with their verdicts — click an unsigned one to target it, a signed one to
|
|
25
|
+
* inspect it.
|
|
26
|
+
*/
|
|
27
|
+
const fieldLabel = (field) => field.kind === "fqn" ? field.name : `#${field.objectNumber}`;
|
|
28
|
+
function SignaturesPanel() {
|
|
29
|
+
const t = (0, _embedpdf_react_i18n.useT)();
|
|
30
|
+
const stamp = (0, _embedpdf_react_stamp.useStamp)();
|
|
31
|
+
const signature = (0, _embedpdf_react_signature.useSignature)();
|
|
32
|
+
const config = require_icons.useSignatureSettings();
|
|
33
|
+
const rows = (0, _embedpdf_react_signature.useSignerRows)();
|
|
34
|
+
const target = (0, _embedpdf_react_signature.useSignatureState)((state) => state.target);
|
|
35
|
+
const busy = (0, _embedpdf_react_signature.useSignatureState)((state) => state.busy);
|
|
36
|
+
const { activeToolId } = (0, _embedpdf_react_interaction.useInteractionState)();
|
|
37
|
+
const maker = (0, _embedpdf_react_shell.useSurface)("signature-maker");
|
|
38
|
+
const [armedId, setArmedId] = (0, react.useState)(null);
|
|
39
|
+
const [error, setError] = (0, react.useState)(null);
|
|
40
|
+
const [invalidating, setInvalidating] = (0, react.useState)(null);
|
|
41
|
+
(0, _embedpdf_react_signature.useSignatureEvent)((signature) => signature.onInvalidationPredicted, (event) => setInvalidating(signature.getSignature(event.field)?.fieldName ?? fieldLabel(event.field)));
|
|
42
|
+
(0, _embedpdf_react_signature.useSignatureEvent)((signature) => signature.onValidated, (event) => {
|
|
43
|
+
if (!event.verdicts.some((verdict) => verdict.summary === "invalid" && verdict.modifications.basis === "working-copy")) setInvalidating(null);
|
|
44
|
+
});
|
|
45
|
+
(0, react.useEffect)(() => {
|
|
46
|
+
require_stamp_store.restoreStampLibrariesOnce(stamp);
|
|
47
|
+
}, [stamp]);
|
|
48
|
+
(0, react.useEffect)(() => {
|
|
49
|
+
if (activeToolId !== "stamp") setArmedId(null);
|
|
50
|
+
}, [activeToolId]);
|
|
51
|
+
const pick = (asset) => {
|
|
52
|
+
setError(null);
|
|
53
|
+
if (target) {
|
|
54
|
+
signature.placeMark({ assetId: asset.id }, { field: target }).catch((error) => {
|
|
55
|
+
console.error("[embedpdf] placing the mark failed:", error);
|
|
56
|
+
setError(t("demo.signError"));
|
|
57
|
+
});
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
setArmedId(asset.id);
|
|
61
|
+
stamp.armAsset(asset.id).catch((error) => {
|
|
62
|
+
console.error("[embedpdf] arm mark failed:", error);
|
|
63
|
+
setArmedId(null);
|
|
64
|
+
setError(t("demo.stampsArmError"));
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
const canAddPerson = (config.libraries ?? "many") === "many" || rows.length === 0;
|
|
68
|
+
const wantsInitials = (config.kinds ?? ["signature", "initials"]).includes("initials");
|
|
69
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
70
|
+
className: "flex min-h-0 flex-1 flex-col",
|
|
71
|
+
children: [
|
|
72
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
73
|
+
className: "min-h-0 flex-1 overflow-y-auto p-3",
|
|
74
|
+
children: [rows.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
75
|
+
className: "flex flex-col items-center gap-3 px-4 py-8 text-center",
|
|
76
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_icons.Icon, {
|
|
77
|
+
name: "signature",
|
|
78
|
+
size: 32,
|
|
79
|
+
className: "text-fg-muted"
|
|
80
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
81
|
+
className: "text-fg-muted text-sm",
|
|
82
|
+
children: t("demo.signaturesEmpty")
|
|
83
|
+
})]
|
|
84
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
85
|
+
className: "flex flex-col gap-3",
|
|
86
|
+
children: rows.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SignerRowView, {
|
|
87
|
+
row,
|
|
88
|
+
armedId,
|
|
89
|
+
wantsInitials,
|
|
90
|
+
onPick: pick,
|
|
91
|
+
onAddInitials: () => maker.open({
|
|
92
|
+
exclusive: "modal",
|
|
93
|
+
props: { libraryId: row.libraryId }
|
|
94
|
+
})
|
|
95
|
+
}, row.libraryId))
|
|
96
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DocumentSignatures, {})]
|
|
97
|
+
}),
|
|
98
|
+
invalidating && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
99
|
+
className: "border-border-subtle shrink-0 border-t px-3 py-2 text-xs text-amber-700",
|
|
100
|
+
children: t("demo.signaturesInvalidating", { params: { field: invalidating } })
|
|
101
|
+
}),
|
|
102
|
+
(error || target || armedId) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
103
|
+
className: `border-border-subtle shrink-0 border-t px-3 py-2 text-xs ${error ? "text-red-600" : "text-fg-muted"}`,
|
|
104
|
+
children: error ?? (target ? busy ? t("demo.signSigning") : t("demo.signaturesTargetHint") : t("demo.signaturesArmedHint"))
|
|
105
|
+
}),
|
|
106
|
+
canAddPerson ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
107
|
+
className: "border-border-subtle shrink-0 border-t p-3",
|
|
108
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
109
|
+
type: "button",
|
|
110
|
+
onClick: () => maker.open({ exclusive: "modal" }),
|
|
111
|
+
className: "border-border text-fg hover:bg-hover flex w-full items-center justify-center gap-2 rounded-md border px-3 py-1.5 text-sm",
|
|
112
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_icons.Icon, {
|
|
113
|
+
name: "plus",
|
|
114
|
+
size: 16
|
|
115
|
+
}), t("demo.signaturesNew")]
|
|
116
|
+
})
|
|
117
|
+
}) : null
|
|
118
|
+
]
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
function SignerRowView({ row, armedId, wantsInitials, onPick, onAddInitials }) {
|
|
122
|
+
const t = (0, _embedpdf_react_i18n.useT)();
|
|
123
|
+
const stamp = (0, _embedpdf_react_stamp.useStamp)();
|
|
124
|
+
const [renaming, setRenaming] = (0, react.useState)(null);
|
|
125
|
+
const exportPdf = async () => {
|
|
126
|
+
const bytes = await stamp.exportLibrary(row.libraryId).catch(() => null);
|
|
127
|
+
if (!bytes) return;
|
|
128
|
+
const blob = new Blob([bytes], { type: "application/pdf" });
|
|
129
|
+
const url = URL.createObjectURL(blob);
|
|
130
|
+
const anchor = document.createElement("a");
|
|
131
|
+
anchor.href = url;
|
|
132
|
+
anchor.download = `${row.name || "signature"}.pdf`;
|
|
133
|
+
anchor.click();
|
|
134
|
+
URL.revokeObjectURL(url);
|
|
135
|
+
};
|
|
136
|
+
const commitRename = () => {
|
|
137
|
+
const name = renaming?.trim();
|
|
138
|
+
setRenaming(null);
|
|
139
|
+
if (name && name !== row.name) stamp.updateLibrary(row.libraryId, { name });
|
|
140
|
+
};
|
|
141
|
+
const iconButton = "text-fg-muted hover:text-fg grid h-6 w-6 shrink-0 place-items-center rounded";
|
|
142
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
143
|
+
className: "border-border-subtle rounded-md border p-2",
|
|
144
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
145
|
+
className: "flex items-center gap-1",
|
|
146
|
+
children: [
|
|
147
|
+
renaming !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
148
|
+
autoFocus: true,
|
|
149
|
+
value: renaming,
|
|
150
|
+
onChange: (event) => setRenaming(event.target.value),
|
|
151
|
+
onBlur: commitRename,
|
|
152
|
+
onKeyDown: (event) => {
|
|
153
|
+
if (event.key === "Enter") commitRename();
|
|
154
|
+
if (event.key === "Escape") setRenaming(null);
|
|
155
|
+
},
|
|
156
|
+
className: "border-border bg-surface text-fg min-w-0 flex-1 rounded border px-1.5 py-0.5 text-sm"
|
|
157
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
158
|
+
className: "text-fg min-w-0 flex-1 truncate text-sm font-medium",
|
|
159
|
+
children: row.name
|
|
160
|
+
}),
|
|
161
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
162
|
+
type: "button",
|
|
163
|
+
onClick: () => setRenaming(row.name),
|
|
164
|
+
title: t("demo.signaturesRename"),
|
|
165
|
+
className: iconButton,
|
|
166
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_icons.Icon, {
|
|
167
|
+
name: "pencilMarker",
|
|
168
|
+
size: 13
|
|
169
|
+
})
|
|
170
|
+
}),
|
|
171
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
172
|
+
type: "button",
|
|
173
|
+
onClick: exportPdf,
|
|
174
|
+
title: t("demo.signaturesExport"),
|
|
175
|
+
className: iconButton,
|
|
176
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_icons.Icon, {
|
|
177
|
+
name: "download",
|
|
178
|
+
size: 13
|
|
179
|
+
})
|
|
180
|
+
}),
|
|
181
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
182
|
+
type: "button",
|
|
183
|
+
onClick: () => void stamp.deleteLibrary(row.libraryId),
|
|
184
|
+
title: t("demo.signaturesRemove"),
|
|
185
|
+
className: iconButton,
|
|
186
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_icons.Icon, {
|
|
187
|
+
name: "trash",
|
|
188
|
+
size: 13
|
|
189
|
+
})
|
|
190
|
+
})
|
|
191
|
+
]
|
|
192
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
193
|
+
className: "mt-2 grid grid-cols-[2fr_1fr] gap-2",
|
|
194
|
+
children: [row.signatures.map((asset) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MarkThumb, {
|
|
195
|
+
asset,
|
|
196
|
+
label: t("demo.signatureLabel"),
|
|
197
|
+
armed: armedId === asset.id,
|
|
198
|
+
onPick: () => onPick(asset)
|
|
199
|
+
}, asset.id)), wantsInitials ? row.initials ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MarkThumb, {
|
|
200
|
+
asset: row.initials,
|
|
201
|
+
label: t("demo.initialsLabel"),
|
|
202
|
+
armed: armedId === row.initials.id,
|
|
203
|
+
onPick: () => onPick(row.initials)
|
|
204
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
205
|
+
type: "button",
|
|
206
|
+
onClick: onAddInitials,
|
|
207
|
+
title: t("demo.signaturesAddInitials"),
|
|
208
|
+
className: "border-border-subtle text-fg-muted hover:bg-hover flex h-16 items-center justify-center rounded-md border border-dashed text-xs",
|
|
209
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_icons.Icon, {
|
|
210
|
+
name: "plus",
|
|
211
|
+
size: 14
|
|
212
|
+
})
|
|
213
|
+
}) : null]
|
|
214
|
+
})]
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
function MarkThumb({ asset, label, armed, onPick }) {
|
|
218
|
+
const url = (0, _embedpdf_react_stamp.useStampAssetPreviewUrl)(asset.id);
|
|
219
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
220
|
+
type: "button",
|
|
221
|
+
onClick: onPick,
|
|
222
|
+
title: label,
|
|
223
|
+
"aria-pressed": armed,
|
|
224
|
+
className: `flex h-16 items-center justify-center rounded-md border p-1 ${armed ? "border-accent bg-accent-light ring-accent ring-2" : "border-border-subtle hover:border-border hover:bg-hover"}`,
|
|
225
|
+
children: url ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
226
|
+
src: url,
|
|
227
|
+
alt: label,
|
|
228
|
+
className: "max-h-full max-w-full object-contain"
|
|
229
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
230
|
+
className: "text-fg-muted text-xs",
|
|
231
|
+
children: label
|
|
232
|
+
})
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
/** The signature fields of this document, with their state. */
|
|
236
|
+
function DocumentSignatures() {
|
|
237
|
+
const t = (0, _embedpdf_react_i18n.useT)();
|
|
238
|
+
const signature = (0, _embedpdf_react_signature.useSignature)();
|
|
239
|
+
const snapshot = (0, _embedpdf_react_runtime.useSelector)(_embedpdf_react_signature.SignatureToken, (capability) => capability.getSnapshot());
|
|
240
|
+
const signed = (0, _embedpdf_react_signature.useSignatureState)((state) => state.signatures);
|
|
241
|
+
const target = (0, _embedpdf_react_signature.useSignatureState)((state) => state.target);
|
|
242
|
+
const [validating, setValidating] = (0, react.useState)(false);
|
|
243
|
+
const fields = snapshot?.signatures ?? [];
|
|
244
|
+
const verdictOf = (dto) => signed.find((entry) => entry.index === dto.index)?.verdict ?? null;
|
|
245
|
+
const verdictLabel = (verdict) => !verdict ? t("demo.signaturesSigned") : verdict.summary === "valid" ? t("demo.verdictValid") : verdict.summary === "valid-untrusted" ? t("demo.verdictValidUntrusted") : verdict.summary === "indeterminate" ? t("demo.verdictIndeterminate") : verdict.modifications.basis === "working-copy" ? t("demo.verdictWillInvalidate") : t("demo.verdictInvalid");
|
|
246
|
+
const verdictTone = (verdict) => !verdict || verdict.summary === "indeterminate" ? "text-fg-muted" : verdict.summary === "invalid" ? verdict.modifications.basis === "working-copy" ? "text-amber-600" : "text-red-600" : verdict.summary === "valid-untrusted" ? "text-amber-600" : "text-green-600";
|
|
247
|
+
const isTarget = (dto) => !!target && (target.kind === "fqn" ? target.name === dto.fieldName : target.objectNumber === (dto.field.kind === "objectNumber" ? dto.field.objectNumber : -1));
|
|
248
|
+
const validate = () => {
|
|
249
|
+
setValidating(true);
|
|
250
|
+
signature.validate().finally(() => setValidating(false));
|
|
251
|
+
};
|
|
252
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
253
|
+
className: "mt-4",
|
|
254
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
255
|
+
className: "flex items-center justify-between",
|
|
256
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
257
|
+
className: "text-fg-muted text-xs font-semibold uppercase tracking-wide",
|
|
258
|
+
children: t("demo.signaturesInDocument")
|
|
259
|
+
}), fields.some((signature) => signature.signed) ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
260
|
+
type: "button",
|
|
261
|
+
disabled: validating,
|
|
262
|
+
onClick: validate,
|
|
263
|
+
className: "text-accent text-xs hover:underline disabled:opacity-60",
|
|
264
|
+
children: validating ? t("demo.signaturesValidating") : t("demo.signaturesValidate")
|
|
265
|
+
}) : null]
|
|
266
|
+
}), fields.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
267
|
+
className: "text-fg-muted mt-1 text-xs",
|
|
268
|
+
children: t("demo.signaturesNone")
|
|
269
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
270
|
+
className: "mt-1 flex flex-col gap-1",
|
|
271
|
+
children: fields.map((dto) => {
|
|
272
|
+
const verdict = verdictOf(dto);
|
|
273
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
274
|
+
type: "button",
|
|
275
|
+
onClick: () => dto.signed ? signature.requestInspection(dto.field) : signature.setTarget(dto.field),
|
|
276
|
+
className: `hover:bg-hover flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-start text-sm ${isTarget(dto) ? "bg-accent-light" : ""}`,
|
|
277
|
+
children: [
|
|
278
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_icons.Icon, {
|
|
279
|
+
name: dto.signed ? "check" : "signature",
|
|
280
|
+
size: 14,
|
|
281
|
+
className: dto.signed ? "text-green-600" : "text-fg-muted"
|
|
282
|
+
}),
|
|
283
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
284
|
+
className: "text-fg min-w-0 flex-1 truncate",
|
|
285
|
+
children: dto.fieldName
|
|
286
|
+
}),
|
|
287
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
288
|
+
className: `shrink-0 text-end text-xs ${dto.signed ? verdictTone(verdict) : "text-fg-muted"}`,
|
|
289
|
+
children: dto.signed ? verdictLabel(verdict) : t("demo.signaturesUnsigned")
|
|
290
|
+
})
|
|
291
|
+
]
|
|
292
|
+
}) }, dto.index);
|
|
293
|
+
})
|
|
294
|
+
})]
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
//#endregion
|
|
298
|
+
exports.SignaturesPanel = SignaturesPanel;
|
|
299
|
+
|
|
300
|
+
//# sourceMappingURL=signatures-panel-B56PC6d9.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signatures-panel-B56PC6d9.cjs","names":["useSignatureSettings","restoreStampLibrariesOnce","Icon","SignatureToken"],"sources":["../src/ui/signatures-panel.tsx"],"sourcesContent":["/**\n * The signatures sidebar (right panel), a signature sheet like Preview's.\n * One row per person: a stamp library of kind `signatures` holding a\n * full signature and, optionally, initials. Nothing is stored beyond the\n * file; rename/export/delete are the library verbs.\n *\n * pick a mark → with a target field (\"sign here\" was clicked, or a\n * signature widget selected) the mark goes into that field by the\n * plugin's mode; otherwise it arms — hover a page for the ghost, click a\n * signature field to sign it, click anywhere else to drop it as a stamp.\n *\n * Below the people: the signature fields of this document, signed or not,\n * with their verdicts — click an unsigned one to target it, a signed one to\n * inspect it.\n */\nimport { useEffect, useState } from 'react';\nimport { useInteractionState } from '@embedpdf/react/interaction';\nimport { useSelector } from '@embedpdf/react/runtime';\nimport { useT } from '@embedpdf/react/i18n';\nimport { useSurface } from '@embedpdf/react/shell';\nimport { useStamp, useStampAssetPreviewUrl, type StampAsset } from '@embedpdf/react/stamp';\nimport {\n SignatureToken,\n useSignature,\n useSignatureState,\n useSignatureEvent,\n useSignerRows,\n type SignatureDTO,\n type SignatureVerdict,\n type SignerRow,\n} from '@embedpdf/react/signature';\nimport { useSignatureSettings } from '../config-context';\nimport { Icon } from './icons';\nimport { restoreStampLibrariesOnce } from './stamp-store';\n\nconst fieldLabel = (\n field: { kind: 'fqn'; name: string } | { kind: 'objectNumber'; objectNumber: number },\n): string => (field.kind === 'fqn' ? field.name : `#${field.objectNumber}`);\n\nexport function SignaturesPanel() {\n const t = useT();\n const stamp = useStamp();\n const signature = useSignature();\n const config = useSignatureSettings();\n const rows = useSignerRows();\n const target = useSignatureState((state) => state.target);\n const busy = useSignatureState((state) => state.busy);\n const { activeToolId } = useInteractionState();\n const maker = useSurface('signature-maker');\n const [armedId, setArmedId] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n // Acrobat's warning: an unsaved edit just turned a signature that held into\n // one a save would invalidate. Shown until the next verdicts land.\n const [invalidating, setInvalidating] = useState<string | null>(null);\n useSignatureEvent(\n (signature) => signature.onInvalidationPredicted,\n (event) =>\n setInvalidating(signature.getSignature(event.field)?.fieldName ?? fieldLabel(event.field)),\n );\n useSignatureEvent(\n (signature) => signature.onValidated,\n (event) => {\n if (\n !event.verdicts.some(\n (verdict) =>\n verdict.summary === 'invalid' && verdict.modifications.basis === 'working-copy',\n )\n )\n setInvalidating(null);\n },\n );\n\n // The people live in the same store as the stamps: bring them back on\n // first open (a no-op when the store component already did).\n useEffect(() => {\n void restoreStampLibrariesOnce(stamp);\n }, [stamp]);\n useEffect(() => {\n if (activeToolId !== 'stamp') setArmedId(null);\n }, [activeToolId]);\n\n const pick = (asset: StampAsset) => {\n setError(null);\n if (target) {\n void signature.placeMark({ assetId: asset.id }, { field: target }).catch((error) => {\n console.error('[embedpdf] placing the mark failed:', error);\n setError(t('demo.signError'));\n });\n return;\n }\n setArmedId(asset.id);\n void stamp.armAsset(asset.id).catch((error) => {\n console.error('[embedpdf] arm mark failed:', error);\n setArmedId(null);\n setError(t('demo.stampsArmError'));\n });\n };\n\n const canAddPerson = (config.libraries ?? 'many') === 'many' || rows.length === 0;\n const wantsInitials = (config.kinds ?? ['signature', 'initials']).includes('initials');\n\n return (\n <div className=\"flex min-h-0 flex-1 flex-col\">\n <div className=\"min-h-0 flex-1 overflow-y-auto p-3\">\n {rows.length === 0 ? (\n <div className=\"flex flex-col items-center gap-3 px-4 py-8 text-center\">\n <Icon name=\"signature\" size={32} className=\"text-fg-muted\" />\n <p className=\"text-fg-muted text-sm\">{t('demo.signaturesEmpty')}</p>\n </div>\n ) : (\n <ul className=\"flex flex-col gap-3\">\n {rows.map((row) => (\n <SignerRowView\n key={row.libraryId}\n row={row}\n armedId={armedId}\n wantsInitials={wantsInitials}\n onPick={pick}\n onAddInitials={() =>\n maker.open({ exclusive: 'modal', props: { libraryId: row.libraryId } })\n }\n />\n ))}\n </ul>\n )}\n <DocumentSignatures />\n </div>\n\n {invalidating && (\n <div className=\"border-border-subtle shrink-0 border-t px-3 py-2 text-xs text-amber-700\">\n {t('demo.signaturesInvalidating', { params: { field: invalidating } })}\n </div>\n )}\n {(error || target || armedId) && (\n <div\n className={`border-border-subtle shrink-0 border-t px-3 py-2 text-xs ${\n error ? 'text-red-600' : 'text-fg-muted'\n }`}\n >\n {error ??\n (target\n ? busy\n ? t('demo.signSigning')\n : t('demo.signaturesTargetHint')\n : t('demo.signaturesArmedHint'))}\n </div>\n )}\n\n {canAddPerson ? (\n <div className=\"border-border-subtle shrink-0 border-t p-3\">\n <button\n type=\"button\"\n onClick={() => maker.open({ exclusive: 'modal' })}\n className=\"border-border text-fg hover:bg-hover flex w-full items-center justify-center gap-2 rounded-md border px-3 py-1.5 text-sm\"\n >\n <Icon name=\"plus\" size={16} />\n {t('demo.signaturesNew')}\n </button>\n </div>\n ) : null}\n </div>\n );\n}\n\nfunction SignerRowView({\n row,\n armedId,\n wantsInitials,\n onPick,\n onAddInitials,\n}: {\n row: SignerRow;\n armedId: string | null;\n wantsInitials: boolean;\n onPick: (asset: StampAsset) => void;\n onAddInitials: () => void;\n}) {\n const t = useT();\n const stamp = useStamp();\n const [renaming, setRenaming] = useState<string | null>(null);\n\n const exportPdf = async () => {\n const bytes = await stamp.exportLibrary(row.libraryId).catch(() => null);\n if (!bytes) return;\n const blob = new Blob([bytes as BlobPart], { type: 'application/pdf' });\n const url = URL.createObjectURL(blob);\n const anchor = document.createElement('a');\n anchor.href = url;\n anchor.download = `${row.name || 'signature'}.pdf`;\n anchor.click();\n URL.revokeObjectURL(url);\n };\n const commitRename = () => {\n const name = renaming?.trim();\n setRenaming(null);\n if (name && name !== row.name) void stamp.updateLibrary(row.libraryId, { name });\n };\n const iconButton = 'text-fg-muted hover:text-fg grid h-6 w-6 shrink-0 place-items-center rounded';\n\n return (\n <li className=\"border-border-subtle rounded-md border p-2\">\n <div className=\"flex items-center gap-1\">\n {renaming !== null ? (\n <input\n autoFocus\n value={renaming}\n onChange={(event) => setRenaming(event.target.value)}\n onBlur={commitRename}\n onKeyDown={(event) => {\n if (event.key === 'Enter') commitRename();\n if (event.key === 'Escape') setRenaming(null);\n }}\n className=\"border-border bg-surface text-fg min-w-0 flex-1 rounded border px-1.5 py-0.5 text-sm\"\n />\n ) : (\n <span className=\"text-fg min-w-0 flex-1 truncate text-sm font-medium\">{row.name}</span>\n )}\n <button\n type=\"button\"\n onClick={() => setRenaming(row.name)}\n title={t('demo.signaturesRename')}\n className={iconButton}\n >\n <Icon name=\"pencilMarker\" size={13} />\n </button>\n <button\n type=\"button\"\n onClick={exportPdf}\n title={t('demo.signaturesExport')}\n className={iconButton}\n >\n <Icon name=\"download\" size={13} />\n </button>\n <button\n type=\"button\"\n onClick={() => void stamp.deleteLibrary(row.libraryId)}\n title={t('demo.signaturesRemove')}\n className={iconButton}\n >\n <Icon name=\"trash\" size={13} />\n </button>\n </div>\n <div className=\"mt-2 grid grid-cols-[2fr_1fr] gap-2\">\n {row.signatures.map((asset) => (\n <MarkThumb\n key={asset.id}\n asset={asset}\n label={t('demo.signatureLabel')}\n armed={armedId === asset.id}\n onPick={() => onPick(asset)}\n />\n ))}\n {wantsInitials ? (\n row.initials ? (\n <MarkThumb\n asset={row.initials}\n label={t('demo.initialsLabel')}\n armed={armedId === row.initials.id}\n onPick={() => onPick(row.initials!)}\n />\n ) : (\n <button\n type=\"button\"\n onClick={onAddInitials}\n title={t('demo.signaturesAddInitials')}\n className=\"border-border-subtle text-fg-muted hover:bg-hover flex h-16 items-center justify-center rounded-md border border-dashed text-xs\"\n >\n <Icon name=\"plus\" size={14} />\n </button>\n )\n ) : null}\n </div>\n </li>\n );\n}\n\nfunction MarkThumb({\n asset,\n label,\n armed,\n onPick,\n}: {\n asset: StampAsset;\n label: string;\n armed: boolean;\n onPick: () => void;\n}) {\n const url = useStampAssetPreviewUrl(asset.id);\n return (\n <button\n type=\"button\"\n onClick={onPick}\n title={label}\n aria-pressed={armed}\n className={`flex h-16 items-center justify-center rounded-md border p-1 ${\n armed\n ? 'border-accent bg-accent-light ring-accent ring-2'\n : 'border-border-subtle hover:border-border hover:bg-hover'\n }`}\n >\n {url ? (\n <img src={url} alt={label} className=\"max-h-full max-w-full object-contain\" />\n ) : (\n <span className=\"text-fg-muted text-xs\">{label}</span>\n )}\n </button>\n );\n}\n\n/** The signature fields of this document, with their state. */\nfunction DocumentSignatures() {\n const t = useT();\n const signature = useSignature();\n // Every signature field, signed or not; the signed ones carry their verdict in the state.\n const snapshot = useSelector(SignatureToken, (capability) => capability.getSnapshot());\n const signed = useSignatureState((state) => state.signatures);\n const target = useSignatureState((state) => state.target);\n const [validating, setValidating] = useState(false);\n const fields = snapshot?.signatures ?? [];\n const verdictOf = (dto: SignatureDTO) =>\n signed.find((entry) => entry.index === dto.index)?.verdict ?? null;\n // Three honest states: valid; valid on disk but unsaved edits would\n // invalidate it (the verdict judged the working copy); invalid on disk.\n const verdictLabel = (verdict: SignatureVerdict | null): string =>\n !verdict\n ? t('demo.signaturesSigned')\n : verdict.summary === 'valid'\n ? t('demo.verdictValid')\n : verdict.summary === 'valid-untrusted'\n ? t('demo.verdictValidUntrusted')\n : verdict.summary === 'indeterminate'\n ? t('demo.verdictIndeterminate')\n : verdict.modifications.basis === 'working-copy'\n ? t('demo.verdictWillInvalidate')\n : t('demo.verdictInvalid');\n const verdictTone = (verdict: SignatureVerdict | null): string =>\n !verdict || verdict.summary === 'indeterminate'\n ? 'text-fg-muted'\n : verdict.summary === 'invalid'\n ? verdict.modifications.basis === 'working-copy'\n ? 'text-amber-600'\n : 'text-red-600'\n : verdict.summary === 'valid-untrusted'\n ? 'text-amber-600'\n : 'text-green-600';\n const isTarget = (dto: SignatureDTO) =>\n !!target &&\n (target.kind === 'fqn'\n ? target.name === dto.fieldName\n : target.objectNumber === (dto.field.kind === 'objectNumber' ? dto.field.objectNumber : -1));\n const validate = () => {\n setValidating(true);\n void signature.validate().finally(() => setValidating(false));\n };\n\n return (\n <div className=\"mt-4\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-fg-muted text-xs font-semibold uppercase tracking-wide\">\n {t('demo.signaturesInDocument')}\n </span>\n {fields.some((signature) => signature.signed) ? (\n <button\n type=\"button\"\n disabled={validating}\n onClick={validate}\n className=\"text-accent text-xs hover:underline disabled:opacity-60\"\n >\n {validating ? t('demo.signaturesValidating') : t('demo.signaturesValidate')}\n </button>\n ) : null}\n </div>\n {fields.length === 0 ? (\n <p className=\"text-fg-muted mt-1 text-xs\">{t('demo.signaturesNone')}</p>\n ) : (\n <ul className=\"mt-1 flex flex-col gap-1\">\n {fields.map((dto) => {\n const verdict = verdictOf(dto);\n return (\n <li key={dto.index}>\n <button\n type=\"button\"\n onClick={() =>\n dto.signed\n ? signature.requestInspection(dto.field)\n : signature.setTarget(dto.field)\n }\n className={`hover:bg-hover flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-start text-sm ${\n isTarget(dto) ? 'bg-accent-light' : ''\n }`}\n >\n <Icon\n name={dto.signed ? 'check' : 'signature'}\n size={14}\n className={dto.signed ? 'text-green-600' : 'text-fg-muted'}\n />\n <span className=\"text-fg min-w-0 flex-1 truncate\">{dto.fieldName}</span>\n <span\n className={`shrink-0 text-end text-xs ${dto.signed ? verdictTone(verdict) : 'text-fg-muted'}`}\n >\n {dto.signed ? verdictLabel(verdict) : t('demo.signaturesUnsigned')}\n </span>\n </button>\n </li>\n );\n })}\n </ul>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { g as useSignatureSettings, n as Icon } from "./icons-BrK5iVL8.js";
|
|
2
|
+
import { n as restoreStampLibrariesOnce } from "./stamp-store-WuF-L3Jj.js";
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
import { useSelector } from "@embedpdf/react/runtime";
|
|
5
|
+
import { useInteractionState } from "@embedpdf/react/interaction";
|
|
6
|
+
import { useStamp, useStampAssetPreviewUrl } from "@embedpdf/react/stamp";
|
|
7
|
+
import { SignatureToken, useSignature, useSignatureEvent, useSignatureState, useSignerRows } from "@embedpdf/react/signature";
|
|
8
|
+
import { useT } from "@embedpdf/react/i18n";
|
|
9
|
+
import { useSurface } from "@embedpdf/react/shell";
|
|
10
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
11
|
+
//#region src/ui/signatures-panel.tsx
|
|
12
|
+
/**
|
|
13
|
+
* The signatures sidebar (right panel), a signature sheet like Preview's.
|
|
14
|
+
* One row per person: a stamp library of kind `signatures` holding a
|
|
15
|
+
* full signature and, optionally, initials. Nothing is stored beyond the
|
|
16
|
+
* file; rename/export/delete are the library verbs.
|
|
17
|
+
*
|
|
18
|
+
* pick a mark → with a target field ("sign here" was clicked, or a
|
|
19
|
+
* signature widget selected) the mark goes into that field by the
|
|
20
|
+
* plugin's mode; otherwise it arms — hover a page for the ghost, click a
|
|
21
|
+
* signature field to sign it, click anywhere else to drop it as a stamp.
|
|
22
|
+
*
|
|
23
|
+
* Below the people: the signature fields of this document, signed or not,
|
|
24
|
+
* with their verdicts — click an unsigned one to target it, a signed one to
|
|
25
|
+
* inspect it.
|
|
26
|
+
*/
|
|
27
|
+
const fieldLabel = (field) => field.kind === "fqn" ? field.name : `#${field.objectNumber}`;
|
|
28
|
+
function SignaturesPanel() {
|
|
29
|
+
const t = useT();
|
|
30
|
+
const stamp = useStamp();
|
|
31
|
+
const signature = useSignature();
|
|
32
|
+
const config = useSignatureSettings();
|
|
33
|
+
const rows = useSignerRows();
|
|
34
|
+
const target = useSignatureState((state) => state.target);
|
|
35
|
+
const busy = useSignatureState((state) => state.busy);
|
|
36
|
+
const { activeToolId } = useInteractionState();
|
|
37
|
+
const maker = useSurface("signature-maker");
|
|
38
|
+
const [armedId, setArmedId] = useState(null);
|
|
39
|
+
const [error, setError] = useState(null);
|
|
40
|
+
const [invalidating, setInvalidating] = useState(null);
|
|
41
|
+
useSignatureEvent((signature) => signature.onInvalidationPredicted, (event) => setInvalidating(signature.getSignature(event.field)?.fieldName ?? fieldLabel(event.field)));
|
|
42
|
+
useSignatureEvent((signature) => signature.onValidated, (event) => {
|
|
43
|
+
if (!event.verdicts.some((verdict) => verdict.summary === "invalid" && verdict.modifications.basis === "working-copy")) setInvalidating(null);
|
|
44
|
+
});
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
restoreStampLibrariesOnce(stamp);
|
|
47
|
+
}, [stamp]);
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
if (activeToolId !== "stamp") setArmedId(null);
|
|
50
|
+
}, [activeToolId]);
|
|
51
|
+
const pick = (asset) => {
|
|
52
|
+
setError(null);
|
|
53
|
+
if (target) {
|
|
54
|
+
signature.placeMark({ assetId: asset.id }, { field: target }).catch((error) => {
|
|
55
|
+
console.error("[embedpdf] placing the mark failed:", error);
|
|
56
|
+
setError(t("demo.signError"));
|
|
57
|
+
});
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
setArmedId(asset.id);
|
|
61
|
+
stamp.armAsset(asset.id).catch((error) => {
|
|
62
|
+
console.error("[embedpdf] arm mark failed:", error);
|
|
63
|
+
setArmedId(null);
|
|
64
|
+
setError(t("demo.stampsArmError"));
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
const canAddPerson = (config.libraries ?? "many") === "many" || rows.length === 0;
|
|
68
|
+
const wantsInitials = (config.kinds ?? ["signature", "initials"]).includes("initials");
|
|
69
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
70
|
+
className: "flex min-h-0 flex-1 flex-col",
|
|
71
|
+
children: [
|
|
72
|
+
/* @__PURE__ */ jsxs("div", {
|
|
73
|
+
className: "min-h-0 flex-1 overflow-y-auto p-3",
|
|
74
|
+
children: [rows.length === 0 ? /* @__PURE__ */ jsxs("div", {
|
|
75
|
+
className: "flex flex-col items-center gap-3 px-4 py-8 text-center",
|
|
76
|
+
children: [/* @__PURE__ */ jsx(Icon, {
|
|
77
|
+
name: "signature",
|
|
78
|
+
size: 32,
|
|
79
|
+
className: "text-fg-muted"
|
|
80
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
81
|
+
className: "text-fg-muted text-sm",
|
|
82
|
+
children: t("demo.signaturesEmpty")
|
|
83
|
+
})]
|
|
84
|
+
}) : /* @__PURE__ */ jsx("ul", {
|
|
85
|
+
className: "flex flex-col gap-3",
|
|
86
|
+
children: rows.map((row) => /* @__PURE__ */ jsx(SignerRowView, {
|
|
87
|
+
row,
|
|
88
|
+
armedId,
|
|
89
|
+
wantsInitials,
|
|
90
|
+
onPick: pick,
|
|
91
|
+
onAddInitials: () => maker.open({
|
|
92
|
+
exclusive: "modal",
|
|
93
|
+
props: { libraryId: row.libraryId }
|
|
94
|
+
})
|
|
95
|
+
}, row.libraryId))
|
|
96
|
+
}), /* @__PURE__ */ jsx(DocumentSignatures, {})]
|
|
97
|
+
}),
|
|
98
|
+
invalidating && /* @__PURE__ */ jsx("div", {
|
|
99
|
+
className: "border-border-subtle shrink-0 border-t px-3 py-2 text-xs text-amber-700",
|
|
100
|
+
children: t("demo.signaturesInvalidating", { params: { field: invalidating } })
|
|
101
|
+
}),
|
|
102
|
+
(error || target || armedId) && /* @__PURE__ */ jsx("div", {
|
|
103
|
+
className: `border-border-subtle shrink-0 border-t px-3 py-2 text-xs ${error ? "text-red-600" : "text-fg-muted"}`,
|
|
104
|
+
children: error ?? (target ? busy ? t("demo.signSigning") : t("demo.signaturesTargetHint") : t("demo.signaturesArmedHint"))
|
|
105
|
+
}),
|
|
106
|
+
canAddPerson ? /* @__PURE__ */ jsx("div", {
|
|
107
|
+
className: "border-border-subtle shrink-0 border-t p-3",
|
|
108
|
+
children: /* @__PURE__ */ jsxs("button", {
|
|
109
|
+
type: "button",
|
|
110
|
+
onClick: () => maker.open({ exclusive: "modal" }),
|
|
111
|
+
className: "border-border text-fg hover:bg-hover flex w-full items-center justify-center gap-2 rounded-md border px-3 py-1.5 text-sm",
|
|
112
|
+
children: [/* @__PURE__ */ jsx(Icon, {
|
|
113
|
+
name: "plus",
|
|
114
|
+
size: 16
|
|
115
|
+
}), t("demo.signaturesNew")]
|
|
116
|
+
})
|
|
117
|
+
}) : null
|
|
118
|
+
]
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
function SignerRowView({ row, armedId, wantsInitials, onPick, onAddInitials }) {
|
|
122
|
+
const t = useT();
|
|
123
|
+
const stamp = useStamp();
|
|
124
|
+
const [renaming, setRenaming] = useState(null);
|
|
125
|
+
const exportPdf = async () => {
|
|
126
|
+
const bytes = await stamp.exportLibrary(row.libraryId).catch(() => null);
|
|
127
|
+
if (!bytes) return;
|
|
128
|
+
const blob = new Blob([bytes], { type: "application/pdf" });
|
|
129
|
+
const url = URL.createObjectURL(blob);
|
|
130
|
+
const anchor = document.createElement("a");
|
|
131
|
+
anchor.href = url;
|
|
132
|
+
anchor.download = `${row.name || "signature"}.pdf`;
|
|
133
|
+
anchor.click();
|
|
134
|
+
URL.revokeObjectURL(url);
|
|
135
|
+
};
|
|
136
|
+
const commitRename = () => {
|
|
137
|
+
const name = renaming?.trim();
|
|
138
|
+
setRenaming(null);
|
|
139
|
+
if (name && name !== row.name) stamp.updateLibrary(row.libraryId, { name });
|
|
140
|
+
};
|
|
141
|
+
const iconButton = "text-fg-muted hover:text-fg grid h-6 w-6 shrink-0 place-items-center rounded";
|
|
142
|
+
return /* @__PURE__ */ jsxs("li", {
|
|
143
|
+
className: "border-border-subtle rounded-md border p-2",
|
|
144
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
145
|
+
className: "flex items-center gap-1",
|
|
146
|
+
children: [
|
|
147
|
+
renaming !== null ? /* @__PURE__ */ jsx("input", {
|
|
148
|
+
autoFocus: true,
|
|
149
|
+
value: renaming,
|
|
150
|
+
onChange: (event) => setRenaming(event.target.value),
|
|
151
|
+
onBlur: commitRename,
|
|
152
|
+
onKeyDown: (event) => {
|
|
153
|
+
if (event.key === "Enter") commitRename();
|
|
154
|
+
if (event.key === "Escape") setRenaming(null);
|
|
155
|
+
},
|
|
156
|
+
className: "border-border bg-surface text-fg min-w-0 flex-1 rounded border px-1.5 py-0.5 text-sm"
|
|
157
|
+
}) : /* @__PURE__ */ jsx("span", {
|
|
158
|
+
className: "text-fg min-w-0 flex-1 truncate text-sm font-medium",
|
|
159
|
+
children: row.name
|
|
160
|
+
}),
|
|
161
|
+
/* @__PURE__ */ jsx("button", {
|
|
162
|
+
type: "button",
|
|
163
|
+
onClick: () => setRenaming(row.name),
|
|
164
|
+
title: t("demo.signaturesRename"),
|
|
165
|
+
className: iconButton,
|
|
166
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
167
|
+
name: "pencilMarker",
|
|
168
|
+
size: 13
|
|
169
|
+
})
|
|
170
|
+
}),
|
|
171
|
+
/* @__PURE__ */ jsx("button", {
|
|
172
|
+
type: "button",
|
|
173
|
+
onClick: exportPdf,
|
|
174
|
+
title: t("demo.signaturesExport"),
|
|
175
|
+
className: iconButton,
|
|
176
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
177
|
+
name: "download",
|
|
178
|
+
size: 13
|
|
179
|
+
})
|
|
180
|
+
}),
|
|
181
|
+
/* @__PURE__ */ jsx("button", {
|
|
182
|
+
type: "button",
|
|
183
|
+
onClick: () => void stamp.deleteLibrary(row.libraryId),
|
|
184
|
+
title: t("demo.signaturesRemove"),
|
|
185
|
+
className: iconButton,
|
|
186
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
187
|
+
name: "trash",
|
|
188
|
+
size: 13
|
|
189
|
+
})
|
|
190
|
+
})
|
|
191
|
+
]
|
|
192
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
193
|
+
className: "mt-2 grid grid-cols-[2fr_1fr] gap-2",
|
|
194
|
+
children: [row.signatures.map((asset) => /* @__PURE__ */ jsx(MarkThumb, {
|
|
195
|
+
asset,
|
|
196
|
+
label: t("demo.signatureLabel"),
|
|
197
|
+
armed: armedId === asset.id,
|
|
198
|
+
onPick: () => onPick(asset)
|
|
199
|
+
}, asset.id)), wantsInitials ? row.initials ? /* @__PURE__ */ jsx(MarkThumb, {
|
|
200
|
+
asset: row.initials,
|
|
201
|
+
label: t("demo.initialsLabel"),
|
|
202
|
+
armed: armedId === row.initials.id,
|
|
203
|
+
onPick: () => onPick(row.initials)
|
|
204
|
+
}) : /* @__PURE__ */ jsx("button", {
|
|
205
|
+
type: "button",
|
|
206
|
+
onClick: onAddInitials,
|
|
207
|
+
title: t("demo.signaturesAddInitials"),
|
|
208
|
+
className: "border-border-subtle text-fg-muted hover:bg-hover flex h-16 items-center justify-center rounded-md border border-dashed text-xs",
|
|
209
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
210
|
+
name: "plus",
|
|
211
|
+
size: 14
|
|
212
|
+
})
|
|
213
|
+
}) : null]
|
|
214
|
+
})]
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
function MarkThumb({ asset, label, armed, onPick }) {
|
|
218
|
+
const url = useStampAssetPreviewUrl(asset.id);
|
|
219
|
+
return /* @__PURE__ */ jsx("button", {
|
|
220
|
+
type: "button",
|
|
221
|
+
onClick: onPick,
|
|
222
|
+
title: label,
|
|
223
|
+
"aria-pressed": armed,
|
|
224
|
+
className: `flex h-16 items-center justify-center rounded-md border p-1 ${armed ? "border-accent bg-accent-light ring-accent ring-2" : "border-border-subtle hover:border-border hover:bg-hover"}`,
|
|
225
|
+
children: url ? /* @__PURE__ */ jsx("img", {
|
|
226
|
+
src: url,
|
|
227
|
+
alt: label,
|
|
228
|
+
className: "max-h-full max-w-full object-contain"
|
|
229
|
+
}) : /* @__PURE__ */ jsx("span", {
|
|
230
|
+
className: "text-fg-muted text-xs",
|
|
231
|
+
children: label
|
|
232
|
+
})
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
/** The signature fields of this document, with their state. */
|
|
236
|
+
function DocumentSignatures() {
|
|
237
|
+
const t = useT();
|
|
238
|
+
const signature = useSignature();
|
|
239
|
+
const snapshot = useSelector(SignatureToken, (capability) => capability.getSnapshot());
|
|
240
|
+
const signed = useSignatureState((state) => state.signatures);
|
|
241
|
+
const target = useSignatureState((state) => state.target);
|
|
242
|
+
const [validating, setValidating] = useState(false);
|
|
243
|
+
const fields = snapshot?.signatures ?? [];
|
|
244
|
+
const verdictOf = (dto) => signed.find((entry) => entry.index === dto.index)?.verdict ?? null;
|
|
245
|
+
const verdictLabel = (verdict) => !verdict ? t("demo.signaturesSigned") : verdict.summary === "valid" ? t("demo.verdictValid") : verdict.summary === "valid-untrusted" ? t("demo.verdictValidUntrusted") : verdict.summary === "indeterminate" ? t("demo.verdictIndeterminate") : verdict.modifications.basis === "working-copy" ? t("demo.verdictWillInvalidate") : t("demo.verdictInvalid");
|
|
246
|
+
const verdictTone = (verdict) => !verdict || verdict.summary === "indeterminate" ? "text-fg-muted" : verdict.summary === "invalid" ? verdict.modifications.basis === "working-copy" ? "text-amber-600" : "text-red-600" : verdict.summary === "valid-untrusted" ? "text-amber-600" : "text-green-600";
|
|
247
|
+
const isTarget = (dto) => !!target && (target.kind === "fqn" ? target.name === dto.fieldName : target.objectNumber === (dto.field.kind === "objectNumber" ? dto.field.objectNumber : -1));
|
|
248
|
+
const validate = () => {
|
|
249
|
+
setValidating(true);
|
|
250
|
+
signature.validate().finally(() => setValidating(false));
|
|
251
|
+
};
|
|
252
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
253
|
+
className: "mt-4",
|
|
254
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
255
|
+
className: "flex items-center justify-between",
|
|
256
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
257
|
+
className: "text-fg-muted text-xs font-semibold uppercase tracking-wide",
|
|
258
|
+
children: t("demo.signaturesInDocument")
|
|
259
|
+
}), fields.some((signature) => signature.signed) ? /* @__PURE__ */ jsx("button", {
|
|
260
|
+
type: "button",
|
|
261
|
+
disabled: validating,
|
|
262
|
+
onClick: validate,
|
|
263
|
+
className: "text-accent text-xs hover:underline disabled:opacity-60",
|
|
264
|
+
children: validating ? t("demo.signaturesValidating") : t("demo.signaturesValidate")
|
|
265
|
+
}) : null]
|
|
266
|
+
}), fields.length === 0 ? /* @__PURE__ */ jsx("p", {
|
|
267
|
+
className: "text-fg-muted mt-1 text-xs",
|
|
268
|
+
children: t("demo.signaturesNone")
|
|
269
|
+
}) : /* @__PURE__ */ jsx("ul", {
|
|
270
|
+
className: "mt-1 flex flex-col gap-1",
|
|
271
|
+
children: fields.map((dto) => {
|
|
272
|
+
const verdict = verdictOf(dto);
|
|
273
|
+
return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("button", {
|
|
274
|
+
type: "button",
|
|
275
|
+
onClick: () => dto.signed ? signature.requestInspection(dto.field) : signature.setTarget(dto.field),
|
|
276
|
+
className: `hover:bg-hover flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-start text-sm ${isTarget(dto) ? "bg-accent-light" : ""}`,
|
|
277
|
+
children: [
|
|
278
|
+
/* @__PURE__ */ jsx(Icon, {
|
|
279
|
+
name: dto.signed ? "check" : "signature",
|
|
280
|
+
size: 14,
|
|
281
|
+
className: dto.signed ? "text-green-600" : "text-fg-muted"
|
|
282
|
+
}),
|
|
283
|
+
/* @__PURE__ */ jsx("span", {
|
|
284
|
+
className: "text-fg min-w-0 flex-1 truncate",
|
|
285
|
+
children: dto.fieldName
|
|
286
|
+
}),
|
|
287
|
+
/* @__PURE__ */ jsx("span", {
|
|
288
|
+
className: `shrink-0 text-end text-xs ${dto.signed ? verdictTone(verdict) : "text-fg-muted"}`,
|
|
289
|
+
children: dto.signed ? verdictLabel(verdict) : t("demo.signaturesUnsigned")
|
|
290
|
+
})
|
|
291
|
+
]
|
|
292
|
+
}) }, dto.index);
|
|
293
|
+
})
|
|
294
|
+
})]
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
//#endregion
|
|
298
|
+
export { SignaturesPanel };
|
|
299
|
+
|
|
300
|
+
//# sourceMappingURL=signatures-panel-CgnFfhFm.js.map
|