@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.
- package/dist/{annotation-fonts-BqoLZ4Mp.js → annotation-fonts-DWjVOTP0.js} +2 -2
- package/dist/{annotation-fonts-BqoLZ4Mp.js.map → annotation-fonts-DWjVOTP0.js.map} +1 -1
- package/dist/{annotation-fonts-Dt0IQXhz.cjs → annotation-fonts-dpfVkhnq.cjs} +2 -2
- package/dist/{annotation-fonts-Dt0IQXhz.cjs.map → annotation-fonts-dpfVkhnq.cjs.map} +1 -1
- package/dist/{annotation-style-Bzvvp8-N.cjs → annotation-style-B3XKAyZ-.cjs} +3 -3
- package/dist/{annotation-style-Bzvvp8-N.cjs.map → annotation-style-B3XKAyZ-.cjs.map} +1 -1
- package/dist/{annotation-style--rPIZsuP.js → annotation-style-CXF3X47Z.js} +3 -3
- package/dist/{annotation-style--rPIZsuP.js.map → annotation-style-CXF3X47Z.js.map} +1 -1
- package/dist/{ar-DnEj5vbP.cjs → ar-B132r9bo.cjs} +93 -1
- package/dist/ar-B132r9bo.cjs.map +1 -0
- package/dist/{ar-D9XKAmDx.js → ar-BLWFJoA_.js} +93 -1
- package/dist/ar-BLWFJoA_.js.map +1 -0
- package/dist/attachments-panel-Cxln8xos.cjs +217 -0
- package/dist/attachments-panel-Cxln8xos.cjs.map +1 -0
- package/dist/attachments-panel-H1ucRbwo.js +217 -0
- package/dist/attachments-panel-H1ucRbwo.js.map +1 -0
- package/dist/chrome.cjs +21 -0
- package/dist/chrome.cjs.map +1 -1
- package/dist/chrome.js +21 -0
- package/dist/chrome.js.map +1 -1
- package/dist/{commands-BmgxhJ8B.js → commands-BkgLP3Om.js} +418 -23
- package/dist/commands-BkgLP3Om.js.map +1 -0
- package/dist/{commands-CYt4SFUX.cjs → commands-DL1gjZt7.cjs} +433 -20
- package/dist/commands-DL1gjZt7.cjs.map +1 -0
- package/dist/commands.cjs +2 -1
- package/dist/commands.d.cts +3 -1
- package/dist/commands.d.ts +3 -1
- package/dist/commands.js +2 -2
- package/dist/{comments-panel-pEcQg4Vs.cjs → comments-panel-BsnWedTh.cjs} +2 -2
- package/dist/{comments-panel-pEcQg4Vs.cjs.map → comments-panel-BsnWedTh.cjs.map} +1 -1
- package/dist/{comments-panel-MUVgLCBI.js → comments-panel-D7Eadws9.js} +2 -2
- package/dist/{comments-panel-MUVgLCBI.js.map → comments-panel-D7Eadws9.js.map} +1 -1
- package/dist/{de-Dv6zvbnd.cjs → de-7_MvFWzN.cjs} +93 -1
- package/dist/de-7_MvFWzN.cjs.map +1 -0
- package/dist/{de-Bdr4aWzm.js → de-BUhwc9-x.js} +93 -1
- package/dist/de-BUhwc9-x.js.map +1 -0
- package/dist/{dialog-B5GBisKv.js → dialog-BIb373cg.js} +3 -3
- package/dist/dialog-BIb373cg.js.map +1 -0
- package/dist/{dialog-t8F13BL_.cjs → dialog-CngKTnAM.cjs} +3 -3
- package/dist/dialog-CngKTnAM.cjs.map +1 -0
- package/dist/{es-BP09DR9w.cjs → es-DgYj-1L3.cjs} +93 -1
- package/dist/es-DgYj-1L3.cjs.map +1 -0
- package/dist/{es-BSXz5tBv.js → es-DoXTqFoO.js} +93 -1
- package/dist/es-DoXTqFoO.js.map +1 -0
- package/dist/{form-design-panel-vWWU9uB-.js → form-design-panel-CgV_5YWC.js} +3 -3
- package/dist/{form-design-panel-vWWU9uB-.js.map → form-design-panel-CgV_5YWC.js.map} +1 -1
- package/dist/{form-design-panel-B5MXTZ96.cjs → form-design-panel-D0FdmWXW.cjs} +3 -3
- package/dist/{form-design-panel-B5MXTZ96.cjs.map → form-design-panel-D0FdmWXW.cjs.map} +1 -1
- package/dist/{fr-Ckn3K9v1.js → fr-B-8NDhBA.js} +93 -1
- package/dist/fr-B-8NDhBA.js.map +1 -0
- package/dist/{fr-BBQQn7tJ.cjs → fr-C856dmQJ.cjs} +93 -1
- package/dist/fr-C856dmQJ.cjs.map +1 -0
- package/dist/{he-DRsG7bH7.cjs → he-BFTeNc4t.cjs} +93 -1
- package/dist/he-BFTeNc4t.cjs.map +1 -0
- package/dist/{he-wdGU5PkU.js → he-Dlh0fNXM.js} +93 -1
- package/dist/he-Dlh0fNXM.js.map +1 -0
- package/dist/{icons-x0Tc05bs.cjs → icons-1UfLUZ_y.cjs} +20 -1
- package/dist/icons-1UfLUZ_y.cjs.map +1 -0
- package/dist/{icons-BrK5iVL8.js → icons-7dB0Y1ct.js} +20 -1
- package/dist/icons-7dB0Y1ct.js.map +1 -0
- package/dist/index.cjs +740 -171
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -2
- package/dist/index.d.ts +16 -2
- package/dist/index.js +727 -170
- package/dist/index.js.map +1 -1
- package/dist/{it-BuP4o0xQ.cjs → it-Bef-2Yxq.cjs} +93 -1
- package/dist/it-Bef-2Yxq.cjs.map +1 -0
- package/dist/{it-Us-VmvIu.js → it-BkirG7ol.js} +93 -1
- package/dist/it-BkirG7ol.js.map +1 -0
- package/dist/{ja-Cizj0-5O.js → ja-CGEjCE-s.js} +93 -1
- package/dist/ja-CGEjCE-s.js.map +1 -0
- package/dist/{ja-DovEd6ts.cjs → ja-DNw2zqSD.cjs} +93 -1
- package/dist/ja-DNw2zqSD.cjs.map +1 -0
- package/dist/{ko-DlrXliE1.js → ko-CTkl5vMh.js} +93 -1
- package/dist/ko-CTkl5vMh.js.map +1 -0
- package/dist/{ko-B-qJEol2.cjs → ko-YHzeBqP6.cjs} +93 -1
- package/dist/ko-YHzeBqP6.cjs.map +1 -0
- package/dist/{link-editor-CSjqPnj1.cjs → link-editor-CAS7MZM2.cjs} +2 -2
- package/dist/{link-editor-CSjqPnj1.cjs.map → link-editor-CAS7MZM2.cjs.map} +1 -1
- package/dist/{link-editor-SxM1nnJU.js → link-editor-CkwtqRvb.js} +2 -2
- package/dist/{link-editor-SxM1nnJU.js.map → link-editor-CkwtqRvb.js.map} +1 -1
- package/dist/{measurement-Bs61XWbJ.js → measurement-CQdbnIrn.js} +2 -2
- package/dist/{measurement-Bs61XWbJ.js.map → measurement-CQdbnIrn.js.map} +1 -1
- package/dist/{measurement-CcCsHPOX.cjs → measurement-Cxv1Ahxx.cjs} +2 -2
- package/dist/{measurement-CcCsHPOX.cjs.map → measurement-Cxv1Ahxx.cjs.map} +1 -1
- package/dist/{nl-D3KZDDiJ.cjs → nl-BjkbaLq3.cjs} +93 -1
- package/dist/nl-BjkbaLq3.cjs.map +1 -0
- package/dist/{nl-0aF7M_0h.js → nl-Dya4sR8_.js} +93 -1
- package/dist/nl-Dya4sR8_.js.map +1 -0
- package/dist/outline-panel-BpGtBdj0.cjs +707 -0
- package/dist/outline-panel-BpGtBdj0.cjs.map +1 -0
- package/dist/outline-panel-DO5_REYw.js +707 -0
- package/dist/outline-panel-DO5_REYw.js.map +1 -0
- package/dist/outline-properties-BCa2cc0t.js +330 -0
- package/dist/outline-properties-BCa2cc0t.js.map +1 -0
- package/dist/outline-properties-DT3FIxCw.cjs +330 -0
- package/dist/outline-properties-DT3FIxCw.cjs.map +1 -0
- package/dist/{pl-DSFhAUqx.cjs → pl-CKKdVnpZ.cjs} +93 -1
- package/dist/pl-CKKdVnpZ.cjs.map +1 -0
- package/dist/{pl-BW--AaCI.js → pl-DA9b5NVg.js} +93 -1
- package/dist/pl-DA9b5NVg.js.map +1 -0
- package/dist/print-dialog-BqIrHCBh.cjs +276 -0
- package/dist/print-dialog-BqIrHCBh.cjs.map +1 -0
- package/dist/print-dialog-DvcI2m2_.js +276 -0
- package/dist/print-dialog-DvcI2m2_.js.map +1 -0
- package/dist/{pt-C5S-8MC5.cjs → pt-DLcszlzi.cjs} +93 -1
- package/dist/pt-DLcszlzi.cjs.map +1 -0
- package/dist/{pt-DIhpU5pT.js → pt-Nm8yyjbX.js} +93 -1
- package/dist/pt-Nm8yyjbX.js.map +1 -0
- package/dist/{redact-confirm-CX_SY2ZN.cjs → redact-confirm-C6EiK8yN.cjs} +2 -2
- package/dist/{redact-confirm-CX_SY2ZN.cjs.map → redact-confirm-C6EiK8yN.cjs.map} +1 -1
- package/dist/{redact-confirm-B6j36TPw.js → redact-confirm-CKuv9iN_.js} +2 -2
- package/dist/{redact-confirm-B6j36TPw.js.map → redact-confirm-CKuv9iN_.js.map} +1 -1
- package/dist/{redaction-panel-IJDshSJI.js → redaction-panel-CgNZ-pQk.js} +2 -2
- package/dist/{redaction-panel-IJDshSJI.js.map → redaction-panel-CgNZ-pQk.js.map} +1 -1
- package/dist/{redaction-panel-BmoIl-Ll.cjs → redaction-panel-ZhX0dlMi.cjs} +2 -2
- package/dist/{redaction-panel-BmoIl-Ll.cjs.map → redaction-panel-ZhX0dlMi.cjs.map} +1 -1
- package/dist/{search-panel-HfIhJm1M.js → search-panel-BE4xzWlm.js} +4 -4
- package/dist/{search-panel-HfIhJm1M.js.map → search-panel-BE4xzWlm.js.map} +1 -1
- package/dist/{search-panel-s9IoiOtv.cjs → search-panel-wcbTNG1z.cjs} +4 -4
- package/dist/{search-panel-s9IoiOtv.cjs.map → search-panel-wcbTNG1z.cjs.map} +1 -1
- package/dist/{sheet-BDsa_Sup.cjs → sheet-DTBFiwde.cjs} +30 -11
- package/dist/sheet-DTBFiwde.cjs.map +1 -0
- package/dist/{sheet-R6XDgyEf.js → sheet-DyjKfAq0.js} +25 -12
- package/dist/sheet-DyjKfAq0.js.map +1 -0
- package/dist/{sign-dialog-CgnR7akj.cjs → sign-dialog-D5WGx-Yn.cjs} +2 -2
- package/dist/{sign-dialog-CgnR7akj.cjs.map → sign-dialog-D5WGx-Yn.cjs.map} +1 -1
- package/dist/{sign-dialog-GDqCoQhA.js → sign-dialog-DerNe8qT.js} +2 -2
- package/dist/{sign-dialog-GDqCoQhA.js.map → sign-dialog-DerNe8qT.js.map} +1 -1
- package/dist/{signature-inspector-DyO313Qf.cjs → signature-inspector-Bz6TAfZM.cjs} +4 -10
- package/dist/signature-inspector-Bz6TAfZM.cjs.map +1 -0
- package/dist/{signature-inspector-okFz8do6.js → signature-inspector-CsGr9EBk.js} +4 -10
- package/dist/signature-inspector-CsGr9EBk.js.map +1 -0
- package/dist/{signature-maker-BoYa8vgA.cjs → signature-maker-D_YfBXLn.cjs} +3 -3
- package/dist/{signature-maker-BoYa8vgA.cjs.map → signature-maker-D_YfBXLn.cjs.map} +1 -1
- package/dist/{signature-maker-BhCmYyHD.js → signature-maker-Dnth4rV4.js} +3 -3
- package/dist/{signature-maker-BhCmYyHD.js.map → signature-maker-Dnth4rV4.js.map} +1 -1
- package/dist/{signatures-panel-B56PC6d9.cjs → signatures-panel-Ci_LaI1C.cjs} +4 -10
- package/dist/signatures-panel-Ci_LaI1C.cjs.map +1 -0
- package/dist/{signatures-panel-CgnFfhFm.js → signatures-panel-DZ9ywrjz.js} +4 -10
- package/dist/signatures-panel-DZ9ywrjz.js.map +1 -0
- package/dist/{stamps-panel-CDO0FPyK.js → stamps-panel-BrczStaz.js} +4 -10
- package/dist/stamps-panel-BrczStaz.js.map +1 -0
- package/dist/{stamps-panel-B9NYGayn.cjs → stamps-panel-Cv9nJBwx.cjs} +4 -10
- package/dist/stamps-panel-Cv9nJBwx.cjs.map +1 -0
- package/dist/theme-Be0OZokZ.js +97 -0
- package/dist/theme-Be0OZokZ.js.map +1 -0
- package/dist/theme-FR26JI3r.cjs +126 -0
- package/dist/theme-FR26JI3r.cjs.map +1 -0
- package/dist/{toolbar-z9od3tKl.js → toolbar-DF361xWY.js} +7 -6
- package/dist/toolbar-DF361xWY.js.map +1 -0
- package/dist/{toolbar-CJu2wKTU.cjs → toolbar-ddvpsvTb.cjs} +7 -6
- package/dist/toolbar-ddvpsvTb.cjs.map +1 -0
- package/dist/{zh-cn-CxkUUBct.js → zh-cn-BMPPkLpL.js} +93 -1
- package/dist/zh-cn-BMPPkLpL.js.map +1 -0
- package/dist/{zh-cn-3tmMeHqv.cjs → zh-cn-CZOO3p4H.cjs} +93 -1
- package/dist/zh-cn-CZOO3p4H.cjs.map +1 -0
- package/package.json +4 -4
- package/src/styles.css +8 -0
- package/dist/ar-D9XKAmDx.js.map +0 -1
- package/dist/ar-DnEj5vbP.cjs.map +0 -1
- package/dist/commands-BmgxhJ8B.js.map +0 -1
- package/dist/commands-CYt4SFUX.cjs.map +0 -1
- package/dist/de-Bdr4aWzm.js.map +0 -1
- package/dist/de-Dv6zvbnd.cjs.map +0 -1
- package/dist/dialog-B5GBisKv.js.map +0 -1
- package/dist/dialog-t8F13BL_.cjs.map +0 -1
- package/dist/es-BP09DR9w.cjs.map +0 -1
- package/dist/es-BSXz5tBv.js.map +0 -1
- package/dist/fr-BBQQn7tJ.cjs.map +0 -1
- package/dist/fr-Ckn3K9v1.js.map +0 -1
- package/dist/he-DRsG7bH7.cjs.map +0 -1
- package/dist/he-wdGU5PkU.js.map +0 -1
- package/dist/icons-BrK5iVL8.js.map +0 -1
- package/dist/icons-x0Tc05bs.cjs.map +0 -1
- package/dist/it-BuP4o0xQ.cjs.map +0 -1
- package/dist/it-Us-VmvIu.js.map +0 -1
- package/dist/ja-Cizj0-5O.js.map +0 -1
- package/dist/ja-DovEd6ts.cjs.map +0 -1
- package/dist/ko-B-qJEol2.cjs.map +0 -1
- package/dist/ko-DlrXliE1.js.map +0 -1
- package/dist/nl-0aF7M_0h.js.map +0 -1
- package/dist/nl-D3KZDDiJ.cjs.map +0 -1
- package/dist/pl-BW--AaCI.js.map +0 -1
- package/dist/pl-DSFhAUqx.cjs.map +0 -1
- package/dist/pt-C5S-8MC5.cjs.map +0 -1
- package/dist/pt-DIhpU5pT.js.map +0 -1
- package/dist/sheet-BDsa_Sup.cjs.map +0 -1
- package/dist/sheet-R6XDgyEf.js.map +0 -1
- package/dist/signature-inspector-DyO313Qf.cjs.map +0 -1
- package/dist/signature-inspector-okFz8do6.js.map +0 -1
- package/dist/signatures-panel-B56PC6d9.cjs.map +0 -1
- package/dist/signatures-panel-CgnFfhFm.js.map +0 -1
- package/dist/stamps-panel-B9NYGayn.cjs.map +0 -1
- package/dist/stamps-panel-CDO0FPyK.js.map +0 -1
- package/dist/toolbar-CJu2wKTU.cjs.map +0 -1
- package/dist/toolbar-z9od3tKl.js.map +0 -1
- package/dist/zh-cn-3tmMeHqv.cjs.map +0 -1
- 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
|
|
191
|
-
* Its handle drags it taller or shorter, or down to close it,
|
|
192
|
-
* moves it a whole step (`sheet-rest.ts`).
|
|
193
|
-
*
|
|
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
|
-
/**
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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: `
|
|
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-
|
|
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
|
|
191
|
-
* Its handle drags it taller or shorter, or down to close it,
|
|
192
|
-
* moves it a whole step (`sheet-rest.ts`).
|
|
193
|
-
*
|
|
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
|
-
/**
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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: `
|
|
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,
|
|
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-
|
|
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-
|
|
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-
|
|
155
|
+
//# sourceMappingURL=sign-dialog-D5WGx-Yn.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sign-dialog-
|
|
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":";;;;;;;;;;;;;;;;AAeA,SAAgB,aAAa;CAC3B,MAAM,KAAA,GAAA,qBAAA,KAAA,CAAS;CACf,MAAM,WAAA,GAAA,sBAAA,WAAA,CAAqB,gBAAgB;CAC3C,MAAM,aAAA,GAAA,0BAAA,aAAA,CAAyB;CAC/B,MAAM,CAAC,QAAQ,cAAA,GAAA,MAAA,SAAA,CAAsB,EAAE;CACvC,MAAM,CAAC,UAAU,gBAAA,GAAA,MAAA,SAAA,CAAwB,EAAE;CAI3C,MAAM,CAAC,UAAU,gBAAA,GAAA,MAAA,SAAA,CACf,UACF;CACA,MAAM,CAAC,MAAM,YAAA,GAAA,MAAA,SAAA,CAA4C,IAAI;CAC7D,MAAM,CAAC,OAAO,aAAA,GAAA,MAAA,SAAA,CAAoC,IAAI;CACtD,MAAM,WAAA,GAAA,MAAA,MAAA,CAAgB;CAEtB,MAAM,QAAQ,QAAQ,OAAO;CAC7B,MAAM,OAAO,QAAQ,OAAO;CAE5B,MAAM,WAAA,GAAA,sBAAA,wBAAA,CADU,QAAQ,aAAa,OAAO,KAAK,UAAU,IACZ;CAC/C,IAAI,CAAC,QAAQ,UAAU,CAAC,SAAS,CAAC,MAAM,OAAO;CAE/C,MAAM,SAAS;EACb,GAAI,OAAO,KAAK,IAAI,EAAE,QAAQ,OAAO,KAAK,EAAE,IAAI,CAAC;EACjD,GAAI,SAAS,KAAK,IAAI,EAAE,UAAU,SAAS,KAAK,EAAE,IAAI,CAAC;CACzD;CACA,MAAM,MAAM,OAAO,SAA0B;EAC3C,QAAQ,IAAI;EACZ,SAAS,IAAI;EACb,IAAI;GACF,IAAI,SAAS,QACX,MAAM,UAAU,KAAK;IACnB;IACA;IACA;IACA,GAAI,aAAa,cAAc,UAAU,WAAW,IAChD,EAAE,SAAS,EAAE,YAAY,OAAO,SAAS,MAAM,EAAE,CAAC,EAAe,EAAE,IACnE,CAAC;GACP,CAAC;QAED,MAAM,UAAU,UAAU,OAAO,IAAI;GAEvC,QAAQ,MAAM;EAChB,SAAS,OAAO;GACd,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;EACjE,UAAU;GACR,QAAQ,IAAI;EACd;CACF;CAEA,MAAM,QAAQ;CACd,OACE,iBAAA,GAAA,kBAAA,KAAA,CAACA,eAAAA,QAAD;EACE,YAAY;EACZ,eAAe;GACb,IAAI,QAAQ,MAAM,QAAQ,MAAM;EAClC;EACA,WAAU;EALZ,UAAA;GAOE,iBAAA,GAAA,kBAAA,IAAA,CAAC,MAAD;IAAI,IAAI;IAAS,WAAU;IACxB,UAAA,EAAE,gBAAgB;GACjB,CAAA;GACJ,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;IAAK,WAAU;IACZ,UAAA,UACC,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;KAAK,KAAK;KAAS,KAAI;KAAG,WAAU;IAAwC,CAAA,IAE5E,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;KAAM,WAAU;KAAyB,UAAA,EAAE,qBAAqB;IAAQ,CAAA;GAEvE,CAAA;GACL,iBAAA,GAAA,kBAAA,KAAA,CAAC,SAAD;IAAO,WAAU;IAAjB,UAAA,CACG,EAAE,iBAAiB,GACpB,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;KACE,OAAO;KACP,WAAW,UAAU,UAAU,MAAM,OAAO,KAAK;KACjD,WAAW;IACZ,CAAA,CACI;;GACP,iBAAA,GAAA,kBAAA,KAAA,CAAC,SAAD;IAAO,WAAU;IAAjB,UAAA,CACG,EAAE,mBAAmB,GACtB,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;KACE,OAAO;KACP,WAAW,UAAU,YAAY,MAAM,OAAO,KAAK;KACnD,WAAW;IACZ,CAAA,CACI;;GACN,UAAU,WAAW,IACpB,iBAAA,GAAA,kBAAA,KAAA,CAAC,SAAD;IAAO,WAAU;IAAjB,UAAA,CACG,EAAE,eAAe,GAClB,iBAAA,GAAA,kBAAA,KAAA,CAAC,UAAD;KACE,OAAO;KACP,WAAW,UAAU,YAAY,MAAM,OAAO,KAAwB;KACtE,WAAW;KAHb,UAAA;MAKE,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;OAAQ,OAAM;OAAY,UAAA,EAAE,uBAAuB;MAAU,CAAA;MAC7D,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;OAAQ,OAAM;OAAa,UAAA,EAAE,8BAA8B;MAAU,CAAA;MACrE,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;OAAQ,OAAM;OAAa,UAAA,EAAE,0BAA0B;MAAU,CAAA;MACjE,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;OAAQ,OAAM;OAAa,UAAA,EAAE,4BAA4B;MAAU,CAAA;KAC7D;IACH,CAAA,CAAA;GACL,CAAA,IAAA;GACH,QAAQ,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;IAAG,WAAU;IAA6B,UAAA;GAAS,CAAA,IAAI;GAChE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;IAAK,WAAU;IAAf,UAAA;KACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;MACE,MAAK;MACL,UAAU,QAAQ;MAClB,SAAS,QAAQ;MACjB,WAAU;MAET,UAAA,EAAE,aAAa;KACV,CAAA;KACP,UAAU,QAAQ,KAAK,IACtB,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;MACE,MAAK;MACL,UAAU,QAAQ;MAClB,eAAe,KAAK,IAAI,MAAM;MAC9B,WAAU;MAET,UAAA,EAAE,eAAe;KACZ,CAAA,IACN;KACH,UAAU,QAAQ,KAAK,IACtB,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;MACE,MAAK;MACL,UAAU,QAAQ;MAClB,eAAe,KAAK,IAAI,MAAM;MAC9B,WAAU;MAET,UAAA,SAAS,SAAS,EAAE,kBAAkB,IAAI,EAAE,iBAAiB;KACxD,CAAA,IACN;IACD;;EACC;;AAEZ"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as Dialog } from "./dialog-
|
|
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-
|
|
155
|
+
//# sourceMappingURL=sign-dialog-DerNe8qT.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sign-dialog-
|
|
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":";;;;;;;;;;;;;;;;AAeA,SAAgB,aAAa;CAC3B,MAAM,IAAI,KAAK;CACf,MAAM,UAAU,WAAW,gBAAgB;CAC3C,MAAM,YAAY,aAAa;CAC/B,MAAM,CAAC,QAAQ,aAAa,SAAS,EAAE;CACvC,MAAM,CAAC,UAAU,eAAe,SAAS,EAAE;CAI3C,MAAM,CAAC,UAAU,eAAe,SAC9B,UACF;CACA,MAAM,CAAC,MAAM,WAAW,SAAiC,IAAI;CAC7D,MAAM,CAAC,OAAO,YAAY,SAAwB,IAAI;CACtD,MAAM,UAAU,MAAM;CAEtB,MAAM,QAAQ,QAAQ,OAAO;CAC7B,MAAM,OAAO,QAAQ,OAAO;CAE5B,MAAM,UAAU,wBADA,QAAQ,aAAa,OAAO,KAAK,UAAU,IACZ;CAC/C,IAAI,CAAC,QAAQ,UAAU,CAAC,SAAS,CAAC,MAAM,OAAO;CAE/C,MAAM,SAAS;EACb,GAAI,OAAO,KAAK,IAAI,EAAE,QAAQ,OAAO,KAAK,EAAE,IAAI,CAAC;EACjD,GAAI,SAAS,KAAK,IAAI,EAAE,UAAU,SAAS,KAAK,EAAE,IAAI,CAAC;CACzD;CACA,MAAM,MAAM,OAAO,SAA0B;EAC3C,QAAQ,IAAI;EACZ,SAAS,IAAI;EACb,IAAI;GACF,IAAI,SAAS,QACX,MAAM,UAAU,KAAK;IACnB;IACA;IACA;IACA,GAAI,aAAa,cAAc,UAAU,WAAW,IAChD,EAAE,SAAS,EAAE,YAAY,OAAO,SAAS,MAAM,EAAE,CAAC,EAAe,EAAE,IACnE,CAAC;GACP,CAAC;QAED,MAAM,UAAU,UAAU,OAAO,IAAI;GAEvC,QAAQ,MAAM;EAChB,SAAS,OAAO;GACd,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;EACjE,UAAU;GACR,QAAQ,IAAI;EACd;CACF;CAEA,MAAM,QAAQ;CACd,OACE,qBAAC,QAAD;EACE,YAAY;EACZ,eAAe;GACb,IAAI,QAAQ,MAAM,QAAQ,MAAM;EAClC;EACA,WAAU;EALZ,UAAA;GAOE,oBAAC,MAAD;IAAI,IAAI;IAAS,WAAU;IACxB,UAAA,EAAE,gBAAgB;GACjB,CAAA;GACJ,oBAAC,OAAD;IAAK,WAAU;IACZ,UAAA,UACC,oBAAC,OAAD;KAAK,KAAK;KAAS,KAAI;KAAG,WAAU;IAAwC,CAAA,IAE5E,oBAAC,QAAD;KAAM,WAAU;KAAyB,UAAA,EAAE,qBAAqB;IAAQ,CAAA;GAEvE,CAAA;GACL,qBAAC,SAAD;IAAO,WAAU;IAAjB,UAAA,CACG,EAAE,iBAAiB,GACpB,oBAAC,SAAD;KACE,OAAO;KACP,WAAW,UAAU,UAAU,MAAM,OAAO,KAAK;KACjD,WAAW;IACZ,CAAA,CACI;;GACP,qBAAC,SAAD;IAAO,WAAU;IAAjB,UAAA,CACG,EAAE,mBAAmB,GACtB,oBAAC,SAAD;KACE,OAAO;KACP,WAAW,UAAU,YAAY,MAAM,OAAO,KAAK;KACnD,WAAW;IACZ,CAAA,CACI;;GACN,UAAU,WAAW,IACpB,qBAAC,SAAD;IAAO,WAAU;IAAjB,UAAA,CACG,EAAE,eAAe,GAClB,qBAAC,UAAD;KACE,OAAO;KACP,WAAW,UAAU,YAAY,MAAM,OAAO,KAAwB;KACtE,WAAW;KAHb,UAAA;MAKE,oBAAC,UAAD;OAAQ,OAAM;OAAY,UAAA,EAAE,uBAAuB;MAAU,CAAA;MAC7D,oBAAC,UAAD;OAAQ,OAAM;OAAa,UAAA,EAAE,8BAA8B;MAAU,CAAA;MACrE,oBAAC,UAAD;OAAQ,OAAM;OAAa,UAAA,EAAE,0BAA0B;MAAU,CAAA;MACjE,oBAAC,UAAD;OAAQ,OAAM;OAAa,UAAA,EAAE,4BAA4B;MAAU,CAAA;KAC7D;IACH,CAAA,CAAA;GACL,CAAA,IAAA;GACH,QAAQ,oBAAC,KAAD;IAAG,WAAU;IAA6B,UAAA;GAAS,CAAA,IAAI;GAChE,qBAAC,OAAD;IAAK,WAAU;IAAf,UAAA;KACE,oBAAC,UAAD;MACE,MAAK;MACL,UAAU,QAAQ;MAClB,SAAS,QAAQ;MACjB,WAAU;MAET,UAAA,EAAE,aAAa;KACV,CAAA;KACP,UAAU,QAAQ,KAAK,IACtB,oBAAC,UAAD;MACE,MAAK;MACL,UAAU,QAAQ;MAClB,eAAe,KAAK,IAAI,MAAM;MAC9B,WAAU;MAET,UAAA,EAAE,eAAe;KACZ,CAAA,IACN;KACH,UAAU,QAAQ,KAAK,IACtB,oBAAC,UAAD;MACE,MAAK;MACL,UAAU,QAAQ;MAClB,eAAe,KAAK,IAAI,MAAM;MAC9B,WAAU;MAET,UAAA,SAAS,SAAS,EAAE,kBAAkB,IAAI,EAAE,iBAAiB;KACxD,CAAA,IACN;IACD;;EACC;;AAEZ"}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
const require_icons = require("./icons-
|
|
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
|
-
|
|
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-
|
|
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-
|
|
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
|
-
|
|
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-
|
|
160
|
+
//# sourceMappingURL=signature-inspector-CsGr9EBk.js.map
|