@unlev/exeq 0.5.0 → 0.5.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.mjs CHANGED
@@ -344,6 +344,8 @@ function PdfViewer({
344
344
  selectedIds: selectedFieldIds,
345
345
  mode,
346
346
  currentSigner,
347
+ pageWidthPt: page.pdfWidth,
348
+ pageHeightPt: page.pdfHeight,
347
349
  renderContent: renderFieldContent
348
350
  },
349
351
  field.id
@@ -434,6 +436,8 @@ function FieldOverlayItem({
434
436
  selectedIds,
435
437
  mode,
436
438
  currentSigner,
439
+ pageWidthPt,
440
+ pageHeightPt,
437
441
  renderContent
438
442
  }) {
439
443
  const overlayRef = useRef(null);
@@ -560,7 +564,7 @@ function FieldOverlayItem({
560
564
  onMouseDown: handleMouseDown,
561
565
  children: [
562
566
  mode === "designer" && !isRedact && /* @__PURE__ */ jsx("div", { className: "field-overlay-label", style: { backgroundColor: color }, children: field.label }),
563
- renderContent ? renderContent(field) : /* @__PURE__ */ jsx("div", { className: "field-overlay-placeholder", children: field.value || field.placeholder }),
567
+ renderContent ? renderContent(field, pageWidthPt, pageHeightPt) : /* @__PURE__ */ jsx("div", { className: "field-overlay-placeholder", children: field.value || field.placeholder }),
564
568
  mode === "designer" && isSelected && !field.locked && /* @__PURE__ */ jsx(
565
569
  "div",
566
570
  {
@@ -2093,39 +2097,56 @@ import {
2093
2097
  } from "pdf-lib";
2094
2098
 
2095
2099
  // src/utils/formulaResolver.ts
2100
+ function parseDate(value) {
2101
+ const s = value.trim();
2102
+ if (!s) return null;
2103
+ if (/^\d+(\.\d+)?$/.test(s)) {
2104
+ const serial = parseFloat(s);
2105
+ if (serial > 59) {
2106
+ const utc = new Date(Math.round((serial - 25569) * 864e5));
2107
+ if (!isNaN(utc.getTime())) {
2108
+ return new Date(utc.getUTCFullYear(), utc.getUTCMonth(), utc.getUTCDate());
2109
+ }
2110
+ }
2111
+ }
2112
+ const iso = s.match(/^(\d{4})-(\d{2})-(\d{2})$/);
2113
+ if (iso) return new Date(+iso[1], +iso[2] - 1, +iso[3]);
2114
+ const d = new Date(s);
2115
+ return isNaN(d.getTime()) ? null : d;
2116
+ }
2096
2117
  var BUILTIN_TRANSFORMS = {
2097
2118
  // Date transforms (expects a parseable date string)
2098
2119
  month: (v) => {
2099
- const d = new Date(v);
2100
- return isNaN(d.getTime()) ? "" : String(d.getMonth() + 1);
2120
+ const d = parseDate(v);
2121
+ return d ? String(d.getMonth() + 1) : "";
2101
2122
  },
2102
2123
  month2: (v) => {
2103
- const d = new Date(v);
2104
- return isNaN(d.getTime()) ? "" : String(d.getMonth() + 1).padStart(2, "0");
2124
+ const d = parseDate(v);
2125
+ return d ? String(d.getMonth() + 1).padStart(2, "0") : "";
2105
2126
  },
2106
2127
  monthname: (v) => {
2107
- const d = new Date(v);
2108
- return isNaN(d.getTime()) ? "" : d.toLocaleString("en", { month: "long" });
2128
+ const d = parseDate(v);
2129
+ return d ? d.toLocaleString("en", { month: "long" }) : "";
2109
2130
  },
2110
2131
  monthshort: (v) => {
2111
- const d = new Date(v);
2112
- return isNaN(d.getTime()) ? "" : d.toLocaleString("en", { month: "short" });
2132
+ const d = parseDate(v);
2133
+ return d ? d.toLocaleString("en", { month: "short" }) : "";
2113
2134
  },
2114
2135
  day: (v) => {
2115
- const d = new Date(v);
2116
- return isNaN(d.getTime()) ? "" : String(d.getDate());
2136
+ const d = parseDate(v);
2137
+ return d ? String(d.getDate()) : "";
2117
2138
  },
2118
2139
  day2: (v) => {
2119
- const d = new Date(v);
2120
- return isNaN(d.getTime()) ? "" : String(d.getDate()).padStart(2, "0");
2140
+ const d = parseDate(v);
2141
+ return d ? String(d.getDate()).padStart(2, "0") : "";
2121
2142
  },
2122
2143
  year: (v) => {
2123
- const d = new Date(v);
2124
- return isNaN(d.getTime()) ? "" : String(d.getFullYear());
2144
+ const d = parseDate(v);
2145
+ return d ? String(d.getFullYear()) : "";
2125
2146
  },
2126
2147
  year2: (v) => {
2127
- const d = new Date(v);
2128
- return isNaN(d.getTime()) ? "" : String(d.getFullYear()).slice(-2);
2148
+ const d = parseDate(v);
2149
+ return d ? String(d.getFullYear()).slice(-2) : "";
2129
2150
  },
2130
2151
  // String transforms
2131
2152
  upper: (v) => v.toUpperCase(),
@@ -2154,15 +2175,27 @@ var BUILTIN_TRANSFORMS = {
2154
2175
  };
2155
2176
  var FORMULA_RE = /\{\{(.+?)\}\}/g;
2156
2177
  var PIPE_RE = /^(.+?)\s*\|\s*(.+)$/;
2157
- function resolveFormula(formula, fields, customTransforms) {
2178
+ function todayIso() {
2179
+ const d = /* @__PURE__ */ new Date();
2180
+ return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
2181
+ }
2182
+ function resolveFormula(formula, fields, customTransforms, values) {
2158
2183
  const transforms = { ...BUILTIN_TRANSFORMS, ...customTransforms };
2159
2184
  return formula.replace(FORMULA_RE, (_match, expr) => {
2160
2185
  const pipeMatch = expr.match(PIPE_RE);
2161
2186
  const sourceLabel = (pipeMatch ? pipeMatch[1] : expr).trim();
2162
2187
  const transformName = pipeMatch ? pipeMatch[2].trim() : null;
2163
- const sourceField = fields.find((f) => f.label.toLowerCase() === sourceLabel.toLowerCase()) || fields.find((f) => f.id === sourceLabel);
2164
- if (!sourceField) return "";
2165
- const rawValue = sourceField.value || "";
2188
+ const lowerLabel = sourceLabel.toLowerCase();
2189
+ const sourceField = fields.find((f) => f.label.toLowerCase() === lowerLabel) || fields.find((f) => f.id === sourceLabel);
2190
+ let rawValue = sourceField ? sourceField.value || "" : void 0;
2191
+ if (rawValue === void 0 && values) {
2192
+ const key = Object.keys(values).find((k) => k.toLowerCase() === lowerLabel);
2193
+ if (key !== void 0) rawValue = values[key] || "";
2194
+ }
2195
+ if (rawValue === void 0 && (lowerLabel === "today" || lowerLabel === "now")) {
2196
+ rawValue = todayIso();
2197
+ }
2198
+ if (rawValue === void 0) return "";
2166
2199
  if (!transformName) return rawValue;
2167
2200
  const fn = transforms[transformName];
2168
2201
  if (!fn) return rawValue;
@@ -2173,14 +2206,38 @@ function resolveFormula(formula, fields, customTransforms) {
2173
2206
  }
2174
2207
  });
2175
2208
  }
2176
- function resolveAllFormulas(fields, customTransforms) {
2209
+ function resolveAllFormulas(fields, customTransforms, values) {
2177
2210
  return fields.map((f) => {
2178
2211
  if (!f.formula) return f;
2179
- const computed = resolveFormula(f.formula, fields, customTransforms);
2212
+ const computed = resolveFormula(f.formula, fields, customTransforms, values);
2180
2213
  return computed !== f.value ? { ...f, value: computed } : f;
2181
2214
  });
2182
2215
  }
2183
2216
 
2217
+ // src/utils/fitText.ts
2218
+ function fitFontSize(o) {
2219
+ let size = o.autoShrink ? Math.min(o.fontSize, o.fieldHeightPt * 0.7) : o.fontSize;
2220
+ if (o.autoShrink && o.value) {
2221
+ const padding = 4;
2222
+ while (size > 4) {
2223
+ if (o.measure(o.value, size) <= o.fieldWidthPt - padding) break;
2224
+ size -= 0.5;
2225
+ }
2226
+ }
2227
+ return size;
2228
+ }
2229
+ var measureCtx = null;
2230
+ function measureTextWidth(text, size, opts) {
2231
+ if (typeof document === "undefined") return text.length * size * 0.5;
2232
+ if (!measureCtx) measureCtx = document.createElement("canvas").getContext("2d");
2233
+ if (!measureCtx) return text.length * size * 0.5;
2234
+ const family = getCssFontFamily(opts?.fontFamily) || "Helvetica, Arial, sans-serif";
2235
+ measureCtx.font = `${opts?.italic ? "italic " : ""}${opts?.bold ? "bold " : ""}${size}px ${family}`;
2236
+ const base = measureCtx.measureText(text).width;
2237
+ const spacing = opts?.letterSpacing ? opts.letterSpacing * Math.max(0, text.length - 1) : 0;
2238
+ return base + spacing;
2239
+ }
2240
+
2184
2241
  // src/utils/pdfFiller.ts
2185
2242
  var FONT_VARIANTS = {
2186
2243
  Helvetica: [StandardFonts.Helvetica, StandardFonts.HelveticaBold, StandardFonts.HelveticaOblique, StandardFonts.HelveticaBoldOblique],
@@ -2272,14 +2329,14 @@ async function renderFieldsOnPages(pages, fields, getFont, getSignature) {
2272
2329
  const font = await getFont(field.fontFamily || "Helvetica", field.bold, field.italic);
2273
2330
  const spacing = field.letterSpacing || 0;
2274
2331
  const textWidthAtSize = (text, size) => font.widthOfTextAtSize(text, size) + spacing * (text.length - 1);
2275
- let fontSize = field.autoShrink ? Math.min(field.fontSize, h * 0.7) : field.fontSize;
2276
- if (field.autoShrink) {
2277
- const padding = 4;
2278
- while (fontSize > 4) {
2279
- if (textWidthAtSize(field.value, fontSize) <= w - padding) break;
2280
- fontSize -= 0.5;
2281
- }
2282
- }
2332
+ const fontSize = fitFontSize({
2333
+ value: field.value,
2334
+ fontSize: field.fontSize,
2335
+ autoShrink: field.autoShrink,
2336
+ fieldWidthPt: w,
2337
+ fieldHeightPt: h,
2338
+ measure: textWidthAtSize
2339
+ });
2283
2340
  const textX = x + 2;
2284
2341
  const baselineY = y + h * 0.3;
2285
2342
  if (spacing > 0) {
@@ -2383,7 +2440,7 @@ async function createPdfBuilder(defaults = {}) {
2383
2440
  doc,
2384
2441
  async addRecord(opts) {
2385
2442
  const pageSize = opts.pageSize ?? defaults.pageSize;
2386
- let fields = opts.resolveFormulas ? resolveAllFormulas(opts.fields, opts.customTransforms) : opts.fields;
2443
+ let fields = opts.resolveFormulas ? resolveAllFormulas(opts.fields, opts.customTransforms, opts.formulaValues) : opts.fields;
2387
2444
  if (opts.calibration) {
2388
2445
  fields = applyCalibration(
2389
2446
  fields,
@@ -2784,7 +2841,7 @@ ${row.join(",")}`, "csv");
2784
2841
  setSubmitting(false);
2785
2842
  }
2786
2843
  }, [pdfSource, fields, callbackUrl, allRequiredFilled, onComplete, isLastSigner, signer, onSignerComplete, includeAuditTrail, exportFormat, onExport, getValues, transforms]);
2787
- const renderFieldContent = useCallback5((field) => {
2844
+ const renderFieldContent = useCallback5((field, pageWidthPt, pageHeightPt) => {
2788
2845
  if (isRedactField(field)) {
2789
2846
  return null;
2790
2847
  }
@@ -2823,8 +2880,23 @@ ${row.join(",")}`, "csv");
2823
2880
  if (field.type === "signed-date") {
2824
2881
  return /* @__PURE__ */ jsx6("div", { className: "field-overlay-value", children: field.value || (/* @__PURE__ */ new Date()).toLocaleDateString() });
2825
2882
  }
2883
+ const fieldWidthPt = pageWidthPt ? field.width / 100 * pageWidthPt : Infinity;
2884
+ const fieldHeightPt = pageHeightPt ? field.height / 100 * pageHeightPt : 0;
2885
+ const effectiveFontSize = pageWidthPt ? fitFontSize({
2886
+ value: field.value || "",
2887
+ fontSize: field.fontSize,
2888
+ autoShrink: field.autoShrink,
2889
+ fieldWidthPt,
2890
+ fieldHeightPt,
2891
+ measure: (t, s) => measureTextWidth(t, s, {
2892
+ fontFamily: field.fontFamily,
2893
+ bold: field.bold,
2894
+ italic: field.italic,
2895
+ letterSpacing: field.letterSpacing
2896
+ })
2897
+ }) : field.fontSize;
2826
2898
  const fontStyle = {
2827
- fontSize: `${field.fontSize}pt`,
2899
+ fontSize: `${effectiveFontSize}pt`,
2828
2900
  letterSpacing: field.letterSpacing ? `${field.letterSpacing}pt` : void 0,
2829
2901
  lineHeight: field.lineHeight ? `${field.lineHeight}` : void 0,
2830
2902
  ...getCssTextStyle(field)
@@ -3105,6 +3177,7 @@ export {
3105
3177
  isRedactField,
3106
3178
  isSignatureField,
3107
3179
  isTextLikeField,
3180
+ parseDate,
3108
3181
  postPdfToCallback,
3109
3182
  preserveFieldValues,
3110
3183
  renderPdfPages,