mobx-easy-form
Version:
Simple and performant form library built with MobX
285 lines (273 loc) • 8.09 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var mobx = require('mobx');
var React = _interopDefault(require('react'));
var useMemoOne = require('use-memo-one');
function isPromise(value) {
return !!(value && typeof value === "object" && "then" in value && typeof value.then === "function");
}
function mapValues(object, callbackFn) {
return Object.fromEntries(Object.entries(object).map(function (_ref) {
var key = _ref[0],
value = _ref[1];
return [key, callbackFn(value)];
}));
}
function createForm(_ref) {
var onSubmit = _ref.onSubmit;
var fields = mobx.observable({});
var state = mobx.observable({
isSubmitting: false,
valuesAtLastSubmit: undefined,
submitCount: 0
});
var computed = mobx.observable({
get isDirty() {
return Object.values(fields).some(function (field) {
return field.computed.isDirty;
});
},
get errorList() {
return Object.values(fields).map(function (field) {
return field.computed.error;
}).filter(function (error) {
return error !== undefined;
});
},
get isError() {
return Object.values(fields).some(function (field) {
return !!field.computed.error;
});
},
get isValid() {
return !this.isError;
},
get valueList() {
return String(Object.values(fields).map(function (field) {
return field.state.value;
}));
},
get isChangedSinceLastSubmit() {
if (state.submitCount === 0) return this.isDirty;
return this.valueList !== state.valuesAtLastSubmit;
}
});
var actions = {
add: function add(field) {
fields[field.state.id] = field;
},
submit: mobx.action(function submit() {
state.isSubmitting = true;
state.submitCount++;
state.valuesAtLastSubmit = computed.valueList;
for (var fieldId in fields) {
var field = fields[fieldId];
field.state.wasEverFocused = true;
field.state.wasEverBlurred = true;
}
if (computed.isError) {
state.isSubmitting = false;
return;
}
var maybePromise = onSubmit({
fields: fields,
rawValues: mapValues(fields, function (field) {
return field.state.value;
}),
values: mapValues(fields, function (field) {
return field.computed.parsed;
})
});
if (isPromise(maybePromise)) {
return Promise.resolve(maybePromise)["finally"](function () {
mobx.runInAction(function () {
state.isSubmitting = false;
});
});
} else {
mobx.runInAction(function () {
state.isSubmitting = false;
});
}
return maybePromise;
})
};
return {
fields: fields,
state: state,
computed: computed,
actions: actions
};
}
function _extends() {
_extends = Object.assign ? Object.assign.bind() : function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
function _objectWithoutPropertiesLoose(source, excluded) {
if (source == null) return {};
var target = {};
var sourceKeys = Object.keys(source);
var key, i;
for (i = 0; i < sourceKeys.length; i++) {
key = sourceKeys[i];
if (excluded.indexOf(key) >= 0) continue;
target[key] = source[key];
}
return target;
}
var _excluded = ["id", "initialValue", "initialError", "form"];
function createField(_ref) {
var id = _ref.id,
initialValue = _ref.initialValue,
initialError = _ref.initialError,
form = _ref.form,
validationProps = _objectWithoutPropertiesLoose(_ref, _excluded);
function getValidateFunction() {
if ("validate" in validationProps && validationProps.validate) {
return validationProps.validate;
}
if ("validationSchema" in validationProps && validationProps.validationSchema) {
return function validate(value) {
if (!validationProps.validationSchema) throw new Error("Missing validation schema");
try {
var parsed = validationProps.validationSchema.validateSync(value === "" ? undefined : value, {
abortEarly: true
});
return {
parsed: parsed,
error: undefined
};
} catch (error) {
if (error instanceof Error && error.name === "ValidationError") {
return {
parsed: undefined,
error: error
};
}
throw error;
}
};
}
return function validate(value) {
return {
parsed: value,
error: undefined
};
};
}
var runValidation = getValidateFunction();
var state = mobx.observable({
id: id,
errorOverride: initialError,
value: initialValue,
isFocused: false,
wasEverFocused: false,
wasEverBlurred: false
});
var computed = mobx.observable({
get parsed() {
var result = runValidation(state.value);
if (result.error) return undefined;
return result.parsed;
},
get isDirty() {
// TODO: Add ability to provide custom equality function.
return JSON.stringify(state.value) !== JSON.stringify(initialValue);
},
get error() {
var _runValidation = runValidation(state.value),
error = _runValidation.error;
if (state.errorOverride) {
return state.errorOverride;
}
if (error instanceof Error && error.name === "ValidationError") {
var _ref2, _err$message$value, _err$message;
var err = error;
return String((_ref2 = (_err$message$value = (_err$message = err.message) == null ? void 0 : _err$message.value) != null ? _err$message$value : err.message) != null ? _ref2 : error);
}
if (error instanceof Error) {
return error.message;
}
return error;
},
get ifWasEverFocusedThenError() {
if (!state.wasEverFocused) return undefined;
if (!computed.error) return undefined;
return String(computed.error);
},
get ifWasEverBlurredThenError() {
if (!state.wasEverBlurred) return undefined;
if (!computed.error) return undefined;
return String(computed.error);
}
});
var actions = {
onFocus: mobx.action(function onFocus() {
state.isFocused = true;
state.wasEverFocused = true;
}),
onBlur: mobx.action(function onBlur() {
state.isFocused = false;
state.wasEverBlurred = true;
}),
onChange: mobx.action(function onChange(value) {
if (state.errorOverride) state.errorOverride = undefined;
state.value = value;
}),
setError: mobx.action(function setError(value) {
state.errorOverride = value;
})
};
var field = {
state: state,
computed: computed,
actions: actions
};
form.actions.add(field);
return field;
}
function useEvent(handler) {
var handlerRef = React.useRef(null);
React.useLayoutEffect(function () {
handlerRef.current = handler;
});
return React.useCallback(function () {
var fn = handlerRef.current;
return fn.apply(void 0, arguments);
}, []);
}
function useForm(args, deps) {
if (deps === void 0) {
deps = [];
}
var onSubmit = useEvent(args.onSubmit);
return useMemoOne.useMemoOne(function () {
return createForm(_extends({}, args, {
onSubmit: onSubmit
}));
}, deps);
}
function useField(args, deps) {
if (deps === void 0) {
deps = [];
}
return useMemoOne.useMemoOne(function () {
return createField(args);
}, deps);
}
exports.createField = createField;
exports.createForm = createForm;
exports.useField = useField;
exports.useForm = useForm;
//# sourceMappingURL=mobx-easy-form.cjs.development.js.map