@embedpdf/react 3.0.0-next.1 → 3.0.0-next.11

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.
Files changed (132) hide show
  1. package/dist/AnnotationListSnapshot-C1U3XUxP-6kGwNoxy.d.cts +546 -0
  2. package/dist/AnnotationListSnapshot-C1U3XUxP-6kGwNoxy.d.ts +546 -0
  3. package/dist/actions.cjs +42 -0
  4. package/dist/actions.cjs.map +1 -0
  5. package/dist/actions.d.cts +18 -0
  6. package/dist/actions.d.ts +18 -0
  7. package/dist/actions.js +34 -0
  8. package/dist/actions.js.map +1 -0
  9. package/dist/anchored.cjs +89 -0
  10. package/dist/anchored.cjs.map +1 -0
  11. package/dist/anchored.d.cts +45 -0
  12. package/dist/anchored.d.ts +45 -0
  13. package/dist/anchored.js +60 -0
  14. package/dist/anchored.js.map +1 -0
  15. package/dist/{annotation-B6jwfTCh.d.cts → annotation-BCED_p9H.d.cts} +56 -69
  16. package/dist/annotation-anchors-CetwM1g7.js +15 -0
  17. package/dist/annotation-anchors-CetwM1g7.js.map +1 -0
  18. package/dist/annotation-anchors-DuYRBJLl.cjs +26 -0
  19. package/dist/annotation-anchors-DuYRBJLl.cjs.map +1 -0
  20. package/dist/annotation-hooks-BB5ZhPpf.cjs +16 -0
  21. package/dist/annotation-hooks-BB5ZhPpf.cjs.map +1 -0
  22. package/dist/annotation-hooks-Df8EmzDO.js +11 -0
  23. package/dist/annotation-hooks-Df8EmzDO.js.map +1 -0
  24. package/dist/{annotation-nUMefyjw.d.ts → annotation-k38KDlIZ.d.ts} +57 -70
  25. package/dist/annotation-menu.cjs +37 -88
  26. package/dist/annotation-menu.cjs.map +1 -1
  27. package/dist/annotation-menu.d.cts +28 -2
  28. package/dist/annotation-menu.d.ts +28 -2
  29. package/dist/annotation-menu.js +28 -79
  30. package/dist/annotation-menu.js.map +1 -1
  31. package/dist/annotation.cjs +738 -16
  32. package/dist/annotation.cjs.map +1 -0
  33. package/dist/annotation.d.cts +2 -2
  34. package/dist/annotation.d.ts +2 -2
  35. package/dist/annotation.js +723 -3
  36. package/dist/annotation.js.map +1 -0
  37. package/dist/commands.js +1 -0
  38. package/dist/commands.js.map +1 -1
  39. package/dist/form-BwrLFpKN.js +805 -0
  40. package/dist/form-BwrLFpKN.js.map +1 -0
  41. package/dist/form-D-ZSfGmB.cjs +836 -0
  42. package/dist/form-D-ZSfGmB.cjs.map +1 -0
  43. package/dist/form.cjs +8 -551
  44. package/dist/form.d.cts +4 -21
  45. package/dist/form.d.ts +4 -21
  46. package/dist/form.js +3 -544
  47. package/dist/index.cjs +49 -16
  48. package/dist/index.d.cts +17 -10
  49. package/dist/index.d.ts +17 -10
  50. package/dist/index.js +14 -9
  51. package/dist/interaction.cjs +17 -4
  52. package/dist/interaction.cjs.map +1 -1
  53. package/dist/interaction.d.cts +2 -2
  54. package/dist/interaction.d.ts +2 -2
  55. package/dist/interaction.js +7 -5
  56. package/dist/interaction.js.map +1 -1
  57. package/dist/link.cjs +70 -7
  58. package/dist/link.cjs.map +1 -1
  59. package/dist/link.d.cts +12 -3
  60. package/dist/link.d.ts +12 -3
  61. package/dist/link.js +73 -10
  62. package/dist/link.js.map +1 -1
  63. package/dist/metadata.js +1 -0
  64. package/dist/metadata.js.map +1 -1
  65. package/dist/page-edit.js +1 -0
  66. package/dist/page-edit.js.map +1 -1
  67. package/dist/page-view.cjs +62 -38
  68. package/dist/page-view.cjs.map +1 -1
  69. package/dist/page-view.js +59 -37
  70. package/dist/page-view.js.map +1 -1
  71. package/dist/redaction.js +1 -0
  72. package/dist/redaction.js.map +1 -1
  73. package/dist/render.cjs +67 -50
  74. package/dist/render.cjs.map +1 -1
  75. package/dist/render.d.cts +6 -19
  76. package/dist/render.d.ts +6 -19
  77. package/dist/render.js +71 -52
  78. package/dist/render.js.map +1 -1
  79. package/dist/rolldown-runtime-BBjsoOtd.js +27 -0
  80. package/dist/{runtime-CMxIuF2O.d.cts → runtime-DE76hlnl.d.ts} +32 -11
  81. package/dist/{runtime-DV98q7I0.d.ts → runtime-umHA_xHt.d.cts} +32 -11
  82. package/dist/runtime.cjs +25 -5
  83. package/dist/runtime.cjs.map +1 -1
  84. package/dist/runtime.d.cts +2 -2
  85. package/dist/runtime.d.ts +2 -2
  86. package/dist/runtime.js +26 -6
  87. package/dist/runtime.js.map +1 -1
  88. package/dist/scrollbar.cjs +3 -3
  89. package/dist/scrollbar.cjs.map +1 -1
  90. package/dist/scrollbar.d.cts +1 -1
  91. package/dist/scrollbar.d.ts +1 -1
  92. package/dist/scrollbar.js +1 -1
  93. package/dist/scrollbar.js.map +1 -1
  94. package/dist/search.cjs +36 -19
  95. package/dist/search.cjs.map +1 -1
  96. package/dist/search.d.cts +1 -1
  97. package/dist/search.d.ts +1 -1
  98. package/dist/search.js +36 -19
  99. package/dist/search.js.map +1 -1
  100. package/dist/selection.cjs +217 -22
  101. package/dist/selection.cjs.map +1 -1
  102. package/dist/selection.d.cts +60 -2
  103. package/dist/selection.d.ts +60 -2
  104. package/dist/selection.js +214 -27
  105. package/dist/selection.js.map +1 -1
  106. package/dist/shell.js +1 -0
  107. package/dist/shell.js.map +1 -1
  108. package/dist/stage.cjs +49 -148
  109. package/dist/stage.cjs.map +1 -1
  110. package/dist/stage.d.cts +18 -5
  111. package/dist/stage.d.ts +18 -5
  112. package/dist/stage.js +51 -149
  113. package/dist/stage.js.map +1 -1
  114. package/dist/stamp.js +1 -0
  115. package/dist/stamp.js.map +1 -1
  116. package/dist/toolbar.cjs +4 -4
  117. package/dist/toolbar.cjs.map +1 -1
  118. package/dist/toolbar.d.cts +1 -1
  119. package/dist/toolbar.d.ts +1 -1
  120. package/dist/toolbar.js +2 -1
  121. package/dist/toolbar.js.map +1 -1
  122. package/dist/views.js +1 -0
  123. package/dist/views.js.map +1 -1
  124. package/package.json +33 -26
  125. package/dist/AnnotationListSnapshot-W0RCsyDE-dWna4TTJ.d.cts +0 -2593
  126. package/dist/AnnotationListSnapshot-W0RCsyDE-dWna4TTJ.d.ts +0 -2593
  127. package/dist/annotation-Ct6dFtzJ.cjs +0 -913
  128. package/dist/annotation-Ct6dFtzJ.cjs.map +0 -1
  129. package/dist/annotation-DjGWJljY.js +0 -829
  130. package/dist/annotation-DjGWJljY.js.map +0 -1
  131. package/dist/form.cjs.map +0 -1
  132. package/dist/form.js.map +0 -1
@@ -1,11 +1,20 @@
1
- import { t as AnnotationDTO } from "./AnnotationListSnapshot-W0RCsyDE-dWna4TTJ.js";
2
- import { o as PageContextValue } from "./runtime-DV98q7I0.js";
1
+ import { d as runtime_d_exports, o as PageContextValue } from "./runtime-DE76hlnl.js";
3
2
  import * as React from "react";
