react-form-krafter
Version:
A flexible form engine for React powered by Standard Schema
693 lines (692 loc) • 15 kB
JavaScript
import { jsx as V, jsxs as G } from "react/jsx-runtime";
import { createContext as B, useMemo as i, useContext as T, memo as ne, useRef as H, useCallback as F, useState as k, useEffect as z, useImperativeHandle as J } from "react";
const K = B(null), fe = ({
children: e,
components: n,
settings: s = {}
}) => {
const f = i(
() => ({ components: n, settings: s }),
[n, s]
);
return /* @__PURE__ */ V(K.Provider, { value: f, children: e });
}, re = () => {
const e = T(K);
if (!e)
throw new Error("useRegister must be used within a Register");
return e;
};
async function X(e, n) {
let s = e["~standard"].validate(n);
return s instanceof Promise && (s = await s), s.issues ? s.issues : !0;
}
const M = B(null), Y = B(
null
), Z = B(
null
);
function se() {
const e = T(M);
if (!e)
throw new Error("useInternalForm must be used within a Form");
return e;
}
function oe({ field: e }) {
const { components: n, settings: s } = re(), {
onUpdate: f,
onChange: v,
setFieldsState: u,
setFieldsInfo: l,
fieldsState: y,
fieldsInfo: t,
schema: R,
didSubmitOnce: D
} = se(), C = H(null), g = H(null), c = i(
() => n?.find((o) => o.type === e.type)?.render,
[n, e.type]
), E = F(
async (o, w, S, x) => {
if ((x == null || x == "") && S.required)
l((m) => ({
...m,
errors: {
...m.errors,
[S.name]: s?.labels?.required || "REQUIRED_FIELD_ERROR"
}
}));
else if (R) {
const m = await X(
R,
o
);
if (m instanceof Array) {
const d = m.reduce(
(P, Q) => {
const $ = Q.path.join(".");
return $ !== S.name || x != null && (P[$] = Q.message), P;
},
{ ...w.errors, [S.name]: null }
);
l((P) => ({
...P,
errors: d
}));
} else m === !0 && l((d) => ({
...d,
errors: {}
}));
}
},
[R, l, s?.labels?.required]
), O = F(
async ({ isBlur: o }) => {
await C.current, g.current && (clearTimeout(g.current), g.current = null), o && l((d) => ({
...d,
blurred: d.blurred.includes(e.name) ? d.blurred : [...d.blurred || [], e.name]
}));
const w = await new Promise(
(d) => {
u((P) => (d(P), P));
}
), S = await new Promise((d) => {
l((P) => (d(P), P));
}), x = w[e.name];
if (S.previousState[e.name] === x)
return;
E(w, S, e, x);
const m = await f?.({
fieldName: e.name,
value: x,
previousState: S.previousState,
currentState: w
});
if (m && "preventUpdate" in m && m.preventUpdate) {
u((d) => ({
...d,
[e.name]: S.previousState[e.name] || e.initialValue
// Fallback to initial value if not set
})), E(
S.previousState,
S,
e,
x
);
return;
}
l((d) => ({
...d,
previousState: {
...d.previousState,
[e.name]: x
}
}));
},
[E, e, f, l, u]
), j = F(() => {
O({ isBlur: !0 });
}, [O]), a = F(
async (o) => {
let w = () => {
};
C.current = new Promise((S) => {
w = S;
});
try {
l((m) => ({
...m,
dirty: m.dirty.includes(e.name) ? m.dirty : [...m.dirty || [], e.name]
}));
const S = await new Promise((m) => {
l((d) => (m(d), d));
}), x = await new Promise(
(m) => {
u((d) => (m(d), d));
}
);
u((m) => ({
...m,
[e.name]: o
})), v?.({
fieldName: e.name,
value: o,
previousState: S.previousState,
currentState: x
}), s?.updateDebounce && (g.current && clearTimeout(g.current), g.current = setTimeout(async () => {
await O({
isBlur: !1
}), g.current = null;
}, s.updateDebounce));
} finally {
C.current && (C.current = null, w());
}
},
[
e.name,
O,
v,
l,
u,
s?.updateDebounce
]
), h = F(() => {
l((o) => ({
...o,
focused: o.focused.includes(e.name) ? o.focused : [...o.focused || [], e.name]
}));
}, [e.name, l]), b = i(
() => y[e.name],
[y, e.name]
), A = i(
() => t.touched?.includes(e.name),
[t, e.name]
), I = i(
() => t.dirty?.includes(e.name),
[t, e.name]
), L = i(() => !I, [I]), q = i(
() => t.blurred?.includes(e.name),
[t, e.name]
), U = i(
() => t.focused?.includes(e.name),
[t, e.name]
), N = i(
() => b === e.initialValue,
[b, e.initialValue]
), _ = i(
() => t.disabled?.includes(e.name),
[t, e.name]
), W = i(() => t.errors?.[e.name] || null, [t.errors, e.name]), r = i(
() => ({
...e,
value: b,
isTouched: A,
isDirty: I,
isFocused: U,
isDefaultValue: N,
isPristine: L,
isBlurred: q,
isDisabled: _ || e.disabled || !1,
error: W,
isErrorVisible: q || D
}),
[
e,
b,
A,
I,
U,
N,
L,
q,
_,
W,
D
]
), p = i(
() => ({
onChange: a,
onBlur: j,
onFocus: h
}),
[a, j, h]
);
return c ? /* @__PURE__ */ V(ae, { Component: c, field: r, methods: p }) : (console.error(`Component for field type "${e.type}" was not found.`), null);
}
const ae = ne(
({
Component: e,
field: n,
methods: s
}) => /* @__PURE__ */ V(e, { field: n, methods: s })
), ee = ({
formApi: e,
formClassName: n,
initialDisabledFields: s,
forceFieldChangeState: f,
fieldWrapper: v,
...u
}) => {
const l = i(
() => Object.fromEntries(
u.fields.map((r) => [r.name, r.initialValue])
),
[u.fields]
), [y, t] = k(l), [R, D] = k(!1), [C, g] = k(!1);
z(() => {
f && t(f);
}, [f]);
const [c, E] = k({
dirty: [],
focused: [],
touched: [],
blurred: [],
initialState: l,
errors: {},
disabled: s ?? [],
previousState: l
}), O = i(
() => Object.keys(c.errors).some(
(r) => c.errors[r] != null
),
[c.errors]
), j = F(() => {
t(c.initialState), E((r) => ({
...r,
previousState: c.initialState,
dirty: [],
focused: [],
touched: [],
blurred: [],
errors: {},
disabled: [],
initialState: c.initialState
})), g(!1);
}, [c.initialState, E, t]), a = F(
(r) => {
t((p) => {
const o = {
...p,
...r
};
return E((w) => ({
...w,
previousState: {
...w.previousState,
...Object.entries(r).reduce(
(S, [x, m]) => ({
...S,
[x]: m
}),
{}
)
}
})), o;
});
},
[E, t]
), h = F(
(r, p) => {
E((o) => {
const w = p ? [...o.disabled, r] : o.disabled.filter((S) => S !== r);
return {
...o,
disabled: w
};
});
},
[E]
), b = F(
(r, p) => {
E((o) => {
const w = { ...o.errors };
return p === null ? delete w[r] : w[r] = p, {
...o,
errors: w
};
});
},
[E]
), A = F(
async (r) => {
try {
if (r.preventDefault(), r.stopPropagation(), D(!0), g(!0), u.onSubmit) {
const p = Object.keys(c.errors).some(
(o) => c.errors[o] != null
);
await u.onSubmit({
state: y,
errors: c.errors,
success: !p
});
}
} finally {
D(!1);
}
},
[u, y, c.errors]
), I = F(
async (r, p) => {
if (!r) {
console.warn("Field name is required to set value");
return;
}
t((o) => ({
...o,
[r]: p
}));
},
[t]
), L = F(async () => {
const r = await X(u.schema, y);
if (r instanceof Array) {
const p = r.reduce((o, w) => {
const S = w.path.join(".");
return o[S] = w.message, o;
}, {});
E((o) => ({
...o,
errors: p
}));
}
}, [u.schema, y]), q = i(
() => ({
...u,
fieldsInfo: c,
setFieldsInfo: E,
fieldsState: y,
setFieldsState: t,
reset: j,
updateFieldsState: a,
didSubmitOnce: C,
isSubmitting: R,
setFieldValue: I
}),
[
C,
c,
y,
R,
u,
j,
I,
a
]
), U = i(
() => ({
reset: j,
updateFieldsState: a,
setFieldsInfo: E,
setFieldsState: t,
fieldsState: y,
fieldsInfo: c,
setDisabled: h,
setError: b,
onFormSubmit: A,
isSubmitting: R,
setFieldValue: I,
didSubmitOnce: C,
hasSomeError: O,
checkForErrors: L,
setDidSubmitOnce: g
}),
[
L,
C,
c,
y,
O,
R,
A,
j,
h,
b,
I,
a
]
);
z(() => {
C && L();
}, [y, C, L]);
const N = F(
({ children: r, field: p }) => v ? v(r, p) : r,
[v]
);
J(e, () => U, [U]);
const _ = i(
() => y,
[y]
), W = i(
() => c,
[c]
);
return /* @__PURE__ */ V(Y.Provider, { value: _, children: /* @__PURE__ */ V(Z.Provider, { value: W, children: /* @__PURE__ */ V(
M.Provider,
{
value: q,
children: /* @__PURE__ */ G("form", { className: n, onSubmit: A, children: [
u.fields?.map((r, p) => /* @__PURE__ */ V(N, { field: r, children: /* @__PURE__ */ V(oe, { field: r }) }, p)),
u.children && typeof u.children == "function" ? u.children(U) : u.children
] })
}
) }) });
};
function he() {
const e = T(M);
if (!e)
throw new Error("useForm must be used within a Form");
return e;
}
function ie() {
const e = T(Y);
if (!e)
throw new Error("useFieldsState must be used within a Form");
return e;
}
function ue() {
const e = T(Z);
if (!e)
throw new Error("useFieldsInfo must be used within a Form");
return e;
}
function Se() {
const e = ue();
return i(
() => e.errors,
[e.errors]
);
}
function we(e) {
const n = ie();
return i(
() => n[e],
[n, e]
);
}
const ce = B(null), te = B(null), be = () => {
const e = T(te);
if (!e)
throw new Error("useListApi must be used within a List component");
return e;
}, Fe = ({
children: e,
...n
}) => {
const [s, f] = k(n.initialItems || []), v = H(null), [u, l] = k(null), y = i(
() => ({ userProps: n }),
[n]
), t = F(async () => {
if (v.current.setDidSubmitOnce(!0), await v.current.checkForErrors(), v.current.hasSomeError) {
n.addProps.onError?.(v.current.fieldsInfo.errors);
return;
}
const a = v.current.fieldsState, { item: h } = await n.addProps.onSuccess?.(a) ?? {}, b = h || a;
v.current.reset(), l(null), f((A) => [...A, b]);
}, [n.addProps]), R = F((a) => {
f((h) => h.filter((b, A) => A !== a));
}, []), D = F((a, h) => {
f(
(b) => b.map((A, I) => I === a ? h : A)
);
}, []), C = F((a) => {
f((h) => [...h, ...a]);
}, []), g = F((a) => {
f((h) => h.filter((b, A) => !a.includes(A)));
}, []), c = F(
(a) => {
f((h) => {
const b = [...h];
return a.forEach(({ index: A, item: I }) => {
b[A] && (b[A] = I);
}), b;
});
},
[]
), E = i(
() => ({
items: s,
addItem: t,
removeItem: R,
updateItem: D,
insertItems: C,
removeItems: g,
updateItems: c
}),
[
s,
t,
R,
D,
C,
g,
c
]
), O = i(
() => n.fields.map((a) => ({
...a,
metadata: {
...a.metadata || {},
isListField: !0
}
})),
[n.fields]
);
J(
n.listApi,
() => ({
addItem: t,
removeItem: R,
updateItem: D,
items: s,
insertItems: C,
removeItems: g,
updateItems: c
}),
[
t,
R,
D,
s,
C,
g,
c
]
);
const j = i(
() => O.map((a) => ({
...a,
metadata: {
...a.metadata || {},
isAddRow: !0
}
})),
[O]
);
return /* @__PURE__ */ V(
ce.Provider,
{
value: y,
children: /* @__PURE__ */ G(te.Provider, { value: E, children: [
/* @__PURE__ */ V(
n.addProps.rowComponent,
{
add: t,
formApi: v,
form: /* @__PURE__ */ V(
ee,
{
forceFieldChangeState: u,
formApi: v,
fields: j,
fieldWrapper: n.formProps?.fieldWrapper,
schema: n.schema,
formClassName: n.formProps?.formClassName,
onChange: ({ currentState: a, value: h, fieldName: b }) => {
l(() => ({
...a,
[b]: h
}));
}
}
)
}
),
s.map((a, h) => /* @__PURE__ */ V(
le,
{
fieldsFormProps: O,
userProps: n,
item: a,
index: h,
updateItem: D,
removeItem: R
},
h
)),
typeof e == "function" ? e(E) : e
] })
}
);
}, le = ({
fieldsFormProps: e,
userProps: n,
item: s,
index: f,
updateItem: v,
removeItem: u
}) => {
const l = H(null), y = i(
() => e.map((t) => ({
...t,
initialValue: s[t.name],
metadata: {
...t.metadata || {},
listIndex: f
}
})),
[e, f, s]
);
return /* @__PURE__ */ V(
n.itemsProps.rowComponent,
{
item: s,
index: f,
formApi: l,
remove: u,
form: /* @__PURE__ */ V(
ee,
{
forceFieldChangeState: s,
formApi: l,
fields: y,
schema: n.schema,
formClassName: n.formProps?.formClassName,
onChange: (t) => {
const { currentState: R, fieldName: D, value: C } = t;
v(f, { ...R, [D]: C }), n.itemsProps.onChange?.({
...t,
item: s,
index: f
});
},
onUpdate: (t) => n.itemsProps.onUpdate?.({
...t,
item: s,
index: f
})
}
)
}
);
};
export {
oe as FieldComponent,
ee as Form,
Fe as List,
fe as Register,
we as useFieldValue,
Se as useFieldsErrors,
ue as useFieldsInfo,
ie as useFieldsState,
he as useForm,
be as useListApi,
re as useRegister
};