@react-form-builder/core
Version:
React JSON Schema Form Builder to create complex, validated, reusable forms with no deep React knowledge required
360 lines (359 loc) • 12.9 kB
JavaScript
import { makeObservable as F, observable as S, runInAction as b } from "mobx";
import { commonActions as C } from "../features/event/consts/actions.js";
import { createActionValuesFromObject as M } from "../features/event/createActionValuesFromObject.js";
import { globalDefaultLanguage as L } from "../features/localization/default.js";
import { findLanguage as A } from "../features/localization/findLanguage.js";
import { Language as x } from "../features/localization/language.js";
import { NoopLocalizationEngine as D } from "../features/localization/NoopLocalizationEngine.js";
import { templateTypeName as k } from "../features/template/templateTypeName.js";
import { buildInternalErrorModel as O } from "../features/ui/internalErrorModel.js";
import { screenModel as P } from "../features/ui/screenModel.js";
import { isTemplateType as W } from "../features/ui/templateUtil.js";
import { calculateProperty as $ } from "../features/validation/utils/calculateProperty.js";
import { codeValidationRule as j, ZodValidationRules as N } from "../features/validation/utils/consts.js";
import { dataPart as g } from "../features/validation/utils/dataPart.js";
import { getDefaultErrorMessage as K, DataValidator as B } from "../features/validation/utils/DataValidator.js";
import { RepeaterField as G } from "../features/validation/utils/RepeaterField.js";
import { SimpleField as J } from "../features/validation/utils/SimpleField.js";
import { TemplateField as U } from "../features/validation/utils/TemplateField.js";
import { isStoreDataInParentForm as V } from "../features/validation/utils/util.js";
import { typedValidatorsResolver as Z } from "../features/validation/utils/validatorsResolver.js";
import { ComponentData as q } from "../utils/contexts/ComponentDataContext.js";
import { isRecord as H } from "../utils/isRecord.js";
import { isString as Q } from "../utils/isString.js";
import { nameObservable as X } from "../utils/observableNaming.js";
import { isNumber as Y, isUndefined as T } from "../utils/tools.js";
import { ComponentStore as v, dataKey as d } from "./ComponentStore.js";
import { Form as y } from "./Form.js";
import { LocalizationStore as w } from "./LocalizationStore.js";
import { PersistedFormVersion as z } from "./PersistedForm.js";
const _ = {
RsDatePicker: ["calendarDefaultDate", "defaultValue", "value"],
RsCalendar: ["defaultValue", "value"]
}, tt = (l) => {
const t = l.languages?.map(x.clone) ?? [], o = t.reduce((e, { fullCode: r }) => (e[r] = {}, e), {});
return {
languages: t,
localization: Object.assign({}, o, l.localization ?? {}),
defaultLanguage: t.find((e) => e.fullCode === l.defaultLanguage) ?? L
};
};
function I(l, t, o, e) {
let r = t(o, l);
return l[e]?.forEach((i) => {
r = I(i, t, r, e);
}), r;
}
class R {
/**
* Creates form viewer settings.
* @param formViewerPropsStore the form viewer store settings.
* @param componentStateFactory the factory for creating component states.
* @param parentStore the form viewer settings, used in templates.
* @param getInitialData the function to get initial data for the Store.
* @param setInitialData the function for updating initial data.
*/
constructor(t, o, e, r, i) {
this.formViewerPropsStore = t, this.componentStateFactory = o, this.parentStore = e, this.getInitialData = r, this.setInitialData = i;
const a = this.createDataRoot(), m = t.localizationEngine ?? new D(), p = new w({}, m);
this.form = new y(a, p, {}, [], L), this.form.modalType = this.getFirstComponentTypeWithRole("modal"), this.form.tooltipType = this.getFirstComponentTypeWithRole("tooltip"), this.form.componentTree.state = this.formViewerPropsStore.initialState, F(this, {
form: S,
viewMode: !0,
selectedLanguage: !0,
clear: !0,
parentStore: S.ref,
initialDataSlice: !0,
formLoadError: !0
}, { name: X("ViewerStore") });
}
formViewerPropsStore;
componentStateFactory;
parentStore;
getInitialData;
setInitialData;
/**
* The currently selected language.
*/
selectedLanguage;
/**
* Current display resolution type.
*/
viewMode = "desktop";
/**
* The form.
*/
form;
/**
* The loading form error.
*/
formLoadError;
/**
* @returns the Record with the common actions.
*/
get commonActions() {
return C;
}
/**
* Returns an action by the specified action name and action type.
* @param name the action name.
* @param type the action type.
* @returns the action.
*/
getAction(t, o) {
let e;
switch (o) {
case "common":
e = this.commonActions[t];
break;
case "code":
e = this.form.actions[t];
break;
case "custom":
e = this.formViewerPropsStore.actions?.[t];
break;
}
if (!e) throw Error(`Action '${t}' with type '${o}' not found!`);
return e;
}
/**
* @inheritDoc
*/
get formData() {
return this.form.componentTree;
}
/**
* Clears the form in Form Viewer.
*/
clear() {
const t = this.form, o = this.createDataRoot(), e = this.formViewerPropsStore.localizationEngine ?? new D(), r = new w({}, e);
this.form = new y(o, r, {}, t.languages, t.defaultLanguage), this.form.modalType = this.getFirstComponentTypeWithRole("modal"), this.form.tooltipType = this.getFirstComponentTypeWithRole("tooltip"), t.dispose();
}
/**
* @inheritDoc
*/
dispose() {
this.form.dispose();
}
/**
* @inheritDoc
*/
get initialDataSlice() {
return this.parentStore ? this.getInitialData?.() : this.formViewerPropsStore.initialData;
}
/**
* @inheritDoc
*/
get showAllValidationErrors() {
return this.formViewerPropsStore?.showAllValidationErrors;
}
/**
* @inheritDoc
*/
reduceScreen(t, o) {
return I(this.form.componentTree, t, o, "children");
}
/**
* Searches for an action, returns definition for the found action.
* @param actionData the action's data.
* @returns the action definition.
* @throws {Error} If the action was not found.
*/
findAction(t) {
return this.getAction(t.name, t.type);
}
/**
* Returns model for the specified type.
* @param type the component type.
* @returns the model for the specified type.
*/
getModel(t) {
const o = this.formViewerPropsStore.view.find(t);
if (o) return o;
if (W(t)) {
const e = this.formViewerPropsStore.view.find(k);
if (e) return e;
}
return O(`Type '${t}' is not found!`);
}
/**
* @inheritDoc
*/
createComponentData(t, o = !1) {
const e = this.getModel(t.type), r = (a) => this.createComponentData(a, o), i = new q(t, e, r, this.getFormValidatorsResult.bind(this));
return i.field = this.createField(i, o), i.componentState = this.componentStateFactory(i, this), i;
}
/**
* Returns the object with validators for the specified value type.
* @param type the value type.
* @returns the object with validators for the specified value type.
*/
getValidationRules(t) {
return {
custom: this.formViewerPropsStore.validators?.[t],
internal: {
...N[t],
code: j
}
};
}
createField(t, o) {
const { model: e, store: r } = t;
if (!e.valued || e.dataBindingType !== "twoWay") return;
const i = (n, s) => this.calculateProperty(t, n, s), a = (n, s, c) => this.createDataValidator(n, s, c), m = () => g(t.initialData, d(r));
if (e.kind === "repeater") {
const n = (s) => {
const c = d(r);
t.dataRoot.updateInitialData(c, s);
};
return new G(
t,
i,
a,
m,
n,
this,
o
);
}
if (e.kind !== "template")
return new J(t, i, a, m, o);
const p = () => V(r) ? t.initialData : g(t.initialData, d(r)), h = (n, s) => {
if (V(r)) return this.updateInitialData(n, s);
const c = d(r), u = g(t.initialData, c) ?? {};
H(u) && (u[n] = s), this.updateInitialData(c, u);
}, f = this.formViewerPropsStore.clone();
f.initialState = {};
const E = new R(
f,
this.componentStateFactory,
this,
p,
h
);
return new U(r, E);
}
/**
* Populates the value of this store with the values of the saved form.
* @param text saved form value.
*/
applyStringForm(t) {
try {
const o = JSON.parse(t);
this.fixPropertyTypes(o.form), this.applyPersistedForm(o), this.formLoadError = void 0;
} catch (o) {
this.formLoadError = o?.message ?? o, console.error(o);
}
}
fixPropertyTypes(t) {
t.props || (t.props = {}), _[t.type]?.forEach((e) => this.fixDateProperty(t, e)), t.children?.forEach((e) => this.fixPropertyTypes(e));
}
fixDateProperty(t, o) {
const e = t.props[o]?.value;
(Q(e) || Y(e)) && (t.props[o].value = new Date(e));
}
/**
* Populates the value of this store with the values of the saved form.
* @param persistedForm saved form value.
*/
applyPersistedForm(t) {
const o = this.form, e = t.version;
!T(e) && e !== z.version1 && console.warn(`An unsupported version of form '${e}' has been detected. An attempt will be made to upload the form as version '${z.version1}'.`), b(() => {
const r = v.createFromObject(t.form), i = this.createComponentData(r, !0);
i.getInitialData = () => this.initialDataSlice, i.setInitialData = this.updateInitialData;
const {
languages: a,
localization: m,
defaultLanguage: p
} = tt(t), h = new w(m, this.form.localization.engine), f = M(t.actions);
this.form = new y(i, h, f, a, p), this.form.errorProps = t.errorProps ?? {}, this.form.tooltipType = t.tooltipType, this.form.errorType = t.errorType, this.form.modalType = t.modalType, this.form.formValidator = t.formValidator;
}), this.form.componentTree.unifyTree(), this.form.initFields(), o.dispose(), this.form.componentTree.state = this.formViewerPropsStore.initialState;
}
/**
* @returns the current display language.
*/
get displayedLanguage() {
if (this.parentStore) return this.parentStore.displayedLanguage;
if (this.formViewerPropsStore.propsLanguage) {
const t = A(this.form.languages, this.formViewerPropsStore.propsLanguage);
if (t) return t;
}
return this.selectedLanguage ?? this.form.defaultLanguage;
}
/**
* @inheritDoc
*/
localizeComponent(t, o, e) {
return this.formViewerPropsStore.localizer ? this.formViewerPropsStore.localizer(e, this.displayedLanguage) : this.form.localization.engine.localizeProperties(this.form, o, this.displayedLanguage, e, t);
}
/**
* @inheritDoc
*/
localizeErrorMessages(t, o, e) {
if (e)
return e.map((r) => {
const { engine: i } = this.form.localization;
return i.localizeErrorMessage(this.form, t, this.displayedLanguage, o, r.settings.key) ?? K(r);
});
}
calculateProperty(t, o, e) {
const r = t.dataRoot;
return $(
o,
e,
r,
(i, a) => this.localizeComponent(i, r, a)
);
}
/**
* Creates a data validator for the field.
* @param componentData the component data.
* @param valueType the field's data type.
* @param onError the callback function called when the validation error text is set.
* @returns the data validator.
*/
createDataValidator(t, o, e) {
const r = this.getValidationRules(o), i = (a) => this.localizeErrorMessages(t.dataRoot, t.store, a);
return B.create(
this,
() => t.dataRoot,
Z(r),
t.store.schema,
e,
i
);
}
createDataRoot() {
const t = new v(P.name, P.type), o = this.createComponentData(t);
return o.getInitialData = () => this.initialDataSlice, o.setInitialData = this.updateInitialData, o;
}
async getFormValidatorsResult() {
const t = this.formViewerPropsStore.formValidators, o = this.form.formValidatorFunction, e = [];
return t && e.push(...t.map((a) => a(this.form.componentTree.data))), o && e.push(o(this.form.componentTree?.data ?? {})), ((await Promise.allSettled(e))?.map((a) => {
if (a.status === "rejected") {
console.error(a.reason);
return;
}
return a.value;
})).filter((a) => !T(a));
}
/**
* Returns the first type of component with the specified role.
* @param componentRole the component role.
* @returns the first type of component with the specified role.
*/
getFirstComponentTypeWithRole(t) {
const o = this.formViewerPropsStore.view.filterModels((e) => e.hasComponentRole(t)).map((e) => e.type);
return o.length ? o[0] : void 0;
}
updateInitialData = (t, o) => {
if (this.setInitialData) {
this.setInitialData(t, o);
return;
}
this.formViewerPropsStore.initialData[t] = o;
};
}
export {
R as Store
};
//# sourceMappingURL=Store.js.map