alinea
Version:
Headless git-based CMS
190 lines (188 loc) • 6.08 kB
JavaScript
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
};