4
- import { Rect } from "@embedpdf/core-geometry";
5
- import { Behavior, FilePickerProvider, SelectionFlags, SelectionFlags as SelectionFlags$1, SelectionProps, SelectionProps as SelectionProps$1 } from "@embedpdf/plugin-annotation";
6
- import { AnnotationFlags, AnnotationProps, AnnotationProps as AnnotationProps$1, AnnotationPropsPatch, AnnotationPropsPatch as AnnotationPropsPatch$1, Border, CreationDraftAnchor, Geom, LineEnding, LineEndings, PropKey, PropSpec, Rect as Rect$1, RenderItem, RenderItem as RenderItem$1, Style, TextAlign, TextStyle, Vec } from "@embedpdf/core-annotation";
7
- //#region src/internal/annotation-menu-position.d.ts
8
- type AnnotationMenuPlacement = 'top' | 'right' | 'bottom' | 'left';
3
+ import { AnnotationHydration, AnnotationRef, Behavior, CommentThread, CommentsApi, FilePickerProvider, SelectionFlags, SelectionFlags as SelectionFlags$1, SelectionProps, SelectionProps as SelectionProps$1 } from "@embedpdf/plugin-annotation";
4
+ import { AnnotationFlags, AnnotationProps, AnnotationProps as AnnotationProps$1, AnnotationPropsPatch, Border, CreationDraftAnchor, CreationDraftAnchor as CreationDraftAnchor$1, Geom, LineEnding, LineEndings, PropKey, PropSpec, Rect, RenderItem, RenderItem as RenderItem$1, Style, TextAlign, TextStyle, Vec } from "@embedpdf/core-annotation";
5
+ //#region src/annotation-anchors.d.ts
6
+ /** The selection's menu anchor on its primary page, in content space. */
7
+ type SelectionAnchor = {
8
+ pon: number;
9
+ bounds: Rect;
10
+ knob?: Vec;
11
+ };
12
+ declare function sameAnchor(a: SelectionAnchor | null, b: SelectionAnchor | null): boolean;
13
+ declare function sameCreationDraftAnchor(a: CreationDraftAnchor | null, b: CreationDraftAnchor | null): boolean;
14
+ //#endregion
15
+ //#region src/annotation-hooks.d.ts
16
+ /** The selected annotations as engine DTOs — for selection-aware toolbars/sidebars. */
17
+ declare function useAnnotationSelected(): import("@embedpdf/plugin-annotation").AnnotationDTO[];
9
18
  //#endregion
10
19
  //#region src/annotation.d.ts
11
20
  /**
@@ -21,7 +30,7 @@ interface AnnotationRendererProps {
21
30
  page: PageContextValue;
22
31
  appearance: {
23
32
  url: string;
24
- box: Rect$1;
33
+ box: Rect;
25
34
  } | null;
26
35
  native: React.ReactNode;
27
36
  interactive: boolean;
@@ -81,8 +90,6 @@ declare const filePickerProvider: FilePickerProvider;
81
90
  declare function useFilePickerProvider(provider?: FilePickerProvider | null): void;
82
91
  declare function useAnnotation(): import("@embedpdf/plugin-annotation").AnnotationCapability;
83
92
  declare function useAnnotationSelection(): string[];
84
- /** The selected annotations as engine DTOs — for selection-aware toolbars/sidebars. */
85
- declare function useAnnotationSelected(): AnnotationDTO[];
86
93
  /**
87
94
  * A tool's RESOLVED defaults (base + per-tool override) as a full flat props
88
95
  * bag, subscribed so a `setDefaults` re-renders the consumer. Use this — not the
@@ -107,67 +114,47 @@ declare function useSelectionProps(): SelectionProps;
107
114
  * between model changes, like {@link useSelectionProps}.
108
115
  */
109
116
  declare function useSelectionFlags(): SelectionFlags | null;
