mobx-autoform
Version:
Ridiculously simple form state management with mobx
234 lines (233 loc) • 9.64 kB
JavaScript
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
});