UNPKG

@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
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