@embedpdf/react 3.0.0-next.13 → 3.0.0-next.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AnnotationList-CkijRHet-HEFNUIZe.d.cts +877 -0
- package/dist/AnnotationList-CkijRHet-HEFNUIZe.d.ts +877 -0
- package/dist/actions.cjs +25 -5
- package/dist/actions.cjs.map +1 -1
- package/dist/actions.d.cts +18 -5
- package/dist/actions.d.ts +18 -5
- package/dist/actions.js +22 -6
- package/dist/actions.js.map +1 -1
- package/dist/anchored-DeX95xSd.d.ts +85 -0
- package/dist/anchored-DzN3JywS.d.cts +85 -0
- package/dist/anchored.cjs +71 -28
- package/dist/anchored.cjs.map +1 -1
- package/dist/anchored.d.cts +2 -45
- package/dist/anchored.d.ts +2 -45
- package/dist/anchored.js +56 -19
- package/dist/anchored.js.map +1 -1
- package/dist/annotation-B48UT2ln.d.ts +302 -0
- package/dist/annotation-C1ttM8Wj.js +708 -0
- package/dist/annotation-C1ttM8Wj.js.map +1 -0
- package/dist/annotation-C9dVwo9b.d.cts +302 -0
- package/dist/annotation-Dex5EBda.cjs +830 -0
- package/dist/annotation-Dex5EBda.cjs.map +1 -0
- package/dist/annotation.cjs +20 -783
- package/dist/annotation.d.cts +2 -2
- package/dist/annotation.d.ts +2 -2
- package/dist/annotation.js +2 -768
- package/dist/commands.cjs +68 -28
- package/dist/commands.cjs.map +1 -1
- package/dist/commands.d.cts +37 -10
- package/dist/commands.d.ts +37 -10
- package/dist/commands.js +68 -31
- package/dist/commands.js.map +1 -1
- package/dist/form-55izw0jS.js +511 -0
- package/dist/form-55izw0jS.js.map +1 -0
- package/dist/form-BNqQfGGH.cjs +548 -0
- package/dist/form-BNqQfGGH.cjs.map +1 -0
- package/dist/form.cjs +5 -5
- package/dist/form.d.cts +32 -25
- package/dist/form.d.ts +32 -25
- package/dist/form.js +2 -3
- package/dist/history.cjs +38 -0
- package/dist/history.cjs.map +1 -0
- package/dist/history.d.cts +27 -0
- package/dist/history.d.ts +27 -0
- package/dist/history.js +27 -0
- package/dist/history.js.map +1 -0
- package/dist/i18n.cjs +26 -30
- package/dist/i18n.cjs.map +1 -1
- package/dist/i18n.d.cts +24 -21
- package/dist/i18n.d.ts +24 -21
- package/dist/i18n.js +24 -31
- package/dist/i18n.js.map +1 -1
- package/dist/index.cjs +213 -57
- package/dist/index.d.cts +32 -31
- package/dist/index.d.ts +32 -31
- package/dist/index.js +33 -29
- package/dist/inert-CmBEbKCn.cjs +19 -0
- package/dist/inert-CmBEbKCn.cjs.map +1 -0
- package/dist/inert-D8fkennb.js +14 -0
- package/dist/inert-D8fkennb.js.map +1 -0
- package/dist/interaction.cjs +48 -102
- package/dist/interaction.cjs.map +1 -1
- package/dist/interaction.d.cts +23 -32
- package/dist/interaction.d.ts +23 -32
- package/dist/interaction.js +33 -102
- package/dist/interaction.js.map +1 -1
- package/dist/link.cjs +65 -104
- package/dist/link.cjs.map +1 -1
- package/dist/link.d.cts +21 -16
- package/dist/link.d.ts +22 -17
- package/dist/link.js +67 -107
- package/dist/link.js.map +1 -1
- package/dist/measurement.cjs +56 -0
- package/dist/measurement.cjs.map +1 -0
- package/dist/measurement.d.cts +38 -0
- package/dist/measurement.d.ts +38 -0
- package/dist/measurement.js +42 -0
- package/dist/measurement.js.map +1 -0
- package/dist/metadata.cjs +20 -7
- package/dist/metadata.cjs.map +1 -1
- package/dist/metadata.d.cts +22 -7
- package/dist/metadata.d.ts +22 -7
- package/dist/metadata.js +20 -10
- package/dist/metadata.js.map +1 -1
- package/dist/page-edit.cjs +7 -3
- package/dist/page-edit.cjs.map +1 -1
- package/dist/page-edit.d.cts +6 -2
- package/dist/page-edit.d.ts +6 -2
- package/dist/page-edit.js +7 -4
- package/dist/page-edit.js.map +1 -1
- package/dist/page-layers-Bkaaz8Zf.js +26 -0
- package/dist/page-layers-Bkaaz8Zf.js.map +1 -0
- package/dist/page-layers-BloZDoOV.cjs +37 -0
- package/dist/page-layers-BloZDoOV.cjs.map +1 -0
- package/dist/page-view.cjs +89 -82
- package/dist/page-view.cjs.map +1 -1
- package/dist/page-view.d.cts +14 -7
- package/dist/page-view.d.ts +18 -11
- package/dist/page-view.js +86 -81
- package/dist/page-view.js.map +1 -1
- package/dist/redaction.cjs +30 -15
- package/dist/redaction.cjs.map +1 -1
- package/dist/redaction.d.cts +27 -9
- package/dist/redaction.d.ts +27 -9
- package/dist/redaction.js +29 -18
- package/dist/redaction.js.map +1 -1
- package/dist/render.cjs +71 -46
- package/dist/render.cjs.map +1 -1
- package/dist/render.d.cts +17 -5
- package/dist/render.d.ts +17 -5
- package/dist/render.js +70 -49
- package/dist/render.js.map +1 -1
- package/dist/runtime-B6xcPsEb.js +398 -0
- package/dist/runtime-B6xcPsEb.js.map +1 -0
- package/dist/runtime-DBEBlpQV.d.cts +237 -0
- package/dist/runtime-V3MVMb91.d.ts +237 -0
- package/dist/runtime-fzpGv78v.cjs +555 -0
- package/dist/runtime-fzpGv78v.cjs.map +1 -0
- package/dist/runtime.cjs +45 -324
- package/dist/runtime.d.cts +2 -2
- package/dist/runtime.d.ts +2 -2
- package/dist/runtime.js +2 -306
- package/dist/search.cjs +81 -50
- package/dist/search.cjs.map +1 -1
- package/dist/search.d.cts +36 -24
- package/dist/search.d.ts +36 -24
- package/dist/search.js +81 -53
- package/dist/search.js.map +1 -1
- package/dist/selection.cjs +105 -108
- package/dist/selection.cjs.map +1 -1
- package/dist/selection.d.cts +54 -26
- package/dist/selection.d.ts +56 -28
- package/dist/selection.js +103 -110
- package/dist/selection.js.map +1 -1
- package/dist/shell.cjs +36 -21
- package/dist/shell.cjs.map +1 -1
- package/dist/shell.d.cts +32 -17
- package/dist/shell.d.ts +32 -17
- package/dist/shell.js +36 -24
- package/dist/shell.js.map +1 -1
- package/dist/signature.cjs +29 -48
- package/dist/signature.cjs.map +1 -1
- package/dist/signature.d.cts +26 -26
- package/dist/signature.d.ts +26 -26
- package/dist/signature.js +30 -48
- package/dist/signature.js.map +1 -1
- package/dist/stage-BIG6aeVd.cjs +369 -0
- package/dist/stage-BIG6aeVd.cjs.map +1 -0
- package/dist/stage-ByFPD-Fv.d.cts +117 -0
- package/dist/stage-DaboaheR.d.ts +117 -0
- package/dist/stage-DoVekb83.js +302 -0
- package/dist/stage-DoVekb83.js.map +1 -0
- package/dist/stage-scope-CQgCuf1w.cjs +46 -0
- package/dist/stage-scope-CQgCuf1w.cjs.map +1 -0
- package/dist/stage-scope-D7ZeNduT.js +23 -0
- package/dist/stage-scope-D7ZeNduT.js.map +1 -0
- package/dist/stage.cjs +12 -235
- package/dist/stage.d.cts +2 -116
- package/dist/stage.d.ts +2 -116
- package/dist/stage.js +3 -228
- package/dist/stamp.cjs +41 -35
- package/dist/stamp.cjs.map +1 -1
- package/dist/stamp.d.cts +25 -18
- package/dist/stamp.d.ts +25 -18
- package/dist/stamp.js +40 -37
- package/dist/stamp.js.map +1 -1
- package/dist/state-0K1I5cM4.d.cts +29 -0
- package/dist/state-0K1I5cM4.d.ts +29 -0
- package/dist/state-CbgxhwLA.js +62 -0
- package/dist/state-CbgxhwLA.js.map +1 -0
- package/dist/state-CsfZy_Eq.cjs +73 -0
- package/dist/state-CsfZy_Eq.cjs.map +1 -0
- package/dist/svg-shapes-Dm1yTR1b.js +40 -0
- package/dist/svg-shapes-Dm1yTR1b.js.map +1 -0
- package/dist/svg-shapes-zo0GNkc9.cjs +53 -0
- package/dist/svg-shapes-zo0GNkc9.cjs.map +1 -0
- package/dist/toolbar.cjs +297 -295
- package/dist/toolbar.cjs.map +1 -1
- package/dist/toolbar.d.cts +58 -68
- package/dist/toolbar.d.ts +67 -77
- package/dist/toolbar.js +271 -293
- package/dist/toolbar.js.map +1 -1
- package/dist/view-manager.cjs +34 -0
- package/dist/view-manager.cjs.map +1 -0
- package/dist/view-manager.d.cts +21 -0
- package/dist/view-manager.d.ts +21 -0
- package/dist/view-manager.js +23 -0
- package/dist/view-manager.js.map +1 -0
- package/package.json +41 -38
- package/dist/AnnotationListSnapshot-D3mFut1e-C_GMpK25.d.cts +0 -546
- package/dist/AnnotationListSnapshot-D3mFut1e-C_GMpK25.d.ts +0 -546
- package/dist/annotation-BCED_p9H.d.cts +0 -160
- package/dist/annotation-anchors-CetwM1g7.js +0 -15
- package/dist/annotation-anchors-CetwM1g7.js.map +0 -1
- package/dist/annotation-anchors-DuYRBJLl.cjs +0 -26
- package/dist/annotation-anchors-DuYRBJLl.cjs.map +0 -1
- package/dist/annotation-hooks-BB5ZhPpf.cjs +0 -16
- package/dist/annotation-hooks-BB5ZhPpf.cjs.map +0 -1
- package/dist/annotation-hooks-Df8EmzDO.js +0 -11
- package/dist/annotation-hooks-Df8EmzDO.js.map +0 -1
- package/dist/annotation-k38KDlIZ.d.ts +0 -160
- package/dist/annotation-menu.cjs +0 -64
- package/dist/annotation-menu.cjs.map +0 -1
- package/dist/annotation-menu.d.cts +0 -33
- package/dist/annotation-menu.d.ts +0 -33
- package/dist/annotation-menu.js +0 -48
- package/dist/annotation-menu.js.map +0 -1
- package/dist/annotation.cjs.map +0 -1
- package/dist/annotation.js.map +0 -1
- package/dist/form-BVumS7up.cjs +0 -926
- package/dist/form-BVumS7up.cjs.map +0 -1
- package/dist/form-CK3mp99m.js +0 -895
- package/dist/form-CK3mp99m.js.map +0 -1
- package/dist/rolldown-runtime-BBjsoOtd.js +0 -27
- package/dist/runtime-DE76hlnl.d.ts +0 -216
- package/dist/runtime-umHA_xHt.d.cts +0 -216
- package/dist/runtime.cjs.map +0 -1
- package/dist/runtime.js.map +0 -1
- package/dist/scrollbar.cjs +0 -241
- package/dist/scrollbar.cjs.map +0 -1
- package/dist/scrollbar.d.cts +0 -42
- package/dist/scrollbar.d.ts +0 -42
- package/dist/scrollbar.js +0 -213
- package/dist/scrollbar.js.map +0 -1
- package/dist/stage.cjs.map +0 -1
- package/dist/stage.js.map +0 -1
- package/dist/views.cjs +0 -35
- package/dist/views.cjs.map +0 -1
- package/dist/views.d.cts +0 -21
- package/dist/views.d.ts +0 -21
- package/dist/views.js +0 -27
- package/dist/views.js.map +0 -1
package/dist/selection.js
CHANGED
|
@@ -1,24 +1,30 @@
|
|
|
1
|
-
import "./
|
|
2
|
-
import {
|
|
3
|
-
import { Anchored } from "./anchored.js";
|
|
1
|
+
import { D as useViewerSettings, E as useSelector, O as devWarn, S as useOptionalCapability, f as useCapability, p as useCapabilityEvent, u as shallowArray, w as usePage, x as useKernelValue } from "./runtime-B6xcPsEb.js";
|
|
2
|
+
import { n as stateHook, t as settingsHook } from "./state-CbgxhwLA.js";
|
|
3
|
+
import { Anchored, useOptionalProjectorBinding } from "./anchored.js";
|
|
4
|
+
import { n as useStageToken } from "./stage-scope-D7ZeNduT.js";
|
|
4
5
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
import { attachSelectionHandle, copySelection, mixAccent, paint, quadInPixels, samePageBounds, sameSelectionEndpoints, svgPoints, wireSelectionClipboard } from "@embedpdf/web";
|
|
5
7
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { textQuadEquals } from "@embedpdf/core-geometry";
|
|
9
|
-
import { HANDLE_BAR, HANDLE_HEAD, HANDLE_PAD, SelectionToken, createSelectionHandleDrag, selectionHandleGeom } from "@embedpdf/plugin-selection";
|
|
10
|
-
import { SelectionToken as SelectionToken$1 } from "@embedpdf/plugin-selection/internal";
|
|
8
|
+
import { HANDLE_BAR, HANDLE_HEAD, HANDLE_PAD, SelectionToken, armSelectionHandle, selectionHandleEndpointsOf, selectionHandleGeom, selectionHandleViewOf, selectionState } from "@embedpdf/plugin-selection";
|
|
9
|
+
import { SelectionToken as SelectionToken$1 } from "@embedpdf/plugin-selection/contract/host";
|
|
11
10
|
export * from "@embedpdf/plugin-selection";
|
|
12
11
|
//#region src/selection.tsx
|
|
13
|
-
|
|
12
|
+
/**
|
|
13
|
+
* The highlight of the selected text on one page, in the selection plugin's
|
|
14
|
+
* `color` setting; the `--epdf-text-selection` CSS variable wins over it.
|
|
15
|
+
*/
|
|
16
|
+
function SelectionLayer() {
|
|
14
17
|
const page = usePage();
|
|
15
18
|
const selection = useCapability(SelectionToken$1);
|
|
16
|
-
const segments = useSelector(SelectionToken$1, (
|
|
17
|
-
const visible = useSelector(SelectionToken$1, (
|
|
19
|
+
const segments = useSelector(SelectionToken$1, (selection) => selection.listSegments(page.ref), shallowArray);
|
|
20
|
+
const visible = useSelector(SelectionToken$1, (selection) => selection.isHighlightVisible());
|
|
21
|
+
const color = useSelectionSettings((settings) => settings.color);
|
|
22
|
+
const accent = useViewerSettings((settings) => settings.accent);
|
|
18
23
|
useEffect(() => {
|
|
19
|
-
selection.
|
|
20
|
-
}, [selection, page.
|
|
24
|
+
selection.ensureLoaded(page.ref);
|
|
25
|
+
}, [selection, page.ref]);
|
|
21
26
|
if (!visible) return null;
|
|
27
|
+
const fill = paint("text-selection", color ?? mixAccent("text-selection", accent));
|
|
22
28
|
return /* @__PURE__ */ jsx("svg", {
|
|
23
29
|
style: {
|
|
24
30
|
position: "absolute",
|
|
@@ -28,47 +34,45 @@ function SelectionLayer({ color = "rgba(33, 150, 243, 0.35)" }) {
|
|
|
28
34
|
overflow: "visible",
|
|
29
35
|
pointerEvents: "none"
|
|
30
36
|
},
|
|
31
|
-
children: segments.map((
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
s.quad.upperEnd,
|
|
36
|
-
s.quad.lowerEnd,
|
|
37
|
-
s.quad.lowerStart
|
|
38
|
-
].map((p) => page.transform.toPixels(p)).map((p) => `${p.x},${p.y}`).join(" "),
|
|
39
|
-
fill: color
|
|
40
|
-
}, i);
|
|
41
|
-
})
|
|
37
|
+
children: segments.map((segment, i) => /* @__PURE__ */ jsx("polygon", {
|
|
38
|
+
points: svgPoints(quadInPixels(segment.quad, page.transform)),
|
|
39
|
+
style: { fill }
|
|
40
|
+
}, i))
|
|
42
41
|
});
|
|
43
42
|
}
|
|
44
|
-
/** The
|
|
43
|
+
/** The public selection capability (select(), readText(), canCopy(), …) for
|
|
45
44
|
* app chrome — toolbars, context menus, automation. */
|
|
46
45
|
function useSelection() {
|
|
47
46
|
return useCapability(SelectionToken);
|
|
48
47
|
}
|
|
49
|
-
/**
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
48
|
+
/** Subscribe to one selection event for the mounted lifetime: `useSelectionEvent((selection) => selection.onCommitted, handler)`. */
|
|
49
|
+
function useSelectionEvent(select, handler) {
|
|
50
|
+
useCapabilityEvent(SelectionToken, select, handler);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The selection's state: whether anything is selected, whether the user is
|
|
54
|
+
* still selecting, the character range and the pages it's on (the page's State
|
|
55
|
+
* table, declared once in `selectionState`). Takes a selector, re-renders only
|
|
56
|
+
* when what it returns changes, and returns the empty state without a document.
|
|
57
|
+
*/
|
|
58
|
+
const useSelectionState = stateHook(selectionState);
|
|
59
|
+
/** The selection settings (`dragThreshold`, `color`, `handles`), with or without a document. Takes a selector. */
|
|
60
|
+
const useSelectionSettings = settingsHook(SelectionToken);
|
|
57
61
|
/**
|
|
58
|
-
* Floats over the current
|
|
62
|
+
* Floats over the current text selection (one anchor regardless of
|
|
59
63
|
* cross-page selection; it rides the gesture's end page) — and only once the
|
|
60
|
-
* selection
|
|
64
|
+
* selection settles: hidden while a gesture is active (mid-drag), it appears at
|
|
61
65
|
* pointer-up; programmatic selections show immediately (born settled). Works
|
|
62
66
|
* under `<Stage>` (mount in the overlay slot) and `<PageView>` alike — the
|
|
63
67
|
* surface provides the projection. Compose the contents from hooks:
|
|
64
68
|
* `useSelection()` for copy/clear, `useAnnotation()` for
|
|
65
69
|
* `markupFromSelection('highlight')`, `copySelection` for the clipboard.
|
|
66
70
|
* For live-follow UI during the drag, compose `<Anchored>` with
|
|
67
|
-
* `
|
|
71
|
+
* `getAnchor()` yourself — the primitive carries no policy.
|
|
68
72
|
*/
|
|
69
73
|
function SelectionMenu({ children, gap = 8, placement = "top" }) {
|
|
70
|
-
const selecting =
|
|
71
|
-
const anchor = useSelector(SelectionToken, (
|
|
74
|
+
const selecting = useSelectionState((state) => state.isSelecting);
|
|
75
|
+
const anchor = useSelector(SelectionToken, (selection) => selection.getAnchor(), samePageBounds);
|
|
72
76
|
if (selecting || !anchor) return null;
|
|
73
77
|
return /* @__PURE__ */ jsx(Anchored, {
|
|
74
78
|
anchor,
|
|
@@ -77,60 +81,37 @@ function SelectionMenu({ children, gap = 8, placement = "top" }) {
|
|
|
77
81
|
children
|
|
78
82
|
});
|
|
79
83
|
}
|
|
80
|
-
const sameEndpoints = (a, b) => {
|
|
81
|
-
if (a === b) return true;
|
|
82
|
-
if (!a || !b) return false;
|
|
83
|
-
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 && textQuadEquals(a.start.glyphQuad, b.start.glyphQuad) && textQuadEquals(a.end.glyphQuad, b.end.glyphQuad);
|
|
84
|
-
};
|
|
85
|
-
/** The plugin's structural view over this Stage: point-exact projection in,
|
|
86
|
-
* page resolution out. (`pageRectToScreen` is the AABB projector — upright
|
|
87
|
-
* overlays only — and would collapse exactly the orientation handles need.) */
|
|
88
|
-
const handleView = (stage) => ({
|
|
89
|
-
toOverlay: (pon, pt) => {
|
|
90
|
-
const world = stage.pageToWorld(pon, pt);
|
|
91
|
-
return world ? stage.toScreen(world) : null;
|
|
92
|
-
},
|
|
93
|
-
pageAt: (overlay) => stage.pageAt(overlay),
|
|
94
|
-
pointOnPage: (pon, overlay) => stage.pointOnPage(pon, overlay)
|
|
95
|
-
});
|
|
96
84
|
/**
|
|
97
85
|
* iOS-style draggable selection handles. Each is drawn the way the platform
|
|
98
|
-
* draws them: a thin caret
|
|
86
|
+
* draws them: a thin caret bar that forms the selection's own start/end edge
|
|
99
87
|
* — the boundary glyph's oriented edge, so rotated text and rotated pages
|
|
100
88
|
* carry the handle with them — capped by a screen-constant circle, above the
|
|
101
89
|
* first line at the start, below the last line at the end.
|
|
102
90
|
*
|
|
103
|
-
* On touch, where a caret drag isn't available, the handles
|
|
91
|
+
* On touch, where a caret drag isn't available, the handles are the way to
|
|
104
92
|
* grow or shrink a selection: a long-press selects a word, then each handle
|
|
105
|
-
* extends from the
|
|
106
|
-
* exactly like a pointer drag — it rides the same `
|
|
93
|
+
* extends from the opposite endpoint, snapping to glyphs and crossing pages
|
|
94
|
+
* exactly like a pointer drag — it rides the same `beginGestureAt`/`extendTo`
|
|
107
95
|
* gesture path, so highlights, menus, and commit signals all behave
|
|
108
96
|
* identically. Mount in the `<Stage>` overlay slot next to `<SelectionMenu>`;
|
|
109
97
|
* outside a Stage it renders nothing (a `PageView` has no camera to project
|
|
110
98
|
* through). Pointer-isolated, so grabbing a handle never pans the stage.
|
|
99
|
+
* Painted in the plugin's `handles` setting, which the
|
|
100
|
+
* `--epdf-text-selection-handle` and `--epdf-text-selection-handle-shadow` CSS
|
|
101
|
+
* variables override.
|
|
111
102
|
*/
|
|
112
|
-
function SelectionHandles({
|
|
103
|
+
function SelectionHandles({ token: explicitToken } = {}) {
|
|
104
|
+
const token = useStageToken(explicitToken);
|
|
113
105
|
const host = useCapability(SelectionToken$1);
|
|
114
106
|
const stage = useOptionalCapability(token);
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
glyphQuad: s.start.glyphQuad,
|
|
124
|
-
advance: s.start.advance
|
|
125
|
-
},
|
|
126
|
-
end: {
|
|
127
|
-
pon: s.end.pon,
|
|
128
|
-
glyphQuad: s.end.glyphQuad,
|
|
129
|
-
advance: s.end.advance
|
|
130
|
-
}
|
|
131
|
-
};
|
|
132
|
-
}, sameEndpoints);
|
|
133
|
-
useKernelValue(() => stage?.visiblePages() ?? null);
|
|
107
|
+
const surface = useOptionalProjectorBinding();
|
|
108
|
+
if (!surface || surface.projector.space !== "overlay") devWarn("selection-handles-outside-stage", "<SelectionHandles> renders nothing here: mount it in the <Stage> overlay slot (a <PageView> has no camera to project the handles through).");
|
|
109
|
+
const selecting = useSelectionState((state) => state.isSelecting);
|
|
110
|
+
const visible = useSelector(SelectionToken$1, (selection) => selection.isHighlightVisible());
|
|
111
|
+
const handles = useSelectionSettings((settings) => settings.handles);
|
|
112
|
+
const accent = useViewerSettings((settings) => settings.accent);
|
|
113
|
+
const endpoints = useSelector(SelectionToken$1, (selection) => selectionHandleEndpointsOf(selection.getSnapshot()), sameSelectionEndpoints);
|
|
114
|
+
useKernelValue(() => stage?.listVisiblePages() ?? null);
|
|
134
115
|
const [dragging, setDragging] = useState(null);
|
|
135
116
|
const armSource = useRef(null);
|
|
136
117
|
armSource.current = stage && endpoints ? {
|
|
@@ -139,28 +120,22 @@ function SelectionHandles({ color = "#2196f3", token = StageToken }) {
|
|
|
139
120
|
} : null;
|
|
140
121
|
const bindHandle = useMemo(() => {
|
|
141
122
|
const detach = {};
|
|
142
|
-
const make = (role) => (
|
|
123
|
+
const make = (role) => (element) => {
|
|
143
124
|
detach[role]?.();
|
|
144
125
|
delete detach[role];
|
|
145
|
-
if (!
|
|
146
|
-
detach[role] = attachSelectionHandle(
|
|
126
|
+
if (!element) return;
|
|
127
|
+
detach[role] = attachSelectionHandle(element, { arm: () => {
|
|
147
128
|
const src = armSource.current;
|
|
148
129
|
if (!src) return null;
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
if (!geom) return null;
|
|
152
|
-
const opposite = src.endpoints[role === "start" ? "end" : "start"];
|
|
153
|
-
const drag = createSelectionHandleDrag(host, view, opposite, src.endpoints[role].pon);
|
|
130
|
+
const armed = armSelectionHandle(host, selectionHandleViewOf(src.stage), src.endpoints, role);
|
|
131
|
+
if (!armed) return null;
|
|
154
132
|
setDragging(role);
|
|
155
133
|
return {
|
|
156
|
-
base:
|
|
157
|
-
x: (geom.bar.from.x + geom.bar.to.x) / 2,
|
|
158
|
-
y: (geom.bar.from.y + geom.bar.to.y) / 2
|
|
159
|
-
},
|
|
134
|
+
base: armed.base,
|
|
160
135
|
session: {
|
|
161
|
-
move: drag.move,
|
|
136
|
+
move: armed.drag.move,
|
|
162
137
|
end: () => {
|
|
163
|
-
drag.end();
|
|
138
|
+
armed.drag.end();
|
|
164
139
|
setDragging(null);
|
|
165
140
|
}
|
|
166
141
|
}
|
|
@@ -174,22 +149,24 @@ function SelectionHandles({ color = "#2196f3", token = StageToken }) {
|
|
|
174
149
|
}, [host]);
|
|
175
150
|
if (!stage || !endpoints || !visible) return null;
|
|
176
151
|
if (selecting && !dragging) return null;
|
|
177
|
-
const view =
|
|
152
|
+
const view = selectionHandleViewOf(stage);
|
|
153
|
+
const color = paint("text-selection-handle", handles.color ?? accent);
|
|
154
|
+
const shadow = paint("text-selection-handle-shadow", handles.shadow);
|
|
178
155
|
const renderHandle = (role) => {
|
|
179
|
-
const
|
|
180
|
-
if (!
|
|
156
|
+
const geometry = selectionHandleGeom(view, endpoints[role], role);
|
|
157
|
+
if (!geometry) return null;
|
|
181
158
|
const barTop = HANDLE_PAD + (role === "start" ? HANDLE_HEAD : 0);
|
|
182
159
|
const pivotX = HANDLE_PAD + HANDLE_BAR / 2;
|
|
183
160
|
return /* @__PURE__ */ jsxs("div", {
|
|
184
161
|
ref: bindHandle[role],
|
|
185
162
|
style: {
|
|
186
163
|
position: "absolute",
|
|
187
|
-
left:
|
|
188
|
-
top:
|
|
164
|
+
left: geometry.bar.from.x - pivotX,
|
|
165
|
+
top: geometry.bar.from.y - barTop,
|
|
189
166
|
width: HANDLE_BAR + 2 * HANDLE_PAD,
|
|
190
|
-
height:
|
|
191
|
-
...
|
|
192
|
-
transform: `rotate(${
|
|
167
|
+
height: geometry.length + HANDLE_HEAD + 2 * HANDLE_PAD,
|
|
168
|
+
...geometry.upright ? null : {
|
|
169
|
+
transform: `rotate(${geometry.rotation}deg)`,
|
|
193
170
|
transformOrigin: `${pivotX}px ${barTop}px`
|
|
194
171
|
},
|
|
195
172
|
touchAction: "none",
|
|
@@ -201,36 +178,52 @@ function SelectionHandles({ color = "#2196f3", token = StageToken }) {
|
|
|
201
178
|
left: HANDLE_PAD,
|
|
202
179
|
top: barTop,
|
|
203
180
|
width: HANDLE_BAR,
|
|
204
|
-
height:
|
|
181
|
+
height: geometry.length,
|
|
205
182
|
background: color,
|
|
206
183
|
borderRadius: HANDLE_BAR / 2
|
|
207
184
|
} }), /* @__PURE__ */ jsx("div", { style: {
|
|
208
185
|
position: "absolute",
|
|
209
186
|
left: pivotX - HANDLE_HEAD / 2,
|
|
210
|
-
top: role === "start" ? HANDLE_PAD : HANDLE_PAD +
|
|
187
|
+
top: role === "start" ? HANDLE_PAD : HANDLE_PAD + geometry.length,
|
|
211
188
|
width: HANDLE_HEAD,
|
|
212
189
|
height: HANDLE_HEAD,
|
|
213
190
|
borderRadius: "50%",
|
|
214
191
|
background: color,
|
|
215
|
-
boxShadow:
|
|
192
|
+
boxShadow: shadow
|
|
216
193
|
} })]
|
|
217
194
|
}, role);
|
|
218
195
|
};
|
|
219
196
|
return /* @__PURE__ */ jsxs(Fragment, { children: [renderHandle("start"), renderHandle("end")] });
|
|
220
197
|
}
|
|
221
198
|
/**
|
|
222
|
-
* Mount
|
|
199
|
+
* Mount once per viewer to wire clipboard copy: prefetches the selected text
|
|
223
200
|
* when the selection settles, answers the native `copy` event synchronously,
|
|
224
201
|
* and falls back to the async Clipboard API for ctrl/cmd+C when the page has
|
|
225
|
-
* no DOM selection.
|
|
226
|
-
* `
|
|
202
|
+
* no DOM selection. A selection the user may not copy isn't copied, and
|
|
203
|
+
* `onRefused` is called. Renders nothing, and wires nothing until a document
|
|
204
|
+
* is ready. For a toolbar Copy button, call `copySelection(useSelection())`
|
|
205
|
+
* from its click handler instead.
|
|
227
206
|
*/
|
|
228
|
-
function SelectionClipboard({ prefetch } = {}) {
|
|
229
|
-
const selection =
|
|
230
|
-
|
|
207
|
+
function SelectionClipboard({ prefetch, onRefused, target } = {}) {
|
|
208
|
+
const selection = useOptionalCapability(SelectionToken);
|
|
209
|
+
const refused = useRef(onRefused);
|
|
210
|
+
refused.current = onRefused;
|
|
211
|
+
useEffect(() => {
|
|
212
|
+
const element = target ? target.current : void 0;
|
|
213
|
+
if (!selection || element === null) return;
|
|
214
|
+
return wireSelectionClipboard(selection, {
|
|
215
|
+
...prefetch === void 0 ? {} : { prefetch },
|
|
216
|
+
...element ? { target: element } : {},
|
|
217
|
+
onRefused: () => refused.current?.()
|
|
218
|
+
});
|
|
219
|
+
}, [
|
|
220
|
+
selection,
|
|
221
|
+
prefetch,
|
|
222
|
+
target
|
|
223
|
+
]);
|
|
231
224
|
return null;
|
|
232
225
|
}
|
|
233
226
|
//#endregion
|
|
234
|
-
export { SelectionClipboard, SelectionHandles, SelectionLayer, SelectionMenu, copySelection, useSelection };
|
|
227
|
+
export { SelectionClipboard, SelectionHandles, SelectionLayer, SelectionMenu, copySelection, useSelection, useSelectionEvent, useSelectionSettings, useSelectionState };
|
|
235
228
|
|
|
236
229
|
//# sourceMappingURL=selection.js.map
|
package/dist/selection.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"selection.js","names":["SelectionHostToken"],"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/contract';\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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"}
|
|
1
|
+
{"version":3,"file":"selection.js","names":["SelectionHostToken"],"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 page-space highlight rects from the capability, and paints them —\n * mapping each rect through PageContext.toPixels (the same path markers use),\n * in the plugin's `color` setting, which the `--epdf-text-selection` CSS\n * variable overrides. Zero pointer handling here; that's the\n * PagePointerSource + the hub.\n *\n * The layer resolves the host lens (`/contract/host`: 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, type RefObject } from 'react';\nimport type { CapabilityToken, EventHook } from '@embedpdf/core';\nimport {\n HANDLE_BAR,\n HANDLE_HEAD,\n HANDLE_PAD,\n SelectionToken,\n armSelectionHandle,\n selectionHandleEndpointsOf,\n selectionHandleGeom,\n selectionHandleViewOf,\n selectionState,\n type SelectionHandleEndpoints,\n type SelectionCapability,\n} from '@embedpdf/plugin-selection';\nimport { SelectionToken as SelectionHostToken } from '@embedpdf/plugin-selection/contract/host';\nimport type { StageCapability } from '@embedpdf/plugin-stage/contract';\nimport {\n attachSelectionHandle,\n mixAccent,\n paint,\n quadInPixels,\n samePageBounds,\n sameSelectionEndpoints,\n svgPoints,\n wireSelectionClipboard,\n type SelectionClipboardOptions,\n} from '@embedpdf/web';\nimport { Anchored, useOptionalProjectorBinding, type AnchoredPlacement } from './anchored';\nimport { devWarn } from './dev';\nimport { useStageToken } from './stage-scope';\nimport {\n shallowArray,\n useCapability,\n useCapabilityEvent,\n useKernelValue,\n useOptionalCapability,\n usePage,\n useSelector,\n useViewerSettings,\n} from './runtime';\nimport { settingsHook, stateHook } from './state';\n\n/**\n * The highlight of the selected text on one page, in the selection plugin's\n * `color` setting; the `--epdf-text-selection` CSS variable wins over it.\n */\nexport function SelectionLayer() {\n const page = usePage();\n const selection = useCapability(SelectionHostToken);\n const segments = useSelector(\n SelectionHostToken,\n (selection) => selection.listSegments(page.ref),\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, (selection) => selection.isHighlightVisible());\n const color = useSelectionSettings((settings) => settings.color);\n // An unset color follows the viewer's accent, behind the `--epdf-accent` variable.\n const accent = useViewerSettings((settings) => settings.accent);\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 void selection.ensureLoaded(page.ref);\n }, [selection, page.ref]);\n\n if (!visible) return null;\n\n // Unset, the color is the accent at 35%, the accent variables included.\n const fill = paint('text-selection', color ?? mixAccent('text-selection', accent));\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((segment, i) => (\n // Page space → the page layer's pixels, which ride the page's CSS\n // rotation: mapping the four corners is exact, for turned text too.\n <polygon\n key={i}\n points={svgPoints(quadInPixels(segment.quad, page.transform))}\n // In `style`: an SVG attribute doesn't read var().\n style={{ fill }}\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/** Subscribe to one selection event for the mounted lifetime: `useSelectionEvent((selection) => selection.onCommitted, handler)`. */\nexport function useSelectionEvent<T>(\n select: (selection: SelectionCapability) => EventHook<T>,\n handler: (event: T) => void,\n): void {\n useCapabilityEvent(SelectionToken, select, handler);\n}\n\n/**\n * The selection's state: whether anything is selected, whether the user is\n * still selecting, the character range and the pages it's on (the page's State\n * table, declared once in `selectionState`). Takes a selector, re-renders only\n * when what it returns changes, and returns the empty state without a document.\n */\nexport const useSelectionState = stateHook(selectionState);\n\n/** The selection settings (`dragThreshold`, `color`, `handles`), with or without a document. Takes a selector. */\nexport const useSelectionSettings = settingsHook(SelectionToken);\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 a gesture is active (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 * `getAnchor()` yourself — the primitive carries no policy.\n */\nexport function SelectionMenu({ children, gap = 8, placement = 'top' }: SelectionMenuProps) {\n const selecting = useSelectionState((state) => state.isSelecting);\n // The capability returns a fresh anchor on every read: compare by value, so\n // the menu re-renders only when the selection moved.\n const anchor = useSelector(SelectionToken, (selection) => selection.getAnchor(), samePageBounds);\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 view over the\n// Stage, the endpoints, the geometry and the armed drag are\n// `@embedpdf/plugin-selection`'s pure `handles` module (one source for every\n// 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\nexport interface SelectionHandlesProps {\n /** The stage lens hosting this overlay (default: the enclosing `<Stage>`'s lens). */\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 `beginGestureAt`/`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 * Painted in the plugin's `handles` setting, which the\n * `--epdf-text-selection-handle` and `--epdf-text-selection-handle-shadow` CSS\n * variables override.\n */\nexport function SelectionHandles({ token: explicitToken }: SelectionHandlesProps = {}) {\n const token = useStageToken(explicitToken);\n const host = useCapability(SelectionHostToken);\n const stage = useOptionalCapability(token);\n // Outside a Stage there is no camera to project through: say so instead of\n // rendering nothing silently.\n const surface = useOptionalProjectorBinding();\n if (!surface || surface.projector.space !== 'overlay') {\n devWarn(\n 'selection-handles-outside-stage',\n '<SelectionHandles> renders nothing here: mount it in the <Stage> overlay slot ' +\n '(a <PageView> has no camera to project the handles through).',\n );\n }\n const selecting = useSelectionState((state) => state.isSelecting);\n const visible = useSelector(SelectionHostToken, (selection) => selection.isHighlightVisible());\n const handles = useSelectionSettings((settings) => settings.handles);\n const accent = useViewerSettings((settings) => settings.accent);\n const endpoints = useSelector(\n SelectionHostToken,\n (selection) => selectionHandleEndpointsOf(selection.getSnapshot()),\n sameSelectionEndpoints,\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?.listVisiblePages() ?? 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: SelectionHandleEndpoints } | null>(\n null,\n );\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') => (element: HTMLDivElement | null) => {\n detach[role]?.();\n delete detach[role];\n if (!element) return;\n detach[role] = attachSelectionHandle(element, {\n arm: () => {\n const src = armSource.current;\n if (!src) return null;\n const armed = armSelectionHandle(\n host,\n selectionHandleViewOf(src.stage),\n src.endpoints,\n role,\n );\n if (!armed) return null;\n setDragging(role);\n return {\n // the point the user grabbed: the bar's midpoint\n base: armed.base,\n session: {\n move: armed.drag.move,\n end: () => {\n armed.drag.end(); // settle → menu reappears, onCommitted 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 = selectionHandleViewOf(stage);\n // Each is its CSS variable first, then the setting; an unset color is the accent.\n const color = paint('text-selection-handle', handles.color ?? accent);\n const shadow = paint('text-selection-handle-shadow', handles.shadow);\n\n const renderHandle = (role: 'start' | 'end') => {\n const geometry = selectionHandleGeom(view, endpoints[role], role);\n if (!geometry) 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: geometry.bar.from.x - pivotX,\n top: geometry.bar.from.y - barTop,\n width: HANDLE_BAR + 2 * HANDLE_PAD,\n height: geometry.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 ...(geometry.upright\n ? null\n : {\n transform: `rotate(${geometry.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: geometry.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 + geometry.length,\n width: HANDLE_HEAD,\n height: HANDLE_HEAD,\n borderRadius: '50%',\n background: color,\n boxShadow: shadow,\n }}\n />\n </div>\n );\n };\n\n return (\n <>\n {renderHandle('start')}\n {renderHandle('end')}\n </>\n );\n}\n\nexport interface SelectionClipboardProps\n extends Pick<SelectionClipboardOptions, 'prefetch' | 'onRefused'> {\n /**\n * Copy only while focus is inside this element, for a page with more than one viewer.\n * Default: anywhere on the page.\n */\n target?: RefObject<HTMLElement | null>;\n}\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. A selection the user may not copy isn't copied, and\n * `onRefused` is called. Renders nothing, and wires nothing until a document\n * is ready. For a toolbar Copy button, call `copySelection(useSelection())`\n * from its click handler instead.\n */\nexport function SelectionClipboard({ prefetch, onRefused, target }: SelectionClipboardProps = {}) {\n const selection = useOptionalCapability(SelectionToken);\n // The latest callback, so a new function each render doesn't wire the clipboard again.\n const refused = useRef(onRefused);\n refused.current = onRefused;\n useEffect(() => {\n // The element mounts before this effect runs, so its ref is set by now.\n const element = target ? target.current : undefined;\n if (!selection || element === null) return;\n return wireSelectionClipboard(selection, {\n ...(prefetch === undefined ? {} : { prefetch }),\n ...(element ? { target: element } : {}),\n onRefused: () => refused.current?.(),\n });\n }, [selection, prefetch, target]);\n return null;\n}\n"],"mappings":"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"}
|
package/dist/shell.cjs
CHANGED
|
@@ -1,25 +1,37 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
require("./rolldown-runtime-DDXJien1.cjs");
|
|
3
|
-
const require_runtime = require("./runtime.cjs");
|
|
3
|
+
const require_runtime = require("./runtime-fzpGv78v.cjs");
|
|
4
|
+
const require_state = require("./state-CsfZy_Eq.cjs");
|
|
5
|
+
let _embedpdf_web = require("@embedpdf/web");
|
|
4
6
|
let react = require("react");
|
|
5
7
|
let _embedpdf_plugin_shell = require("@embedpdf/plugin-shell");
|
|
6
8
|
//#region src/shell.ts
|
|
7
|
-
/** The
|
|
9
|
+
/** The shell capability: open and close panels, dialogs and menus. Without a document its methods throw `not-ready`. */
|
|
8
10
|
function useShell() {
|
|
9
11
|
return require_runtime.useCapability(_embedpdf_plugin_shell.ShellToken);
|
|
10
12
|
}
|
|
11
|
-
/**
|
|
12
|
-
|
|
13
|
+
/** Subscribe to one shell event for the mounted lifetime: `useShellEvent((shell) => shell.onSurfaceOpened, handler)`. */
|
|
14
|
+
function useShellEvent(select, handler) {
|
|
15
|
+
require_runtime.useCapabilityEvent(_embedpdf_plugin_shell.ShellToken, select, handler);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Which panels, dialogs and menus are open (the page's State table, declared
|
|
19
|
+
* once in `shellState`). Takes a selector, and re-renders only when what it
|
|
20
|
+
* returns changes. Without a document nothing is open.
|
|
21
|
+
*/
|
|
22
|
+
const useShellState = require_state.stateHook(_embedpdf_plugin_shell.shellState);
|
|
23
|
+
const NO_PROPS = Object.freeze({});
|
|
24
|
+
/** One named surface (panel, dialog, overlay) of this subtree's document. Reads as closed, and its verbs do nothing, while no document is open. */
|
|
13
25
|
function useSurface(id) {
|
|
14
26
|
const shell = require_runtime.useOptionalCapability(_embedpdf_plugin_shell.ShellToken);
|
|
15
|
-
const isOpen = require_runtime.useOptionalSelector(_embedpdf_plugin_shell.ShellToken, (
|
|
16
|
-
const props = require_runtime.useOptionalSelector(_embedpdf_plugin_shell.ShellToken, (
|
|
27
|
+
const isOpen = require_runtime.useOptionalSelector(_embedpdf_plugin_shell.ShellToken, (shell) => shell.isOpen(id), false);
|
|
28
|
+
const props = require_runtime.useOptionalSelector(_embedpdf_plugin_shell.ShellToken, (shell) => shell.getSurface(id)?.props ?? NO_PROPS, NO_PROPS);
|
|
17
29
|
return (0, react.useMemo)(() => ({
|
|
18
30
|
isOpen,
|
|
19
31
|
props,
|
|
20
|
-
open: (
|
|
32
|
+
open: (options) => shell?.open(id, options),
|
|
21
33
|
close: () => shell?.close(id),
|
|
22
|
-
toggle: (
|
|
34
|
+
toggle: (options) => shell?.toggle(id, options)
|
|
23
35
|
}), [
|
|
24
36
|
shell,
|
|
25
37
|
id,
|
|
@@ -27,24 +39,27 @@ function useSurface(id) {
|
|
|
27
39
|
props
|
|
28
40
|
]);
|
|
29
41
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Put the cursor in `ref`'s element when the surface `id` asks for it: on every
|
|
44
|
+
* `open(id, { focus: true })`, a command whose panel says `focus` among them, and
|
|
45
|
+
* when the element mounts while the request is up (the panel opened, or its code
|
|
46
|
+
* loaded, because of it). A plain open never moves it. `select` selects an input's
|
|
47
|
+
* text, so typing replaces it. Reads the surface of this subtree's document; use
|
|
48
|
+
* it in the component that renders the element.
|
|
49
|
+
*/
|
|
50
|
+
function useSurfaceFocus(id, ref, options = {}) {
|
|
34
51
|
const shell = require_runtime.useOptionalCapability(_embedpdf_plugin_shell.ShellToken);
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
toggle: (id) => shell?.toggleMenu(id),
|
|
40
|
-
close: (id) => shell?.closeMenu(id),
|
|
41
|
-
closeAll: () => shell?.closeAllMenus()
|
|
42
|
-
}), [shell, open]);
|
|
52
|
+
const request = require_runtime.useOptionalSelector(_embedpdf_plugin_shell.ShellToken, (current) => current.getSurface(id)?.focusRequest ?? 0, 0);
|
|
53
|
+
const [follower] = (0, react.useState)(_embedpdf_web.createSurfaceFocusFollower);
|
|
54
|
+
const select = options.select ?? false;
|
|
55
|
+
(0, react.useEffect)(() => follower.follow(ref.current, shell, request, { select }));
|
|
43
56
|
}
|
|
44
57
|
//#endregion
|
|
45
|
-
exports.useMenus = useMenus;
|
|
46
58
|
exports.useShell = useShell;
|
|
59
|
+
exports.useShellEvent = useShellEvent;
|
|
60
|
+
exports.useShellState = useShellState;
|
|
47
61
|
exports.useSurface = useSurface;
|
|
62
|
+
exports.useSurfaceFocus = useSurfaceFocus;
|
|
48
63
|
Object.keys(_embedpdf_plugin_shell).forEach(function(k) {
|
|
49
64
|
if (k !== "default" && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
50
65
|
enumerable: true,
|
package/dist/shell.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shell.cjs","names":["useCapability","ShellToken","useOptionalCapability","useOptionalSelector"],"sources":["../src/shell.ts"],"sourcesContent":["/**\n * The React surface for @embedpdf/plugin-shell. The app owns every\n * surface's DOM; these hooks bind its open/closed state to the kernel so\n * commands can drive it and applications can restore it.\n *\n *
|
|
1
|
+
{"version":3,"file":"shell.cjs","names":["useCapability","ShellToken","stateHook","shellState","useOptionalCapability","useOptionalSelector","createSurfaceFocusFollower"],"sources":["../src/shell.ts"],"sourcesContent":["/**\n * The React surface for @embedpdf/plugin-shell. The app owns every\n * surface's DOM; these hooks bind its open/closed state to the kernel so\n * commands can drive it and applications can restore it.\n *\n * The state hooks work without a document: shell state is document-scoped,\n * and chrome that uses it (panel buttons, menu anchors) stays mounted across\n * the empty workspace. With no document every surface reads as closed, no menu\n * as open, and `useSurface`'s verbs do nothing. `useShell()` then returns the\n * stand-in, whose methods throw `not-ready`.\n */\n\n// One-line-per-feature: registration travels with the UI.\nexport * from '@embedpdf/plugin-shell';\nimport { useEffect, useMemo, useState, type RefObject } from 'react';\nimport { ShellToken, shellState } from '@embedpdf/plugin-shell';\nimport type { OpenSurfaceOptions, ShellCapability, SurfaceProps } from '@embedpdf/plugin-shell';\nimport type { EventHook } from '@embedpdf/core';\nimport { createSurfaceFocusFollower, type SurfaceFocusOptions } from '@embedpdf/web';\nimport {\n useCapability,\n useCapabilityEvent,\n useOptionalCapability,\n useOptionalSelector,\n} from './runtime';\nimport { stateHook } from './state';\n\n/** The shell capability: open and close panels, dialogs and menus. Without a document its methods throw `not-ready`. */\nexport function useShell(): ShellCapability {\n return useCapability(ShellToken);\n}\n\n/** Subscribe to one shell event for the mounted lifetime: `useShellEvent((shell) => shell.onSurfaceOpened, handler)`. */\nexport function useShellEvent<T>(\n select: (shell: ShellCapability) => EventHook<T>,\n handler: (event: T) => void,\n): void {\n useCapabilityEvent(ShellToken, select, handler);\n}\n\n/**\n * Which panels, dialogs and menus are open (the page's State table, declared\n * once in `shellState`). Takes a selector, and re-renders only when what it\n * returns changes. Without a document nothing is open.\n */\nexport const useShellState = stateHook(shellState);\n\nexport interface SurfaceHandle {\n readonly isOpen: boolean;\n /** The props the surface was opened with, or updated to; empty when it has none. */\n readonly props: SurfaceProps;\n open(options?: OpenSurfaceOptions): void;\n close(): void;\n toggle(options?: OpenSurfaceOptions): void;\n}\n\nconst NO_PROPS: SurfaceProps = Object.freeze({});\n\n/** One named surface (panel, dialog, overlay) of this subtree's document. Reads as closed, and its verbs do nothing, while no document is open. */\nexport function useSurface(id: string): SurfaceHandle {\n const shell = useOptionalCapability(ShellToken);\n const isOpen = useOptionalSelector(ShellToken, (shell) => shell.isOpen(id), false);\n const props = useOptionalSelector(\n ShellToken,\n (shell) => shell.getSurface(id)?.props ?? NO_PROPS,\n NO_PROPS,\n );\n return useMemo(\n () => ({\n isOpen,\n props,\n open: (options?: OpenSurfaceOptions) => shell?.open(id, options),\n close: () => shell?.close(id),\n toggle: (options?: OpenSurfaceOptions) => shell?.toggle(id, options),\n }),\n [shell, id, isOpen, props],\n );\n}\n\nexport type { SurfaceFocusOptions };\n\n/**\n * Put the cursor in `ref`'s element when the surface `id` asks for it: on every\n * `open(id, { focus: true })`, a command whose panel says `focus` among them, and\n * when the element mounts while the request is up (the panel opened, or its code\n * loaded, because of it). A plain open never moves it. `select` selects an input's\n * text, so typing replaces it. Reads the surface of this subtree's document; use\n * it in the component that renders the element.\n */\nexport function useSurfaceFocus(\n id: string,\n ref: RefObject<HTMLElement | null>,\n options: SurfaceFocusOptions = {},\n): void {\n const shell = useOptionalCapability(ShellToken);\n const request = useOptionalSelector(\n ShellToken,\n (current) => current.getSurface(id)?.focusRequest ?? 0,\n 0,\n );\n const [follower] = useState(createSurfaceFocusFollower);\n const select = options.select ?? false;\n // After every render, so an element that has just appeared is seen, and after the layout\n // effects, so a panel's frame has placed and shown it (a sheet's layer is hidden until then).\n useEffect(() => follower.follow(ref.current, shell, request, { select }));\n}\n"],"mappings":";;;;;;;;;AA4BA,SAAgB,WAA4B;CAC1C,OAAOA,gBAAAA,cAAcC,uBAAAA,UAAU;AACjC;;AAGA,SAAgB,cACd,QACA,SACM;CACN,gBAAA,mBAAmBA,uBAAAA,YAAY,QAAQ,OAAO;AAChD;;;;;;AAOA,MAAa,gBAAgBC,cAAAA,UAAUC,uBAAAA,UAAU;AAWjD,MAAM,WAAyB,OAAO,OAAO,CAAC,CAAC;;AAG/C,SAAgB,WAAW,IAA2B;CACpD,MAAM,QAAQC,gBAAAA,sBAAsBH,uBAAAA,UAAU;CAC9C,MAAM,SAASI,gBAAAA,oBAAoBJ,uBAAAA,aAAa,UAAU,MAAM,OAAO,EAAE,GAAG,KAAK;CACjF,MAAM,QAAQI,gBAAAA,oBACZJ,uBAAAA,aACC,UAAU,MAAM,WAAW,EAAE,CAAC,EAAE,SAAS,UAC1C,QACF;CACA,QAAA,GAAA,MAAA,QAAA,QACS;EACL;EACA;EACA,OAAO,YAAiC,OAAO,KAAK,IAAI,OAAO;EAC/D,aAAa,OAAO,MAAM,EAAE;EAC5B,SAAS,YAAiC,OAAO,OAAO,IAAI,OAAO;CACrE,IACA;EAAC;EAAO;EAAI;EAAQ;CAAK,CAC3B;AACF;;;;;;;;;AAYA,SAAgB,gBACd,IACA,KACA,UAA+B,CAAC,GAC1B;CACN,MAAM,QAAQG,gBAAAA,sBAAsBH,uBAAAA,UAAU;CAC9C,MAAM,UAAUI,gBAAAA,oBACdJ,uBAAAA,aACC,YAAY,QAAQ,WAAW,EAAE,CAAC,EAAE,gBAAgB,GACrD,CACF;CACA,MAAM,CAAC,aAAA,GAAA,MAAA,SAAA,CAAqBK,cAAAA,0BAA0B;CACtD,MAAM,SAAS,QAAQ,UAAU;CAGjC,CAAA,GAAA,MAAA,UAAA,OAAgB,SAAS,OAAO,IAAI,SAAS,OAAO,SAAS,EAAE,OAAO,CAAC,CAAC;AAC1E"}
|
package/dist/shell.d.cts
CHANGED
|
@@ -1,27 +1,42 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as StateHook } from "./state-0K1I5cM4.cjs";
|
|
2
|
+
import { EventHook } from "@embedpdf/core";
|
|
3
|
+
import { RefObject } from "react";
|
|
4
|
+
import { SurfaceFocusOptions } from "@embedpdf/web";
|
|
5
|
+
import { OpenSurfaceOptions, ShellCapability, SurfaceProps } from "@embedpdf/plugin-shell";
|
|
2
6
|
export * from "@embedpdf/plugin-shell";
|
|
3
7
|
//#region src/shell.d.ts
|
|
4
|
-
/** The
|
|
8
|
+
/** The shell capability: open and close panels, dialogs and menus. Without a document its methods throw `not-ready`. */
|
|
5
9
|
declare function useShell(): ShellCapability;
|
|
10
|
+
/** Subscribe to one shell event for the mounted lifetime: `useShellEvent((shell) => shell.onSurfaceOpened, handler)`. */
|
|
11
|
+
declare function useShellEvent<T>(select: (shell: ShellCapability) => EventHook<T>, handler: (event: T) => void): void;
|
|
12
|
+
/**
|
|
13
|
+
* Which panels, dialogs and menus are open (the page's State table, declared
|
|
14
|
+
* once in `shellState`). Takes a selector, and re-renders only when what it
|
|
15
|
+
* returns changes. Without a document nothing is open.
|
|
16
|
+
*/
|
|
17
|
+
declare const useShellState: StateHook<{
|
|
18
|
+
openSurfaces: readonly import("@embedpdf/plugin-shell").SurfaceState[];
|
|
19
|
+
openMenus: readonly string[];
|
|
20
|
+
}>;
|
|
6
21
|
interface SurfaceHandle {
|
|
7
22
|
readonly isOpen: boolean;
|
|
8
|
-
|
|
9
|
-
|
|
23
|
+
/** The props the surface was opened with, or updated to; empty when it has none. */
|
|
24
|
+
readonly props: SurfaceProps;
|
|
25
|
+
open(options?: OpenSurfaceOptions): void;
|
|
10
26
|
close(): void;
|
|
11
|
-
toggle(
|
|
27
|
+
toggle(options?: OpenSurfaceOptions): void;
|
|
12
28
|
}
|
|
13
|
-
/** One named surface (panel
|
|
14
|
-
* document. Reads as closed — and intents no-op — while no document is open. */
|
|
29
|
+
/** One named surface (panel, dialog, overlay) of this subtree's document. Reads as closed, and its verbs do nothing, while no document is open. */
|
|
15
30
|
declare function useSurface(id: string): SurfaceHandle;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
declare function
|
|
31
|
+
/**
|
|
32
|
+
* Put the cursor in `ref`'s element when the surface `id` asks for it: on every
|
|
33
|
+
* `open(id, { focus: true })`, a command whose panel says `focus` among them, and
|
|
34
|
+
* when the element mounts while the request is up (the panel opened, or its code
|
|
35
|
+
* loaded, because of it). A plain open never moves it. `select` selects an input's
|
|
36
|
+
* text, so typing replaces it. Reads the surface of this subtree's document; use
|
|
37
|
+
* it in the component that renders the element.
|
|
38
|
+
*/
|
|
39
|
+
declare function useSurfaceFocus(id: string, ref: RefObject<HTMLElement | null>, options?: SurfaceFocusOptions): void;
|
|
25
40
|
//#endregion
|
|
26
|
-
export {
|
|
41
|
+
export { type SurfaceFocusOptions, SurfaceHandle, useShell, useShellEvent, useShellState, useSurface, useSurfaceFocus };
|
|
27
42
|
//# sourceMappingURL=shell.d.cts.map
|