UNPKG

react-form-krafter

Version:

A flexible form engine for React powered by Standard Schema

693 lines (692 loc) 15 kB
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 };