@embedpdf/viewer-chrome 3.0.0-next.15 → 3.0.0-next.16

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 (200) hide show
  1. package/dist/{annotation-fonts-BqoLZ4Mp.js → annotation-fonts-DWjVOTP0.js} +2 -2
  2. package/dist/{annotation-fonts-BqoLZ4Mp.js.map → annotation-fonts-DWjVOTP0.js.map} +1 -1
  3. package/dist/{annotation-fonts-Dt0IQXhz.cjs → annotation-fonts-dpfVkhnq.cjs} +2 -2
  4. package/dist/{annotation-fonts-Dt0IQXhz.cjs.map → annotation-fonts-dpfVkhnq.cjs.map} +1 -1
  5. package/dist/{annotation-style-Bzvvp8-N.cjs → annotation-style-B3XKAyZ-.cjs} +3 -3
  6. package/dist/{annotation-style-Bzvvp8-N.cjs.map → annotation-style-B3XKAyZ-.cjs.map} +1 -1
  7. package/dist/{annotation-style--rPIZsuP.js → annotation-style-CXF3X47Z.js} +3 -3
  8. package/dist/{annotation-style--rPIZsuP.js.map → annotation-style-CXF3X47Z.js.map} +1 -1
  9. package/dist/{ar-DnEj5vbP.cjs → ar-B132r9bo.cjs} +93 -1
  10. package/dist/ar-B132r9bo.cjs.map +1 -0
  11. package/dist/{ar-D9XKAmDx.js → ar-BLWFJoA_.js} +93 -1
  12. package/dist/ar-BLWFJoA_.js.map +1 -0
  13. package/dist/attachments-panel-Cxln8xos.cjs +217 -0
  14. package/dist/attachments-panel-Cxln8xos.cjs.map +1 -0
  15. package/dist/attachments-panel-H1ucRbwo.js +217 -0
  16. package/dist/attachments-panel-H1ucRbwo.js.map +1 -0
  17. package/dist/chrome.cjs +21 -0
  18. package/dist/chrome.cjs.map +1 -1
  19. package/dist/chrome.js +21 -0
  20. package/dist/chrome.js.map +1 -1
  21. package/dist/{commands-BmgxhJ8B.js → commands-BkgLP3Om.js} +418 -23
  22. package/dist/commands-BkgLP3Om.js.map +1 -0
  23. package/dist/{commands-CYt4SFUX.cjs → commands-DL1gjZt7.cjs} +433 -20
  24. package/dist/commands-DL1gjZt7.cjs.map +1 -0
  25. package/dist/commands.cjs +2 -1
  26. package/dist/commands.d.cts +3 -1
  27. package/dist/commands.d.ts +3 -1
  28. package/dist/commands.js +2 -2
  29. package/dist/{comments-panel-pEcQg4Vs.cjs → comments-panel-BsnWedTh.cjs} +2 -2
  30. package/dist/{comments-panel-pEcQg4Vs.cjs.map → comments-panel-BsnWedTh.cjs.map} +1 -1
  31. package/dist/{comments-panel-MUVgLCBI.js → comments-panel-D7Eadws9.js} +2 -2
  32. package/dist/{comments-panel-MUVgLCBI.js.map → comments-panel-D7Eadws9.js.map} +1 -1
  33. package/dist/{de-Dv6zvbnd.cjs → de-7_MvFWzN.cjs} +93 -1
  34. package/dist/de-7_MvFWzN.cjs.map +1 -0
  35. package/dist/{de-Bdr4aWzm.js → de-BUhwc9-x.js} +93 -1
  36. package/dist/de-BUhwc9-x.js.map +1 -0
  37. package/dist/{dialog-B5GBisKv.js → dialog-BIb373cg.js} +3 -3
  38. package/dist/dialog-BIb373cg.js.map +1 -0
  39. package/dist/{dialog-t8F13BL_.cjs → dialog-CngKTnAM.cjs} +3 -3
  40. package/dist/dialog-CngKTnAM.cjs.map +1 -0
  41. package/dist/{es-BP09DR9w.cjs → es-DgYj-1L3.cjs} +93 -1
  42. package/dist/es-DgYj-1L3.cjs.map +1 -0
  43. package/dist/{es-BSXz5tBv.js → es-DoXTqFoO.js} +93 -1
  44. package/dist/es-DoXTqFoO.js.map +1 -0
  45. package/dist/{form-design-panel-vWWU9uB-.js → form-design-panel-CgV_5YWC.js} +3 -3
  46. package/dist/{form-design-panel-vWWU9uB-.js.map → form-design-panel-CgV_5YWC.js.map} +1 -1
  47. package/dist/{form-design-panel-B5MXTZ96.cjs → form-design-panel-D0FdmWXW.cjs} +3 -3
  48. package/dist/{form-design-panel-B5MXTZ96.cjs.map → form-design-panel-D0FdmWXW.cjs.map} +1 -1
  49. package/dist/{fr-Ckn3K9v1.js → fr-B-8NDhBA.js} +93 -1
  50. package/dist/fr-B-8NDhBA.js.map +1 -0
  51. package/dist/{fr-BBQQn7tJ.cjs → fr-C856dmQJ.cjs} +93 -1
  52. package/dist/fr-C856dmQJ.cjs.map +1 -0
  53. package/dist/{he-DRsG7bH7.cjs → he-BFTeNc4t.cjs} +93 -1
  54. package/dist/he-BFTeNc4t.cjs.map +1 -0
  55. package/dist/{he-wdGU5PkU.js → he-Dlh0fNXM.js} +93 -1
  56. package/dist/he-Dlh0fNXM.js.map +1 -0
  57. package/dist/{icons-x0Tc05bs.cjs → icons-1UfLUZ_y.cjs} +20 -1
  58. package/dist/icons-1UfLUZ_y.cjs.map +1 -0
  59. package/dist/{icons-BrK5iVL8.js → icons-7dB0Y1ct.js} +20 -1
  60. package/dist/icons-7dB0Y1ct.js.map +1 -0
  61. package/dist/index.cjs +740 -171
  62. package/dist/index.cjs.map +1 -1
  63. package/dist/index.d.cts +16 -2
  64. package/dist/index.d.ts +16 -2
  65. package/dist/index.js +727 -170
  66. package/dist/index.js.map +1 -1
  67. package/dist/{it-BuP4o0xQ.cjs → it-Bef-2Yxq.cjs} +93 -1
  68. package/dist/it-Bef-2Yxq.cjs.map +1 -0
  69. package/dist/{it-Us-VmvIu.js → it-BkirG7ol.js} +93 -1
  70. package/dist/it-BkirG7ol.js.map +1 -0
  71. package/dist/{ja-Cizj0-5O.js → ja-CGEjCE-s.js} +93 -1
  72. package/dist/ja-CGEjCE-s.js.map +1 -0
  73. package/dist/{ja-DovEd6ts.cjs → ja-DNw2zqSD.cjs} +93 -1
  74. package/dist/ja-DNw2zqSD.cjs.map +1 -0
  75. package/dist/{ko-DlrXliE1.js → ko-CTkl5vMh.js} +93 -1
  76. package/dist/ko-CTkl5vMh.js.map +1 -0
  77. package/dist/{ko-B-qJEol2.cjs → ko-YHzeBqP6.cjs} +93 -1
  78. package/dist/ko-YHzeBqP6.cjs.map +1 -0
  79. package/dist/{link-editor-CSjqPnj1.cjs → link-editor-CAS7MZM2.cjs} +2 -2
  80. package/dist/{link-editor-CSjqPnj1.cjs.map → link-editor-CAS7MZM2.cjs.map} +1 -1
  81. package/dist/{link-editor-SxM1nnJU.js → link-editor-CkwtqRvb.js} +2 -2
  82. package/dist/{link-editor-SxM1nnJU.js.map → link-editor-CkwtqRvb.js.map} +1 -1
  83. package/dist/{measurement-Bs61XWbJ.js → measurement-CQdbnIrn.js} +2 -2
  84. package/dist/{measurement-Bs61XWbJ.js.map → measurement-CQdbnIrn.js.map} +1 -1
  85. package/dist/{measurement-CcCsHPOX.cjs → measurement-Cxv1Ahxx.cjs} +2 -2
  86. package/dist/{measurement-CcCsHPOX.cjs.map → measurement-Cxv1Ahxx.cjs.map} +1 -1
  87. package/dist/{nl-D3KZDDiJ.cjs → nl-BjkbaLq3.cjs} +93 -1
  88. package/dist/nl-BjkbaLq3.cjs.map +1 -0
  89. package/dist/{nl-0aF7M_0h.js → nl-Dya4sR8_.js} +93 -1
  90. package/dist/nl-Dya4sR8_.js.map +1 -0
  91. package/dist/outline-panel-BpGtBdj0.cjs +707 -0
  92. package/dist/outline-panel-BpGtBdj0.cjs.map +1 -0
  93. package/dist/outline-panel-DO5_REYw.js +707 -0
  94. package/dist/outline-panel-DO5_REYw.js.map +1 -0
  95. package/dist/outline-properties-BCa2cc0t.js +330 -0
  96. package/dist/outline-properties-BCa2cc0t.js.map +1 -0
  97. package/dist/outline-properties-DT3FIxCw.cjs +330 -0
  98. package/dist/outline-properties-DT3FIxCw.cjs.map +1 -0
  99. package/dist/{pl-DSFhAUqx.cjs → pl-CKKdVnpZ.cjs} +93 -1
  100. package/dist/pl-CKKdVnpZ.cjs.map +1 -0
  101. package/dist/{pl-BW--AaCI.js → pl-DA9b5NVg.js} +93 -1
  102. package/dist/pl-DA9b5NVg.js.map +1 -0
  103. package/dist/print-dialog-BqIrHCBh.cjs +276 -0
  104. package/dist/print-dialog-BqIrHCBh.cjs.map +1 -0
  105. package/dist/print-dialog-DvcI2m2_.js +276 -0
  106. package/dist/print-dialog-DvcI2m2_.js.map +1 -0
  107. package/dist/{pt-C5S-8MC5.cjs → pt-DLcszlzi.cjs} +93 -1
  108. package/dist/pt-DLcszlzi.cjs.map +1 -0
  109. package/dist/{pt-DIhpU5pT.js → pt-Nm8yyjbX.js} +93 -1
  110. package/dist/pt-Nm8yyjbX.js.map +1 -0
  111. package/dist/{redact-confirm-CX_SY2ZN.cjs → redact-confirm-C6EiK8yN.cjs} +2 -2
  112. package/dist/{redact-confirm-CX_SY2ZN.cjs.map → redact-confirm-C6EiK8yN.cjs.map} +1 -1
  113. package/dist/{redact-confirm-B6j36TPw.js → redact-confirm-CKuv9iN_.js} +2 -2
  114. package/dist/{redact-confirm-B6j36TPw.js.map → redact-confirm-CKuv9iN_.js.map} +1 -1
  115. package/dist/{redaction-panel-IJDshSJI.js → redaction-panel-CgNZ-pQk.js} +2 -2
  116. package/dist/{redaction-panel-IJDshSJI.js.map → redaction-panel-CgNZ-pQk.js.map} +1 -1
  117. package/dist/{redaction-panel-BmoIl-Ll.cjs → redaction-panel-ZhX0dlMi.cjs} +2 -2
  118. package/dist/{redaction-panel-BmoIl-Ll.cjs.map → redaction-panel-ZhX0dlMi.cjs.map} +1 -1
  119. package/dist/{search-panel-HfIhJm1M.js → search-panel-BE4xzWlm.js} +4 -4
  120. package/dist/{search-panel-HfIhJm1M.js.map → search-panel-BE4xzWlm.js.map} +1 -1
  121. package/dist/{search-panel-s9IoiOtv.cjs → search-panel-wcbTNG1z.cjs} +4 -4
  122. package/dist/{search-panel-s9IoiOtv.cjs.map → search-panel-wcbTNG1z.cjs.map} +1 -1
  123. package/dist/{sheet-BDsa_Sup.cjs → sheet-DTBFiwde.cjs} +30 -11
  124. package/dist/sheet-DTBFiwde.cjs.map +1 -0
  125. package/dist/{sheet-R6XDgyEf.js → sheet-DyjKfAq0.js} +25 -12
  126. package/dist/sheet-DyjKfAq0.js.map +1 -0
  127. package/dist/{sign-dialog-CgnR7akj.cjs → sign-dialog-D5WGx-Yn.cjs} +2 -2
  128. package/dist/{sign-dialog-CgnR7akj.cjs.map → sign-dialog-D5WGx-Yn.cjs.map} +1 -1
  129. package/dist/{sign-dialog-GDqCoQhA.js → sign-dialog-DerNe8qT.js} +2 -2
  130. package/dist/{sign-dialog-GDqCoQhA.js.map → sign-dialog-DerNe8qT.js.map} +1 -1
  131. package/dist/{signature-inspector-DyO313Qf.cjs → signature-inspector-Bz6TAfZM.cjs} +4 -10
  132. package/dist/signature-inspector-Bz6TAfZM.cjs.map +1 -0
  133. package/dist/{signature-inspector-okFz8do6.js → signature-inspector-CsGr9EBk.js} +4 -10
  134. package/dist/signature-inspector-CsGr9EBk.js.map +1 -0
  135. package/dist/{signature-maker-BoYa8vgA.cjs → signature-maker-D_YfBXLn.cjs} +3 -3
  136. package/dist/{signature-maker-BoYa8vgA.cjs.map → signature-maker-D_YfBXLn.cjs.map} +1 -1
  137. package/dist/{signature-maker-BhCmYyHD.js → signature-maker-Dnth4rV4.js} +3 -3
  138. package/dist/{signature-maker-BhCmYyHD.js.map → signature-maker-Dnth4rV4.js.map} +1 -1
  139. package/dist/{signatures-panel-B56PC6d9.cjs → signatures-panel-Ci_LaI1C.cjs} +4 -10
  140. package/dist/signatures-panel-Ci_LaI1C.cjs.map +1 -0
  141. package/dist/{signatures-panel-CgnFfhFm.js → signatures-panel-DZ9ywrjz.js} +4 -10
  142. package/dist/signatures-panel-DZ9ywrjz.js.map +1 -0
  143. package/dist/{stamps-panel-CDO0FPyK.js → stamps-panel-BrczStaz.js} +4 -10
  144. package/dist/stamps-panel-BrczStaz.js.map +1 -0
  145. package/dist/{stamps-panel-B9NYGayn.cjs → stamps-panel-Cv9nJBwx.cjs} +4 -10
  146. package/dist/stamps-panel-Cv9nJBwx.cjs.map +1 -0
  147. package/dist/theme-Be0OZokZ.js +97 -0
  148. package/dist/theme-Be0OZokZ.js.map +1 -0
  149. package/dist/theme-FR26JI3r.cjs +126 -0
  150. package/dist/theme-FR26JI3r.cjs.map +1 -0
  151. package/dist/{toolbar-z9od3tKl.js → toolbar-DF361xWY.js} +7 -6
  152. package/dist/toolbar-DF361xWY.js.map +1 -0
  153. package/dist/{toolbar-CJu2wKTU.cjs → toolbar-ddvpsvTb.cjs} +7 -6
  154. package/dist/toolbar-ddvpsvTb.cjs.map +1 -0
  155. package/dist/{zh-cn-CxkUUBct.js → zh-cn-BMPPkLpL.js} +93 -1
  156. package/dist/zh-cn-BMPPkLpL.js.map +1 -0
  157. package/dist/{zh-cn-3tmMeHqv.cjs → zh-cn-CZOO3p4H.cjs} +93 -1
  158. package/dist/zh-cn-CZOO3p4H.cjs.map +1 -0
  159. package/package.json +4 -4
  160. package/src/styles.css +8 -0
  161. package/dist/ar-D9XKAmDx.js.map +0 -1
  162. package/dist/ar-DnEj5vbP.cjs.map +0 -1
  163. package/dist/commands-BmgxhJ8B.js.map +0 -1
  164. package/dist/commands-CYt4SFUX.cjs.map +0 -1
  165. package/dist/de-Bdr4aWzm.js.map +0 -1
  166. package/dist/de-Dv6zvbnd.cjs.map +0 -1
  167. package/dist/dialog-B5GBisKv.js.map +0 -1
  168. package/dist/dialog-t8F13BL_.cjs.map +0 -1
  169. package/dist/es-BP09DR9w.cjs.map +0 -1
  170. package/dist/es-BSXz5tBv.js.map +0 -1
  171. package/dist/fr-BBQQn7tJ.cjs.map +0 -1
  172. package/dist/fr-Ckn3K9v1.js.map +0 -1
  173. package/dist/he-DRsG7bH7.cjs.map +0 -1
  174. package/dist/he-wdGU5PkU.js.map +0 -1
  175. package/dist/icons-BrK5iVL8.js.map +0 -1
  176. package/dist/icons-x0Tc05bs.cjs.map +0 -1
  177. package/dist/it-BuP4o0xQ.cjs.map +0 -1
  178. package/dist/it-Us-VmvIu.js.map +0 -1
  179. package/dist/ja-Cizj0-5O.js.map +0 -1
  180. package/dist/ja-DovEd6ts.cjs.map +0 -1
  181. package/dist/ko-B-qJEol2.cjs.map +0 -1
  182. package/dist/ko-DlrXliE1.js.map +0 -1
  183. package/dist/nl-0aF7M_0h.js.map +0 -1
  184. package/dist/nl-D3KZDDiJ.cjs.map +0 -1
  185. package/dist/pl-BW--AaCI.js.map +0 -1
  186. package/dist/pl-DSFhAUqx.cjs.map +0 -1
  187. package/dist/pt-C5S-8MC5.cjs.map +0 -1
  188. package/dist/pt-DIhpU5pT.js.map +0 -1
  189. package/dist/sheet-BDsa_Sup.cjs.map +0 -1
  190. package/dist/sheet-R6XDgyEf.js.map +0 -1
  191. package/dist/signature-inspector-DyO313Qf.cjs.map +0 -1
  192. package/dist/signature-inspector-okFz8do6.js.map +0 -1
  193. package/dist/signatures-panel-B56PC6d9.cjs.map +0 -1
  194. package/dist/signatures-panel-CgnFfhFm.js.map +0 -1
  195. package/dist/stamps-panel-B9NYGayn.cjs.map +0 -1
  196. package/dist/stamps-panel-CDO0FPyK.js.map +0 -1
  197. package/dist/toolbar-CJu2wKTU.cjs.map +0 -1
  198. package/dist/toolbar-z9od3tKl.js.map +0 -1
  199. package/dist/zh-cn-3tmMeHqv.cjs.map +0 -1
  200. package/dist/zh-cn-CxkUUBct.js.map +0 -1
