@drvillo/react-browser-e-signing 0.1.0 → 0.1.2

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/index.js CHANGED
@@ -5,7 +5,49 @@ import SignaturePadLibrary from 'signature_pad';
5
5
  import fontkit from '@pdf-lib/fontkit';
6
6
  import { PDFDocument, StandardFonts } from 'pdf-lib';
7
7
 
8
- // src/components/pdf-viewer.tsx
8
+ // src/lib/signature-fonts.ts
9
+ var loadedFonts = /* @__PURE__ */ new Set();
10
+ var SIGNATURE_FONTS = [
11
+ "Caveat",
12
+ "Homemade Apple",
13
+ "Reenie Beanie",
14
+ "Mr Dafoe",
15
+ "Pacifico",
16
+ "Qwitcher Grypen"
17
+ ];
18
+ function buildGoogleFontsCssUrl(family) {
19
+ const encoded = family.trim().replace(/\s+/g, "+");
20
+ return `https://fonts.googleapis.com/css2?family=${encoded}&display=swap`;
21
+ }
22
+ async function loadCssFromGoogleFonts(url) {
23
+ const response = await fetch(url);
24
+ if (!response.ok) throw new Error(`Unable to load font css from ${url}`);
25
+ return response.text();
26
+ }
27
+ function extractFontSource(cssText) {
28
+ const sourceMatch = cssText.match(/src:\s*url\(([^)]+)\)\s*format\(['"]?([^'")]+)['"]?\)/i);
29
+ if (!sourceMatch) return null;
30
+ return sourceMatch[1].replace(/['"]/g, "");
31
+ }
32
+ async function loadSignatureFont(fontFamily) {
33
+ if (loadedFonts.has(fontFamily)) return;
34
+ if (typeof document === "undefined") return;
35
+ if (typeof FontFace === "undefined") return;
36
+ const cssUrl = buildGoogleFontsCssUrl(fontFamily);
37
+ const cssText = await loadCssFromGoogleFonts(cssUrl);
38
+ const fontSource = extractFontSource(cssText);
39
+ if (!fontSource) throw new Error(`Unable to extract font source for ${fontFamily}`);
40
+ const fontFace = new FontFace(fontFamily, `url(${fontSource})`);
41
+ await fontFace.load();
42
+ const fontSet = document.fonts;
43
+ fontSet.add(fontFace);
44
+ loadedFonts.add(fontFamily);
45
+ }
46
+
47
+ // src/lib/cn.ts
48
+ function cn(...values) {
49
+ return values.filter(Boolean).join(" ");
50
+ }
9
51
  if (typeof window !== "undefined") {
10
52
  pdfjs.GlobalWorkerOptions.workerSrc = `https://unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`;
11
53
  }
@@ -19,27 +61,28 @@ function PdfViewer({
19
61
  onScaleChange,
20
62
  onDocumentLoadSuccess,
21
63
  onPageDimensions,
22
- renderOverlay
64
+ renderOverlay,
65
+ className
23
66
  }) {
24
67
  if (!pdfData)
25
- return /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-dashed border-slate-300 bg-slate-50 p-8 text-center text-sm text-slate-500", children: "Upload a PDF to begin" });
26
- return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
27
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between rounded-lg border border-slate-300 bg-white p-3", children: [
28
- /* @__PURE__ */ jsxs("div", { className: "text-sm text-slate-700", children: [
68
+ return /* @__PURE__ */ jsx("div", { "data-slot": "pdf-viewer-empty", className: cn(className), children: "Upload a PDF to begin" });
69
+ return /* @__PURE__ */ jsxs("div", { "data-slot": "pdf-viewer", className: cn(className), children: [
70
+ /* @__PURE__ */ jsxs("div", { "data-slot": "pdf-viewer-toolbar", children: [
71
+ /* @__PURE__ */ jsxs("div", { "data-slot": "pdf-viewer-page-count", children: [
29
72
  "Pages: ",
30
73
  numPages || "\u2014"
31
74
  ] }),
32
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
75
+ /* @__PURE__ */ jsxs("div", { "data-slot": "pdf-viewer-zoom", children: [
33
76
  /* @__PURE__ */ jsx(
34
77
  "button",
35
78
  {
36
79
  type: "button",
37
- className: "rounded border border-slate-300 px-2 py-1 text-sm hover:bg-slate-50",
80
+ "data-slot": "pdf-viewer-zoom-button",
38
81
  onClick: () => onScaleChange(Math.max(MIN_SCALE, Number((scale - SCALE_STEP).toFixed(2)))),
39
82
  children: "-"
40
83
  }
41
84
  ),
42
- /* @__PURE__ */ jsxs("span", { className: "w-16 text-center text-sm text-slate-700", children: [
85
+ /* @__PURE__ */ jsxs("span", { "data-slot": "pdf-viewer-zoom-value", children: [
43
86
  Math.round(scale * 100),
44
87
  "%"
45
88
  ] }),
@@ -47,7 +90,7 @@ function PdfViewer({
47
90
  "button",
48
91
  {
49
92
  type: "button",
50
- className: "rounded border border-slate-300 px-2 py-1 text-sm hover:bg-slate-50",
93
+ "data-slot": "pdf-viewer-zoom-button",
51
94
  onClick: () => onScaleChange(Math.min(MAX_SCALE, Number((scale + SCALE_STEP).toFixed(2)))),
52
95
  children: "+"
53
96
  }
@@ -59,25 +102,33 @@ function PdfViewer({
59
102
  {
60
103
  file: pdfData,
61
104
  onLoadSuccess: (loadedPdf) => onDocumentLoadSuccess(loadedPdf.numPages),
62
- loading: /* @__PURE__ */ jsx("div", { className: "text-sm text-slate-500", children: "Loading PDF..." }),
63
- error: /* @__PURE__ */ jsx("div", { className: "text-sm text-red-600", children: "Unable to render this PDF." }),
64
- children: /* @__PURE__ */ jsx("div", { className: "space-y-6", children: Array.from({ length: numPages }, (_, pageIndex) => /* @__PURE__ */ jsxs("div", { className: "relative mx-auto w-fit rounded bg-white p-2 shadow", children: [
65
- /* @__PURE__ */ jsx(
66
- Page,
67
- {
68
- pageNumber: pageIndex + 1,
69
- scale,
70
- renderTextLayer: false,
71
- renderAnnotationLayer: false,
72
- onLoadSuccess: (page) => onPageDimensions({
73
- pageIndex,
74
- widthPt: page.view[2],
75
- heightPt: page.view[3]
76
- })
77
- }
78
- ),
79
- renderOverlay?.(pageIndex)
80
- ] }, `pdf-page-${pageIndex}`)) })
105
+ loading: /* @__PURE__ */ jsx("div", { "data-slot": "pdf-viewer-loading", children: "Loading PDF..." }),
106
+ error: /* @__PURE__ */ jsx("div", { "data-slot": "pdf-viewer-error", children: "Unable to render this PDF." }),
107
+ children: /* @__PURE__ */ jsx("div", { "data-slot": "pdf-viewer-pages", children: Array.from({ length: numPages }, (_, pageIndex) => /* @__PURE__ */ jsxs(
108
+ "div",
109
+ {
110
+ "data-slot": "pdf-viewer-page",
111
+ style: { position: "relative", margin: "0 auto", width: "fit-content" },
112
+ children: [
113
+ /* @__PURE__ */ jsx(
114
+ Page,
115
+ {
116
+ pageNumber: pageIndex + 1,
117
+ scale,
118
+ renderTextLayer: false,
119
+ renderAnnotationLayer: false,
120
+ onLoadSuccess: (page) => onPageDimensions({
121
+ pageIndex,
122
+ widthPt: page.view[2],
123
+ heightPt: page.view[3]
124
+ })
125
+ }
126
+ ),
127
+ renderOverlay?.(pageIndex)
128
+ ]
129
+ },
130
+ `pdf-page-${pageIndex}`
131
+ )) })
81
132
  }
82
133
  )
83
134
  ] });
@@ -95,7 +146,7 @@ function getFieldPreviewText(field, preview) {
95
146
  if (field.type === "date") return preview.dateText;
96
147
  return "";
97
148
  }
98
- function SignatureField({ field, onUpdateField, onRemoveField, preview }) {
149
+ function SignatureField({ field, onUpdateField, onRemoveField, preview, className }) {
99
150
  const rootRef = useRef(null);
100
151
  const dragStateRef = useRef(null);
101
152
  const resizeStateRef = useRef(null);
@@ -170,49 +221,74 @@ function SignatureField({ field, onUpdateField, onRemoveField, preview }) {
170
221
  "div",
171
222
  {
172
223
  ref: rootRef,
173
- className: "absolute rounded border-2 border-blue-500 bg-blue-50/80 shadow-sm select-none",
224
+ "data-slot": "signature-field",
225
+ "data-field-type": field.type,
226
+ className: cn(className),
174
227
  style: {
228
+ position: "absolute",
175
229
  left: `${field.xPercent}%`,
176
230
  top: `${field.yPercent}%`,
177
231
  width: `${field.widthPercent}%`,
178
- height: `${field.heightPercent}%`
232
+ height: `${field.heightPercent}%`,
233
+ userSelect: "none"
179
234
  },
180
235
  onPointerDown: handleDragPointerDown,
181
236
  onPointerMove: handleDragPointerMove,
182
237
  onPointerUp: handleDragPointerUp,
183
238
  children: [
184
- /* @__PURE__ */ jsxs("div", { className: "flex h-full w-full items-start justify-between gap-2 p-1.5 text-[11px] text-slate-800", children: [
185
- /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 overflow-hidden", children: [
186
- /* @__PURE__ */ jsx("div", { className: "truncate font-semibold capitalize", children: field.type }),
187
- field.type === "signature" && preview.signatureDataUrl ? /* @__PURE__ */ jsx(
188
- "img",
189
- {
190
- src: preview.signatureDataUrl,
191
- alt: "signature preview",
192
- className: "mt-1 h-[calc(100%-18px)] max-h-full w-full object-contain",
193
- draggable: false
194
- }
195
- ) : /* @__PURE__ */ jsx("div", { className: "truncate text-slate-600", children: previewText || "\u2014" })
196
- ] }),
197
- /* @__PURE__ */ jsx(
198
- "button",
199
- {
200
- type: "button",
201
- className: "rounded bg-white/80 px-1 text-xs text-red-600 hover:bg-white",
202
- onPointerDown: (event) => event.stopPropagation(),
203
- onClick: (event) => {
204
- event.stopPropagation();
205
- onRemoveField(field.id);
206
- },
207
- "aria-label": "Remove field",
208
- children: "\xD7"
209
- }
210
- )
211
- ] }),
239
+ /* @__PURE__ */ jsxs(
240
+ "div",
241
+ {
242
+ "data-slot": "signature-field-content",
243
+ style: {
244
+ display: "flex",
245
+ height: "100%",
246
+ width: "100%",
247
+ alignItems: "stretch",
248
+ justifyContent: "space-between"
249
+ },
250
+ children: [
251
+ /* @__PURE__ */ jsxs("div", { "data-slot": "signature-field-preview", children: [
252
+ /* @__PURE__ */ jsx("div", { "data-slot": "signature-field-label", children: field.type }),
253
+ field.type === "signature" && preview.signatureDataUrl ? /* @__PURE__ */ jsx(
254
+ "img",
255
+ {
256
+ "data-slot": "signature-field-preview-image",
257
+ src: preview.signatureDataUrl,
258
+ alt: "signature preview",
259
+ draggable: false
260
+ }
261
+ ) : /* @__PURE__ */ jsx("div", { "data-slot": "signature-field-preview-text", children: previewText || "\u2014" })
262
+ ] }),
263
+ /* @__PURE__ */ jsx(
264
+ "button",
265
+ {
266
+ type: "button",
267
+ "data-slot": "signature-field-remove",
268
+ onPointerDown: (event) => event.stopPropagation(),
269
+ onClick: (event) => {
270
+ event.stopPropagation();
271
+ onRemoveField(field.id);
272
+ },
273
+ "aria-label": "Remove field",
274
+ children: "\xD7"
275
+ }
276
+ )
277
+ ]
278
+ }
279
+ ),
212
280
  /* @__PURE__ */ jsx(
213
281
  "div",
214
282
  {
215
- className: "absolute -bottom-1.5 -right-1.5 h-3 w-3 cursor-nwse-resize rounded-full bg-blue-600",
283
+ "data-slot": "signature-field-resize",
284
+ style: {
285
+ position: "absolute",
286
+ right: "-0.375rem",
287
+ bottom: "-0.375rem",
288
+ width: "0.75rem",
289
+ height: "0.75rem",
290
+ cursor: "nwse-resize"
291
+ },
216
292
  onPointerDown: handleResizePointerDown,
217
293
  onPointerMove: handleResizePointerMove,
218
294
  onPointerUp: handleResizePointerUp
@@ -229,7 +305,8 @@ function FieldOverlay({
229
305
  onAddField,
230
306
  onUpdateField,
231
307
  onRemoveField,
232
- preview
308
+ preview,
309
+ className
233
310
  }) {
234
311
  function handleOverlayPointerDown(event) {
235
312
  if (!selectedFieldType) return;
@@ -249,7 +326,15 @@ function FieldOverlay({
249
326
  return /* @__PURE__ */ jsx(
250
327
  "div",
251
328
  {
252
- className: `absolute inset-0 z-20 rounded ${selectedFieldType ? "cursor-crosshair" : "cursor-default"}`,
329
+ "data-slot": "field-overlay",
330
+ "data-state": selectedFieldType ? "placing" : "idle",
331
+ className: cn(className),
332
+ style: {
333
+ position: "absolute",
334
+ inset: 0,
335
+ zIndex: 20,
336
+ cursor: selectedFieldType ? "crosshair" : "default"
337
+ },
253
338
  onPointerDown: handleOverlayPointerDown,
254
339
  "aria-label": `Field overlay page ${pageIndex + 1}`,
255
340
  children: pageFields.map((field) => /* @__PURE__ */ jsx(
@@ -272,14 +357,15 @@ var FIELD_LABELS = {
272
357
  date: "Date"
273
358
  };
274
359
  var FIELD_TYPES = ["signature", "fullName", "title", "date"];
275
- function FieldPalette({ selectedFieldType, onSelectFieldType }) {
276
- return /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-2 rounded-lg border border-slate-300 bg-white p-2", children: FIELD_TYPES.map((fieldType) => {
360
+ function FieldPalette({ selectedFieldType, onSelectFieldType, className }) {
361
+ return /* @__PURE__ */ jsx("div", { "data-slot": "field-palette", className: cn(className), children: FIELD_TYPES.map((fieldType) => {
277
362
  const isSelected = selectedFieldType === fieldType;
278
363
  return /* @__PURE__ */ jsx(
279
364
  "button",
280
365
  {
281
366
  type: "button",
282
- className: `rounded-md border px-3 py-1.5 text-sm font-medium transition ${isSelected ? "border-blue-600 bg-blue-600 text-white" : "border-slate-300 bg-slate-50 text-slate-700 hover:bg-slate-100"}`,
367
+ "data-slot": "field-palette-button",
368
+ "data-state": isSelected ? "selected" : "idle",
283
369
  onClick: () => onSelectFieldType(isSelected ? null : fieldType),
284
370
  "aria-pressed": isSelected,
285
371
  children: FIELD_LABELS[fieldType]
@@ -288,43 +374,43 @@ function FieldPalette({ selectedFieldType, onSelectFieldType }) {
288
374
  );
289
375
  }) });
290
376
  }
291
- function SignerDetailsPanel({ signerInfo, onSignerInfoChange }) {
377
+ function SignerDetailsPanel({ signerInfo, onSignerInfoChange, className }) {
292
378
  function handleInputChange(fieldName, fieldValue) {
293
379
  onSignerInfoChange({
294
380
  ...signerInfo,
295
381
  [fieldName]: fieldValue
296
382
  });
297
383
  }
298
- return /* @__PURE__ */ jsxs("div", { className: "space-y-3 rounded-lg border border-slate-300 bg-white p-4", children: [
299
- /* @__PURE__ */ jsx("h2", { className: "text-sm font-semibold text-slate-800", children: "Signer Details" }),
300
- /* @__PURE__ */ jsxs("label", { className: "block text-sm text-slate-700", children: [
384
+ return /* @__PURE__ */ jsxs("div", { "data-slot": "signer-panel", className: cn(className), children: [
385
+ /* @__PURE__ */ jsx("h2", { "data-slot": "signer-panel-heading", children: "Signer Details" }),
386
+ /* @__PURE__ */ jsxs("label", { "data-slot": "signer-panel-label", children: [
301
387
  "First Name",
302
388
  /* @__PURE__ */ jsx(
303
389
  "input",
304
390
  {
305
- className: "mt-1 w-full rounded border border-slate-300 px-2 py-1.5 text-sm",
391
+ "data-slot": "signer-panel-input",
306
392
  value: signerInfo.firstName,
307
393
  onChange: (event) => handleInputChange("firstName", event.target.value)
308
394
  }
309
395
  )
310
396
  ] }),
311
- /* @__PURE__ */ jsxs("label", { className: "block text-sm text-slate-700", children: [
397
+ /* @__PURE__ */ jsxs("label", { "data-slot": "signer-panel-label", children: [
312
398
  "Last Name",
313
399
  /* @__PURE__ */ jsx(
314
400
  "input",
315
401
  {
316
- className: "mt-1 w-full rounded border border-slate-300 px-2 py-1.5 text-sm",
402
+ "data-slot": "signer-panel-input",
317
403
  value: signerInfo.lastName,
318
404
  onChange: (event) => handleInputChange("lastName", event.target.value)
319
405
  }
320
406
  )
321
407
  ] }),
322
- /* @__PURE__ */ jsxs("label", { className: "block text-sm text-slate-700", children: [
408
+ /* @__PURE__ */ jsxs("label", { "data-slot": "signer-panel-label", children: [
323
409
  "Title",
324
410
  /* @__PURE__ */ jsx(
325
411
  "input",
326
412
  {
327
- className: "mt-1 w-full rounded border border-slate-300 px-2 py-1.5 text-sm",
413
+ "data-slot": "signer-panel-input",
328
414
  value: signerInfo.title,
329
415
  onChange: (event) => handleInputChange("title", event.target.value)
330
416
  }
@@ -332,39 +418,7 @@ function SignerDetailsPanel({ signerInfo, onSignerInfoChange }) {
332
418
  ] })
333
419
  ] });
334
420
  }
335
-
336
- // src/lib/signature-fonts.ts
337
- var loadedFonts = /* @__PURE__ */ new Set();
338
- var SIGNATURE_FONTS = ["Dancing Script", "Great Vibes", "Sacramento", "Alex Brush"];
339
- function buildGoogleFontsCssUrl(family) {
340
- const encoded = family.trim().replace(/\s+/g, "+");
341
- return `https://fonts.googleapis.com/css2?family=${encoded}&display=swap`;
342
- }
343
- async function loadCssFromGoogleFonts(url) {
344
- const response = await fetch(url);
345
- if (!response.ok) throw new Error(`Unable to load font css from ${url}`);
346
- return response.text();
347
- }
348
- function extractFontSource(cssText) {
349
- const sourceMatch = cssText.match(/src:\s*url\(([^)]+)\)\s*format\(['"]?([^'")]+)['"]?\)/i);
350
- if (!sourceMatch) return null;
351
- return sourceMatch[1].replace(/['"]/g, "");
352
- }
353
- async function loadSignatureFont(fontFamily) {
354
- if (loadedFonts.has(fontFamily)) return;
355
- if (typeof document === "undefined") return;
356
- if (typeof FontFace === "undefined") return;
357
- const cssUrl = buildGoogleFontsCssUrl(fontFamily);
358
- const cssText = await loadCssFromGoogleFonts(cssUrl);
359
- const fontSource = extractFontSource(cssText);
360
- if (!fontSource) throw new Error(`Unable to extract font source for ${fontFamily}`);
361
- const fontFace = new FontFace(fontFamily, `url(${fontSource})`);
362
- await fontFace.load();
363
- const fontSet = document.fonts;
364
- fontSet.add(fontFace);
365
- loadedFonts.add(fontFamily);
366
- }
367
- function SignaturePad({ onDrawn }) {
421
+ function SignaturePad({ onDrawn, className }) {
368
422
  const canvasRef = useRef(null);
369
423
  const signaturePadRef = useRef(null);
370
424
  useEffect(() => {
@@ -391,13 +445,13 @@ function SignaturePad({ onDrawn }) {
391
445
  signaturePadRef.current?.clear();
392
446
  onDrawn("");
393
447
  }
394
- return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
395
- /* @__PURE__ */ jsx("canvas", { ref: canvasRef, width: 420, height: 140, className: "w-full rounded border border-slate-300 bg-white" }),
396
- /* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx(
448
+ return /* @__PURE__ */ jsxs("div", { "data-slot": "signature-pad", className: cn(className), children: [
449
+ /* @__PURE__ */ jsx("canvas", { "data-slot": "signature-pad-canvas", ref: canvasRef, width: 420, height: 140 }),
450
+ /* @__PURE__ */ jsx("div", { "data-slot": "signature-pad-actions", children: /* @__PURE__ */ jsx(
397
451
  "button",
398
452
  {
399
453
  type: "button",
400
- className: "rounded border border-slate-300 px-2 py-1 text-xs text-slate-700 hover:bg-slate-50",
454
+ "data-slot": "signature-pad-clear",
401
455
  onClick: handleClear,
402
456
  children: "Clear"
403
457
  }
@@ -409,17 +463,20 @@ function SignaturePreview({
409
463
  style,
410
464
  signatureDataUrl,
411
465
  isRendering,
412
- onStyleChange
466
+ onStyleChange,
467
+ className
413
468
  }) {
414
469
  const canShowPreview = useMemo(() => signerName.trim().length > 0, [signerName]);
415
- return /* @__PURE__ */ jsxs("div", { className: "space-y-3 rounded-lg border border-slate-300 bg-white p-4", children: [
416
- /* @__PURE__ */ jsx("h2", { className: "text-sm font-semibold text-slate-800", children: "Signature" }),
417
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
470
+ return /* @__PURE__ */ jsxs("div", { "data-slot": "signature-preview", className: cn(className), children: [
471
+ /* @__PURE__ */ jsx("h2", { "data-slot": "signature-preview-heading", children: "Signature" }),
472
+ /* @__PURE__ */ jsxs("div", { "data-slot": "signature-preview-mode-toggle", children: [
418
473
  /* @__PURE__ */ jsx(
419
474
  "button",
420
475
  {
421
476
  type: "button",
422
- className: `rounded border px-2 py-1 text-xs ${style.mode === "typed" ? "border-blue-600 bg-blue-600 text-white" : "border-slate-300 bg-slate-50 text-slate-700"}`,
477
+ "data-slot": "signature-preview-mode-button",
478
+ "data-mode": "typed",
479
+ "data-state": style.mode === "typed" ? "active" : "idle",
423
480
  onClick: () => onStyleChange({ mode: "typed", fontFamily: style.mode === "typed" ? style.fontFamily : SIGNATURE_FONTS[0] }),
424
481
  children: "Typed"
425
482
  }
@@ -428,25 +485,34 @@ function SignaturePreview({
428
485
  "button",
429
486
  {
430
487
  type: "button",
431
- className: `rounded border px-2 py-1 text-xs ${style.mode === "drawn" ? "border-blue-600 bg-blue-600 text-white" : "border-slate-300 bg-slate-50 text-slate-700"}`,
488
+ "data-slot": "signature-preview-mode-button",
489
+ "data-mode": "drawn",
490
+ "data-state": style.mode === "drawn" ? "active" : "idle",
432
491
  onClick: () => onStyleChange({ mode: "drawn", dataUrl: style.mode === "drawn" ? style.dataUrl : "" }),
433
492
  children: "Drawn"
434
493
  }
435
494
  )
436
495
  ] }),
437
- style.mode === "typed" ? /* @__PURE__ */ jsxs("label", { className: "block text-sm text-slate-700", children: [
496
+ style.mode === "typed" ? /* @__PURE__ */ jsxs("label", { "data-slot": "signature-preview-font-label", children: [
438
497
  "Font",
439
498
  /* @__PURE__ */ jsx(
440
499
  "select",
441
500
  {
442
- className: "mt-1 w-full rounded border border-slate-300 px-2 py-1.5 text-sm",
501
+ "data-slot": "signature-preview-font-select",
443
502
  value: style.fontFamily,
444
503
  onChange: (event) => onStyleChange({ mode: "typed", fontFamily: event.target.value }),
445
504
  children: SIGNATURE_FONTS.map((fontFamily) => /* @__PURE__ */ jsx("option", { value: fontFamily, children: fontFamily }, fontFamily))
446
505
  }
447
506
  )
448
507
  ] }) : /* @__PURE__ */ jsx(SignaturePad, { onDrawn: (dataUrl) => onStyleChange({ mode: "drawn", dataUrl }) }),
449
- /* @__PURE__ */ jsx("div", { className: "rounded border border-dashed border-slate-300 bg-slate-50 p-3", children: !canShowPreview ? /* @__PURE__ */ jsx("p", { className: "text-xs text-slate-500", children: "Enter signer first and last name to render a signature." }) : isRendering ? /* @__PURE__ */ jsx("p", { className: "text-xs text-slate-500", children: "Rendering signature..." }) : signatureDataUrl ? /* @__PURE__ */ jsx("img", { src: signatureDataUrl, alt: "Signature preview", className: "h-24 w-full object-contain" }) : /* @__PURE__ */ jsx("p", { className: "text-xs text-slate-500", children: "No signature available yet." }) })
508
+ /* @__PURE__ */ jsx(
509
+ "div",
510
+ {
511
+ "data-slot": "signature-preview-display",
512
+ "data-state": !canShowPreview ? "empty" : isRendering ? "rendering" : signatureDataUrl ? "ready" : "missing-signature",
513
+ children: !canShowPreview ? /* @__PURE__ */ jsx("p", { "data-slot": "signature-preview-placeholder", children: "Enter signer first and last name to render a signature." }) : isRendering ? /* @__PURE__ */ jsx("p", { "data-slot": "signature-preview-placeholder", children: "Rendering signature..." }) : signatureDataUrl ? /* @__PURE__ */ jsx("img", { "data-slot": "signature-preview-image", src: signatureDataUrl, alt: "Signature preview" }) : /* @__PURE__ */ jsx("p", { "data-slot": "signature-preview-placeholder", children: "No signature available yet." })
514
+ }
515
+ )
450
516
  ] });
451
517
  }
452
518
  function SigningComplete({
@@ -456,11 +522,12 @@ function SigningComplete({
456
522
  documentHash,
457
523
  downloadUrl,
458
524
  fileName = "signed-document.pdf",
459
- onReset
525
+ onReset,
526
+ className
460
527
  }) {
461
- return /* @__PURE__ */ jsxs("div", { className: "space-y-4 rounded-lg border border-emerald-300 bg-emerald-50 p-4", children: [
462
- /* @__PURE__ */ jsx("h2", { className: "text-base font-semibold text-emerald-900", children: "Document Signed" }),
463
- /* @__PURE__ */ jsxs("div", { className: "space-y-1 text-sm text-emerald-900", children: [
528
+ return /* @__PURE__ */ jsxs("div", { "data-slot": "signing-complete", className: cn(className), children: [
529
+ /* @__PURE__ */ jsx("h2", { "data-slot": "signing-complete-heading", children: "Document Signed" }),
530
+ /* @__PURE__ */ jsxs("div", { "data-slot": "signing-complete-details", children: [
464
531
  /* @__PURE__ */ jsxs("p", { children: [
465
532
  "Signer: ",
466
533
  signerName || "Unknown"
@@ -474,17 +541,17 @@ function SigningComplete({
474
541
  signedAt
475
542
  ] })
476
543
  ] }),
477
- /* @__PURE__ */ jsxs("div", { className: "rounded border border-emerald-200 bg-white p-3", children: [
478
- /* @__PURE__ */ jsx("p", { className: "mb-1 text-xs font-medium uppercase tracking-wide text-slate-500", children: "SHA-256" }),
479
- /* @__PURE__ */ jsx("p", { className: "break-all font-mono text-xs text-slate-800", children: documentHash })
544
+ /* @__PURE__ */ jsxs("div", { "data-slot": "signing-complete-hash", children: [
545
+ /* @__PURE__ */ jsx("p", { "data-slot": "signing-complete-hash-label", children: "SHA-256" }),
546
+ /* @__PURE__ */ jsx("p", { "data-slot": "signing-complete-hash-value", children: documentHash })
480
547
  ] }),
481
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
548
+ /* @__PURE__ */ jsxs("div", { "data-slot": "signing-complete-actions", children: [
482
549
  /* @__PURE__ */ jsx(
483
550
  "a",
484
551
  {
485
552
  href: downloadUrl,
486
553
  download: fileName,
487
- className: "rounded bg-emerald-700 px-3 py-1.5 text-sm text-white hover:bg-emerald-800",
554
+ "data-slot": "signing-complete-download",
488
555
  children: "Download Signed PDF"
489
556
  }
490
557
  ),
@@ -492,7 +559,7 @@ function SigningComplete({
492
559
  "button",
493
560
  {
494
561
  type: "button",
495
- className: "rounded border border-slate-300 bg-white px-3 py-1.5 text-sm text-slate-700 hover:bg-slate-50",
562
+ "data-slot": "signing-complete-reset",
496
563
  onClick: onReset,
497
564
  children: "Sign Another"
498
565
  }
@@ -647,6 +714,41 @@ function useFieldPlacement(options = {}) {
647
714
  function buildCanvas() {
648
715
  return document.createElement("canvas");
649
716
  }
717
+ function hashString(s) {
718
+ let h = 2166136261;
719
+ for (let i = 0; i < s.length; i++) {
720
+ h ^= s.charCodeAt(i);
721
+ h = Math.imul(h, 16777619);
722
+ }
723
+ return h >>> 0;
724
+ }
725
+ function mulberry32(seed) {
726
+ return function() {
727
+ let t = seed += 1831565813;
728
+ t = Math.imul(t ^ t >>> 15, t | 1);
729
+ t ^= t + Math.imul(t ^ t >>> 7, t | 61);
730
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
731
+ };
732
+ }
733
+ function layoutTypedGlyphs({
734
+ signerName,
735
+ fontFamily,
736
+ fontSize,
737
+ context
738
+ }) {
739
+ const rng = mulberry32(hashString(`${signerName}\0${fontFamily}`));
740
+ context.font = `${fontSize}px "${fontFamily}"`;
741
+ const chars = Array.from(signerName);
742
+ const layouts = [];
743
+ for (let i = 0; i < chars.length; i++) {
744
+ const char = chars[i];
745
+ const width = context.measureText(char).width;
746
+ const rotation = (rng() * 2 - 1) * 0.07;
747
+ const gapAfter = i < chars.length - 1 ? (rng() * 2 - 1) * 1.25 : 0;
748
+ layouts.push({ char, width, rotation, gapAfter });
749
+ }
750
+ return layouts;
751
+ }
650
752
  function drawTypedSignature({
651
753
  signerName,
652
754
  fontFamily
@@ -656,15 +758,27 @@ function drawTypedSignature({
656
758
  if (!context) return "";
657
759
  const padding = 16;
658
760
  const fontSize = 56;
659
- context.font = `${fontSize}px "${fontFamily}"`;
660
- const metrics = context.measureText(signerName);
661
- canvas.width = Math.max(240, Math.ceil(metrics.width + padding * 2));
761
+ const baselineY = 50;
762
+ const glyphs = layoutTypedGlyphs({ signerName, fontFamily, fontSize, context });
763
+ const textWidth = glyphs.reduce((sum, g) => sum + g.width + g.gapAfter, 0);
764
+ canvas.width = Math.max(240, Math.ceil(textWidth + padding * 2));
662
765
  canvas.height = 100;
663
766
  context.clearRect(0, 0, canvas.width, canvas.height);
664
767
  context.font = `${fontSize}px "${fontFamily}"`;
665
768
  context.fillStyle = "#111827";
769
+ context.textAlign = "center";
666
770
  context.textBaseline = "middle";
667
- context.fillText(signerName, padding, canvas.height / 2);
771
+ let x = padding;
772
+ for (const glyph of glyphs) {
773
+ const { char, width, rotation, gapAfter } = glyph;
774
+ const cx = x + width / 2;
775
+ context.save();
776
+ context.translate(cx, baselineY);
777
+ context.rotate(rotation);
778
+ context.fillText(char, 0, 0);
779
+ context.restore();
780
+ x += width + gapAfter;
781
+ }
668
782
  return canvas.toDataURL("image/png");
669
783
  }
670
784
  function useSignatureRenderer({ signerName, style }) {
@@ -796,13 +910,65 @@ async function sha256(data) {
796
910
  return Array.from(new Uint8Array(hashBuffer)).map((byte) => byte.toString(16).padStart(2, "0")).join("");
797
911
  }
798
912
 
913
+ // src/lib/slots.ts
914
+ var SLOTS = {
915
+ pdfViewer: "pdf-viewer",
916
+ pdfViewerEmpty: "pdf-viewer-empty",
917
+ pdfViewerToolbar: "pdf-viewer-toolbar",
918
+ pdfViewerPageCount: "pdf-viewer-page-count",
919
+ pdfViewerZoom: "pdf-viewer-zoom",
920
+ pdfViewerZoomButton: "pdf-viewer-zoom-button",
921
+ pdfViewerZoomValue: "pdf-viewer-zoom-value",
922
+ pdfViewerPages: "pdf-viewer-pages",
923
+ pdfViewerPage: "pdf-viewer-page",
924
+ pdfViewerLoading: "pdf-viewer-loading",
925
+ pdfViewerError: "pdf-viewer-error",
926
+ fieldOverlay: "field-overlay",
927
+ signatureField: "signature-field",
928
+ signatureFieldContent: "signature-field-content",
929
+ signatureFieldLabel: "signature-field-label",
930
+ signatureFieldPreview: "signature-field-preview",
931
+ signatureFieldPreviewImage: "signature-field-preview-image",
932
+ signatureFieldPreviewText: "signature-field-preview-text",
933
+ signatureFieldRemove: "signature-field-remove",
934
+ signatureFieldResize: "signature-field-resize",
935
+ fieldPalette: "field-palette",
936
+ fieldPaletteButton: "field-palette-button",
937
+ signerPanel: "signer-panel",
938
+ signerPanelHeading: "signer-panel-heading",
939
+ signerPanelLabel: "signer-panel-label",
940
+ signerPanelInput: "signer-panel-input",
941
+ signaturePreview: "signature-preview",
942
+ signaturePreviewHeading: "signature-preview-heading",
943
+ signaturePreviewModeToggle: "signature-preview-mode-toggle",
944
+ signaturePreviewModeButton: "signature-preview-mode-button",
945
+ signaturePreviewFontLabel: "signature-preview-font-label",
946
+ signaturePreviewFontSelect: "signature-preview-font-select",
947
+ signaturePreviewDisplay: "signature-preview-display",
948
+ signaturePreviewImage: "signature-preview-image",
949
+ signaturePreviewPlaceholder: "signature-preview-placeholder",
950
+ signaturePad: "signature-pad",
951
+ signaturePadCanvas: "signature-pad-canvas",
952
+ signaturePadActions: "signature-pad-actions",
953
+ signaturePadClear: "signature-pad-clear",
954
+ signingComplete: "signing-complete",
955
+ signingCompleteHeading: "signing-complete-heading",
956
+ signingCompleteDetails: "signing-complete-details",
957
+ signingCompleteHash: "signing-complete-hash",
958
+ signingCompleteHashLabel: "signing-complete-hash-label",
959
+ signingCompleteHashValue: "signing-complete-hash-value",
960
+ signingCompleteActions: "signing-complete-actions",
961
+ signingCompleteDownload: "signing-complete-download",
962
+ signingCompleteReset: "signing-complete-reset"
963
+ };
964
+
799
965
  // src/index.ts
800
966
  var defaults = {
801
- SIGNATURE_FONTS: ["Dancing Script", "Great Vibes", "Sacramento", "Alex Brush"],
967
+ SIGNATURE_FONTS: [...SIGNATURE_FONTS],
802
968
  DEFAULT_FIELD_WIDTH_PERCENT: 25,
803
969
  DEFAULT_FIELD_HEIGHT_PERCENT: 5
804
970
  };
805
971
 
806
- export { FieldOverlay, FieldPalette, PdfViewer, SIGNATURE_FONTS, SignatureField, SignaturePad, SignaturePreview, SignerDetailsPanel, SigningComplete, defaults, loadSignatureFont, mapFromPoints, mapToPoints, modifyPdf, sha256, useFieldPlacement, usePdfDocument, useSignatureRenderer };
972
+ export { FieldOverlay, FieldPalette, PdfViewer, SIGNATURE_FONTS, SLOTS, SignatureField, SignaturePad, SignaturePreview, SignerDetailsPanel, SigningComplete, defaults, loadSignatureFont, mapFromPoints, mapToPoints, modifyPdf, sha256, useFieldPlacement, usePdfDocument, useSignatureRenderer };
807
973
  //# sourceMappingURL=index.js.map
808
974
  //# sourceMappingURL=index.js.map