UNPKG

react-form-krafter

Version:

A flexible form engine for React powered by Standard Schema

704 lines (703 loc) 15.5 kB
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 };