@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,16 @@
|
|
|
1
|
+
import { useT } from "@embedpdf/react/i18n";
|
|
2
|
+
//#region src/ui/labels.ts
|
|
3
|
+
/**
|
|
4
|
+
* The labels the libraries give in English with a translation key beside them: the annotation
|
|
5
|
+
* kinds' style properties and their options, and the form plugin's field settings. The viewer
|
|
6
|
+
* shows each in its language, and the English where a language has no string for the key.
|
|
7
|
+
*/
|
|
8
|
+
/** Reads a {@link Labelled} in the viewer's language. */
|
|
9
|
+
function useLabel() {
|
|
10
|
+
const t = useT();
|
|
11
|
+
return (item) => t(item.labelKey, { fallback: item.label });
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
export { useLabel as t };
|
|
15
|
+
|
|
16
|
+
//# sourceMappingURL=labels-C6_i87Oj.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"labels-C6_i87Oj.js","names":[],"sources":["../src/ui/labels.ts"],"sourcesContent":["/**\n * The labels the libraries give in English with a translation key beside them: the annotation\n * kinds' style properties and their options, and the form plugin's field settings. The viewer\n * shows each in its language, and the English where a language has no string for the key.\n */\nimport { useT } from '@embedpdf/react/i18n';\n\n/** A label as the libraries give it: the English, and its key. */\nexport interface Labelled {\n readonly label: string;\n readonly labelKey: string;\n}\n\n/** Reads a {@link Labelled} in the viewer's language. */\nexport function useLabel(): (item: Labelled) => string {\n const t = useT();\n return (item) => t(item.labelKey, { fallback: item.label });\n}\n"],"mappings":";;;;;;;;AAcA,SAAgB,WAAuC;CACrD,MAAM,IAAI,KAAK;CACf,QAAQ,SAAS,EAAE,KAAK,UAAU,EAAE,UAAU,KAAK,MAAM,CAAC;AAC5D"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
const require_icons = require("./icons-x0Tc05bs.cjs");
|
|
2
|
+
let react = require("react");
|
|
3
|
+
let _embedpdf_react_runtime = require("@embedpdf/react/runtime");
|
|
4
|
+
let _embedpdf_react_annotation = require("@embedpdf/react/annotation");
|
|
5
|
+
let _embedpdf_react_i18n = require("@embedpdf/react/i18n");
|
|
6
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
7
|
+
//#region src/ui/link-editor.tsx
|
|
8
|
+
/**
|
|
9
|
+
* The link editor popover. It rides the same selection anchor as the action
|
|
10
|
+
* strip (`<AnnotationMenu>` solves where), opening from the strip's link icon
|
|
11
|
+
* (`annotation:link` toggles the `link-editor` shell surface) and rendering in
|
|
12
|
+
* place of the strip while open — one anchored card at a time.
|
|
13
|
+
*
|
|
14
|
+
* Reads the selection's current target from selection props (the `linkOf`
|
|
15
|
+
* lens — parents derive from their committed child annotations) and writes
|
|
16
|
+
* through the one `selection.updateLink(target)` path; the plugin's reconciler
|
|
17
|
+
* materializes/retargets the attached children. Links are a verb on the
|
|
18
|
+
* selection, not a style — which is why this is a popover, not a sidebar
|
|
19
|
+
* section.
|
|
20
|
+
*/
|
|
21
|
+
function ModeToggle({ active, title, onClick, children }) {
|
|
22
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
23
|
+
type: "button",
|
|
24
|
+
title,
|
|
25
|
+
onClick,
|
|
26
|
+
className: `rounded border px-2.5 py-1 text-xs font-medium transition-colors ${active ? "border-accent bg-accent text-on-accent" : "border-border bg-surface text-fg hover:bg-hover"}`,
|
|
27
|
+
children
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
function LinkEditorCard({ onClose }) {
|
|
31
|
+
const t = (0, _embedpdf_react_i18n.useT)();
|
|
32
|
+
const kernel = (0, _embedpdf_react_runtime.useKernel)();
|
|
33
|
+
const anno = (0, _embedpdf_react_annotation.useAnnotation)();
|
|
34
|
+
const value = (0, _embedpdf_react_annotation.useAnnotationProperties)().values.link ?? null;
|
|
35
|
+
const [mode, setMode] = (0, react.useState)(value?.kind === "goto" ? "page" : "uri");
|
|
36
|
+
const [uri, setUri] = (0, react.useState)(value?.kind === "uri" ? value.uri : "");
|
|
37
|
+
const [pageNo, setPageNo] = (0, react.useState)("1");
|
|
38
|
+
const apply = () => {
|
|
39
|
+
if (mode === "uri") {
|
|
40
|
+
const trimmed = uri.trim();
|
|
41
|
+
if (!trimmed) return;
|
|
42
|
+
anno.selection.updateLink({
|
|
43
|
+
kind: "uri",
|
|
44
|
+
uri: trimmed
|
|
45
|
+
});
|
|
46
|
+
onClose();
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const page = kernel.documents.getPage(Math.max(0, Number(pageNo) - 1));
|
|
50
|
+
if (!page) return;
|
|
51
|
+
anno.selection.updateLink({
|
|
52
|
+
kind: "goto",
|
|
53
|
+
destination: {
|
|
54
|
+
kind: "fit",
|
|
55
|
+
page: page.ref
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
onClose();
|
|
59
|
+
};
|
|
60
|
+
const inputCls = "border-border bg-surface text-fg focus:border-accent w-full rounded border px-2.5 py-1.5 text-sm outline-none";
|
|
61
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
62
|
+
className: "border-border bg-surface w-64 rounded-lg border p-3 shadow-lg",
|
|
63
|
+
children: [
|
|
64
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
65
|
+
className: "mb-2 flex items-center justify-between",
|
|
66
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
67
|
+
className: "text-fg text-sm font-semibold",
|
|
68
|
+
children: t("commands.annotate.link")
|
|
69
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
70
|
+
type: "button",
|
|
71
|
+
onClick: onClose,
|
|
72
|
+
"aria-label": t("panel.close"),
|
|
73
|
+
title: t("panel.close"),
|
|
74
|
+
className: "text-fg-muted hover:bg-hover grid h-6 w-6 place-items-center rounded",
|
|
75
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_icons.Icon, {
|
|
76
|
+
name: "x",
|
|
77
|
+
size: 14
|
|
78
|
+
})
|
|
79
|
+
})]
|
|
80
|
+
}),
|
|
81
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
82
|
+
className: "mb-2 flex gap-1.5",
|
|
83
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModeToggle, {
|
|
84
|
+
title: t("linkEditor.toUrl"),
|
|
85
|
+
active: mode === "uri",
|
|
86
|
+
onClick: () => setMode("uri"),
|
|
87
|
+
children: t("linkEditor.url")
|
|
88
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModeToggle, {
|
|
89
|
+
title: t("linkEditor.toPage"),
|
|
90
|
+
active: mode === "page",
|
|
91
|
+
onClick: () => setMode("page"),
|
|
92
|
+
children: t("linkEditor.page")
|
|
93
|
+
})]
|
|
94
|
+
}),
|
|
95
|
+
mode === "uri" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
96
|
+
type: "url",
|
|
97
|
+
className: inputCls,
|
|
98
|
+
placeholder: t("linkEditor.urlPlaceholder"),
|
|
99
|
+
autoFocus: true,
|
|
100
|
+
value: uri,
|
|
101
|
+
onChange: (event) => setUri(event.target.value),
|
|
102
|
+
onKeyDown: (event) => event.key === "Enter" && apply()
|
|
103
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
104
|
+
type: "number",
|
|
105
|
+
min: 1,
|
|
106
|
+
className: inputCls,
|
|
107
|
+
placeholder: t("linkEditor.pagePlaceholder"),
|
|
108
|
+
autoFocus: true,
|
|
109
|
+
value: pageNo,
|
|
110
|
+
onChange: (event) => setPageNo(event.target.value),
|
|
111
|
+
onKeyDown: (event) => event.key === "Enter" && apply()
|
|
112
|
+
}),
|
|
113
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
114
|
+
type: "button",
|
|
115
|
+
className: "bg-accent text-on-accent mt-2.5 w-full rounded px-2 py-1.5 text-sm font-medium",
|
|
116
|
+
onClick: apply,
|
|
117
|
+
children: value == null ? t("linkEditor.add") : t("linkEditor.update")
|
|
118
|
+
})
|
|
119
|
+
]
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
//#endregion
|
|
123
|
+
exports.LinkEditorCard = LinkEditorCard;
|
|
124
|
+
|
|
125
|
+
//# sourceMappingURL=link-editor-CSjqPnj1.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link-editor-CSjqPnj1.cjs","names":["Icon"],"sources":["../src/ui/link-editor.tsx"],"sourcesContent":["/**\n * The link editor popover. It rides the same selection anchor as the action\n * strip (`<AnnotationMenu>` solves where), opening from the strip's link icon\n * (`annotation:link` toggles the `link-editor` shell surface) and rendering in\n * place of the strip while open — one anchored card at a time.\n *\n * Reads the selection's current target from selection props (the `linkOf`\n * lens — parents derive from their committed child annotations) and writes\n * through the one `selection.updateLink(target)` path; the plugin's reconciler\n * materializes/retargets the attached children. Links are a verb on the\n * selection, not a style — which is why this is a popover, not a sidebar\n * section.\n */\nimport { useState, type ReactNode } from 'react';\nimport { useAnnotation, useAnnotationProperties } from '@embedpdf/react/annotation';\nimport type { PdfLinkTarget } from '@embedpdf/react/link';\nimport { useKernel } from '@embedpdf/react/runtime';\nimport { useT } from '@embedpdf/react/i18n';\nimport { Icon } from './icons';\n\nfunction ModeToggle({\n active,\n title,\n onClick,\n children,\n}: {\n active: boolean;\n title: string;\n onClick: () => void;\n children: ReactNode;\n}) {\n return (\n <button\n type=\"button\"\n title={title}\n onClick={onClick}\n className={`rounded border px-2.5 py-1 text-xs font-medium transition-colors ${\n active\n ? 'border-accent bg-accent text-on-accent'\n : 'border-border bg-surface text-fg hover:bg-hover'\n }`}\n >\n {children}\n </button>\n );\n}\n\nexport function LinkEditorCard({ onClose }: { onClose: () => void }) {\n const t = useT();\n const kernel = useKernel();\n const anno = useAnnotation();\n const props = useAnnotationProperties();\n const value = (props.values.link ?? null) as PdfLinkTarget | null;\n const [mode, setMode] = useState<'uri' | 'page'>(value?.kind === 'goto' ? 'page' : 'uri');\n const [uri, setUri] = useState(value?.kind === 'uri' ? value.uri : '');\n const [pageNo, setPageNo] = useState('1');\n\n const apply = () => {\n if (mode === 'uri') {\n const trimmed = uri.trim();\n if (!trimmed) return;\n anno.selection.updateLink({ kind: 'uri', uri: trimmed });\n onClose();\n return;\n }\n // Page number (1-based) → the page's object number (stable across moves).\n const page = kernel.documents.getPage(Math.max(0, Number(pageNo) - 1));\n if (!page) return;\n anno.selection.updateLink({ kind: 'goto', destination: { kind: 'fit', page: page.ref } });\n onClose();\n };\n\n const inputCls =\n 'border-border bg-surface text-fg focus:border-accent w-full rounded border px-2.5 py-1.5 text-sm outline-none';\n return (\n <div className=\"border-border bg-surface w-64 rounded-lg border p-3 shadow-lg\">\n <div className=\"mb-2 flex items-center justify-between\">\n <span className=\"text-fg text-sm font-semibold\">{t('commands.annotate.link')}</span>\n <button\n type=\"button\"\n onClick={onClose}\n aria-label={t('panel.close')}\n title={t('panel.close')}\n className=\"text-fg-muted hover:bg-hover grid h-6 w-6 place-items-center rounded\"\n >\n <Icon name=\"x\" size={14} />\n </button>\n </div>\n <div className=\"mb-2 flex gap-1.5\">\n <ModeToggle\n title={t('linkEditor.toUrl')}\n active={mode === 'uri'}\n onClick={() => setMode('uri')}\n >\n {t('linkEditor.url')}\n </ModeToggle>\n <ModeToggle\n title={t('linkEditor.toPage')}\n active={mode === 'page'}\n onClick={() => setMode('page')}\n >\n {t('linkEditor.page')}\n </ModeToggle>\n </div>\n {mode === 'uri' ? (\n <input\n type=\"url\"\n className={inputCls}\n placeholder={t('linkEditor.urlPlaceholder')}\n autoFocus\n value={uri}\n onChange={(event) => setUri(event.target.value)}\n onKeyDown={(event) => event.key === 'Enter' && apply()}\n />\n ) : (\n <input\n type=\"number\"\n min={1}\n className={inputCls}\n placeholder={t('linkEditor.pagePlaceholder')}\n autoFocus\n value={pageNo}\n onChange={(event) => setPageNo(event.target.value)}\n onKeyDown={(event) => event.key === 'Enter' && apply()}\n />\n )}\n <button\n type=\"button\"\n className=\"bg-accent text-on-accent mt-2.5 w-full rounded px-2 py-1.5 text-sm font-medium\"\n onClick={apply}\n >\n {value == null ? t('linkEditor.add') : t('linkEditor.update')}\n </button>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { n as Icon } from "./icons-BrK5iVL8.js";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { useKernel } from "@embedpdf/react/runtime";
|
|
4
|
+
import { useAnnotation, useAnnotationProperties } from "@embedpdf/react/annotation";
|
|
5
|
+
import { useT } from "@embedpdf/react/i18n";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
//#region src/ui/link-editor.tsx
|
|
8
|
+
/**
|
|
9
|
+
* The link editor popover. It rides the same selection anchor as the action
|
|
10
|
+
* strip (`<AnnotationMenu>` solves where), opening from the strip's link icon
|
|
11
|
+
* (`annotation:link` toggles the `link-editor` shell surface) and rendering in
|
|
12
|
+
* place of the strip while open — one anchored card at a time.
|
|
13
|
+
*
|
|
14
|
+
* Reads the selection's current target from selection props (the `linkOf`
|
|
15
|
+
* lens — parents derive from their committed child annotations) and writes
|
|
16
|
+
* through the one `selection.updateLink(target)` path; the plugin's reconciler
|
|
17
|
+
* materializes/retargets the attached children. Links are a verb on the
|
|
18
|
+
* selection, not a style — which is why this is a popover, not a sidebar
|
|
19
|
+
* section.
|
|
20
|
+
*/
|
|
21
|
+
function ModeToggle({ active, title, onClick, children }) {
|
|
22
|
+
return /* @__PURE__ */ jsx("button", {
|
|
23
|
+
type: "button",
|
|
24
|
+
title,
|
|
25
|
+
onClick,
|
|
26
|
+
className: `rounded border px-2.5 py-1 text-xs font-medium transition-colors ${active ? "border-accent bg-accent text-on-accent" : "border-border bg-surface text-fg hover:bg-hover"}`,
|
|
27
|
+
children
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
function LinkEditorCard({ onClose }) {
|
|
31
|
+
const t = useT();
|
|
32
|
+
const kernel = useKernel();
|
|
33
|
+
const anno = useAnnotation();
|
|
34
|
+
const value = useAnnotationProperties().values.link ?? null;
|
|
35
|
+
const [mode, setMode] = useState(value?.kind === "goto" ? "page" : "uri");
|
|
36
|
+
const [uri, setUri] = useState(value?.kind === "uri" ? value.uri : "");
|
|
37
|
+
const [pageNo, setPageNo] = useState("1");
|
|
38
|
+
const apply = () => {
|
|
39
|
+
if (mode === "uri") {
|
|
40
|
+
const trimmed = uri.trim();
|
|
41
|
+
if (!trimmed) return;
|
|
42
|
+
anno.selection.updateLink({
|
|
43
|
+
kind: "uri",
|
|
44
|
+
uri: trimmed
|
|
45
|
+
});
|
|
46
|
+
onClose();
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const page = kernel.documents.getPage(Math.max(0, Number(pageNo) - 1));
|
|
50
|
+
if (!page) return;
|
|
51
|
+
anno.selection.updateLink({
|
|
52
|
+
kind: "goto",
|
|
53
|
+
destination: {
|
|
54
|
+
kind: "fit",
|
|
55
|
+
page: page.ref
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
onClose();
|
|
59
|
+
};
|
|
60
|
+
const inputCls = "border-border bg-surface text-fg focus:border-accent w-full rounded border px-2.5 py-1.5 text-sm outline-none";
|
|
61
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
62
|
+
className: "border-border bg-surface w-64 rounded-lg border p-3 shadow-lg",
|
|
63
|
+
children: [
|
|
64
|
+
/* @__PURE__ */ jsxs("div", {
|
|
65
|
+
className: "mb-2 flex items-center justify-between",
|
|
66
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
67
|
+
className: "text-fg text-sm font-semibold",
|
|
68
|
+
children: t("commands.annotate.link")
|
|
69
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
70
|
+
type: "button",
|
|
71
|
+
onClick: onClose,
|
|
72
|
+
"aria-label": t("panel.close"),
|
|
73
|
+
title: t("panel.close"),
|
|
74
|
+
className: "text-fg-muted hover:bg-hover grid h-6 w-6 place-items-center rounded",
|
|
75
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
76
|
+
name: "x",
|
|
77
|
+
size: 14
|
|
78
|
+
})
|
|
79
|
+
})]
|
|
80
|
+
}),
|
|
81
|
+
/* @__PURE__ */ jsxs("div", {
|
|
82
|
+
className: "mb-2 flex gap-1.5",
|
|
83
|
+
children: [/* @__PURE__ */ jsx(ModeToggle, {
|
|
84
|
+
title: t("linkEditor.toUrl"),
|
|
85
|
+
active: mode === "uri",
|
|
86
|
+
onClick: () => setMode("uri"),
|
|
87
|
+
children: t("linkEditor.url")
|
|
88
|
+
}), /* @__PURE__ */ jsx(ModeToggle, {
|
|
89
|
+
title: t("linkEditor.toPage"),
|
|
90
|
+
active: mode === "page",
|
|
91
|
+
onClick: () => setMode("page"),
|
|
92
|
+
children: t("linkEditor.page")
|
|
93
|
+
})]
|
|
94
|
+
}),
|
|
95
|
+
mode === "uri" ? /* @__PURE__ */ jsx("input", {
|
|
96
|
+
type: "url",
|
|
97
|
+
className: inputCls,
|
|
98
|
+
placeholder: t("linkEditor.urlPlaceholder"),
|
|
99
|
+
autoFocus: true,
|
|
100
|
+
value: uri,
|
|
101
|
+
onChange: (event) => setUri(event.target.value),
|
|
102
|
+
onKeyDown: (event) => event.key === "Enter" && apply()
|
|
103
|
+
}) : /* @__PURE__ */ jsx("input", {
|
|
104
|
+
type: "number",
|
|
105
|
+
min: 1,
|
|
106
|
+
className: inputCls,
|
|
107
|
+
placeholder: t("linkEditor.pagePlaceholder"),
|
|
108
|
+
autoFocus: true,
|
|
109
|
+
value: pageNo,
|
|
110
|
+
onChange: (event) => setPageNo(event.target.value),
|
|
111
|
+
onKeyDown: (event) => event.key === "Enter" && apply()
|
|
112
|
+
}),
|
|
113
|
+
/* @__PURE__ */ jsx("button", {
|
|
114
|
+
type: "button",
|
|
115
|
+
className: "bg-accent text-on-accent mt-2.5 w-full rounded px-2 py-1.5 text-sm font-medium",
|
|
116
|
+
onClick: apply,
|
|
117
|
+
children: value == null ? t("linkEditor.add") : t("linkEditor.update")
|
|
118
|
+
})
|
|
119
|
+
]
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
//#endregion
|
|
123
|
+
export { LinkEditorCard };
|
|
124
|
+
|
|
125
|
+
//# sourceMappingURL=link-editor-SxM1nnJU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link-editor-SxM1nnJU.js","names":[],"sources":["../src/ui/link-editor.tsx"],"sourcesContent":["/**\n * The link editor popover. It rides the same selection anchor as the action\n * strip (`<AnnotationMenu>` solves where), opening from the strip's link icon\n * (`annotation:link` toggles the `link-editor` shell surface) and rendering in\n * place of the strip while open — one anchored card at a time.\n *\n * Reads the selection's current target from selection props (the `linkOf`\n * lens — parents derive from their committed child annotations) and writes\n * through the one `selection.updateLink(target)` path; the plugin's reconciler\n * materializes/retargets the attached children. Links are a verb on the\n * selection, not a style — which is why this is a popover, not a sidebar\n * section.\n */\nimport { useState, type ReactNode } from 'react';\nimport { useAnnotation, useAnnotationProperties } from '@embedpdf/react/annotation';\nimport type { PdfLinkTarget } from '@embedpdf/react/link';\nimport { useKernel } from '@embedpdf/react/runtime';\nimport { useT } from '@embedpdf/react/i18n';\nimport { Icon } from './icons';\n\nfunction ModeToggle({\n active,\n title,\n onClick,\n children,\n}: {\n active: boolean;\n title: string;\n onClick: () => void;\n children: ReactNode;\n}) {\n return (\n <button\n type=\"button\"\n title={title}\n onClick={onClick}\n className={`rounded border px-2.5 py-1 text-xs font-medium transition-colors ${\n active\n ? 'border-accent bg-accent text-on-accent'\n : 'border-border bg-surface text-fg hover:bg-hover'\n }`}\n >\n {children}\n </button>\n );\n}\n\nexport function LinkEditorCard({ onClose }: { onClose: () => void }) {\n const t = useT();\n const kernel = useKernel();\n const anno = useAnnotation();\n const props = useAnnotationProperties();\n const value = (props.values.link ?? null) as PdfLinkTarget | null;\n const [mode, setMode] = useState<'uri' | 'page'>(value?.kind === 'goto' ? 'page' : 'uri');\n const [uri, setUri] = useState(value?.kind === 'uri' ? value.uri : '');\n const [pageNo, setPageNo] = useState('1');\n\n const apply = () => {\n if (mode === 'uri') {\n const trimmed = uri.trim();\n if (!trimmed) return;\n anno.selection.updateLink({ kind: 'uri', uri: trimmed });\n onClose();\n return;\n }\n // Page number (1-based) → the page's object number (stable across moves).\n const page = kernel.documents.getPage(Math.max(0, Number(pageNo) - 1));\n if (!page) return;\n anno.selection.updateLink({ kind: 'goto', destination: { kind: 'fit', page: page.ref } });\n onClose();\n };\n\n const inputCls =\n 'border-border bg-surface text-fg focus:border-accent w-full rounded border px-2.5 py-1.5 text-sm outline-none';\n return (\n <div className=\"border-border bg-surface w-64 rounded-lg border p-3 shadow-lg\">\n <div className=\"mb-2 flex items-center justify-between\">\n <span className=\"text-fg text-sm font-semibold\">{t('commands.annotate.link')}</span>\n <button\n type=\"button\"\n onClick={onClose}\n aria-label={t('panel.close')}\n title={t('panel.close')}\n className=\"text-fg-muted hover:bg-hover grid h-6 w-6 place-items-center rounded\"\n >\n <Icon name=\"x\" size={14} />\n </button>\n </div>\n <div className=\"mb-2 flex gap-1.5\">\n <ModeToggle\n title={t('linkEditor.toUrl')}\n active={mode === 'uri'}\n onClick={() => setMode('uri')}\n >\n {t('linkEditor.url')}\n </ModeToggle>\n <ModeToggle\n title={t('linkEditor.toPage')}\n active={mode === 'page'}\n onClick={() => setMode('page')}\n >\n {t('linkEditor.page')}\n </ModeToggle>\n </div>\n {mode === 'uri' ? (\n <input\n type=\"url\"\n className={inputCls}\n placeholder={t('linkEditor.urlPlaceholder')}\n autoFocus\n value={uri}\n onChange={(event) => setUri(event.target.value)}\n onKeyDown={(event) => event.key === 'Enter' && apply()}\n />\n ) : (\n <input\n type=\"number\"\n min={1}\n className={inputCls}\n placeholder={t('linkEditor.pagePlaceholder')}\n autoFocus\n value={pageNo}\n onChange={(event) => setPageNo(event.target.value)}\n onKeyDown={(event) => event.key === 'Enter' && apply()}\n />\n )}\n <button\n type=\"button\"\n className=\"bg-accent text-on-accent mt-2.5 w-full rounded px-2 py-1.5 text-sm font-medium\"\n onClick={apply}\n >\n {value == null ? t('linkEditor.add') : t('linkEditor.update')}\n </button>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAoBA,SAAS,WAAW,EAClB,QACA,OACA,SACA,YAMC;CACD,OACE,oBAAC,UAAD;EACE,MAAK;EACE;EACE;EACT,WAAW,oEACT,SACI,2CACA;EAGL;CACK,CAAA;AAEZ;AAEA,SAAgB,eAAe,EAAE,WAAoC;CACnE,MAAM,IAAI,KAAK;CACf,MAAM,SAAS,UAAU;CACzB,MAAM,OAAO,cAAc;CAE3B,MAAM,QADQ,wBACK,CAAC,CAAC,OAAO,QAAQ;CACpC,MAAM,CAAC,MAAM,WAAW,SAAyB,OAAO,SAAS,SAAS,SAAS,KAAK;CACxF,MAAM,CAAC,KAAK,UAAU,SAAS,OAAO,SAAS,QAAQ,MAAM,MAAM,EAAE;CACrE,MAAM,CAAC,QAAQ,aAAa,SAAS,GAAG;CAExC,MAAM,cAAc;EAClB,IAAI,SAAS,OAAO;GAClB,MAAM,UAAU,IAAI,KAAK;GACzB,IAAI,CAAC,SAAS;GACd,KAAK,UAAU,WAAW;IAAE,MAAM;IAAO,KAAK;GAAQ,CAAC;GACvD,QAAQ;GACR;EACF;EAEA,MAAM,OAAO,OAAO,UAAU,QAAQ,KAAK,IAAI,GAAG,OAAO,MAAM,IAAI,CAAC,CAAC;EACrE,IAAI,CAAC,MAAM;EACX,KAAK,UAAU,WAAW;GAAE,MAAM;GAAQ,aAAa;IAAE,MAAM;IAAO,MAAM,KAAK;GAAI;EAAE,CAAC;EACxF,QAAQ;CACV;CAEA,MAAM,WACJ;CACF,OACE,qBAAC,OAAD;EAAK,WAAU;EAAf,UAAA;GACE,qBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,oBAAC,QAAD;KAAM,WAAU;KAAiC,UAAA,EAAE,wBAAwB;IAAQ,CAAA,GACnF,oBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,cAAY,EAAE,aAAa;KAC3B,OAAO,EAAE,aAAa;KACtB,WAAU;KAEV,UAAA,oBAAC,MAAD;MAAM,MAAK;MAAI,MAAM;KAAK,CAAA;IACpB,CAAA,CACL;;GACL,qBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,oBAAC,YAAD;KACE,OAAO,EAAE,kBAAkB;KAC3B,QAAQ,SAAS;KACjB,eAAe,QAAQ,KAAK;KAE3B,UAAA,EAAE,gBAAgB;IACT,CAAA,GACZ,oBAAC,YAAD;KACE,OAAO,EAAE,mBAAmB;KAC5B,QAAQ,SAAS;KACjB,eAAe,QAAQ,MAAM;KAE5B,UAAA,EAAE,iBAAiB;IACV,CAAA,CACT;;GACJ,SAAS,QACR,oBAAC,SAAD;IACE,MAAK;IACL,WAAW;IACX,aAAa,EAAE,2BAA2B;IAC1C,WAAA;IACA,OAAO;IACP,WAAW,UAAU,OAAO,MAAM,OAAO,KAAK;IAC9C,YAAY,UAAU,MAAM,QAAQ,WAAW,MAAM;GACtD,CAAA,IAED,oBAAC,SAAD;IACE,MAAK;IACL,KAAK;IACL,WAAW;IACX,aAAa,EAAE,4BAA4B;IAC3C,WAAA;IACA,OAAO;IACP,WAAW,UAAU,UAAU,MAAM,OAAO,KAAK;IACjD,YAAY,UAAU,MAAM,QAAQ,WAAW,MAAM;GACtD,CAAA;GAEH,oBAAC,UAAD;IACE,MAAK;IACL,WAAU;IACV,SAAS;IAER,UAAA,SAAS,OAAO,EAAE,gBAAgB,IAAI,EAAE,mBAAmB;GACtD,CAAA;EACL;;AAET"}
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import { t as Dialog } from "./dialog-B5GBisKv.js";
|
|
2
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
|
+
import { useCapability, useSelector } from "@embedpdf/react/runtime";
|
|
4
|
+
import { StageToken } from "@embedpdf/react/stage";
|
|
5
|
+
import { AnnotationToken, useAnnotationState } from "@embedpdf/react/annotation";
|
|
6
|
+
import { MeasurementToken, useMeasurement, useMeasurementState, usePageScale } from "@embedpdf/react/measurement";
|
|
7
|
+
import { useT } from "@embedpdf/react/i18n";
|
|
8
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
//#region src/ui/measurement.tsx
|
|
10
|
+
const control = "border-border bg-surface text-fg w-full rounded-md border px-2 py-1.5 text-sm disabled:opacity-50";
|
|
11
|
+
const button = "border-border text-fg hover:bg-hover rounded-md border px-3 py-1.5 text-sm disabled:opacity-50";
|
|
12
|
+
/** The current page's address, or null with no page (empty document). */
|
|
13
|
+
const useCurrentPage = () => useSelector(StageToken, (stage) => stage.getCurrentPage());
|
|
14
|
+
function MeasurementSection() {
|
|
15
|
+
const t = useT();
|
|
16
|
+
const page = useCurrentPage();
|
|
17
|
+
const measurement = useMeasurement();
|
|
18
|
+
const busy = useMeasurementState((state) => state.busy);
|
|
19
|
+
const canCalibrate = useSelector(MeasurementToken, (current) => current.canCalibrate());
|
|
20
|
+
const scale = usePageScale(page);
|
|
21
|
+
const anno = useCapability(AnnotationToken);
|
|
22
|
+
const selected = useAnnotationState((state) => state.selected);
|
|
23
|
+
const resettable = useSelector(AnnotationToken, (annotation) => annotation.selection.list().filter((candidate) => (candidate.subtype === "polygon" || candidate.subtype === "polyline") && candidate.captionCenter && annotation.canUpdate(candidate.ref) && !candidate.lockedContents), (left, right) => left.length === right.length && left.every((annotation, i) => annotation === right[i]));
|
|
24
|
+
const readouts = useSelector(MeasurementToken, (measurement) => anno.selection.list().map((annotation) => measurement.getReadout(annotation.ref)).filter((readout) => !("unavailable" in readout)), (left, right) => JSON.stringify(left) === JSON.stringify(right));
|
|
25
|
+
const reports = useMeasurementState((state) => state.lastReports);
|
|
26
|
+
const [allPages, setAllPages] = useState(false);
|
|
27
|
+
const [recalculate, setRecalculate] = useState(true);
|
|
28
|
+
const [error, setError] = useState(null);
|
|
29
|
+
const disabled = !page || busy || !canCalibrate || !scale?.ready;
|
|
30
|
+
const rectilinear = scale?.measure?.subtype === "rectilinear" ? scale.measure : null;
|
|
31
|
+
const target = allPages ? "all" : page;
|
|
32
|
+
const options = { recalculate };
|
|
33
|
+
const run = async (work) => {
|
|
34
|
+
setError(null);
|
|
35
|
+
try {
|
|
36
|
+
await work();
|
|
37
|
+
} catch (error) {
|
|
38
|
+
setError(error instanceof Error ? error.message : String(error));
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
const precision = rectilinear?.distance[rectilinear.distance.length - 1]?.precision ?? 100;
|
|
42
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
43
|
+
className: "flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto p-4 text-sm",
|
|
44
|
+
children: [
|
|
45
|
+
/* @__PURE__ */ jsx("p", {
|
|
46
|
+
className: "text-fg-muted",
|
|
47
|
+
children: t("measurement.instructions")
|
|
48
|
+
}),
|
|
49
|
+
/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs("span", {
|
|
50
|
+
className: "text-fg-muted",
|
|
51
|
+
children: [t("measurement.scale"), ": "]
|
|
52
|
+
}), /* @__PURE__ */ jsx("strong", { children: rectilinear?.ratio ?? t("measurement.unavailable") })] }),
|
|
53
|
+
scale?.error && /* @__PURE__ */ jsx("p", {
|
|
54
|
+
role: "alert",
|
|
55
|
+
className: "text-red-600",
|
|
56
|
+
children: scale.error.message
|
|
57
|
+
}),
|
|
58
|
+
scale && !scale.persistent && scale.ready && /* @__PURE__ */ jsx("p", {
|
|
59
|
+
className: "text-fg-muted",
|
|
60
|
+
children: t("measurement.sessionOnly")
|
|
61
|
+
}),
|
|
62
|
+
/* @__PURE__ */ jsxs("label", {
|
|
63
|
+
className: "flex items-center gap-2",
|
|
64
|
+
children: [/* @__PURE__ */ jsx("input", {
|
|
65
|
+
type: "checkbox",
|
|
66
|
+
checked: allPages,
|
|
67
|
+
disabled: busy,
|
|
68
|
+
onChange: (event) => setAllPages(event.target.checked)
|
|
69
|
+
}), t("measurement.allPages")]
|
|
70
|
+
}),
|
|
71
|
+
/* @__PURE__ */ jsxs("label", {
|
|
72
|
+
className: "flex items-center gap-2",
|
|
73
|
+
children: [/* @__PURE__ */ jsx("input", {
|
|
74
|
+
type: "checkbox",
|
|
75
|
+
checked: recalculate,
|
|
76
|
+
disabled: busy,
|
|
77
|
+
onChange: (event) => setRecalculate(event.target.checked)
|
|
78
|
+
}), t("measurement.recalculate")]
|
|
79
|
+
}),
|
|
80
|
+
/* @__PURE__ */ jsxs("label", { children: [t("measurement.preset"), /* @__PURE__ */ jsxs("select", {
|
|
81
|
+
"aria-label": t("measurement.preset"),
|
|
82
|
+
className: control,
|
|
83
|
+
value: "",
|
|
84
|
+
disabled,
|
|
85
|
+
onChange: (event) => void run(() => measurement.setPreset(target, event.target.value, options)),
|
|
86
|
+
children: [/* @__PURE__ */ jsx("option", {
|
|
87
|
+
value: "",
|
|
88
|
+
disabled: true,
|
|
89
|
+
children: t("measurement.choosePreset")
|
|
90
|
+
}), measurement.listPresets().map((preset) => /* @__PURE__ */ jsx("option", {
|
|
91
|
+
value: preset.id,
|
|
92
|
+
children: preset.label
|
|
93
|
+
}, preset.id))]
|
|
94
|
+
})] }),
|
|
95
|
+
/* @__PURE__ */ jsxs("label", { children: [t("measurement.unit"), /* @__PURE__ */ jsxs("select", {
|
|
96
|
+
"aria-label": t("measurement.unit"),
|
|
97
|
+
className: control,
|
|
98
|
+
value: rectilinear?.distance[0]?.unit.trim() ?? "",
|
|
99
|
+
disabled: disabled || !rectilinear,
|
|
100
|
+
onChange: (event) => void run(() => measurement.setUnit(target, event.target.value, options)),
|
|
101
|
+
children: [!measurement.listUnits().includes(rectilinear?.distance[0]?.unit.trim()) && /* @__PURE__ */ jsx("option", {
|
|
102
|
+
value: rectilinear?.distance[0]?.unit.trim() ?? "",
|
|
103
|
+
children: t("measurement.custom")
|
|
104
|
+
}), measurement.listUnits().map((unit) => /* @__PURE__ */ jsx("option", {
|
|
105
|
+
value: unit,
|
|
106
|
+
children: unit
|
|
107
|
+
}, unit))]
|
|
108
|
+
})] }),
|
|
109
|
+
/* @__PURE__ */ jsxs("label", { children: [t("measurement.areaUnit"), /* @__PURE__ */ jsxs("select", {
|
|
110
|
+
"aria-label": t("measurement.areaUnit"),
|
|
111
|
+
className: control,
|
|
112
|
+
value: rectilinear?.area[0]?.unit.trim().replace("²", "2") ?? "",
|
|
113
|
+
disabled: disabled || !rectilinear,
|
|
114
|
+
onChange: (event) => void run(() => measurement.setAreaUnit(target, event.target.value, options)),
|
|
115
|
+
children: [!measurement.listAreaUnits().includes(rectilinear?.area[0]?.unit.trim().replace("²", "2")) && /* @__PURE__ */ jsx("option", {
|
|
116
|
+
value: rectilinear?.area[0]?.unit.trim().replace("²", "2") ?? "",
|
|
117
|
+
children: t("measurement.custom")
|
|
118
|
+
}), measurement.listAreaUnits().map((unit) => /* @__PURE__ */ jsx("option", {
|
|
119
|
+
value: unit,
|
|
120
|
+
children: unit.replace("2", "²")
|
|
121
|
+
}, unit))]
|
|
122
|
+
})] }),
|
|
123
|
+
/* @__PURE__ */ jsxs("label", { children: [t("measurement.precision"), /* @__PURE__ */ jsxs("select", {
|
|
124
|
+
"aria-label": t("measurement.precision"),
|
|
125
|
+
className: control,
|
|
126
|
+
value: precision,
|
|
127
|
+
disabled: disabled || !rectilinear,
|
|
128
|
+
onChange: (event) => void run(() => measurement.setPrecision(target, Number(event.target.value), options)),
|
|
129
|
+
children: [![
|
|
130
|
+
1,
|
|
131
|
+
10,
|
|
132
|
+
100,
|
|
133
|
+
1e3,
|
|
134
|
+
1e4
|
|
135
|
+
].includes(precision) && /* @__PURE__ */ jsx("option", {
|
|
136
|
+
value: precision,
|
|
137
|
+
children: t("measurement.custom")
|
|
138
|
+
}), [
|
|
139
|
+
1,
|
|
140
|
+
10,
|
|
141
|
+
100,
|
|
142
|
+
1e3,
|
|
143
|
+
1e4
|
|
144
|
+
].map((candidate) => /* @__PURE__ */ jsx("option", {
|
|
145
|
+
value: candidate,
|
|
146
|
+
children: 1 / candidate
|
|
147
|
+
}, candidate))]
|
|
148
|
+
})] }),
|
|
149
|
+
/* @__PURE__ */ jsx("button", {
|
|
150
|
+
className: button,
|
|
151
|
+
disabled,
|
|
152
|
+
onClick: () => measurement.startCalibration(),
|
|
153
|
+
children: t("measurement.calibrate")
|
|
154
|
+
}),
|
|
155
|
+
selected.length > 0 && /* @__PURE__ */ jsxs("section", {
|
|
156
|
+
className: "border-border-subtle border-t pt-3",
|
|
157
|
+
children: [
|
|
158
|
+
/* @__PURE__ */ jsx("h3", {
|
|
159
|
+
className: "mb-1 font-medium",
|
|
160
|
+
children: t("measurement.selection")
|
|
161
|
+
}),
|
|
162
|
+
readouts.length ? readouts.map((readout, i) => /* @__PURE__ */ jsxs("div", {
|
|
163
|
+
className: "mb-2",
|
|
164
|
+
children: [/* @__PURE__ */ jsxs("p", { children: [
|
|
165
|
+
t(`measurement.${readout.kind}`),
|
|
166
|
+
": ",
|
|
167
|
+
readout.label
|
|
168
|
+
] }), readout.perimeter && /* @__PURE__ */ jsxs("p", { children: [
|
|
169
|
+
t("measurement.perimeter"),
|
|
170
|
+
": ",
|
|
171
|
+
readout.perimeter
|
|
172
|
+
] })]
|
|
173
|
+
}, i)) : /* @__PURE__ */ jsx("p", {
|
|
174
|
+
className: "text-fg-muted",
|
|
175
|
+
children: t("measurement.unavailable")
|
|
176
|
+
}),
|
|
177
|
+
resettable.length > 0 && /* @__PURE__ */ jsx("button", {
|
|
178
|
+
className: button,
|
|
179
|
+
disabled: busy,
|
|
180
|
+
onClick: () => void run(async () => {
|
|
181
|
+
for (const annotation of resettable) if (annotation.subtype === "polygon" || annotation.subtype === "polyline") await anno.update(annotation.ref, {
|
|
182
|
+
subtype: annotation.subtype,
|
|
183
|
+
captionCenter: null
|
|
184
|
+
});
|
|
185
|
+
}),
|
|
186
|
+
children: t("measurement.resetLabel")
|
|
187
|
+
})
|
|
188
|
+
]
|
|
189
|
+
}),
|
|
190
|
+
reports.length > 0 && /* @__PURE__ */ jsx("p", {
|
|
191
|
+
role: "status",
|
|
192
|
+
children: t("measurement.report", { params: {
|
|
193
|
+
updated: reports.reduce((total, report) => total + report.updated.length, 0),
|
|
194
|
+
skipped: reports.reduce((total, report) => total + report.skipped.length, 0),
|
|
195
|
+
failed: reports.reduce((total, report) => total + report.failed.length + (report.scaleError || report.error ? 1 : 0), 0)
|
|
196
|
+
} })
|
|
197
|
+
}),
|
|
198
|
+
error && /* @__PURE__ */ jsx("p", {
|
|
199
|
+
role: "alert",
|
|
200
|
+
className: "text-red-600",
|
|
201
|
+
children: error
|
|
202
|
+
})
|
|
203
|
+
]
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
function CalibrationDialog() {
|
|
207
|
+
const t = useT();
|
|
208
|
+
const measurement = useMeasurement();
|
|
209
|
+
const { busy, calibrationRequest: request } = useMeasurementState();
|
|
210
|
+
const canCalibrate = useSelector(MeasurementToken, (current) => current.canCalibrate());
|
|
211
|
+
const input = useRef(null);
|
|
212
|
+
const [value, setValue] = useState("");
|
|
213
|
+
const [unit, setUnit] = useState("m");
|
|
214
|
+
const [allPages, setAllPages] = useState(false);
|
|
215
|
+
const [recalculate, setRecalculate] = useState(true);
|
|
216
|
+
const [error, setError] = useState(null);
|
|
217
|
+
useEffect(() => {
|
|
218
|
+
setValue("");
|
|
219
|
+
setError(null);
|
|
220
|
+
input.current?.focus();
|
|
221
|
+
}, [request]);
|
|
222
|
+
if (!request) return null;
|
|
223
|
+
const submit = async () => {
|
|
224
|
+
setError(null);
|
|
225
|
+
try {
|
|
226
|
+
await measurement.calibrate({
|
|
227
|
+
page: request.page,
|
|
228
|
+
from: request.from,
|
|
229
|
+
to: request.to,
|
|
230
|
+
distance: {
|
|
231
|
+
value: Number(value),
|
|
232
|
+
unit
|
|
233
|
+
}
|
|
234
|
+
}, {
|
|
235
|
+
recalculate,
|
|
236
|
+
applyTo: allPages ? "all" : void 0
|
|
237
|
+
});
|
|
238
|
+
measurement.dismissCalibration();
|
|
239
|
+
} catch (error) {
|
|
240
|
+
setError(error instanceof Error ? error.message : String(error));
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
return /* @__PURE__ */ jsx(Dialog, {
|
|
244
|
+
labelledBy: "measurement-calibration-title",
|
|
245
|
+
onClose: () => {
|
|
246
|
+
if (!busy) measurement.dismissCalibration();
|
|
247
|
+
},
|
|
248
|
+
className: "border-border bg-surface w-96 rounded-lg border p-5 shadow-xl",
|
|
249
|
+
children: /* @__PURE__ */ jsxs("form", {
|
|
250
|
+
className: "flex flex-col gap-4",
|
|
251
|
+
onSubmit: (event) => {
|
|
252
|
+
event.preventDefault();
|
|
253
|
+
submit();
|
|
254
|
+
},
|
|
255
|
+
children: [
|
|
256
|
+
/* @__PURE__ */ jsx("h2", {
|
|
257
|
+
id: "measurement-calibration-title",
|
|
258
|
+
className: "font-semibold",
|
|
259
|
+
children: t("measurement.calibrate")
|
|
260
|
+
}),
|
|
261
|
+
/* @__PURE__ */ jsx("p", {
|
|
262
|
+
className: "text-fg-muted text-sm",
|
|
263
|
+
children: t("measurement.knownLength")
|
|
264
|
+
}),
|
|
265
|
+
/* @__PURE__ */ jsxs("label", { children: [t("measurement.length"), /* @__PURE__ */ jsx("input", {
|
|
266
|
+
ref: input,
|
|
267
|
+
"aria-label": t("measurement.length"),
|
|
268
|
+
className: control,
|
|
269
|
+
type: "number",
|
|
270
|
+
min: "0",
|
|
271
|
+
step: "any",
|
|
272
|
+
value,
|
|
273
|
+
onChange: (event) => setValue(event.target.value)
|
|
274
|
+
})] }),
|
|
275
|
+
/* @__PURE__ */ jsxs("label", { children: [t("measurement.unit"), /* @__PURE__ */ jsx("select", {
|
|
276
|
+
className: control,
|
|
277
|
+
"aria-label": t("measurement.unit"),
|
|
278
|
+
value: unit,
|
|
279
|
+
onChange: (event) => setUnit(event.target.value),
|
|
280
|
+
children: measurement.listUnits().map((lengthUnit) => /* @__PURE__ */ jsx("option", { children: lengthUnit }, lengthUnit))
|
|
281
|
+
})] }),
|
|
282
|
+
/* @__PURE__ */ jsxs("label", {
|
|
283
|
+
className: "flex items-center gap-2 text-sm",
|
|
284
|
+
children: [/* @__PURE__ */ jsx("input", {
|
|
285
|
+
type: "checkbox",
|
|
286
|
+
checked: allPages,
|
|
287
|
+
onChange: (event) => setAllPages(event.target.checked)
|
|
288
|
+
}), t("measurement.allPages")]
|
|
289
|
+
}),
|
|
290
|
+
/* @__PURE__ */ jsxs("label", {
|
|
291
|
+
className: "flex items-center gap-2 text-sm",
|
|
292
|
+
children: [/* @__PURE__ */ jsx("input", {
|
|
293
|
+
type: "checkbox",
|
|
294
|
+
checked: recalculate,
|
|
295
|
+
onChange: (event) => setRecalculate(event.target.checked)
|
|
296
|
+
}), t("measurement.recalculate")]
|
|
297
|
+
}),
|
|
298
|
+
error && /* @__PURE__ */ jsx("p", {
|
|
299
|
+
role: "alert",
|
|
300
|
+
className: "text-sm text-red-600",
|
|
301
|
+
children: error
|
|
302
|
+
}),
|
|
303
|
+
/* @__PURE__ */ jsxs("div", {
|
|
304
|
+
className: "flex justify-end gap-2",
|
|
305
|
+
children: [/* @__PURE__ */ jsx("button", {
|
|
306
|
+
type: "button",
|
|
307
|
+
className: button,
|
|
308
|
+
disabled: busy,
|
|
309
|
+
onClick: () => measurement.dismissCalibration(),
|
|
310
|
+
children: t("demo.cancel")
|
|
311
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
312
|
+
className: button,
|
|
313
|
+
type: "submit",
|
|
314
|
+
disabled: busy || !canCalibrate || !(Number(value) > 0) || !Number.isFinite(Number(value)),
|
|
315
|
+
children: busy ? t("measurement.applying") : t("measurement.apply")
|
|
316
|
+
})]
|
|
317
|
+
})
|
|
318
|
+
]
|
|
319
|
+
})
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
//#endregion
|
|
323
|
+
export { CalibrationDialog, MeasurementSection };
|
|
324
|
+
|
|
325
|
+
//# sourceMappingURL=measurement-Bs61XWbJ.js.map
|