UNPKG

alinea

Version:
190 lines (188 loc) 6.08 kB
import { unwrap } from "../../chunks/chunk-WDCPVJJC.js"; import { atom } from "../../chunks/chunk-WJ67RR7S.js"; import { Doc } from "../../chunks/chunk-QUIANN6B.js"; import "../../chunks/chunk-AJJSW27C.js"; import "../../chunks/chunk-NZLE2WMY.js"; // src/dashboard/atoms/FormAtoms.tsx import { Field } from "alinea/core/Field"; import { Type } from "alinea/core/Type"; import { entries } from "alinea/core/util/Objects"; import { createContext, useContext, useMemo } from "react"; import { DOC_KEY, applyEntryData } from "alinea/core/Doc"; import { Section } from "alinea/core/Section"; import { optionTrackerOf, valueTrackerOf } from "alinea/core/Tracker"; import { jsx } from "react/jsx-runtime"; var FormAtoms = class { constructor(type, container, path = "", options = {}) { this.type = type; this.container = container; this.path = path; this.options = options; const readOnly = options.readOnly; const forcedOptions = readOnly === true ? { readOnly } : {}; container.doc.transact(() => { for (const section of Type.sections(type)) { for (const [key, field] of entries(Section.fields(section))) { const ref = Field.ref(field); const shape = Field.shape(field); const defaultOptions = { ...Field.options(field), ...forcedOptions }; const optionsTracker = optionTrackerOf(field); shape.init(container, key); const mutator = shape.mutator(container, key); const options2 = optionsTracker ? unwrap( atom((get) => { const tracked = optionsTracker(this.getter(get)); if (tracked instanceof Promise) return tracked.then((partial) => { return { ...defaultOptions, ...partial, ...forcedOptions }; }); return { ...defaultOptions, ...tracked, ...forcedOptions }; }), (prev) => prev ?? defaultOptions ) : atom(defaultOptions); const valueTracker = valueTrackerOf(field); const value = this.valueAtom(field, key, valueTracker); const error = atom((get) => { const { validate, required } = get(options2); if (validate) { const res = validate(get(value)); if (typeof res === "boolean") return !res; return res; } if (required) { const current = get(value); if (current === void 0 || current === null) return true; if (typeof current === "string" && current === "") return true; if (Array.isArray(current) && current.length === 0) return true; } }); this.fields.set(ref, { key, field, value, mutator, error, options: options2 }); } } }, "self"); } fields = /* @__PURE__ */ new Map(); errorMap = atom( /* @__PURE__ */ new Map() ); errors = atom( (get) => get(this.errorMap), (get, set, path, field, error) => { const current = get(this.errorMap); if (!error && !current.has(path)) return; const errors = new Map(current); if (error) errors.set(path, { field, error }); else errors.delete(path); set(this.errorMap, errors); if (this.options.parent) set(this.options.parent.errors, path, field, error); } ); hasErrors = atom((get) => get(this.errorMap).size > 0); data() { return Type.shape(this.type).fromY(this.container); } getter = (get) => (field) => { const info = this.fieldInfo(field); if (!info) throw new Error(`Field not found: ${Field.label(field)}`); return get(info.value); }; valueAtom(field, key, tracker) { const shape = Field.shape(field); const revision = atom(0); revision.onMount = (setAtom) => { const onChange = () => setAtom((x) => x + 1); const watch = shape.watch(this.container, key); onChange(); return watch(onChange); }; return atom((g) => { g(revision); const current = shape.fromY(this.container.get(key)); const next = tracker ? tracker(this.getter(g)) : current; if (tracker && next !== current) shape.applyY(next, this.container, key); return next; }); } fieldByKey(key) { for (const info of this.fields.values()) if (info.key === key) return info.field; throw new Error(`Field not found: "${key}"`); } keyOf(field) { return this.fieldInfo(field).key; } fieldInfo(field) { const res = this.fields.get(Field.ref(field)); const label = Field.label(field); if (!res) { if (this.options.parent) return this.options.parent.fieldInfo(field); throw new Error(`Field not found: ${label}`); } return res; } }; var formAtomsContext = createContext(void 0); function useForm(type, options = {}) { return useMemo(() => { const doc = options.doc ?? new Doc(); if (options.initialValue) { applyEntryData(doc, type, { data: options.initialValue }); } return new FormAtoms(type, doc.getMap(DOC_KEY)); }, [type, options.doc]); } function useFormContext() { const formAtoms = useContext(formAtomsContext); if (!formAtoms) throw new Error("FormAtoms is not provided"); return formAtoms; } function FormProvider({ children, form }) { return /* @__PURE__ */ jsx(formAtomsContext.Provider, { value: form, children }); } function FormRow({ children, field, type, rowId, readOnly }) { const form = useFormContext(); const rowForm = useMemo(() => { const key = form.keyOf(field); const inner = form.container.get(key); const row = rowId ? inner.get(rowId) : inner; const path = `${form.path}.${key}${rowId ? `[${rowId}]` : ""}`; return new FormAtoms(type, row, path, { readOnly, parent: form }); }, [form, rowId, readOnly]); return /* @__PURE__ */ jsx(FormProvider, { form: rowForm, children }); } export { FormAtoms, FormProvider, FormRow, useForm, useFormContext };