110
- /** The selection's menu anchor: the primary page + the union box of the selection
111
- * on that page (content space). */
112
- type SelectionAnchor = {
113
- pon: number;
114
- bounds: Rect$1;
115
- knob?: Vec;
116
- };
117
- /** Structural equality for the selection anchor — keeps the menu from re-rendering
118
- * on unrelated dispatches (the capability returns a fresh object each call). */
119
- declare function sameAnchor(a: SelectionAnchor | null, b: SelectionAnchor | null): boolean;
120
- /** What a menu's render prop receives: the live selection (engine DTOs) + the
121
- * selection-scoped intents. Fully headless — you render all UI. */
122
- interface AnnotationMenuRenderArgs {
123
- selected: ReturnType<typeof useAnnotationSelected>;
124
- deleteSelection: () => void;
125
- deselect: () => void;
126
- /** Restyle the selection with a flat props patch (see `useSelectionProps`). */
127
- updateSelection: (patch: AnnotationPropsPatch) => void;
128
- /** Rotate the current selection a quarter-turn clockwise about its centre. */
129
- rotate90: () => void;
130
- /** Reset the current selection to its as-authored orientation. */
131
- resetRotation: () => void;
132
- /** Group the current selection into one unit (selecting any member then
133
- * selects all). */
134
- group: () => Promise<void>;
135
- /** Ungroup the group(s) the current selection touches. */
136
- ungroup: () => Promise<void>;
137
- /** Whether {@link group} would do something for the current selection. */
138
- canGroup: boolean;
139
- /** Whether {@link ungroup} would do something for the current selection. */
140
- canUngroup: boolean;
141
- }
142
- interface AnnotationMenuProps {
143
- children: (args: AnnotationMenuRenderArgs) => React.ReactNode;
144
- /** Gap in screen px between the selection box and the menu (default 8). */
145
- gap?: number;
146
- /** Where to place the menu relative to the selection box. Default 'top'. */
147
- placement?: AnnotationMenuPlacement;
148
- }
149
- declare function sameCreationDraftAnchor(a: CreationDraftAnchor | null, b: CreationDraftAnchor | null): boolean;
150
- interface AnnotationDraftMenuRenderArgs extends CreationDraftAnchor {
151
- finish: () => void;
152
- cancel: () => void;
153
- }
154
- interface AnnotationDraftMenuProps {
155
- children: (args: AnnotationDraftMenuRenderArgs) => React.ReactNode;
156
- /** Gap in screen px between the draft anchor and the menu (default 8). */
157
- gap?: number;
158
- /** Where to place the menu relative to the draft anchor. Default 'top'. */
159
- placement?: AnnotationMenuPlacement;
117
+ /**
118
+ * A {@link CommentThread} enriched with its page's live display position —
119
+ * the framework-layer join. Identity stays `pageObjectNumber` (like every
120
+ * annotation surface); these two fields are PRESENTATION, tracking page
121
+ * moves and deletes.
122
+ */
123
+ interface CommentThreadView extends CommentThread {
124
+ /** Current 0-based display index of the thread's page; `-1` when the page
125
+ * is no longer in the document (one-frame teardown race on delete). */
126
+ pageIndex: number;
127
+ /** The page's `/PageLabels` label when the PDF declares one ("iv", "A-2"),
128
+ * else the 1-based position as a string — print it verbatim. */
129
+ pageLabel: string;
130
+ /**
131
+ * The root annotation's rect in CONTENT space (y-down, crop-relative,
132
+ * unscaled points) — the space `StageCapability.reveal` takes, so a
133
+ * "jump to this comment" is `stage.reveal(pageIndex, { rect: contentRect })`.
134
+ * Null when the page is gone. Identity still travels as `pageObjectNumber`;
135
+ * this, like `pageIndex`, is presentation.
136
+ */
137
+ contentRect: Rect | null;
160
138
  }
139
+ /** Pure join behind {@link useCommentThreads} — exported for tests. */
140
+ declare function enrichCommentThreads(threads: readonly CommentThread[], pages: readonly runtime_d_exports.PageLayout[]): CommentThreadView[];
141
+ /** The comments surface (verbs + `permissionsFor`) — imperative; pair with
142
+ * {@link useCommentThreads} for the subscribed view. */
143
+ declare function useComments(): CommentsApi;
161
144
  /**
162
- * The Stage-FREE selection menu, for `<PageView>` (no camera). It transforms a
163
- * selected content rect to client px via `page.toClientRect`, then renders an
164
- * UPRIGHT menu through a portal to
165
- * `document.body` (so it never rotates with the page nor clips at a container
166
- * edge). Re-measures on scroll/resize. For `<Stage>`, prefer `<AnnotationMenu>`
167
- * from `@embedpdf/react/annotation-menu` (camera-driven, more responsive).
145
+ * Every comment thread in the document, display-ordered (page position →
146
+ * top of page → creation date) and enriched with `pageIndex`/`pageLabel`.
147
+ * Subscribed to BOTH stores: annotation writes (own, remote, hydration)
148
+ * recompute the threads; page moves/deletes re-run the join. Reference-
149
+ * stable between changes — safe to memo child renders on the array.
168
150
  */
169
- declare function PageAnnotationMenu({ children, gap, placement }: AnnotationMenuProps): React.ReactPortal | null;
170
- declare function PageAnnotationDraftMenu({ children, gap, placement }: AnnotationDraftMenuProps): React.ReactPortal | null;
151
+ declare function useCommentThreads(): CommentThreadView[];
152
+ /** The enriched thread containing ANY member ref (root, reply, grouped part,
153
+ * state annotation), or null. */
154
+ declare function useCommentThread(ref: AnnotationRef | null): CommentThreadView | null;
155
+ /** Whole-document hydration status — the comments sidebar's honest loading
156
+ * state (`loading` until `listRawAll` lands, then `complete`/`error`). */
157
+ declare function useCommentsHydration(): AnnotationHydration;
171
158
  //#endregion
172
- export { useAnnotation as A, SelectionProps$1 as C, filePickerProvider as D, TextStyle as E, useSelectionFlags as F, useSelectionProps as I, AnnotationMenuPlacement as L, useAnnotationSelected as M, useAnnotationSelection as N, sameAnchor as O, useFilePickerProvider as P, SelectionFlags$1 as S, TextAlign as T, PageAnnotationMenu as _, AnnotationLayerProps as a, RenderItem$1 as b, AnnotationProps$1 as c, AnnotationRendererProps as d, Border as f, PageAnnotationDraftMenu as g, LineEndings as h, AnnotationLayer as i, useAnnotationDefaults as j, sameCreationDraftAnchor as k, AnnotationPropsPatch$1 as l, LineEnding as m, AnnotationDraftMenuRenderArgs as n, AnnotationMenuProps as o, Geom as p, AnnotationFlags as r, AnnotationMenuRenderArgs as s, AnnotationDraftMenuProps as t, AnnotationRenderer as u, PropKey as v, Style as w, SelectionAnchor as x, PropSpec as y };
173
- //# sourceMappingURL=annotation-nUMefyjw.d.ts.map
159
+ export { useCommentsHydration as A, filePickerProvider as C, useCommentThread as D, useAnnotationSelection as E, SelectionAnchor as F, sameAnchor as I, sameCreationDraftAnchor as L, useSelectionFlags as M, useSelectionProps as N, useCommentThreads as O, useAnnotationSelected as P, enrichCommentThreads as S, useAnnotationDefaults as T, SelectionFlags$1 as _, AnnotationPropsPatch as a, TextAlign as b, Border as c, Geom as d, LineEnding as f, RenderItem$1 as g, PropSpec as h, AnnotationProps$1 as i, useFilePickerProvider as j, useComments as k, CommentThreadView as l, PropKey as m, AnnotationLayer as n, AnnotationRenderer as o, LineEndings as p, AnnotationLayerProps as r, AnnotationRendererProps as s, AnnotationFlags as t, CreationDraftAnchor$1 as u, SelectionProps$1 as v, useAnnotation as w, TextStyle as x, Style as y };
160
+ //# sourceMappingURL=annotation-k38KDlIZ.d.ts.map
@@ -1,111 +1,60 @@
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_annotation = require("./annotation-Ct6dFtzJ.cjs");
4
+ const require_anchored = require("./anchored.cjs");
5
+ const require_annotation_anchors = require("./annotation-anchors-DuYRBJLl.cjs");
5
6
  let react_jsx_runtime = require("react/jsx-runtime");
6
7
  let react = require("react");
7
8
  react = require_rolldown_runtime.__toESM(react, 1);
8
- let _embedpdf_plugin_stage = require("@embedpdf/plugin-stage");
9
- let _embedpdf_plugin_annotation_internal = require("@embedpdf/plugin-annotation/internal");
9
+ let _embedpdf_plugin_annotation_contract_host = require("@embedpdf/plugin-annotation/contract/host");
10
10
  //#region src/annotation-menu.tsx
11
11
  /**
12
- * <AnnotationMenu> — the DEFAULT, Stage-bound selection menu.
12
+ * Annotation menus — thin anchor plumbing over the ONE `<Anchored>` primitive.
13
13
  *
14
- * Marker-style: it transforms the selected content rect through the Stage camera
15
- * and renders an upright menu in viewport space. Mount it in the Stage `overlay`
16
- * slot. It re-renders on `visiblePages()`, so it tracks pan/zoom and page
17
- * rotation/layout changes with no DOM measurement, no portal, no scroll listeners.
14
+ * These work identically under `<Stage>` (mount in the overlay slot; the
15
+ * camera projects, no DOM reads) and `<PageView>` (measured + portalled) —
16
+ * the SURFACE provides the projector, the menu doesn't care.
18
17
  *
19
- * This module is the ONLY annotation menu that imports `@embedpdf/plugin-stage`.
20
- * For a Stage-free `<PageView>`, use `<PageAnnotationMenu>` from
21
- * `@embedpdf/react/annotation` instead.
18
+ * No action bags: actions come from the capability hooks (`useAnnotation()`,
19
+ * `useAnnotationSelected()`, …), which subscribe properly and compose across
20
+ * plugins. Render props carry only the ANCHOR'S DATA (the draft menu's
21
+ * progress facts); the annotation-selection menu takes plain children.
22
+ */
23
+ /**
24
+ * Floats over the current annotation selection (one anchor regardless of
25
+ * cross-page selection), dodging the rotate knob. Compose the contents from
26
+ * hooks: `useAnnotation()` for the verbs, `useAnnotationSelected()` /
27
+ * `useSelectionProps()` for the data.
22
28
  */
23
29
  function AnnotationMenu({ children, gap = 15, placement = "top" }) {
24
- const stage = require_runtime.useCapability(_embedpdf_plugin_stage.StageToken);
25
- const anno = require_runtime.useCapability(_embedpdf_plugin_annotation_internal.AnnotationToken);
26
- require_runtime.useSelector(_embedpdf_plugin_stage.StageToken, (c) => c.visiblePages());
27
- const anchor = require_runtime.useSelector(_embedpdf_plugin_annotation_internal.AnnotationToken, (c) => {
30
+ const { projector } = require_anchored.useProjectorBinding();
31
+ const anchor = require_runtime.useSelector(_embedpdf_plugin_annotation_contract_host.AnnotationToken, (c) => {
28
32
  const a = c.selectionAnchor();
29
33
  if (!a) return null;
30
- const t = stage.pageRect(a.pon)?.transform;
31
- return t?.viewScale ? c.selectionAnchor(t.viewScale, t.rotation, t.zoom) : a;
32
- }, require_annotation.sameAnchor);
33
- const selected = require_annotation.useAnnotationSelected();
34
- const ref = (0, react.useRef)(null);
35
- (0, react.useEffect)(() => {
36
- const el = ref.current;
37
- if (!el) return;
38
- const stop = (e) => e.stopPropagation();
39
- el.addEventListener("pointerdown", stop);
40
- return () => el.removeEventListener("pointerdown", stop);
41
- });
34
+ const env = projector.viewEnv(a.pon);
35
+ return env ? c.selectionAnchor(env.scale, env.rotation, env.zoom) : a;
36
+ }, require_annotation_anchors.sameAnchor);
42
37
  if (!anchor) return null;
43
- const box = stage.pageRectToScreen(anchor.pon, anchor.bounds);
44
- if (!box) return null;
45
- const kr = anchor.knob ? stage.pageRectToScreen(anchor.pon, {
46
- x: anchor.knob.x,
47
- y: anchor.knob.y,
48
- width: 0,
49
- height: 0
50
- }) : null;
51
- const pos = require_annotation.positionMenuAroundRect(box, placement, gap, kr ? {
52
- x: kr.x,
53
- y: kr.y
54
- } : null);
55
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
56
- ref,
57
- style: {
58
- position: "absolute",
59
- left: pos.left,
60
- top: pos.top,
61
- transform: pos.transform,
62
- pointerEvents: "auto"
38
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_anchored.Anchored, {
39
+ anchor: {
40
+ pon: anchor.pon,
41
+ bounds: anchor.bounds,
42
+ ...anchor.knob ? { avoid: [anchor.knob] } : {}
63
43
  },
64
- children: children({
65
- selected,
66
- deleteSelection: anno.deleteSelection,
67
- deselect: anno.deselect,
68
- updateSelection: anno.updateSelection,
69
- rotate90: anno.rotateSelection90,
70
- resetRotation: anno.resetSelectionRotation,
71
- group: anno.group,
72
- ungroup: anno.ungroup,
73
- canGroup: anno.canGroup(),
74
- canUngroup: anno.canUngroup()
75
- })
44
+ placement,
45
+ gap,
46
+ children
76
47
  });
77
48
  }
49
+ /** Floats over a live multi-click creation draft (polygon, polyline, …). */
78
50
  function AnnotationDraftMenu({ children, gap = 8, placement = "top" }) {
79
- const stage = require_runtime.useCapability(_embedpdf_plugin_stage.StageToken);
80
- const anno = require_runtime.useCapability(_embedpdf_plugin_annotation_internal.AnnotationToken);
81
- require_runtime.useSelector(_embedpdf_plugin_stage.StageToken, (c) => c.visiblePages());
82
- const anchor = require_runtime.useSelector(_embedpdf_plugin_annotation_internal.AnnotationToken, (c) => c.creationDraftAnchor(), require_annotation.sameCreationDraftAnchor);
83
- const ref = (0, react.useRef)(null);
84
- (0, react.useEffect)(() => {
85
- const el = ref.current;
86
- if (!el) return;
87
- const stop = (e) => e.stopPropagation();
88
- el.addEventListener("pointerdown", stop);
89
- return () => el.removeEventListener("pointerdown", stop);
90
- });
51
+ const anchor = require_runtime.useSelector(_embedpdf_plugin_annotation_contract_host.AnnotationToken, (c) => c.creationDraftAnchor(), require_annotation_anchors.sameCreationDraftAnchor);
91
52
  if (!anchor) return null;
92
- const box = stage.pageRectToScreen(anchor.pon, anchor.bounds);
93
- if (!box) return null;
94
- const pos = require_annotation.positionMenuAroundRect(box, placement, gap);
95
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
96
- ref,
97
- style: {
98
- position: "absolute",
99
- left: pos.left,
100
- top: pos.top,
101
- transform: pos.transform,
102
- pointerEvents: "auto"
103
- },
104
- children: children({
105
- ...anchor,
106
- finish: anno.finishCreationDraft,
107
- cancel: anno.cancelCreationDraft
108
- })
53
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_anchored.Anchored, {
54
+ anchor,
55
+ placement,
56
+ gap,
57
+ children: children(anchor)
109
58
  });
110
59
  }
