@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
@@ -0,0 +1,836 @@
1
+ const require_rolldown_runtime = require("./rolldown-runtime-DDXJien1.cjs");
2
+ const require_runtime = require("./runtime.cjs");
3
+ const require_annotation_hooks = require("./annotation-hooks-BB5ZhPpf.cjs");
4
+ let react_jsx_runtime = require("react/jsx-runtime");
5
+ let react = require("react");
6
+ react = require_rolldown_runtime.__toESM(react, 1);
7
+ let _embedpdf_plugin_interaction_contract = require("@embedpdf/plugin-interaction/contract");
8
+ let _embedpdf_plugin_form = require("@embedpdf/plugin-form");
9
+ //#region src/form-focus-ring.tsx
10
+ /**
11
+ * Paint the form focus indicator above baked PDF appearances and transparent
12
+ * native controls. Putting an inset outline on the widget wrapper lets an
13
+ * opaque child cover it, which makes rectangular checkboxes and combo boxes
14
+ * appear unfocused even though they are active in the native Tab order.
15
+ */
16
+ function FormFocusRing({ visible }) {
17
+ if (!visible) return null;
18
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
19
+ "aria-hidden": "true",
20
+ "data-embedpdf-form-focus-ring": "",
21
+ style: {
22
+ position: "absolute",
23
+ inset: 0,
24
+ zIndex: 1,
25
+ boxSizing: "border-box",
26
+ outline: "2px solid rgba(66, 133, 244, 0.8)",
27
+ outlineOffset: -2,
28
+ pointerEvents: "none"
29
+ }
30
+ });
31
+ }
32
+ //#endregion
33
+ //#region src/form-listbox.tsx
34
+ /**
35
+ * A PDF list box needs one owner for pixels, hit-testing, and scrolling. A
36
+ * baked /AP picture with an invisible native select cannot provide that: the
37
+ * browser and PDF each keep an independent top-visible row. This visible
38
+ * native control keeps a local optimistic selection while the engine write is
39
+ * pending and synchronizes when engine truth changes, without remounting the
40
+ * DOM node or resetting its scroll position.
41
+ */
42
+ function NativeListBox({ ariaLabel, disabled, multi, options, selected, onSelect, onFocus, onBlur, style }) {
43
+ const [draft, setDraft] = (0, react.useState)(() => [...selected]);
44
+ const controlRef = (0, react.useRef)(null);
45
+ const selectedRef = (0, react.useRef)(selected);
46
+ selectedRef.current = selected;
47
+ const selectedSignature = JSON.stringify(selected);
48
+ (0, react.useEffect)(() => {
49
+ const control = controlRef.current;
50
+ if (!control) return;
51
+ const isolateWheel = (event) => event.stopPropagation();
52
+ control.addEventListener("wheel", isolateWheel);
53
+ return () => control.removeEventListener("wheel", isolateWheel);
54
+ }, []);
55
+ (0, react.useEffect)(() => {
56
+ setDraft([...selectedRef.current]);
57
+ }, [selectedSignature]);
58
+ const rollback = () => setDraft([...selectedRef.current]);
59
+ const choose = (values) => {
60
+ setDraft(values);
61
+ try {
62
+ const result = onSelect(values);
63
+ if (result) result.catch(rollback);
64
+ } catch {
65
+ rollback();
66
+ }
67
+ };
68
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
69
+ ref: controlRef,
70
+ "aria-label": ariaLabel,
71
+ multiple: multi,
72
+ size: Math.max(2, options.length),
73
+ disabled,
74
+ value: multi ? draft : draft[0] ?? "",
75
+ onFocus,
76
+ onBlur,
77
+ onChange: (event) => choose(Array.from(event.currentTarget.selectedOptions).map((option) => option.value)),
78
+ style,
79
+ children: options.map((option, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
80
+ value: option.value,
81
+ children: option.label
82
+ }, index))
83
+ });
84
+ }
85
+ //#endregion
86
+ //#region src/form.tsx
87
+ /** Content rect → a view-px box (the page wrapper's own coordinate space). */
88
+ function viewBox(r, page) {
89
+ const tl = page.transform.toPixels({
90
+ x: r.x,
91
+ y: r.y
92
+ });
93
+ const br = page.transform.toPixels({
94
+ x: r.x + r.width,
95
+ y: r.y + r.height
96
+ });
97
+ return {
98
+ left: tl.x,
99
+ top: tl.y,
100
+ width: br.x - tl.x,
101
+ height: br.y - tl.y
102
+ };
103
+ }
104
+ /**
105
+ * Keep the interaction hub out of gestures that begin inside a fill control.
106
+ * Native listener (not React's) so it runs during real DOM bubbling, before
107
+ * the Stage's own native listener on an ancestor — same trick as FreeText.
108
+ */
109
+ function useIsolated() {
110
+ const ref = (0, react.useRef)(null);
111
+ (0, react.useEffect)(() => {
112
+ const el = ref.current;
113
+ if (!el) return;
114
+ const stop = (e) => e.stopPropagation();
115
+ el.addEventListener("pointerdown", stop);
116
+ return () => el.removeEventListener("pointerdown", stop);
117
+ }, []);
118
+ return ref;
119
+ }
120
+ /**
121
+ * The widget DOM-event feed (Phase 2): wire one widget surface's pointer and
122
+ * focus events to `form.notifyWidgetEvent` — the `/AA` E/X/D/U/Fo/Bl trigger
123
+ * door. Handlers live on the ALWAYS-ACTIVE event surface, never the inner
124
+ * control: "may edit this field" and "may receive PDF action events" are
125
+ * different rights, and a read-only session (no `doc.forms.fill`) must still
126
+ * see hover tooltips (session Hide needs no write authority).
127
+ */
128
+ function useWidgetEvents(key, annotationRef) {
129
+ const form = require_runtime.useCapability(_embedpdf_plugin_form.FormToken);
130
+ const refBox = (0, react.useRef)(annotationRef);
131
+ refBox.current = annotationRef;
132
+ return (0, react.useMemo)(() => {
133
+ const notify = (event) => {
134
+ const target = refBox.current;
135
+ if (target) form.notifyWidgetEvent(key, target, event);
136
+ };
137
+ return {
138
+ onPointerEnter: () => notify("cursorEnter"),
139
+ onPointerLeave: () => notify("cursorExit"),
140
+ onPointerDown: () => notify("mouseDown"),
141
+ onPointerUp: () => notify("mouseUp"),
142
+ onFocus: () => notify("focus"),
143
+ onBlur: () => notify("blur")
144
+ };
145
+ }, [form, key]);
146
+ }
147
+ /**
148
+ * Widget ACTIVATION: a click on ANY widget runs its `/A`. ISO puts the
149
+ * activate action on the annotation dictionary — any subtype, any field
150
+ * type — so activation is a WIDGET behavior, not a push-button behavior:
151
+ * real-world producers ship "buttons" as read-only text fields carrying an
152
+ * `/A` (the Test Lab's Reset/Next/Hide controls), and Acrobat runs them.
153
+ * A widget without an `/A` resolves inert — dispatching is the cheap,
154
+ * honest way to ask (the dispatcher owns `/A`-vs-`/AA U` precedence).
155
+ * Push buttons keep their own gated door: `disabled` still blocks
156
+ * activation there — unchanged shipped semantics.
157
+ */
158
+ function useWidgetActivation(key, annotationRef) {
159
+ const form = require_runtime.useCapability(_embedpdf_plugin_form.FormToken);
160
+ const refBox = (0, react.useRef)(annotationRef);
161
+ refBox.current = annotationRef;
162
+ return (0, react.useCallback)(() => {
163
+ const target = refBox.current;
164
+ if (target) form.activateWidget(key, target);
165
+ }, [form, key]);
166
+ }
167
+ /** The baked /AP raster, blitted by its OWN box (exactly like BakedImage). */
168
+ function Picture({ page, appearance, apBox, frame, hidden }) {
169
+ if (!appearance) return null;
170
+ const b = viewBox(apBox ?? appearance.box, page);
171
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
172
+ src: appearance.url,
173
+ alt: "",
174
+ draggable: false,
175
+ style: {
176
+ position: "absolute",
177
+ left: b.left - (frame?.left ?? 0),
178
+ top: b.top - (frame?.top ?? 0),
179
+ width: b.width,
180
+ height: b.height,
181
+ maxWidth: "none",
182
+ pointerEvents: "none",
183
+ visibility: hidden ? "hidden" : "visible"
184
+ }
185
+ });
186
+ }
187
+ /**
188
+ * Dispatch one engaged widget to its fill control. The FIELD plane (value,
189
+ * behavior, disabled) comes from the form plugin's single-widget projection;
190
+ * the WIDGET plane (live box, /DA font, baked raster) rides in on the
191
+ * renderer props — no geometry is read from the form model here.
192
+ */
193
+ function FormWidget({ item, page, appearance }) {
194
+ const annot = item.ref?.kind === "objectNumber" ? item.ref.annotObjectNumber : 0;
195
+ const fill = require_runtime.useSelector(_embedpdf_plugin_form.FormToken, (c) => annot > 0 ? c.fillItem(annot) : null);
196
+ if (!fill) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Picture, {
197
+ page,
198
+ appearance,
199
+ apBox: item.apBox
200
+ });
201
+ switch (fill.control) {
202
+ case "text": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TextWidget, {
203
+ fill,
204
+ item,
205
+ page,
206
+ appearance
207
+ });
208
+ case "toggle": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleWidget, {
209
+ fill,
210
+ item,
211
+ page,
212
+ appearance
213
+ });
214
+ case "choice": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChoiceWidget, {
215
+ fill,
216
+ item,
217
+ page,
218
+ appearance
219
+ });
220
+ case "button": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ButtonWidget, {
221
+ fill,
222
+ item,
223
+ page,
224
+ appearance
225
+ });
226
+ }
227
+ }
228
+ function ButtonWidget({ fill, item, page, appearance }) {
229
+ const form = require_runtime.useCapability(_embedpdf_plugin_form.FormToken);
230
+ const wrap = useIsolated();
231
+ const events = useWidgetEvents(fill.key, item.ref);
232
+ const b = viewBox(item.box, page);
233
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
234
+ ref: wrap,
235
+ ...events,
236
+ style: {
237
+ position: "absolute",
238
+ left: b.left,
239
+ top: b.top,
240
+ width: b.width,
241
+ height: b.height,
242
+ cursor: fill.disabled ? "default" : "pointer",
243
+ pointerEvents: "auto"
244
+ },
245
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
246
+ type: "button",
247
+ "aria-label": fill.label,
248
+ disabled: fill.disabled,
249
+ onClick: () => {
250
+ if (item.ref) form.activateWidget(fill.key, item.ref);
251
+ },
252
+ style: {
253
+ position: "absolute",
254
+ inset: 0,
255
+ width: "100%",
256
+ height: "100%",
257
+ boxSizing: "border-box",
258
+ padding: 0,
259
+ border: 0,
260
+ background: "transparent",
261
+ cursor: "inherit",
262
+ pointerEvents: fill.disabled ? "none" : "auto"
263
+ },
264
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Picture, {
265
+ page,
266
+ appearance,
267
+ apBox: item.apBox,
268
+ frame: b
269
+ })
270
+ })
271
+ });
272
+ }
273
+ function TextWidget({ fill, item, page, appearance }) {
274
+ const form = require_runtime.useCapability(_embedpdf_plugin_form.FormToken);
275
+ const wrap = useIsolated();
276
+ const events = useWidgetEvents(fill.key, item.ref);
277
+ const activate = useWidgetActivation(fill.key, item.ref);
278
+ const [focused, setFocused] = (0, react.useState)(false);
279
+ const [draft, setDraft] = (0, react.useState)(fill.value);
280
+ const cancelled = (0, react.useRef)(false);
281
+ (0, react.useEffect)(() => {
282
+ if (!focused) setDraft(fill.value);
283
+ }, [fill.value, focused]);
284
+ const b = viewBox(item.box, page);
285
+ const scale = item.box.width > 0 ? b.width / item.box.width : 1;
286
+ const fontSize = item.text && item.text.fontSize > 0 ? item.text.fontSize * scale : fill.multiline ? 12 * scale : Math.max(6, b.height * .72);
287
+ const editorStyle = {
288
+ position: "absolute",
289
+ inset: 0,
290
+ width: "100%",
291
+ height: "100%",
292
+ boxSizing: "border-box",
293
+ border: "none",
294
+ outline: focused ? "2px solid rgba(66, 133, 244, 0.8)" : "none",
295
+ outlineOffset: -2,
296
+ padding: "0 2px",
297
+ margin: 0,
298
+ background: item.style.interiorColor ?? "#fff",
299
+ color: item.text?.fontColor ?? "#000",
300
+ fontFamily: item.text?.fontFamily ?? "Helvetica, Arial, sans-serif",
301
+ fontSize,
302
+ textAlign: item.text?.textAlign ?? "left",
303
+ resize: "none",
304
+ cursor: "text",
305
+ opacity: focused ? 1 : 0,
306
+ ...fill.disabled ? { pointerEvents: "none" } : {}
307
+ };
308
+ const editorProps = {
309
+ value: draft,
310
+ maxLength: fill.maxLength ?? void 0,
311
+ "aria-label": fill.label,
312
+ disabled: fill.disabled,
313
+ onFocus: () => setFocused(true),
314
+ onChange: (e) => setDraft(e.target.value),
315
+ onBlur: () => {
316
+ setFocused(false);
317
+ if (cancelled.current) {
318
+ cancelled.current = false;
319
+ setDraft(fill.value);
320
+ return;
321
+ }
322
+ if (draft !== fill.value) form.setText(fill.key, draft);
323
+ },
324
+ style: editorStyle
325
+ };
326
+ const onKeys = (e) => {
327
+ if (e.key === "Escape") {
328
+ cancelled.current = true;
329
+ e.currentTarget.blur();
330
+ }
331
+ if (e.key === "Enter" && !fill.multiline) e.currentTarget.blur();
332
+ };
333
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
334
+ ref: wrap,
335
+ ...events,
336
+ onClick: activate,
337
+ style: {
338
+ position: "absolute",
339
+ left: b.left,
340
+ top: b.top,
341
+ width: b.width,
342
+ height: b.height,
343
+ pointerEvents: "auto"
344
+ },
345
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Picture, {
346
+ page,
347
+ appearance,
348
+ apBox: item.apBox,
349
+ frame: b,
350
+ hidden: focused
351
+ }), fill.multiline ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
352
+ ...editorProps,
353
+ onKeyDown: onKeys
354
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
355
+ ...editorProps,
356
+ type: fill.password ? "password" : "text",
357
+ onKeyDown: onKeys
358
+ })]
359
+ });
360
+ }
361
+ function ToggleWidget({ fill, item, page, appearance }) {
362
+ const form = require_runtime.useCapability(_embedpdf_plugin_form.FormToken);
363
+ const wrap = useIsolated();
364
+ const events = useWidgetEvents(fill.key, item.ref);
365
+ const activate = useWidgetActivation(fill.key, item.ref);
366
+ const [focused, setFocused] = (0, react.useState)(false);
367
+ const b = viewBox(item.box, page);
368
+ const press = () => {
369
+ const flipped = fill.disabled ? void 0 : form.toggle(fill.key, fill.kind === "checkbox" && fill.checked ? null : fill.onState);
370
+ Promise.resolve(flipped).then(activate, activate);
371
+ };
372
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
373
+ ref: wrap,
374
+ role: fill.kind === "checkbox" ? "checkbox" : "radio",
375
+ "aria-checked": fill.checked,
376
+ "aria-label": fill.label,
377
+ tabIndex: fill.disabled ? -1 : 0,
378
+ ...events,
379
+ onClick: press,
380
+ onFocus: (e) => {
381
+ setFocused(true);
382
+ events.onFocus?.(e);
383
+ },
384
+ onBlur: (e) => {
385
+ setFocused(false);
386
+ events.onBlur?.(e);
387
+ },
388
+ onKeyDown: (e) => {
389
+ if (e.key === " " || e.key === "Enter") {
390
+ e.preventDefault();
391
+ press();
392
+ }
393
+ },
394
+ style: {
395
+ position: "absolute",
396
+ left: b.left,
397
+ top: b.top,
398
+ width: b.width,
399
+ height: b.height,
400
+ cursor: fill.disabled ? "default" : "pointer",
401
+ pointerEvents: "auto",
402
+ outline: "none"
403
+ },
404
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Picture, {
405
+ page,
406
+ appearance,
407
+ apBox: item.apBox,
408
+ frame: b
409
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FormFocusRing, { visible: focused })]
410
+ });
411
+ }
412
+ function ChoiceWidget(props) {
413
+ return props.fill.kind === "list" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ListBoxWidget, { ...props }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ComboBoxWidget, { ...props });
414
+ }
415
+ function ChoiceFrame({ fill, item, page, focused, innerRef, children }) {
416
+ const events = useWidgetEvents(fill.key, item.ref);
417
+ const activate = useWidgetActivation(fill.key, item.ref);
418
+ const b = viewBox(item.box, page);
419
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
420
+ ref: innerRef,
421
+ ...events,
422
+ onClick: activate,
423
+ style: {
424
+ position: "absolute",
425
+ left: b.left,
426
+ top: b.top,
427
+ width: b.width,
428
+ height: b.height,
429
+ pointerEvents: "auto",
430
+ outline: "none"
431
+ },
432
+ children: [children, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FormFocusRing, { visible: focused })]
433
+ });
434
+ }
435
+ function ComboBoxWidget({ fill, item, page, appearance }) {
436
+ const form = require_runtime.useCapability(_embedpdf_plugin_form.FormToken);
437
+ const wrap = useIsolated();
438
+ const [focused, setFocused] = (0, react.useState)(false);
439
+ const b = viewBox(item.box, page);
440
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ChoiceFrame, {
441
+ fill,
442
+ item,
443
+ page,
444
+ focused,
445
+ innerRef: wrap,
446
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Picture, {
447
+ page,
448
+ appearance,
449
+ apBox: item.apBox,
450
+ frame: b
451
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
452
+ defaultValue: fill.selected[0] ?? "",
453
+ "aria-label": fill.label,
454
+ disabled: fill.disabled,
455
+ onFocus: () => setFocused(true),
456
+ onBlur: () => setFocused(false),
457
+ onChange: (e) => {
458
+ const values = Array.from(e.currentTarget.selectedOptions).map((o) => o.value);
459
+ form.choose(fill.key, values);
460
+ },
461
+ style: {
462
+ position: "absolute",
463
+ inset: 0,
464
+ width: "100%",
465
+ height: "100%",
466
+ opacity: 0,
467
+ cursor: fill.disabled ? "default" : "pointer",
468
+ ...fill.disabled ? { pointerEvents: "none" } : {}
469
+ },
470
+ children: fill.options.map((o, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
471
+ value: o.value,
472
+ children: o.label
473
+ }, i))
474
+ }, fill.selected.join("\0"))]
475
+ });
476
+ }
477
+ function ListBoxWidget({ fill, item, page }) {
478
+ const form = require_runtime.useCapability(_embedpdf_plugin_form.FormToken);
479
+ const wrap = useIsolated();
480
+ const [focused, setFocused] = (0, react.useState)(false);
481
+ const b = viewBox(item.box, page);
482
+ const scale = item.box.width > 0 ? b.width / item.box.width : 1;
483
+ const strokeWidth = item.style.strokeWidth * scale;
484
+ const borderStyle = item.style.border.kind === "dashed" ? "dashed" : "solid";
485
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChoiceFrame, {
486
+ fill,
487
+ item,
488
+ page,
489
+ focused,
490
+ innerRef: wrap,
491
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NativeListBox, {
492
+ ariaLabel: fill.label,
493
+ disabled: fill.disabled,
494
+ multi: fill.multi,
495
+ options: fill.options,
496
+ selected: fill.selected,
497
+ onFocus: () => setFocused(true),
498
+ onBlur: () => setFocused(false),
499
+ onSelect: (values) => form.choose(fill.key, values),
500
+ style: {
501
+ position: "absolute",
502
+ inset: 0,
503
+ width: "100%",
504
+ height: "100%",
505
+ boxSizing: "border-box",
506
+ margin: 0,
507
+ padding: 0,
508
+ borderWidth: strokeWidth,
509
+ borderStyle,
510
+ borderColor: item.style.color,
511
+ borderRadius: 0,
512
+ outline: "none",
513
+ background: item.style.interiorColor ?? "#fff",
514
+ color: item.text?.fontColor ?? "#000",
515
+ fontFamily: item.text?.fontFamily ?? "Helvetica, Arial, sans-serif",
516
+ fontSize: (item.text?.fontSize || 12) * scale,
517
+ textAlign: item.text?.textAlign ?? "left",
518
+ cursor: fill.disabled ? "default" : "pointer",
519
+ ...fill.disabled ? { pointerEvents: "none" } : {}
520
+ }
521
+ })
522
+ });
523
+ }
524
+ /**
525
+ * The form plugin's renderer entry, ready for
526
+ * `<AnnotationLayer renderers={[formWidgetRenderer]} />`. It answers for the
527
+ * PLUGIN-registered 'form-widgets' Behavior — the form plugin decides when
528
+ * widgets are fill controls vs. editable annotations, never the app.
529
+ */
530
+ const formWidgetRenderer = {
531
+ behavior: "form-widgets",
532
+ component: FormWidget
533
+ };
534
+ const controlBase = {
535
+ position: "absolute",
536
+ boxSizing: "border-box",
537
+ border: "1px solid rgba(56, 88, 233, 0.55)",
538
+ background: "rgba(255, 255, 255, 0.92)",
539
+ borderRadius: 2,
540
+ font: "inherit"
541
+ };
542
+ const fillBox = (item, page) => viewBox(item.box, page);
543
+ /** The standalone layer's event surface: positions ONE control's box and
544
+ * carries the /AA DOM-event feed — always pointer-active (see
545
+ * useWidgetEvents); the control inside fills it and self-gates edits.
546
+ * With `activate`, a click on the box runs the widget's /A (see
547
+ * useWidgetActivation — activation is a WIDGET behavior); push buttons
548
+ * keep their own gated inner door and do NOT set it. */
549
+ function FillEventBox({ item, page, cursor, activate, children }) {
550
+ const events = useWidgetEvents(item.key, {
551
+ kind: "objectNumber",
552
+ pageObjectNumber: page.pon,
553
+ annotObjectNumber: item.annotObjectNumber
554
+ });
555
+ const onActivate = useWidgetActivation(item.key, {
556
+ kind: "objectNumber",
557
+ pageObjectNumber: page.pon,
558
+ annotObjectNumber: item.annotObjectNumber
559
+ });
560
+ const css = fillBox(item, page);
561
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
562
+ ...events,
563
+ onClick: activate ? onActivate : void 0,
564
+ style: {
565
+ position: "absolute",
566
+ ...css,
567
+ pointerEvents: "auto",
568
+ ...cursor ? { cursor } : {}
569
+ },
570
+ children
571
+ });
572
+ }
573
+ const fillControl = {
574
+ position: "absolute",
575
+ inset: 0,
576
+ width: "100%",
577
+ height: "100%"
578
+ };
579
+ /** Text control: keystrokes stay LOCAL (the input is the draft store);
580
+ * the engine write happens on blur or Enter. */
581
+ function FillText({ item, page }) {
582
+ const form = require_runtime.useCapability(_embedpdf_plugin_form.FormToken);
583
+ const [draft, setDraft] = (0, react.useState)(item.value);
584
+ (0, react.useEffect)(() => setDraft(item.value), [item.value]);
585
+ const css = fillBox(item, page);
586
+ const fontSize = Math.max(9, Math.min(css.height * .62, 24));
587
+ const commit = () => {
588
+ if (draft !== item.value) form.setText(item.key, draft).catch(() => setDraft(item.value));
589
+ };
590
+ const shared = {
591
+ ...controlBase,
592
+ ...fillControl,
593
+ fontSize,
594
+ padding: "0 3px",
595
+ ...item.comb ? { letterSpacing: css.width / Math.max(1, item.maxLength ?? 1) / 2 } : {}
596
+ };
597
+ const editorGate = item.disabled ? { pointerEvents: "none" } : {};
598
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FillEventBox, {
599
+ item,
600
+ page,
601
+ activate: true,
602
+ children: item.multiline ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
603
+ "aria-label": item.label,
604
+ value: draft,
605
+ disabled: item.disabled,
606
+ onChange: (e) => setDraft(e.target.value),
607
+ onBlur: commit,
608
+ style: {
609
+ ...shared,
610
+ resize: "none",
611
+ ...editorGate
612
+ }
613
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
614
+ "aria-label": item.label,
615
+ type: item.password ? "password" : "text",
616
+ value: draft,
617
+ maxLength: item.maxLength ?? void 0,
618
+ disabled: item.disabled,
619
+ onChange: (e) => setDraft(e.target.value),
620
+ onBlur: commit,
621
+ onKeyDown: (e) => {
622
+ if (e.key === "Enter") e.target.blur();
623
+ },
624
+ style: {
625
+ ...shared,
626
+ ...editorGate
627
+ }
628
+ })
629
+ });
630
+ }
631
+ function FillToggle({ item, page }) {
632
+ const form = require_runtime.useCapability(_embedpdf_plugin_form.FormToken);
633
+ const activate = useWidgetActivation(item.key, {
634
+ kind: "objectNumber",
635
+ pageObjectNumber: page.pon,
636
+ annotObjectNumber: item.annotObjectNumber
637
+ });
638
+ const css = fillBox(item, page);
639
+ const glyphSize = Math.min(css.width, css.height) * .72;
640
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FillEventBox, {
641
+ item,
642
+ page,
643
+ cursor: item.disabled ? "default" : "pointer",
644
+ activate: true,
645
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
646
+ role: item.kind,
647
+ "aria-checked": item.checked,
648
+ "aria-label": item.label,
649
+ disabled: item.disabled,
650
+ onClick: (e) => {
651
+ e.stopPropagation();
652
+ Promise.resolve(form.toggle(item.key, item.kind === "checkbox" && item.checked ? null : item.onState)).then(activate, activate);
653
+ },
654
+ style: {
655
+ ...controlBase,
656
+ ...fillControl,
657
+ cursor: "inherit",
658
+ display: "grid",
659
+ placeItems: "center",
660
+ padding: 0,
661
+ borderRadius: item.kind === "radio" ? "50%" : 2,
662
+ fontSize: glyphSize,
663
+ lineHeight: 1,
664
+ color: "#1f2a44",
665
+ ...item.disabled ? { pointerEvents: "none" } : {}
666
+ },
667
+ children: item.checked ? item.kind === "radio" ? "●" : "✓" : ""
668
+ })
669
+ });
670
+ }
671
+ function FillChoice({ item, page }) {
672
+ const form = require_runtime.useCapability(_embedpdf_plugin_form.FormToken);
673
+ const css = fillBox(item, page);
674
+ const controlGate = item.disabled ? { pointerEvents: "none" } : {};
675
+ if (item.kind === "list") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FillEventBox, {
676
+ item,
677
+ page,
678
+ activate: true,
679
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NativeListBox, {
680
+ ariaLabel: item.label,
681
+ disabled: item.disabled,
682
+ multi: item.multi,
683
+ options: item.options,
684
+ selected: item.selected,
685
+ onSelect: (values) => form.choose(item.key, values),
686
+ style: {
687
+ ...controlBase,
688
+ ...fillControl,
689
+ fontSize: Math.max(9, Math.min(css.height * .55, 18)),
690
+ ...controlGate
691
+ }
692
+ })
693
+ });
694
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FillEventBox, {
695
+ item,
696
+ page,
697
+ activate: true,
698
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
699
+ "aria-label": item.label,
700
+ disabled: item.disabled,
701
+ value: item.selected[0] ?? "",
702
+ onChange: (e) => void form.choose(item.key, [e.target.value]),
703
+ style: {
704
+ ...controlBase,
705
+ ...fillControl,
706
+ fontSize: Math.max(9, Math.min(css.height * .55, 18)),
707
+ ...controlGate
708
+ },
709
+ children: [item.selected.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", { value: "" }), item.options.map((o) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
710
+ value: o.value,
711
+ children: o.label
712
+ }, o.value))]
713
+ })
714
+ });
715
+ }
716
+ function FillButton({ item, page }) {
717
+ const form = require_runtime.useCapability(_embedpdf_plugin_form.FormToken);
718
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FillEventBox, {
719
+ item,
720
+ page,
721
+ cursor: item.disabled ? "default" : "pointer",
722
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
723
+ type: "button",
724
+ "aria-label": item.label,
725
+ disabled: item.disabled,
726
+ onClick: () => void form.activateWidget(item.key, {
727
+ kind: "objectNumber",
728
+ pageObjectNumber: page.pon,
729
+ annotObjectNumber: item.annotObjectNumber
730
+ }),
731
+ style: {
732
+ ...controlBase,
733
+ ...fillControl,
734
+ padding: 0,
735
+ border: 0,
736
+ background: "transparent",
737
+ cursor: "inherit",
738
+ pointerEvents: item.disabled ? "none" : "auto"
739
+ }
740
+ })
741
+ });
742
+ }
743
+ /**
744
+ * Fill-mode form controls for one page, positioned from the form model's own
745
+ * widget geometry — the ANNOTATION-LESS path (a fill-only viewer whose page
746
+ * pixels come from an annotated RenderLayer raster). Active whenever the
747
+ * active tool carries the 'form-fill' tag (the built-in pointer/pan tools
748
+ * do); any other tool stands the controls down. Do not mount next to an
749
+ * AnnotationLayer wired with `formWidgetBehaviors`.
750
+ */
751
+ function FormLayer() {
752
+ const page = require_runtime.usePage();
753
+ const form = require_runtime.useCapability(_embedpdf_plugin_form.FormToken);
754
+ const active = require_runtime.useSelector(_embedpdf_plugin_interaction_contract.InteractionToken, (c) => c.activeTool().enables.has("form-fill"));
755
+ (0, react.useEffect)(() => {
756
+ if (active) form.ensureGeom(page.pon);
757
+ }, [
758
+ active,
759
+ form,
760
+ page.pon
761
+ ]);
762
+ const items = require_runtime.useSelector(_embedpdf_plugin_form.FormToken, (c) => c.fillItems(page.pon), require_runtime.shallowArray);
763
+ if (!active) return null;
764
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
765
+ style: {
766
+ position: "absolute",
767
+ inset: 0
768
+ },
769
+ children: items.map((item) => item.control === "text" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FillText, {
770
+ item,
771
+ page
772
+ }, `${item.key}:${item.annotObjectNumber}`) : item.control === "toggle" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FillToggle, {
773
+ item,
774
+ page
775
+ }, `${item.key}:${item.annotObjectNumber}`) : item.control === "choice" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FillChoice, {
776
+ item,
777
+ page
778
+ }, `${item.key}:${item.annotObjectNumber}`) : item.control === "button" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FillButton, {
779
+ item,
780
+ page
781
+ }, `${item.key}:${item.annotObjectNumber}`) : null)
782
+ });
783
+ }
784
+ /** The form capability — values, interchange, design-mode verbs. */
785
+ function useForm() {
786
+ return require_runtime.useCapability(_embedpdf_plugin_form.FormToken);
787
+ }
788
+ /** The reconciled form snapshot (null until the first load lands),
789
+ * re-rendering on every form model change. */
790
+ function useFormSnapshot() {
791
+ return require_runtime.useSelector(_embedpdf_plugin_form.FormToken, (c) => c.snapshot());
792
+ }
793
+ /**
794
+ * The FIELD behind the currently selected widget annotation (single
795
+ * selection), or null. The design-mode join: widget selection lives in the
796
+ * annotation plane, field properties live here.
797
+ */
798
+ function useFormField() {
799
+ const selected = require_annotation_hooks.useAnnotationSelected();
800
+ const widget = selected.length === 1 && selected[0].subtype === "widget" ? selected[0] : null;
801
+ const objnum = widget && widget.ref.kind === "objectNumber" ? widget.ref.annotObjectNumber : 0;
802
+ return require_runtime.useSelector(_embedpdf_plugin_form.FormToken, (c) => objnum > 0 ? c.fieldForWidget(objnum) : null);
803
+ }
804
+ //#endregion
805
+ Object.defineProperty(exports, "FormLayer", {
806
+ enumerable: true,
807
+ get: function() {
808
+ return FormLayer;
809
+ }
810
+ });
811
+ Object.defineProperty(exports, "formWidgetRenderer", {
812
+ enumerable: true,
813
+ get: function() {
814
+ return formWidgetRenderer;
815
+ }
816
+ });
817
+ Object.defineProperty(exports, "useForm", {
818
+ enumerable: true,
819
+ get: function() {
820
+ return useForm;
821
+ }
822
+ });
823
+ Object.defineProperty(exports, "useFormField", {
824
+ enumerable: true,
825
+ get: function() {
826
+ return useFormField;
827
+ }
828
+ });
829
+ Object.defineProperty(exports, "useFormSnapshot", {
830
+ enumerable: true,
831
+ get: function() {
832
+ return useFormSnapshot;
833
+ }
834
+ });
835
+
836
+ //# sourceMappingURL=form-D-ZSfGmB.cjs.map