@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,231 @@
|
|
|
1
|
+
import { _ as useStampSettings, n as Icon } from "./icons-BrK5iVL8.js";
|
|
2
|
+
import { i as ensureDefaultLibrary, n as restoreStampLibrariesOnce, o as resolveStampsLocale, r as DEFAULT_LIBRARY_ID } from "./stamp-store-WuF-L3Jj.js";
|
|
3
|
+
import { useEffect, useRef, useState } from "react";
|
|
4
|
+
import { useInteractionState } from "@embedpdf/react/interaction";
|
|
5
|
+
import { useStamp, useStampAssetPreviewUrl, useStampAssets, useStampLibraries } from "@embedpdf/react/stamp";
|
|
6
|
+
import { useI18nState, useT } from "@embedpdf/react/i18n";
|
|
7
|
+
import { useSurface } from "@embedpdf/react/shell";
|
|
8
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
//#region src/ui/stamps-panel.tsx
|
|
10
|
+
/**
|
|
11
|
+
* The stamps sidebar (right panel). The stamp plugin owns the libraries, the
|
|
12
|
+
* asset bytes and the cached previews; this panel is the picker over them:
|
|
13
|
+
*
|
|
14
|
+
* a library <select> ("All stamps" + one per library, shown once there
|
|
15
|
+
* are two) → a two-column grid of thumbnails → click a stamp → `armAsset`
|
|
16
|
+
* arms the annotation plugin's stamp tool → hovering a page ghosts the
|
|
17
|
+
* exact placement → each click on a page places one.
|
|
18
|
+
*
|
|
19
|
+
* Deliberately not a file dialog: picking arbitrary image bytes is the Image
|
|
20
|
+
* tool's job (a click-then-pick tool, `tool:image`). The stamp button
|
|
21
|
+
* opens a library; a library is a set of reusable, named assets — the two
|
|
22
|
+
* gestures are different, so they are different controls.
|
|
23
|
+
*
|
|
24
|
+
* The panel stays open while you place, so a stamp can be dropped on several
|
|
25
|
+
* pages and swapped without a round trip through the toolbar. A command can
|
|
26
|
+
* open it on a library (`annotation:stamp-from-selection` → "My stamps")
|
|
27
|
+
* through the surface's open props.
|
|
28
|
+
*/
|
|
29
|
+
const ALL = "all";
|
|
30
|
+
function StampsPanel() {
|
|
31
|
+
const t = useT();
|
|
32
|
+
const stamp = useStamp();
|
|
33
|
+
const { standard, sidebar } = useStampSettings();
|
|
34
|
+
const libraries = useStampLibraries({ kind: sidebar ?? ["stamps"] });
|
|
35
|
+
const assets = useStampAssets();
|
|
36
|
+
const { activeToolId } = useInteractionState();
|
|
37
|
+
const { locale } = useI18nState();
|
|
38
|
+
const surface = useSurface("stamps");
|
|
39
|
+
const fileRef = useRef(null);
|
|
40
|
+
const [armedId, setArmedId] = useState(null);
|
|
41
|
+
const [busy, setBusy] = useState(null);
|
|
42
|
+
const [error, setError] = useState(null);
|
|
43
|
+
const [picked, setPicked] = useState(ALL);
|
|
44
|
+
const openedOn = surface.props?.libraryId;
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
if (typeof openedOn === "string") setPicked(openedOn);
|
|
47
|
+
}, [openedOn]);
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
let live = true;
|
|
50
|
+
setBusy("loading");
|
|
51
|
+
restoreStampLibrariesOnce(stamp).then(() => ensureDefaultLibrary(stamp, resolveStampsLocale(locale), standard)).catch((error) => {
|
|
52
|
+
console.error("[embedpdf] default stamps failed:", error);
|
|
53
|
+
if (live) setError(t("demo.stampsError"));
|
|
54
|
+
}).finally(() => live && setBusy(null));
|
|
55
|
+
return () => {
|
|
56
|
+
live = false;
|
|
57
|
+
};
|
|
58
|
+
}, [stamp, locale]);
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
if (activeToolId !== "stamp") setArmedId(null);
|
|
61
|
+
}, [activeToolId]);
|
|
62
|
+
const arm = (asset) => {
|
|
63
|
+
setError(null);
|
|
64
|
+
setArmedId(asset.id);
|
|
65
|
+
stamp.armAsset(asset.id).catch((error) => {
|
|
66
|
+
console.error("[embedpdf] arm stamp failed:", error);
|
|
67
|
+
setArmedId(null);
|
|
68
|
+
setError(t("demo.stampsArmError"));
|
|
69
|
+
});
|
|
70
|
+
};
|
|
71
|
+
const importPdf = (file) => {
|
|
72
|
+
setError(null);
|
|
73
|
+
setBusy("importing");
|
|
74
|
+
stamp.importLibrary(file, { name: file.name.replace(/\.pdf$/i, "") }).then(({ library }) => setPicked(library.id)).catch((error) => {
|
|
75
|
+
console.error("[embedpdf] stamp library import failed:", error);
|
|
76
|
+
setError(t("demo.stampsImportError"));
|
|
77
|
+
}).finally(() => setBusy(null));
|
|
78
|
+
};
|
|
79
|
+
/** The library as the PDF it is — title, registry, artwork — for Acrobat
|
|
80
|
+
* or another viewer. */
|
|
81
|
+
const exportPdf = async (libraryId, name) => {
|
|
82
|
+
const bytes = await stamp.exportLibrary(libraryId).catch(() => null);
|
|
83
|
+
if (!bytes) return;
|
|
84
|
+
const blob = new Blob([bytes], { type: "application/pdf" });
|
|
85
|
+
const url = URL.createObjectURL(blob);
|
|
86
|
+
const anchor = document.createElement("a");
|
|
87
|
+
anchor.href = url;
|
|
88
|
+
anchor.download = `${name || "stamps"}.pdf`;
|
|
89
|
+
anchor.click();
|
|
90
|
+
URL.revokeObjectURL(url);
|
|
91
|
+
};
|
|
92
|
+
const selectedId = libraries.length === 1 ? libraries[0].id : libraries.some((library) => library.id === picked) ? picked : ALL;
|
|
93
|
+
const selected = selectedId === ALL ? null : libraries.find((library) => library.id === selectedId) ?? null;
|
|
94
|
+
const shown = selectedId === ALL ? assets : assets.filter((asset) => asset.libraryId === selectedId);
|
|
95
|
+
const isDefault = (libraryId) => libraryId === DEFAULT_LIBRARY_ID;
|
|
96
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
97
|
+
className: "flex min-h-0 flex-1 flex-col",
|
|
98
|
+
children: [
|
|
99
|
+
libraries.length > 0 && /* @__PURE__ */ jsxs("div", {
|
|
100
|
+
className: "border-border-subtle flex items-center gap-1 border-b p-2",
|
|
101
|
+
children: [libraries.length > 1 ? /* @__PURE__ */ jsxs("select", {
|
|
102
|
+
value: selectedId,
|
|
103
|
+
onChange: (event) => setPicked(event.target.value),
|
|
104
|
+
"aria-label": t("panels.stamps"),
|
|
105
|
+
className: "border-border bg-surface text-fg min-w-0 flex-1 rounded border px-2 py-1.5 text-sm",
|
|
106
|
+
children: [/* @__PURE__ */ jsx("option", {
|
|
107
|
+
value: ALL,
|
|
108
|
+
children: t("demo.stampsAll")
|
|
109
|
+
}), libraries.map((library) => /* @__PURE__ */ jsx("option", {
|
|
110
|
+
value: library.id,
|
|
111
|
+
children: library.name
|
|
112
|
+
}, library.id))]
|
|
113
|
+
}) : /* @__PURE__ */ jsx("span", {
|
|
114
|
+
className: "text-fg-muted min-w-0 flex-1 truncate px-1 text-xs font-semibold uppercase tracking-wide",
|
|
115
|
+
children: libraries[0].name
|
|
116
|
+
}), selected && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("button", {
|
|
117
|
+
type: "button",
|
|
118
|
+
onClick: () => exportPdf(selected.id, selected.name),
|
|
119
|
+
title: t("demo.stampsExportLibrary"),
|
|
120
|
+
className: "text-fg-muted hover:text-fg grid h-7 w-7 shrink-0 place-items-center rounded",
|
|
121
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
122
|
+
name: "download",
|
|
123
|
+
size: 14
|
|
124
|
+
})
|
|
125
|
+
}), !isDefault(selected.id) && /* @__PURE__ */ jsx("button", {
|
|
126
|
+
type: "button",
|
|
127
|
+
onClick: () => void stamp.deleteLibrary(selected.id),
|
|
128
|
+
title: t("demo.stampsRemoveLibrary"),
|
|
129
|
+
className: "text-fg-muted hover:text-fg grid h-7 w-7 shrink-0 place-items-center rounded",
|
|
130
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
131
|
+
name: "trash",
|
|
132
|
+
size: 14
|
|
133
|
+
})
|
|
134
|
+
})] })]
|
|
135
|
+
}),
|
|
136
|
+
/* @__PURE__ */ jsx("div", {
|
|
137
|
+
className: "min-h-0 flex-1 overflow-y-auto p-3",
|
|
138
|
+
children: busy === "loading" && shown.length === 0 ? /* @__PURE__ */ jsxs("div", {
|
|
139
|
+
className: "flex flex-col items-center gap-3 px-4 py-10 text-center",
|
|
140
|
+
children: [/* @__PURE__ */ jsx("div", { className: "border-border-subtle border-t-accent h-6 w-6 animate-spin rounded-full border-2" }), /* @__PURE__ */ jsx("p", {
|
|
141
|
+
className: "text-fg-muted text-sm",
|
|
142
|
+
children: t("demo.stampsLoading")
|
|
143
|
+
})]
|
|
144
|
+
}) : shown.length === 0 ? /* @__PURE__ */ jsxs("div", {
|
|
145
|
+
className: "flex flex-col items-center gap-3 px-4 py-10 text-center",
|
|
146
|
+
children: [/* @__PURE__ */ jsx(Icon, {
|
|
147
|
+
name: "rubberStamp",
|
|
148
|
+
size: 32,
|
|
149
|
+
className: "text-fg-muted"
|
|
150
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
151
|
+
className: "text-fg-muted text-sm",
|
|
152
|
+
children: t("demo.stampsEmpty")
|
|
153
|
+
})]
|
|
154
|
+
}) : /* @__PURE__ */ jsx("ul", {
|
|
155
|
+
className: "grid grid-cols-2 gap-3",
|
|
156
|
+
children: shown.map((asset) => /* @__PURE__ */ jsxs("li", {
|
|
157
|
+
className: "group relative",
|
|
158
|
+
children: [/* @__PURE__ */ jsx(StampAssetCell, {
|
|
159
|
+
asset,
|
|
160
|
+
armed: armedId === asset.id,
|
|
161
|
+
onArm: () => arm(asset)
|
|
162
|
+
}), !isDefault(asset.libraryId) && /* @__PURE__ */ jsx("button", {
|
|
163
|
+
type: "button",
|
|
164
|
+
onClick: () => void stamp.deleteAsset(asset.id),
|
|
165
|
+
title: t("demo.stampsRemoveStamp"),
|
|
166
|
+
className: "bg-surface border-border text-fg-muted hover:text-fg absolute end-1 top-1 grid h-5 w-5 place-items-center rounded-full border opacity-0 shadow-sm focus:opacity-100 group-hover:opacity-100",
|
|
167
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
168
|
+
name: "x",
|
|
169
|
+
size: 12
|
|
170
|
+
})
|
|
171
|
+
})]
|
|
172
|
+
}, asset.id))
|
|
173
|
+
})
|
|
174
|
+
}),
|
|
175
|
+
(error || armedId) && /* @__PURE__ */ jsx("div", {
|
|
176
|
+
className: `border-border-subtle shrink-0 border-t px-3 py-2 text-xs ${error ? "text-red-600" : "text-fg-muted"}`,
|
|
177
|
+
children: error ?? t("demo.stampsArmedHint")
|
|
178
|
+
}),
|
|
179
|
+
/* @__PURE__ */ jsxs("div", {
|
|
180
|
+
className: "border-border-subtle shrink-0 border-t p-3",
|
|
181
|
+
children: [/* @__PURE__ */ jsxs("button", {
|
|
182
|
+
type: "button",
|
|
183
|
+
disabled: busy != null,
|
|
184
|
+
onClick: () => fileRef.current?.click(),
|
|
185
|
+
title: t("demo.stampsImportHint"),
|
|
186
|
+
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 disabled:opacity-40",
|
|
187
|
+
children: [/* @__PURE__ */ jsx(Icon, {
|
|
188
|
+
name: "rubberStampPlus",
|
|
189
|
+
size: 16
|
|
190
|
+
}), busy === "importing" ? t("demo.stampsImporting") : t("demo.stampsImport")]
|
|
191
|
+
}), /* @__PURE__ */ jsx("input", {
|
|
192
|
+
ref: fileRef,
|
|
193
|
+
type: "file",
|
|
194
|
+
accept: "application/pdf",
|
|
195
|
+
className: "hidden",
|
|
196
|
+
onChange: (event) => {
|
|
197
|
+
const file = event.currentTarget.files?.[0];
|
|
198
|
+
event.currentTarget.value = "";
|
|
199
|
+
if (file) importPdf(file);
|
|
200
|
+
}
|
|
201
|
+
})]
|
|
202
|
+
})
|
|
203
|
+
]
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
/** One grid cell: the plugin's cached preview through the object-URL hook
|
|
207
|
+
* (the DOM resource's lifetime belongs to the framework layer, not the
|
|
208
|
+
* store). Image only — the artwork is the label; the label is the
|
|
209
|
+
* tooltip for the curious. */
|
|
210
|
+
function StampAssetCell({ asset, armed, onArm }) {
|
|
211
|
+
const url = useStampAssetPreviewUrl(asset.id);
|
|
212
|
+
return /* @__PURE__ */ jsx("button", {
|
|
213
|
+
type: "button",
|
|
214
|
+
onClick: onArm,
|
|
215
|
+
title: asset.label,
|
|
216
|
+
"aria-pressed": armed,
|
|
217
|
+
className: `flex aspect-square w-full items-center justify-center rounded-md border p-2 ${armed ? "border-accent bg-accent-light ring-accent ring-2" : "border-border-subtle hover:border-border hover:bg-hover"}`,
|
|
218
|
+
children: url ? /* @__PURE__ */ jsx("img", {
|
|
219
|
+
src: url,
|
|
220
|
+
alt: asset.label,
|
|
221
|
+
className: "max-h-full max-w-full object-contain"
|
|
222
|
+
}) : /* @__PURE__ */ jsx("span", {
|
|
223
|
+
className: "text-fg truncate text-sm",
|
|
224
|
+
children: asset.label
|
|
225
|
+
})
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
//#endregion
|
|
229
|
+
export { StampsPanel };
|
|
230
|
+
|
|
231
|
+
//# sourceMappingURL=stamps-panel-CDO0FPyK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stamps-panel-CDO0FPyK.js","names":[],"sources":["../src/ui/stamps-panel.tsx"],"sourcesContent":["/**\n * The stamps sidebar (right panel). The stamp plugin owns the libraries, the\n * asset bytes and the cached previews; this panel is the picker over them:\n *\n * a library <select> (\"All stamps\" + one per library, shown once there\n * are two) → a two-column grid of thumbnails → click a stamp → `armAsset`\n * arms the annotation plugin's stamp tool → hovering a page ghosts the\n * exact placement → each click on a page places one.\n *\n * Deliberately not a file dialog: picking arbitrary image bytes is the Image\n * tool's job (a click-then-pick tool, `tool:image`). The stamp button\n * opens a library; a library is a set of reusable, named assets — the two\n * gestures are different, so they are different controls.\n *\n * The panel stays open while you place, so a stamp can be dropped on several\n * pages and swapped without a round trip through the toolbar. A command can\n * open it on a library (`annotation:stamp-from-selection` → \"My stamps\")\n * through the surface's open props.\n */\nimport { useEffect, useRef, useState } from 'react';\nimport { useInteractionState } from '@embedpdf/react/interaction';\nimport {\n useStamp,\n useStampAssetPreviewUrl,\n useStampAssets,\n useStampLibraries,\n type StampAsset,\n} from '@embedpdf/react/stamp';\nimport { useSurface } from '@embedpdf/react/shell';\nimport { useI18nState, useT } from '@embedpdf/react/i18n';\nimport { useStampSettings } from '../config-context';\nimport {\n DEFAULT_LIBRARY_ID,\n ensureDefaultLibrary,\n resolveStampsLocale,\n} from '../config/default-stamps';\nimport { Icon } from './icons';\nimport { restoreStampLibrariesOnce } from './stamp-store';\n\nconst ALL = 'all';\n\nexport function StampsPanel() {\n const t = useT();\n const stamp = useStamp();\n const { standard, sidebar } = useStampSettings();\n // Which catalogs this sidebar shows (`plugins.stamp.sidebar`): the plain stamps by\n // default — never the people's marks, which have their own panel.\n const libraries = useStampLibraries({ kind: sidebar ?? ['stamps'] });\n const assets = useStampAssets();\n const { activeToolId } = useInteractionState();\n const { locale } = useI18nState();\n const surface = useSurface('stamps');\n const fileRef = useRef<HTMLInputElement>(null);\n const [armedId, setArmedId] = useState<string | null>(null);\n const [busy, setBusy] = useState<'loading' | 'importing' | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [picked, setPicked] = useState<string>(ALL);\n\n // A command opened the panel on a library (the surface's `libraryId` prop).\n const openedOn = surface.props?.libraryId;\n useEffect(() => {\n if (typeof openedOn === 'string') setPicked(openedOn);\n }, [openedOn]);\n\n // First open: the user's stored libraries (the workspace store restores\n // them; this only awaits it), then the built-in library for this locale —\n // never at viewer boot, so a viewer whose user never opens this panel pays\n // nothing for it. A locale change while open swaps the built-in library;\n // the user's own are untouched.\n useEffect(() => {\n let live = true;\n setBusy('loading');\n restoreStampLibrariesOnce(stamp)\n .then(() => ensureDefaultLibrary(stamp, resolveStampsLocale(locale), standard))\n .catch((error: unknown) => {\n console.error('[embedpdf] default stamps failed:', error);\n if (live) setError(t('demo.stampsError'));\n })\n .finally(() => live && setBusy(null));\n return () => {\n live = false;\n };\n // `t` and `standard` are init-stable; only the locale re-runs this.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [stamp, locale]);\n\n // The interaction hub owns tool state: leaving the stamp tool (picking\n // another tool, pressing Escape) un-highlights the gallery.\n useEffect(() => {\n if (activeToolId !== 'stamp') setArmedId(null);\n }, [activeToolId]);\n\n const arm = (asset: StampAsset) => {\n setError(null);\n setArmedId(asset.id);\n void stamp.armAsset(asset.id).catch((error) => {\n console.error('[embedpdf] arm stamp failed:', error);\n setArmedId(null);\n setError(t('demo.stampsArmError'));\n });\n };\n\n const importPdf = (file: File) => {\n setError(null);\n setBusy('importing');\n // The file name is only a fallback: an Acrobat-authored or previously\n // exported library names itself through its /Title.\n stamp\n .importLibrary(file, { name: file.name.replace(/\\.pdf$/i, '') })\n .then(({ library }) => setPicked(library.id))\n .catch((error) => {\n console.error('[embedpdf] stamp library import failed:', error);\n setError(t('demo.stampsImportError'));\n })\n .finally(() => setBusy(null));\n };\n\n /** The library as the PDF it is — title, registry, artwork — for Acrobat\n * or another viewer. */\n const exportPdf = async (libraryId: string, name: string) => {\n const bytes = await stamp.exportLibrary(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 = `${name || 'stamps'}.pdf`;\n anchor.click();\n URL.revokeObjectURL(url);\n };\n\n // A removed library falls back to \"All\"; the picker never names a ghost.\n // One library needs no picker: it is simply the selection (so its export\n // button is reachable).\n const selectedId =\n libraries.length === 1\n ? libraries[0].id\n : libraries.some((library) => library.id === picked)\n ? picked\n : ALL;\n const selected =\n selectedId === ALL ? null : (libraries.find((library) => library.id === selectedId) ?? null);\n const shown =\n selectedId === ALL ? assets : assets.filter((asset) => asset.libraryId === selectedId);\n const isDefault = (libraryId: string) => libraryId === DEFAULT_LIBRARY_ID;\n\n return (\n <div className=\"flex min-h-0 flex-1 flex-col\">\n {libraries.length > 0 && (\n <div className=\"border-border-subtle flex items-center gap-1 border-b p-2\">\n {libraries.length > 1 ? (\n <select\n value={selectedId}\n onChange={(event) => setPicked(event.target.value)}\n aria-label={t('panels.stamps')}\n className=\"border-border bg-surface text-fg min-w-0 flex-1 rounded border px-2 py-1.5 text-sm\"\n >\n <option value={ALL}>{t('demo.stampsAll')}</option>\n {libraries.map((library) => (\n <option key={library.id} value={library.id}>\n {library.name}\n </option>\n ))}\n </select>\n ) : (\n <span className=\"text-fg-muted min-w-0 flex-1 truncate px-1 text-xs font-semibold uppercase tracking-wide\">\n {libraries[0].name}\n </span>\n )}\n {selected && (\n <>\n <button\n type=\"button\"\n onClick={() => exportPdf(selected.id, selected.name)}\n title={t('demo.stampsExportLibrary')}\n className=\"text-fg-muted hover:text-fg grid h-7 w-7 shrink-0 place-items-center rounded\"\n >\n <Icon name=\"download\" size={14} />\n </button>\n {!isDefault(selected.id) && (\n <button\n type=\"button\"\n onClick={() => void stamp.deleteLibrary(selected.id)}\n title={t('demo.stampsRemoveLibrary')}\n className=\"text-fg-muted hover:text-fg grid h-7 w-7 shrink-0 place-items-center rounded\"\n >\n <Icon name=\"trash\" size={14} />\n </button>\n )}\n </>\n )}\n </div>\n )}\n\n <div className=\"min-h-0 flex-1 overflow-y-auto p-3\">\n {busy === 'loading' && shown.length === 0 ? (\n <div className=\"flex flex-col items-center gap-3 px-4 py-10 text-center\">\n <div className=\"border-border-subtle border-t-accent h-6 w-6 animate-spin rounded-full border-2\" />\n <p className=\"text-fg-muted text-sm\">{t('demo.stampsLoading')}</p>\n </div>\n ) : shown.length === 0 ? (\n <div className=\"flex flex-col items-center gap-3 px-4 py-10 text-center\">\n <Icon name=\"rubberStamp\" size={32} className=\"text-fg-muted\" />\n <p className=\"text-fg-muted text-sm\">{t('demo.stampsEmpty')}</p>\n </div>\n ) : (\n <ul className=\"grid grid-cols-2 gap-3\">\n {shown.map((asset) => (\n <li key={asset.id} className=\"group relative\">\n <StampAssetCell\n asset={asset}\n armed={armedId === asset.id}\n onArm={() => arm(asset)}\n />\n {!isDefault(asset.libraryId) && (\n <button\n type=\"button\"\n onClick={() => void stamp.deleteAsset(asset.id)}\n title={t('demo.stampsRemoveStamp')}\n className=\"bg-surface border-border text-fg-muted hover:text-fg absolute end-1 top-1 grid h-5 w-5 place-items-center rounded-full border opacity-0 shadow-sm focus:opacity-100 group-hover:opacity-100\"\n >\n <Icon name=\"x\" size={12} />\n </button>\n )}\n </li>\n ))}\n </ul>\n )}\n </div>\n\n {(error || 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 ?? t('demo.stampsArmedHint')}\n </div>\n )}\n\n <div className=\"border-border-subtle shrink-0 border-t p-3\">\n <button\n type=\"button\"\n disabled={busy != null}\n onClick={() => fileRef.current?.click()}\n title={t('demo.stampsImportHint')}\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 disabled:opacity-40\"\n >\n <Icon name=\"rubberStampPlus\" size={16} />\n {busy === 'importing' ? t('demo.stampsImporting') : t('demo.stampsImport')}\n </button>\n <input\n ref={fileRef}\n type=\"file\"\n accept=\"application/pdf\"\n className=\"hidden\"\n onChange={(event) => {\n const file = event.currentTarget.files?.[0];\n event.currentTarget.value = ''; // allow re-picking the same file\n if (file) importPdf(file);\n }}\n />\n </div>\n </div>\n );\n}\n\n/** One grid cell: the plugin's cached preview through the object-URL hook\n * (the DOM resource's lifetime belongs to the framework layer, not the\n * store). Image only — the artwork is the label; the label is the\n * tooltip for the curious. */\nfunction StampAssetCell({\n asset,\n armed,\n onArm,\n}: {\n asset: StampAsset;\n armed: boolean;\n onArm: () => void;\n}) {\n const url = useStampAssetPreviewUrl(asset.id);\n return (\n <button\n type=\"button\"\n onClick={onArm}\n title={asset.label}\n aria-pressed={armed}\n className={`flex aspect-square w-full items-center justify-center rounded-md border p-2 ${\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={asset.label} className=\"max-h-full max-w-full object-contain\" />\n ) : (\n <span className=\"text-fg truncate text-sm\">{asset.label}</span>\n )}\n </button>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuCA,MAAM,MAAM;AAEZ,SAAgB,cAAc;CAC5B,MAAM,IAAI,KAAK;CACf,MAAM,QAAQ,SAAS;CACvB,MAAM,EAAE,UAAU,YAAY,iBAAiB;CAG/C,MAAM,YAAY,kBAAkB,EAAE,MAAM,WAAW,CAAC,QAAQ,EAAE,CAAC;CACnE,MAAM,SAAS,eAAe;CAC9B,MAAM,EAAE,iBAAiB,oBAAoB;CAC7C,MAAM,EAAE,WAAW,aAAa;CAChC,MAAM,UAAU,WAAW,QAAQ;CACnC,MAAM,UAAU,OAAyB,IAAI;CAC7C,MAAM,CAAC,SAAS,cAAc,SAAwB,IAAI;CAC1D,MAAM,CAAC,MAAM,WAAW,SAAyC,IAAI;CACrE,MAAM,CAAC,OAAO,YAAY,SAAwB,IAAI;CACtD,MAAM,CAAC,QAAQ,aAAa,SAAiB,GAAG;CAGhD,MAAM,WAAW,QAAQ,OAAO;CAChC,gBAAgB;EACd,IAAI,OAAO,aAAa,UAAU,UAAU,QAAQ;CACtD,GAAG,CAAC,QAAQ,CAAC;CAOb,gBAAgB;EACd,IAAI,OAAO;EACX,QAAQ,SAAS;EACjB,0BAA0B,KAAK,CAAC,CAC7B,WAAW,qBAAqB,OAAO,oBAAoB,MAAM,GAAG,QAAQ,CAAC,CAAC,CAC9E,OAAO,UAAmB;GACzB,QAAQ,MAAM,qCAAqC,KAAK;GACxD,IAAI,MAAM,SAAS,EAAE,kBAAkB,CAAC;EAC1C,CAAC,CAAC,CACD,cAAc,QAAQ,QAAQ,IAAI,CAAC;EACtC,aAAa;GACX,OAAO;EACT;CAGF,GAAG,CAAC,OAAO,MAAM,CAAC;CAIlB,gBAAgB;EACd,IAAI,iBAAiB,SAAS,WAAW,IAAI;CAC/C,GAAG,CAAC,YAAY,CAAC;CAEjB,MAAM,OAAO,UAAsB;EACjC,SAAS,IAAI;EACb,WAAW,MAAM,EAAE;EACnB,MAAW,SAAS,MAAM,EAAE,CAAC,CAAC,OAAO,UAAU;GAC7C,QAAQ,MAAM,gCAAgC,KAAK;GACnD,WAAW,IAAI;GACf,SAAS,EAAE,qBAAqB,CAAC;EACnC,CAAC;CACH;CAEA,MAAM,aAAa,SAAe;EAChC,SAAS,IAAI;EACb,QAAQ,WAAW;EAGnB,MACG,cAAc,MAAM,EAAE,MAAM,KAAK,KAAK,QAAQ,WAAW,EAAE,EAAE,CAAC,CAAC,CAC/D,MAAM,EAAE,cAAc,UAAU,QAAQ,EAAE,CAAC,CAAC,CAC5C,OAAO,UAAU;GAChB,QAAQ,MAAM,2CAA2C,KAAK;GAC9D,SAAS,EAAE,wBAAwB,CAAC;EACtC,CAAC,CAAC,CACD,cAAc,QAAQ,IAAI,CAAC;CAChC;;;CAIA,MAAM,YAAY,OAAO,WAAmB,SAAiB;EAC3D,MAAM,QAAQ,MAAM,MAAM,cAAc,SAAS,CAAC,CAAC,YAAY,IAAI;EACnE,IAAI,CAAC,OAAO;EACZ,MAAM,OAAO,IAAI,KAAK,CAAC,KAAiB,GAAG,EAAE,MAAM,kBAAkB,CAAC;EACtE,MAAM,MAAM,IAAI,gBAAgB,IAAI;EACpC,MAAM,SAAS,SAAS,cAAc,GAAG;EACzC,OAAO,OAAO;EACd,OAAO,WAAW,GAAG,QAAQ,SAAS;EACtC,OAAO,MAAM;EACb,IAAI,gBAAgB,GAAG;CACzB;CAKA,MAAM,aACJ,UAAU,WAAW,IACjB,UAAU,EAAE,CAAC,KACb,UAAU,MAAM,YAAY,QAAQ,OAAO,MAAM,IAC/C,SACA;CACR,MAAM,WACJ,eAAe,MAAM,OAAQ,UAAU,MAAM,YAAY,QAAQ,OAAO,UAAU,KAAK;CACzF,MAAM,QACJ,eAAe,MAAM,SAAS,OAAO,QAAQ,UAAU,MAAM,cAAc,UAAU;CACvF,MAAM,aAAa,cAAsB,cAAc;CAEvD,OACE,qBAAC,OAAD;EAAK,WAAU;EAAf,UAAA;GACG,UAAU,SAAS,KAClB,qBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACG,UAAU,SAAS,IAClB,qBAAC,UAAD;KACE,OAAO;KACP,WAAW,UAAU,UAAU,MAAM,OAAO,KAAK;KACjD,cAAY,EAAE,eAAe;KAC7B,WAAU;KAJZ,UAAA,CAME,oBAAC,UAAD;MAAQ,OAAO;MAAM,UAAA,EAAE,gBAAgB;KAAU,CAAA,GAChD,UAAU,KAAK,YACd,oBAAC,UAAD;MAAyB,OAAO,QAAQ;MACrC,UAAA,QAAQ;KACH,GAFK,QAAQ,EAEb,CACT,CACK;IAER,CAAA,IAAA,oBAAC,QAAD;KAAM,WAAU;KACb,UAAA,UAAU,EAAE,CAAC;IACV,CAAA,GAEP,YACC,qBAAA,YAAA,EAAA,UAAA,CACE,oBAAC,UAAD;KACE,MAAK;KACL,eAAe,UAAU,SAAS,IAAI,SAAS,IAAI;KACnD,OAAO,EAAE,0BAA0B;KACnC,WAAU;KAEV,UAAA,oBAAC,MAAD;MAAM,MAAK;MAAW,MAAM;KAAK,CAAA;IAC3B,CAAA,GACP,CAAC,UAAU,SAAS,EAAE,KACrB,oBAAC,UAAD;KACE,MAAK;KACL,eAAe,KAAK,MAAM,cAAc,SAAS,EAAE;KACnD,OAAO,EAAE,0BAA0B;KACnC,WAAU;KAEV,UAAA,oBAAC,MAAD;MAAM,MAAK;MAAQ,MAAM;KAAK,CAAA;IACxB,CAAA,CAEV,EAAA,CAAA,CAED;;GAGP,oBAAC,OAAD;IAAK,WAAU;IACZ,UAAA,SAAS,aAAa,MAAM,WAAW,IACtC,qBAAC,OAAD;KAAK,WAAU;KAAf,UAAA,CACE,oBAAC,OAAD,EAAK,WAAU,kFAAmF,CAAA,GAClG,oBAAC,KAAD;MAAG,WAAU;MAAyB,UAAA,EAAE,oBAAoB;KAAK,CAAA,CAC9D;IACH,CAAA,IAAA,MAAM,WAAW,IACnB,qBAAC,OAAD;KAAK,WAAU;KAAf,UAAA,CACE,oBAAC,MAAD;MAAM,MAAK;MAAc,MAAM;MAAI,WAAU;KAAiB,CAAA,GAC9D,oBAAC,KAAD;MAAG,WAAU;MAAyB,UAAA,EAAE,kBAAkB;KAAK,CAAA,CAC5D;IAEL,CAAA,IAAA,oBAAC,MAAD;KAAI,WAAU;KACX,UAAA,MAAM,KAAK,UACV,qBAAC,MAAD;MAAmB,WAAU;MAA7B,UAAA,CACE,oBAAC,gBAAD;OACS;OACP,OAAO,YAAY,MAAM;OACzB,aAAa,IAAI,KAAK;MACvB,CAAA,GACA,CAAC,UAAU,MAAM,SAAS,KACzB,oBAAC,UAAD;OACE,MAAK;OACL,eAAe,KAAK,MAAM,YAAY,MAAM,EAAE;OAC9C,OAAO,EAAE,wBAAwB;OACjC,WAAU;OAEV,UAAA,oBAAC,MAAD;QAAM,MAAK;QAAI,MAAM;OAAK,CAAA;MACpB,CAAA,CAER;KAhBK,GAAA,MAAM,EAgBX,CACL;IACC,CAAA;GAEH,CAAA;IAEH,SAAS,YACT,oBAAC,OAAD;IACE,WAAW,4DACT,QAAQ,iBAAiB;IAG1B,UAAA,SAAS,EAAE,sBAAsB;GAC/B,CAAA;GAGP,qBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,qBAAC,UAAD;KACE,MAAK;KACL,UAAU,QAAQ;KAClB,eAAe,QAAQ,SAAS,MAAM;KACtC,OAAO,EAAE,uBAAuB;KAChC,WAAU;KALZ,UAAA,CAOE,oBAAC,MAAD;MAAM,MAAK;MAAkB,MAAM;KAAK,CAAA,GACvC,SAAS,cAAc,EAAE,sBAAsB,IAAI,EAAE,mBAAmB,CACnE;IACR,CAAA,GAAA,oBAAC,SAAD;KACE,KAAK;KACL,MAAK;KACL,QAAO;KACP,WAAU;KACV,WAAW,UAAU;MACnB,MAAM,OAAO,MAAM,cAAc,QAAQ;MACzC,MAAM,cAAc,QAAQ;MAC5B,IAAI,MAAM,UAAU,IAAI;KAC1B;IACD,CAAA,CACE;;EACF;;AAET;;;;;AAMA,SAAS,eAAe,EACtB,OACA,OACA,SAKC;CACD,MAAM,MAAM,wBAAwB,MAAM,EAAE;CAC5C,OACE,oBAAC,UAAD;EACE,MAAK;EACL,SAAS;EACT,OAAO,MAAM;EACb,gBAAc;EACd,WAAW,+EACT,QACI,qDACA;EAGL,UAAA,MACC,oBAAC,OAAD;GAAK,KAAK;GAAK,KAAK,MAAM;GAAO,WAAU;EAAwC,CAAA,IAEnF,oBAAC,QAAD;GAAM,WAAU;GAA4B,UAAA,MAAM;EAAY,CAAA;CAE1D,CAAA;AAEZ"}
|