@embedpdf/react 3.0.0-next.0 → 3.0.0-next.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AnnotationListSnapshot-DDxHpJQU-BG32YXxG.d.cts +347 -0
- package/dist/AnnotationListSnapshot-DDxHpJQU-BG32YXxG.d.ts +347 -0
- package/dist/anchored.cjs +89 -0
- package/dist/anchored.cjs.map +1 -0
- package/dist/anchored.d.cts +45 -0
- package/dist/anchored.d.ts +45 -0
- package/dist/anchored.js +60 -0
- package/dist/anchored.js.map +1 -0
- package/dist/annotation-menu.cjs +33 -84
- package/dist/annotation-menu.cjs.map +1 -1
- package/dist/annotation-menu.d.cts +28 -2
- package/dist/annotation-menu.d.ts +28 -2
- package/dist/annotation-menu.js +27 -78
- package/dist/annotation-menu.js.map +1 -1
- package/dist/annotation.cjs +752 -16
- package/dist/annotation.cjs.map +1 -0
- package/dist/annotation.d.cts +159 -2
- package/dist/annotation.d.ts +159 -2
- package/dist/annotation.js +737 -3
- package/dist/annotation.js.map +1 -0
- package/dist/commands.js +1 -0
- package/dist/commands.js.map +1 -1
- package/dist/form.cjs +3 -3
- package/dist/form.cjs.map +1 -1
- package/dist/form.d.cts +2 -2
- package/dist/form.d.ts +2 -2
- package/dist/form.js +3 -3
- package/dist/form.js.map +1 -1
- package/dist/index.cjs +34 -5
- package/dist/index.d.cts +14 -8
- package/dist/index.d.ts +14 -8
- package/dist/index.js +8 -6
- package/dist/interaction.cjs +17 -4
- package/dist/interaction.cjs.map +1 -1
- package/dist/interaction.d.cts +2 -2
- package/dist/interaction.d.ts +2 -2
- package/dist/interaction.js +7 -5
- package/dist/interaction.js.map +1 -1
- package/dist/link.cjs +29 -6
- package/dist/link.cjs.map +1 -1
- package/dist/link.d.cts +12 -3
- package/dist/link.d.ts +12 -3
- package/dist/link.js +31 -8
- package/dist/link.js.map +1 -1
- package/dist/metadata.js +1 -0
- package/dist/metadata.js.map +1 -1
- package/dist/page-edit.js +1 -0
- package/dist/page-edit.js.map +1 -1
- package/dist/page-view.cjs +62 -38
- package/dist/page-view.cjs.map +1 -1
- package/dist/page-view.js +59 -37
- package/dist/page-view.js.map +1 -1
- package/dist/redaction.js +1 -0
- package/dist/redaction.js.map +1 -1
- package/dist/render.cjs +67 -50
- package/dist/render.cjs.map +1 -1
- package/dist/render.d.cts +6 -19
- package/dist/render.d.ts +6 -19
- package/dist/render.js +71 -52
- package/dist/render.js.map +1 -1
- package/dist/rolldown-runtime-BBjsoOtd.js +27 -0
- package/dist/{runtime-DV98q7I0.d.ts → runtime-Cvi0wQ4d.d.cts} +23 -10
- package/dist/{runtime-CMxIuF2O.d.cts → runtime-DXxr8T4L.d.ts} +23 -10
- package/dist/runtime.cjs +6 -5
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.d.cts +1 -1
- package/dist/runtime.d.ts +1 -1
- package/dist/runtime.js +7 -5
- package/dist/runtime.js.map +1 -1
- package/dist/search.cjs +36 -19
- package/dist/search.cjs.map +1 -1
- package/dist/search.d.cts +1 -1
- package/dist/search.d.ts +1 -1
- package/dist/search.js +36 -19
- package/dist/search.js.map +1 -1
- package/dist/selection.cjs +217 -22
- package/dist/selection.cjs.map +1 -1
- package/dist/selection.d.cts +60 -2
- package/dist/selection.d.ts +60 -2
- package/dist/selection.js +214 -27
- package/dist/selection.js.map +1 -1
- package/dist/shell.js +1 -0
- package/dist/shell.js.map +1 -1
- package/dist/stage.cjs +47 -146
- package/dist/stage.cjs.map +1 -1
- package/dist/stage.d.cts +18 -5
- package/dist/stage.d.ts +18 -5
- package/dist/stage.js +50 -148
- package/dist/stage.js.map +1 -1
- package/dist/stamp.js +1 -0
- package/dist/stamp.js.map +1 -1
- package/dist/toolbar.js +1 -0
- package/dist/toolbar.js.map +1 -1
- package/dist/views.js +1 -0
- package/dist/views.js.map +1 -1
- package/package.json +27 -26
- package/dist/AnnotationListSnapshot-W0RCsyDE-dWna4TTJ.d.cts +0 -2593
- package/dist/AnnotationListSnapshot-W0RCsyDE-dWna4TTJ.d.ts +0 -2593
- package/dist/annotation-B6jwfTCh.d.cts +0 -173
- package/dist/annotation-Ct6dFtzJ.cjs +0 -913
- package/dist/annotation-Ct6dFtzJ.cjs.map +0 -1
- package/dist/annotation-DjGWJljY.js +0 -829
- package/dist/annotation-DjGWJljY.js.map +0 -1
- package/dist/annotation-nUMefyjw.d.ts +0 -173
package/dist/search.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.js","names":[],"sources":["../src/search.tsx"],"sourcesContent":["/**\n * The React surface for @embedpdf/plugin-search.\n *\n * <SearchLayer> is a dumb renderer, the SelectionLayer's twin: it reads\n * the page's content-space hit rects from the capability and paints them\n * through PageContext.
|
|
1
|
+
{"version":3,"file":"search.js","names":[],"sources":["../src/search.tsx"],"sourcesContent":["/**\n * The React surface for @embedpdf/plugin-search.\n *\n * <SearchLayer> is a dumb renderer, the SelectionLayer's twin: it reads\n * the page's content-space hit rects from the capability and paints them\n * through PageContext.toPixels. The active hit gets its own colour.\n * No pointer handling, no engine calls — search is driven from app chrome\n * via useSearch().\n */\n\n// One-line-per-feature: registration travels with the UI.\nexport * from '@embedpdf/plugin-search';\nimport * as React from 'react';\nimport { SearchToken } from '@embedpdf/plugin-search';\nimport type { SearchHit } from '@embedpdf/plugin-search';\nimport { shallowArray, useCapability, usePage, useSelector } from './runtime';\n\nexport interface SearchLayerProps {\n /** Highlight colour for hits — SOLID (default: highlighter yellow). */\n color?: string;\n /** Highlight colour for the ACTIVE hit — SOLID (default: orange). */\n activeColor?: string;\n /**\n * How the highlight composites with the page. `'multiply'` (default) is\n * the real-highlighter look: text stays crisp black through the colour,\n * only the paper tints. Pass `'normal'` (with translucent colours) for\n * dark/scanned documents where multiply-on-dark would vanish.\n */\n blendMode?: React.CSSProperties['mixBlendMode'];\n}\n\nexport function SearchLayer({\n color = '#ffd500',\n activeColor = '#ff9632',\n blendMode = 'multiply',\n}: SearchLayerProps) {\n const page = usePage();\n const hits = useSelector(SearchToken, (c) => c.hitsForPage(page.pon), shallowArray);\n const active = useSelector(SearchToken, (c) => c.activeHit());\n\n if (hits.length === 0) return null;\n\n return (\n <div style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}>\n {hits.map((hit: SearchHit) =>\n hit.segments.map(({ quad: q }, i) => {\n const fill = hit === active ? activeColor : color;\n // Upright hits keep the classic rounded div (pixel-identical to the\n // pre-orientation layer); rotated hits draw their true oriented cell.\n const upright =\n q.upperStart.y === q.upperEnd.y &&\n q.lowerStart.y === q.lowerEnd.y &&\n q.upperStart.x === q.lowerStart.x;\n if (upright) {\n const tl = page.transform.toPixels(q.upperStart);\n const br = page.transform.toPixels(q.lowerEnd);\n return (\n <div\n key={`${hit.charStart}:${i}`}\n style={{\n position: 'absolute',\n left: tl.x,\n top: tl.y,\n width: br.x - tl.x,\n height: br.y - tl.y,\n background: fill,\n mixBlendMode: blendMode,\n borderRadius: 2,\n }}\n />\n );\n }\n const ring = [q.upperStart, q.upperEnd, q.lowerEnd, q.lowerStart].map((p) =>\n page.transform.toPixels(p),\n );\n return (\n <svg\n key={`${hit.charStart}:${i}`}\n style={{\n position: 'absolute',\n inset: 0,\n width: '100%',\n height: '100%',\n overflow: 'visible',\n mixBlendMode: blendMode,\n }}\n >\n <polygon points={ring.map((p) => `${p.x},${p.y}`).join(' ')} fill={fill} />\n </svg>\n );\n }),\n )}\n </div>\n );\n}\n\n/** The search capability (search/clear/next/prev/…) for app chrome. */\nexport function useSearch() {\n return useCapability(SearchToken);\n}\n\n/** Reactive search read-model for chrome: the query, status, counts, progress. */\nexport function useSearchState() {\n const query = useSelector(SearchToken, (c) => c.query());\n const status = useSelector(SearchToken, (c) => c.status());\n const hitCount = useSelector(SearchToken, (c) => c.hitCount());\n const activeIndex = useSelector(SearchToken, (c) => c.activeIndex());\n const progress = useSelector(\n SearchToken,\n (c) => c.progress(),\n (a, b) => a.scanned === b.scanned && a.total === b.total,\n );\n const error = useSelector(SearchToken, (c) => c.errorMessage());\n return { query, status, hitCount, activeIndex, progress, error };\n}\n"],"mappings":";;;;;;AA+BA,SAAgB,YAAY,EAC1B,QAAQ,WACR,cAAc,WACd,YAAY,cACO;CACnB,MAAM,OAAO,QAAQ;CACrB,MAAM,OAAO,YAAY,cAAc,MAAM,EAAE,YAAY,KAAK,GAAG,GAAG,YAAY;CAClF,MAAM,SAAS,YAAY,cAAc,MAAM,EAAE,UAAU,CAAC;CAE5D,IAAI,KAAK,WAAW,GAAG,OAAO;CAE9B,OACE,oBAAC,OAAD;EAAK,OAAO;GAAE,UAAU;GAAY,OAAO;GAAG,eAAe;EAAO;EACjE,UAAA,KAAK,KAAK,QACT,IAAI,SAAS,KAAK,EAAE,MAAM,KAAK,MAAM;GACnC,MAAM,OAAO,QAAQ,SAAS,cAAc;GAO5C,IAHE,EAAE,WAAW,MAAM,EAAE,SAAS,KAC9B,EAAE,WAAW,MAAM,EAAE,SAAS,KAC9B,EAAE,WAAW,MAAM,EAAE,WAAW,GACrB;IACX,MAAM,KAAK,KAAK,UAAU,SAAS,EAAE,UAAU;IAC/C,MAAM,KAAK,KAAK,UAAU,SAAS,EAAE,QAAQ;IAC7C,OACE,oBAAC,OAAD,EAEE,OAAO;KACL,UAAU;KACV,MAAM,GAAG;KACT,KAAK,GAAG;KACR,OAAO,GAAG,IAAI,GAAG;KACjB,QAAQ,GAAG,IAAI,GAAG;KAClB,YAAY;KACZ,cAAc;KACd,cAAc;IAChB,EACD,GAXM,GAAG,IAAI,UAAU,GAAG,GAW1B;GAEL;GACA,MAAM,OAAO;IAAC,EAAE;IAAY,EAAE;IAAU,EAAE;IAAU,EAAE;GAAU,CAAC,CAAC,KAAK,MACrE,KAAK,UAAU,SAAS,CAAC,CAC3B;GACA,OACE,oBAAC,OAAD;IAEE,OAAO;KACL,UAAU;KACV,OAAO;KACP,OAAO;KACP,QAAQ;KACR,UAAU;KACV,cAAc;IAChB;IAEA,UAAA,oBAAC,WAAD;KAAS,QAAQ,KAAK,KAAK,MAAM,GAAG,EAAE,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,KAAK,GAAG;KAAS;IAAO,CAAA;GACvE,GAXE,GAAG,IAAI,UAAU,GAAG,GAWtB;EAET,CAAC,CACH;CACG,CAAA;AAET;;AAGA,SAAgB,YAAY;CAC1B,OAAO,cAAc,WAAW;AAClC;;AAGA,SAAgB,iBAAiB;CAW/B,OAAO;EAAE,OAVK,YAAY,cAAc,MAAM,EAAE,MAAM,CAUzC;EAAG,QATD,YAAY,cAAc,MAAM,EAAE,OAAO,CASnC;EAAG,UARP,YAAY,cAAc,MAAM,EAAE,SAAS,CAQ7B;EAAG,aAPd,YAAY,cAAc,MAAM,EAAE,YAAY,CAOtB;EAAG,UAN9B,YACf,cACC,MAAM,EAAE,SAAS,IACjB,GAAG,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,KAGC;EAAG,OAD3C,YAAY,cAAc,MAAM,EAAE,aAAa,CACA;CAAE;AACjE"}
|
package/dist/selection.cjs
CHANGED
|
@@ -1,52 +1,247 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_rolldown_runtime = require("./rolldown-runtime-DDXJien1.cjs");
|
|
3
3
|
const require_runtime = require("./runtime.cjs");
|
|
4
|
+
const require_anchored = require("./anchored.cjs");
|
|
4
5
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
5
6
|
let react = require("react");
|
|
6
7
|
react = require_rolldown_runtime.__toESM(react, 1);
|
|
8
|
+
let _embedpdf_web = require("@embedpdf/web");
|
|
9
|
+
let _embedpdf_plugin_stage = require("@embedpdf/plugin-stage");
|
|
10
|
+
let _embedpdf_core_geometry = require("@embedpdf/core-geometry");
|
|
7
11
|
let _embedpdf_plugin_selection = require("@embedpdf/plugin-selection");
|
|
12
|
+
let _embedpdf_plugin_selection_internal = require("@embedpdf/plugin-selection/internal");
|
|
8
13
|
//#region src/selection.tsx
|
|
9
14
|
function SelectionLayer({ color = "rgba(33, 150, 243, 0.35)" }) {
|
|
10
15
|
const page = require_runtime.usePage();
|
|
11
|
-
const selection = require_runtime.useCapability(
|
|
12
|
-
const
|
|
13
|
-
const visible = require_runtime.useSelector(
|
|
16
|
+
const selection = require_runtime.useCapability(_embedpdf_plugin_selection_internal.SelectionToken);
|
|
17
|
+
const segments = require_runtime.useSelector(_embedpdf_plugin_selection_internal.SelectionToken, (c) => c.segmentsForPage(page.pon), require_runtime.shallowArray);
|
|
18
|
+
const visible = require_runtime.useSelector(_embedpdf_plugin_selection_internal.SelectionToken, (c) => c.highlightVisible());
|
|
14
19
|
(0, react.useEffect)(() => {
|
|
15
20
|
selection.ensurePage(page.pon);
|
|
16
21
|
}, [selection, page.pon]);
|
|
17
22
|
if (!visible) return null;
|
|
18
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
23
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
19
24
|
style: {
|
|
20
25
|
position: "absolute",
|
|
21
26
|
inset: 0,
|
|
27
|
+
width: "100%",
|
|
28
|
+
height: "100%",
|
|
29
|
+
overflow: "visible",
|
|
22
30
|
pointerEvents: "none"
|
|
23
31
|
},
|
|
24
|
-
children:
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
position: "absolute",
|
|
35
|
-
left: tl.x,
|
|
36
|
-
top: tl.y,
|
|
37
|
-
width: br.x - tl.x,
|
|
38
|
-
height: br.y - tl.y,
|
|
39
|
-
background: color
|
|
40
|
-
} }, i);
|
|
32
|
+
children: segments.map((s, i) => {
|
|
33
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("polygon", {
|
|
34
|
+
points: [
|
|
35
|
+
s.quad.upperStart,
|
|
36
|
+
s.quad.upperEnd,
|
|
37
|
+
s.quad.lowerEnd,
|
|
38
|
+
s.quad.lowerStart
|
|
39
|
+
].map((p) => page.transform.toPixels(p)).map((p) => `${p.x},${p.y}`).join(" "),
|
|
40
|
+
fill: color
|
|
41
|
+
}, i);
|
|
41
42
|
})
|
|
42
43
|
});
|
|
43
44
|
}
|
|
44
|
-
/** The selection capability (
|
|
45
|
+
/** The PUBLIC selection capability (select(), readText(), canCopy(), …) for
|
|
46
|
+
* app chrome — toolbars, context menus, automation. */
|
|
45
47
|
function useSelection() {
|
|
46
48
|
return require_runtime.useCapability(_embedpdf_plugin_selection.SelectionToken);
|
|
47
49
|
}
|
|
50
|
+
/** Structural equality for the selection's menu anchor — keeps the menu from
|
|
51
|
+
* re-rendering on unrelated dispatches (the capability returns a fresh
|
|
52
|
+
* object each call). */
|
|
53
|
+
const sameMenuAnchor = (a, b) => {
|
|
54
|
+
if (a === b) return true;
|
|
55
|
+
if (!a || !b) return false;
|
|
56
|
+
return a.pon === b.pon && a.bounds.x === b.bounds.x && a.bounds.y === b.bounds.y && a.bounds.width === b.bounds.width && a.bounds.height === b.bounds.height;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Floats over the current TEXT selection (one anchor regardless of
|
|
60
|
+
* cross-page selection; it rides the gesture's end page) — and only once the
|
|
61
|
+
* selection SETTLES: hidden while `isSelecting()` (mid-drag), it appears at
|
|
62
|
+
* pointer-up; programmatic selections show immediately (born settled). Works
|
|
63
|
+
* under `<Stage>` (mount in the overlay slot) and `<PageView>` alike — the
|
|
64
|
+
* surface provides the projection. Compose the contents from hooks:
|
|
65
|
+
* `useSelection()` for copy/clear, `useAnnotation()` for
|
|
66
|
+
* `markupFromSelection('highlight')`, `copySelection` for the clipboard.
|
|
67
|
+
* For live-follow UI during the drag, compose `<Anchored>` with
|
|
68
|
+
* `menuAnchor()` yourself — the primitive carries no policy.
|
|
69
|
+
*/
|
|
70
|
+
function SelectionMenu({ children, gap = 8, placement = "top" }) {
|
|
71
|
+
const selecting = require_runtime.useSelector(_embedpdf_plugin_selection.SelectionToken, (c) => c.isSelecting());
|
|
72
|
+
const anchor = require_runtime.useSelector(_embedpdf_plugin_selection.SelectionToken, (c) => c.menuAnchor(), sameMenuAnchor);
|
|
73
|
+
if (selecting || !anchor) return null;
|
|
74
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_anchored.Anchored, {
|
|
75
|
+
anchor,
|
|
76
|
+
placement,
|
|
77
|
+
gap,
|
|
78
|
+
children
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
const sameEndpoints = (a, b) => {
|
|
82
|
+
if (a === b) return true;
|
|
83
|
+
if (!a || !b) return false;
|
|
84
|
+
return a.start.pon === b.start.pon && a.end.pon === b.end.pon && a.start.advance === b.start.advance && a.end.advance === b.end.advance && (0, _embedpdf_core_geometry.textQuadEquals)(a.start.glyphQuad, b.start.glyphQuad) && (0, _embedpdf_core_geometry.textQuadEquals)(a.end.glyphQuad, b.end.glyphQuad);
|
|
85
|
+
};
|
|
86
|
+
/** The plugin's structural view over this Stage: point-exact projection in,
|
|
87
|
+
* page resolution out. (`pageRectToScreen` is the AABB projector — upright
|
|
88
|
+
* overlays only — and would collapse exactly the orientation handles need.) */
|
|
89
|
+
const handleView = (stage) => ({
|
|
90
|
+
toOverlay: (pon, pt) => {
|
|
91
|
+
const world = stage.pageToWorld(pon, pt);
|
|
92
|
+
return world ? stage.toScreen(world) : null;
|
|
93
|
+
},
|
|
94
|
+
pageAt: (overlay) => stage.pageAt(overlay),
|
|
95
|
+
pointOnPage: (pon, overlay) => stage.pointOnPage(pon, overlay)
|
|
96
|
+
});
|
|
97
|
+
/**
|
|
98
|
+
* iOS-style draggable selection handles. Each is drawn the way the platform
|
|
99
|
+
* draws them: a thin caret BAR that forms the selection's own start/end edge
|
|
100
|
+
* — the boundary glyph's oriented edge, so rotated text and rotated pages
|
|
101
|
+
* carry the handle with them — capped by a screen-constant circle, above the
|
|
102
|
+
* first line at the start, below the last line at the end.
|
|
103
|
+
*
|
|
104
|
+
* On touch, where a caret drag isn't available, the handles ARE the way to
|
|
105
|
+
* grow or shrink a selection: a long-press selects a word, then each handle
|
|
106
|
+
* extends from the OPPOSITE endpoint, snapping to glyphs and crossing pages
|
|
107
|
+
* exactly like a pointer drag — it rides the same `beginAt`/`extendTo`
|
|
108
|
+
* gesture path, so highlights, menus, and commit signals all behave
|
|
109
|
+
* identically. Mount in the `<Stage>` overlay slot next to `<SelectionMenu>`;
|
|
110
|
+
* outside a Stage it renders nothing (a `PageView` has no camera to project
|
|
111
|
+
* through). Pointer-isolated, so grabbing a handle never pans the stage.
|
|
112
|
+
*/
|
|
113
|
+
function SelectionHandles({ color = "#2196f3", token = _embedpdf_plugin_stage.StageToken }) {
|
|
114
|
+
const host = require_runtime.useCapability(_embedpdf_plugin_selection_internal.SelectionToken);
|
|
115
|
+
const stage = require_runtime.useOptionalCapability(token);
|
|
116
|
+
const selecting = require_runtime.useSelector(_embedpdf_plugin_selection.SelectionToken, (c) => c.isSelecting());
|
|
117
|
+
const visible = require_runtime.useSelector(_embedpdf_plugin_selection_internal.SelectionToken, (c) => c.highlightVisible());
|
|
118
|
+
const endpoints = require_runtime.useSelector(_embedpdf_plugin_selection_internal.SelectionToken, (c) => {
|
|
119
|
+
const s = c.snapshot();
|
|
120
|
+
if (!s.start || !s.end) return null;
|
|
121
|
+
return {
|
|
122
|
+
start: {
|
|
123
|
+
pon: s.start.pon,
|
|
124
|
+
glyphQuad: s.start.glyphQuad,
|
|
125
|
+
advance: s.start.advance
|
|
126
|
+
},
|
|
127
|
+
end: {
|
|
128
|
+
pon: s.end.pon,
|
|
129
|
+
glyphQuad: s.end.glyphQuad,
|
|
130
|
+
advance: s.end.advance
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
}, sameEndpoints);
|
|
134
|
+
require_runtime.useKernelValue(() => stage?.visiblePages() ?? null);
|
|
135
|
+
const [dragging, setDragging] = (0, react.useState)(null);
|
|
136
|
+
const armSource = (0, react.useRef)(null);
|
|
137
|
+
armSource.current = stage && endpoints ? {
|
|
138
|
+
stage,
|
|
139
|
+
endpoints
|
|
140
|
+
} : null;
|
|
141
|
+
const bindHandle = (0, react.useMemo)(() => {
|
|
142
|
+
const detach = {};
|
|
143
|
+
const make = (role) => (el) => {
|
|
144
|
+
detach[role]?.();
|
|
145
|
+
delete detach[role];
|
|
146
|
+
if (!el) return;
|
|
147
|
+
detach[role] = (0, _embedpdf_web.attachSelectionHandle)(el, { arm: () => {
|
|
148
|
+
const src = armSource.current;
|
|
149
|
+
if (!src) return null;
|
|
150
|
+
const view = handleView(src.stage);
|
|
151
|
+
const geom = (0, _embedpdf_plugin_selection.selectionHandleGeom)(view, src.endpoints[role], role);
|
|
152
|
+
if (!geom) return null;
|
|
153
|
+
const opposite = src.endpoints[role === "start" ? "end" : "start"];
|
|
154
|
+
const drag = (0, _embedpdf_plugin_selection.createSelectionHandleDrag)(host, view, opposite, src.endpoints[role].pon);
|
|
155
|
+
setDragging(role);
|
|
156
|
+
return {
|
|
157
|
+
base: {
|
|
158
|
+
x: (geom.bar.from.x + geom.bar.to.x) / 2,
|
|
159
|
+
y: (geom.bar.from.y + geom.bar.to.y) / 2
|
|
160
|
+
},
|
|
161
|
+
session: {
|
|
162
|
+
move: drag.move,
|
|
163
|
+
end: () => {
|
|
164
|
+
drag.end();
|
|
165
|
+
setDragging(null);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
} });
|
|
170
|
+
};
|
|
171
|
+
return {
|
|
172
|
+
start: make("start"),
|
|
173
|
+
end: make("end")
|
|
174
|
+
};
|
|
175
|
+
}, [host]);
|
|
176
|
+
if (!stage || !endpoints || !visible) return null;
|
|
177
|
+
if (selecting && !dragging) return null;
|
|
178
|
+
const view = handleView(stage);
|
|
179
|
+
const renderHandle = (role) => {
|
|
180
|
+
const geom = (0, _embedpdf_plugin_selection.selectionHandleGeom)(view, endpoints[role], role);
|
|
181
|
+
if (!geom) return null;
|
|
182
|
+
const barTop = _embedpdf_plugin_selection.HANDLE_PAD + (role === "start" ? _embedpdf_plugin_selection.HANDLE_HEAD : 0);
|
|
183
|
+
const pivotX = _embedpdf_plugin_selection.HANDLE_PAD + _embedpdf_plugin_selection.HANDLE_BAR / 2;
|
|
184
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
185
|
+
ref: bindHandle[role],
|
|
186
|
+
style: {
|
|
187
|
+
position: "absolute",
|
|
188
|
+
left: geom.bar.from.x - pivotX,
|
|
189
|
+
top: geom.bar.from.y - barTop,
|
|
190
|
+
width: _embedpdf_plugin_selection.HANDLE_BAR + 2 * _embedpdf_plugin_selection.HANDLE_PAD,
|
|
191
|
+
height: geom.length + _embedpdf_plugin_selection.HANDLE_HEAD + 2 * _embedpdf_plugin_selection.HANDLE_PAD,
|
|
192
|
+
...geom.upright ? null : {
|
|
193
|
+
transform: `rotate(${geom.rotation}deg)`,
|
|
194
|
+
transformOrigin: `${pivotX}px ${barTop}px`
|
|
195
|
+
},
|
|
196
|
+
touchAction: "none",
|
|
197
|
+
cursor: "grab",
|
|
198
|
+
pointerEvents: "auto"
|
|
199
|
+
},
|
|
200
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: {
|
|
201
|
+
position: "absolute",
|
|
202
|
+
left: _embedpdf_plugin_selection.HANDLE_PAD,
|
|
203
|
+
top: barTop,
|
|
204
|
+
width: _embedpdf_plugin_selection.HANDLE_BAR,
|
|
205
|
+
height: geom.length,
|
|
206
|
+
background: color,
|
|
207
|
+
borderRadius: _embedpdf_plugin_selection.HANDLE_BAR / 2
|
|
208
|
+
} }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: {
|
|
209
|
+
position: "absolute",
|
|
210
|
+
left: pivotX - _embedpdf_plugin_selection.HANDLE_HEAD / 2,
|
|
211
|
+
top: role === "start" ? _embedpdf_plugin_selection.HANDLE_PAD : _embedpdf_plugin_selection.HANDLE_PAD + geom.length,
|
|
212
|
+
width: _embedpdf_plugin_selection.HANDLE_HEAD,
|
|
213
|
+
height: _embedpdf_plugin_selection.HANDLE_HEAD,
|
|
214
|
+
borderRadius: "50%",
|
|
215
|
+
background: color,
|
|
216
|
+
boxShadow: "0 1px 4px rgba(0, 0, 0, 0.35)"
|
|
217
|
+
} })]
|
|
218
|
+
}, role);
|
|
219
|
+
};
|
|
220
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [renderHandle("start"), renderHandle("end")] });
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Mount ONCE per viewer to wire clipboard copy: prefetches the selected text
|
|
224
|
+
* when the selection settles, answers the native `copy` event synchronously,
|
|
225
|
+
* and falls back to the async Clipboard API for ctrl/cmd+C when the page has
|
|
226
|
+
* no DOM selection. Renders nothing. For a toolbar Copy button, call
|
|
227
|
+
* `copySelection(useSelection())` from its click handler instead.
|
|
228
|
+
*/
|
|
229
|
+
function SelectionClipboard({ prefetch } = {}) {
|
|
230
|
+
const selection = require_runtime.useCapability(_embedpdf_plugin_selection.SelectionToken);
|
|
231
|
+
(0, react.useEffect)(() => (0, _embedpdf_web.wireSelectionClipboard)(selection, prefetch === void 0 ? {} : { prefetch }), [selection, prefetch]);
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
48
234
|
//#endregion
|
|
235
|
+
exports.SelectionClipboard = SelectionClipboard;
|
|
236
|
+
exports.SelectionHandles = SelectionHandles;
|
|
49
237
|
exports.SelectionLayer = SelectionLayer;
|
|
238
|
+
exports.SelectionMenu = SelectionMenu;
|
|
239
|
+
Object.defineProperty(exports, "copySelection", {
|
|
240
|
+
enumerable: true,
|
|
241
|
+
get: function() {
|
|
242
|
+
return _embedpdf_web.copySelection;
|
|
243
|
+
}
|
|
244
|
+
});
|
|
50
245
|
exports.useSelection = useSelection;
|
|
51
246
|
Object.keys(_embedpdf_plugin_selection).forEach(function(k) {
|
|
52
247
|
if (k !== "default" && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
package/dist/selection.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"selection.cjs","names":["usePage","useCapability","SelectionToken","useSelector","shallowArray"],"sources":["../src/selection.tsx"],"sourcesContent":["/**\n * The React surface for @embedpdf/plugin-selection.\n *\n * <SelectionLayer> is a dumb renderer: it warms the page's geometry on mount,\n * reads the content-space highlight rects from the capability, and paints them —\n * mapping each rect through PageContext.pageToContent (the same path markers use).\n * Zero pointer handling here; that's the PagePointerSource + the hub.\n */\n\n// One-line-per-feature: registration travels with the UI.\nexport * from '@embedpdf/plugin-selection';\nimport * as React from 'react';\nimport { useEffect } from 'react';\nimport { SelectionToken } from '@embedpdf/plugin-selection';\nimport { shallowArray, useCapability, usePage, useSelector } from './runtime';\n\nexport interface SelectionLayerProps {\n /** Highlight colour (default: translucent blue). */\n color?: string;\n}\n\nexport function SelectionLayer({ color = 'rgba(33, 150, 243, 0.35)' }: SelectionLayerProps) {\n const page = usePage();\n const selection = useCapability(SelectionToken);\n const rects = useSelector(SelectionToken, (c) => c.rectsForPage(page.pon), shallowArray);\n // A consumer (e.g. a markup tool drawing its own preview) can take over the\n // selection visual; when it does, we render nothing so the two never overlap.\n const visible = useSelector(SelectionToken, (c) => c.highlightVisible());\n\n // Warm this page's text geometry as soon as it's on screen, so the first\n // pointer-down can hit-test without waiting on the engine round-trip.\n useEffect(() => {\n selection.ensurePage(page.pon);\n }, [selection, page.pon]);\n\n if (!visible) return null;\n\n return (\n <div style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}>\n {rects.map((r, i) => {\n // content space → un-rotated content view px (rides the page's CSS rotation)\n const tl = page.transform.pageToContent({ x: r.x, y: r.y });\n const br = page.transform.pageToContent({ x: r.x + r.width, y: r.y + r.height });\n return (\n <div\n key={i}\n style={{\n position: 'absolute',\n left: tl.x,\n top: tl.y,\n width: br.x - tl.x,\n height: br.y - tl.y,\n background: color,\n }}\n />\n );\n })}\n </div>\n );\n}\n\n/** The selection capability (clear(), hasSelection(), …) for app chrome. */\nexport function useSelection() {\n return useCapability(SelectionToken);\n}\n"],"mappings":";;;;;;;;AAqBA,SAAgB,eAAe,EAAE,QAAQ,8BAAmD;CAC1F,MAAM,OAAOA,gBAAAA,QAAQ;CACrB,MAAM,YAAYC,gBAAAA,cAAcC,2BAAAA,cAAc;CAC9C,MAAM,QAAQC,gBAAAA,YAAYD,2BAAAA,iBAAiB,MAAM,EAAE,aAAa,KAAK,GAAG,GAAGE,gBAAAA,YAAY;CAGvF,MAAM,UAAUD,gBAAAA,YAAYD,2BAAAA,iBAAiB,MAAM,EAAE,iBAAiB,CAAC;CAIvE,CAAA,GAAA,MAAA,UAAA,OAAgB;EACd,UAAU,WAAW,KAAK,GAAG;CAC/B,GAAG,CAAC,WAAW,KAAK,GAAG,CAAC;CAExB,IAAI,CAAC,SAAS,OAAO;CAErB,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;EAAK,OAAO;GAAE,UAAU;GAAY,OAAO;GAAG,eAAe;EAAO;EACjE,UAAA,MAAM,KAAK,GAAG,MAAM;GAEnB,MAAM,KAAK,KAAK,UAAU,cAAc;IAAE,GAAG,EAAE;IAAG,GAAG,EAAE;GAAE,CAAC;GAC1D,MAAM,KAAK,KAAK,UAAU,cAAc;IAAE,GAAG,EAAE,IAAI,EAAE;IAAO,GAAG,EAAE,IAAI,EAAE;GAAO,CAAC;GAC/E,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD,EAEE,OAAO;IACL,UAAU;IACV,MAAM,GAAG;IACT,KAAK,GAAG;IACR,OAAO,GAAG,IAAI,GAAG;IACjB,QAAQ,GAAG,IAAI,GAAG;IAClB,YAAY;GACd,EACD,GATM,CASN;EAEL,CAAC;CACE,CAAA;AAET;;AAGA,SAAgB,eAAe;CAC7B,OAAOD,gBAAAA,cAAcC,2BAAAA,cAAc;AACrC"}
|
|
1
|
+
{"version":3,"file":"selection.cjs","names":["usePage","useCapability","SelectionHostToken","useSelector","shallowArray","SelectionToken","Anchored","StageToken","useOptionalCapability","HANDLE_PAD","HANDLE_HEAD","HANDLE_BAR"],"sources":["../src/selection.tsx"],"sourcesContent":["/**\n * The React surface for @embedpdf/plugin-selection.\n *\n * <SelectionLayer> is a dumb renderer: it warms the page's geometry on mount,\n * reads the content-space highlight rects from the capability, and paints them —\n * mapping each rect through PageContext.toPixels (the same path markers use).\n * Zero pointer handling here; that's the PagePointerSource + the hub.\n *\n * The layer resolves the HOST lens (`/internal`: geometry warming, the\n * highlight handshake) — the adapter is exactly what that entry exists for.\n * `useSelection()` hands app code the PUBLIC lens only.\n */\n\n// One-line-per-feature: registration travels with the UI.\nexport * from '@embedpdf/plugin-selection';\n// The clipboard side effect lives in @embedpdf/web (the plugin is DOM-free);\n// re-exported here so app code has one import for the whole feature.\nexport { copySelection } from '@embedpdf/web';\nimport * as React from 'react';\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport { textQuadEquals } from '@embedpdf/core-geometry';\nimport type { CapabilityToken } from '@embedpdf/core';\nimport {\n HANDLE_BAR,\n HANDLE_HEAD,\n HANDLE_PAD,\n SelectionToken,\n createSelectionHandleDrag,\n selectionHandleGeom,\n type SelectionHandleEndpoint,\n type SelectionHandleView,\n type SelectionMenuAnchor,\n} from '@embedpdf/plugin-selection';\nimport { SelectionToken as SelectionHostToken } from '@embedpdf/plugin-selection/internal';\nimport { StageToken, type StageCapability } from '@embedpdf/plugin-stage';\nimport {\n attachSelectionHandle,\n wireSelectionClipboard,\n type SelectionClipboardOptions,\n} from '@embedpdf/web';\nimport { Anchored, type AnchoredPlacement } from './anchored';\nimport {\n shallowArray,\n useCapability,\n useKernelValue,\n useOptionalCapability,\n usePage,\n useSelector,\n} from './runtime';\n\nexport interface SelectionLayerProps {\n /** Highlight colour (default: translucent blue). */\n color?: string;\n}\n\nexport function SelectionLayer({ color = 'rgba(33, 150, 243, 0.35)' }: SelectionLayerProps) {\n const page = usePage();\n const selection = useCapability(SelectionHostToken);\n const segments = useSelector(\n SelectionHostToken,\n (c) => c.segmentsForPage(page.pon),\n shallowArray,\n );\n // A consumer (e.g. a markup tool drawing its own preview) can take over the\n // selection visual; when it does, we render nothing so the two never overlap.\n const visible = useSelector(SelectionHostToken, (c) => c.highlightVisible());\n\n // Warm this page's text geometry as soon as it's on screen, so the first\n // pointer-down can hit-test without waiting on the engine round-trip.\n // (A no-op without doc.text.select — nothing warms, nothing renders.)\n useEffect(() => {\n selection.ensurePage(page.pon);\n }, [selection, page.pon]);\n\n if (!visible) return null;\n\n return (\n <svg\n style={{\n position: 'absolute',\n inset: 0,\n width: '100%',\n height: '100%',\n overflow: 'visible',\n pointerEvents: 'none',\n }}\n >\n {segments.map((s, i) => {\n // content space → un-rotated content view px (rides the page's CSS\n // rotation). An affine map, so mapping the four corners is exact —\n // upright segments render pixel-identical to the old div-per-rect.\n const ring = [s.quad.upperStart, s.quad.upperEnd, s.quad.lowerEnd, s.quad.lowerStart].map(\n (p) => page.transform.toPixels(p),\n );\n return (\n <polygon key={i} points={ring.map((p) => `${p.x},${p.y}`).join(' ')} fill={color} />\n );\n })}\n </svg>\n );\n}\n\n/** The PUBLIC selection capability (select(), readText(), canCopy(), …) for\n * app chrome — toolbars, context menus, automation. */\nexport function useSelection() {\n return useCapability(SelectionToken);\n}\n\n/** Structural equality for the selection's menu anchor — keeps the menu from\n * re-rendering on unrelated dispatches (the capability returns a fresh\n * object each call). */\nconst sameMenuAnchor = (\n a: SelectionMenuAnchor | null,\n b: SelectionMenuAnchor | null,\n): boolean => {\n if (a === b) return true;\n if (!a || !b) return false;\n return (\n a.pon === b.pon &&\n a.bounds.x === b.bounds.x &&\n a.bounds.y === b.bounds.y &&\n a.bounds.width === b.bounds.width &&\n a.bounds.height === b.bounds.height\n );\n};\n\nexport interface SelectionMenuProps {\n children: React.ReactNode;\n /** Gap in screen px between the selection and the menu (default 8). */\n gap?: number;\n /** Where to place the menu relative to the selection. Default 'top'. */\n placement?: AnchoredPlacement;\n}\n\n/**\n * Floats over the current TEXT selection (one anchor regardless of\n * cross-page selection; it rides the gesture's end page) — and only once the\n * selection SETTLES: hidden while `isSelecting()` (mid-drag), it appears at\n * pointer-up; programmatic selections show immediately (born settled). Works\n * under `<Stage>` (mount in the overlay slot) and `<PageView>` alike — the\n * surface provides the projection. Compose the contents from hooks:\n * `useSelection()` for copy/clear, `useAnnotation()` for\n * `markupFromSelection('highlight')`, `copySelection` for the clipboard.\n * For live-follow UI during the drag, compose `<Anchored>` with\n * `menuAnchor()` yourself — the primitive carries no policy.\n */\nexport function SelectionMenu({ children, gap = 8, placement = 'top' }: SelectionMenuProps) {\n const selecting = useSelector(SelectionToken, (c) => c.isSelecting());\n const anchor = useSelector(SelectionToken, (c) => c.menuAnchor(), sameMenuAnchor);\n if (selecting || !anchor) return null;\n return (\n <Anchored anchor={anchor} placement={placement} gap={gap}>\n {children}\n </Anchored>\n );\n}\n\n// ── selection handles (the touch affordance) ────────────────────────────────\n//\n// Policy lives out of this file, per the layering razor: the geometry and the\n// drag session are `@embedpdf/plugin-selection`'s pure `handles` module (one\n// source for every adapter), the native listener mechanics are\n// `@embedpdf/web`'s `attachSelectionHandle` (the down-shield timing subtlety\n// lives ONCE), and this component keeps what only React can do —\n// subscriptions, markup, theming.\n\ninterface Endpoints {\n start: SelectionHandleEndpoint;\n end: SelectionHandleEndpoint;\n}\nconst sameEndpoints = (a: Endpoints | null, b: Endpoints | null): boolean => {\n if (a === b) return true;\n if (!a || !b) return false;\n return (\n a.start.pon === b.start.pon &&\n a.end.pon === b.end.pon &&\n a.start.advance === b.start.advance &&\n a.end.advance === b.end.advance &&\n // corner-wise, so a boundary that ROTATES without moving its bounding box\n // still re-renders (an AABB comparison would call that \"unchanged\")\n textQuadEquals(a.start.glyphQuad, b.start.glyphQuad) &&\n textQuadEquals(a.end.glyphQuad, b.end.glyphQuad)\n );\n};\n\n/** The plugin's structural view over this Stage: point-exact projection in,\n * page resolution out. (`pageRectToScreen` is the AABB projector — upright\n * overlays only — and would collapse exactly the orientation handles need.) */\nconst handleView = (stage: StageCapability): SelectionHandleView => ({\n toOverlay: (pon, pt) => {\n const world = stage.pageToWorld(pon, pt);\n return world ? stage.toScreen(world) : null;\n },\n pageAt: (overlay) => stage.pageAt(overlay),\n pointOnPage: (pon, overlay) => stage.pointOnPage(pon, overlay),\n});\n\nexport interface SelectionHandlesProps {\n /** Handle colour (default: the selection blue). */\n color?: string;\n /** The stage lens hosting this overlay (default: the main StageToken). */\n token?: CapabilityToken<StageCapability>;\n}\n\n/**\n * iOS-style draggable selection handles. Each is drawn the way the platform\n * draws them: a thin caret BAR that forms the selection's own start/end edge\n * — the boundary glyph's oriented edge, so rotated text and rotated pages\n * carry the handle with them — capped by a screen-constant circle, above the\n * first line at the start, below the last line at the end.\n *\n * On touch, where a caret drag isn't available, the handles ARE the way to\n * grow or shrink a selection: a long-press selects a word, then each handle\n * extends from the OPPOSITE endpoint, snapping to glyphs and crossing pages\n * exactly like a pointer drag — it rides the same `beginAt`/`extendTo`\n * gesture path, so highlights, menus, and commit signals all behave\n * identically. Mount in the `<Stage>` overlay slot next to `<SelectionMenu>`;\n * outside a Stage it renders nothing (a `PageView` has no camera to project\n * through). Pointer-isolated, so grabbing a handle never pans the stage.\n */\nexport function SelectionHandles({ color = '#2196f3', token = StageToken }: SelectionHandlesProps) {\n const host = useCapability(SelectionHostToken);\n const stage = useOptionalCapability(token);\n const selecting = useSelector(SelectionToken, (c) => c.isSelecting());\n const visible = useSelector(SelectionHostToken, (c) => c.highlightVisible());\n const endpoints = useSelector(\n SelectionHostToken,\n (c): Endpoints | null => {\n const s = c.snapshot();\n if (!s.start || !s.end) return null;\n return {\n start: { pon: s.start.pon, glyphQuad: s.start.glyphQuad, advance: s.start.advance },\n end: { pon: s.end.pon, glyphQuad: s.end.glyphQuad, advance: s.end.advance },\n };\n },\n sameEndpoints,\n );\n // The handles are positioned by PROJECTING the endpoint corners through the\n // camera, so they must re-render whenever the camera moves — visiblePages is\n // the stage's reference-stable revision for exactly that (the same value the\n // page surfaces re-render on, so handle and highlight move in one commit).\n useKernelValue(() => stage?.visiblePages() ?? null);\n const [dragging, setDragging] = useState<'start' | 'end' | null>(null);\n // The web binder's `arm` must read the CURRENT endpoints/stage at pointer\n // down, not the ones captured when the listener attached — a stable ref\n // callback with a live arm-source is the standard escape from that.\n const armSource = useRef<{ stage: StageCapability; endpoints: Endpoints } | null>(null);\n armSource.current = stage && endpoints ? { stage, endpoints } : null;\n const bindHandle = useMemo(() => {\n const detach: Partial<Record<'start' | 'end', () => void>> = {};\n const make = (role: 'start' | 'end') => (el: HTMLDivElement | null) => {\n detach[role]?.();\n delete detach[role];\n if (!el) return;\n detach[role] = attachSelectionHandle(el, {\n arm: () => {\n const src = armSource.current;\n if (!src) return null;\n const view = handleView(src.stage);\n const geom = selectionHandleGeom(view, src.endpoints[role], role);\n if (!geom) return null;\n const opposite = src.endpoints[role === 'start' ? 'end' : 'start'];\n const drag = createSelectionHandleDrag(\n host,\n view,\n opposite,\n src.endpoints[role].pon,\n );\n setDragging(role);\n return {\n // the point the user grabbed: the bar's midpoint\n base: {\n x: (geom.bar.from.x + geom.bar.to.x) / 2,\n y: (geom.bar.from.y + geom.bar.to.y) / 2,\n },\n session: {\n move: drag.move,\n end: () => {\n drag.end(); // settle → menu reappears, onCommit fires\n setDragging(null);\n },\n },\n };\n },\n });\n };\n return { start: make('start'), end: make('end') };\n }, [host]);\n\n if (!stage || !endpoints || !visible) return null;\n // Hidden while a pointer drag-select is in flight (like the menu) — but a\n // HANDLE drag is itself a selection gesture, so it keeps its handles.\n if (selecting && !dragging) return null;\n const view = handleView(stage);\n\n const renderHandle = (role: 'start' | 'end') => {\n const geom = selectionHandleGeom(view, endpoints[role], role);\n if (!geom) return null; // endpoint page not laid out right now\n // The shell is laid out UPRIGHT in its own frame — bar of the edge's\n // length, head stacked above (start) or below (end) — then rotated onto\n // the projected edge. The pivot is the bar's ascent-side tip, the one\n // point that must land exactly on the glyph corner.\n const barTop = HANDLE_PAD + (role === 'start' ? HANDLE_HEAD : 0);\n const pivotX = HANDLE_PAD + HANDLE_BAR / 2;\n return (\n <div\n key={role}\n ref={bindHandle[role]}\n style={{\n position: 'absolute',\n left: geom.bar.from.x - pivotX,\n top: geom.bar.from.y - barTop,\n width: HANDLE_BAR + 2 * HANDLE_PAD,\n height: geom.length + HANDLE_HEAD + 2 * HANDLE_PAD,\n // Upright text carries NO transform — pixel-identical to an\n // axis-aligned box (the geometry's float-noise guard decides).\n ...(geom.upright\n ? null\n : {\n transform: `rotate(${geom.rotation}deg)`,\n transformOrigin: `${pivotX}px ${barTop}px`,\n }),\n touchAction: 'none',\n cursor: 'grab',\n pointerEvents: 'auto',\n }}\n >\n {/* the caret bar — the selection's own edge, spanning the glyph's ink\n height (which is why it stays the text's height at any tilt) */}\n <div\n style={{\n position: 'absolute',\n left: HANDLE_PAD,\n top: barTop,\n width: HANDLE_BAR,\n height: geom.length,\n background: color,\n borderRadius: HANDLE_BAR / 2,\n }}\n />\n {/* the head — flush against the bar, beyond the ascent at the start and\n past the baseline at the end, in the TEXT's frame */}\n <div\n style={{\n position: 'absolute',\n left: pivotX - HANDLE_HEAD / 2,\n top: role === 'start' ? HANDLE_PAD : HANDLE_PAD + geom.length,\n width: HANDLE_HEAD,\n height: HANDLE_HEAD,\n borderRadius: '50%',\n background: color,\n boxShadow: '0 1px 4px rgba(0, 0, 0, 0.35)',\n }}\n />\n </div>\n );\n };\n\n return (\n <>\n {renderHandle('start')}\n {renderHandle('end')}\n </>\n );\n}\n\n\nexport type SelectionClipboardProps = Pick<SelectionClipboardOptions, 'prefetch'>;\n\n/**\n * Mount ONCE per viewer to wire clipboard copy: prefetches the selected text\n * when the selection settles, answers the native `copy` event synchronously,\n * and falls back to the async Clipboard API for ctrl/cmd+C when the page has\n * no DOM selection. Renders nothing. For a toolbar Copy button, call\n * `copySelection(useSelection())` from its click handler instead.\n */\nexport function SelectionClipboard({ prefetch }: SelectionClipboardProps = {}) {\n const selection = useCapability(SelectionToken);\n useEffect(\n () => wireSelectionClipboard(selection, prefetch === undefined ? {} : { prefetch }),\n [selection, prefetch],\n );\n return null;\n}\n"],"mappings":"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"}
|
package/dist/selection.d.cts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import { AnchoredPlacement as AnchoredPlacement$1 } from "./anchored.cjs";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { SelectionClipboardOptions, copySelection } from "@embedpdf/web";
|
|
4
|
+
import { CapabilityToken } from "@embedpdf/core";
|
|
5
|
+
import { StageCapability } from "@embedpdf/plugin-stage";
|
|
1
6
|
export * from "@embedpdf/plugin-selection";
|
|
2
7
|
//#region src/selection.d.ts
|
|
3
8
|
interface SelectionLayerProps {
|
|
@@ -5,8 +10,61 @@ interface SelectionLayerProps {
|
|
|
5
10
|
color?: string;
|
|
6
11
|
}
|
|
7
12
|
declare function SelectionLayer({ color }: SelectionLayerProps): import("react/jsx-runtime").JSX.Element | null;
|
|
8
|
-
/** The selection capability (
|
|
13
|
+
/** The PUBLIC selection capability (select(), readText(), canCopy(), …) for
|
|
14
|
+
* app chrome — toolbars, context menus, automation. */
|
|
9
15
|
declare function useSelection(): import("@embedpdf/plugin-selection").SelectionCapability;
|
|
16
|
+
interface SelectionMenuProps {
|
|
17
|
+
children: React.ReactNode;
|
|
18
|
+
/** Gap in screen px between the selection and the menu (default 8). */
|
|
19
|
+
gap?: number;
|
|
20
|
+
/** Where to place the menu relative to the selection. Default 'top'. */
|
|
21
|
+
placement?: AnchoredPlacement$1;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Floats over the current TEXT selection (one anchor regardless of
|
|
25
|
+
* cross-page selection; it rides the gesture's end page) — and only once the
|
|
26
|
+
* selection SETTLES: hidden while `isSelecting()` (mid-drag), it appears at
|
|
27
|
+
* pointer-up; programmatic selections show immediately (born settled). Works
|
|
28
|
+
* under `<Stage>` (mount in the overlay slot) and `<PageView>` alike — the
|
|
29
|
+
* surface provides the projection. Compose the contents from hooks:
|
|
30
|
+
* `useSelection()` for copy/clear, `useAnnotation()` for
|
|
31
|
+
* `markupFromSelection('highlight')`, `copySelection` for the clipboard.
|
|
32
|
+
* For live-follow UI during the drag, compose `<Anchored>` with
|
|
33
|
+
* `menuAnchor()` yourself — the primitive carries no policy.
|
|
34
|
+
*/
|
|
35
|
+
declare function SelectionMenu({ children, gap, placement }: SelectionMenuProps): import("react/jsx-runtime").JSX.Element | null;
|
|
36
|
+
interface SelectionHandlesProps {
|
|
37
|
+
/** Handle colour (default: the selection blue). */
|
|
38
|
+
color?: string;
|
|
39
|
+
/** The stage lens hosting this overlay (default: the main StageToken). */
|
|
40
|
+
token?: CapabilityToken<StageCapability>;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* iOS-style draggable selection handles. Each is drawn the way the platform
|
|
44
|
+
* draws them: a thin caret BAR that forms the selection's own start/end edge
|
|
45
|
+
* — the boundary glyph's oriented edge, so rotated text and rotated pages
|
|
46
|
+
* carry the handle with them — capped by a screen-constant circle, above the
|
|
47
|
+
* first line at the start, below the last line at the end.
|
|
48
|
+
*
|
|
49
|
+
* On touch, where a caret drag isn't available, the handles ARE the way to
|
|
50
|
+
* grow or shrink a selection: a long-press selects a word, then each handle
|
|
51
|
+
* extends from the OPPOSITE endpoint, snapping to glyphs and crossing pages
|
|
52
|
+
* exactly like a pointer drag — it rides the same `beginAt`/`extendTo`
|
|
53
|
+
* gesture path, so highlights, menus, and commit signals all behave
|
|
54
|
+
* identically. Mount in the `<Stage>` overlay slot next to `<SelectionMenu>`;
|
|
55
|
+
* outside a Stage it renders nothing (a `PageView` has no camera to project
|
|
56
|
+
* through). Pointer-isolated, so grabbing a handle never pans the stage.
|
|
57
|
+
*/
|
|
58
|
+
declare function SelectionHandles({ color, token }: SelectionHandlesProps): import("react/jsx-runtime").JSX.Element | null;
|
|
59
|
+
type SelectionClipboardProps = Pick<SelectionClipboardOptions, 'prefetch'>;
|
|
60
|
+
/**
|
|
61
|
+
* Mount ONCE per viewer to wire clipboard copy: prefetches the selected text
|
|
62
|
+
* when the selection settles, answers the native `copy` event synchronously,
|
|
63
|
+
* and falls back to the async Clipboard API for ctrl/cmd+C when the page has
|
|
64
|
+
* no DOM selection. Renders nothing. For a toolbar Copy button, call
|
|
65
|
+
* `copySelection(useSelection())` from its click handler instead.
|
|
66
|
+
*/
|
|
67
|
+
declare function SelectionClipboard({ prefetch }?: SelectionClipboardProps): null;
|
|
10
68
|
//#endregion
|
|
11
|
-
export { SelectionLayer, SelectionLayerProps, useSelection };
|
|
69
|
+
export { SelectionClipboard, SelectionClipboardProps, SelectionHandles, SelectionHandlesProps, SelectionLayer, SelectionLayerProps, SelectionMenu, SelectionMenuProps, copySelection, useSelection };
|
|
12
70
|
//# sourceMappingURL=selection.d.cts.map
|
package/dist/selection.d.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import { AnchoredPlacement as AnchoredPlacement$1 } from "./anchored.js";
|
|
2
|
+
import { CapabilityToken } from "@embedpdf/core";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { SelectionClipboardOptions, copySelection } from "@embedpdf/web";
|
|
5
|
+
import { StageCapability } from "@embedpdf/plugin-stage";
|
|
1
6
|
export * from "@embedpdf/plugin-selection";
|
|
2
7
|
//#region src/selection.d.ts
|
|
3
8
|
interface SelectionLayerProps {
|
|
@@ -5,8 +10,61 @@ interface SelectionLayerProps {
|
|
|
5
10
|
color?: string;
|
|
6
11
|
}
|
|
7
12
|
declare function SelectionLayer({ color }: SelectionLayerProps): import("react/jsx-runtime").JSX.Element | null;
|
|
8
|
-
/** The selection capability (
|
|
13
|
+
/** The PUBLIC selection capability (select(), readText(), canCopy(), …) for
|
|
14
|
+
* app chrome — toolbars, context menus, automation. */
|
|
9
15
|
declare function useSelection(): import("@embedpdf/plugin-selection").SelectionCapability;
|
|
16
|
+
interface SelectionMenuProps {
|
|
17
|
+
children: React.ReactNode;
|
|
18
|
+
/** Gap in screen px between the selection and the menu (default 8). */
|
|
19
|
+
gap?: number;
|
|
20
|
+
/** Where to place the menu relative to the selection. Default 'top'. */
|
|
21
|
+
placement?: AnchoredPlacement$1;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Floats over the current TEXT selection (one anchor regardless of
|
|
25
|
+
* cross-page selection; it rides the gesture's end page) — and only once the
|
|
26
|
+
* selection SETTLES: hidden while `isSelecting()` (mid-drag), it appears at
|
|
27
|
+
* pointer-up; programmatic selections show immediately (born settled). Works
|
|
28
|
+
* under `<Stage>` (mount in the overlay slot) and `<PageView>` alike — the
|
|
29
|
+
* surface provides the projection. Compose the contents from hooks:
|
|
30
|
+
* `useSelection()` for copy/clear, `useAnnotation()` for
|
|
31
|
+
* `markupFromSelection('highlight')`, `copySelection` for the clipboard.
|
|
32
|
+
* For live-follow UI during the drag, compose `<Anchored>` with
|
|
33
|
+
* `menuAnchor()` yourself — the primitive carries no policy.
|
|
34
|
+
*/
|
|
35
|
+
declare function SelectionMenu({ children, gap, placement }: SelectionMenuProps): import("react/jsx-runtime").JSX.Element | null;
|
|
36
|
+
interface SelectionHandlesProps {
|
|
37
|
+
/** Handle colour (default: the selection blue). */
|
|
38
|
+
color?: string;
|
|
39
|
+
/** The stage lens hosting this overlay (default: the main StageToken). */
|
|
40
|
+
token?: CapabilityToken<StageCapability>;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* iOS-style draggable selection handles. Each is drawn the way the platform
|
|
44
|
+
* draws them: a thin caret BAR that forms the selection's own start/end edge
|
|
45
|
+
* — the boundary glyph's oriented edge, so rotated text and rotated pages
|
|
46
|
+
* carry the handle with them — capped by a screen-constant circle, above the
|
|
47
|
+
* first line at the start, below the last line at the end.
|
|
48
|
+
*
|
|
49
|
+
* On touch, where a caret drag isn't available, the handles ARE the way to
|
|
50
|
+
* grow or shrink a selection: a long-press selects a word, then each handle
|
|
51
|
+
* extends from the OPPOSITE endpoint, snapping to glyphs and crossing pages
|
|
52
|
+
* exactly like a pointer drag — it rides the same `beginAt`/`extendTo`
|
|
53
|
+
* gesture path, so highlights, menus, and commit signals all behave
|
|
54
|
+
* identically. Mount in the `<Stage>` overlay slot next to `<SelectionMenu>`;
|
|
55
|
+
* outside a Stage it renders nothing (a `PageView` has no camera to project
|
|
56
|
+
* through). Pointer-isolated, so grabbing a handle never pans the stage.
|
|
57
|
+
*/
|
|
58
|
+
declare function SelectionHandles({ color, token }: SelectionHandlesProps): import("react/jsx-runtime").JSX.Element | null;
|
|
59
|
+
type SelectionClipboardProps = Pick<SelectionClipboardOptions, 'prefetch'>;
|
|
60
|
+
/**
|
|
61
|
+
* Mount ONCE per viewer to wire clipboard copy: prefetches the selected text
|
|
62
|
+
* when the selection settles, answers the native `copy` event synchronously,
|
|
63
|
+
* and falls back to the async Clipboard API for ctrl/cmd+C when the page has
|
|
64
|
+
* no DOM selection. Renders nothing. For a toolbar Copy button, call
|
|
65
|
+
* `copySelection(useSelection())` from its click handler instead.
|
|
66
|
+
*/
|
|
67
|
+
declare function SelectionClipboard({ prefetch }?: SelectionClipboardProps): null;
|
|
10
68
|
//#endregion
|
|
11
|
-
export { SelectionLayer, SelectionLayerProps, useSelection };
|
|
69
|
+
export { SelectionClipboard, SelectionClipboardProps, SelectionHandles, SelectionHandlesProps, SelectionLayer, SelectionLayerProps, SelectionMenu, SelectionMenuProps, copySelection, useSelection };
|
|
12
70
|
//# sourceMappingURL=selection.d.ts.map
|