@forms.expert/sdk 0.4.2 → 0.4.4

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.
@@ -94,7 +94,11 @@ interface FormStyling {
94
94
  formWidth?: 'narrow' | 'medium' | 'wide' | 'full';
95
95
  fieldLayout?: 'stacked' | 'inline';
96
96
  fieldBorderStyle?: 'full' | 'bottom';
97
+ fieldBorderColor?: string;
97
98
  fieldBorderRadius?: 'none' | 'small' | 'medium' | 'large' | 'full';
99
+ fieldPaddingX?: number;
100
+ fieldPaddingY?: number;
101
+ separatorColor?: string;
98
102
  buttonColor?: string;
99
103
  buttonText?: string;
100
104
  buttonRadius?: 'none' | 'small' | 'medium' | 'large' | 'full';
@@ -94,7 +94,11 @@ interface FormStyling {
94
94
  formWidth?: 'narrow' | 'medium' | 'wide' | 'full';
95
95
  fieldLayout?: 'stacked' | 'inline';
96
96
  fieldBorderStyle?: 'full' | 'bottom';
97
+ fieldBorderColor?: string;
97
98
  fieldBorderRadius?: 'none' | 'small' | 'medium' | 'large' | 'full';
99
+ fieldPaddingX?: number;
100
+ fieldPaddingY?: number;
101
+ separatorColor?: string;
98
102
  buttonColor?: string;
99
103
  buttonText?: string;
100
104
  buttonRadius?: 'none' | 'small' | 'medium' | 'large' | 'full';
@@ -1058,7 +1058,7 @@ function FormsExpertForm({
1058
1058
  form[data-fe-scope="${formScopeId}"] button[type="submit"]:active:not(:disabled) { filter: brightness(0.85); transform: scale(0.98); }
1059
1059
  form[data-fe-scope="${formScopeId}"] a[href]:hover { filter: brightness(0.9); }
1060
1060
  form[data-fe-scope="${formScopeId}"] a[href]:active { filter: brightness(0.85); }
1061
- form[data-fe-scope="${formScopeId}"] input:focus,
1061
+ form[data-fe-scope="${formScopeId}"] input:not([type="checkbox"]):not([type="radio"]):focus,
1062
1062
  form[data-fe-scope="${formScopeId}"] textarea:focus,
1063
1063
  form[data-fe-scope="${formScopeId}"] select:focus {
1064
1064
  outline: none !important;
@@ -1282,11 +1282,13 @@ function FormFieldInput({
1282
1282
  const fontSize = getFontSize(styling.fontSize);
1283
1283
  const isInline = styling.labelPosition === "left" || styling.fieldLayout === "inline";
1284
1284
  const isBottomBorder = styling.fieldBorderStyle === "bottom";
1285
+ const fieldPadding = styling.fieldPaddingX != null || styling.fieldPaddingY != null ? `${styling.fieldPaddingY ?? 8}px ${styling.fieldPaddingX ?? 12}px` : "0.5rem 0.75rem";
1286
+ const defaultBorderColor = error ? "#ef4444" : styling.fieldBorderColor || (styling.theme === "dark" ? "#4b5563" : "#d1d5db");
1285
1287
  const inputStyle = {
1286
1288
  width: "100%",
1287
- padding: "0.5rem 0.75rem",
1288
- border: isBottomBorder ? "none" : `1px solid ${error ? "#ef4444" : styling.theme === "dark" ? "#4b5563" : "#d1d5db"}`,
1289
- ...isBottomBorder ? { borderBottom: `1px solid ${error ? "#ef4444" : styling.theme === "dark" ? "#4b5563" : "#d1d5db"}` } : {},
1289
+ padding: fieldPadding,
1290
+ border: isBottomBorder ? "none" : `1px solid ${defaultBorderColor}`,
1291
+ ...isBottomBorder ? { borderBottom: `1px solid ${defaultBorderColor}` } : {},
1290
1292
  borderRadius: isBottomBorder ? 0 : fieldRadius,
1291
1293
  fontSize,
1292
1294
  fontFamily: "inherit",
@@ -1304,7 +1306,7 @@ function FormFieldInput({
1304
1306
  ] });
1305
1307
  }
1306
1308
  if (field.type === "divider") {
1307
- return /* @__PURE__ */ jsx2("hr", { style: { marginBottom: fieldSpacing, border: "none", borderTop: `1px solid ${styling.theme === "dark" ? "#4b5563" : "#d1d5db"}` } });
1309
+ return /* @__PURE__ */ jsx2("hr", { style: { marginBottom: fieldSpacing, border: "none", borderTop: `1px solid ${styling.separatorColor || (styling.theme === "dark" ? "#4b5563" : "#d1d5db")}` } });
1308
1310
  }
1309
1311
  if (field.type === "paragraph") {
1310
1312
  const pSize = field.paragraphFontSize ? `${field.paragraphFontSize}px` : getParagraphSize(styling.paragraphSize);
@@ -1334,7 +1336,7 @@ function FormFieldInput({
1334
1336
  checked: Boolean(value),
1335
1337
  onChange,
1336
1338
  required: field.required,
1337
- style: { width: "1rem", height: "1rem", accentColor: styling.primaryColor, marginTop: "0.125rem", flexShrink: 0 }
1339
+ style: { width: "1rem", height: "1rem", accentColor: styling.primaryColor, marginTop: "0.125rem", flexShrink: 0, backgroundColor: "transparent" }
1338
1340
  }
1339
1341
  ),
1340
1342
  /* @__PURE__ */ jsxs("div", { children: [
@@ -1514,18 +1516,91 @@ function FormFieldInput({
1514
1516
  opt.value
1515
1517
  )) });
1516
1518
  } else if (field.type === "file") {
1517
- fieldEl = /* @__PURE__ */ jsx2(
1518
- "input",
1519
+ const fileValue = value;
1520
+ const formatSize = (size) => size < 1024 ? `${size} B` : size < 1048576 ? `${(size / 1024).toFixed(1)} KB` : `${(size / 1048576).toFixed(1)} MB`;
1521
+ const borderColor = error ? styling.errorColor || "#ef4444" : styling.theme === "dark" ? "#4b5563" : "#d1d5db";
1522
+ const mutedColor = styling.theme === "dark" ? "#9ca3af" : "#6b7280";
1523
+ fieldEl = /* @__PURE__ */ jsxs(
1524
+ "label",
1519
1525
  {
1520
- type: "file",
1521
- id: field.name,
1522
- name: field.name,
1523
- onChange,
1524
- required: field.required,
1525
- accept: field.allowedMimeTypes?.join(","),
1526
- multiple: field.multiple,
1527
- style: inputStyle,
1528
- className: styling.fieldClassName
1526
+ htmlFor: field.name,
1527
+ style: {
1528
+ position: "relative",
1529
+ display: "flex",
1530
+ flexDirection: "column",
1531
+ alignItems: "center",
1532
+ justifyContent: "center",
1533
+ gap: "0.5rem",
1534
+ borderRadius: fieldRadius,
1535
+ border: `2px dashed ${borderColor}`,
1536
+ padding: "1.5rem",
1537
+ cursor: "pointer",
1538
+ transition: "border-color 0.15s, background-color 0.15s",
1539
+ backgroundColor: fileValue ? styling.theme === "dark" ? "rgba(255,255,255,0.05)" : "rgba(0,0,0,0.02)" : "transparent"
1540
+ },
1541
+ onDragOver: (e) => {
1542
+ e.preventDefault();
1543
+ e.currentTarget.style.borderColor = styling.primaryColor || "#3b82f6";
1544
+ e.currentTarget.style.backgroundColor = styling.theme === "dark" ? "rgba(255,255,255,0.08)" : "rgba(0,0,0,0.04)";
1545
+ },
1546
+ onDragLeave: (e) => {
1547
+ e.currentTarget.style.borderColor = borderColor;
1548
+ e.currentTarget.style.backgroundColor = fileValue ? styling.theme === "dark" ? "rgba(255,255,255,0.05)" : "rgba(0,0,0,0.02)" : "transparent";
1549
+ },
1550
+ onDrop: (e) => {
1551
+ e.preventDefault();
1552
+ e.currentTarget.style.borderColor = borderColor;
1553
+ e.currentTarget.style.backgroundColor = "transparent";
1554
+ const file = e.dataTransfer.files?.[0];
1555
+ if (file) onValueChange(field.name, file);
1556
+ },
1557
+ children: [
1558
+ /* @__PURE__ */ jsx2(
1559
+ "input",
1560
+ {
1561
+ id: field.name,
1562
+ name: field.name,
1563
+ type: "file",
1564
+ style: { position: "absolute", width: "1px", height: "1px", overflow: "hidden", clip: "rect(0,0,0,0)", whiteSpace: "nowrap", border: 0 },
1565
+ onChange,
1566
+ required: field.required,
1567
+ accept: field.allowedMimeTypes?.join(","),
1568
+ multiple: field.multiple
1569
+ }
1570
+ ),
1571
+ fileValue ? /* @__PURE__ */ jsxs(Fragment, { children: [
1572
+ /* @__PURE__ */ jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", stroke: styling.primaryColor || "#3b82f6", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1573
+ /* @__PURE__ */ jsx2("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
1574
+ /* @__PURE__ */ jsx2("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" })
1575
+ ] }),
1576
+ /* @__PURE__ */ jsx2("span", { style: { fontSize: "0.875rem", fontWeight: 500, maxWidth: "100%", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: fileValue.name }),
1577
+ /* @__PURE__ */ jsx2("span", { style: { fontSize: "0.75rem", color: mutedColor }, children: formatSize(fileValue.size) }),
1578
+ /* @__PURE__ */ jsx2(
1579
+ "button",
1580
+ {
1581
+ type: "button",
1582
+ style: { fontSize: "0.75rem", color: styling.errorColor || "#ef4444", background: "none", border: "none", cursor: "pointer", marginTop: "0.25rem", textDecoration: "underline" },
1583
+ onClick: (e) => {
1584
+ e.preventDefault();
1585
+ onValueChange(field.name, void 0);
1586
+ },
1587
+ children: "Remove"
1588
+ }
1589
+ )
1590
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1591
+ /* @__PURE__ */ jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", stroke: mutedColor, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1592
+ /* @__PURE__ */ jsx2("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
1593
+ /* @__PURE__ */ jsx2("polyline", { points: "17 8 12 3 7 8" }),
1594
+ /* @__PURE__ */ jsx2("line", { x1: "12", x2: "12", y1: "3", y2: "15" })
1595
+ ] }),
1596
+ /* @__PURE__ */ jsx2("span", { style: { fontSize: "0.875rem", fontWeight: 500 }, children: "Drag & drop a file here, or click to browse" }),
1597
+ field.allowedMimeTypes && field.allowedMimeTypes.length > 0 && /* @__PURE__ */ jsx2("span", { style: { fontSize: "0.75rem", color: mutedColor }, children: field.allowedMimeTypes.join(", ") }),
1598
+ field.maxFileSize && /* @__PURE__ */ jsxs("span", { style: { fontSize: "0.75rem", color: mutedColor }, children: [
1599
+ "Max size: ",
1600
+ formatSize(field.maxFileSize)
1601
+ ] })
1602
+ ] })
1603
+ ]
1529
1604
  }
1530
1605
  );
1531
1606
  } else if (field.type === "name") {