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