111
60
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"annotation-menu.cjs","names":["useCapability","StageToken","AnnotationHostToken","useSelector","sameAnchor","useAnnotationSelected","positionMenuAroundRect","sameCreationDraftAnchor"],"sources":["../src/annotation-menu.tsx"],"sourcesContent":["/**\n * <AnnotationMenu> — the DEFAULT, Stage-bound selection menu.\n *\n * Marker-style: it transforms the selected content rect through the Stage camera\n * and renders an upright menu in viewport space. Mount it in the Stage `overlay`\n * slot. It re-renders on `visiblePages()`, so it tracks pan/zoom and page\n * rotation/layout changes with no DOM measurement, no portal, no scroll listeners.\n *\n * This module is the ONLY annotation menu that imports `@embedpdf/plugin-stage`.\n * For a Stage-free `<PageView>`, use `<PageAnnotationMenu>` from\n * `@embedpdf/react/annotation` instead.\n */\nimport * as React from 'react';\nimport { useEffect, useRef } from 'react';\nimport { StageToken } from '@embedpdf/plugin-stage';\nimport { AnnotationToken as AnnotationHostToken } from '@embedpdf/plugin-annotation/internal';\nimport { useCapability, useSelector } from './runtime';\nimport {\n sameAnchor,\n sameCreationDraftAnchor,\n useAnnotationSelected,\n type AnnotationDraftMenuProps,\n type AnnotationMenuProps,\n} from './annotation';\nimport { positionMenuAroundRect } from './internal/annotation-menu-position';\n\nexport function AnnotationMenu({ children, gap = 15, placement = 'top' }: AnnotationMenuProps) {\n const stage = useCapability(StageToken);\n const anno = useCapability(AnnotationHostToken);\n // Reposition on pan/zoom AND rotation/layout changes. `visiblePages()` folds in\n // the scene key, camera, viewport, and DPR, while staying referentially stable.\n useSelector(StageToken, (c) => c.visiblePages());\n // Two-step anchor resolve: the pon comes from the scale-less anchor, then the\n // knob is re-projected with THAT page's view scale so the menu dodges the knob\n // where it actually renders (screen-constant stalk).\n const anchor = useSelector(\n AnnotationHostToken,\n (c) => {\n const a = c.selectionAnchor();\n if (!a) return null;\n const t = stage.pageRect(a.pon)?.transform;\n return t?.viewScale ? c.selectionAnchor(t.viewScale, t.rotation, t.zoom) : a;\n },\n sameAnchor,\n );\n const selected = useAnnotationSelected();\n\n // Isolate the menu from the Stage's pointer forwarding: a pointerdown inside it\n // must NOT bubble up to the Stage container's NATIVE listener, which would\n // forward to the interaction hub and read it as a click-outside → deselect (and\n // unmount this menu). Native listener (not React's) so it runs during real DOM\n // bubbling, before the Stage's own native listener on the ancestor. Inner button\n // `onClick` still fires — `click` is a separate event that reaches React's root.\n const ref = useRef<HTMLDivElement>(null);\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n const stop = (e: Event) => e.stopPropagation();\n el.addEventListener('pointerdown', stop);\n return () => el.removeEventListener('pointerdown', stop);\n });\n\n if (!anchor) return null;\n const box = stage.pageRectToScreen(anchor.pon, anchor.bounds);\n if (!box) return null;\n // Transform the knob via a zero-size rect so page rotation is respected; the\n // menu then nudges only the edge it sits on (and only if the knob protrudes).\n const kr = anchor.knob\n ? stage.pageRectToScreen(anchor.pon, {\n x: anchor.knob.x,\n y: anchor.knob.y,\n width: 0,\n height: 0,\n })\n : null;\n const pos = positionMenuAroundRect(box, placement, gap, kr ? { x: kr.x, y: kr.y } : null);\n return (\n <div\n ref={ref}\n style={{\n position: 'absolute',\n left: pos.left,\n top: pos.top,\n transform: pos.transform,\n pointerEvents: 'auto',\n }}\n >\n {children({\n selected,\n deleteSelection: anno.deleteSelection,\n deselect: anno.deselect,\n updateSelection: anno.updateSelection,\n rotate90: anno.rotateSelection90,\n resetRotation: anno.resetSelectionRotation,\n group: anno.group,\n ungroup: anno.ungroup,\n canGroup: anno.canGroup(),\n canUngroup: anno.canUngroup(),\n })}\n </div>\n );\n}\n\nexport function AnnotationDraftMenu({\n children,\n gap = 8,\n placement = 'top',\n}: AnnotationDraftMenuProps) {\n const stage = useCapability(StageToken);\n const anno = useCapability(AnnotationHostToken);\n useSelector(StageToken, (c) => c.visiblePages());\n const anchor = useSelector(\n AnnotationHostToken,\n (c) => c.creationDraftAnchor(),\n sameCreationDraftAnchor,\n );\n\n const ref = useRef<HTMLDivElement>(null);\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n const stop = (e: Event) => e.stopPropagation();\n el.addEventListener('pointerdown', stop);\n return () => el.removeEventListener('pointerdown', stop);\n });\n\n if (!anchor) return null;\n const box = stage.pageRectToScreen(anchor.pon, anchor.bounds);\n if (!box) return null;\n const pos = positionMenuAroundRect(box, placement, gap);\n return (\n <div\n ref={ref}\n style={{\n position: 'absolute',\n left: pos.left,\n top: pos.top,\n transform: pos.transform,\n pointerEvents: 'auto',\n }}\n >\n {children({\n ...anchor,\n finish: anno.finishCreationDraft,\n cancel: anno.cancelCreationDraft,\n })}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AA0BA,SAAgB,eAAe,EAAE,UAAU,MAAM,IAAI,YAAY,SAA8B;CAC7F,MAAM,QAAQA,gBAAAA,cAAcC,uBAAAA,UAAU;CACtC,MAAM,OAAOD,gBAAAA,cAAcE,qCAAAA,eAAmB;CAG9C,gBAAA,YAAYD,uBAAAA,aAAa,MAAM,EAAE,aAAa,CAAC;CAI/C,MAAM,SAASE,gBAAAA,YACbD,qCAAAA,kBACC,MAAM;EACL,MAAM,IAAI,EAAE,gBAAgB;EAC5B,IAAI,CAAC,GAAG,OAAO;EACf,MAAM,IAAI,MAAM,SAAS,EAAE,GAAG,CAAC,EAAE;EACjC,OAAO,GAAG,YAAY,EAAE,gBAAgB,EAAE,WAAW,EAAE,UAAU,EAAE,IAAI,IAAI;CAC7E,GACAE,mBAAAA,UACF;CACA,MAAM,WAAWC,mBAAAA,sBAAsB;CAQvC,MAAM,OAAA,GAAA,MAAA,OAAA,CAA6B,IAAI;CACvC,CAAA,GAAA,MAAA,UAAA,OAAgB;EACd,MAAM,KAAK,IAAI;EACf,IAAI,CAAC,IAAI;EACT,MAAM,QAAQ,MAAa,EAAE,gBAAgB;EAC7C,GAAG,iBAAiB,eAAe,IAAI;EACvC,aAAa,GAAG,oBAAoB,eAAe,IAAI;CACzD,CAAC;CAED,IAAI,CAAC,QAAQ,OAAO;CACpB,MAAM,MAAM,MAAM,iBAAiB,OAAO,KAAK,OAAO,MAAM;CAC5D,IAAI,CAAC,KAAK,OAAO;CAGjB,MAAM,KAAK,OAAO,OACd,MAAM,iBAAiB,OAAO,KAAK;EACjC,GAAG,OAAO,KAAK;EACf,GAAG,OAAO,KAAK;EACf,OAAO;EACP,QAAQ;CACV,CAAC,IACD;CACJ,MAAM,MAAMC,mBAAAA,uBAAuB,KAAK,WAAW,KAAK,KAAK;EAAE,GAAG,GAAG;EAAG,GAAG,GAAG;CAAE,IAAI,IAAI;CACxF,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;EACO;EACL,OAAO;GACL,UAAU;GACV,MAAM,IAAI;GACV,KAAK,IAAI;GACT,WAAW,IAAI;GACf,eAAe;EACjB;EAEC,UAAA,SAAS;GACR;GACA,iBAAiB,KAAK;GACtB,UAAU,KAAK;GACf,iBAAiB,KAAK;GACtB,UAAU,KAAK;GACf,eAAe,KAAK;GACpB,OAAO,KAAK;GACZ,SAAS,KAAK;GACd,UAAU,KAAK,SAAS;GACxB,YAAY,KAAK,WAAW;EAC9B,CAAC;CACE,CAAA;AAET;AAEA,SAAgB,oBAAoB,EAClC,UACA,MAAM,GACN,YAAY,SACe;CAC3B,MAAM,QAAQN,gBAAAA,cAAcC,uBAAAA,UAAU;CACtC,MAAM,OAAOD,gBAAAA,cAAcE,qCAAAA,eAAmB;CAC9C,gBAAA,YAAYD,uBAAAA,aAAa,MAAM,EAAE,aAAa,CAAC;CAC/C,MAAM,SAASE,gBAAAA,YACbD,qCAAAA,kBACC,MAAM,EAAE,oBAAoB,GAC7BK,mBAAAA,uBACF;CAEA,MAAM,OAAA,GAAA,MAAA,OAAA,CAA6B,IAAI;CACvC,CAAA,GAAA,MAAA,UAAA,OAAgB;EACd,MAAM,KAAK,IAAI;EACf,IAAI,CAAC,IAAI;EACT,MAAM,QAAQ,MAAa,EAAE,gBAAgB;EAC7C,GAAG,iBAAiB,eAAe,IAAI;EACvC,aAAa,GAAG,oBAAoB,eAAe,IAAI;CACzD,CAAC;CAED,IAAI,CAAC,QAAQ,OAAO;CACpB,MAAM,MAAM,MAAM,iBAAiB,OAAO,KAAK,OAAO,MAAM;CAC5D,IAAI,CAAC,KAAK,OAAO;CACjB,MAAM,MAAMD,mBAAAA,uBAAuB,KAAK,WAAW,GAAG;CACtD,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;EACO;EACL,OAAO;GACL,UAAU;GACV,MAAM,IAAI;GACV,KAAK,IAAI;GACT,WAAW,IAAI;GACf,eAAe;EACjB;EAEC,UAAA,SAAS;GACR,GAAG;GACH,QAAQ,KAAK;GACb,QAAQ,KAAK;EACf,CAAC;CACE,CAAA;AAET"}
1
+ {"version":3,"file":"annotation-menu.cjs","names":["useProjectorBinding","useSelector","AnnotationHostToken","sameAnchor","Anchored","sameCreationDraftAnchor"],"sources":["../src/annotation-menu.tsx"],"sourcesContent":["/**\n * Annotation menus — thin anchor plumbing over the ONE `<Anchored>` primitive.\n *\n * These work identically under `<Stage>` (mount in the overlay slot; the\n * camera projects, no DOM reads) and `<PageView>` (measured + portalled) —\n * the SURFACE provides the projector, the menu doesn't care.\n *\n * No action bags: actions come from the capability hooks (`useAnnotation()`,\n * `useAnnotationSelected()`, …), which subscribe properly and compose across\n * plugins. Render props carry only the ANCHOR'S DATA (the draft menu's\n * progress facts); the annotation-selection menu takes plain children.\n */\nimport * as React from 'react';\nimport { AnnotationToken as AnnotationHostToken } from '@embedpdf/plugin-annotation/contract/host';\nimport type { CreationDraftAnchor } from '@embedpdf/core-annotation';\nimport { Anchored, useProjectorBinding, type AnchoredPlacement } from './anchored';\nimport { useSelector } from './runtime';\nimport { sameAnchor, sameCreationDraftAnchor } from './annotation-anchors';\n\nexport interface AnnotationMenuProps {\n children: React.ReactNode;\n /** Gap in screen px between the selection box and the menu (default 15). */\n gap?: number;\n /** Where to place the menu relative to the selection box. Default 'top'. */\n placement?: AnchoredPlacement;\n}\n\n/**\n * Floats over the current annotation selection (one anchor regardless of\n * cross-page selection), dodging the rotate knob. Compose the contents from\n * hooks: `useAnnotation()` for the verbs, `useAnnotationSelected()` /\n * `useSelectionProps()` for the data.\n */\nexport function AnnotationMenu({ children, gap = 15, placement = 'top' }: AnnotationMenuProps) {\n // Reading the binding subscribes this component to projection changes\n // (its identity IS the revision), so the anchor read below re-runs with\n // fresh view facts in the SAME commit as the surface — the knob offset is\n // screen-constant, so its content-space position depends on the page's\n // live view scale.\n const { projector } = useProjectorBinding();\n const anchor = useSelector(\n AnnotationHostToken,\n (c) => {\n const a = c.selectionAnchor();\n if (!a) return null;\n const env = projector.viewEnv(a.pon);\n return env ? c.selectionAnchor(env.scale, env.rotation, env.zoom) : a;\n },\n sameAnchor,\n );\n if (!anchor) return null;\n return (\n <Anchored\n anchor={{\n pon: anchor.pon,\n bounds: anchor.bounds,\n ...(anchor.knob ? { avoid: [anchor.knob] } : {}),\n }}\n placement={placement}\n gap={gap}\n >\n {children}\n </Anchored>\n );\n}\n\nexport interface AnnotationDraftMenuProps {\n /** Render prop receiving the DRAFT ANCHOR'S DATA (subtype, pointCount,\n * minPoints, canFinish, …). The verbs are capability calls:\n * `useAnnotation().finishCreationDraft()` / `.cancelCreationDraft()`. */\n children: (anchor: CreationDraftAnchor) => React.ReactNode;\n /** Gap in screen px between the draft anchor and the menu (default 8). */\n gap?: number;\n /** Where to place the menu relative to the draft anchor. Default 'top'. */\n placement?: AnchoredPlacement;\n}\n\n/** Floats over a live multi-click creation draft (polygon, polyline, …). */\nexport function AnnotationDraftMenu({\n children,\n gap = 8,\n placement = 'top',\n}: AnnotationDraftMenuProps) {\n const anchor = useSelector(\n AnnotationHostToken,\n (c) => c.creationDraftAnchor(),\n sameCreationDraftAnchor,\n );\n if (!anchor) return null;\n return (\n <Anchored anchor={anchor} placement={placement} gap={gap}>\n {children(anchor)}\n </Anchored>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,SAAgB,eAAe,EAAE,UAAU,MAAM,IAAI,YAAY,SAA8B;CAM7F,MAAM,EAAE,cAAcA,iBAAAA,oBAAoB;CAC1C,MAAM,SAASC,gBAAAA,YACbC,0CAAAA,kBACC,MAAM;EACL,MAAM,IAAI,EAAE,gBAAgB;EAC5B,IAAI,CAAC,GAAG,OAAO;EACf,MAAM,MAAM,UAAU,QAAQ,EAAE,GAAG;EACnC,OAAO,MAAM,EAAE,gBAAgB,IAAI,OAAO,IAAI,UAAU,IAAI,IAAI,IAAI;CACtE,GACAC,2BAAAA,UACF;CACA,IAAI,CAAC,QAAQ,OAAO;CACpB,OACE,iBAAA,GAAA,kBAAA,IAAA,CAACC,iBAAAA,UAAD;EACE,QAAQ;GACN,KAAK,OAAO;GACZ,QAAQ,OAAO;GACf,GAAI,OAAO,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,EAAE,IAAI,CAAC;EAChD;EACW;EACN;EAEJ;CACO,CAAA;AAEd;;AAcA,SAAgB,oBAAoB,EAClC,UACA,MAAM,GACN,YAAY,SACe;CAC3B,MAAM,SAASH,gBAAAA,YACbC,0CAAAA,kBACC,MAAM,EAAE,oBAAoB,GAC7BG,2BAAAA,uBACF;CACA,IAAI,CAAC,QAAQ,OAAO;CACpB,OACE,iBAAA,GAAA,kBAAA,IAAA,CAACD,iBAAAA,UAAD;EAAkB;EAAmB;EAAgB;EAClD,UAAA,SAAS,MAAM;CACR,CAAA;AAEd"}
@@ -1,7 +1,33 @@
1
- import { o as AnnotationMenuProps, t as AnnotationDraftMenuProps } from "./annotation-B6jwfTCh.cjs";
1
+ import { AnchoredPlacement } from "./anchored.cjs";
2
+ import * as React from "react";
3
+ import { CreationDraftAnchor } from "@embedpdf/core-annotation";
2
4
  //#region src/annotation-menu.d.ts
5
+ interface AnnotationMenuProps {
6
+ children: React.ReactNode;
7
+ /** Gap in screen px between the selection box and the menu (default 15). */
8
+ gap?: number;
9
+ /** Where to place the menu relative to the selection box. Default 'top'. */
10
+ placement?: AnchoredPlacement;
11
+ }
12
+ /**
13
+ * Floats over the current annotation selection (one anchor regardless of
14
+ * cross-page selection), dodging the rotate knob. Compose the contents from
15
+ * hooks: `useAnnotation()` for the verbs, `useAnnotationSelected()` /
16
+ * `useSelectionProps()` for the data.
17
+ */
3
18
  declare function AnnotationMenu({ children, gap, placement }: AnnotationMenuProps): import("react/jsx-runtime").JSX.Element | null;
19
+ interface AnnotationDraftMenuProps {
20
+ /** Render prop receiving the DRAFT ANCHOR'S DATA (subtype, pointCount,
21
+ * minPoints, canFinish, …). The verbs are capability calls:
22
+ * `useAnnotation().finishCreationDraft()` / `.cancelCreationDraft()`. */
23
+ children: (anchor: CreationDraftAnchor) => React.ReactNode;
24
+ /** Gap in screen px between the draft anchor and the menu (default 8). */
25
+ gap?: number;
26
+ /** Where to place the menu relative to the draft anchor. Default 'top'. */
27
+ placement?: AnchoredPlacement;
28
+ }
29
+ /** Floats over a live multi-click creation draft (polygon, polyline, …). */
4
30
  declare function AnnotationDraftMenu({ children, gap, placement }: AnnotationDraftMenuProps): import("react/jsx-runtime").JSX.Element | null;
5
31
  //#endregion
6
- export { AnnotationDraftMenu, AnnotationMenu };
32
+ export { AnnotationDraftMenu, AnnotationDraftMenuProps, AnnotationMenu, AnnotationMenuProps };
7
33
  //# sourceMappingURL=annotation-menu.d.cts.map
@@ -1,7 +1,33 @@
1
- import { o as AnnotationMenuProps, t as AnnotationDraftMenuProps } from "./annotation-nUMefyjw.js";
1
+ import { AnchoredPlacement } from "./anchored.js";
2
+ import * as React from "react";
3
+ import { CreationDraftAnchor } from "@embedpdf/core-annotation";
2
4
  //#region src/annotation-menu.d.ts
5
+ interface AnnotationMenuProps {
6
+ children: React.ReactNode;
7
+ /** Gap in screen px between the selection box and the menu (default 15). */
8
+ gap?: number;
9
+ /** Where to place the menu relative to the selection box. Default 'top'. */
10
+ placement?: AnchoredPlacement;
11
+ }
12
+ /**
13
+ * Floats over the current annotation selection (one anchor regardless of
14
+ * cross-page selection), dodging the rotate knob. Compose the contents from
15
+ * hooks: `useAnnotation()` for the verbs, `useAnnotationSelected()` /
16
+ * `useSelectionProps()` for the data.
17
+ */
3
18
  declare function AnnotationMenu({ children, gap, placement }: AnnotationMenuProps): import("react/jsx-runtime").JSX.Element | null;
19
+ interface AnnotationDraftMenuProps {
20
+ /** Render prop receiving the DRAFT ANCHOR'S DATA (subtype, pointCount,
21
+ * minPoints, canFinish, …). The verbs are capability calls:
22
+ * `useAnnotation().finishCreationDraft()` / `.cancelCreationDraft()`. */
23
+ children: (anchor: CreationDraftAnchor) => React.ReactNode;
24
+ /** Gap in screen px between the draft anchor and the menu (default 8). */
25
+ gap?: number;
26
+ /** Where to place the menu relative to the draft anchor. Default 'top'. */
27
+ placement?: AnchoredPlacement;
28
+ }
29
+ /** Floats over a live multi-click creation draft (polygon, polyline, …). */
4
30
  declare function AnnotationDraftMenu({ children, gap, placement }: AnnotationDraftMenuProps): import("react/jsx-runtime").JSX.Element | null;
5
31
  //#endregion
6
- export { AnnotationDraftMenu, AnnotationMenu };
32
+ export { AnnotationDraftMenu, AnnotationDraftMenuProps, AnnotationMenu, AnnotationMenuProps };
7
33
  //# sourceMappingURL=annotation-menu.d.ts.map
@@ -1,96 +1,45 @@
1
- import { useCapability, useSelector } from "./runtime.js";
2
- import { a as sameAnchor, l as useAnnotationSelected, m as positionMenuAroundRect, o as sameCreationDraftAnchor } from "./annotation-DjGWJljY.js";
1
+ import { useSelector } from "./runtime.js";
2
+ import { Anchored, useProjectorBinding } from "./anchored.js";
3
+ import { n as sameCreationDraftAnchor, t as sameAnchor } from "./annotation-anchors-CetwM1g7.js";
3
4
  import { jsx } from "react/jsx-runtime";
4
- import { useEffect, useRef } from "react";
5
- import { StageToken } from "@embedpdf/plugin-stage";
6
- import { AnnotationToken } from "@embedpdf/plugin-annotation/internal";
5
+ import "react";
6
+ import { AnnotationToken } from "@embedpdf/plugin-annotation/contract/host";
7
7
  //#region src/annotation-menu.tsx
8
+ /**
9
+ * Floats over the current annotation selection (one anchor regardless of
10
+ * cross-page selection), dodging the rotate knob. Compose the contents from
11
+ * hooks: `useAnnotation()` for the verbs, `useAnnotationSelected()` /
12
+ * `useSelectionProps()` for the data.
13
+ */
8
14
  function AnnotationMenu({ children, gap = 15, placement = "top" }) {
9
- const stage = useCapability(StageToken);
10
- const anno = useCapability(AnnotationToken);
11
- useSelector(StageToken, (c) => c.visiblePages());
15
+ const { projector } = useProjectorBinding();
12
16
  const anchor = useSelector(AnnotationToken, (c) => {
13
17
  const a = c.selectionAnchor();
14
18
  if (!a) return null;
15
- const t = stage.pageRect(a.pon)?.transform;
16
- return t?.viewScale ? c.selectionAnchor(t.viewScale, t.rotation, t.zoom) : a;
19
+ const env = projector.viewEnv(a.pon);
20
+ return env ? c.selectionAnchor(env.scale, env.rotation, env.zoom) : a;
17
21
  }, sameAnchor);
18
- const selected = useAnnotationSelected();
19
- const ref = useRef(null);
20
- useEffect(() => {
21
- const el = ref.current;
22
- if (!el) return;
23
- const stop = (e) => e.stopPropagation();
24
- el.addEventListener("pointerdown", stop);
25
- return () => el.removeEventListener("pointerdown", stop);
26
- });
27
22
  if (!anchor) return null;
28
- const box = stage.pageRectToScreen(anchor.pon, anchor.bounds);
29
- if (!box) return null;
30
- const kr = anchor.knob ? stage.pageRectToScreen(anchor.pon, {
31
- x: anchor.knob.x,
32
- y: anchor.knob.y,
33
- width: 0,
34
- height: 0
35
- }) : null;
36
- const pos = positionMenuAroundRect(box, placement, gap, kr ? {
37
- x: kr.x,
38
- y: kr.y
39
- } : null);
40
- return /* @__PURE__ */ jsx("div", {
41
- ref,
42
- style: {
43
- position: "absolute",
44
- left: pos.left,
45
- top: pos.top,
46
- transform: pos.transform,
47
- pointerEvents: "auto"
23
+ return /* @__PURE__ */ jsx(Anchored, {
24
+ anchor: {
25
+ pon: anchor.pon,
26
+ bounds: anchor.bounds,
27
+ ...anchor.knob ? { avoid: [anchor.knob] } : {}
48
28
  },
49
- children: children({
50
- selected,
51
- deleteSelection: anno.deleteSelection,
52
- deselect: anno.deselect,
53
- updateSelection: anno.updateSelection,
54
- rotate90: anno.rotateSelection90,
55
- resetRotation: anno.resetSelectionRotation,
56
- group: anno.group,
57
- ungroup: anno.ungroup,
58
- canGroup: anno.canGroup(),
59
- canUngroup: anno.canUngroup()
60
- })
29
+ placement,
30
+ gap,
31
+ children
61
32
  });
62
33
  }
34
+ /** Floats over a live multi-click creation draft (polygon, polyline, …). */
63
35
  function AnnotationDraftMenu({ children, gap = 8, placement = "top" }) {
64
- const stage = useCapability(StageToken);
65
- const anno = useCapability(AnnotationToken);
66
- useSelector(StageToken, (c) => c.visiblePages());
67
36
  const anchor = useSelector(AnnotationToken, (c) => c.creationDraftAnchor(), sameCreationDraftAnchor);
68
- const ref = useRef(null);
69
- useEffect(() => {
70
- const el = ref.current;
71
- if (!el) return;
72
- const stop = (e) => e.stopPropagation();
73
- el.addEventListener("pointerdown", stop);
74
- return () => el.removeEventListener("pointerdown", stop);
75
- });
76
37
  if (!anchor) return null;
77
- const box = stage.pageRectToScreen(anchor.pon, anchor.bounds);
78
- if (!box) return null;
79
- const pos = positionMenuAroundRect(box, placement, gap);
80
- return /* @__PURE__ */ jsx("div", {
81
- ref,
82
- style: {
83
- position: "absolute",
84
- left: pos.left,
85
- top: pos.top,
86
- transform: pos.transform,
87
- pointerEvents: "auto"
88
- },
89
- children: children({
90
- ...anchor,
91
- finish: anno.finishCreationDraft,
92
- cancel: anno.cancelCreationDraft
93
- })
38
+ return /* @__PURE__ */ jsx(Anchored, {
39
+ anchor,
40
+ placement,
41
+ gap,
42
+ children: children(anchor)
94
43
  });
95
44
  }
96
45
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"annotation-menu.js","names":["AnnotationHostToken"],"sources":["../src/annotation-menu.tsx"],"sourcesContent":["/**\n * <AnnotationMenu> — the DEFAULT, Stage-bound selection menu.\n *\n * Marker-style: it transforms the selected content rect through the Stage camera\n * and renders an upright menu in viewport space. Mount it in the Stage `overlay`\n * slot. It re-renders on `visiblePages()`, so it tracks pan/zoom and page\n * rotation/layout changes with no DOM measurement, no portal, no scroll listeners.\n *\n * This module is the ONLY annotation menu that imports `@embedpdf/plugin-stage`.\n * For a Stage-free `<PageView>`, use `<PageAnnotationMenu>` from\n * `@embedpdf/react/annotation` instead.\n */\nimport * as React from 'react';\nimport { useEffect, useRef } from 'react';\nimport { StageToken } from '@embedpdf/plugin-stage';\nimport { AnnotationToken as AnnotationHostToken } from '@embedpdf/plugin-annotation/internal';\nimport { useCapability, useSelector } from './runtime';\nimport {\n sameAnchor,\n sameCreationDraftAnchor,\n useAnnotationSelected,\n type AnnotationDraftMenuProps,\n type AnnotationMenuProps,\n} from './annotation';\nimport { positionMenuAroundRect } from './internal/annotation-menu-position';\n\nexport function AnnotationMenu({ children, gap = 15, placement = 'top' }: AnnotationMenuProps) {\n const stage = useCapability(StageToken);\n const anno = useCapability(AnnotationHostToken);\n // Reposition on pan/zoom AND rotation/layout changes. `visiblePages()` folds in\n // the scene key, camera, viewport, and DPR, while staying referentially stable.\n useSelector(StageToken, (c) => c.visiblePages());\n // Two-step anchor resolve: the pon comes from the scale-less anchor, then the\n // knob is re-projected with THAT page's view scale so the menu dodges the knob\n // where it actually renders (screen-constant stalk).\n const anchor = useSelector(\n AnnotationHostToken,\n (c) => {\n const a = c.selectionAnchor();\n if (!a) return null;\n const t = stage.pageRect(a.pon)?.transform;\n return t?.viewScale ? c.selectionAnchor(t.viewScale, t.rotation, t.zoom) : a;\n },\n sameAnchor,\n );\n const selected = useAnnotationSelected();\n\n // Isolate the menu from the Stage's pointer forwarding: a pointerdown inside it\n // must NOT bubble up to the Stage container's NATIVE listener, which would\n // forward to the interaction hub and read it as a click-outside → deselect (and\n // unmount this menu). Native listener (not React's) so it runs during real DOM\n // bubbling, before the Stage's own native listener on the ancestor. Inner button\n // `onClick` still fires — `click` is a separate event that reaches React's root.\n const ref = useRef<HTMLDivElement>(null);\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n const stop = (e: Event) => e.stopPropagation();\n el.addEventListener('pointerdown', stop);\n return () => el.removeEventListener('pointerdown', stop);\n });\n\n if (!anchor) return null;\n const box = stage.pageRectToScreen(anchor.pon, anchor.bounds);\n if (!box) return null;\n // Transform the knob via a zero-size rect so page rotation is respected; the\n // menu then nudges only the edge it sits on (and only if the knob protrudes).\n const kr = anchor.knob\n ? stage.pageRectToScreen(anchor.pon, {\n x: anchor.knob.x,\n y: anchor.knob.y,\n width: 0,\n height: 0,\n })\n : null;\n const pos = positionMenuAroundRect(box, placement, gap, kr ? { x: kr.x, y: kr.y } : null);\n return (\n <div\n ref={ref}\n style={{\n position: 'absolute',\n left: pos.left,\n top: pos.top,\n transform: pos.transform,\n pointerEvents: 'auto',\n }}\n >\n {children({\n selected,\n deleteSelection: anno.deleteSelection,\n deselect: anno.deselect,\n updateSelection: anno.updateSelection,\n rotate90: anno.rotateSelection90,\n resetRotation: anno.resetSelectionRotation,\n group: anno.group,\n ungroup: anno.ungroup,\n canGroup: anno.canGroup(),\n canUngroup: anno.canUngroup(),\n })}\n </div>\n );\n}\n\nexport function AnnotationDraftMenu({\n children,\n gap = 8,\n placement = 'top',\n}: AnnotationDraftMenuProps) {\n const stage = useCapability(StageToken);\n const anno = useCapability(AnnotationHostToken);\n useSelector(StageToken, (c) => c.visiblePages());\n const anchor = useSelector(\n AnnotationHostToken,\n (c) => c.creationDraftAnchor(),\n sameCreationDraftAnchor,\n );\n\n const ref = useRef<HTMLDivElement>(null);\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n const stop = (e: Event) => e.stopPropagation();\n el.addEventListener('pointerdown', stop);\n return () => el.removeEventListener('pointerdown', stop);\n });\n\n if (!anchor) return null;\n const box = stage.pageRectToScreen(anchor.pon, anchor.bounds);\n if (!box) return null;\n const pos = positionMenuAroundRect(box, placement, gap);\n return (\n <div\n ref={ref}\n style={{\n position: 'absolute',\n left: pos.left,\n top: pos.top,\n transform: pos.transform,\n pointerEvents: 'auto',\n }}\n >\n {children({\n ...anchor,\n finish: anno.finishCreationDraft,\n cancel: anno.cancelCreationDraft,\n })}\n </div>\n );\n}\n"],"mappings":";;;;;;;AA0BA,SAAgB,eAAe,EAAE,UAAU,MAAM,IAAI,YAAY,SAA8B;CAC7F,MAAM,QAAQ,cAAc,UAAU;CACtC,MAAM,OAAO,cAAcA,eAAmB;CAG9C,YAAY,aAAa,MAAM,EAAE,aAAa,CAAC;CAI/C,MAAM,SAAS,YACbA,kBACC,MAAM;EACL,MAAM,IAAI,EAAE,gBAAgB;EAC5B,IAAI,CAAC,GAAG,OAAO;EACf,MAAM,IAAI,MAAM,SAAS,EAAE,GAAG,CAAC,EAAE;EACjC,OAAO,GAAG,YAAY,EAAE,gBAAgB,EAAE,WAAW,EAAE,UAAU,EAAE,IAAI,IAAI;CAC7E,GACA,UACF;CACA,MAAM,WAAW,sBAAsB;CAQvC,MAAM,MAAM,OAAuB,IAAI;CACvC,gBAAgB;EACd,MAAM,KAAK,IAAI;EACf,IAAI,CAAC,IAAI;EACT,MAAM,QAAQ,MAAa,EAAE,gBAAgB;EAC7C,GAAG,iBAAiB,eAAe,IAAI;EACvC,aAAa,GAAG,oBAAoB,eAAe,IAAI;CACzD,CAAC;CAED,IAAI,CAAC,QAAQ,OAAO;CACpB,MAAM,MAAM,MAAM,iBAAiB,OAAO,KAAK,OAAO,MAAM;CAC5D,IAAI,CAAC,KAAK,OAAO;CAGjB,MAAM,KAAK,OAAO,OACd,MAAM,iBAAiB,OAAO,KAAK;EACjC,GAAG,OAAO,KAAK;EACf,GAAG,OAAO,KAAK;EACf,OAAO;EACP,QAAQ;CACV,CAAC,IACD;CACJ,MAAM,MAAM,uBAAuB,KAAK,WAAW,KAAK,KAAK;EAAE,GAAG,GAAG;EAAG,GAAG,GAAG;CAAE,IAAI,IAAI;CACxF,OACE,oBAAC,OAAD;EACO;EACL,OAAO;GACL,UAAU;GACV,MAAM,IAAI;GACV,KAAK,IAAI;GACT,WAAW,IAAI;GACf,eAAe;EACjB;EAEC,UAAA,SAAS;GACR;GACA,iBAAiB,KAAK;GACtB,UAAU,KAAK;GACf,iBAAiB,KAAK;GACtB,UAAU,KAAK;GACf,eAAe,KAAK;GACpB,OAAO,KAAK;GACZ,SAAS,KAAK;GACd,UAAU,KAAK,SAAS;GACxB,YAAY,KAAK,WAAW;EAC9B,CAAC;CACE,CAAA;AAET;AAEA,SAAgB,oBAAoB,EAClC,UACA,MAAM,GACN,YAAY,SACe;CAC3B,MAAM,QAAQ,cAAc,UAAU;CACtC,MAAM,OAAO,cAAcA,eAAmB;CAC9C,YAAY,aAAa,MAAM,EAAE,aAAa,CAAC;CAC/C,MAAM,SAAS,YACbA,kBACC,MAAM,EAAE,oBAAoB,GAC7B,uBACF;CAEA,MAAM,MAAM,OAAuB,IAAI;CACvC,gBAAgB;EACd,MAAM,KAAK,IAAI;EACf,IAAI,CAAC,IAAI;EACT,MAAM,QAAQ,MAAa,EAAE,gBAAgB;EAC7C,GAAG,iBAAiB,eAAe,IAAI;EACvC,aAAa,GAAG,oBAAoB,eAAe,IAAI;CACzD,CAAC;CAED,IAAI,CAAC,QAAQ,OAAO;CACpB,MAAM,MAAM,MAAM,iBAAiB,OAAO,KAAK,OAAO,MAAM;CAC5D,IAAI,CAAC,KAAK,OAAO;CACjB,MAAM,MAAM,uBAAuB,KAAK,WAAW,GAAG;CACtD,OACE,oBAAC,OAAD;EACO;EACL,OAAO;GACL,UAAU;GACV,MAAM,IAAI;GACV,KAAK,IAAI;GACT,WAAW,IAAI;GACf,eAAe;EACjB;EAEC,UAAA,SAAS;GACR,GAAG;GACH,QAAQ,KAAK;GACb,QAAQ,KAAK;EACf,CAAC;CACE,CAAA;AAET"}
1
+ {"version":3,"file":"annotation-menu.js","names":["AnnotationHostToken"],"sources":["../src/annotation-menu.tsx"],"sourcesContent":["/**\n * Annotation menus — thin anchor plumbing over the ONE `<Anchored>` primitive.\n *\n * These work identically under `<Stage>` (mount in the overlay slot; the\n * camera projects, no DOM reads) and `<PageView>` (measured + portalled) —\n * the SURFACE provides the projector, the menu doesn't care.\n *\n * No action bags: actions come from the capability hooks (`useAnnotation()`,\n * `useAnnotationSelected()`, …), which subscribe properly and compose across\n * plugins. Render props carry only the ANCHOR'S DATA (the draft menu's\n * progress facts); the annotation-selection menu takes plain children.\n */\nimport * as React from 'react';\nimport { AnnotationToken as AnnotationHostToken } from '@embedpdf/plugin-annotation/contract/host';\nimport type { CreationDraftAnchor } from '@embedpdf/core-annotation';\nimport { Anchored, useProjectorBinding, type AnchoredPlacement } from './anchored';\nimport { useSelector } from './runtime';\nimport { sameAnchor, sameCreationDraftAnchor } from './annotation-anchors';\n\nexport interface AnnotationMenuProps {\n children: React.ReactNode;\n /** Gap in screen px between the selection box and the menu (default 15). */\n gap?: number;\n /** Where to place the menu relative to the selection box. Default 'top'. */\n placement?: AnchoredPlacement;\n}\n\n/**\n * Floats over the current annotation selection (one anchor regardless of\n * cross-page selection), dodging the rotate knob. Compose the contents from\n * hooks: `useAnnotation()` for the verbs, `useAnnotationSelected()` /\n * `useSelectionProps()` for the data.\n */\nexport function AnnotationMenu({ children, gap = 15, placement = 'top' }: AnnotationMenuProps) {\n // Reading the binding subscribes this component to projection changes\n // (its identity IS the revision), so the anchor read below re-runs with\n // fresh view facts in the SAME commit as the surface — the knob offset is\n // screen-constant, so its content-space position depends on the page's\n // live view scale.\n const { projector } = useProjectorBinding();\n const anchor = useSelector(\n AnnotationHostToken,\n (c) => {\n const a = c.selectionAnchor();\n if (!a) return null;\n const env = projector.viewEnv(a.pon);\n return env ? c.selectionAnchor(env.scale, env.rotation, env.zoom) : a;\n },\n sameAnchor,\n );\n if (!anchor) return null;\n return (\n <Anchored\n anchor={{\n pon: anchor.pon,\n bounds: anchor.bounds,\n ...(anchor.knob ? { avoid: [anchor.knob] } : {}),\n }}\n placement={placement}\n gap={gap}\n >\n {children}\n </Anchored>\n );\n}\n\nexport interface AnnotationDraftMenuProps {\n /** Render prop receiving the DRAFT ANCHOR'S DATA (subtype, pointCount,\n * minPoints, canFinish, …). The verbs are capability calls:\n * `useAnnotation().finishCreationDraft()` / `.cancelCreationDraft()`. */\n children: (anchor: CreationDraftAnchor) => React.ReactNode;\n /** Gap in screen px between the draft anchor and the menu (default 8). */\n gap?: number;\n /** Where to place the menu relative to the draft anchor. Default 'top'. */\n placement?: AnchoredPlacement;\n}\n\n/** Floats over a live multi-click creation draft (polygon, polyline, …). */\nexport function AnnotationDraftMenu({\n children,\n gap = 8,\n placement = 'top',\n}: AnnotationDraftMenuProps) {\n const anchor = useSelector(\n AnnotationHostToken,\n (c) => c.creationDraftAnchor(),\n sameCreationDraftAnchor,\n );\n if (!anchor) return null;\n return (\n <Anchored anchor={anchor} placement={placement} gap={gap}>\n {children(anchor)}\n </Anchored>\n );\n}\n"],"mappings":";;;;;;;;;;;;;AAiCA,SAAgB,eAAe,EAAE,UAAU,MAAM,IAAI,YAAY,SAA8B;CAM7F,MAAM,EAAE,cAAc,oBAAoB;CAC1C,MAAM,SAAS,YACbA,kBACC,MAAM;EACL,MAAM,IAAI,EAAE,gBAAgB;EAC5B,IAAI,CAAC,GAAG,OAAO;EACf,MAAM,MAAM,UAAU,QAAQ,EAAE,GAAG;EACnC,OAAO,MAAM,EAAE,gBAAgB,IAAI,OAAO,IAAI,UAAU,IAAI,IAAI,IAAI;CACtE,GACA,UACF;CACA,IAAI,CAAC,QAAQ,OAAO;CACpB,OACE,oBAAC,UAAD;EACE,QAAQ;GACN,KAAK,OAAO;GACZ,QAAQ,OAAO;GACf,GAAI,OAAO,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,EAAE,IAAI,CAAC;EAChD;EACW;EACN;EAEJ;CACO,CAAA;AAEd;;AAcA,SAAgB,oBAAoB,EAClC,UACA,MAAM,GACN,YAAY,SACe;CAC3B,MAAM,SAAS,YACbA,kBACC,MAAM,EAAE,oBAAoB,GAC7B,uBACF;CACA,IAAI,CAAC,QAAQ,OAAO;CACpB,OACE,oBAAC,UAAD;EAAkB;EAAmB;EAAgB;EAClD,UAAA,SAAS,MAAM;CACR,CAAA;AAEd"}