@unlev/exeq 0.5.4 → 0.5.6

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.mjs CHANGED
@@ -60,11 +60,17 @@ function getCssTextStyle(field) {
60
60
  const decorations = [];
61
61
  if (field.underline) decorations.push("underline");
62
62
  if (field.strikethrough) decorations.push("line-through");
63
+ const align = field.align ?? "left";
63
64
  return {
64
65
  fontFamily: getCssFontFamily(field.fontFamily),
65
66
  fontWeight: field.bold ? "bold" : void 0,
66
67
  fontStyle: field.italic ? "italic" : void 0,
67
- textDecorationLine: decorations.length ? decorations.join(" ") : void 0
68
+ textDecorationLine: decorations.length ? decorations.join(" ") : void 0,
69
+ textAlign: field.align,
70
+ // Hug the alignment edge — no padding on the trailing side — so text reaches
71
+ // the container edge (matches the PDF's 2pt offsets).
72
+ paddingLeft: align === "right" ? "0px" : "2px",
73
+ paddingRight: align === "left" ? "0px" : "2px"
68
74
  };
69
75
  }
70
76
  function sortFieldsByPosition(fields) {
@@ -735,6 +741,27 @@ function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete, prefillCon
735
741
  /* @__PURE__ */ jsx2("button", { type: "button", className: `text-format-btn ${field.strikethrough ? "active" : ""}`, style: { textDecorationLine: "line-through" }, title: "Strikethrough", onClick: () => onUpdate(field.id, { strikethrough: !field.strikethrough }), children: "S" })
736
742
  ] })
737
743
  ] }),
