auto-form-builder
Version:
🧱 Vue Auto Form Builder with schema-based generation, themes, validation and localization.
267 lines (266 loc) • 11.4 kB
JavaScript
import { defineComponent as J, reactive as P, ref as G, createElementBlock as r, openBlock as s, normalizeClass as i, withModifiers as H, createElementVNode as p, Fragment as v, renderList as q, createCommentVNode as g, toDisplayString as y, withDirectives as h, createTextVNode as B, vModelDynamic as I, vModelText as _, vModelRadio as K, vModelCheckbox as O, vModelSelect as Q } from "vue";
const W = ["for"], X = {
key: 0,
class: "text-red-500"
}, Y = ["type", "id", "onUpdate:modelValue", "required"], Z = ["id", "onUpdate:modelValue", "required"], ee = ["id", "onChange", "required"], te = {
key: 4,
class: "flex flex-col gap-1"
}, ae = ["id", "value", "name", "onUpdate:modelValue", "required"], ne = ["for"], oe = {
key: 5,
class: "flex items-center gap-2"
}, re = ["id", "onUpdate:modelValue", "required"], se = ["for"], me = ["id", "onUpdate:modelValue", "required"], ue = ["id", "onUpdate:modelValue", "required"], ie = ["id", "onUpdate:modelValue", "required"], le = ["id", "onUpdate:modelValue", "required"], ce = ["value"], de = {
key: 10,
class: "flex items-center gap-2"
}, pe = ["id", "onUpdate:modelValue", "required"], he = ["for"], ye = {
key: 0,
class: "text-red-500"
}, be = {
key: 11,
class: "text-red-500 text-sm mt-1"
}, x = "border rounded px-3 py-2 bg-white text-black dark:bg-gray-800 dark:text-white dark:border-gray-600 focus:outline-none focus:ring focus:border-blue-400", ge = /* @__PURE__ */ J({
__name: "AutoForm",
props: {
schema: {},
submitUrl: {},
themeConfig: {}
},
emits: ["submit"],
setup(w, { emit: c }) {
const k = w, V = c, m = P({}), l = G({});
function z(n, a) {
var b;
const u = a.target;
(b = u == null ? void 0 : u.files) != null && b.length && (m[n] = u.files[0]);
}
k.schema.forEach((n) => {
n.fields.forEach((a) => {
m[a.name] = a.type === "checkbox" ? !1 : "";
});
});
function A() {
l.value = {};
for (const n of k.schema)
for (const a of n.fields) {
const u = m[a.name], b = a.type === "checkbox" ? !u : u === "";
if (a.required && b) {
l.value[a.name] = t("required");
continue;
}
if (a.type === "number") {
const d = Number(u);
a.min !== void 0 && d < a.min && (l.value[a.name] = t("minValue", a.min)), a.max !== void 0 && d > a.max && (l.value[a.name] = t("maxValue", a.max));
}
if (a.type === "text" || a.type === "email" || a.type === "textarea" || a.type === "password") {
const d = String(u).length;
a.min !== void 0 && d < a.min && (l.value[a.name] = t("minLength", a.min)), a.max !== void 0 && d > a.max && (l.value[a.name] = t("maxLength", a.max)), a.pattern && (new RegExp(a.pattern).test(String(u)) || (l.value[a.name] = t("pattern")));
}
}
Object.keys(l.value).length === 0 && (k.submitUrl ? fetch(k.submitUrl, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(m)
}).then(async (n) => {
if (!n.ok) {
const u = await n.text();
throw new Error(u);
}
const a = await n.json();
V("submit", a);
}).catch((n) => {
console.error("خطأ في الإرسال:", n), l.value.form = t("submitFailed") || "فشل إرسال البيانات. يرجى المحاولة لاحقًا.";
}) : V("submit", m));
}
return (n, a) => {
var u, b;
return s(), r("form", {
onSubmit: H(A, ["prevent"]),
class: i(["max-w-md mx-auto space-y-4", (u = n.themeConfig) == null ? void 0 : u.form]),
dir: "rtl"
}, [
(s(!0), r(v, null, q(n.schema, (d) => {
var C;
return s(), r("div", {
key: d.section || Math.random(),
class: "space-y-4"
}, [
d.section ? (s(), r("h2", {
key: 0,
class: i(
((C = n.themeConfig) == null ? void 0 : C.sectionTitle) || "text-xl font-bold mb-2 dark:text-white border-b pb-1"
)
}, y(d.section), 3)) : g("", !0),
(s(!0), r(v, null, q(d.fields, (e) => {
var U, S, E, M, L, T, f, F, N, D, $;
return s(), r("div", {
key: e.name,
class: "flex flex-col gap-1"
}, [
e.type !== "checkbox" && e.type !== "radio" && e.type !== "switch" ? (s(), r("label", {
key: 0,
for: e.name,
class: i(((U = n.themeConfig) == null ? void 0 : U.label) || "font-medium dark:text-white")
}, [
B(y(e.label) + " ", 1),
e.required ? (s(), r("span", X, "*")) : g("", !0)
], 10, W)) : g("", !0),
e.type === "text" || e.type === "email" ? h((s(), r("input", {
key: 1,
type: e.type,
id: e.name,
"onUpdate:modelValue": (o) => m[e.name] = o,
required: e.required,
class: i(((S = n.themeConfig) == null ? void 0 : S.input) || x)
}, null, 10, Y)), [
[I, m[e.name]]
]) : e.type === "password" ? h((s(), r("input", {
key: 2,
type: "password",
id: e.name,
"onUpdate:modelValue": (o) => m[e.name] = o,
required: e.required,
class: i(((E = n.themeConfig) == null ? void 0 : E.input) || x)
}, null, 10, Z)), [
[_, m[e.name]]
]) : e.type === "file" ? (s(), r("input", {
key: 3,
type: "file",
id: e.name,
onChange: (o) => z(e.name, o),
required: e.required,
class: i(((M = n.themeConfig) == null ? void 0 : M.input) || x)
}, null, 42, ee)) : e.type === "radio" ? (s(), r("div", te, [
p("label", {
class: i(((L = n.themeConfig) == null ? void 0 : L.label) || "font-medium dark:text-white mb-1")
}, y(e.label), 3),
(s(!0), r(v, null, q(e.options, (o) => {
var j;
return s(), r("div", {
key: o,
class: "flex items-center gap-2"
}, [
h(p("input", {
type: "radio",
id: `${e.name}-${o}`,
value: o,
name: e.name,
"onUpdate:modelValue": (R) => m[e.name] = R,
required: e.required
}, null, 8, ae), [
[K, m[e.name]]
]),
p("label", {
for: `${e.name}-${o}`,
class: i(((j = n.themeConfig) == null ? void 0 : j.radioLabel) || "text-sm dark:text-white")
}, y(o), 11, ne)
]);
}), 128))
])) : e.type === "switch" ? (s(), r("div", oe, [
h(p("input", {
type: "checkbox",
id: e.name,
"onUpdate:modelValue": (o) => m[e.name] = o,
required: e.required,
class: "accent-blue-600"
}, null, 8, re), [
[O, m[e.name]]
]),
p("label", {
for: e.name,
class: i(((T = n.themeConfig) == null ? void 0 : T.switchLabel) || "text-sm dark:text-white")
}, y(e.label), 11, se)
])) : e.type === "textarea" ? h((s(), r("textarea", {
key: 6,
id: e.name,
"onUpdate:modelValue": (o) => m[e.name] = o,
required: e.required,
rows: "4",
class: i(((f = n.themeConfig) == null ? void 0 : f.textarea) || x + " resize-y")
}, null, 10, me)), [
[_, m[e.name]]
]) : e.type === "number" ? h((s(), r("input", {
key: 7,
type: "number",
id: e.name,
"onUpdate:modelValue": (o) => m[e.name] = o,
required: e.required,
class: i(((F = n.themeConfig) == null ? void 0 : F.input) || x)
}, null, 10, ue)), [
[_, m[e.name]]
]) : e.type === "date" ? h((s(), r("input", {
key: 8,
type: "date",
id: e.name,
"onUpdate:modelValue": (o) => m[e.name] = o,
required: e.required,
class: i(((N = n.themeConfig) == null ? void 0 : N.input) || x)
}, null, 10, ie)), [
[_, m[e.name]]
]) : e.type === "select" ? h((s(), r("select", {
key: 9,
id: e.name,
"onUpdate:modelValue": (o) => m[e.name] = o,
required: e.required,
class: i(((D = n.themeConfig) == null ? void 0 : D.input) || x)
}, [
a[0] || (a[0] = p("option", {
disabled: "",
value: ""
}, "اختر...", -1)),
(s(!0), r(v, null, q(e.options, (o) => (s(), r("option", {
key: o,
value: o
}, y(o), 9, ce))), 128))
], 10, le)), [
[Q, m[e.name]]
]) : e.type === "checkbox" ? (s(), r("div", de, [
h(p("input", {
type: "checkbox",
id: e.name,
"onUpdate:modelValue": (o) => m[e.name] = o,
required: e.required,
class: "accent-blue-600"
}, null, 8, pe), [
[O, m[e.name]]
]),
p("label", {
for: e.name,
class: i((($ = n.themeConfig) == null ? void 0 : $.checkboxLabel) || "text-sm dark:text-white")
}, [
B(y(e.label) + " ", 1),
e.required ? (s(), r("span", ye, "*")) : g("", !0)
], 10, he)
])) : g("", !0),
l.value[e.name] ? (s(), r("p", be, y(l.value[e.name]), 1)) : g("", !0)
]);
}), 128))
]);
}), 128)),
p("button", {
type: "submit",
class: i(
((b = n.themeConfig) == null ? void 0 : b.submitButton) || "bg-blue-600 hover:bg-blue-700 text-white font-semibold py-2 px-4 rounded dark:bg-blue-500 dark:hover:bg-blue-600"
)
}, " إرسال ", 2)
], 34);
};
}
});
function ke(w) {
return w.map((c) => ({
name: c.name,
label: c.label,
type: c.type,
required: c.required ?? !1,
options: c.options ?? [],
min: c.min,
max: c.max,
pattern: c.pattern
}));
}
export {
ge as AutoForm,
ge as default,
ke as parseSchema
};