@@ -61,6 +61,12 @@ function windowSizeOf(element) {
61
61
  */
62
62
  /** The `popover` attribute, which React's types don't list yet. */
63
63
  const POPOVER = { popover: "manual" };
64
+ /**
65
+ * The classes every layer starts from. It clips what overflows it rather than hiding it: a box
66
+ * with hidden overflow still scrolls, and Safari scrolls it to show a control that takes focus
67
+ * while the sheet holding it still slides in from below.
68
+ */
69
+ const LAYER_CLASS = "text-fg m-0 overflow-clip border-0 bg-transparent";
64
70
  /** The custom element's root: the viewer's box. */
65
71
  const VIEWER_ROOT_SELECTOR = "[data-embedpdf-root]";
66
72
  /**
@@ -187,10 +193,11 @@ function panelSheetHeightsOf(viewerHeight) {
187
193
  /**
188
194
  * A sheet: what a panel or a menu is on a phone. It slides up from the bottom
189
195
  * of the viewer, over the pages, in a layer over the whole viewer
190
- * (`viewer-layer.ts`); the viewer above it dims, and a tap there closes it.
191
- * Its handle drags it taller or shorter, or down to close it, and a flick
192
- * moves it a whole step (`sheet-rest.ts`). It keeps clear of the home bar and
193
- * the screen's rounded corners when the viewer reaches the screen's edge.
196
+ * (`viewer-layer.ts`); the viewer behind it dims, and a tap there closes it.
197
+ * Its handle drags it taller or shorter, or down to close it, the dimming
198
+ * fading as it goes, and a flick moves it a whole step (`sheet-rest.ts`).
199
+ * It keeps clear of the home bar and the screen's rounded corners when the
200
+ * viewer reaches the screen's edge.
194
201
  * A sheet takes focus when it opens, keeps Tab inside it, closes on Escape
195
202
  * and gives focus back to the button that opened it; a menu's sheet leaves
196
203
  * the keyboard to its menu. A sheet that holds a list closes when one of its
@@ -219,6 +226,7 @@ const dataAttributesOf = (data) => Object.fromEntries(Object.entries(data ?? {})
219
226
  const DRAG_SAMPLES = 8;
220
227
  function Sheet({ label, onClose, size = "content", list = false, trapFocus = true, header, part, data, children }) {
221
228
  const layer = (0, react.useRef)(null);
229
+ const scrim = (0, react.useRef)(null);
222
230
  const sheet = (0, react.useRef)(null);
223
231
  const close = (0, react.useRef)(onClose);
224
232
  close.current = onClose;
@@ -227,7 +235,10 @@ function Sheet({ label, onClose, size = "content", list = false, trapFocus = tru
227
235
  const drag = (0, react.useRef)(null);
228
236
  /** The heights it rests at now: a panel's from the viewer's height, a menu's as tall as it is. */
229
237
  const heightsNow = () => size === "panel" ? panelSheetHeightsOf(layer.current?.offsetHeight ?? 0) : [sheet.current?.offsetHeight ?? 0];
230
- /** Show the sheet dragged to `height` (its top that far above the viewer's bottom). */
238
+ /**
239
+ * Show the sheet dragged to `height` (its top that far above the viewer's bottom). Below its
240
+ * lowest rest, the viewer behind it dims less the further down it goes.
241
+ */
231
242
  const show = (height, heights) => {
232
243
  const element = sheet.current;
233
244
  if (!element || heights.length === 0) return;
@@ -236,6 +247,7 @@ function Sheet({ label, onClose, size = "content", list = false, trapFocus = tru
236
247
  if (size === "panel") element.style.height = `${Math.min(tallest, Math.max(lowest, height))}px`;
237
248
  const below = Math.max(0, lowest - height);
238
249
  element.style.translate = below > 0 ? `0 ${below}px` : "";
250
+ if (scrim.current) scrim.current.style.opacity = below > 0 ? `${Math.max(0, 1 - below / lowest)}` : "";
239
251
  };
240
252
  (0, react.useLayoutEffect)(() => {
241
253
  const layerElement = layer.current;
@@ -285,7 +297,7 @@ function Sheet({ label, onClose, size = "content", list = false, trapFocus = tru
285
297
  }],
286
298
  height: startHeight
287
299
  };
288
- element.setAttribute("data-dragging", "");
300
+ layer.current?.setAttribute("data-dragging", "");
289
301
  };
290
302
  const onGripMove = (event) => {
291
303
  const current = drag.current;
@@ -303,7 +315,7 @@ function Sheet({ label, onClose, size = "content", list = false, trapFocus = tru
303
315
  const current = drag.current;
304
316
  if (!current || current.pointerId !== event.pointerId) return;
305
317
  drag.current = null;
306
- sheet.current?.removeAttribute("data-dragging");
318
+ layer.current?.removeAttribute("data-dragging");
307
319
  const cancelled = event.type === "pointercancel";
308
320
  const velocity = cancelled ? 0 : (0, _embedpdf_web.computeReleaseVelocity)(current.samples, event.timeStamp)?.vy ?? 0;
309
321
  const rest = cancelled ? current.heights[restIndex.current] : sheetRestOf(current.heights, current.height, velocity);
@@ -318,7 +330,7 @@ function Sheet({ label, onClose, size = "content", list = false, trapFocus = tru
318
330
  ref: layer,
319
331
  ...POPOVER,
320
332
  "data-epdf-layer": "",
321
- className: "text-fg m-0 overflow-hidden border-0 bg-transparent p-0 open:flex open:flex-col",
333
+ className: `${LAYER_CLASS} p-0 open:flex open:flex-col open:justify-end`,
322
334
  style: {
323
335
  position: "fixed",
324
336
  inset: "auto",
@@ -326,8 +338,9 @@ function Sheet({ label, onClose, size = "content", list = false, trapFocus = tru
326
338
  maxHeight: "none"
327
339
  },
328
340
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
341
+ ref: scrim,
329
342
  "aria-hidden": "true",
330
- className: `min-h-0 flex-1 ${SCRIM_CLASS}`,
343
+ className: `absolute inset-0 ${SCRIM_CLASS} in-data-dragging:transition-none`,
331
344
  onClick: () => close.current()
332
345
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
333
346
  ref: sheet,
@@ -337,7 +350,7 @@ function Sheet({ label, onClose, size = "content", list = false, trapFocus = tru
337
350
  part,
338
351
  ...dataAttributesOf(data),
339
352
  "data-epdf-sheet": "",
340
- className: "border-border-subtle bg-elevated starting:translate-y-full data-dragging:transition-none relative flex w-full shrink-0 flex-col rounded-t-2xl border-t shadow-2xl transition-[translate,height] duration-200 ease-out motion-reduce:transition-none",
353
+ className: "border-border-subtle bg-elevated starting:translate-y-full in-data-dragging:transition-none relative flex w-full shrink-0 flex-col rounded-t-2xl border-t shadow-2xl transition-[translate,height] duration-200 ease-out motion-reduce:transition-none",
341
354
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
342
355
  className: "shrink-0 touch-pan-x select-none",
343
356
  onPointerDown: onGripDown,
@@ -372,6 +385,12 @@ Object.defineProperty(exports, "FOCUS_ROOT_ATTRIBUTE", {
372
385
  return FOCUS_ROOT_ATTRIBUTE;
373
386
  }
374
387
  });
388
+ Object.defineProperty(exports, "LAYER_CLASS", {
389
+ enumerable: true,
390
+ get: function() {
391
+ return LAYER_CLASS;
392
+ }
393
+ });
375
394
  Object.defineProperty(exports, "NO_SCREEN_EDGES", {
376
395
  enumerable: true,
377
396
  get: function() {
@@ -439,4 +458,4 @@ Object.defineProperty(exports, "windowSizeOf", {
439
458
  }
440
459
  });
441
460
 
442
- //# sourceMappingURL=sheet-BDsa_Sup.cjs.map
461
+ //# sourceMappingURL=sheet-DTBFiwde.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sheet-DTBFiwde.cjs","names":[],"sources":["../src/ui/screen-edges.ts","../src/ui/viewer-layer.ts","../src/ui/sheet-rest.ts","../src/ui/sheet.tsx"],"sourcesContent":["/**\n * Which edges of the window the viewer reaches, and the padding that keeps\n * what sits at those edges clear of the notch, the rounded corners and the\n * home bar. The padding is the browser's `env(safe-area-inset-*)`, which is\n * zero unless the page lets content under those areas (`viewport-fit=cover`),\n * so it changes nothing on an ordinary page. The viewer reaches an edge when\n * its box is at that edge of the window or past it, as in full screen or as\n * the whole page of a phone app.\n */\n/** For each edge of the window, whether the viewer reaches it. */\nexport interface ScreenEdges {\n readonly top: boolean;\n readonly right: boolean;\n readonly bottom: boolean;\n readonly left: boolean;\n}\n\nexport const NO_SCREEN_EDGES: ScreenEdges = {\n top: false,\n right: false,\n bottom: false,\n left: false,\n};\n\n/** A box in window px. */\nexport interface ScreenBox {\n readonly left: number;\n readonly top: number;\n readonly right: number;\n readonly bottom: number;\n}\n\n/** How close to the window's edge a box counts as reaching it, for rounding. */\nconst EDGE_TOLERANCE_PX = 1;\n\n/** The edges of a `width` × `height` window that `box` reaches while some of it is on screen. */\nexport function screenEdgesOf(\n box: ScreenBox,\n view: { readonly width: number; readonly height: number },\n): ScreenEdges {\n const across = box.right > 0 && box.left < view.width;\n const down = box.bottom > 0 && box.top < view.height;\n return {\n top: across && box.top <= EDGE_TOLERANCE_PX && box.bottom > 0,\n right: down && box.right >= view.width - EDGE_TOLERANCE_PX && box.left < view.width,\n bottom: across && box.bottom >= view.height - EDGE_TOLERANCE_PX && box.top < view.height,\n left: down && box.left <= EDGE_TOLERANCE_PX && box.right > 0,\n };\n}\n\nexport const sameScreenEdges = (left: ScreenEdges, right: ScreenEdges): boolean =>\n left.top === right.top &&\n left.right === right.right &&\n left.bottom === right.bottom &&\n left.left === right.left;\n\n/** The padding of each side in `sides` whose edge the viewer reaches. */\nexport interface SafeAreaPadding {\n paddingTop?: string;\n paddingRight?: string;\n paddingBottom?: string;\n paddingLeft?: string;\n}\n\nconst PADDING: Readonly<Record<keyof ScreenEdges, keyof SafeAreaPadding>> = {\n top: 'paddingTop',\n right: 'paddingRight',\n bottom: 'paddingBottom',\n left: 'paddingLeft',\n};\n\n/** The safe-area padding for the reached edges among `sides` (default all four), as style properties. */\nexport function safeAreaPaddingOf(\n edges: ScreenEdges,\n sides: readonly (keyof ScreenEdges)[] = ['top', 'right', 'bottom', 'left'],\n): SafeAreaPadding {\n const padding: SafeAreaPadding = {};\n for (const side of sides) {\n if (edges[side]) padding[PADDING[side]] = `env(safe-area-inset-${side}, 0px)`;\n }\n return padding;\n}\n\n/** The window's size, without its scrollbars. */\nexport function windowSizeOf(element: Element): { width: number; height: number } {\n const { clientWidth, clientHeight } = element.ownerDocument.documentElement;\n return { width: clientWidth, height: clientHeight };\n}\n","/**\n * What opens over the whole viewer: a layer the size of the viewer's box on\n * screen, in the browser's top layer, where sheets and dialogs open. In the\n * top layer nothing the viewer draws covers it, and what opens later (a menu\n * from a sheet, a dialog from a panel) sits above what opened before. And\n * what holds the keyboard while it's open (a sheet, a dialog): it takes\n * focus, keeps Tab inside it, closes on Escape, and gives focus back to what\n * had it before, the button that opened it. An overlay panel takes focus the\n * same way but lets Tab move on, so the rest of the viewer stays in reach.\n */\nimport {\n attachFocusTrap,\n deepActiveElementOf,\n moveFocusInto,\n observeClientGeometry,\n} from '@embedpdf/web';\n\nimport { screenEdgesOf, windowSizeOf, type ScreenEdges } from './screen-edges';\n\n/** The `popover` attribute, which React's types don't list yet. */\nexport const POPOVER = { popover: 'manual' } as Record<string, string>;\n\n/**\n * The classes every layer starts from. It clips what overflows it rather than hiding it: a box\n * with hidden overflow still scrolls, and Safari scrolls it to show a control that takes focus\n * while the sheet holding it still slides in from below.\n */\nexport const LAYER_CLASS = 'text-fg m-0 overflow-clip border-0 bg-transparent';\n\n/** The custom element's root: the viewer's box. */\nconst VIEWER_ROOT_SELECTOR = '[data-embedpdf-root]';\n\n/** What another surface that holds focus is marked with: a press in it is not a press outside. */\nexport const MODAL_SELECTOR = '[aria-modal=\"true\"]';\n\n/**\n * What the viewer's root is marked with: it takes focus on a press that lands on nothing that\n * takes focus itself (the pages), so the viewer's keys reach it from there.\n */\nexport const FOCUS_ROOT_ATTRIBUTE = 'data-epdf-focus-root';\n\n/** Place `element` (`position: fixed`) at a box in window px. */\nfunction placeFixed(\n element: HTMLElement,\n box: { left: number; top: number; width: number; height: number },\n): void {\n element.style.left = `${box.left}px`;\n element.style.top = `${box.top}px`;\n element.style.width = `${box.width}px`;\n element.style.height = `${box.height}px`;\n // Without the top layer, `position: fixed` starts at the viewer's box (its `contain`): move by\n // however far from the window's origin the layer landed.\n const landed = element.getBoundingClientRect();\n if (Math.abs(landed.left - box.left) > 0.5 || Math.abs(landed.top - box.top) > 0.5) {\n element.style.left = `${2 * box.left - landed.left}px`;\n element.style.top = `${2 * box.top - landed.top}px`;\n }\n}\n\n/**\n * Show `layer` in the top layer over the part of the viewer on screen, and keep it there as the\n * viewer or the window resizes or scrolls. `onPlace` hears which edges of the window the viewer\n * reaches, each time the layer is placed. Returns the stop, which hides the layer.\n */\nexport function attachViewerLayer(\n layer: HTMLElement,\n onPlace?: (edges: ScreenEdges) => void,\n): () => void {\n const root = layer.parentElement?.closest(VIEWER_ROOT_SELECTOR) ?? null;\n if (typeof layer.showPopover === 'function') layer.showPopover();\n let placed = '';\n const place = () => {\n const view = windowSizeOf(layer);\n const rect = root?.getBoundingClientRect() ?? {\n left: 0,\n top: 0,\n right: view.width,\n bottom: view.height,\n };\n const left = Math.max(0, rect.left);\n const top = Math.max(0, rect.top);\n const box = {\n left,\n top,\n width: Math.max(0, Math.min(view.width, rect.right) - left),\n height: Math.max(0, Math.min(view.height, rect.bottom) - top),\n };\n // Scrolling inside the layer reaches here too: only a move of the viewer changes anything.\n const key = `${box.left},${box.top},${box.width},${box.height},${view.width},${view.height}`;\n if (key === placed) return;\n placed = key;\n placeFixed(layer, box);\n onPlace?.(screenEdgesOf(rect, view));\n };\n place();\n const resizes = new ResizeObserver(place);\n if (root) resizes.observe(root);\n const stopGeometry = observeClientGeometry(place);\n return () => {\n stopGeometry();\n resizes.disconnect();\n if (typeof layer.hidePopover === 'function' && layer.matches(':popover-open')) {\n layer.hidePopover();\n }\n };\n}\n\nexport interface HeldFocus {\n /** Stop holding focus, leaving it where it is. */\n standDown(): void;\n /** Stop holding focus, and give it back to what had it before when it's still in `container`. */\n release(): void;\n}\n\n/**\n * Hold the keyboard in `container` until released: focus moves into it unless it's there\n * already (to `initialFocus`, else its first control), so a field that took the cursor as it\n * appeared keeps it; Tab and Shift+Tab stay inside it, and Escape calls `onEscape`. With\n * `trap: false` Tab moves on past it, as it does past anything else in the viewer. Releasing\n * gives focus back to what had it before, unless the user has moved it elsewhere: a press on\n * the pages, which puts focus on the viewer's root, doesn't count.\n */\nexport function holdFocus(\n container: HTMLElement,\n options: {\n readonly onEscape: () => void;\n readonly initialFocus?: HTMLElement | null;\n readonly trap?: boolean;\n },\n): HeldFocus {\n const doc = container.ownerDocument;\n const before = deepActiveElementOf(doc) as HTMLElement | null;\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key !== 'Escape' || event.defaultPrevented) return;\n event.preventDefault();\n // Escape closes this, not what it opened over.\n event.stopPropagation();\n options.onEscape();\n };\n container.addEventListener('keydown', onKeyDown);\n let releaseTrap: (() => void) | null = null;\n if (options.trap === false) {\n moveFocusInto(container, options.initialFocus);\n } else {\n releaseTrap = attachFocusTrap(container, {\n initialFocus: options.initialFocus,\n returnFocus: false,\n });\n }\n const standDown = () => {\n releaseTrap?.();\n releaseTrap = null;\n container.removeEventListener('keydown', onKeyDown);\n };\n return {\n standDown,\n release: () => {\n standDown();\n const active = deepActiveElementOf(doc);\n const nowhere =\n active === null || container.contains(active) || active.hasAttribute(FOCUS_ROOT_ATTRIBUTE);\n if (before?.isConnected && nowhere) before.focus();\n },\n };\n}\n","/**\n * Where a sheet comes to rest after its handle is let go: at one of the\n * heights it rests at, or closed. A flick moves it a whole step, down to the\n * next lower height (closing from the lowest) or up to the next higher one;\n * a slow drag settles at the nearest height, and closes when the sheet was\n * dragged below half of the lowest.\n */\n\n/** A release faster than this, in px per second, is a flick. */\nexport const SHEET_FLICK_PX_PER_S = 500;\n\n/** How much of the viewer's height a panel's sheet opens at. */\nexport const PANEL_SHEET_OPEN_FRACTION = 0.5;\n\n/** Px a sheet leaves above itself at its tallest, so the pages show it's a sheet. */\nexport const SHEET_TOP_CLEARANCE_PX = 40;\n\n/**\n * The height a sheet dragged to `height` comes to rest at, one of `heights` (ascending), or null\n * when it closes. `velocity` is the release speed in px per second, downward positive.\n */\nexport function sheetRestOf(\n heights: readonly number[],\n height: number,\n velocity: number,\n): number | null {\n if (heights.length === 0) return null;\n if (velocity >= SHEET_FLICK_PX_PER_S) {\n const lower = heights.filter((rest) => rest < height - 1);\n return lower.length > 0 ? lower[lower.length - 1]! : null;\n }\n if (velocity <= -SHEET_FLICK_PX_PER_S) {\n return heights.find((rest) => rest > height + 1) ?? heights[heights.length - 1]!;\n }\n if (height < heights[0]! / 2) return null;\n return heights.reduce((nearest, rest) =>\n Math.abs(rest - height) < Math.abs(nearest - height) ? rest : nearest,\n );\n}\n\n/** The heights a panel's sheet rests at in a viewer `viewerHeight` px tall: half of it, and nearly all. */\nexport function panelSheetHeightsOf(viewerHeight: number): readonly number[] {\n const tallest = Math.max(0, Math.round(viewerHeight - SHEET_TOP_CLEARANCE_PX));\n const open = Math.min(tallest, Math.round(viewerHeight * PANEL_SHEET_OPEN_FRACTION));\n return open < tallest ? [open, tallest] : [tallest];\n}\n","/**\n * A sheet: what a panel or a menu is on a phone. It slides up from the bottom\n * of the viewer, over the pages, in a layer over the whole viewer\n * (`viewer-layer.ts`); the viewer behind it dims, and a tap there closes it.\n * Its handle drags it taller or shorter, or down to close it, the dimming\n * fading as it goes, and a flick moves it a whole step (`sheet-rest.ts`).\n * It keeps clear of the home bar and the screen's rounded corners when the\n * viewer reaches the screen's edge.\n * A sheet takes focus when it opens, keeps Tab inside it, closes on Escape\n * and gives focus back to the button that opened it; a menu's sheet leaves\n * the keyboard to its menu. A sheet that holds a list closes when one of its\n * items is picked ({@link useCloseOnPick}).\n */\nimport { computeReleaseVelocity, observeOutsidePress } from '@embedpdf/web';\nimport {\n createContext,\n useContext,\n useLayoutEffect,\n useRef,\n type PointerEvent as ReactPointerEvent,\n type ReactNode,\n} from 'react';\n\nimport { safeAreaPaddingOf } from './screen-edges';\nimport { SHEET_TOP_CLEARANCE_PX, panelSheetHeightsOf, sheetRestOf } from './sheet-rest';\nimport { LAYER_CLASS, MODAL_SELECTOR, POPOVER, attachViewerLayer, holdFocus } from './viewer-layer';\n\n/** A press that starts on one of these in the handle's row is the control's, not a drag. */\nconst CONTROL_SELECTOR =\n 'button, a[href], input, select, textarea, [role=\"tab\"], [contenteditable]:not([contenteditable=\"false\"])';\n\n/** The dimmed viewer behind a sheet or a dialog. */\nexport const SCRIM_CLASS = 'bg-black/30 transition-opacity duration-200 starting:opacity-0';\n\nconst NO_PICK = () => {};\n\nconst CloseOnPickContext = createContext<() => void>(NO_PICK);\n\n/** Gives what's inside a sheet or an overlay the close it runs when one of its items is picked. */\nexport const CloseOnPickProvider = CloseOnPickContext.Provider;\n\n/**\n * What a list's item runs when it's picked: it closes the sheet or the overlay the list is in,\n * so you see where it took you. It does nothing in a docked panel, or in one that isn't a list.\n */\nexport function useCloseOnPick(): () => void {\n return useContext(CloseOnPickContext);\n}\n\n/** Whether a press landed in another surface that holds focus, like a dialog opened over this one. */\nconst pressedInOtherModal = (event: PointerEvent, own: Element): boolean =>\n event\n .composedPath()\n .some((node) => node instanceof Element && node !== own && node.matches(MODAL_SELECTOR));\n\nexport interface SheetProps {\n /** Names the sheet for assistive technology. */\n readonly label: string;\n readonly onClose: () => void;\n /**\n * `'content'`: as tall as what's in it, like a menu. `'panel'`: opens at half the viewer's\n * height, and drags up to nearly all of it. Default `'content'`.\n */\n readonly size?: 'content' | 'panel';\n /** It holds a list: picking an item ({@link useCloseOnPick}) closes it. */\n readonly list?: boolean;\n /**\n * Whether it holds the keyboard (default true). A menu's sheet doesn't: its menu takes focus\n * and gives it back.\n */\n readonly trapFocus?: boolean;\n /** Drawn under the handle, above the part that scrolls; dragging it moves the sheet too. */\n readonly header?: ReactNode;\n /** The sheet's part name, for page CSS through `::part()`, when it is one: a panel's `panel`. */\n readonly part?: string;\n /** `data-*` attributes for the sheet, by their names without `data-`. */\n readonly data?: Readonly<Record<string, string>>;\n readonly children: ReactNode;\n}\n\ninterface Drag {\n readonly pointerId: number;\n readonly startY: number;\n readonly startHeight: number;\n /** The heights it can rest at, ascending. */\n readonly heights: readonly number[];\n readonly samples: { t: number; x: number; y: number }[];\n height: number;\n}\n\n/** `data` as the `data-*` attributes it names. */\nconst dataAttributesOf = (data: Readonly<Record<string, string>> | undefined) =>\n Object.fromEntries(Object.entries(data ?? {}).map(([name, value]) => [`data-${name}`, value]));\n\n/** How many pointer samples a drag keeps for its release speed. */\nconst DRAG_SAMPLES = 8;\n\nexport function Sheet({\n label,\n onClose,\n size = 'content',\n list = false,\n trapFocus = true,\n header,\n part,\n data,\n children,\n}: SheetProps) {\n const layer = useRef<HTMLDivElement>(null);\n const scrim = useRef<HTMLDivElement>(null);\n const sheet = useRef<HTMLDivElement>(null);\n const close = useRef(onClose);\n close.current = onClose;\n /** Which of its heights a panel's sheet rests at: the first (open) or a taller one. */\n const restIndex = useRef(0);\n const drag = useRef<Drag | null>(null);\n\n /** The heights it rests at now: a panel's from the viewer's height, a menu's as tall as it is. */\n const heightsNow = (): readonly number[] =>\n size === 'panel'\n ? panelSheetHeightsOf(layer.current?.offsetHeight ?? 0)\n : [sheet.current?.offsetHeight ?? 0];\n\n /**\n * Show the sheet dragged to `height` (its top that far above the viewer's bottom). Below its\n * lowest rest, the viewer behind it dims less the further down it goes.\n */\n const show = (height: number, heights: readonly number[]) => {\n const element = sheet.current;\n if (!element || heights.length === 0) return;\n const lowest = heights[0]!;\n const tallest = heights[heights.length - 1]!;\n if (size === 'panel') element.style.height = `${Math.min(tallest, Math.max(lowest, height))}px`;\n const below = Math.max(0, lowest - height);\n element.style.translate = below > 0 ? `0 ${below}px` : '';\n if (scrim.current) {\n scrim.current.style.opacity = below > 0 ? `${Math.max(0, 1 - below / lowest)}` : '';\n }\n };\n\n useLayoutEffect(() => {\n const layerElement = layer.current;\n const sheetElement = sheet.current;\n if (!layerElement || !sheetElement) return;\n const stopLayer = attachViewerLayer(layerElement, (edges) => {\n const padding = safeAreaPaddingOf(edges, ['right', 'bottom', 'left']);\n sheetElement.style.paddingLeft = padding.paddingLeft ?? '';\n sheetElement.style.paddingRight = padding.paddingRight ?? '';\n sheetElement.style.paddingBottom = padding.paddingBottom ?? '';\n sheetElement.style.maxHeight = `${Math.max(0, layerElement.offsetHeight - SHEET_TOP_CLEARANCE_PX)}px`;\n if (size === 'panel' && !drag.current) {\n const heights = panelSheetHeightsOf(layerElement.offsetHeight);\n show(heights[Math.min(restIndex.current, heights.length - 1)]!, heights);\n }\n });\n const held = trapFocus ? holdFocus(sheetElement, { onEscape: () => close.current() }) : null;\n // A press on the viewer above the sheet is the scrim's click; one outside the viewer closes it too.\n const stopOutside = observeOutsidePress(layerElement, (event) => {\n if (!pressedInOtherModal(event, sheetElement)) close.current();\n });\n return () => {\n stopOutside();\n held?.release();\n stopLayer();\n };\n // The sheet opens once; it closes by unmounting.\n }, []);\n\n const onGripDown = (event: ReactPointerEvent<HTMLDivElement>) => {\n if (event.pointerType === 'mouse' && event.button !== 0) return;\n if ((event.target as Element).closest(CONTROL_SELECTOR)) return;\n const element = sheet.current;\n if (!element) return;\n event.currentTarget.setPointerCapture(event.pointerId);\n const startHeight = element.offsetHeight;\n drag.current = {\n pointerId: event.pointerId,\n startY: event.clientY,\n startHeight,\n heights: heightsNow(),\n samples: [{ t: event.timeStamp, x: event.clientX, y: event.clientY }],\n height: startHeight,\n };\n // The sheet and the dimming follow the finger, without easing.\n layer.current?.setAttribute('data-dragging', '');\n };\n\n const onGripMove = (event: ReactPointerEvent<HTMLDivElement>) => {\n const current = drag.current;\n if (!current || current.pointerId !== event.pointerId) return;\n current.height = current.startHeight - (event.clientY - current.startY);\n current.samples.push({ t: event.timeStamp, x: event.clientX, y: event.clientY });\n if (current.samples.length > DRAG_SAMPLES) current.samples.shift();\n show(current.height, current.heights);\n };\n\n const onGripUp = (event: ReactPointerEvent<HTMLDivElement>) => {\n const current = drag.current;\n if (!current || current.pointerId !== event.pointerId) return;\n drag.current = null;\n layer.current?.removeAttribute('data-dragging');\n const cancelled = event.type === 'pointercancel';\n const velocity = cancelled\n ? 0\n : (computeReleaseVelocity(current.samples, event.timeStamp)?.vy ?? 0);\n const rest = cancelled\n ? current.heights[restIndex.current]!\n : sheetRestOf(current.heights, current.height, velocity);\n if (rest === null) {\n close.current();\n return;\n }\n restIndex.current = Math.max(0, current.heights.indexOf(rest));\n show(rest, current.heights);\n };\n\n return (\n <div\n ref={layer}\n {...POPOVER}\n data-epdf-layer=\"\"\n className={`${LAYER_CLASS} p-0 open:flex open:flex-col open:justify-end`}\n style={{ position: 'fixed', inset: 'auto', maxWidth: 'none', maxHeight: 'none' }}\n >\n {/* Behind the whole viewer, not just above the sheet: the sheet sliding in or dragged down\n never uncovers it. */}\n <div\n ref={scrim}\n aria-hidden=\"true\"\n className={`absolute inset-0 ${SCRIM_CLASS} in-data-dragging:transition-none`}\n onClick={() => close.current()}\n />\n <div\n ref={sheet}\n role={trapFocus ? 'dialog' : undefined}\n aria-modal={trapFocus ? true : undefined}\n aria-label={trapFocus ? label : undefined}\n part={part}\n {...dataAttributesOf(data)}\n data-epdf-sheet=\"\"\n className=\"border-border-subtle bg-elevated starting:translate-y-full in-data-dragging:transition-none relative flex w-full shrink-0 flex-col rounded-t-2xl border-t shadow-2xl transition-[translate,height] duration-200 ease-out motion-reduce:transition-none\"\n >\n <div\n className=\"shrink-0 touch-pan-x select-none\"\n onPointerDown={onGripDown}\n onPointerMove={onGripMove}\n onPointerUp={onGripUp}\n onPointerCancel={onGripUp}\n >\n <div\n data-epdf-sheet-handle=\"\"\n className=\"flex h-6 cursor-grab items-center justify-center active:cursor-grabbing\"\n >\n <span className=\"bg-border-strong h-1 w-10 rounded-full\" />\n </div>\n {header}\n </div>\n <div className=\"flex min-h-0 flex-1 flex-col overflow-y-auto overflow-x-hidden overscroll-contain\">\n <CloseOnPickProvider value={list ? () => close.current() : NO_PICK}>\n {children}\n </CloseOnPickProvider>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -61,6 +61,12 @@ function windowSizeOf(element) {
61
61
  */
62
62
  /** The `popover` attribute, which React's types don't list yet. */
63
63
  const POPOVER = { popover: "manual" };
64
+ /**
65
+ * The classes every layer starts from. It clips what overflows it rather than hiding it: a box
66
+ * with hidden overflow still scrolls, and Safari scrolls it to show a control that takes focus
67
+ * while the sheet holding it still slides in from below.
68
+ */
69
+ const LAYER_CLASS = "text-fg m-0 overflow-clip border-0 bg-transparent";
64
70
  /** The custom element's root: the viewer's box. */
65
71
  const VIEWER_ROOT_SELECTOR = "[data-embedpdf-root]";
66
72
  /**
@@ -187,10 +193,11 @@ function panelSheetHeightsOf(viewerHeight) {
187
193
  /**
188
194
  * A sheet: what a panel or a menu is on a phone. It slides up from the bottom
189
195
  * of the viewer, over the pages, in a layer over the whole viewer
190
- * (`viewer-layer.ts`); the viewer above it dims, and a tap there closes it.
191
- * Its handle drags it taller or shorter, or down to close it, and a flick
192
- * moves it a whole step (`sheet-rest.ts`). It keeps clear of the home bar and
193
- * the screen's rounded corners when the viewer reaches the screen's edge.
196
+ * (`viewer-layer.ts`); the viewer behind it dims, and a tap there closes it.
197
+ * Its handle drags it taller or shorter, or down to close it, the dimming
198
+ * fading as it goes, and a flick moves it a whole step (`sheet-rest.ts`).
199
+ * It keeps clear of the home bar and the screen's rounded corners when the
200
+ * viewer reaches the screen's edge.
194
201
  * A sheet takes focus when it opens, keeps Tab inside it, closes on Escape
195
202
  * and gives focus back to the button that opened it; a menu's sheet leaves
196
203
  * the keyboard to its menu. A sheet that holds a list closes when one of its
@@ -219,6 +226,7 @@ const dataAttributesOf = (data) => Object.fromEntries(Object.entries(data ?? {})
219
226
  const DRAG_SAMPLES = 8;
220
227
  function Sheet({ label, onClose, size = "content", list = false, trapFocus = true, header, part, data, children }) {
221
228
  const layer = useRef(null);
229
+ const scrim = useRef(null);
222
230
  const sheet = useRef(null);
223
231
  const close = useRef(onClose);
224
232
  close.current = onClose;
@@ -227,7 +235,10 @@ function Sheet({ label, onClose, size = "content", list = false, trapFocus = tru
227
235
  const drag = useRef(null);
228
236
  /** The heights it rests at now: a panel's from the viewer's height, a menu's as tall as it is. */
229
237
  const heightsNow = () => size === "panel" ? panelSheetHeightsOf(layer.current?.offsetHeight ?? 0) : [sheet.current?.offsetHeight ?? 0];
230
- /** Show the sheet dragged to `height` (its top that far above the viewer's bottom). */
238
+ /**
239
+ * Show the sheet dragged to `height` (its top that far above the viewer's bottom). Below its
240
+ * lowest rest, the viewer behind it dims less the further down it goes.
241
+ */
231
242
  const show = (height, heights) => {
232
243
  const element = sheet.current;
233
244
  if (!element || heights.length === 0) return;
@@ -236,6 +247,7 @@ function Sheet({ label, onClose, size = "content", list = false, trapFocus = tru
236
247
  if (size === "panel") element.style.height = `${Math.min(tallest, Math.max(lowest, height))}px`;
237
248
  const below = Math.max(0, lowest - height);
238
249
  element.style.translate = below > 0 ? `0 ${below}px` : "";
250
+ if (scrim.current) scrim.current.style.opacity = below > 0 ? `${Math.max(0, 1 - below / lowest)}` : "";
239
251
  };
240
252
  useLayoutEffect(() => {
241
253
  const layerElement = layer.current;
@@ -285,7 +297,7 @@ function Sheet({ label, onClose, size = "content", list = false, trapFocus = tru
285
297
  }],
286
298
  height: startHeight
287
299
  };
288
- element.setAttribute("data-dragging", "");
300
+ layer.current?.setAttribute("data-dragging", "");
289
301
  };
290
302
  const onGripMove = (event) => {
291
303
  const current = drag.current;
@@ -303,7 +315,7 @@ function Sheet({ label, onClose, size = "content", list = false, trapFocus = tru
303
315
  const current = drag.current;
304
316
  if (!current || current.pointerId !== event.pointerId) return;
305
317
  drag.current = null;
306
- sheet.current?.removeAttribute("data-dragging");
318
+ layer.current?.removeAttribute("data-dragging");
307
319
  const cancelled = event.type === "pointercancel";
308
320
  const velocity = cancelled ? 0 : computeReleaseVelocity(current.samples, event.timeStamp)?.vy ?? 0;
309
321
  const rest = cancelled ? current.heights[restIndex.current] : sheetRestOf(current.heights, current.height, velocity);
@@ -318,7 +330,7 @@ function Sheet({ label, onClose, size = "content", list = false, trapFocus = tru
318
330
  ref: layer,
319
331
  ...POPOVER,
320
332
  "data-epdf-layer": "",
321
- className: "text-fg m-0 overflow-hidden border-0 bg-transparent p-0 open:flex open:flex-col",
333
+ className: `${LAYER_CLASS} p-0 open:flex open:flex-col open:justify-end`,
322
334
  style: {
323
335
  position: "fixed",
324
336
  inset: "auto",
@@ -326,8 +338,9 @@ function Sheet({ label, onClose, size = "content", list = false, trapFocus = tru
326
338
  maxHeight: "none"
327
339
  },
328
340
  children: [/* @__PURE__ */ jsx("div", {
341
+ ref: scrim,
329
342
  "aria-hidden": "true",
330
- className: `min-h-0 flex-1 ${SCRIM_CLASS}`,
343
+ className: `absolute inset-0 ${SCRIM_CLASS} in-data-dragging:transition-none`,
331
344
  onClick: () => close.current()
332
345
  }), /* @__PURE__ */ jsxs("div", {
333
346
  ref: sheet,
@@ -337,7 +350,7 @@ function Sheet({ label, onClose, size = "content", list = false, trapFocus = tru
337
350
  part,
338
351
  ...dataAttributesOf(data),
339
352
  "data-epdf-sheet": "",
340
- className: "border-border-subtle bg-elevated starting:translate-y-full data-dragging:transition-none relative flex w-full shrink-0 flex-col rounded-t-2xl border-t shadow-2xl transition-[translate,height] duration-200 ease-out motion-reduce:transition-none",
353
+ className: "border-border-subtle bg-elevated starting:translate-y-full in-data-dragging:transition-none relative flex w-full shrink-0 flex-col rounded-t-2xl border-t shadow-2xl transition-[translate,height] duration-200 ease-out motion-reduce:transition-none",
341
354
  children: [/* @__PURE__ */ jsxs("div", {
342
355
  className: "shrink-0 touch-pan-x select-none",
343
356
  onPointerDown: onGripDown,
@@ -360,6 +373,6 @@ function Sheet({ label, onClose, size = "content", list = false, trapFocus = tru
360
373
  });
361
374
  }
362
375
  //#endregion
363
- export { FOCUS_ROOT_ATTRIBUTE as a, holdFocus as c, sameScreenEdges as d, screenEdgesOf as f, useCloseOnPick as i, NO_SCREEN_EDGES as l, SCRIM_CLASS as n, POPOVER as o, windowSizeOf as p, Sheet as r, attachViewerLayer as s, CloseOnPickProvider as t, safeAreaPaddingOf as u };
376
+ export { FOCUS_ROOT_ATTRIBUTE as a, attachViewerLayer as c, safeAreaPaddingOf as d, sameScreenEdges as f, useCloseOnPick as i, holdFocus as l, windowSizeOf as m, SCRIM_CLASS as n, LAYER_CLASS as o, screenEdgesOf as p, Sheet as r, POPOVER as s, CloseOnPickProvider as t, NO_SCREEN_EDGES as u };
364
377
 
365
- //# sourceMappingURL=sheet-R6XDgyEf.js.map
378
+ //# sourceMappingURL=sheet-DyjKfAq0.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sheet-DyjKfAq0.js","names":[],"sources":["../src/ui/screen-edges.ts","../src/ui/viewer-layer.ts","../src/ui/sheet-rest.ts","../src/ui/sheet.tsx"],"sourcesContent":["/**\n * Which edges of the window the viewer reaches, and the padding that keeps\n * what sits at those edges clear of the notch, the rounded corners and the\n * home bar. The padding is the browser's `env(safe-area-inset-*)`, which is\n * zero unless the page lets content under those areas (`viewport-fit=cover`),\n * so it changes nothing on an ordinary page. The viewer reaches an edge when\n * its box is at that edge of the window or past it, as in full screen or as\n * the whole page of a phone app.\n */\n/** For each edge of the window, whether the viewer reaches it. */\nexport interface ScreenEdges {\n readonly top: boolean;\n readonly right: boolean;\n readonly bottom: boolean;\n readonly left: boolean;\n}\n\nexport const NO_SCREEN_EDGES: ScreenEdges = {\n top: false,\n right: false,\n bottom: false,\n left: false,\n};\n\n/** A box in window px. */\nexport interface ScreenBox {\n readonly left: number;\n readonly top: number;\n readonly right: number;\n readonly bottom: number;\n}\n\n/** How close to the window's edge a box counts as reaching it, for rounding. */\nconst EDGE_TOLERANCE_PX = 1;\n\n/** The edges of a `width` × `height` window that `box` reaches while some of it is on screen. */\nexport function screenEdgesOf(\n box: ScreenBox,\n view: { readonly width: number; readonly height: number },\n): ScreenEdges {\n const across = box.right > 0 && box.left < view.width;\n const down = box.bottom > 0 && box.top < view.height;\n return {\n top: across && box.top <= EDGE_TOLERANCE_PX && box.bottom > 0,\n right: down && box.right >= view.width - EDGE_TOLERANCE_PX && box.left < view.width,\n bottom: across && box.bottom >= view.height - EDGE_TOLERANCE_PX && box.top < view.height,\n left: down && box.left <= EDGE_TOLERANCE_PX && box.right > 0,\n };\n}\n\nexport const sameScreenEdges = (left: ScreenEdges, right: ScreenEdges): boolean =>\n left.top === right.top &&\n left.right === right.right &&\n left.bottom === right.bottom &&\n left.left === right.left;\n\n/** The padding of each side in `sides` whose edge the viewer reaches. */\nexport interface SafeAreaPadding {\n paddingTop?: string;\n paddingRight?: string;\n paddingBottom?: string;\n paddingLeft?: string;\n}\n\nconst PADDING: Readonly<Record<keyof ScreenEdges, keyof SafeAreaPadding>> = {\n top: 'paddingTop',\n right: 'paddingRight',\n bottom: 'paddingBottom',\n left: 'paddingLeft',\n};\n\n/** The safe-area padding for the reached edges among `sides` (default all four), as style properties. */\nexport function safeAreaPaddingOf(\n edges: ScreenEdges,\n sides: readonly (keyof ScreenEdges)[] = ['top', 'right', 'bottom', 'left'],\n): SafeAreaPadding {\n const padding: SafeAreaPadding = {};\n for (const side of sides) {\n if (edges[side]) padding[PADDING[side]] = `env(safe-area-inset-${side}, 0px)`;\n }\n return padding;\n}\n\n/** The window's size, without its scrollbars. */\nexport function windowSizeOf(element: Element): { width: number; height: number } {\n const { clientWidth, clientHeight } = element.ownerDocument.documentElement;\n return { width: clientWidth, height: clientHeight };\n}\n","/**\n * What opens over the whole viewer: a layer the size of the viewer's box on\n * screen, in the browser's top layer, where sheets and dialogs open. In the\n * top layer nothing the viewer draws covers it, and what opens later (a menu\n * from a sheet, a dialog from a panel) sits above what opened before. And\n * what holds the keyboard while it's open (a sheet, a dialog): it takes\n * focus, keeps Tab inside it, closes on Escape, and gives focus back to what\n * had it before, the button that opened it. An overlay panel takes focus the\n * same way but lets Tab move on, so the rest of the viewer stays in reach.\n */\nimport {\n attachFocusTrap,\n deepActiveElementOf,\n moveFocusInto,\n observeClientGeometry,\n} from '@embedpdf/web';\n\nimport { screenEdgesOf, windowSizeOf, type ScreenEdges } from './screen-edges';\n\n/** The `popover` attribute, which React's types don't list yet. */\nexport const POPOVER = { popover: 'manual' } as Record<string, string>;\n\n/**\n * The classes every layer starts from. It clips what overflows it rather than hiding it: a box\n * with hidden overflow still scrolls, and Safari scrolls it to show a control that takes focus\n * while the sheet holding it still slides in from below.\n */\nexport const LAYER_CLASS = 'text-fg m-0 overflow-clip border-0 bg-transparent';\n\n/** The custom element's root: the viewer's box. */\nconst VIEWER_ROOT_SELECTOR = '[data-embedpdf-root]';\n\n/** What another surface that holds focus is marked with: a press in it is not a press outside. */\nexport const MODAL_SELECTOR = '[aria-modal=\"true\"]';\n\n/**\n * What the viewer's root is marked with: it takes focus on a press that lands on nothing that\n * takes focus itself (the pages), so the viewer's keys reach it from there.\n */\nexport const FOCUS_ROOT_ATTRIBUTE = 'data-epdf-focus-root';\n\n/** Place `element` (`position: fixed`) at a box in window px. */\nfunction placeFixed(\n element: HTMLElement,\n box: { left: number; top: number; width: number; height: number },\n): void {\n element.style.left = `${box.left}px`;\n element.style.top = `${box.top}px`;\n element.style.width = `${box.width}px`;\n element.style.height = `${box.height}px`;\n // Without the top layer, `position: fixed` starts at the viewer's box (its `contain`): move by\n // however far from the window's origin the layer landed.\n const landed = element.getBoundingClientRect();\n if (Math.abs(landed.left - box.left) > 0.5 || Math.abs(landed.top - box.top) > 0.5) {\n element.style.left = `${2 * box.left - landed.left}px`;\n element.style.top = `${2 * box.top - landed.top}px`;\n }\n}\n\n/**\n * Show `layer` in the top layer over the part of the viewer on screen, and keep it there as the\n * viewer or the window resizes or scrolls. `onPlace` hears which edges of the window the viewer\n * reaches, each time the layer is placed. Returns the stop, which hides the layer.\n */\nexport function attachViewerLayer(\n layer: HTMLElement,\n onPlace?: (edges: ScreenEdges) => void,\n): () => void {\n const root = layer.parentElement?.closest(VIEWER_ROOT_SELECTOR) ?? null;\n if (typeof layer.showPopover === 'function') layer.showPopover();\n let placed = '';\n const place = () => {\n const view = windowSizeOf(layer);\n const rect = root?.getBoundingClientRect() ?? {\n left: 0,\n top: 0,\n right: view.width,\n bottom: view.height,\n };\n const left = Math.max(0, rect.left);\n const top = Math.max(0, rect.top);\n const box = {\n left,\n top,\n width: Math.max(0, Math.min(view.width, rect.right) - left),\n height: Math.max(0, Math.min(view.height, rect.bottom) - top),\n };\n // Scrolling inside the layer reaches here too: only a move of the viewer changes anything.\n const key = `${box.left},${box.top},${box.width},${box.height},${view.width},${view.height}`;\n if (key === placed) return;\n placed = key;\n placeFixed(layer, box);\n onPlace?.(screenEdgesOf(rect, view));\n };\n place();\n const resizes = new ResizeObserver(place);\n if (root) resizes.observe(root);\n const stopGeometry = observeClientGeometry(place);\n return () => {\n stopGeometry();\n resizes.disconnect();\n if (typeof layer.hidePopover === 'function' && layer.matches(':popover-open')) {\n layer.hidePopover();\n }\n };\n}\n\nexport interface HeldFocus {\n /** Stop holding focus, leaving it where it is. */\n standDown(): void;\n /** Stop holding focus, and give it back to what had it before when it's still in `container`. */\n release(): void;\n}\n\n/**\n * Hold the keyboard in `container` until released: focus moves into it unless it's there\n * already (to `initialFocus`, else its first control), so a field that took the cursor as it\n * appeared keeps it; Tab and Shift+Tab stay inside it, and Escape calls `onEscape`. With\n * `trap: false` Tab moves on past it, as it does past anything else in the viewer. Releasing\n * gives focus back to what had it before, unless the user has moved it elsewhere: a press on\n * the pages, which puts focus on the viewer's root, doesn't count.\n */\nexport function holdFocus(\n container: HTMLElement,\n options: {\n readonly onEscape: () => void;\n readonly initialFocus?: HTMLElement | null;\n readonly trap?: boolean;\n },\n): HeldFocus {\n const doc = container.ownerDocument;\n const before = deepActiveElementOf(doc) as HTMLElement | null;\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key !== 'Escape' || event.defaultPrevented) return;\n event.preventDefault();\n // Escape closes this, not what it opened over.\n event.stopPropagation();\n options.onEscape();\n };\n container.addEventListener('keydown', onKeyDown);\n let releaseTrap: (() => void) | null = null;\n if (options.trap === false) {\n moveFocusInto(container, options.initialFocus);\n } else {\n releaseTrap = attachFocusTrap(container, {\n initialFocus: options.initialFocus,\n returnFocus: false,\n });\n }\n const standDown = () => {\n releaseTrap?.();\n releaseTrap = null;\n container.removeEventListener('keydown', onKeyDown);\n };\n return {\n standDown,\n release: () => {\n standDown();\n const active = deepActiveElementOf(doc);\n const nowhere =\n active === null || container.contains(active) || active.hasAttribute(FOCUS_ROOT_ATTRIBUTE);\n if (before?.isConnected && nowhere) before.focus();\n },\n };\n}\n","/**\n * Where a sheet comes to rest after its handle is let go: at one of the\n * heights it rests at, or closed. A flick moves it a whole step, down to the\n * next lower height (closing from the lowest) or up to the next higher one;\n * a slow drag settles at the nearest height, and closes when the sheet was\n * dragged below half of the lowest.\n */\n\n/** A release faster than this, in px per second, is a flick. */\nexport const SHEET_FLICK_PX_PER_S = 500;\n\n/** How much of the viewer's height a panel's sheet opens at. */\nexport const PANEL_SHEET_OPEN_FRACTION = 0.5;\n\n/** Px a sheet leaves above itself at its tallest, so the pages show it's a sheet. */\nexport const SHEET_TOP_CLEARANCE_PX = 40;\n\n/**\n * The height a sheet dragged to `height` comes to rest at, one of `heights` (ascending), or null\n * when it closes. `velocity` is the release speed in px per second, downward positive.\n */\nexport function sheetRestOf(\n heights: readonly number[],\n height: number,\n velocity: number,\n): number | null {\n if (heights.length === 0) return null;\n if (velocity >= SHEET_FLICK_PX_PER_S) {\n const lower = heights.filter((rest) => rest < height - 1);\n return lower.length > 0 ? lower[lower.length - 1]! : null;\n }\n if (velocity <= -SHEET_FLICK_PX_PER_S) {\n return heights.find((rest) => rest > height + 1) ?? heights[heights.length - 1]!;\n }\n if (height < heights[0]! / 2) return null;\n return heights.reduce((nearest, rest) =>\n Math.abs(rest - height) < Math.abs(nearest - height) ? rest : nearest,\n );\n}\n\n/** The heights a panel's sheet rests at in a viewer `viewerHeight` px tall: half of it, and nearly all. */\nexport function panelSheetHeightsOf(viewerHeight: number): readonly number[] {\n const tallest = Math.max(0, Math.round(viewerHeight - SHEET_TOP_CLEARANCE_PX));\n const open = Math.min(tallest, Math.round(viewerHeight * PANEL_SHEET_OPEN_FRACTION));\n return open < tallest ? [open, tallest] : [tallest];\n}\n","/**\n * A sheet: what a panel or a menu is on a phone. It slides up from the bottom\n * of the viewer, over the pages, in a layer over the whole viewer\n * (`viewer-layer.ts`); the viewer behind it dims, and a tap there closes it.\n * Its handle drags it taller or shorter, or down to close it, the dimming\n * fading as it goes, and a flick moves it a whole step (`sheet-rest.ts`).\n * It keeps clear of the home bar and the screen's rounded corners when the\n * viewer reaches the screen's edge.\n * A sheet takes focus when it opens, keeps Tab inside it, closes on Escape\n * and gives focus back to the button that opened it; a menu's sheet leaves\n * the keyboard to its menu. A sheet that holds a list closes when one of its\n * items is picked ({@link useCloseOnPick}).\n */\nimport { computeReleaseVelocity, observeOutsidePress } from '@embedpdf/web';\nimport {\n createContext,\n useContext,\n useLayoutEffect,\n useRef,\n type PointerEvent as ReactPointerEvent,\n type ReactNode,\n} from 'react';\n\nimport { safeAreaPaddingOf } from './screen-edges';\nimport { SHEET_TOP_CLEARANCE_PX, panelSheetHeightsOf, sheetRestOf } from './sheet-rest';\nimport { LAYER_CLASS, MODAL_SELECTOR, POPOVER, attachViewerLayer, holdFocus } from './viewer-layer';\n\n/** A press that starts on one of these in the handle's row is the control's, not a drag. */\nconst CONTROL_SELECTOR =\n 'button, a[href], input, select, textarea, [role=\"tab\"], [contenteditable]:not([contenteditable=\"false\"])';\n\n/** The dimmed viewer behind a sheet or a dialog. */\nexport const SCRIM_CLASS = 'bg-black/30 transition-opacity duration-200 starting:opacity-0';\n\nconst NO_PICK = () => {};\n\nconst CloseOnPickContext = createContext<() => void>(NO_PICK);\n\n/** Gives what's inside a sheet or an overlay the close it runs when one of its items is picked. */\nexport const CloseOnPickProvider = CloseOnPickContext.Provider;\n\n/**\n * What a list's item runs when it's picked: it closes the sheet or the overlay the list is in,\n * so you see where it took you. It does nothing in a docked panel, or in one that isn't a list.\n */\nexport function useCloseOnPick(): () => void {\n return useContext(CloseOnPickContext);\n}\n\n/** Whether a press landed in another surface that holds focus, like a dialog opened over this one. */\nconst pressedInOtherModal = (event: PointerEvent, own: Element): boolean =>\n event\n .composedPath()\n .some((node) => node instanceof Element && node !== own && node.matches(MODAL_SELECTOR));\n\nexport interface SheetProps {\n /** Names the sheet for assistive technology. */\n readonly label: string;\n readonly onClose: () => void;\n /**\n * `'content'`: as tall as what's in it, like a menu. `'panel'`: opens at half the viewer's\n * height, and drags up to nearly all of it. Default `'content'`.\n */\n readonly size?: 'content' | 'panel';\n /** It holds a list: picking an item ({@link useCloseOnPick}) closes it. */\n readonly list?: boolean;\n /**\n * Whether it holds the keyboard (default true). A menu's sheet doesn't: its menu takes focus\n * and gives it back.\n */\n readonly trapFocus?: boolean;\n /** Drawn under the handle, above the part that scrolls; dragging it moves the sheet too. */\n readonly header?: ReactNode;\n /** The sheet's part name, for page CSS through `::part()`, when it is one: a panel's `panel`. */\n readonly part?: string;\n /** `data-*` attributes for the sheet, by their names without `data-`. */\n readonly data?: Readonly<Record<string, string>>;\n readonly children: ReactNode;\n}\n\ninterface Drag {\n readonly pointerId: number;\n readonly startY: number;\n readonly startHeight: number;\n /** The heights it can rest at, ascending. */\n readonly heights: readonly number[];\n readonly samples: { t: number; x: number; y: number }[];\n height: number;\n}\n\n/** `data` as the `data-*` attributes it names. */\nconst dataAttributesOf = (data: Readonly<Record<string, string>> | undefined) =>\n Object.fromEntries(Object.entries(data ?? {}).map(([name, value]) => [`data-${name}`, value]));\n\n/** How many pointer samples a drag keeps for its release speed. */\nconst DRAG_SAMPLES = 8;\n\nexport function Sheet({\n label,\n onClose,\n size = 'content',\n list = false,\n trapFocus = true,\n header,\n part,\n data,\n children,\n}: SheetProps) {\n const layer = useRef<HTMLDivElement>(null);\n const scrim = useRef<HTMLDivElement>(null);\n const sheet = useRef<HTMLDivElement>(null);\n const close = useRef(onClose);\n close.current = onClose;\n /** Which of its heights a panel's sheet rests at: the first (open) or a taller one. */\n const restIndex = useRef(0);\n const drag = useRef<Drag | null>(null);\n\n /** The heights it rests at now: a panel's from the viewer's height, a menu's as tall as it is. */\n const heightsNow = (): readonly number[] =>\n size === 'panel'\n ? panelSheetHeightsOf(layer.current?.offsetHeight ?? 0)\n : [sheet.current?.offsetHeight ?? 0];\n\n /**\n * Show the sheet dragged to `height` (its top that far above the viewer's bottom). Below its\n * lowest rest, the viewer behind it dims less the further down it goes.\n */\n const show = (height: number, heights: readonly number[]) => {\n const element = sheet.current;\n if (!element || heights.length === 0) return;\n const lowest = heights[0]!;\n const tallest = heights[heights.length - 1]!;\n if (size === 'panel') element.style.height = `${Math.min(tallest, Math.max(lowest, height))}px`;\n const below = Math.max(0, lowest - height);\n element.style.translate = below > 0 ? `0 ${below}px` : '';\n if (scrim.current) {\n scrim.current.style.opacity = below > 0 ? `${Math.max(0, 1 - below / lowest)}` : '';\n }\n };\n\n useLayoutEffect(() => {\n const layerElement = layer.current;\n const sheetElement = sheet.current;\n if (!layerElement || !sheetElement) return;\n const stopLayer = attachViewerLayer(layerElement, (edges) => {\n const padding = safeAreaPaddingOf(edges, ['right', 'bottom', 'left']);\n sheetElement.style.paddingLeft = padding.paddingLeft ?? '';\n sheetElement.style.paddingRight = padding.paddingRight ?? '';\n sheetElement.style.paddingBottom = padding.paddingBottom ?? '';\n sheetElement.style.maxHeight = `${Math.max(0, layerElement.offsetHeight - SHEET_TOP_CLEARANCE_PX)}px`;\n if (size === 'panel' && !drag.current) {\n const heights = panelSheetHeightsOf(layerElement.offsetHeight);\n show(heights[Math.min(restIndex.current, heights.length - 1)]!, heights);\n }\n });\n const held = trapFocus ? holdFocus(sheetElement, { onEscape: () => close.current() }) : null;\n // A press on the viewer above the sheet is the scrim's click; one outside the viewer closes it too.\n const stopOutside = observeOutsidePress(layerElement, (event) => {\n if (!pressedInOtherModal(event, sheetElement)) close.current();\n });\n return () => {\n stopOutside();\n held?.release();\n stopLayer();\n };\n // The sheet opens once; it closes by unmounting.\n }, []);\n\n const onGripDown = (event: ReactPointerEvent<HTMLDivElement>) => {\n if (event.pointerType === 'mouse' && event.button !== 0) return;\n if ((event.target as Element).closest(CONTROL_SELECTOR)) return;\n const element = sheet.current;\n if (!element) return;\n event.currentTarget.setPointerCapture(event.pointerId);\n const startHeight = element.offsetHeight;\n drag.current = {\n pointerId: event.pointerId,\n startY: event.clientY,\n startHeight,\n heights: heightsNow(),\n samples: [{ t: event.timeStamp, x: event.clientX, y: event.clientY }],\n height: startHeight,\n };\n // The sheet and the dimming follow the finger, without easing.\n layer.current?.setAttribute('data-dragging', '');\n };\n\n const onGripMove = (event: ReactPointerEvent<HTMLDivElement>) => {\n const current = drag.current;\n if (!current || current.pointerId !== event.pointerId) return;\n current.height = current.startHeight - (event.clientY - current.startY);\n current.samples.push({ t: event.timeStamp, x: event.clientX, y: event.clientY });\n if (current.samples.length > DRAG_SAMPLES) current.samples.shift();\n show(current.height, current.heights);\n };\n\n const onGripUp = (event: ReactPointerEvent<HTMLDivElement>) => {\n const current = drag.current;\n if (!current || current.pointerId !== event.pointerId) return;\n drag.current = null;\n layer.current?.removeAttribute('data-dragging');\n const cancelled = event.type === 'pointercancel';\n const velocity = cancelled\n ? 0\n : (computeReleaseVelocity(current.samples, event.timeStamp)?.vy ?? 0);\n const rest = cancelled\n ? current.heights[restIndex.current]!\n : sheetRestOf(current.heights, current.height, velocity);\n if (rest === null) {\n close.current();\n return;\n }\n restIndex.current = Math.max(0, current.heights.indexOf(rest));\n show(rest, current.heights);\n };\n\n return (\n <div\n ref={layer}\n {...POPOVER}\n data-epdf-layer=\"\"\n className={`${LAYER_CLASS} p-0 open:flex open:flex-col open:justify-end`}\n style={{ position: 'fixed', inset: 'auto', maxWidth: 'none', maxHeight: 'none' }}\n >\n {/* Behind the whole viewer, not just above the sheet: the sheet sliding in or dragged down\n never uncovers it. */}\n <div\n ref={scrim}\n aria-hidden=\"true\"\n className={`absolute inset-0 ${SCRIM_CLASS} in-data-dragging:transition-none`}\n onClick={() => close.current()}\n />\n <div\n ref={sheet}\n role={trapFocus ? 'dialog' : undefined}\n aria-modal={trapFocus ? true : undefined}\n aria-label={trapFocus ? label : undefined}\n part={part}\n {...dataAttributesOf(data)}\n data-epdf-sheet=\"\"\n className=\"border-border-subtle bg-elevated starting:translate-y-full in-data-dragging:transition-none relative flex w-full shrink-0 flex-col rounded-t-2xl border-t shadow-2xl transition-[translate,height] duration-200 ease-out motion-reduce:transition-none\"\n >\n <div\n className=\"shrink-0 touch-pan-x select-none\"\n onPointerDown={onGripDown}\n onPointerMove={onGripMove}\n onPointerUp={onGripUp}\n onPointerCancel={onGripUp}\n >\n <div\n data-epdf-sheet-handle=\"\"\n className=\"flex h-6 cursor-grab items-center justify-center active:cursor-grabbing\"\n >\n <span className=\"bg-border-strong h-1 w-10 rounded-full\" />\n </div>\n {header}\n </div>\n <div className=\"flex min-h-0 flex-1 flex-col overflow-y-auto overflow-x-hidden overscroll-contain\">\n <CloseOnPickProvider value={list ? () => close.current() : NO_PICK}>\n {children}\n </CloseOnPickProvider>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- const require_dialog = require("./dialog-t8F13BL_.cjs");
1
+ const require_dialog = require("./dialog-CngKTnAM.cjs");
2
2
  let react = require("react");
3
3
  let _embedpdf_react_stamp = require("@embedpdf/react/stamp");
4
4
  let _embedpdf_react_signature = require("@embedpdf/react/signature");
@@ -152,4 +152,4 @@ function SignDialog() {
152
152
  //#endregion
153
153
  exports.SignDialog = SignDialog;
154
154
 
155
- //# sourceMappingURL=sign-dialog-CgnR7akj.cjs.map
155
+ //# sourceMappingURL=sign-dialog-D5WGx-Yn.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"sign-dialog-CgnR7akj.cjs","names":["Dialog"],"sources":["../src/ui/sign-dialog.tsx"],"sourcesContent":["/**\n * The sign dialog (`signature-sign` modal surface): a mark met a field and\n * the mode is `ask` — or a \"sign here\" flow wants the facts first. Shows\n * the mark as it will sit in the field, takes reason and location, offers\n * a certification when the config allows it, and either seals the field\n * (`sign`) or only draws the mark into it (`fillField`). Escape cancels,\n * unless it's signing.\n */\nimport { useId, useState } from 'react';\nimport { useSurface } from '@embedpdf/react/shell';\nimport { useT } from '@embedpdf/react/i18n';\nimport { useStampAssetPreviewUrl } from '@embedpdf/react/stamp';\nimport { useSignature, type FormFieldRef, type Mark } from '@embedpdf/react/signature';\nimport { Dialog } from './dialog';\n\nexport function SignDialog() {\n const t = useT();\n const surface = useSurface('signature-sign');\n const signature = useSignature();\n const [reason, setReason] = useState('');\n const [location, setLocation] = useState('');\n // What the signature declares about later changes: a plain approval (form\n // filling and further signatures keep it valid; Acrobat's reading), or a\n // certification with its DocMDP permission. Only the first signature can certify.\n const [signKind, setSignKind] = useState<'approval' | 'certify-3' | 'certify-2' | 'certify-1'>(\n 'approval',\n );\n const [busy, setBusy] = useState<'sign' | 'fill' | null>(null);\n const [error, setError] = useState<string | null>(null);\n const titleId = useId();\n\n const field = surface.props?.field as FormFieldRef | undefined;\n const mark = surface.props?.mark as Mark | undefined;\n const assetId = mark && 'assetId' in mark ? mark.assetId : null;\n const preview = useStampAssetPreviewUrl(assetId);\n if (!surface.isOpen || !field || !mark) return null;\n\n const signer = {\n ...(reason.trim() ? { reason: reason.trim() } : {}),\n ...(location.trim() ? { location: location.trim() } : {}),\n };\n const run = async (kind: 'sign' | 'fill') => {\n setBusy(kind);\n setError(null);\n try {\n if (kind === 'sign') {\n await signature.sign({\n field,\n mark,\n signer,\n ...(signKind !== 'approval' && signature.canCertify()\n ? { certify: { permission: Number(signKind.slice(-1)) as 1 | 2 | 3 } }\n : {}),\n });\n } else {\n await signature.fillField(field, mark);\n }\n surface.close();\n } catch (error) {\n setError(error instanceof Error ? error.message : String(error));\n } finally {\n setBusy(null);\n }\n };\n\n const input = 'border-border bg-surface text-fg w-full rounded border px-2 py-1.5 text-sm';\n return (\n <Dialog\n labelledBy={titleId}\n onClose={() => {\n if (busy == null) surface.close();\n }}\n className=\"border-border bg-surface w-96 rounded-lg border p-4 shadow-xl\"\n >\n <h2 id={titleId} className=\"text-fg text-base font-semibold\">\n {t('demo.signTitle')}\n </h2>\n <div className=\"border-border-subtle mt-3 flex h-24 items-center justify-center rounded-md border p-2\">\n {preview ? (\n <img src={preview} alt=\"\" className=\"max-h-full max-w-full object-contain\" />\n ) : (\n <span className=\"text-fg-muted text-sm\">{t('demo.signatureLabel')}</span>\n )}\n </div>\n <label className=\"text-fg-muted mt-3 block text-xs\">\n {t('demo.signReason')}\n <input\n value={reason}\n onChange={(event) => setReason(event.target.value)}\n className={input}\n />\n </label>\n <label className=\"text-fg-muted mt-2 block text-xs\">\n {t('demo.signLocation')}\n <input\n value={location}\n onChange={(event) => setLocation(event.target.value)}\n className={input}\n />\n </label>\n {signature.canCertify() ? (\n <label className=\"text-fg-muted mt-3 block text-xs\">\n {t('demo.signKind')}\n <select\n value={signKind}\n onChange={(event) => setSignKind(event.target.value as typeof signKind)}\n className={input}\n >\n <option value=\"approval\">{t('demo.signKindApproval')}</option>\n <option value=\"certify-3\">{t('demo.signKindCertifyAnnotate')}</option>\n <option value=\"certify-2\">{t('demo.signKindCertifyFill')}</option>\n <option value=\"certify-1\">{t('demo.signKindCertifyLocked')}</option>\n </select>\n </label>\n ) : null}\n {error ? <p className=\"mt-2 text-sm text-red-600\">{error}</p> : null}\n <div className=\"mt-4 flex justify-end gap-2\">\n <button\n type=\"button\"\n disabled={busy != null}\n onClick={surface.close}\n className=\"border-border text-fg hover:bg-hover rounded-md border px-3 py-1.5 text-sm\"\n >\n {t('demo.cancel')}\n </button>\n {signature.canFill(field) ? (\n <button\n type=\"button\"\n disabled={busy != null}\n onClick={() => void run('fill')}\n className=\"border-border text-fg hover:bg-hover rounded-md border px-3 py-1.5 text-sm disabled:opacity-60\"\n >\n {t('demo.signFill')}\n </button>\n ) : null}\n {signature.canSign(field) ? (\n <button\n type=\"button\"\n disabled={busy != null}\n onClick={() => void run('sign')}\n className=\"bg-accent text-on-accent rounded-md px-3 py-1.5 text-sm disabled:opacity-60\"\n >\n {busy === 'sign' ? t('demo.signSigning') : t('demo.signButton')}\n </button>\n ) : null}\n </div>\n </Dialog>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"sign-dialog-D5WGx-Yn.cjs","names":["Dialog"],"sources":["../src/ui/sign-dialog.tsx"],"sourcesContent":["/**\n * The sign dialog (`signature-sign` modal surface): a mark met a field and\n * the mode is `ask` — or a \"sign here\" flow wants the facts first. Shows\n * the mark as it will sit in the field, takes reason and location, offers\n * a certification when the config allows it, and either seals the field\n * (`sign`) or only draws the mark into it (`fillField`). Escape cancels,\n * unless it's signing.\n */\nimport { useId, useState } from 'react';\nimport { useSurface } from '@embedpdf/react/shell';\nimport { useT } from '@embedpdf/react/i18n';\nimport { useStampAssetPreviewUrl } from '@embedpdf/react/stamp';\nimport { useSignature, type FormFieldRef, type Mark } from '@embedpdf/react/signature';\nimport { Dialog } from './dialog';\n\nexport function SignDialog() {\n const t = useT();\n const surface = useSurface('signature-sign');\n const signature = useSignature();\n const [reason, setReason] = useState('');\n const [location, setLocation] = useState('');\n // What the signature declares about later changes: a plain approval (form\n // filling and further signatures keep it valid; Acrobat's reading), or a\n // certification with its DocMDP permission. Only the first signature can certify.\n const [signKind, setSignKind] = useState<'approval' | 'certify-3' | 'certify-2' | 'certify-1'>(\n 'approval',\n );\n const [busy, setBusy] = useState<'sign' | 'fill' | null>(null);\n const [error, setError] = useState<string | null>(null);\n const titleId = useId();\n\n const field = surface.props?.field as FormFieldRef | undefined;\n const mark = surface.props?.mark as Mark | undefined;\n const assetId = mark && 'assetId' in mark ? mark.assetId : null;\n const preview = useStampAssetPreviewUrl(assetId);\n if (!surface.isOpen || !field || !mark) return null;\n\n const signer = {\n ...(reason.trim() ? { reason: reason.trim() } : {}),\n ...(location.trim() ? { location: location.trim() } : {}),\n };\n const run = async (kind: 'sign' | 'fill') => {\n setBusy(kind);\n setError(null);\n try {\n if (kind === 'sign') {\n await signature.sign({\n field,\n mark,\n signer,\n ...(signKind !== 'approval' && signature.canCertify()\n ? { certify: { permission: Number(signKind.slice(-1)) as 1 | 2 | 3 } }\n : {}),\n });\n } else {\n await signature.fillField(field, mark);\n }\n surface.close();\n } catch (error) {\n setError(error instanceof Error ? error.message : String(error));\n } finally {\n setBusy(null);\n }\n };\n\n const input = 'border-border bg-surface text-fg w-full rounded border px-2 py-1.5 text-sm';\n return (\n <Dialog\n labelledBy={titleId}\n onClose={() => {\n if (busy == null) surface.close();\n }}\n className=\"border-border bg-surface w-96 rounded-lg border p-4 shadow-xl\"\n >\n <h2 id={titleId} className=\"text-fg text-base font-semibold\">\n {t('demo.signTitle')}\n </h2>\n <div className=\"border-border-subtle mt-3 flex h-24 items-center justify-center rounded-md border p-2\">\n {preview ? (\n <img src={preview} alt=\"\" className=\"max-h-full max-w-full object-contain\" />\n ) : (\n <span className=\"text-fg-muted text-sm\">{t('demo.signatureLabel')}</span>\n )}\n </div>\n <label className=\"text-fg-muted mt-3 block text-xs\">\n {t('demo.signReason')}\n <input\n value={reason}\n onChange={(event) => setReason(event.target.value)}\n className={input}\n />\n </label>\n <label className=\"text-fg-muted mt-2 block text-xs\">\n {t('demo.signLocation')}\n <input\n value={location}\n onChange={(event) => setLocation(event.target.value)}\n className={input}\n />\n </label>\n {signature.canCertify() ? (\n <label className=\"text-fg-muted mt-3 block text-xs\">\n {t('demo.signKind')}\n <select\n value={signKind}\n onChange={(event) => setSignKind(event.target.value as typeof signKind)}\n className={input}\n >\n <option value=\"approval\">{t('demo.signKindApproval')}</option>\n <option value=\"certify-3\">{t('demo.signKindCertifyAnnotate')}</option>\n <option value=\"certify-2\">{t('demo.signKindCertifyFill')}</option>\n <option value=\"certify-1\">{t('demo.signKindCertifyLocked')}</option>\n </select>\n </label>\n ) : null}\n {error ? <p className=\"mt-2 text-sm text-red-600\">{error}</p> : null}\n <div className=\"mt-4 flex justify-end gap-2\">\n <button\n type=\"button\"\n disabled={busy != null}\n onClick={surface.close}\n className=\"border-border text-fg hover:bg-hover rounded-md border px-3 py-1.5 text-sm\"\n >\n {t('demo.cancel')}\n </button>\n {signature.canFill(field) ? (\n <button\n type=\"button\"\n disabled={busy != null}\n onClick={() => void run('fill')}\n className=\"border-border text-fg hover:bg-hover rounded-md border px-3 py-1.5 text-sm disabled:opacity-60\"\n >\n {t('demo.signFill')}\n </button>\n ) : null}\n {signature.canSign(field) ? (\n <button\n type=\"button\"\n disabled={busy != null}\n onClick={() => void run('sign')}\n className=\"bg-accent text-on-accent rounded-md px-3 py-1.5 text-sm disabled:opacity-60\"\n >\n {busy === 'sign' ? t('demo.signSigning') : t('demo.signButton')}\n </button>\n ) : null}\n </div>\n </Dialog>\n );\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { t as Dialog } from "./dialog-B5GBisKv.js";
1
+ import { t as Dialog } from "./dialog-BIb373cg.js";
2
2
  import { useId, useState } from "react";
3
3
  import { useStampAssetPreviewUrl } from "@embedpdf/react/stamp";
4
4
  import { useSignature } from "@embedpdf/react/signature";
@@ -152,4 +152,4 @@ function SignDialog() {
152
152
  //#endregion
153
153
  export { SignDialog };
154
154
 
155
- //# sourceMappingURL=sign-dialog-GDqCoQhA.js.map
155
+ //# sourceMappingURL=sign-dialog-DerNe8qT.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sign-dialog-GDqCoQhA.js","names":[],"sources":["../src/ui/sign-dialog.tsx"],"sourcesContent":["/**\n * The sign dialog (`signature-sign` modal surface): a mark met a field and\n * the mode is `ask` — or a \"sign here\" flow wants the facts first. Shows\n * the mark as it will sit in the field, takes reason and location, offers\n * a certification when the config allows it, and either seals the field\n * (`sign`) or only draws the mark into it (`fillField`). Escape cancels,\n * unless it's signing.\n */\nimport { useId, useState } from 'react';\nimport { useSurface } from '@embedpdf/react/shell';\nimport { useT } from '@embedpdf/react/i18n';\nimport { useStampAssetPreviewUrl } from '@embedpdf/react/stamp';\nimport { useSignature, type FormFieldRef, type Mark } from '@embedpdf/react/signature';\nimport { Dialog } from './dialog';\n\nexport function SignDialog() {\n const t = useT();\n const surface = useSurface('signature-sign');\n const signature = useSignature();\n const [reason, setReason] = useState('');\n const [location, setLocation] = useState('');\n // What the signature declares about later changes: a plain approval (form\n // filling and further signatures keep it valid; Acrobat's reading), or a\n // certification with its DocMDP permission. Only the first signature can certify.\n const [signKind, setSignKind] = useState<'approval' | 'certify-3' | 'certify-2' | 'certify-1'>(\n 'approval',\n );\n const [busy, setBusy] = useState<'sign' | 'fill' | null>(null);\n const [error, setError] = useState<string | null>(null);\n const titleId = useId();\n\n const field = surface.props?.field as FormFieldRef | undefined;\n const mark = surface.props?.mark as Mark | undefined;\n const assetId = mark && 'assetId' in mark ? mark.assetId : null;\n const preview = useStampAssetPreviewUrl(assetId);\n if (!surface.isOpen || !field || !mark) return null;\n\n const signer = {\n ...(reason.trim() ? { reason: reason.trim() } : {}),\n ...(location.trim() ? { location: location.trim() } : {}),\n };\n const run = async (kind: 'sign' | 'fill') => {\n setBusy(kind);\n setError(null);\n try {\n if (kind === 'sign') {\n await signature.sign({\n field,\n mark,\n signer,\n ...(signKind !== 'approval' && signature.canCertify()\n ? { certify: { permission: Number(signKind.slice(-1)) as 1 | 2 | 3 } }\n : {}),\n });\n } else {\n await signature.fillField(field, mark);\n }\n surface.close();\n } catch (error) {\n setError(error instanceof Error ? error.message : String(error));\n } finally {\n setBusy(null);\n }\n };\n\n const input = 'border-border bg-surface text-fg w-full rounded border px-2 py-1.5 text-sm';\n return (\n <Dialog\n labelledBy={titleId}\n onClose={() => {\n if (busy == null) surface.close();\n }}\n className=\"border-border bg-surface w-96 rounded-lg border p-4 shadow-xl\"\n >\n <h2 id={titleId} className=\"text-fg text-base font-semibold\">\n {t('demo.signTitle')}\n </h2>\n <div className=\"border-border-subtle mt-3 flex h-24 items-center justify-center rounded-md border p-2\">\n {preview ? (\n <img src={preview} alt=\"\" className=\"max-h-full max-w-full object-contain\" />\n ) : (\n <span className=\"text-fg-muted text-sm\">{t('demo.signatureLabel')}</span>\n )}\n </div>\n <label className=\"text-fg-muted mt-3 block text-xs\">\n {t('demo.signReason')}\n <input\n value={reason}\n onChange={(event) => setReason(event.target.value)}\n className={input}\n />\n </label>\n <label className=\"text-fg-muted mt-2 block text-xs\">\n {t('demo.signLocation')}\n <input\n value={location}\n onChange={(event) => setLocation(event.target.value)}\n className={input}\n />\n </label>\n {signature.canCertify() ? (\n <label className=\"text-fg-muted mt-3 block text-xs\">\n {t('demo.signKind')}\n <select\n value={signKind}\n onChange={(event) => setSignKind(event.target.value as typeof signKind)}\n className={input}\n >\n <option value=\"approval\">{t('demo.signKindApproval')}</option>\n <option value=\"certify-3\">{t('demo.signKindCertifyAnnotate')}</option>\n <option value=\"certify-2\">{t('demo.signKindCertifyFill')}</option>\n <option value=\"certify-1\">{t('demo.signKindCertifyLocked')}</option>\n </select>\n </label>\n ) : null}\n {error ? <p className=\"mt-2 text-sm text-red-600\">{error}</p> : null}\n <div className=\"mt-4 flex justify-end gap-2\">\n <button\n type=\"button\"\n disabled={busy != null}\n onClick={surface.close}\n className=\"border-border text-fg hover:bg-hover rounded-md border px-3 py-1.5 text-sm\"\n >\n {t('demo.cancel')}\n </button>\n {signature.canFill(field) ? (\n <button\n type=\"button\"\n disabled={busy != null}\n onClick={() => void run('fill')}\n className=\"border-border text-fg hover:bg-hover rounded-md border px-3 py-1.5 text-sm disabled:opacity-60\"\n >\n {t('demo.signFill')}\n </button>\n ) : null}\n {signature.canSign(field) ? (\n <button\n type=\"button\"\n disabled={busy != null}\n onClick={() => void run('sign')}\n className=\"bg-accent text-on-accent rounded-md px-3 py-1.5 text-sm disabled:opacity-60\"\n >\n {busy === 'sign' ? t('demo.signSigning') : t('demo.signButton')}\n </button>\n ) : null}\n </div>\n </Dialog>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"sign-dialog-DerNe8qT.js","names":[],"sources":["../src/ui/sign-dialog.tsx"],"sourcesContent":["/**\n * The sign dialog (`signature-sign` modal surface): a mark met a field and\n * the mode is `ask` — or a \"sign here\" flow wants the facts first. Shows\n * the mark as it will sit in the field, takes reason and location, offers\n * a certification when the config allows it, and either seals the field\n * (`sign`) or only draws the mark into it (`fillField`). Escape cancels,\n * unless it's signing.\n */\nimport { useId, useState } from 'react';\nimport { useSurface } from '@embedpdf/react/shell';\nimport { useT } from '@embedpdf/react/i18n';\nimport { useStampAssetPreviewUrl } from '@embedpdf/react/stamp';\nimport { useSignature, type FormFieldRef, type Mark } from '@embedpdf/react/signature';\nimport { Dialog } from './dialog';\n\nexport function SignDialog() {\n const t = useT();\n const surface = useSurface('signature-sign');\n const signature = useSignature();\n const [reason, setReason] = useState('');\n const [location, setLocation] = useState('');\n // What the signature declares about later changes: a plain approval (form\n // filling and further signatures keep it valid; Acrobat's reading), or a\n // certification with its DocMDP permission. Only the first signature can certify.\n const [signKind, setSignKind] = useState<'approval' | 'certify-3' | 'certify-2' | 'certify-1'>(\n 'approval',\n );\n const [busy, setBusy] = useState<'sign' | 'fill' | null>(null);\n const [error, setError] = useState<string | null>(null);\n const titleId = useId();\n\n const field = surface.props?.field as FormFieldRef | undefined;\n const mark = surface.props?.mark as Mark | undefined;\n const assetId = mark && 'assetId' in mark ? mark.assetId : null;\n const preview = useStampAssetPreviewUrl(assetId);\n if (!surface.isOpen || !field || !mark) return null;\n\n const signer = {\n ...(reason.trim() ? { reason: reason.trim() } : {}),\n ...(location.trim() ? { location: location.trim() } : {}),\n };\n const run = async (kind: 'sign' | 'fill') => {\n setBusy(kind);\n setError(null);\n try {\n if (kind === 'sign') {\n await signature.sign({\n field,\n mark,\n signer,\n ...(signKind !== 'approval' && signature.canCertify()\n ? { certify: { permission: Number(signKind.slice(-1)) as 1 | 2 | 3 } }\n : {}),\n });\n } else {\n await signature.fillField(field, mark);\n }\n surface.close();\n } catch (error) {\n setError(error instanceof Error ? error.message : String(error));\n } finally {\n setBusy(null);\n }\n };\n\n const input = 'border-border bg-surface text-fg w-full rounded border px-2 py-1.5 text-sm';\n return (\n <Dialog\n labelledBy={titleId}\n onClose={() => {\n if (busy == null) surface.close();\n }}\n className=\"border-border bg-surface w-96 rounded-lg border p-4 shadow-xl\"\n >\n <h2 id={titleId} className=\"text-fg text-base font-semibold\">\n {t('demo.signTitle')}\n </h2>\n <div className=\"border-border-subtle mt-3 flex h-24 items-center justify-center rounded-md border p-2\">\n {preview ? (\n <img src={preview} alt=\"\" className=\"max-h-full max-w-full object-contain\" />\n ) : (\n <span className=\"text-fg-muted text-sm\">{t('demo.signatureLabel')}</span>\n )}\n </div>\n <label className=\"text-fg-muted mt-3 block text-xs\">\n {t('demo.signReason')}\n <input\n value={reason}\n onChange={(event) => setReason(event.target.value)}\n className={input}\n />\n </label>\n <label className=\"text-fg-muted mt-2 block text-xs\">\n {t('demo.signLocation')}\n <input\n value={location}\n onChange={(event) => setLocation(event.target.value)}\n className={input}\n />\n </label>\n {signature.canCertify() ? (\n <label className=\"text-fg-muted mt-3 block text-xs\">\n {t('demo.signKind')}\n <select\n value={signKind}\n onChange={(event) => setSignKind(event.target.value as typeof signKind)}\n className={input}\n >\n <option value=\"approval\">{t('demo.signKindApproval')}</option>\n <option value=\"certify-3\">{t('demo.signKindCertifyAnnotate')}</option>\n <option value=\"certify-2\">{t('demo.signKindCertifyFill')}</option>\n <option value=\"certify-1\">{t('demo.signKindCertifyLocked')}</option>\n </select>\n </label>\n ) : null}\n {error ? <p className=\"mt-2 text-sm text-red-600\">{error}</p> : null}\n <div className=\"mt-4 flex justify-end gap-2\">\n <button\n type=\"button\"\n disabled={busy != null}\n onClick={surface.close}\n className=\"border-border text-fg hover:bg-hover rounded-md border px-3 py-1.5 text-sm\"\n >\n {t('demo.cancel')}\n </button>\n {signature.canFill(field) ? (\n <button\n type=\"button\"\n disabled={busy != null}\n onClick={() => void run('fill')}\n className=\"border-border text-fg hover:bg-hover rounded-md border px-3 py-1.5 text-sm disabled:opacity-60\"\n >\n {t('demo.signFill')}\n </button>\n ) : null}\n {signature.canSign(field) ? (\n <button\n type=\"button\"\n disabled={busy != null}\n onClick={() => void run('sign')}\n className=\"bg-accent text-on-accent rounded-md px-3 py-1.5 text-sm disabled:opacity-60\"\n >\n {busy === 'sign' ? t('demo.signSigning') : t('demo.signButton')}\n </button>\n ) : null}\n </div>\n </Dialog>\n );\n}\n"],"mappings":"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"}
@@ -1,10 +1,11 @@
1
- const require_icons = require("./icons-x0Tc05bs.cjs");
1
+ const require_icons = require("./icons-1UfLUZ_y.cjs");
2
2
  let react = require("react");
3
3
  let _embedpdf_react_runtime = require("@embedpdf/react/runtime");
4
4
  let _embedpdf_react_form = require("@embedpdf/react/form");
5
5
  let _embedpdf_react_signature = require("@embedpdf/react/signature");
6
6
  let _embedpdf_react_i18n = require("@embedpdf/react/i18n");
7
7
  let _embedpdf_react_shell = require("@embedpdf/react/shell");
8
+ let _embedpdf_web = require("@embedpdf/web");
8
9
  let react_jsx_runtime = require("react/jsx-runtime");
9
10
  let _embedpdf_react_anchored = require("@embedpdf/react/anchored");
10
11
  //#region src/ui/signature-inspector.tsx
@@ -138,14 +139,7 @@ function DownloadRevision({ field }) {
138
139
  const run = async () => {
139
140
  setBusy(true);
140
141
  try {
141
- const bytes = await signature.readRevision({ revisionIndex });
142
- const blob = new Blob([bytes], { type: "application/pdf" });
143
- const url = URL.createObjectURL(blob);
144
- const anchor = document.createElement("a");
145
- anchor.href = url;
146
- anchor.download = `${dto.fieldName}-signed.pdf`;
147
- anchor.click();
148
- URL.revokeObjectURL(url);
142
+ (0, _embedpdf_web.saveFile)(await signature.readRevision({ revisionIndex }), `${dto.fieldName}-signed.pdf`, "application/pdf");
149
143
  } catch (error) {
150
144
  console.error("[embedpdf] signed revision download failed:", error);
151
145
  } finally {
@@ -163,4 +157,4 @@ function DownloadRevision({ field }) {
163
157
  //#endregion
164
158
  exports.SignatureInspector = SignatureInspector;
165
159
 
166
- //# sourceMappingURL=signature-inspector-DyO313Qf.cjs.map
160
+ //# sourceMappingURL=signature-inspector-Bz6TAfZM.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signature-inspector-Bz6TAfZM.cjs","names":["FormToken","Icon","Anchored"],"sources":["../src/ui/signature-inspector.tsx"],"sourcesContent":["/**\n * The validation popover (`signature-inspector` surface): what a signed\n * field's signature says and whether it holds — the four independent facts\n * (integrity, cryptography, trust, changes since) and the signer's claims,\n * anchored at the widget when its box is known, plus the signed revision as\n * a download (the exact bytes the signature covers). Mounted in the Stage\n * overlay so `<Anchored>` can project onto the page.\n */\nimport { useEffect, useState } from 'react';\nimport { Anchored } from '@embedpdf/react/anchored';\nimport { FormToken } from '@embedpdf/react/form';\nimport { useOptionalCapability } from '@embedpdf/react/runtime';\nimport { useSurface } from '@embedpdf/react/shell';\nimport { useT } from '@embedpdf/react/i18n';\nimport { saveFile } from '@embedpdf/web';\nimport {\n useSignature,\n useSignatureState,\n type FormFieldRef,\n type SignatureVerdict,\n} from '@embedpdf/react/signature';\nimport { Icon } from './icons';\n\nconst verdictKey = (verdict: SignatureVerdict | null): string =>\n !verdict\n ? 'demo.verdictIndeterminate'\n : verdict.summary === 'valid'\n ? 'demo.verdictValid'\n : verdict.summary === 'valid-untrusted'\n ? 'demo.verdictValidUntrusted'\n : verdict.summary === 'invalid'\n ? verdict.modifications.basis === 'working-copy'\n ? 'demo.verdictWillInvalidate'\n : 'demo.verdictInvalid'\n : 'demo.verdictIndeterminate';\n\n/** A PDF date string (`D:YYYYMMDDHHmmSSZ` or with a zone offset) as a locale date; the raw text when it is not one. */\n/** A moment in the reader's locale; the text itself if it isn't one. */\nconst dateTime = (iso: string): string => {\n const date = new Date(iso);\n return Number.isNaN(date.getTime()) ? iso : date.toLocaleString();\n};\n\nconst tone = (summary: SignatureVerdict['summary'] | null): string =>\n summary === 'valid'\n ? 'text-green-600'\n : summary === 'invalid'\n ? 'text-red-600'\n : summary === 'valid-untrusted'\n ? 'text-amber-600'\n : 'text-fg-muted';\n\nexport function SignatureInspector() {\n const t = useT();\n const surface = useSurface('signature-inspector');\n const signature = useSignature();\n // Re-render when the signatures are read, and when one or its verdict changes.\n const status = useSignatureState((state) => state.status);\n useSignatureState((state) => state.signatures);\n const [validating, setValidating] = useState(false);\n const field = surface.props?.field as FormFieldRef | undefined;\n const dto = field ? signature.getSignature(field) : null;\n const verdict = field ? signature.getVerdict(field) : null;\n const widget = dto?.widget ?? null;\n // Where the signature shows is its widget's row in the form.\n const form = useOptionalCapability(FormToken);\n const box = (widget && form?.getWidget(widget)?.rect) ?? null;\n\n // Open on a field never validated: judge it now.\n useEffect(() => {\n if (!surface.isOpen || !dto?.signed || signature.listVerdicts()) return;\n setValidating(true);\n void signature.validate().finally(() => setValidating(false));\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [surface.isOpen, dto?.index]);\n\n if (!surface.isOpen || !field || !dto || status !== 'ready') return null;\n\n const revalidate = () => {\n setValidating(true);\n void signature.validate().finally(() => setValidating(false));\n };\n const summary = verdict?.summary ?? null;\n const modifications = verdict?.modifications.verdict ?? null;\n const row = (label: string, value: string, className = 'text-fg') => (\n <div className=\"flex items-baseline justify-between gap-3 text-xs\">\n <span className=\"text-fg-muted\">{label}</span>\n <span className={`text-end ${className}`}>{value}</span>\n </div>\n );\n\n const card = (\n <div className=\"border-border bg-elevated w-72 rounded-lg border p-3 text-sm shadow-xl\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-fg font-semibold\">{t('demo.inspectTitle')}</span>\n <button\n type=\"button\"\n onClick={surface.close}\n className=\"text-fg-muted hover:bg-hover grid h-6 w-6 place-items-center rounded\"\n aria-label={t('demo.cancel')}\n >\n <Icon name=\"x\" size={14} />\n </button>\n </div>\n <p className={`mt-1 text-sm font-medium ${tone(summary)}`}>\n {validating ? t('demo.signaturesValidating') : t(verdictKey(verdict))}\n </p>\n <div className=\"mt-2 flex flex-col gap-1\">\n {row(t('demo.inspectSigner'), dto.signer.name ?? '—')}\n {dto.signer.reason ? row(t('demo.inspectReason'), dto.signer.reason) : null}\n {dto.signer.location ? row(t('demo.inspectLocation'), dto.signer.location) : null}\n {dto.signer.signedAt ? row(t('demo.inspectTime'), dateTime(dto.signer.signedAt)) : null}\n </div>\n <div className=\"border-border-subtle mt-2 flex flex-col gap-1 border-t pt-2\">\n {row(\n t('demo.inspectIntegrity'),\n verdict?.integrity ?? '—',\n tone(\n verdict?.integrity === 'valid'\n ? 'valid'\n : verdict?.integrity === 'invalid'\n ? 'invalid'\n : null,\n ),\n )}\n {row(t('demo.inspectCryptography'), verdict?.cryptography ?? '—')}\n {row(t('demo.inspectTrust'), verdict?.trust ?? '—')}\n {row(\n t('demo.inspectChanges'),\n modifications === 'unchanged'\n ? verdict?.modifications.undone\n ? t('demo.verdictRestored')\n : t('demo.verdictUnchanged')\n : modifications === 'permitted'\n ? t('demo.verdictPermitted')\n : modifications === 'forbidden'\n ? t('demo.verdictForbidden')\n : '—',\n modifications === 'forbidden' ? 'text-red-600' : 'text-fg',\n )}\n {verdict?.modifications.laterRevisions\n ? row(t('demo.inspectLaterRevisions'), String(verdict.modifications.laterRevisions))\n : null}\n </div>\n <div className=\"mt-3 flex justify-end gap-2\">\n <button\n type=\"button\"\n disabled={validating}\n onClick={revalidate}\n className=\"border-border text-fg hover:bg-hover rounded-md border px-2 py-1 text-xs disabled:opacity-60\"\n >\n {t('demo.signaturesValidate')}\n </button>\n <DownloadRevision field={field} />\n </div>\n </div>\n );\n\n if (widget?.page && box) {\n return (\n <Anchored anchor={{ page: widget.page, bounds: box }} placement=\"bottom\" gap={8}>\n {card}\n </Anchored>\n );\n }\n return <div className=\"absolute end-4 top-4 z-40\">{card}</div>;\n}\n\n/** The signed revision — the exact bytes the signature covers — as a file. */\nfunction DownloadRevision({ field }: { field: FormFieldRef }) {\n const t = useT();\n const signature = useSignature();\n const dto = signature.getSignature(field);\n const [busy, setBusy] = useState(false);\n if (!dto || dto.revisionIndex === null) return null;\n const revisionIndex = dto.revisionIndex;\n const run = async () => {\n setBusy(true);\n try {\n const bytes = await signature.readRevision({ revisionIndex });\n saveFile(bytes, `${dto.fieldName}-signed.pdf`, 'application/pdf');\n } catch (error) {\n console.error('[embedpdf] signed revision download failed:', error);\n } finally {\n setBusy(false);\n }\n };\n return (\n <button\n type=\"button\"\n disabled={busy}\n onClick={() => void run()}\n className=\"border-border text-fg hover:bg-hover rounded-md border px-2 py-1 text-xs disabled:opacity-60\"\n >\n {t('demo.inspectDownload')}\n </button>\n );\n}\n"],"mappings":"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"}
@@ -1,10 +1,11 @@
1
- import { n as Icon } from "./icons-BrK5iVL8.js";
1
+ import { n as Icon } from "./icons-7dB0Y1ct.js";
2
2
  import { useEffect, useState } from "react";
3
3
  import { useOptionalCapability } from "@embedpdf/react/runtime";
4
4
  import { FormToken } from "@embedpdf/react/form";
5
5
  import { useSignature, useSignatureState } from "@embedpdf/react/signature";
6
6
  import { useT } from "@embedpdf/react/i18n";
7
7
  import { useSurface } from "@embedpdf/react/shell";
8
+ import { saveFile } from "@embedpdf/web";
8
9
  import { jsx, jsxs } from "react/jsx-runtime";
9
10
  import { Anchored } from "@embedpdf/react/anchored";
10
11
  //#region src/ui/signature-inspector.tsx
@@ -138,14 +139,7 @@ function DownloadRevision({ field }) {
138
139
  const run = async () => {
139
140
  setBusy(true);
140
141
  try {
141
- const bytes = await signature.readRevision({ revisionIndex });
142
- const blob = new Blob([bytes], { type: "application/pdf" });
143
- const url = URL.createObjectURL(blob);
144
- const anchor = document.createElement("a");
145
- anchor.href = url;
146
- anchor.download = `${dto.fieldName}-signed.pdf`;
147
- anchor.click();
148
- URL.revokeObjectURL(url);
142
+ saveFile(await signature.readRevision({ revisionIndex }), `${dto.fieldName}-signed.pdf`, "application/pdf");
149
143
  } catch (error) {
150
144
  console.error("[embedpdf] signed revision download failed:", error);
151
145
  } finally {
@@ -163,4 +157,4 @@ function DownloadRevision({ field }) {
163
157
  //#endregion
164
158
  export { SignatureInspector };
165
159
 
166
- //# sourceMappingURL=signature-inspector-okFz8do6.js.map
160
+ //# sourceMappingURL=signature-inspector-CsGr9EBk.js.map