@embedpdf/react 3.0.0-next.0
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/LICENSE +192 -0
- package/dist/AnnotationListSnapshot-W0RCsyDE-dWna4TTJ.d.cts +2593 -0
- package/dist/AnnotationListSnapshot-W0RCsyDE-dWna4TTJ.d.ts +2593 -0
- package/dist/annotation-B6jwfTCh.d.cts +173 -0
- package/dist/annotation-Ct6dFtzJ.cjs +913 -0
- package/dist/annotation-Ct6dFtzJ.cjs.map +1 -0
- package/dist/annotation-DjGWJljY.js +829 -0
- package/dist/annotation-DjGWJljY.js.map +1 -0
- package/dist/annotation-menu.cjs +115 -0
- package/dist/annotation-menu.cjs.map +1 -0
- package/dist/annotation-menu.d.cts +7 -0
- package/dist/annotation-menu.d.ts +7 -0
- package/dist/annotation-menu.js +99 -0
- package/dist/annotation-menu.js.map +1 -0
- package/dist/annotation-nUMefyjw.d.ts +173 -0
- package/dist/annotation.cjs +25 -0
- package/dist/annotation.d.cts +3 -0
- package/dist/annotation.d.ts +3 -0
- package/dist/annotation.js +4 -0
- package/dist/commands.cjs +59 -0
- package/dist/commands.cjs.map +1 -0
- package/dist/commands.d.cts +20 -0
- package/dist/commands.d.ts +20 -0
- package/dist/commands.js +47 -0
- package/dist/commands.js.map +1 -0
- package/dist/form.cjs +560 -0
- package/dist/form.cjs.map +1 -0
- package/dist/form.d.cts +52 -0
- package/dist/form.d.ts +52 -0
- package/dist/form.js +545 -0
- package/dist/form.js.map +1 -0
- package/dist/i18n.cjs +52 -0
- package/dist/i18n.cjs.map +1 -0
- package/dist/i18n.d.cts +30 -0
- package/dist/i18n.d.ts +30 -0
- package/dist/i18n.js +42 -0
- package/dist/i18n.js.map +1 -0
- package/dist/index.cjs +240 -0
- package/dist/index.d.cts +37 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.js +37 -0
- package/dist/interaction.cjs +152 -0
- package/dist/interaction.cjs.map +1 -0
- package/dist/interaction.d.cts +46 -0
- package/dist/interaction.d.ts +46 -0
- package/dist/interaction.js +139 -0
- package/dist/interaction.js.map +1 -0
- package/dist/link.cjs +108 -0
- package/dist/link.cjs.map +1 -0
- package/dist/link.d.cts +16 -0
- package/dist/link.d.ts +16 -0
- package/dist/link.js +98 -0
- package/dist/link.js.map +1 -0
- package/dist/metadata.cjs +25 -0
- package/dist/metadata.cjs.map +1 -0
- package/dist/metadata.d.cts +11 -0
- package/dist/metadata.d.ts +11 -0
- package/dist/metadata.js +16 -0
- package/dist/metadata.js.map +1 -0
- package/dist/page-edit.cjs +20 -0
- package/dist/page-edit.cjs.map +1 -0
- package/dist/page-edit.d.cts +7 -0
- package/dist/page-edit.d.ts +7 -0
- package/dist/page-edit.js +11 -0
- package/dist/page-edit.js.map +1 -0
- package/dist/page-view.cjs +107 -0
- package/dist/page-view.cjs.map +1 -0
- package/dist/page-view.d.cts +23 -0
- package/dist/page-view.d.ts +23 -0
- package/dist/page-view.js +95 -0
- package/dist/page-view.js.map +1 -0
- package/dist/redaction.cjs +35 -0
- package/dist/redaction.cjs.map +1 -0
- package/dist/redaction.d.cts +14 -0
- package/dist/redaction.d.ts +14 -0
- package/dist/redaction.js +25 -0
- package/dist/redaction.js.map +1 -0
- package/dist/render.cjs +142 -0
- package/dist/render.cjs.map +1 -0
- package/dist/render.d.cts +32 -0
- package/dist/render.d.ts +32 -0
- package/dist/render.js +131 -0
- package/dist/render.js.map +1 -0
- package/dist/rolldown-runtime-DDXJien1.cjs +50 -0
- package/dist/runtime-CMxIuF2O.d.cts +195 -0
- package/dist/runtime-DV98q7I0.d.ts +195 -0
- package/dist/runtime.cjs +314 -0
- package/dist/runtime.cjs.map +1 -0
- package/dist/runtime.d.cts +3 -0
- package/dist/runtime.d.ts +3 -0
- package/dist/runtime.js +287 -0
- package/dist/runtime.js.map +1 -0
- package/dist/scrollbar.cjs +241 -0
- package/dist/scrollbar.cjs.map +1 -0
- package/dist/scrollbar.d.cts +42 -0
- package/dist/scrollbar.d.ts +42 -0
- package/dist/scrollbar.js +213 -0
- package/dist/scrollbar.js.map +1 -0
- package/dist/search.cjs +70 -0
- package/dist/search.cjs.map +1 -0
- package/dist/search.d.cts +35 -0
- package/dist/search.d.ts +35 -0
- package/dist/search.js +58 -0
- package/dist/search.js.map +1 -0
- package/dist/selection.cjs +60 -0
- package/dist/selection.cjs.map +1 -0
- package/dist/selection.d.cts +12 -0
- package/dist/selection.d.ts +12 -0
- package/dist/selection.js +49 -0
- package/dist/selection.js.map +1 -0
- package/dist/shell.cjs +57 -0
- package/dist/shell.cjs.map +1 -0
- package/dist/shell.d.cts +27 -0
- package/dist/shell.d.ts +27 -0
- package/dist/shell.js +46 -0
- package/dist/shell.js.map +1 -0
- package/dist/stage.cjs +343 -0
- package/dist/stage.cjs.map +1 -0
- package/dist/stage.d.cts +104 -0
- package/dist/stage.d.ts +104 -0
- package/dist/stage.js +327 -0
- package/dist/stage.js.map +1 -0
- package/dist/stamp.cjs +76 -0
- package/dist/stamp.cjs.map +1 -0
- package/dist/stamp.d.cts +28 -0
- package/dist/stamp.d.ts +28 -0
- package/dist/stamp.js +63 -0
- package/dist/stamp.js.map +1 -0
- package/dist/toolbar.cjs +515 -0
- package/dist/toolbar.cjs.map +1 -0
- package/dist/toolbar.d.cts +100 -0
- package/dist/toolbar.d.ts +100 -0
- package/dist/toolbar.js +505 -0
- package/dist/toolbar.js.map +1 -0
- package/dist/views.cjs +35 -0
- package/dist/views.cjs.map +1 -0
- package/dist/views.d.cts +21 -0
- package/dist/views.d.ts +21 -0
- package/dist/views.js +26 -0
- package/dist/views.js.map +1 -0
- package/package.json +158 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_rolldown_runtime = require("./rolldown-runtime-DDXJien1.cjs");
|
|
3
|
+
const require_runtime = require("./runtime.cjs");
|
|
4
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
5
|
+
let react = require("react");
|
|
6
|
+
react = require_rolldown_runtime.__toESM(react, 1);
|
|
7
|
+
let _embedpdf_plugin_interaction = require("@embedpdf/plugin-interaction");
|
|
8
|
+
let _embedpdf_web = require("@embedpdf/web");
|
|
9
|
+
//#region src/interaction.tsx
|
|
10
|
+
const mods = (e) => ({
|
|
11
|
+
shift: e.shiftKey,
|
|
12
|
+
alt: e.altKey,
|
|
13
|
+
ctrl: e.ctrlKey,
|
|
14
|
+
meta: e.metaKey
|
|
15
|
+
});
|
|
16
|
+
/**
|
|
17
|
+
* Robust multi-click counter. `pointerdown.detail` is 0/1 in several browsers, so
|
|
18
|
+
* we count clicks ourselves from timing + proximity — the standard double/triple
|
|
19
|
+
* detection. Input normalization belongs in the adapter; the hub/handlers stay pure.
|
|
20
|
+
*/
|
|
21
|
+
function createClickCounter(maxGapMs = 400, maxDistPx = 6) {
|
|
22
|
+
let last = 0;
|
|
23
|
+
let lx = 0;
|
|
24
|
+
let ly = 0;
|
|
25
|
+
let count = 0;
|
|
26
|
+
return (now, x, y) => {
|
|
27
|
+
count = now - last <= maxGapMs && Math.hypot(x - lx, y - ly) <= maxDistPx ? count + 1 : 1;
|
|
28
|
+
last = now;
|
|
29
|
+
lx = x;
|
|
30
|
+
ly = y;
|
|
31
|
+
return count;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function PagePointerSource() {
|
|
35
|
+
const page = require_runtime.usePage();
|
|
36
|
+
const interaction = require_runtime.useCapability(_embedpdf_plugin_interaction.InteractionToken);
|
|
37
|
+
const cursor = require_runtime.useSelector(_embedpdf_plugin_interaction.InteractionToken, (c) => c.cursor());
|
|
38
|
+
const ref = (0, react.useRef)(null);
|
|
39
|
+
(0, react.useEffect)(() => {
|
|
40
|
+
const el = ref.current;
|
|
41
|
+
if (!el) return;
|
|
42
|
+
const clicks = createClickCounter();
|
|
43
|
+
const sample = (phase, e, clickCount = 1) => {
|
|
44
|
+
const r = el.getBoundingClientRect();
|
|
45
|
+
return {
|
|
46
|
+
phase,
|
|
47
|
+
viewport: {
|
|
48
|
+
x: e.clientX - r.left,
|
|
49
|
+
y: e.clientY - r.top
|
|
50
|
+
},
|
|
51
|
+
page: {
|
|
52
|
+
pon: page.pon,
|
|
53
|
+
point: page.toPagePoint(e.clientX, e.clientY),
|
|
54
|
+
scale: page.transform.viewScale,
|
|
55
|
+
rotation: page.transform.rotation,
|
|
56
|
+
zoom: page.transform.zoom
|
|
57
|
+
},
|
|
58
|
+
project: (pon) => pon === page.pon ? page.toPagePoint(e.clientX, e.clientY) : null,
|
|
59
|
+
modifiers: mods(e),
|
|
60
|
+
clickCount
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
let dragging = false;
|
|
64
|
+
const down = (e) => {
|
|
65
|
+
if (e.button !== 0) return;
|
|
66
|
+
dragging = true;
|
|
67
|
+
interaction.dispatch(sample("down", e, clicks(Date.now(), e.clientX, e.clientY)));
|
|
68
|
+
};
|
|
69
|
+
const hover = (e) => {
|
|
70
|
+
if (dragging) return;
|
|
71
|
+
interaction.dispatch(sample("move", e));
|
|
72
|
+
};
|
|
73
|
+
const drag = (e) => {
|
|
74
|
+
if (!dragging) return;
|
|
75
|
+
interaction.dispatch(sample("move", e));
|
|
76
|
+
};
|
|
77
|
+
const up = (e) => {
|
|
78
|
+
if (!dragging) return;
|
|
79
|
+
dragging = false;
|
|
80
|
+
interaction.dispatch(sample("up", e));
|
|
81
|
+
};
|
|
82
|
+
el.addEventListener("pointerdown", down);
|
|
83
|
+
el.addEventListener("pointermove", hover);
|
|
84
|
+
window.addEventListener("pointermove", drag);
|
|
85
|
+
window.addEventListener("pointerup", up);
|
|
86
|
+
return () => {
|
|
87
|
+
el.removeEventListener("pointerdown", down);
|
|
88
|
+
el.removeEventListener("pointermove", hover);
|
|
89
|
+
window.removeEventListener("pointermove", drag);
|
|
90
|
+
window.removeEventListener("pointerup", up);
|
|
91
|
+
};
|
|
92
|
+
}, [interaction, page]);
|
|
93
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
94
|
+
ref,
|
|
95
|
+
style: {
|
|
96
|
+
position: "absolute",
|
|
97
|
+
inset: 0,
|
|
98
|
+
cursor,
|
|
99
|
+
touchAction: "none"
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
/** Read + switch the active tool (for a toolbar). */
|
|
104
|
+
function useTool() {
|
|
105
|
+
const interaction = require_runtime.useCapability(_embedpdf_plugin_interaction.InteractionToken);
|
|
106
|
+
return {
|
|
107
|
+
activeToolId: require_runtime.useSelector(_embedpdf_plugin_interaction.InteractionToken, (c) => c.activeToolId()),
|
|
108
|
+
activate: interaction.activateTool,
|
|
109
|
+
tools: interaction.tools()
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
const toCursor = (img, fallback) => typeof img === "string" ? img : (0, _embedpdf_web.svgCursor)(img.fallback === void 0 && fallback ? {
|
|
113
|
+
...img,
|
|
114
|
+
fallback
|
|
115
|
+
} : img);
|
|
116
|
+
/**
|
|
117
|
+
* Give a tool IMAGE cursors — the armed-tool indicator. The cursor is the only
|
|
118
|
+
* zero-latency pointer-locked pixel the platform has, and the hub already
|
|
119
|
+
* arbitrates it: unmapped hover claims beat it over annotations/text, page
|
|
120
|
+
* gaps fall back to the tool's `gapCursor`, and other UI (menus, form fill
|
|
121
|
+
* controls) carries its own CSS cursor — so nothing chases the pointer in
|
|
122
|
+
* DOM. `null` installs nothing. A re-render with new content rebuilds the
|
|
123
|
+
* cursor (live recolor from tool defaults); unmount restores the tool's
|
|
124
|
+
* declared cursors.
|
|
125
|
+
*/
|
|
126
|
+
function useToolCursor(spec) {
|
|
127
|
+
const interaction = require_runtime.useCapability(_embedpdf_plugin_interaction.InteractionToken);
|
|
128
|
+
const key = spec && JSON.stringify(spec);
|
|
129
|
+
const ref = (0, react.useRef)(spec);
|
|
130
|
+
ref.current = spec;
|
|
131
|
+
(0, react.useEffect)(() => {
|
|
132
|
+
const s = ref.current;
|
|
133
|
+
if (!s) return;
|
|
134
|
+
interaction.setToolCursor(s.toolId, Object.fromEntries(Object.entries(s.cursors).map(([k, v]) => [k, toCursor(v, k)])));
|
|
135
|
+
return () => interaction.setToolCursor(s.toolId, null);
|
|
136
|
+
}, [interaction, key]);
|
|
137
|
+
}
|
|
138
|
+
//#endregion
|
|
139
|
+
exports.PagePointerSource = PagePointerSource;
|
|
140
|
+
exports.createClickCounter = createClickCounter;
|
|
141
|
+
exports.useTool = useTool;
|
|
142
|
+
exports.useToolCursor = useToolCursor;
|
|
143
|
+
Object.keys(_embedpdf_plugin_interaction).forEach(function(k) {
|
|
144
|
+
if (k !== "default" && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
145
|
+
enumerable: true,
|
|
146
|
+
get: function() {
|
|
147
|
+
return _embedpdf_plugin_interaction[k];
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
//# sourceMappingURL=interaction.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interaction.cjs","names":["usePage","useCapability","InteractionToken","useSelector"],"sources":["../src/interaction.tsx"],"sourcesContent":["/**\n * The React surface for @embedpdf/plugin-interaction.\n *\n * <PagePointerSource> is the ONE pointer listener per page: it converts events to\n * page space via PageContext.toPagePoint and forwards normalized samples to the\n * hub. It binds only to the page context, so it works identically inside a\n * virtualized <Stage> page and a standalone <PageView>. Features never attach\n * their own pointer listeners — they register handlers with the hub.\n */\n\n// One-line-per-feature: registration travels with the UI.\nexport * from '@embedpdf/plugin-interaction';\nimport * as React from 'react';\nimport { useEffect, useRef } from 'react';\nimport { InteractionToken } from '@embedpdf/plugin-interaction';\nimport type { Modifiers, PointerSample } from '@embedpdf/plugin-interaction';\nimport { svgCursor } from '@embedpdf/web';\nimport type { SvgCursorOptions } from '@embedpdf/web';\nimport { useCapability, usePage, useSelector } from './runtime';\n\nconst mods = (e: PointerEvent): Modifiers => ({\n shift: e.shiftKey,\n alt: e.altKey,\n ctrl: e.ctrlKey,\n meta: e.metaKey,\n});\n\n/**\n * Robust multi-click counter. `pointerdown.detail` is 0/1 in several browsers, so\n * we count clicks ourselves from timing + proximity — the standard double/triple\n * detection. Input normalization belongs in the adapter; the hub/handlers stay pure.\n */\nexport function createClickCounter(maxGapMs = 400, maxDistPx = 6) {\n let last = 0;\n let lx = 0;\n let ly = 0;\n let count = 0;\n return (now: number, x: number, y: number): number => {\n count = now - last <= maxGapMs && Math.hypot(x - lx, y - ly) <= maxDistPx ? count + 1 : 1;\n last = now;\n lx = x;\n ly = y;\n return count;\n };\n}\n\nexport function PagePointerSource() {\n const page = usePage();\n const interaction = useCapability(InteractionToken);\n const cursor = useSelector(InteractionToken, (c) => c.cursor());\n const ref = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n const clicks = createClickCounter();\n const sample = (\n phase: PointerSample['phase'],\n e: PointerEvent,\n clickCount = 1,\n ): PointerSample => {\n const r = el.getBoundingClientRect();\n return {\n phase,\n viewport: { x: e.clientX - r.left, y: e.clientY - r.top },\n // `scale`/`rotation`/`zoom` carry the same per-page environmental\n // context the Stage source resolves via `pageAt` — read off the page\n // transform so a standalone <PageView> drives handlers identically.\n page: {\n pon: page.pon,\n point: page.toPagePoint(e.clientX, e.clientY),\n scale: page.transform.viewScale,\n rotation: page.transform.rotation,\n zoom: page.transform.zoom,\n },\n // A per-page source can only project onto its OWN page — toPagePoint is\n // already unclamped (the drag listener lives on window), so a gesture\n // anchored here keeps tracking past the page bounds.\n project: (pon) => (pon === page.pon ? page.toPagePoint(e.clientX, e.clientY) : null),\n modifiers: mods(e),\n clickCount,\n };\n };\n let dragging = false;\n\n const down = (e: PointerEvent) => {\n if (e.button !== 0) return;\n dragging = true;\n interaction.dispatch(sample('down', e, clicks(Date.now(), e.clientX, e.clientY)));\n };\n // hover (no gesture): drive cursor feedback only — fires from the element\n const hover = (e: PointerEvent) => {\n if (dragging) return;\n interaction.dispatch(sample('move', e));\n };\n // active drag: track on window so it survives leaving the page bounds\n const drag = (e: PointerEvent) => {\n if (!dragging) return;\n interaction.dispatch(sample('move', e));\n };\n const up = (e: PointerEvent) => {\n if (!dragging) return;\n dragging = false;\n interaction.dispatch(sample('up', e));\n };\n\n el.addEventListener('pointerdown', down);\n el.addEventListener('pointermove', hover);\n window.addEventListener('pointermove', drag);\n window.addEventListener('pointerup', up);\n return () => {\n el.removeEventListener('pointerdown', down);\n el.removeEventListener('pointermove', hover);\n window.removeEventListener('pointermove', drag);\n window.removeEventListener('pointerup', up);\n };\n }, [interaction, page]);\n\n // Sits on top as the page's event surface; visual layers below use pointerEvents:none.\n return <div ref={ref} style={{ position: 'absolute', inset: 0, cursor, touchAction: 'none' }} />;\n}\n\n/** Read + switch the active tool (for a toolbar). */\nexport function useTool() {\n const interaction = useCapability(InteractionToken);\n const activeToolId = useSelector(InteractionToken, (c) => c.activeToolId());\n return {\n activeToolId,\n activate: interaction.activateTool,\n tools: interaction.tools(),\n };\n}\n\n/** One cursor slot: an SVG image ({@link SvgCursorOptions}) or a plain CSS\n * cursor string ('crosshair', 'url(…) 4 4, copy'). */\nexport type ToolCursorImage = SvgCursorOptions | string;\n\n/** What {@link useToolCursor} installs: the tool to reskin + its keyword map.\n * Apps build `svg` from the SAME icon their toolbar renders, so the cursor\n * and the button can never drift apart. */\nexport interface ToolCursorSpec {\n toolId: string;\n /** keyword → cursor: restyle the keywords this tool can show — its declared\n * base ('crosshair', 'copy') and hover claims ('text' over text) — in the\n * tool's identity. Keywords the map omits render as-is: a markup tool's\n * 'default' base stays the bare arrow, a foreign 'move' claim drops the\n * icon. An SVG value defaults its keyword `fallback` to the keyword it\n * replaces. */\n cursors: Record<string, ToolCursorImage>;\n}\n\nconst toCursor = (img: ToolCursorImage, fallback?: string): string =>\n typeof img === 'string'\n ? img\n : svgCursor(img.fallback === undefined && fallback ? { ...img, fallback } : img);\n\n/**\n * Give a tool IMAGE cursors — the armed-tool indicator. The cursor is the only\n * zero-latency pointer-locked pixel the platform has, and the hub already\n * arbitrates it: unmapped hover claims beat it over annotations/text, page\n * gaps fall back to the tool's `gapCursor`, and other UI (menus, form fill\n * controls) carries its own CSS cursor — so nothing chases the pointer in\n * DOM. `null` installs nothing. A re-render with new content rebuilds the\n * cursor (live recolor from tool defaults); unmount restores the tool's\n * declared cursors.\n */\nexport function useToolCursor(spec: ToolCursorSpec | null): void {\n const interaction = useCapability(InteractionToken);\n // Key the effect by VALUE: specs are built inline in render, and a\n // fresh-but-identical object must not thrash the skin.\n const key = spec && JSON.stringify(spec);\n const ref = useRef(spec);\n ref.current = spec;\n useEffect(() => {\n const s = ref.current;\n if (!s) return;\n interaction.setToolCursor(\n s.toolId,\n Object.fromEntries(Object.entries(s.cursors).map(([k, v]) => [k, toCursor(v, k)])),\n );\n return () => interaction.setToolCursor(s.toolId, null);\n }, [interaction, key]);\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { SvgCursorOptions } from "@embedpdf/web";
|
|
2
|
+
export * from "@embedpdf/plugin-interaction";
|
|
3
|
+
//#region src/interaction.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Robust multi-click counter. `pointerdown.detail` is 0/1 in several browsers, so
|
|
6
|
+
* we count clicks ourselves from timing + proximity — the standard double/triple
|
|
7
|
+
* detection. Input normalization belongs in the adapter; the hub/handlers stay pure.
|
|
8
|
+
*/
|
|
9
|
+
declare function createClickCounter(maxGapMs?: number, maxDistPx?: number): (now: number, x: number, y: number) => number;
|
|
10
|
+
declare function PagePointerSource(): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
/** Read + switch the active tool (for a toolbar). */
|
|
12
|
+
declare function useTool(): {
|
|
13
|
+
activeToolId: string;
|
|
14
|
+
activate: (id: import("@embedpdf/plugin-interaction").ToolId) => void;
|
|
15
|
+
tools: import("@embedpdf/plugin-interaction").Tool[];
|
|
16
|
+
};
|
|
17
|
+
/** One cursor slot: an SVG image ({@link SvgCursorOptions}) or a plain CSS
|
|
18
|
+
* cursor string ('crosshair', 'url(…) 4 4, copy'). */
|
|
19
|
+
type ToolCursorImage = SvgCursorOptions | string;
|
|
20
|
+
/** What {@link useToolCursor} installs: the tool to reskin + its keyword map.
|
|
21
|
+
* Apps build `svg` from the SAME icon their toolbar renders, so the cursor
|
|
22
|
+
* and the button can never drift apart. */
|
|
23
|
+
interface ToolCursorSpec {
|
|
24
|
+
toolId: string;
|
|
25
|
+
/** keyword → cursor: restyle the keywords this tool can show — its declared
|
|
26
|
+
* base ('crosshair', 'copy') and hover claims ('text' over text) — in the
|
|
27
|
+
* tool's identity. Keywords the map omits render as-is: a markup tool's
|
|
28
|
+
* 'default' base stays the bare arrow, a foreign 'move' claim drops the
|
|
29
|
+
* icon. An SVG value defaults its keyword `fallback` to the keyword it
|
|
30
|
+
* replaces. */
|
|
31
|
+
cursors: Record<string, ToolCursorImage>;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Give a tool IMAGE cursors — the armed-tool indicator. The cursor is the only
|
|
35
|
+
* zero-latency pointer-locked pixel the platform has, and the hub already
|
|
36
|
+
* arbitrates it: unmapped hover claims beat it over annotations/text, page
|
|
37
|
+
* gaps fall back to the tool's `gapCursor`, and other UI (menus, form fill
|
|
38
|
+
* controls) carries its own CSS cursor — so nothing chases the pointer in
|
|
39
|
+
* DOM. `null` installs nothing. A re-render with new content rebuilds the
|
|
40
|
+
* cursor (live recolor from tool defaults); unmount restores the tool's
|
|
41
|
+
* declared cursors.
|
|
42
|
+
*/
|
|
43
|
+
declare function useToolCursor(spec: ToolCursorSpec | null): void;
|
|
44
|
+
//#endregion
|
|
45
|
+
export { PagePointerSource, ToolCursorImage, ToolCursorSpec, createClickCounter, useTool, useToolCursor };
|
|
46
|
+
//# sourceMappingURL=interaction.d.cts.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { SvgCursorOptions } from "@embedpdf/web";
|
|
2
|
+
export * from "@embedpdf/plugin-interaction";
|
|
3
|
+
//#region src/interaction.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Robust multi-click counter. `pointerdown.detail` is 0/1 in several browsers, so
|
|
6
|
+
* we count clicks ourselves from timing + proximity — the standard double/triple
|
|
7
|
+
* detection. Input normalization belongs in the adapter; the hub/handlers stay pure.
|
|
8
|
+
*/
|
|
9
|
+
declare function createClickCounter(maxGapMs?: number, maxDistPx?: number): (now: number, x: number, y: number) => number;
|
|
10
|
+
declare function PagePointerSource(): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
/** Read + switch the active tool (for a toolbar). */
|
|
12
|
+
declare function useTool(): {
|
|
13
|
+
activeToolId: string;
|
|
14
|
+
activate: (id: import("@embedpdf/plugin-interaction").ToolId) => void;
|
|
15
|
+
tools: import("@embedpdf/plugin-interaction").Tool[];
|
|
16
|
+
};
|
|
17
|
+
/** One cursor slot: an SVG image ({@link SvgCursorOptions}) or a plain CSS
|
|
18
|
+
* cursor string ('crosshair', 'url(…) 4 4, copy'). */
|
|
19
|
+
type ToolCursorImage = SvgCursorOptions | string;
|
|
20
|
+
/** What {@link useToolCursor} installs: the tool to reskin + its keyword map.
|
|
21
|
+
* Apps build `svg` from the SAME icon their toolbar renders, so the cursor
|
|
22
|
+
* and the button can never drift apart. */
|
|
23
|
+
interface ToolCursorSpec {
|
|
24
|
+
toolId: string;
|
|
25
|
+
/** keyword → cursor: restyle the keywords this tool can show — its declared
|
|
26
|
+
* base ('crosshair', 'copy') and hover claims ('text' over text) — in the
|
|
27
|
+
* tool's identity. Keywords the map omits render as-is: a markup tool's
|
|
28
|
+
* 'default' base stays the bare arrow, a foreign 'move' claim drops the
|
|
29
|
+
* icon. An SVG value defaults its keyword `fallback` to the keyword it
|
|
30
|
+
* replaces. */
|
|
31
|
+
cursors: Record<string, ToolCursorImage>;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Give a tool IMAGE cursors — the armed-tool indicator. The cursor is the only
|
|
35
|
+
* zero-latency pointer-locked pixel the platform has, and the hub already
|
|
36
|
+
* arbitrates it: unmapped hover claims beat it over annotations/text, page
|
|
37
|
+
* gaps fall back to the tool's `gapCursor`, and other UI (menus, form fill
|
|
38
|
+
* controls) carries its own CSS cursor — so nothing chases the pointer in
|
|
39
|
+
* DOM. `null` installs nothing. A re-render with new content rebuilds the
|
|
40
|
+
* cursor (live recolor from tool defaults); unmount restores the tool's
|
|
41
|
+
* declared cursors.
|
|
42
|
+
*/
|
|
43
|
+
declare function useToolCursor(spec: ToolCursorSpec | null): void;
|
|
44
|
+
//#endregion
|
|
45
|
+
export { PagePointerSource, ToolCursorImage, ToolCursorSpec, createClickCounter, useTool, useToolCursor };
|
|
46
|
+
//# sourceMappingURL=interaction.d.ts.map
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { useCapability, usePage, useSelector } from "./runtime.js";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useRef } from "react";
|
|
4
|
+
import { InteractionToken } from "@embedpdf/plugin-interaction";
|
|
5
|
+
import { svgCursor } from "@embedpdf/web";
|
|
6
|
+
export * from "@embedpdf/plugin-interaction";
|
|
7
|
+
//#region src/interaction.tsx
|
|
8
|
+
const mods = (e) => ({
|
|
9
|
+
shift: e.shiftKey,
|
|
10
|
+
alt: e.altKey,
|
|
11
|
+
ctrl: e.ctrlKey,
|
|
12
|
+
meta: e.metaKey
|
|
13
|
+
});
|
|
14
|
+
/**
|
|
15
|
+
* Robust multi-click counter. `pointerdown.detail` is 0/1 in several browsers, so
|
|
16
|
+
* we count clicks ourselves from timing + proximity — the standard double/triple
|
|
17
|
+
* detection. Input normalization belongs in the adapter; the hub/handlers stay pure.
|
|
18
|
+
*/
|
|
19
|
+
function createClickCounter(maxGapMs = 400, maxDistPx = 6) {
|
|
20
|
+
let last = 0;
|
|
21
|
+
let lx = 0;
|
|
22
|
+
let ly = 0;
|
|
23
|
+
let count = 0;
|
|
24
|
+
return (now, x, y) => {
|
|
25
|
+
count = now - last <= maxGapMs && Math.hypot(x - lx, y - ly) <= maxDistPx ? count + 1 : 1;
|
|
26
|
+
last = now;
|
|
27
|
+
lx = x;
|
|
28
|
+
ly = y;
|
|
29
|
+
return count;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function PagePointerSource() {
|
|
33
|
+
const page = usePage();
|
|
34
|
+
const interaction = useCapability(InteractionToken);
|
|
35
|
+
const cursor = useSelector(InteractionToken, (c) => c.cursor());
|
|
36
|
+
const ref = useRef(null);
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
const el = ref.current;
|
|
39
|
+
if (!el) return;
|
|
40
|
+
const clicks = createClickCounter();
|
|
41
|
+
const sample = (phase, e, clickCount = 1) => {
|
|
42
|
+
const r = el.getBoundingClientRect();
|
|
43
|
+
return {
|
|
44
|
+
phase,
|
|
45
|
+
viewport: {
|
|
46
|
+
x: e.clientX - r.left,
|
|
47
|
+
y: e.clientY - r.top
|
|
48
|
+
},
|
|
49
|
+
page: {
|
|
50
|
+
pon: page.pon,
|
|
51
|
+
point: page.toPagePoint(e.clientX, e.clientY),
|
|
52
|
+
scale: page.transform.viewScale,
|
|
53
|
+
rotation: page.transform.rotation,
|
|
54
|
+
zoom: page.transform.zoom
|
|
55
|
+
},
|
|
56
|
+
project: (pon) => pon === page.pon ? page.toPagePoint(e.clientX, e.clientY) : null,
|
|
57
|
+
modifiers: mods(e),
|
|
58
|
+
clickCount
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
let dragging = false;
|
|
62
|
+
const down = (e) => {
|
|
63
|
+
if (e.button !== 0) return;
|
|
64
|
+
dragging = true;
|
|
65
|
+
interaction.dispatch(sample("down", e, clicks(Date.now(), e.clientX, e.clientY)));
|
|
66
|
+
};
|
|
67
|
+
const hover = (e) => {
|
|
68
|
+
if (dragging) return;
|
|
69
|
+
interaction.dispatch(sample("move", e));
|
|
70
|
+
};
|
|
71
|
+
const drag = (e) => {
|
|
72
|
+
if (!dragging) return;
|
|
73
|
+
interaction.dispatch(sample("move", e));
|
|
74
|
+
};
|
|
75
|
+
const up = (e) => {
|
|
76
|
+
if (!dragging) return;
|
|
77
|
+
dragging = false;
|
|
78
|
+
interaction.dispatch(sample("up", e));
|
|
79
|
+
};
|
|
80
|
+
el.addEventListener("pointerdown", down);
|
|
81
|
+
el.addEventListener("pointermove", hover);
|
|
82
|
+
window.addEventListener("pointermove", drag);
|
|
83
|
+
window.addEventListener("pointerup", up);
|
|
84
|
+
return () => {
|
|
85
|
+
el.removeEventListener("pointerdown", down);
|
|
86
|
+
el.removeEventListener("pointermove", hover);
|
|
87
|
+
window.removeEventListener("pointermove", drag);
|
|
88
|
+
window.removeEventListener("pointerup", up);
|
|
89
|
+
};
|
|
90
|
+
}, [interaction, page]);
|
|
91
|
+
return /* @__PURE__ */ jsx("div", {
|
|
92
|
+
ref,
|
|
93
|
+
style: {
|
|
94
|
+
position: "absolute",
|
|
95
|
+
inset: 0,
|
|
96
|
+
cursor,
|
|
97
|
+
touchAction: "none"
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
/** Read + switch the active tool (for a toolbar). */
|
|
102
|
+
function useTool() {
|
|
103
|
+
const interaction = useCapability(InteractionToken);
|
|
104
|
+
return {
|
|
105
|
+
activeToolId: useSelector(InteractionToken, (c) => c.activeToolId()),
|
|
106
|
+
activate: interaction.activateTool,
|
|
107
|
+
tools: interaction.tools()
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
const toCursor = (img, fallback) => typeof img === "string" ? img : svgCursor(img.fallback === void 0 && fallback ? {
|
|
111
|
+
...img,
|
|
112
|
+
fallback
|
|
113
|
+
} : img);
|
|
114
|
+
/**
|
|
115
|
+
* Give a tool IMAGE cursors — the armed-tool indicator. The cursor is the only
|
|
116
|
+
* zero-latency pointer-locked pixel the platform has, and the hub already
|
|
117
|
+
* arbitrates it: unmapped hover claims beat it over annotations/text, page
|
|
118
|
+
* gaps fall back to the tool's `gapCursor`, and other UI (menus, form fill
|
|
119
|
+
* controls) carries its own CSS cursor — so nothing chases the pointer in
|
|
120
|
+
* DOM. `null` installs nothing. A re-render with new content rebuilds the
|
|
121
|
+
* cursor (live recolor from tool defaults); unmount restores the tool's
|
|
122
|
+
* declared cursors.
|
|
123
|
+
*/
|
|
124
|
+
function useToolCursor(spec) {
|
|
125
|
+
const interaction = useCapability(InteractionToken);
|
|
126
|
+
const key = spec && JSON.stringify(spec);
|
|
127
|
+
const ref = useRef(spec);
|
|
128
|
+
ref.current = spec;
|
|
129
|
+
useEffect(() => {
|
|
130
|
+
const s = ref.current;
|
|
131
|
+
if (!s) return;
|
|
132
|
+
interaction.setToolCursor(s.toolId, Object.fromEntries(Object.entries(s.cursors).map(([k, v]) => [k, toCursor(v, k)])));
|
|
133
|
+
return () => interaction.setToolCursor(s.toolId, null);
|
|
134
|
+
}, [interaction, key]);
|
|
135
|
+
}
|
|
136
|
+
//#endregion
|
|
137
|
+
export { PagePointerSource, createClickCounter, useTool, useToolCursor };
|
|
138
|
+
|
|
139
|
+
//# sourceMappingURL=interaction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interaction.js","names":[],"sources":["../src/interaction.tsx"],"sourcesContent":["/**\n * The React surface for @embedpdf/plugin-interaction.\n *\n * <PagePointerSource> is the ONE pointer listener per page: it converts events to\n * page space via PageContext.toPagePoint and forwards normalized samples to the\n * hub. It binds only to the page context, so it works identically inside a\n * virtualized <Stage> page and a standalone <PageView>. Features never attach\n * their own pointer listeners — they register handlers with the hub.\n */\n\n// One-line-per-feature: registration travels with the UI.\nexport * from '@embedpdf/plugin-interaction';\nimport * as React from 'react';\nimport { useEffect, useRef } from 'react';\nimport { InteractionToken } from '@embedpdf/plugin-interaction';\nimport type { Modifiers, PointerSample } from '@embedpdf/plugin-interaction';\nimport { svgCursor } from '@embedpdf/web';\nimport type { SvgCursorOptions } from '@embedpdf/web';\nimport { useCapability, usePage, useSelector } from './runtime';\n\nconst mods = (e: PointerEvent): Modifiers => ({\n shift: e.shiftKey,\n alt: e.altKey,\n ctrl: e.ctrlKey,\n meta: e.metaKey,\n});\n\n/**\n * Robust multi-click counter. `pointerdown.detail` is 0/1 in several browsers, so\n * we count clicks ourselves from timing + proximity — the standard double/triple\n * detection. Input normalization belongs in the adapter; the hub/handlers stay pure.\n */\nexport function createClickCounter(maxGapMs = 400, maxDistPx = 6) {\n let last = 0;\n let lx = 0;\n let ly = 0;\n let count = 0;\n return (now: number, x: number, y: number): number => {\n count = now - last <= maxGapMs && Math.hypot(x - lx, y - ly) <= maxDistPx ? count + 1 : 1;\n last = now;\n lx = x;\n ly = y;\n return count;\n };\n}\n\nexport function PagePointerSource() {\n const page = usePage();\n const interaction = useCapability(InteractionToken);\n const cursor = useSelector(InteractionToken, (c) => c.cursor());\n const ref = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n const clicks = createClickCounter();\n const sample = (\n phase: PointerSample['phase'],\n e: PointerEvent,\n clickCount = 1,\n ): PointerSample => {\n const r = el.getBoundingClientRect();\n return {\n phase,\n viewport: { x: e.clientX - r.left, y: e.clientY - r.top },\n // `scale`/`rotation`/`zoom` carry the same per-page environmental\n // context the Stage source resolves via `pageAt` — read off the page\n // transform so a standalone <PageView> drives handlers identically.\n page: {\n pon: page.pon,\n point: page.toPagePoint(e.clientX, e.clientY),\n scale: page.transform.viewScale,\n rotation: page.transform.rotation,\n zoom: page.transform.zoom,\n },\n // A per-page source can only project onto its OWN page — toPagePoint is\n // already unclamped (the drag listener lives on window), so a gesture\n // anchored here keeps tracking past the page bounds.\n project: (pon) => (pon === page.pon ? page.toPagePoint(e.clientX, e.clientY) : null),\n modifiers: mods(e),\n clickCount,\n };\n };\n let dragging = false;\n\n const down = (e: PointerEvent) => {\n if (e.button !== 0) return;\n dragging = true;\n interaction.dispatch(sample('down', e, clicks(Date.now(), e.clientX, e.clientY)));\n };\n // hover (no gesture): drive cursor feedback only — fires from the element\n const hover = (e: PointerEvent) => {\n if (dragging) return;\n interaction.dispatch(sample('move', e));\n };\n // active drag: track on window so it survives leaving the page bounds\n const drag = (e: PointerEvent) => {\n if (!dragging) return;\n interaction.dispatch(sample('move', e));\n };\n const up = (e: PointerEvent) => {\n if (!dragging) return;\n dragging = false;\n interaction.dispatch(sample('up', e));\n };\n\n el.addEventListener('pointerdown', down);\n el.addEventListener('pointermove', hover);\n window.addEventListener('pointermove', drag);\n window.addEventListener('pointerup', up);\n return () => {\n el.removeEventListener('pointerdown', down);\n el.removeEventListener('pointermove', hover);\n window.removeEventListener('pointermove', drag);\n window.removeEventListener('pointerup', up);\n };\n }, [interaction, page]);\n\n // Sits on top as the page's event surface; visual layers below use pointerEvents:none.\n return <div ref={ref} style={{ position: 'absolute', inset: 0, cursor, touchAction: 'none' }} />;\n}\n\n/** Read + switch the active tool (for a toolbar). */\nexport function useTool() {\n const interaction = useCapability(InteractionToken);\n const activeToolId = useSelector(InteractionToken, (c) => c.activeToolId());\n return {\n activeToolId,\n activate: interaction.activateTool,\n tools: interaction.tools(),\n };\n}\n\n/** One cursor slot: an SVG image ({@link SvgCursorOptions}) or a plain CSS\n * cursor string ('crosshair', 'url(…) 4 4, copy'). */\nexport type ToolCursorImage = SvgCursorOptions | string;\n\n/** What {@link useToolCursor} installs: the tool to reskin + its keyword map.\n * Apps build `svg` from the SAME icon their toolbar renders, so the cursor\n * and the button can never drift apart. */\nexport interface ToolCursorSpec {\n toolId: string;\n /** keyword → cursor: restyle the keywords this tool can show — its declared\n * base ('crosshair', 'copy') and hover claims ('text' over text) — in the\n * tool's identity. Keywords the map omits render as-is: a markup tool's\n * 'default' base stays the bare arrow, a foreign 'move' claim drops the\n * icon. An SVG value defaults its keyword `fallback` to the keyword it\n * replaces. */\n cursors: Record<string, ToolCursorImage>;\n}\n\nconst toCursor = (img: ToolCursorImage, fallback?: string): string =>\n typeof img === 'string'\n ? img\n : svgCursor(img.fallback === undefined && fallback ? { ...img, fallback } : img);\n\n/**\n * Give a tool IMAGE cursors — the armed-tool indicator. The cursor is the only\n * zero-latency pointer-locked pixel the platform has, and the hub already\n * arbitrates it: unmapped hover claims beat it over annotations/text, page\n * gaps fall back to the tool's `gapCursor`, and other UI (menus, form fill\n * controls) carries its own CSS cursor — so nothing chases the pointer in\n * DOM. `null` installs nothing. A re-render with new content rebuilds the\n * cursor (live recolor from tool defaults); unmount restores the tool's\n * declared cursors.\n */\nexport function useToolCursor(spec: ToolCursorSpec | null): void {\n const interaction = useCapability(InteractionToken);\n // Key the effect by VALUE: specs are built inline in render, and a\n // fresh-but-identical object must not thrash the skin.\n const key = spec && JSON.stringify(spec);\n const ref = useRef(spec);\n ref.current = spec;\n useEffect(() => {\n const s = ref.current;\n if (!s) return;\n interaction.setToolCursor(\n s.toolId,\n Object.fromEntries(Object.entries(s.cursors).map(([k, v]) => [k, toCursor(v, k)])),\n );\n return () => interaction.setToolCursor(s.toolId, null);\n }, [interaction, key]);\n}\n"],"mappings":"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"}
|
package/dist/link.cjs
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_rolldown_runtime = require("./rolldown-runtime-DDXJien1.cjs");
|
|
3
|
+
const require_runtime = require("./runtime.cjs");
|
|
4
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
5
|
+
let react = require("react");
|
|
6
|
+
react = require_rolldown_runtime.__toESM(react, 1);
|
|
7
|
+
let _embedpdf_web = require("@embedpdf/web");
|
|
8
|
+
let _embedpdf_plugin_link = require("@embedpdf/plugin-link");
|
|
9
|
+
//#region src/link.tsx
|
|
10
|
+
/** Content rect → view px (the page wrapper's own space) — the same idiom as
|
|
11
|
+
* the annotation and form layers: never re-derive `x * scale`. */
|
|
12
|
+
function boxOf(item, page) {
|
|
13
|
+
const tl = page.transform.pageToContent({
|
|
14
|
+
x: item.rect.x,
|
|
15
|
+
y: item.rect.y
|
|
16
|
+
});
|
|
17
|
+
const br = page.transform.pageToContent({
|
|
18
|
+
x: item.rect.x + item.rect.width,
|
|
19
|
+
y: item.rect.y + item.rect.height
|
|
20
|
+
});
|
|
21
|
+
return {
|
|
22
|
+
left: tl.x,
|
|
23
|
+
top: tl.y,
|
|
24
|
+
width: br.x - tl.x,
|
|
25
|
+
height: br.y - tl.y
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/** A human label for the hover tooltip. Apps with i18n render their own via
|
|
29
|
+
* `renderLink`; this is the sensible default. */
|
|
30
|
+
function labelOf(item) {
|
|
31
|
+
switch (item.target.kind) {
|
|
32
|
+
case "uri": return item.target.uri;
|
|
33
|
+
case "goto": return "Go to destination";
|
|
34
|
+
case "named": return item.target.name;
|
|
35
|
+
default: return "Link";
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function LinkLayer({ renderLink } = {}) {
|
|
39
|
+
const page = require_runtime.usePage();
|
|
40
|
+
const link = require_runtime.useCapability(_embedpdf_plugin_link.LinkToken);
|
|
41
|
+
const items = require_runtime.useSelector(_embedpdf_plugin_link.LinkToken, (c) => c.linksOn(page.pon), require_runtime.shallowArray);
|
|
42
|
+
const engaged = require_runtime.useSelector(_embedpdf_plugin_link.LinkToken, (c) => c.engaged());
|
|
43
|
+
(0, react.useEffect)(() => {
|
|
44
|
+
link.ensurePage(page.pon);
|
|
45
|
+
}, [link, page.pon]);
|
|
46
|
+
if (!engaged || !items.length) return null;
|
|
47
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
48
|
+
style: {
|
|
49
|
+
position: "absolute",
|
|
50
|
+
inset: 0,
|
|
51
|
+
pointerEvents: "none"
|
|
52
|
+
},
|
|
53
|
+
children: items.map((item) => {
|
|
54
|
+
const box = boxOf(item, page);
|
|
55
|
+
const native = /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
56
|
+
href: (item.target.kind === "uri" ? (0, _embedpdf_web.sanitizeExternalUri)(item.target.uri) : null) ?? void 0,
|
|
57
|
+
target: "_blank",
|
|
58
|
+
rel: "noopener noreferrer",
|
|
59
|
+
role: "link",
|
|
60
|
+
tabIndex: 0,
|
|
61
|
+
title: labelOf(item),
|
|
62
|
+
"aria-label": labelOf(item),
|
|
63
|
+
onClick: (e) => {
|
|
64
|
+
if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
|
|
65
|
+
e.preventDefault();
|
|
66
|
+
link.activate(item.target);
|
|
67
|
+
},
|
|
68
|
+
onKeyDown: (e) => {
|
|
69
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
70
|
+
e.preventDefault();
|
|
71
|
+
link.activate(item.target);
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
onPointerDown: (e) => e.stopPropagation(),
|
|
75
|
+
style: {
|
|
76
|
+
position: "absolute",
|
|
77
|
+
left: box.left,
|
|
78
|
+
top: box.top,
|
|
79
|
+
width: box.width,
|
|
80
|
+
height: box.height,
|
|
81
|
+
cursor: "pointer",
|
|
82
|
+
pointerEvents: "auto"
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
const out = renderLink?.({
|
|
86
|
+
item,
|
|
87
|
+
nativeComponent: native
|
|
88
|
+
}) ?? native;
|
|
89
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.Fragment, { children: out }, item.id);
|
|
90
|
+
})
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
function useLink() {
|
|
94
|
+
return require_runtime.useCapability(_embedpdf_plugin_link.LinkToken);
|
|
95
|
+
}
|
|
96
|
+
//#endregion
|
|
97
|
+
exports.LinkLayer = LinkLayer;
|
|
98
|
+
exports.useLink = useLink;
|
|
99
|
+
Object.keys(_embedpdf_plugin_link).forEach(function(k) {
|
|
100
|
+
if (k !== "default" && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
101
|
+
enumerable: true,
|
|
102
|
+
get: function() {
|
|
103
|
+
return _embedpdf_plugin_link[k];
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
//# sourceMappingURL=link.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link.cjs","names":["usePage","useCapability","LinkToken","useSelector","shallowArray","React"],"sources":["../src/link.tsx"],"sourcesContent":["/**\n * The React view of @embedpdf/plugin-link — the navigation plane's paint.\n *\n * One absolutely-positioned <a> per clickable link area, real anchors on\n * purpose: URIs get an `href` (middle-click, copy-link, status-bar preview\n * and keyboard focus for free), internal destinations activate through the\n * plugin (stage reveal). The layer stands down entirely while the active\n * tool doesn't enable `link-nav` — the annotation plane owns links then\n * (select / move / resize / retarget through the selection editor).\n */\n\n// One-line-per-feature: registration travels with the UI.\nexport * from '@embedpdf/plugin-link';\nimport * as React from 'react';\nimport { useEffect } from 'react';\nimport { LinkToken, type LinkNavItem } from '@embedpdf/plugin-link';\nimport { sanitizeExternalUri } from '@embedpdf/web';\n\nimport { shallowArray, useCapability, usePage, useSelector } from './runtime';\nimport type { PageContextValue } from './runtime';\n\n/** Content rect → view px (the page wrapper's own space) — the same idiom as\n * the annotation and form layers: never re-derive `x * scale`. */\nfunction boxOf(item: LinkNavItem, page: PageContextValue) {\n const tl = page.transform.pageToContent({ x: item.rect.x, y: item.rect.y });\n const br = page.transform.pageToContent({\n x: item.rect.x + item.rect.width,\n y: item.rect.y + item.rect.height,\n });\n return { left: tl.x, top: tl.y, width: br.x - tl.x, height: br.y - tl.y };\n}\n\n/** A human label for the hover tooltip. Apps with i18n render their own via\n * `renderLink`; this is the sensible default. */\nfunction labelOf(item: LinkNavItem): string {\n switch (item.target.kind) {\n case 'uri':\n return item.target.uri;\n case 'goto':\n return 'Go to destination';\n case 'named':\n return item.target.name;\n default:\n return 'Link';\n }\n}\n\nexport interface LinkLayerProps {\n /** Wrap or replace a link's native anchor (badging, custom tooltips). */\n renderLink?: (args: {\n item: LinkNavItem;\n nativeComponent: React.ReactNode;\n }) => React.ReactNode | undefined;\n}\n\nexport function LinkLayer({ renderLink }: LinkLayerProps = {}) {\n const page = usePage();\n const link = useCapability(LinkToken);\n const items = useSelector(LinkToken, (c) => c.linksOn(page.pon), shallowArray);\n const engaged = useSelector(LinkToken, (c) => c.engaged());\n\n useEffect(() => {\n link.ensurePage(page.pon);\n }, [link, page.pon]);\n\n // An authoring tool is active → the annotation plane owns links (they're\n // plain editable rects there); no nav anchors, no swallowed pointer events.\n if (!engaged || !items.length) return null;\n\n return (\n <div style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}>\n {items.map((item) => {\n const box = boxOf(item, page);\n // Real href ONLY for sanitized external URIs; blocked schemes and\n // internal targets activate through the plugin instead.\n const href = item.target.kind === 'uri' ? sanitizeExternalUri(item.target.uri) : null;\n const native = (\n <a\n href={href ?? undefined}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n role=\"link\"\n tabIndex={0}\n title={labelOf(item)}\n aria-label={labelOf(item)}\n onClick={(e) => {\n // Plain left-click activates through the plugin (stage reveal /\n // policy / analytics). Modified clicks and middle-clicks on a\n // real href keep their native browser behaviour.\n if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;\n e.preventDefault();\n link.activate(item.target);\n }}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n link.activate(item.target);\n }\n }}\n // Keep the hub out of it: a down inside the anchor must not reach\n // the Stage's native listener (same isolation idiom as FreeText).\n onPointerDown={(e) => e.stopPropagation()}\n style={{\n position: 'absolute',\n left: box.left,\n top: box.top,\n width: box.width,\n height: box.height,\n cursor: 'pointer',\n pointerEvents: 'auto',\n }}\n />\n );\n const out = renderLink?.({ item, nativeComponent: native }) ?? native;\n return <React.Fragment key={item.id}>{out}</React.Fragment>;\n })}\n </div>\n );\n}\n\nexport function useLink() {\n return useCapability(LinkToken);\n}\n"],"mappings":";;;;;;;;;;;AAuBA,SAAS,MAAM,MAAmB,MAAwB;CACxD,MAAM,KAAK,KAAK,UAAU,cAAc;EAAE,GAAG,KAAK,KAAK;EAAG,GAAG,KAAK,KAAK;CAAE,CAAC;CAC1E,MAAM,KAAK,KAAK,UAAU,cAAc;EACtC,GAAG,KAAK,KAAK,IAAI,KAAK,KAAK;EAC3B,GAAG,KAAK,KAAK,IAAI,KAAK,KAAK;CAC7B,CAAC;CACD,OAAO;EAAE,MAAM,GAAG;EAAG,KAAK,GAAG;EAAG,OAAO,GAAG,IAAI,GAAG;EAAG,QAAQ,GAAG,IAAI,GAAG;CAAE;AAC1E;;;AAIA,SAAS,QAAQ,MAA2B;CAC1C,QAAQ,KAAK,OAAO,MAApB;EACE,KAAK,OACH,OAAO,KAAK,OAAO;EACrB,KAAK,QACH,OAAO;EACT,KAAK,SACH,OAAO,KAAK,OAAO;EACrB,SACE,OAAO;CACX;AACF;AAUA,SAAgB,UAAU,EAAE,eAA+B,CAAC,GAAG;CAC7D,MAAM,OAAOA,gBAAAA,QAAQ;CACrB,MAAM,OAAOC,gBAAAA,cAAcC,sBAAAA,SAAS;CACpC,MAAM,QAAQC,gBAAAA,YAAYD,sBAAAA,YAAY,MAAM,EAAE,QAAQ,KAAK,GAAG,GAAGE,gBAAAA,YAAY;CAC7E,MAAM,UAAUD,gBAAAA,YAAYD,sBAAAA,YAAY,MAAM,EAAE,QAAQ,CAAC;CAEzD,CAAA,GAAA,MAAA,UAAA,OAAgB;EACd,KAAK,WAAW,KAAK,GAAG;CAC1B,GAAG,CAAC,MAAM,KAAK,GAAG,CAAC;CAInB,IAAI,CAAC,WAAW,CAAC,MAAM,QAAQ,OAAO;CAEtC,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;EAAK,OAAO;GAAE,UAAU;GAAY,OAAO;GAAG,eAAe;EAAO;EACjE,UAAA,MAAM,KAAK,SAAS;GACnB,MAAM,MAAM,MAAM,MAAM,IAAI;GAI5B,MAAM,SACJ,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;IACE,OAHS,KAAK,OAAO,SAAS,SAAA,GAAA,cAAA,oBAAA,CAA4B,KAAK,OAAO,GAAG,IAAI,SAG/D,KAAA;IACd,QAAO;IACP,KAAI;IACJ,MAAK;IACL,UAAU;IACV,OAAO,QAAQ,IAAI;IACnB,cAAY,QAAQ,IAAI;IACxB,UAAU,MAAM;KAId,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ;KACtD,EAAE,eAAe;KACjB,KAAK,SAAS,KAAK,MAAM;IAC3B;IACA,YAAY,MAAM;KAChB,IAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,KAAK;MACtC,EAAE,eAAe;MACjB,KAAK,SAAS,KAAK,MAAM;KAC3B;IACF;IAGA,gBAAgB,MAAM,EAAE,gBAAgB;IACxC,OAAO;KACL,UAAU;KACV,MAAM,IAAI;KACV,KAAK,IAAI;KACT,OAAO,IAAI;KACX,QAAQ,IAAI;KACZ,QAAQ;KACR,eAAe;IACjB;GACD,CAAA;GAEH,MAAM,MAAM,aAAa;IAAE;IAAM,iBAAiB;GAAO,CAAC,KAAK;GAC/D,OAAO,iBAAA,GAAA,kBAAA,IAAA,CAACG,MAAM,UAAP,EAAA,UAA+B,IAAoB,GAA9B,KAAK,EAAyB;EAC5D,CAAC;CACE,CAAA;AAET;AAEA,SAAgB,UAAU;CACxB,OAAOJ,gBAAAA,cAAcC,sBAAAA,SAAS;AAChC"}
|
package/dist/link.d.cts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { LinkNavItem } from "@embedpdf/plugin-link";
|
|
3
|
+
export * from "@embedpdf/plugin-link";
|
|
4
|
+
//#region src/link.d.ts
|
|
5
|
+
interface LinkLayerProps {
|
|
6
|
+
/** Wrap or replace a link's native anchor (badging, custom tooltips). */
|
|
7
|
+
renderLink?: (args: {
|
|
8
|
+
item: LinkNavItem;
|
|
9
|
+
nativeComponent: React.ReactNode;
|
|
10
|
+
}) => React.ReactNode | undefined;
|
|
11
|
+
}
|
|
12
|
+
declare function LinkLayer({ renderLink }?: LinkLayerProps): import("react/jsx-runtime").JSX.Element | null;
|
|
13
|
+
declare function useLink(): import("@embedpdf/plugin-link").LinkCapability;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { LinkLayer, LinkLayerProps, useLink };
|
|
16
|
+
//# sourceMappingURL=link.d.cts.map
|
package/dist/link.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { LinkNavItem } from "@embedpdf/plugin-link";
|
|
3
|
+
export * from "@embedpdf/plugin-link";
|
|
4
|
+
//#region src/link.d.ts
|
|
5
|
+
interface LinkLayerProps {
|
|
6
|
+
/** Wrap or replace a link's native anchor (badging, custom tooltips). */
|
|
7
|
+
renderLink?: (args: {
|
|
8
|
+
item: LinkNavItem;
|
|
9
|
+
nativeComponent: React.ReactNode;
|
|
10
|
+
}) => React.ReactNode | undefined;
|
|
11
|
+
}
|
|
12
|
+
declare function LinkLayer({ renderLink }?: LinkLayerProps): import("react/jsx-runtime").JSX.Element | null;
|
|
13
|
+
declare function useLink(): import("@embedpdf/plugin-link").LinkCapability;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { LinkLayer, LinkLayerProps, useLink };
|
|
16
|
+
//# sourceMappingURL=link.d.ts.map
|