houseform
Version:
Simple to use React forms, where your validation and UI code live together in harmony.
993 lines (992 loc) • 21.6 kB
JavaScript
import { createContext as He, useContext as Me, memo as Ve, forwardRef as ge, useRef as z, useState as $, useCallback as a, useMemo as G, useImperativeHandle as ye, useLayoutEffect as xe, useEffect as Pe } from "react";
import { jsx as Le } from "react/jsx-runtime";
const ze = {
formFieldsRef: { current: [] },
recomputeErrors: () => {
},
recomputeIsDirty: () => {
},
recomputeIsTouched: () => {
},
recomputeIsValidating: () => {
},
errors: [],
errorsMap: {},
submit: async () => !0,
getFieldValue: () => {
},
deleteField: () => {
},
setIsSubmitted: () => {
},
reset: () => {
},
onChangeListenerRefs: { current: {} },
onBlurListenerRefs: { current: {} },
onMountListenerRefs: { current: {} },
setIsTouched: () => {
},
setIsDirty: () => {
},
isValid: !1,
isDirty: !1,
isTouched: !1,
isValidating: !1,
isSubmitted: !1,
value: {},
recomputeFormValue: () => {
}
}, Ae = He(ze), oe = () => Me(Ae);
function ne(e, n, i) {
return ((u) => u instanceof Object && u.parseAsync)(i) ? i.parseAsync(e) : i(e, n);
}
function se(e) {
return ((i) => i instanceof Object && i.errors)(e) ? e.errors.map((i) => i.message) : [e];
}
const Se = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g, Oe = /\\(\\)?/g, N = (e) => {
const n = [];
return e.replace(Se, (i, l, u, o) => {
const r = u ? o.replace(Oe, "$1") : l || i;
r && n.push(r);
}), n;
}, ve = (e, n, i) => {
const l = N(n);
let u = e;
for (let o = 0; o < l.length; o++) {
const r = l[o];
o === l.length - 1 ? u[r] = i : (u[r] || (u[r] = {}), u = u[r]);
}
return e;
}, je = (e, n) => {
const i = N(n);
let l = e;
for (let u = 0; u < i.length; u++) {
const o = i[u];
if (u === i.length - 1)
return l[o];
l = l[o];
}
};
function Ge(e, n) {
const { onSubmit: i, children: l, memoChild: u, submitWhenInvalid: o = !1 } = e, r = z([]), [_, d] = $(!1), s = a(
(t) => {
const h = r.current, g = N(t).join(".");
return h.find(
(Z) => Z._normalizedDotName === g
);
},
[r]
), C = a(
(t) => {
const h = r.current, g = N(t).join("."), Z = h.find(
(Y) => Y._normalizedDotName === g
);
Z && h.splice(h.indexOf(Z), 1);
},
[r]
), R = z({}), b = z({}), S = z({}), k = a((t) => {
r.current.forEach((h) => {
h.setIsTouched(t);
});
}, []), M = a((t) => {
r.current.forEach((h) => {
h.setIsDirty(t);
});
}, []), [O, I] = $(null), [L, E] = $(null), [p, B] = $(null), [F, T] = $(null), [x, w] = $(null), [P, H] = $(null), [m, j] = $(null), f = z(!1), D = z(!1), X = z(!1), y = z(!1), c = z(!1), v = z(!1), V = a(() => r.current.reduce((t, h) => t.concat(h.errors), []), [r]), K = a(() => {
const t = {};
return r.current.forEach((h) => {
const g = h.props.name;
t[g] = h.errors;
}), t;
}, [r]), q = a(() => r.current.length === 0 ? !0 : r.current.every((t) => t.errors.length === 0), [r]), A = a(
(t) => r.current.some((h) => !!h[t]),
[r]
), J = a(() => r.current.reduce((t, h) => (ve(t, h.props.name, h.value), t), {}), [r]), ie = a(() => {
if (f.current) {
const t = V(), h = K();
I(t), E(h);
}
if (D.current) {
const t = q();
B(t);
}
}, [V, K, q]), Te = a(() => {
if (X.current) {
const t = A("isDirty");
T(t);
}
}, [A]), Fe = a(() => {
if (y.current) {
const t = A("isTouched");
w(t);
}
}, [A]), De = a(() => {
if (c.current) {
const t = J();
H(t);
}
}, [J]), Ee = a(() => {
if (v.current) {
const t = A("isValidating");
j(t);
}
}, [A]), ue = a(() => {
if (f.current = !0, O === null) {
const t = V();
return I(t), t;
}
return O;
}, [O, V]), ae = a(() => {
if (f.current = !0, L === null) {
const t = K();
return E(t), t;
}
return L;
}, [L, K]), ce = a(() => {
if (D.current = !0, p === null) {
const t = q();
return B(t), t;
}
return p;
}, [p, q]), le = a(() => {
if (F === null) {
X.current = !0;
const t = A("isDirty");
return T(t), t;
}
return F;
}, [F, A]), de = a(() => {
if (x === null) {
y.current = !0;
const t = A("isTouched");
return w(t), t;
}
return x;
}, [x, A]), me = a(() => {
if (P === null) {
c.current = !0;
const t = J();
return H(t), t;
}
return P;
}, [P, J]), he = a(() => {
r.current.sort((t, h) => N(h.props.name).length - N(t.props.name).length).forEach((t) => {
const h = t.props.resetWithValue ?? t.props.initialValue, g = (Y) => !!Y.setValues, Z = (Y) => !Y.setValues;
t.setErrors([]), t.setIsTouched(!1), t.setIsDirty(!1), g(t) ? t.setValues({
__value: h || [],
__isResetting: !0
}) : Z(t) && t.setValue({
__value: h || "",
__isResetting: !0
});
}), I([]), B(!1), T(!1), w(!1);
}, [w, T, I]), fe = a(() => {
if (m === null) {
v.current = !0;
const t = A("isValidating");
return j(t), t;
}
return m;
}, [m, A]), Q = G(() => ({
getFieldValue: s,
deleteField: C,
onChangeListenerRefs: R,
onBlurListenerRefs: b,
onMountListenerRefs: S,
isSubmitted: _,
setIsSubmitted: d,
formFieldsRef: r,
setIsTouched: k,
setIsDirty: M,
recomputeErrors: ie,
recomputeIsDirty: Te,
recomputeIsTouched: Fe,
recomputeFormValue: De,
recomputeIsValidating: Ee,
reset: he,
get errors() {
return ue();
},
get errorsMap() {
return ae();
},
get isValid() {
return ce();
},
get isDirty() {
return le();
},
get isTouched() {
return de();
},
get value() {
return me();
},
get isValidating() {
return fe();
},
submit: () => Promise.resolve(!0)
}), [
s,
C,
_,
k,
M,
ie,
Te,
Fe,
De,
Ee,
ue,
ae,
ce,
le,
de,
me,
fe,
he
]), we = a(async () => {
d(!0);
const t = {}, h = await Promise.all(
r.current.map(async (g) => {
const Z = async (U) => {
const ee = g.props[U];
if (!ee)
return !0;
try {
return U === "onSubmitValidate" && g._setIsValidating(!0), await ne(g.value, Q, ee), !0;
} catch (_e) {
return g.setErrors(se(_e)), !1;
} finally {
U === "onSubmitValidate" && g._setIsValidating(!1);
}
}, Y = async (U) => {
const ee = g.props[U];
if (!ee)
return !0;
try {
return await ne(g.value, Q, ee), !0;
} catch (_e) {
return g.setHints(se(_e)), !1;
}
};
if (g.setHints([]), await Y("onMountHint"), await Y("onChangeHint"), await Y("onBlurHint"), await Y("onSubmitHint"), g.setErrors([]), !await Z("onMountValidate") || !await Z("onChangeValidate") || !await Z("onBlurValidate") || !await Z("onSubmitValidate"))
return !1;
const ke = await (async () => {
const U = g.props.onSubmitTransform;
if (!U)
return g.value;
try {
return await ne(g.value, Q, U);
} catch (ee) {
g.setErrors(se(ee));
}
})();
return g.errors.length > 0 ? !1 : (ve(t, g.props.name, ke), !0);
})
);
return !o && !h.every((g) => !!g) ? !1 : (i == null || i(t, Q), !0);
}, [Q, i, o]), te = G(() => {
const t = [
"errors",
"errorsMap",
"isValid",
"isDirty",
"isTouched",
"isValidating",
"submit",
"value",
"reset"
], h = {
reset: he,
submit: we,
get errors() {
return ue();
},
get errorsMap() {
return ae();
},
get isValid() {
return ce();
},
get isDirty() {
return le();
},
get isTouched() {
return de();
},
get value() {
return me();
},
get isValidating() {
return fe();
}
};
for (const g of Object.keys(Q)) {
if (t.includes(g))
continue;
const Z = g;
h[Z] = Q[Z];
}
return h;
}, [
he,
Q,
ue,
ae,
le,
de,
ce,
me,
fe,
we
]);
ye(n, () => te, [te]);
const be = G(
() => l(te),
// eslint-disable-next-line react-hooks/exhaustive-deps
u ? u.concat(te) : [l, te]
);
return /* @__PURE__ */ Le(Ae.Provider, { value: te, children: be });
}
const tt = Ve(ge(Ge)), pe = typeof window < "u", W = pe ? xe : Pe, re = (e) => !Array.isArray(e) && e !== null && typeof e == "object" && "__isResetting" in e;
function Ce({
listenTo: e,
runFieldValidation: n,
valueRef: i
}) {
const l = oe();
W(() => {
if (!e || e.length === 0)
return;
function u() {
n("onChangeValidate", i.current);
}
function o() {
n("onBlurValidate", i.current);
}
function r() {
n("onMountValidate", i.current);
}
function _(s, C, R) {
return l[s].current[C] = l[s].current[C] ?? [], l[s].current[C].push(R), () => {
l[s].current[C].splice(
l[s].current[C].indexOf(R),
1
);
};
}
const d = e.flatMap((s) => {
const C = _(
"onChangeListenerRefs",
s,
u
), R = _(
"onBlurListenerRefs",
s,
o
), b = _(
"onMountListenerRefs",
s,
r
);
return [C, R, b];
});
return () => d.forEach((s) => s());
}, [l, e, n, i]);
}
const Re = ({
initialValue: e,
props: n
}) => {
const { name: i } = n, l = G(() => N(i).join("."), [i]), u = oe(), o = u.formFieldsRef.current.find(
(m) => m._normalizedDotName === l
), [r, _] = $((o == null ? void 0 : o.errors) ?? []), [d, s] = $((o == null ? void 0 : o.hints) ?? []), [C, R] = $(
(o == null ? void 0 : o.isTouched) ?? !1
), [b, S] = $(
(o == null ? void 0 : o.isDirty) ?? !1
), [k, M] = $(
(o == null ? void 0 : o.isValidating) ?? !1
), O = u.isSubmitted, I = a(
(m, j) => {
let f = n.onChangeValidate;
m === "onBlurValidate" && (f = n == null ? void 0 : n.onBlurValidate), m === "onMountValidate" && (f = n == null ? void 0 : n.onMountValidate), f && (M(!0), ne(j, u, f).then(() => {
_([]);
}).catch((D) => {
_(se(D));
}).finally(() => {
M(!1);
}));
},
[u, n]
), L = a(
(m, j) => {
let f = n.onChangeHint;
m === "onBlurHint" && (f = n == null ? void 0 : n.onBlurHint), m === "onMountHint" && (f = n == null ? void 0 : n.onMountHint), f && (M(!0), ne(j, u, f).then(() => {
s([]);
}).catch((D) => {
s(se(D));
}).finally(() => {
M(!1);
}));
},
[u, n]
), E = n.initialValue ?? e, p = z(!1), [B, F] = $((o == null ? void 0 : o.value) ?? E);
W(() => {
p.current || (p.current = !0, L("onMountHint", E), I("onMountValidate", E));
});
const T = z(B);
T.current = B;
const x = a(
(m) => {
F((j) => {
const f = re(m) && m.__isResetting, D = re(m) ? m.__value : m, y = ((c) => typeof c == "function")(D) ? D(j) : D;
return f || (S(y !== E), setTimeout(() => {
var c;
(c = u.onChangeListenerRefs.current[n.name]) == null || c.forEach(
(v) => v()
);
}, 0), L("onChangeHint", y), I("onChangeValidate", y)), y;
});
},
[
E,
I,
L,
u.onChangeListenerRefs,
n.name
]
), w = G(() => r.length === 0, [r]), P = a(
(m) => {
I(m, T.current);
},
[I, T]
), H = a(
(m) => {
L(m, T.current);
},
[L, T]
);
return {
value: B,
setErrors: _,
errors: r,
hints: d,
setHints: s,
setIsDirty: S,
setIsTouched: R,
setValue: x,
isTouched: C,
isDirty: b,
isValid: w,
isValidating: k,
isSubmitted: O,
runFieldValidation: I,
runFieldHintCheck: L,
valueRef: T,
validate: P,
checkHint: H,
_normalizedDotName: l,
_setIsValidating: M
};
}, Ie = ({
fieldInstanceRef: e,
props: n,
value: i,
errors: l,
isDirty: u,
isValid: o,
isTouched: r,
isValidating: _,
preserveValue: d
}) => {
const s = oe();
W(() => {
e.current.props = n;
const C = e.current, R = s.formFieldsRef.current;
if (s.deleteField(n.name), R.push(C), !d)
return () => {
s.deleteField(n.name);
};
}, [
s.deleteField,
s.formFieldsRef,
e,
n,
d
]), W(() => {
e.current.value = i;
}, [e, i]), W(() => {
e.current.errors = l;
}, [l, e]), W(() => {
e.current.isDirty = u;
}, [u, e]), W(() => {
e.current.isValid = o;
}, [o, e]), W(() => {
e.current.isTouched = r;
}, [r, e]), W(() => {
e.current.isValidating = _;
}, [_, e]), W(() => (s.recomputeErrors(), () => {
s.recomputeErrors();
}), [l, s.recomputeErrors]), W(() => (s.recomputeIsTouched(), () => {
s.recomputeIsTouched();
}), [r, s.recomputeIsTouched]), W(() => (s.recomputeIsDirty(), () => {
s.recomputeIsDirty();
}), [u, s.recomputeIsDirty]), W(() => (s.recomputeIsValidating(), () => {
s.recomputeIsValidating();
}), [_, s.recomputeIsValidating]), W(() => (s.recomputeFormValue(), () => {
s.recomputeFormValue();
}), [i, s.recomputeFormValue]);
};
function Ze(e, n) {
const i = oe(), { children: l, memoChild: u, preserveValue: o } = e, {
value: r,
setErrors: _,
errors: d,
hints: s,
setHints: C,
setIsDirty: R,
setIsTouched: b,
setValue: S,
isTouched: k,
isDirty: M,
isValid: O,
isValidating: I,
isSubmitted: L,
runFieldValidation: E,
runFieldHintCheck: p,
valueRef: B,
_normalizedDotName: F,
_setIsValidating: T,
validate: x,
checkHint: w
} = Re({
props: e,
initialValue: ""
});
Ce({
listenTo: e.listenTo,
runFieldValidation: E,
valueRef: B
});
const P = a(() => {
b(!0), setTimeout(() => {
var f;
(f = i.onBlurListenerRefs.current[e.name]) == null || f.forEach((D) => D());
}, 0), p("onBlurHint", B.current), E("onBlurValidate", B.current);
}, [
i.onBlurListenerRefs,
e.name,
E,
b,
B
]), H = G(() => ({
value: r,
props: e,
setErrors: _,
errors: d,
setIsDirty: R,
setIsTouched: b,
setValue: S,
isTouched: k,
isDirty: M,
isValid: O,
isValidating: I,
isSubmitted: L,
onBlur: P,
_normalizedDotName: F,
_setIsValidating: T,
validate: x,
hints: s,
setHints: C,
checkHint: w
}), [
e,
r,
_,
d,
R,
b,
S,
k,
M,
O,
I,
L,
P,
F,
T,
x,
s,
C,
w
]), m = z(H);
return Ie({
fieldInstanceRef: m,
props: e,
value: r,
errors: d,
isValid: O,
isDirty: M,
isTouched: k,
isValidating: I,
preserveValue: o
}), ye(n, () => H, [H]), G(
() => l(H),
u ? u.concat(H) : [l, H]
);
}
const rt = Ve(ge(Ze)), We = {
value: [],
setValue: () => {
},
setValues: () => {
},
props: {
name: ""
},
errors: [],
setErrors: () => {
},
setIsDirty: () => {
},
isValid: !1,
isValidating: !1,
setIsTouched: () => {
},
isDirty: !1,
isTouched: !1,
_normalizedDotName: "",
add: () => {
},
remove: () => {
},
insert: () => {
},
move: () => {
},
swap: () => {
},
replace: () => {
},
validate: () => {
},
_setIsValidating: () => {
},
hints: [],
setHints: () => {
},
checkHint: () => {
}
}, Be = He(We), $e = () => Me(Be);
function Ke(e, n) {
const { children: i, memoChild: l, preserveValue: u } = e, {
value: o,
errors: r,
setErrors: _,
setValue: d,
hints: s,
setHints: C,
isTouched: R,
isDirty: b,
isValid: S,
isValidating: k,
_normalizedDotName: M,
_setIsValidating: O,
runFieldValidation: I,
valueRef: L,
setIsDirty: E,
setIsTouched: p,
validate: B,
checkHint: F
} = Re({
props: e,
initialValue: []
});
Ce({
listenTo: e.listenTo,
runFieldValidation: I,
valueRef: L
});
const T = a(
(y, c) => {
const v = re(c) && c.__isResetting, V = re(c) ? c.__value : c, K = (q) => {
const A = [...q];
return A[y] = V, A;
};
if (v) {
d({
__value: K,
__isResetting: !0
});
return;
}
d(K);
},
[d]
), x = a(
(y) => {
d((c) => [...c, y]);
},
[d]
), w = a(
(y) => {
d((c) => {
const v = [...c];
return v.splice(y, 1), v;
});
},
[d]
), P = a(
(y, c) => {
d((v) => {
const V = [...v];
return V.splice(y, 0, c), V;
});
},
[d]
), H = a(
(y, c) => {
d((v) => {
const V = [...v], K = V[y];
return V.splice(y, 1), V.splice(c, 0, K), V;
});
},
[d]
), m = a(
(y, c) => {
d((v) => {
const V = [...v];
return V[y] = c, V;
});
},
[d]
), j = a(
(y, c) => {
d((v) => {
const V = [...v], K = V[y];
return V[y] = V[c], V[c] = K, V;
});
},
[d]
), f = G(() => ({
value: o,
add: x,
move: H,
insert: P,
remove: w,
swap: j,
replace: m,
setValue: T,
props: e,
_normalizedDotName: M,
_setIsValidating: O,
errors: r,
setErrors: _,
hints: s,
setHints: C,
isValid: S,
isValidating: k,
setIsDirty: E,
isDirty: b,
setIsTouched: p,
isTouched: R,
setValues: d,
validate: B,
checkHint: F
}), [
o,
x,
H,
P,
w,
j,
m,
T,
e,
M,
O,
r,
_,
s,
C,
S,
k,
E,
b,
p,
R,
d,
B,
F
]), D = z(f);
Ie({
value: o,
fieldInstanceRef: D,
isValid: S,
isValidating: k,
isDirty: b,
isTouched: R,
props: e,
errors: r,
preserveValue: u
}), ye(n, () => f, [f]);
const X = G(
() => i(f),
l ? l.concat(f) : [i, f]
);
return /* @__PURE__ */ Le(Be.Provider, { value: f, children: X });
}
const nt = Ve(ge(Ke));
function Ye(e, n) {
const { children: i, memoChild: l, preserveValue: u } = e, {
_normalizedDotName: o,
_setIsValidating: r,
errors: _,
setErrors: d,
hints: s,
setHints: C,
runFieldValidation: R,
runFieldHintCheck: b,
isValid: S,
isValidating: k,
isTouched: M,
isSubmitted: O,
setIsTouched: I,
isDirty: L,
setIsDirty: E,
validate: p,
checkHint: B
} = Re({
props: e,
initialValue: ""
}), F = $e(), T = oe(), x = G(() => {
const c = N(F.props.name), v = N(e.name);
for (const V of c)
if (V !== v.shift())
throw new Error(
"You must prepend the FieldArrayItem name with the name of the parent FieldArray"
);
return v;
}, [F.props.name, e.name]), w = G(() => parseInt(x[0]), [x]), P = G(() => x.slice(1), [x]), H = G(() => je(F.value[w], P.join(".")), [P, F.value, w]), m = z(H);
m.current = H, Ce({
listenTo: e.listenTo,
runFieldValidation: R,
valueRef: m
});
const j = a(
(c) => {
const v = re(c) && c.__isResetting, V = re(c) ? c.__value : c, q = ((J) => typeof J == "function")(V) ? V(F.value[w]) : V, A = { ...F.value[w] };
if (ve(A, P.join("."), q), v) {
F.setValue(w, {
__value: A,
__isResetting: !0
});
return;
}
F.setValue(w, A), E(!0), setTimeout(() => {
var J;
(J = T.onChangeListenerRefs.current[e.name]) == null || J.forEach(
(ie) => ie()
);
}, 0), R("onChangeValidate", q);
},
[
P,
F,
T.onChangeListenerRefs,
w,
e.name,
R,
E
]
), f = a(() => {
I(!0), setTimeout(() => {
var c;
(c = T.onBlurListenerRefs.current[e.name]) == null || c.forEach((v) => v());
}, 0), b("onBlurHint", m.current), R("onBlurValidate", m.current);
}, [
T.onBlurListenerRefs,
e.name,
b,
R,
I
]), D = G(() => ({
setValue: j,
errors: _,
value: H,
_normalizedDotName: o,
_setIsValidating: r,
onBlur: f,
props: e,
isTouched: M,
isValid: S,
isDirty: L,
isValidating: k,
setIsDirty: E,
setErrors: d,
setIsTouched: I,
validate: p,
isSubmitted: O,
hints: s,
setHints: C,
checkHint: B
}), [
j,
_,
H,
o,
r,
f,
e,
M,
S,
L,
k,
E,
d,
I,
p,
O,
s,
C,
B
]), X = z(D);
return Ie({
fieldInstanceRef: X,
props: e,
value: H,
errors: _,
isValid: S,
isDirty: L,
isTouched: M,
isValidating: k,
preserveValue: u
}), ye(n, () => D, [D]), G(
() => i(D),
l ? l.concat(D) : [i, D]
);
}
const st = Ve(ge(Ye));
export {
rt as Field,
nt as FieldArray,
Be as FieldArrayContext,
st as FieldArrayItem,
Ye as FieldArrayItemComp,
tt as Form,
Ae as FormContext,
ve as fillPath,
je as getPath,
se as getValidationError,
We as initialFieldArrayContext,
ze as initialFormContext,
re as isInternal,
N as stringToPath,
$e as useFieldArrayContext,
Re as useFieldLike,
Ie as useFieldLikeSync,
oe as useFormContext,
Ce as useListenToListenToArray,
ne as validate
};