UNPKG

mobx-autoform

Version:

Ridiculously simple form state management with mobx

234 lines (233 loc) 9.64 kB
var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var src_exports = {}; __export(src_exports, { ValidationError: () => import_util.ValidationError, default: () => src_default, jsonSchemaKeys: () => jsonSchemaKeys, legacyKeys: () => legacyKeys, validators: () => validators }); module.exports = __toCommonJS(src_exports); var import_futil = __toESM(require("futil"), 1); var import_fp = __toESM(require("lodash/fp.js"), 1); var import_mobx = require("mobx"); var validators = __toESM(require("./validators.js"), 1); var import_util = require("./util.js"); var import_futil2 = require("./futil.js"); var import_mobx2 = require("./mobx.js"); let changed = (x, y) => !import_fp.default.isEqual(x, y) && !(import_futil.default.isBlank(x) && import_futil.default.isBlank(y)); let Command = import_futil.default.aspects.command((x) => (y) => (0, import_mobx.extendObservable)(y, x)); let jsonSchemaKeys = { label: "title", fields: "properties", itemField: "items", defaultValue: "default" }; let legacyKeys = { label: "label", fields: "fields", itemField: "itemField", defaultValue: "value" }; let defaultGetPatch = (form) => import_fp.default.mapValues("to", import_futil.default.diff(form.saved.value, (0, import_mobx2.toJS)(form.value))); let defaultGetSnapshot = (form) => import_futil.default.flattenObject((0, import_mobx2.toJS)((0, import_util.gatherFormValues)(form))); let defaultGetNestedSnapshot = (form) => import_futil.default.unflattenObject(form.getSnapshot()); const spliceErrors = (errors, parentPath, nodePosition) => { const parent = parentPath.join("."); const parentLength = parentPath.length; const arrayErrors = (0, import_futil2.pickByPrefixes)([parent], errors); const newErrors = (0, import_futil2.omitByPrefixes)([parent], errors); for (const [key, value] of Object.entries(arrayErrors)) { const keyFields = key.split("."); const idx = parseInt(keyFields[parentLength]); if (idx > nodePosition) { keyFields.splice(parentLength, 1, idx - 1); const newKey = keyFields.join("."); newErrors[newKey] = value; } else if (idx < nodePosition) { newErrors[key] = value; } } return newErrors; }; var src_default = ({ submit: configSubmit, value = {}, afterInitField = (x) => x, validate = validators.functions, identifier = "unknown", keys = legacyKeys, getPatch = defaultGetPatch, getSnapshot = defaultGetSnapshot, getNestedSnapshot = defaultGetNestedSnapshot, ...autoFormConfig }) => { let fieldPath = import_fp.default.flow(import_futil.default.intersperse(keys.fields), import_fp.default.compact); let flattenField = import_futil.default.flattenTree((x) => x[keys.fields])( (...x) => import_fp.default.join(".", (0, import_futil2.treePath)(...x)) ); let saved = {}; let state = (0, import_mobx2.observable)({ value, errors: {}, disposers: {} }); let initField = (config, rootPath = []) => { let dotPath = import_fp.default.join(".", rootPath); let valuePath = ["value", ...rootPath]; let node = (0, import_mobx2.observable)({ ...config, field: import_fp.default.last(rootPath), [keys.label]: config[keys.label] || import_fp.default.startCase(import_fp.default.last(rootPath)), "data-testid": import_fp.default.snakeCase([identifier, ...rootPath]), get value() { return (0, import_mobx2.get)(valuePath, state); }, set value(x) { (0, import_mobx2.set)(valuePath, x, state); }, get errors() { return (0, import_mobx2.get)(import_fp.default.compact(["errors", dotPath]), state) || []; }, get isValid() { return import_fp.default.isEmpty(node.errors); }, get isDirty() { return changed(import_fp.default.get(valuePath, saved), (0, import_mobx2.toJS)(node.value)); }, reset() { node.value = (0, import_mobx2.toJS)(import_fp.default.get(valuePath, saved)); state.errors = (0, import_futil2.omitByPrefixes)([dotPath], state.errors); }, validate(paths = [dotPath]) { let errors = validate(form, (0, import_futil2.pickByPrefixes)(paths, flattenField(form))); state.errors = { ...(0, import_futil2.omitByPrefixes)(paths, state.errors), ...errors }; return errors; }, clean() { import_futil.default.setOn(valuePath, (0, import_mobx2.toJS)(node.value), saved); }, getField(path) { return import_fp.default.get( (0, import_util.safeJoinPaths)(fieldPath((0, import_util.tokenizePath)(path))), node[keys.fields] ); }, dispose() { import_fp.default.over(import_fp.default.values((0, import_futil2.pickByPrefixes)([dotPath], state.disposers)))(); state.disposers = (0, import_futil2.omitByPrefixes)([dotPath], state.disposers); }, remove() { let parent = form.getField(import_fp.default.dropRight(1, rootPath)) || form; if (parent[keys.itemField]) { parent.value.splice(node.field, 1); state.errors = spliceErrors(state.errors, parent.path, node.field); } else { node.dispose(); import_futil.default.unsetOn(node.field, parent.value); import_futil.default.unsetOn(node.field, parent[keys.fields]); state.errors = (0, import_futil2.omitByPrefixes)([dotPath], state.errors, node.field); } } }); node.path = rootPath; if (import_fp.default.isUndefined(node.value) && !import_fp.default.isUndefined(config[keys.defaultValue])) node.value = (0, import_mobx2.toJS)(config[keys.defaultValue]); if (node[keys.fields]) node.add = (configs) => (0, import_mobx.extendObservable)( node[keys.fields], import_futil.default.mapValuesIndexed((x, k) => initTree(x, [...rootPath, k]), configs) ); if (node[keys.itemField]) { node[keys.fields] = (0, import_mobx2.observable)([]); state.disposers[dotPath] = (0, import_mobx.reaction)( () => import_fp.default.size(node.value), (size) => { import_fp.default.each((x) => x.dispose(), node[keys.fields]); node[keys.fields].replace( import_fp.default.times( (index) => initTree(node[keys.itemField], [...rootPath, index]), size ) ); } ); } return afterInitField(node, { ...config, keys }); }; let initTree = (config, rootPath = []) => import_futil.default.reduceTree((x) => x[keys.fields])((tree, node, ...args) => { let path = (0, import_futil2.treePath)(node, ...args); let field = initField(node, [...rootPath, ...path]); if (node[keys.itemField]) node[keys.fields] = import_fp.default.times( () => (0, import_mobx2.toJS)(node[keys.itemField]), import_fp.default.size(field.value) ); return import_fp.default.isEmpty(path) ? field : (0, import_mobx2.set)([keys.fields, ...fieldPath(path)], field, tree); })({})((0, import_mobx2.toJS)(config)); let form = (0, import_mobx.extendObservable)(initTree(autoFormConfig), { getPatch: () => getPatch(form), getSnapshot: () => getSnapshot(form), getNestedSnapshot: () => getNestedSnapshot(form), get submitError() { return import_futil.default.getOrReturn("message", form.submit.state.error); } }); let submit = Command(async () => { if (import_fp.default.isEmpty(form.validate())) { form.submit.state.error = null; try { return await configSubmit(form.getSnapshot(), form); } catch (err) { if (err instanceof import_util.ValidationError) { state.errors = err.cause; } throw err; } } throw "Validation Error"; }); (0, import_mobx.extendObservable)(form, { submit }); form.submit.state = submit.state; form.keys = keys; form.saved = saved; form.reset = import_futil.default.aspectSync({ before: () => form.submit.state.error = null })(form.reset); import_futil.default.unsetOn("field", form); import_futil.default.unsetOn("remove", form); form.clean(); return form; }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { ValidationError, jsonSchemaKeys, legacyKeys, validators });