744
+ isText && /* @__PURE__ */ jsxs2("div", { className: "panel-field", children: [
745
+ /* @__PURE__ */ jsx2("label", { children: "Alignment" }),
746
+ /* @__PURE__ */ jsx2("div", { className: "text-format-buttons", children: [
747
+ ["left", "Left", "\u2AF7"],
748
+ ["center", "Center", "\u2261"],
749
+ ["right", "Right", "\u2AF8"]
750
+ ].map(([value, label, glyph]) => {
751
+ const active = (field.align || "left") === value;
752
+ return /* @__PURE__ */ jsx2(
753
+ "button",
754
+ {
755
+ type: "button",
756
+ className: `text-format-btn ${active ? "active" : ""}`,
757
+ title: `Align ${label}`,
758
+ onClick: () => onUpdate(field.id, { align: value === "left" ? void 0 : value }),
759
+ children: glyph
760
+ },
761
+ value
762
+ );
763
+ }) })
764
+ ] }),
738
765
  isText && /* @__PURE__ */ jsxs2("div", { className: "panel-field-row", children: [
739
766
  /* @__PURE__ */ jsxs2("div", { className: "panel-field panel-field-half", children: [
740
767
  /* @__PURE__ */ jsx2("label", { children: "Letter Spacing" }),
@@ -2187,11 +2214,17 @@ function resolveFormula(formula, fields, customTransforms, values) {
2187
2214
  const transformName = pipeMatch ? pipeMatch[2].trim() : null;
2188
2215
  const lowerLabel = sourceLabel.toLowerCase();
2189
2216
  const sourceField = fields.find((f) => f.label.toLowerCase() === lowerLabel) || fields.find((f) => f.id === sourceLabel);
2190
- let rawValue = sourceField ? sourceField.value || "" : void 0;
2217
+ let rawValue;
2218
+ if (sourceField && sourceField.value) {
2219
+ rawValue = sourceField.value;
2220
+ }
2191
2221
  if (rawValue === void 0 && values) {
2192
2222
  const key = Object.keys(values).find((k) => k.toLowerCase() === lowerLabel);
2193
2223
  if (key !== void 0) rawValue = values[key] || "";
2194
2224
  }
2225
+ if (rawValue === void 0 && sourceField) {
2226
+ rawValue = sourceField.value || "";
2227
+ }
2195
2228
  if (rawValue === void 0 && (lowerLabel === "today" || lowerLabel === "now")) {
2196
2229
  rawValue = todayIso();
2197
2230
  }
@@ -2326,7 +2359,9 @@ async function renderFieldsOnPages(pages, fields, getFont, getSignature) {
2326
2359
  fieldHeightPt: h,
2327
2360
  measure: textWidthAtSize
2328
2361
  });
2329
- const textX = x + 2;
2362
+ const fullTextWidth = textWidthAtSize(field.value, fontSize);
2363
+ const pad = 2;
2364
+ const textX = field.align === "center" ? x + Math.max(pad, (w - fullTextWidth) / 2) : field.align === "right" ? x + Math.max(pad, w - fullTextWidth - pad) : x + pad;
2330
2365
  const baselineY = y + h * 0.3;
2331
2366
  if (spacing > 0) {
2332
2367
  let cx = textX;
@@ -2580,37 +2615,54 @@ function FieldNavigator({
2580
2615
  // src/components/pdf-builder/SignerView.tsx
2581
2616
  import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
2582
2617
  var useIsoLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect3;
2583
- function shrinkToFit(el, maxPt, selfBox) {
2584
- if (!el) return;
2585
- const box = selfBox ? el : el.parentElement;
2586
- if (!box) return;
2618
+ var measureCtx = null;
2619
+ function fitFont(el, box, maxPt, text, shrink) {
2620
+ if (!shrink || typeof document === "undefined") {
2621
+ el.style.fontSize = `${maxPt}pt`;
2622
+ return;
2623
+ }
2624
+ if (!measureCtx) measureCtx = document.createElement("canvas").getContext("2d");
2625
+ const boxStyle = getComputedStyle(box);
2626
+ const padX = (parseFloat(boxStyle.paddingLeft) || 0) + (parseFloat(boxStyle.paddingRight) || 0);
2627
+ const avail = box.clientWidth - padX - 1;
2628
+ if (!text || !measureCtx || avail <= 0) {
2629
+ el.style.fontSize = `${maxPt}pt`;
2630
+ return;
2631
+ }
2632
+ const cs = getComputedStyle(el);
2633
+ const PX_PER_PT = 96 / 72;
2587
2634
  let pt = Math.max(1, maxPt);
2588
- el.style.fontSize = `${pt}pt`;
2589
- let guard = 0;
2590
- while (pt > 2 && guard++ < 400 && (el.scrollWidth > box.clientWidth + 0.5 || el.scrollHeight > box.clientHeight + 0.5)) {
2635
+ while (pt > 2) {
2636
+ measureCtx.font = `${cs.fontStyle} ${cs.fontWeight} ${pt * PX_PER_PT}px ${cs.fontFamily}`;
2637
+ if (measureCtx.measureText(text).width <= avail) break;
2591
2638
  pt -= 0.5;
2592
- el.style.fontSize = `${pt}pt`;
2593
2639
  }
2640
+ el.style.fontSize = `${pt}pt`;
2594
2641
  }
2595
2642
  function FitInput({
2596
2643
  maxPt,
2644
+ shrink,
2597
2645
  ...rest
2598
2646
  }) {
2599
2647
  const ref = useRef5(null);
2600
2648
  useIsoLayoutEffect(() => {
2601
- shrinkToFit(ref.current, maxPt, true);
2649
+ const el = ref.current;
2650
+ if (el) fitFont(el, el, maxPt, String(el.value ?? ""), shrink);
2602
2651
  });
2603
2652
  return /* @__PURE__ */ jsx6("input", { ref, ...rest });
2604
2653
  }
2605
2654
  function FitText({
2606
2655
  maxPt,
2656
+ shrink,
2607
2657
  className,
2608
2658
  style,
2609
2659
  children
2610
2660
  }) {
2611
2661
  const ref = useRef5(null);
2612
2662
  useIsoLayoutEffect(() => {
2613
- shrinkToFit(ref.current, maxPt, false);
2663
+ const el = ref.current;
2664
+ const box = el?.parentElement;
2665
+ if (el && box) fitFont(el, box, maxPt, el.textContent ?? "", shrink);
2614
2666
  });
2615
2667
  return /* @__PURE__ */ jsx6("div", { ref, className, style, children });
2616
2668
  }
@@ -2814,7 +2866,7 @@ function SignerView({
2814
2866
  if (!pdfSource) return;
2815
2867
  setSubmitting(true);
2816
2868
  try {
2817
- const finalFields = resolveAllFormulas(fields, transforms);
2869
+ const finalFields = resolveAllFormulas(fields, transforms, initialValues);
2818
2870
  let pdfBytes;
2819
2871
  if (includeAuditTrail) {
2820
2872
  const { PDFDocument: PDFDocument2, StandardFonts: StandardFonts2, rgb: rgb2 } = await import("pdf-lib");
@@ -2864,13 +2916,13 @@ ${row.join(",")}`, "csv");
2864
2916
  } finally {
2865
2917
  setSubmitting(false);
2866
2918
  }
2867
- }, [pdfSource, fields, callbackUrl, allRequiredFilled, onComplete, isLastSigner, signer, onSignerComplete, includeAuditTrail, exportFormat, onExport, getValues, transforms]);
2919
+ }, [pdfSource, fields, callbackUrl, allRequiredFilled, onComplete, isLastSigner, signer, onSignerComplete, includeAuditTrail, exportFormat, onExport, getValues, transforms, initialValues]);
2868
2920
  const renderFieldContent = useCallback5((field) => {
2869
2921
  if (isRedactField(field)) {
2870
2922
  return null;
2871
2923
  }
2872
2924
  if (field.formula) {
2873
- return /* @__PURE__ */ jsx6(FitText, { maxPt: field.fontSize, className: "field-overlay-value formula", style: getCssTextStyle(field), children: field.value || "..." });
2925
+ return /* @__PURE__ */ jsx6(FitText, { maxPt: field.fontSize, shrink: !!field.autoShrink, className: "field-overlay-value formula", style: getCssTextStyle(field), children: field.value || "" });
2874
2926
  }
2875
2927
  const editable = field.assignee === signer;
2876
2928
  if (!editable) {
@@ -2880,7 +2932,7 @@ ${row.join(",")}`, "csv");
2880
2932
  if (field.type === "checkbox") {
2881
2933
  return /* @__PURE__ */ jsx6("div", { className: "field-checkbox-display readonly", children: field.value === "true" ? "\u2713" : "" });
2882
2934
  }
2883
- return /* @__PURE__ */ jsx6(FitText, { maxPt: field.fontSize, className: "field-overlay-placeholder readonly", style: getCssTextStyle(field), children: field.value || field.placeholder });
2935
+ return /* @__PURE__ */ jsx6(FitText, { maxPt: field.fontSize, shrink: !!field.autoShrink, className: "field-overlay-placeholder readonly", style: getCssTextStyle(field), children: field.value || field.placeholder });
2884
2936
  }
2885
2937
  if (isSignatureField(field)) {
2886
2938
  if (field.value) {
@@ -2902,7 +2954,7 @@ ${row.join(",")}`, "csv");
2902
2954
  );
2903
2955
  }
2904
2956
  if (field.type === "signed-date") {
2905
- return /* @__PURE__ */ jsx6(FitText, { maxPt: field.fontSize, className: "field-overlay-value", style: getCssTextStyle(field), children: field.value || (/* @__PURE__ */ new Date()).toLocaleDateString() });
2957
+ return /* @__PURE__ */ jsx6(FitText, { maxPt: field.fontSize, shrink: !!field.autoShrink, className: "field-overlay-value", style: getCssTextStyle(field), children: field.value || (/* @__PURE__ */ new Date()).toLocaleDateString() });
2906
2958
  }
2907
2959
  const fontStyle = {
2908
2960
  letterSpacing: field.letterSpacing ? `${field.letterSpacing}pt` : void 0,
@@ -2930,6 +2982,7 @@ ${row.join(",")}`, "csv");
2930
2982
  FitInput,
2931
2983
  {
2932
2984
  maxPt: field.fontSize,
2985
+ shrink: !!field.autoShrink,
2933
2986
  type: field.textSubtype === "email" ? "email" : field.textSubtype === "number" ? "number" : field.textSubtype === "phone" ? "tel" : field.textSubtype === "date" ? "date" : "text",
2934
2987
  className: "field-inline-input",
2935
2988
  value: field.value,
@@ -2957,10 +3010,10 @@ ${row.join(",")}`, "csv");
2957
3010
  useEffect3(() => {
2958
3011
  const hasFormulas = fields.some((f) => f.formula);
2959
3012
  if (!hasFormulas) return;
2960
- const resolved = resolveAllFormulas(fields, transforms);
3013
+ const resolved = resolveAllFormulas(fields, transforms, initialValues);
2961
3014
  const changed = resolved.some((f, i) => f.value !== fields[i].value);
2962
3015
  if (changed) setFields(resolved);
2963
- }, [fields, transforms]);
3016
+ }, [fields, transforms, initialValues]);
2964
3017
  return /* @__PURE__ */ jsxs6("div", { className: "signer-layout", ref: containerRef, children: [
2965
3018
  loading && /* @__PURE__ */ jsx6("div", { className: "loading-indicator", children: "Loading document..." }),
2966
3019
  /* @__PURE__ */ jsxs6("div", { className: "signer-content", children: [