formanitor 0.0.5 → 0.0.8

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.d.cts CHANGED
@@ -52,27 +52,24 @@ interface TextFieldDef extends BaseFieldDef {
52
52
  interface NumberFieldDef extends BaseFieldDef {
53
53
  type: 'number';
54
54
  }
55
+ /** Option for select/radio/checkbox. When score is set, sum computation uses it for aggregation. */
56
+ interface SelectOption {
57
+ label: string;
58
+ value: any;
59
+ score?: number;
60
+ }
55
61
  interface SelectFieldDef extends BaseFieldDef {
56
62
  type: 'select' | 'multiselect';
57
- options?: {
58
- label: string;
59
- value: any;
60
- }[];
63
+ options?: SelectOption[];
61
64
  }
62
65
  interface RadioFieldDef extends BaseFieldDef {
63
66
  type: 'radio';
64
- options?: {
65
- label: string;
66
- value: any;
67
- }[];
67
+ options?: SelectOption[];
68
68
  }
69
69
  interface CheckboxFieldDef extends BaseFieldDef {
70
70
  type: 'checkbox';
71
71
  /** Options for the checkbox group; value is stored as array of selected option values. */
72
- options?: {
73
- label: string;
74
- value: any;
75
- }[];
72
+ options?: SelectOption[];
76
73
  }
77
74
  interface RepeatableFieldDef extends BaseFieldDef {
78
75
  type: 'repeatable';
@@ -440,4 +437,4 @@ interface CreateUploadHandlerOptions {
440
437
  */
441
438
  declare function createUploadHandler(options: CreateUploadHandlerOptions): (file: File | Blob, filename: string) => Promise<string>;
442
439
 
443
- export { type BaseFieldDef, type CheckboxFieldDef, type ColumnLayout, type Computation, type Condition, type CreateUploadHandlerOptions, type DataSource, DynamicForm, EMAIL_REGEX, type EditableTableColumnDef, type EditableTableFieldDef, type EventDefinition, type FieldDef, type FieldPrefill, FieldRenderer, type FieldState, type FieldType, FormControls, type FormControlsProps, type FormDef, type FormErrors, FormProvider, type FormState, FormStore, type FormValues, type ImageUploadFieldDef, ImageUploadWidget, type LayoutNode, type NumberFieldDef, type Permission, type PresignedUploadResponse, type RadioFieldDef, ReadOnlyForm, ReadOnlyImageUpload, ReadOnlySignature, ReadOnlyTable, type RepeatableFieldDef, type Rule, type RuleAction, type RuleResult, type ScoreComputation, type ScoreRange, type Section, type SectionChild, type SelectFieldDef, type SignatureFieldDef, SignatureUploadWidget, type StaticTextFieldDef, type StoreConfig, type SubmissionData, type SumComputation, type TextFieldDef, type ValidationRule, type WebhookDef, type Workflow, type WorkflowTransition, createUploadHandler, evaluateRules, useField, useForm, useFormStore, validateField, validateForm };
440
+ export { type BaseFieldDef, type CheckboxFieldDef, type ColumnLayout, type Computation, type Condition, type CreateUploadHandlerOptions, type DataSource, DynamicForm, EMAIL_REGEX, type EditableTableColumnDef, type EditableTableFieldDef, type EventDefinition, type FieldDef, type FieldPrefill, FieldRenderer, type FieldState, type FieldType, FormControls, type FormControlsProps, type FormDef, type FormErrors, FormProvider, type FormState, FormStore, type FormValues, type ImageUploadFieldDef, ImageUploadWidget, type LayoutNode, type NumberFieldDef, type Permission, type PresignedUploadResponse, type RadioFieldDef, ReadOnlyForm, ReadOnlyImageUpload, ReadOnlySignature, ReadOnlyTable, type RepeatableFieldDef, type Rule, type RuleAction, type RuleResult, type ScoreComputation, type ScoreRange, type Section, type SectionChild, type SelectFieldDef, type SelectOption, type SignatureFieldDef, SignatureUploadWidget, type StaticTextFieldDef, type StoreConfig, type SubmissionData, type SumComputation, type TextFieldDef, type ValidationRule, type WebhookDef, type Workflow, type WorkflowTransition, createUploadHandler, evaluateRules, useField, useForm, useFormStore, validateField, validateForm };
package/dist/index.d.ts CHANGED
@@ -52,27 +52,24 @@ interface TextFieldDef extends BaseFieldDef {
52
52
  interface NumberFieldDef extends BaseFieldDef {
53
53
  type: 'number';
54
54
  }
55
+ /** Option for select/radio/checkbox. When score is set, sum computation uses it for aggregation. */
56
+ interface SelectOption {
57
+ label: string;
58
+ value: any;
59
+ score?: number;
60
+ }
55
61
  interface SelectFieldDef extends BaseFieldDef {
56
62
  type: 'select' | 'multiselect';
57
- options?: {
58
- label: string;
59
- value: any;
60
- }[];
63
+ options?: SelectOption[];
61
64
  }
62
65
  interface RadioFieldDef extends BaseFieldDef {
63
66
  type: 'radio';
64
- options?: {
65
- label: string;
66
- value: any;
67
- }[];
67
+ options?: SelectOption[];
68
68
  }
69
69
  interface CheckboxFieldDef extends BaseFieldDef {
70
70
  type: 'checkbox';
71
71
  /** Options for the checkbox group; value is stored as array of selected option values. */
72
- options?: {
73
- label: string;
74
- value: any;
75
- }[];
72
+ options?: SelectOption[];
76
73
  }
77
74
  interface RepeatableFieldDef extends BaseFieldDef {
78
75
  type: 'repeatable';
@@ -440,4 +437,4 @@ interface CreateUploadHandlerOptions {
440
437
  */
441
438
  declare function createUploadHandler(options: CreateUploadHandlerOptions): (file: File | Blob, filename: string) => Promise<string>;
442
439
 
443
- export { type BaseFieldDef, type CheckboxFieldDef, type ColumnLayout, type Computation, type Condition, type CreateUploadHandlerOptions, type DataSource, DynamicForm, EMAIL_REGEX, type EditableTableColumnDef, type EditableTableFieldDef, type EventDefinition, type FieldDef, type FieldPrefill, FieldRenderer, type FieldState, type FieldType, FormControls, type FormControlsProps, type FormDef, type FormErrors, FormProvider, type FormState, FormStore, type FormValues, type ImageUploadFieldDef, ImageUploadWidget, type LayoutNode, type NumberFieldDef, type Permission, type PresignedUploadResponse, type RadioFieldDef, ReadOnlyForm, ReadOnlyImageUpload, ReadOnlySignature, ReadOnlyTable, type RepeatableFieldDef, type Rule, type RuleAction, type RuleResult, type ScoreComputation, type ScoreRange, type Section, type SectionChild, type SelectFieldDef, type SignatureFieldDef, SignatureUploadWidget, type StaticTextFieldDef, type StoreConfig, type SubmissionData, type SumComputation, type TextFieldDef, type ValidationRule, type WebhookDef, type Workflow, type WorkflowTransition, createUploadHandler, evaluateRules, useField, useForm, useFormStore, validateField, validateForm };
440
+ export { type BaseFieldDef, type CheckboxFieldDef, type ColumnLayout, type Computation, type Condition, type CreateUploadHandlerOptions, type DataSource, DynamicForm, EMAIL_REGEX, type EditableTableColumnDef, type EditableTableFieldDef, type EventDefinition, type FieldDef, type FieldPrefill, FieldRenderer, type FieldState, type FieldType, FormControls, type FormControlsProps, type FormDef, type FormErrors, FormProvider, type FormState, FormStore, type FormValues, type ImageUploadFieldDef, ImageUploadWidget, type LayoutNode, type NumberFieldDef, type Permission, type PresignedUploadResponse, type RadioFieldDef, ReadOnlyForm, ReadOnlyImageUpload, ReadOnlySignature, ReadOnlyTable, type RepeatableFieldDef, type Rule, type RuleAction, type RuleResult, type ScoreComputation, type ScoreRange, type Section, type SectionChild, type SelectFieldDef, type SelectOption, type SignatureFieldDef, SignatureUploadWidget, type StaticTextFieldDef, type StoreConfig, type SubmissionData, type SumComputation, type TextFieldDef, type ValidationRule, type WebhookDef, type Workflow, type WorkflowTransition, createUploadHandler, evaluateRules, useField, useForm, useFormStore, validateField, validateForm };
package/dist/index.mjs CHANGED
@@ -327,10 +327,13 @@ var FormStore = class {
327
327
  if (field.type === "image_upload" || field.type === "signature") {
328
328
  const raw = values[field.id];
329
329
  if (raw !== void 0 && raw !== null && raw !== "") {
330
- result[field.id] = {
331
- _type: "s3_key",
332
- value: raw
333
- };
330
+ const key = typeof raw === "string" ? raw : raw.s3_key || raw.value || null;
331
+ if (key && typeof key === "string") {
332
+ result[field.id] = {
333
+ _type: "s3_key",
334
+ value: key
335
+ };
336
+ }
334
337
  }
335
338
  }
336
339
  });
@@ -342,7 +345,20 @@ var FormStore = class {
342
345
  this.schema.computations.forEach((comp) => {
343
346
  if (comp.type === "sum") {
344
347
  const sum = comp.fields.reduce((acc, fieldId) => {
345
- const val = Number(this.state.values[fieldId]) || 0;
348
+ const raw = this.state.values[fieldId];
349
+ const fieldDef = this.fieldMap[fieldId];
350
+ const options = fieldDef?.type === "select" || fieldDef?.type === "multiselect" || fieldDef?.type === "radio" || fieldDef?.type === "checkbox" ? fieldDef.options : void 0;
351
+ const hasScoreOptions = options?.some((o) => o.score !== void 0);
352
+ let val;
353
+ if (hasScoreOptions && options) {
354
+ const selected = fieldDef?.type === "multiselect" || fieldDef?.type === "checkbox" ? (Array.isArray(raw) ? raw : [raw]).filter(Boolean) : [raw];
355
+ val = selected.reduce((s, v) => {
356
+ const opt = options.find((o) => o.value === v || String(o.value) === String(v));
357
+ return s + (opt && "score" in opt && typeof opt.score === "number" ? opt.score : 0);
358
+ }, 0);
359
+ } else {
360
+ val = Number(raw) || 0;
361
+ }
346
362
  return acc + val;
347
363
  }, 0);
348
364
  if (this.state.values[comp.target] !== sum) {
@@ -1513,8 +1529,15 @@ var smoothPoint = (prev, curr, smoothing = 0.75) => {
1513
1529
  y: prev.y * smoothing + curr.y * (1 - smoothing)
1514
1530
  };
1515
1531
  };
1532
+ function extractDisplayUrl(value) {
1533
+ if (!value || typeof value !== "object") return null;
1534
+ if (typeof value.presigned_url === "string" && value.presigned_url) {
1535
+ return value.presigned_url;
1536
+ }
1537
+ return null;
1538
+ }
1516
1539
  var SignatureUploadWidget = ({ fieldId }) => {
1517
- const { fieldDef, setValue, setTouched, error, disabled, touched } = useField(fieldId);
1540
+ const { fieldDef, setValue, setTouched, error, disabled, touched, value } = useField(fieldId);
1518
1541
  const store = useFormStore();
1519
1542
  const { state } = useForm();
1520
1543
  const canvasRef = useRef(null);
@@ -1523,13 +1546,14 @@ var SignatureUploadWidget = ({ fieldId }) => {
1523
1546
  const lastTimeRef = useRef(null);
1524
1547
  const lastWidthRef = useRef(2);
1525
1548
  const [isDrawing, setIsDrawing] = useState(false);
1526
- const [hasSignature, setHasSignature] = useState(false);
1549
+ const [hasDrawing, setHasDrawing] = useState(false);
1527
1550
  const [isUploading, setIsUploading] = useState(false);
1551
+ const [isConfirmed, setIsConfirmed] = useState(false);
1528
1552
  const showError = !!error && (touched || state.submitAttempted);
1529
- if (!fieldDef) return null;
1530
- const uploadHandler = store.getUploadHandler();
1531
- if (!uploadHandler) return null;
1553
+ const displayUrl = useMemo(() => extractDisplayUrl(value), [value]);
1554
+ const showPreview = displayUrl !== null;
1532
1555
  useEffect(() => {
1556
+ if (showPreview) return;
1533
1557
  const canvas = canvasRef.current;
1534
1558
  if (!canvas) return;
1535
1559
  const ctx = canvas.getContext("2d");
@@ -1547,7 +1571,9 @@ var SignatureUploadWidget = ({ fieldId }) => {
1547
1571
  ctx.strokeStyle = "#000";
1548
1572
  ctx.fillStyle = "#fff";
1549
1573
  ctx.fillRect(0, 0, width, height);
1550
- }, []);
1574
+ }, [showPreview]);
1575
+ const uploadHandler = store.getUploadHandler();
1576
+ if (!fieldDef || !uploadHandler) return null;
1551
1577
  const getPoint = (event) => {
1552
1578
  const canvas = canvasRef.current;
1553
1579
  const rect = canvas.getBoundingClientRect();
@@ -1560,6 +1586,7 @@ var SignatureUploadWidget = ({ fieldId }) => {
1560
1586
  const startDrawing = (event) => {
1561
1587
  if (disabled) return;
1562
1588
  setIsDrawing(true);
1589
+ setIsConfirmed(false);
1563
1590
  setTouched();
1564
1591
  const ctx = canvasRef.current?.getContext("2d");
1565
1592
  if (!ctx) return;
@@ -1601,23 +1628,31 @@ var SignatureUploadWidget = ({ fieldId }) => {
1601
1628
  lastPointRef.current = smoothed;
1602
1629
  lastTimeRef.current = now;
1603
1630
  lastWidthRef.current = width;
1604
- setHasSignature(true);
1631
+ setHasDrawing(true);
1605
1632
  };
1606
1633
  const stopDrawing = () => {
1607
1634
  setIsDrawing(false);
1608
1635
  lastPointRef.current = null;
1609
1636
  lastTimeRef.current = null;
1610
1637
  };
1611
- const clearCanvas = () => {
1638
+ const clearSignature = () => {
1639
+ if (showPreview) {
1640
+ setValue(null);
1641
+ setTouched();
1642
+ return;
1643
+ }
1612
1644
  const canvas = canvasRef.current;
1613
1645
  const ctx = canvas?.getContext("2d");
1614
- if (!canvas || !ctx) return;
1615
- ctx.fillStyle = "#fff";
1616
- ctx.fillRect(0, 0, 400, 200);
1646
+ if (canvas && ctx) {
1647
+ ctx.fillStyle = "#fff";
1648
+ ctx.fillRect(0, 0, 400, 200);
1649
+ }
1617
1650
  pathRef.current = "";
1618
1651
  lastPointRef.current = null;
1619
- setHasSignature(false);
1652
+ setHasDrawing(false);
1653
+ setIsConfirmed(false);
1620
1654
  setValue(null);
1655
+ setTouched();
1621
1656
  };
1622
1657
  const generateSVG = () => `
1623
1658
  <svg width="400" height="200" xmlns="http://www.w3.org/2000/svg">
@@ -1630,7 +1665,7 @@ var SignatureUploadWidget = ({ fieldId }) => {
1630
1665
  fill="none"/>
1631
1666
  </svg>`.trim();
1632
1667
  const confirmSignature = async () => {
1633
- if (!hasSignature) return;
1668
+ if (!hasDrawing) return;
1634
1669
  setIsUploading(true);
1635
1670
  setTouched();
1636
1671
  try {
@@ -1639,55 +1674,95 @@ var SignatureUploadWidget = ({ fieldId }) => {
1639
1674
  const filename = `signature_${Date.now()}.svg`;
1640
1675
  const key = await uploadHandler(blob, filename);
1641
1676
  setValue(key);
1677
+ setIsConfirmed(true);
1678
+ setHasDrawing(false);
1679
+ } catch (err) {
1680
+ console.error("[SignatureUpload] Upload failed:", err);
1642
1681
  } finally {
1643
1682
  setIsUploading(false);
1644
1683
  }
1645
1684
  };
1685
+ const canClear = showPreview ? !disabled : hasDrawing || isConfirmed;
1646
1686
  return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1647
1687
  /* @__PURE__ */ jsxs(Label, { children: [
1648
1688
  fieldDef.label,
1649
1689
  fieldDef.required && /* @__PURE__ */ jsx("span", { className: "text-red-500", children: "*" })
1650
1690
  ] }),
1651
- /* @__PURE__ */ jsxs("div", { className: "relative", children: [
1652
- /* @__PURE__ */ jsx(
1653
- "canvas",
1654
- {
1655
- ref: canvasRef,
1656
- className: "border border-gray-200 rounded cursor-crosshair touch-none",
1657
- onMouseDown: startDrawing,
1658
- onMouseMove: draw,
1659
- onMouseUp: stopDrawing,
1660
- onMouseLeave: stopDrawing,
1661
- onTouchStart: startDrawing,
1662
- onTouchMove: draw,
1663
- onTouchEnd: stopDrawing
1664
- }
1665
- ),
1666
- /* @__PURE__ */ jsx(
1667
- Button,
1668
- {
1669
- type: "button",
1670
- size: "icon",
1671
- variant: "ghost",
1672
- className: "absolute top-2 left-2 bg-white/80",
1673
- onClick: clearCanvas,
1674
- disabled: !hasSignature,
1675
- children: /* @__PURE__ */ jsx(Trash2, { className: "h-4 w-4" })
1676
- }
1677
- )
1678
- ] }),
1679
- /* @__PURE__ */ jsxs(
1680
- Button,
1681
- {
1682
- type: "button",
1683
- onClick: confirmSignature,
1684
- disabled: !hasSignature || isUploading,
1685
- className: "text-xs",
1686
- children: [
1687
- /* @__PURE__ */ jsx(Check, { className: "h-4 w-4 mr-1" }),
1688
- isUploading ? "Saving\u2026" : "Confirm"
1689
- ]
1690
- }
1691
+ showPreview ? (
1692
+ /* ── Preview mode: existing signature from backend ── */
1693
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
1694
+ /* @__PURE__ */ jsx("div", { className: "border border-gray-200 rounded bg-white flex items-center justify-start p-2", children: /* @__PURE__ */ jsx(
1695
+ "img",
1696
+ {
1697
+ src: displayUrl,
1698
+ alt: "Signature",
1699
+ className: "object-contain max-w-full",
1700
+ style: { width: 400, height: 200 }
1701
+ }
1702
+ ) }),
1703
+ /* @__PURE__ */ jsx(
1704
+ Button,
1705
+ {
1706
+ type: "button",
1707
+ size: "icon",
1708
+ variant: "ghost",
1709
+ className: "absolute top-2 left-2 bg-white/80 hover:bg-red-50",
1710
+ onClick: clearSignature,
1711
+ disabled: !canClear,
1712
+ title: "Delete signature and draw a new one",
1713
+ children: /* @__PURE__ */ jsx(Trash2, { className: "h-4 w-4" })
1714
+ }
1715
+ )
1716
+ ] })
1717
+ ) : (
1718
+ /* ── Canvas mode: drawing / confirmed ── */
1719
+ /* @__PURE__ */ jsxs(Fragment, { children: [
1720
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
1721
+ /* @__PURE__ */ jsx(
1722
+ "canvas",
1723
+ {
1724
+ ref: canvasRef,
1725
+ className: `border rounded touch-none ${isConfirmed ? "border-green-300 cursor-default" : "border-gray-200 cursor-crosshair"}`,
1726
+ onMouseDown: startDrawing,
1727
+ onMouseMove: draw,
1728
+ onMouseUp: stopDrawing,
1729
+ onMouseLeave: stopDrawing,
1730
+ onTouchStart: startDrawing,
1731
+ onTouchMove: draw,
1732
+ onTouchEnd: stopDrawing
1733
+ }
1734
+ ),
1735
+ /* @__PURE__ */ jsx(
1736
+ Button,
1737
+ {
1738
+ type: "button",
1739
+ size: "icon",
1740
+ variant: "ghost",
1741
+ className: "absolute top-2 left-2 bg-white/80",
1742
+ onClick: clearSignature,
1743
+ disabled: !canClear,
1744
+ title: isConfirmed ? "Clear saved signature" : "Clear canvas",
1745
+ children: /* @__PURE__ */ jsx(Trash2, { className: "h-4 w-4" })
1746
+ }
1747
+ )
1748
+ ] }),
1749
+ isConfirmed ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm text-green-600", children: [
1750
+ /* @__PURE__ */ jsx(Check, { className: "h-4 w-4" }),
1751
+ /* @__PURE__ */ jsx("span", { children: "Signature saved" })
1752
+ ] }) : /* @__PURE__ */ jsxs(
1753
+ Button,
1754
+ {
1755
+ type: "button",
1756
+ onClick: confirmSignature,
1757
+ disabled: !hasDrawing || isUploading,
1758
+ className: "text-xs",
1759
+ children: [
1760
+ /* @__PURE__ */ jsx(Check, { className: "h-4 w-4 mr-1" }),
1761
+ isUploading ? "Saving\u2026" : "Confirm"
1762
+ ]
1763
+ }
1764
+ )
1765
+ ] })
1691
1766
  ),
1692
1767
  showError && /* @__PURE__ */ jsx("p", { className: "text-xs text-red-500", children: error })
1693
1768
  ] });
@@ -2245,7 +2320,7 @@ var ReadOnlyText = ({ fieldDef, value }) => {
2245
2320
  className: "prose prose-sm max-w-none text-gray-900 border border-gray-200 rounded-md p-3 bg-gray-50",
2246
2321
  dangerouslySetInnerHTML: { __html: displayValue }
2247
2322
  }
2248
- ) : /* @__PURE__ */ jsx("div", { className: "text-gray-900 border border-gray-200 rounded-md p-3 bg-gray-50 min-h-[2.5rem] flex items-center", children: displayValue || /* @__PURE__ */ jsx("span", { className: "text-gray-400 italic", children: "No value" }) })
2323
+ ) : /* @__PURE__ */ jsx("div", { className: "text-gray-900 border border-gray-200 rounded-md p-3 bg-gray-50 min-h-[2.5rem] whitespace-pre-wrap", children: displayValue || /* @__PURE__ */ jsx("span", { className: "text-gray-400 italic", children: "No value" }) })
2249
2324
  ] });
2250
2325
  };
2251
2326
  var ReadOnlySelect = ({ fieldDef, value }) => {