New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@reactway/forms-core

Package Overview
Dependencies
Maintainers
3
Versions
32
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@reactway/forms-core - npm Package Compare versions

Comparing version 0.0.0-canary.cd320b7 to 0.0.0-canary.ded513d

2

dist/contracts/state-updaters.d.ts

@@ -24,2 +24,4 @@ import { FieldStatus, Updater, TextSelection } from "./field-state";

setFormErrors(errors: NestedDictionary<ValidationResultOrString[]>): void;
setFieldValidationResults(fieldSelector: FieldSelector, errors: ValidationResultOrString[]): void;
resetFieldValidationResults(fieldSelector: FieldSelector): void;
}

@@ -1,2 +0,27 @@

import{TinyEmitter as t}from"@reactway/tiny-emitter";import e from"immer";import n from"shortid";import i from"debug";const r="value",s="status",a="validation";var l,o;!function(t){t[t.Error=0]="Error",t[t.Warning=1]="Warning"}(l||(l={})),function(t){t[t.Unknown=0]="Unknown",t[t.Validation=1]="Validation",t[t.FormSubmit=2]="FormSubmit"}(o||(o={}));const d=".",u=Symbol("form");
import { TinyEmitter } from '@reactway/tiny-emitter';
import produce from 'immer';
import shortid from 'shortid';
import Debug from 'debug';
const ValueUpdater = "value";
const StatusUpdater = "status";
const ValidationUpdater = "validation";
// These comments originated in the v4 codebase and are kept for historical purpose:
// https://github.com/SimplrJS/react-forms/blame/e8443591f215fbd3fa76898520c8490d5c6673b6/packages/simplr-forms/src/contracts/error.ts
var ValidationResultType;
(function (ValidationResultType) {
ValidationResultType[ValidationResultType["Error"] = 0] = "Error";
ValidationResultType[ValidationResultType["Warning"] = 1] = "Warning";
})(ValidationResultType || (ValidationResultType = {}));
var ValidationResultOrigin;
(function (ValidationResultOrigin) {
ValidationResultOrigin[ValidationResultOrigin["Unknown"] = 0] = "Unknown";
ValidationResultOrigin[ValidationResultOrigin["Validation"] = 1] = "Validation";
ValidationResultOrigin[ValidationResultOrigin["FormSubmit"] = 2] = "FormSubmit";
})(ValidationResultOrigin || (ValidationResultOrigin = {}));
const IdSeparator = ".";
const FormSelector = Symbol("form");
/*! *****************************************************************************

@@ -16,2 +41,891 @@ Copyright (c) Microsoft Corporation. All rights reserved.

***************************************************************************** */
function c(t,e,n,i){return new(n||(n=Promise))((function(r,s){function a(t){try{o(i.next(t))}catch(t){s(t)}}function l(t){try{o(i.throw(t))}catch(t){s(t)}}function o(t){t.done?r(t.value):new n((function(e){e(t.value)})).then(a,l)}o((i=i.apply(t,e||[])).next())}))}class f{constructor(t,e){this.tokenName=e,this.cancelled=!1,this.cancellationCallback=t}get cancellationRequested(){return this.cancelled}cancel(){var t;this.cancelled||(this.cancelled=!0,null===(t=this.cancellationCallback)||void 0===t||t.call(this))}}class h extends t{constructor(){super(...arguments),this.storesRegistry={}}registerStore(t,e){if(null!=this.storesRegistry[t])throw new Error(`Form with formId "${t}" is already registered.`);this.storesRegistry[t]=e,this.emit()}unregisterStore(t){this.storesRegistry[t]=void 0,this.emit()}getStore(t){return this.storesRegistry[t]}getStoresIds(){return Object.keys(this.storesRegistry)}}const g=new class{setFormStoresHandler(t,e=!0){e&&null!=this.instance&&delete this.instance,this.instance=t}get registry(){var t;return this.instance=null!==(t=this.instance)&&void 0!==t?t:new h,this.instance}};function v(t,e){return{registerValidator:e=>n=>n.getUpdater("validation").registerValidator(t,e),unregisterValidator:e=>n=>{n.getUpdater("validation").unregisterValidator(t,e)},reportValidatorIndex:e.reportValidatorIndex}}function p(t,e){return Object.assign(Object.assign(Object.assign({},v(t,e)),e),{registerModifier:e=>n=>n.getUpdater("value").registerModifier(t,e),unregisterModifier:e=>n=>{n.getUpdater("value").unregisterModifier(t,e)}})}function m(t,e,i){return{id:"validation",validateField:e=>c(this,void 0,void 0,(function*(){return function(t,e,n,i){return c(this,void 0,void 0,(function*(){const e=t.selectField(i);if(k(e,i),null!=e.validation.currentValidation&&e.validation.currentValidation.cancellationToken.cancel(),0===Object.keys(e.validation.validators).length)return;const r=[];for(const t of e.validation.validatorsOrder){const n=e.validation.validators[t];null!=n&&r.push(Object.assign({},n))}const s=e.getValue(e),a=new Date,l=new f(()=>{n.update(t=>{const e=t.selectField(i);if(null==e)return;const n=e.validation;null!=n.currentValidation&&n.currentValidation.started.getTime()===a.getTime()&&(n.currentValidation=void 0)})});e.validation.currentValidation={started:a,cancellationToken:l},e.validation.results=[];for(const t of r){if(!t.shouldValidate(s))continue;if(l.cancellationRequested)return;const e=t.validate(s,V(o.Validation,t.name));if(null==e)continue;let r=void 0;if(r=j(e)?yield e:e,0===r.length)continue;const a=r.map(e=>F(e,t.name));y(i,n,l,t=>{t.validation.results.push(...a)})}y(i,n,l,t=>{t.validation.currentValidation=void 0})}))}(t,0,i,e)})),registerValidator:(e,i)=>{const r=t.selectField(e);k(r,e);const s=n.generate();return r.validation.validators[s]=Object.assign(Object.assign({},i),{id:s}),r.validation.validatorsOrder.push(s),s},unregisterValidator:(e,n)=>{const i=t.selectField(e);if(null==i||null==i.validation.validators[n])return;delete i.validation.validators[n];const r=i.validation.validatorsOrder.findIndex(t=>t===n);-1!==r&&i.validation.validatorsOrder.splice(r,1)},setFormErrors:t=>{!function t(e,n){for(const i of Object.keys(n)){const r=n[i],s=e.fields[i];if(null==s||null==r)continue;if("object"==typeof r&&!Array.isArray(r)){t(s,r);continue}const a=[];for(const t of r){let e;e="string"!=typeof t?t:{message:t,type:l.Error,origin:o.FormSubmit},a.push(e)}s.validation.results=a}}(e,t)}}}function V(t,e){return{error:(n,i)=>({type:l.Error,message:n,code:i,origin:t,validatorName:e}),warning:(n,i)=>({type:l.Warning,message:n,code:i,origin:t,validatorName:e})}}function y(t,e,n,i){n.cancellationRequested||setTimeout(()=>{n.cancellationRequested||e.update(e=>{const n=e.selectField(t);null!=n&&i(n)})},0)}function F(t,e){return"string"!=typeof t?t:{message:t,validatorName:e,type:l.Error,origin:o.Validation}}const w=i("@reactway:forms");function b(t){const e={id:"value",updateFieldValue:(e,n,i)=>{var r,s;const a=t.selectField(e);if(k(a,e),!M(a.data))throw new Error("Not implemented.");const l=a.data.modifiers,o=Object.keys(l);if(0===o.length)return a.data.currentValue=n,a.data.selection=i,void t.updateFieldStatus(e,t=>{t.touched=!0,t.pristine=n===a.data.initialValue});const d={value:null!==(r=a.data.transientValue)&&void 0!==r?r:a.data.currentValue,caretPosition:null===(s=a.data.selection)||void 0===s?void 0:s.selectionStart};let u=n,c=void 0,f=null==i?void 0:i.selectionStart;for(const t of o){const e=l[t];if(null==e)throw new Error("Should never happen.");const n={value:u,caretPosition:f},i=e.parse(n,d);u=i.currentValue,null!=i.caretPosition&&(f=i.caretPosition),null==c&&null!=i.transientValue&&(c=i.transientValue)}let h=void 0;null!=f&&(h={selectionStart:f,selectionEnd:f,selectionDirection:"none"}),a.data.currentValue=u,a.data.transientValue=c,w.log("Setting new selection to:",Object.assign({},h)),a.data.selection=h},resetFieldValue:n=>{const i=t.selectField(n);if(k(i,n),!M(i.data))throw new Error("Only input field can be reset.");e.updateFieldValue(n,i.data.initialValue)},clearFieldValue:n=>{const i=t.selectField(n);if(k(i,n),!M(i.data))throw new Error("Only input field can be cleared.");e.updateFieldValue(n,i.data.defaultValue)},registerModifier:(e,i)=>{const r=t.selectField(e);if(k(r,e),!M(r.data))throw new Error("Not implemented.");const s=n.generate();return r.data.modifiers[s]=Object.assign(Object.assign({},i),{id:s}),s},unregisterModifier:(e,n)=>{const i=t.selectField(e);if(null==i)return;if(!M(i.data))throw new Error("Not implemented.");if(null==i.data.modifiers[n])return;const r=i.data.modifiersOrder.findIndex(t=>t===n);-1!==r&&i.data.modifiersOrder.splice(r,1)}};return e}function O(t,n){return{id:"status",updateFieldStatus:(i,r)=>{const s=t.selectField(i);k(s,i);const a=e(s.status,()=>{});let l;const o=e(a,t=>{r(t)},t=>{l=t});r(s.status);const d=t=>null!=l.find(e=>e.path.includes(t));o.touched&&d("touched")&&I(n,t,i,t=>{t.touched=!0}),d("pristine")&&I(n,t,i,t=>{t.pristine=o.pristine}),d("readonly")&&function t(e,n){for(const i of Object.keys(e.fields)){const r=e.fields[i];null!=r&&(n(r.status),t(r,n))}}(s,t=>{t.readonly=o.readonly})}}}function I(t,e,n,i){k(e.selectField(n),n);let r=n;for(;null!=(r=e.getFieldParentId(r));){const t=e.selectField(r);k(t,n),i(t.status)}i(t.status)}function j(t){return null!=t.then&&null!=t.catch}function S(t,e){return null==e?t:`${e}.${t}`}function E(t){const e=t.lastIndexOf(".");return-1===e?t:t.slice(e+".".length)}function k(t,e){if(null==t)throw new Error(`Field '${String(e)}' does not exist in a given state.`)}function x(t,e){if(null==t)throw new Error(`Updater '${e}' does not exist.`)}function R(){return{fields:{},status:{disabled:!1,pristine:!0,touched:!1,readonly:!1,permanent:!1},validation:{results:[],validators:{},validatorsOrder:[]}}}function U(){return{disabled:!1,pristine:!0,touched:!1,readonly:!1,permanent:!1}}function P(t,e,n,i){return void 0===e&&(e=t),void 0===n&&(n=e),{defaultValue:t,initialValue:e,currentValue:n,transientValue:i}}function $(t,e,n,i){return void 0===e&&(e=t),void 0===n&&(n=e),{defaultValue:t,initialValue:e,currentValue:n,transientValue:i,modifiers:{},modifiersOrder:[]}}function A(){return{validation:m,value:b,status:O}}function B(){return{results:[],validators:{},validatorsOrder:[]}}function M(t){const e=t;return void 0!==e.defaultValue&&void 0!==e.initialValue&&void 0!==e.currentValue&&void 0!==e.modifiers&&"object"==typeof e.modifiers&&Array.isArray(e.modifiersOrder)}function N(t,e){const n=n=>{if(n===u)return t;const i=e[n];if(null!=i)return i;const r=q(t,n);return null!=r&&(e[n]=r),r};return{selectField:n,selectFieldParent:t=>{const e=L(t);if(null!=e)return n(e)},getActiveFieldId:()=>t.data.activeFieldId,getFieldParentId:L,getFormValue:()=>t.getValue(t)}}function T(t,e,n,i){const r=N(e,i),s=Object.assign(Object.assign({},r),{registerField:(t,n)=>{!function(t,e,n){if(n.computedValue&&M(n.data))throw new Error(`Field ${e} is marked to have computedValue, but also has data of an input field.`);const i=E(e),r=function t(e,n){const i=n.indexOf(".");if(-1===i)return e;{const r=n.slice(0,i),s=n.slice(i+".".length),a=e.fields[r];if(null==a)return;return t(a,s)}}(t,e);if(null==r)throw new Error(`Parent for field '${e}' to be registered on was not found.`);const s=r.fields[i];if(null!=s&&!1===s.status.permanent)throw new Error(`Field '${e}' has already been registered.`);if(null==s){r.fields[i]=Object.assign(Object.assign(Object.assign({},{fields:{},status:{disabled:!1,pristine:!0,touched:!1,readonly:!1,permanent:!1},validation:{results:[],validators:{},validatorsOrder:[]}}),n),{id:e,name:i,fields:{}})}}(e,t,n)},unregisterField:t=>{!function(t,e){const n=_(t,e);if(null==n)return;const i=E(e),r=n.fields[i];if(null==r)return;if(r.status.permanent)return;n.fields[i]=void 0}(e,t)},setActiveFieldId:t=>{e.data.activeFieldId=t},updateFieldStatus:(t,e)=>{s.getUpdater("status").updateFieldStatus(t,e)},getUpdater:i=>function(t,e,n,i,r){const s=i[r];if(null!=s)return s(t,e,n);return}(s,e,n,t,i),enqueueUpdate:t=>{setTimeout(()=>n.update(t),0)}});return s}function q(t,e){if(e===u)return t;const n=e,i=n.indexOf(".");if(-1===i)return t.fields[n];{const e=n.slice(0,i),r=n.slice(i+".".length),s=t.fields[e];if(null==s)return;return q(s,r)}}function L(t){if(t===u)return;const e=t.lastIndexOf(".");return-1===e?u:t.slice(0,e)}function _(t,e){const n=L(e);if(null!=n)return q(t,n)}class W{constructor(n,i){this.emitter=new t,this.handlers={},this.handlerIdsByFieldId={},this.count=0,this.fieldHandlersListener=t=>{const e=Object.keys(this.handlerIdsByFieldId);if(0===e.length)return;const n=[];t.map(t=>{const e=t.path.filter(t=>"fields"!==t).join(".");n.push(e)});const i=[];for(const t of e){if(i.includes(t))continue;n.some(e=>!!e.startsWith(t)&&"."===e.charAt(t.length))&&i.push(t)}const r=i.map(t=>this.handlerIdsByFieldId[t]).flatMap(t=>t);for(const e of r){const n=this.handlers[e];null!=n&&n(t)}},this.state=e(n(),()=>{}),this.updaters=null!=i?Object.assign({},i):A(),this.emitter.addListener(this.fieldHandlersListener)}get state(){return this._state}set state(t){var e;if(!0===window.debugState){const n=new Error;console.groupCollapsed("State being updated:",Object.assign({},t)),console.log(null===(e=n.stack)||void 0===e?void 0:e.split("\n")[3]),console.groupEnd()}this._state=t,this._helpers=N(this._state,{})}get helpers(){return this._helpers}getState(){return this.state}addListener(t,e){const n=`h${this.count++}`;if(this.handlers[n]=t,null==e||0===e.length)return this.emitter.addListener(e=>{t(e)});const i=[];for(const t of e){let e=this.handlerIdsByFieldId[t];if(null==e&&(e=[],this.handlerIdsByFieldId[t]=e),e.includes(n))continue;e.push(n);const r=()=>{if(null==this.handlerIdsByFieldId[t])return;const e=this.handlerIdsByFieldId[t],i=e.indexOf(n);-1!==i&&e.splice(i,1)};i.push(r)}return()=>{for(const t of i)t()}}update(t){const n=[],i=e(this.state,e=>{t(T(this.updaters,e,this,{}),e)},t=>{n.push(...t)});this.state!==i&&(this.state=i,this.emitter.emit(n))}}export{f as CancellationTokenImpl,u as FormSelector,g as FormsStores,d as IdSeparator,s as StatusUpdater,O as StatusUpdaterFactory,W as Store,o as ValidationResultOrigin,l as ValidationResultType,a as ValidationUpdater,m as ValidationUpdaterFactory,r as ValueUpdater,b as ValueUpdaterFactory,k as assertFieldIsDefined,x as assertUpdaterIsDefined,v as constructFieldHelpers,p as constructInputFieldHelpers,N as constructStoreHelpers,T as constructUpdateStoreHelpers,V as constructValidatorHelpers,w as formsLogger,S as generateFieldId,R as getDefaultState,U as getDefaultStatuses,A as getDefaultUpdatersFactories,B as getDefaultValidation,E as getFieldNameFromId,L as getFieldParentId,$ as getInitialInputData,P as getInputValues,M as isInputFieldData,j as isPromise,q as selectField,_ as selectFieldParent};
function __awaiter(thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
class CancellationTokenImpl {
constructor(cancellationCallback, tokenName) {
this.tokenName = tokenName;
this.cancelled = false;
this.cancellationCallback = cancellationCallback;
}
get cancellationRequested() {
return this.cancelled;
}
cancel() {
var _a;
if (this.cancelled) {
return;
}
this.cancelled = true;
(_a = this.cancellationCallback) === null || _a === void 0 ? void 0 : _a.call(this);
}
}
class StoresRegistry extends TinyEmitter {
constructor() {
super(...arguments);
this.storesRegistry = {};
}
registerStore(formId, store) {
if (this.storesRegistry[formId] != null) {
throw new Error(`Form with formId "${formId}" is already registered.`);
}
this.storesRegistry[formId] = store;
this.emit();
}
unregisterStore(formId) {
this.storesRegistry[formId] = undefined;
this.emit();
}
getStore(formId) {
return this.storesRegistry[formId];
}
getStoresIds() {
return Object.keys(this.storesRegistry);
}
}
class FormsStoresClass {
setFormStoresHandler(newHandler, disposeOldOne = true) {
if (disposeOldOne) {
if (this.instance != null) {
delete this.instance;
}
}
this.instance = newHandler;
}
get registry() {
var _a;
this.instance = (_a = this.instance) !== null && _a !== void 0 ? _a : new StoresRegistry();
return this.instance;
}
}
const FormsStores = new FormsStoresClass();
function constructFieldHelpers(fieldSelector, orderGuards) {
return {
registerValidator: validator => registerHelpers => {
const validationUpdater = registerHelpers.getUpdater("validation");
return validationUpdater.registerValidator(fieldSelector, validator);
},
unregisterValidator: validator => unregisterHelpers => {
const validationUpdater = unregisterHelpers.getUpdater("validation");
validationUpdater.unregisterValidator(fieldSelector, validator);
},
reportValidatorIndex: orderGuards.reportValidatorIndex
};
}
function constructInputFieldHelpers(fieldId, orderGuards) {
return Object.assign(Object.assign(Object.assign({}, constructFieldHelpers(fieldId, orderGuards)), orderGuards), { registerModifier: modifier => registerHelpers => {
const valueUpdater = registerHelpers.getUpdater("value");
return valueUpdater.registerModifier(fieldId, modifier);
}, unregisterModifier: modifierId => unregisterHelpers => {
const valueUpdater = unregisterHelpers.getUpdater("value");
valueUpdater.unregisterModifier(fieldId, modifierId);
} });
}
function ValidationUpdaterFactory(helpers, state, store) {
return {
id: "validation",
validateField: (fieldSelector) => __awaiter(this, void 0, void 0, function* () {
return validateField(helpers, state, store, fieldSelector);
}),
registerValidator: (fieldSelector, validator) => {
const fieldState = helpers.selectField(fieldSelector);
assertFieldIsDefined(fieldState, fieldSelector);
const id = shortid.generate();
const mutableValidators = fieldState.validation.validators;
mutableValidators[id] = Object.assign(Object.assign({}, validator), { id: id });
const mutableValidatorsOrder = fieldState.validation.validatorsOrder;
mutableValidatorsOrder.push(id);
return id;
},
unregisterValidator: (fieldId, validatorId) => {
const fieldState = helpers.selectField(fieldId);
if (fieldState == null || fieldState.validation.validators[validatorId] == null) {
// Gracefully return if fieldState or validator is not found, no need to throw.
return;
}
const mutableValidators = fieldState.validation.validators;
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete mutableValidators[validatorId];
const validatorOrderIndex = fieldState.validation.validatorsOrder.findIndex(x => x === validatorId);
if (validatorOrderIndex === -1) {
return;
}
const mutableValidatorsOrder = fieldState.validation.validatorsOrder;
mutableValidatorsOrder.splice(validatorOrderIndex, 1);
},
setFormErrors: errors => {
// TODO: Rename this updater.
setFormErrors(state, errors);
},
setFieldValidationResults: (fieldSelector, errors) => {
setFieldValidationResults(helpers, fieldSelector, errors);
},
resetFieldValidationResults: fieldSelector => {
resetFieldValidationResults(helpers, fieldSelector);
}
};
}
function validateField(helpers, _draft, store, fieldSelector) {
return __awaiter(this, void 0, void 0, function* () {
const fieldState = helpers.selectField(fieldSelector);
assertFieldIsDefined(fieldState, fieldSelector);
if (fieldState.validation.currentValidation != null) {
fieldState.validation.currentValidation.cancellationToken.cancel();
}
const validatorsKeys = Object.keys(fieldState.validation.validators);
if (validatorsKeys.length === 0) {
return;
}
// Copy validators because we're in an asynchronous context
// and their proxy into current state might be revoked by immer.
const validators = [];
for (const validatorId of fieldState.validation.validatorsOrder) {
const validator = fieldState.validation.validators[validatorId];
if (validator == null) {
// TODO: Should we throw if the validator is not found?
continue;
}
validators.push(Object.assign({}, validator));
}
const fieldValue = fieldState.getValue(fieldState);
// Indicate that the validation has started.
const validationStarted = new Date();
const cancellationToken = new CancellationTokenImpl(() => {
store.update(asyncHelpers => {
const asyncFieldState = asyncHelpers.selectField(fieldSelector);
if (asyncFieldState == null) {
return;
}
const validation = asyncFieldState.validation;
if (validation.currentValidation == null) {
return;
}
// Start times are different, thus a new validation is already in progress.
if (validation.currentValidation.started.getTime() !== validationStarted.getTime()) {
return;
}
validation.currentValidation = undefined;
});
});
// Set current validation and reset results synchronously
// for other validations to not clutter the results and find the cancellation token.
fieldState.validation.currentValidation = {
started: validationStarted,
cancellationToken: cancellationToken
};
fieldState.validation.results = [];
for (const validator of validators) {
if (!validator.shouldValidate(fieldValue)) {
continue;
}
// Check for cancellation before executing possibly costy validate function.
if (cancellationToken.cancellationRequested) {
return;
}
const validatorResult = validator.validate(fieldValue, constructValidatorHelpers(ValidationResultOrigin.Validation, validator.name));
if (validatorResult == null) {
continue;
}
// Gather results.
let results = undefined;
if (!isPromise(validatorResult)) {
results = validatorResult;
}
else {
results = yield validatorResult;
}
if (results.length === 0) {
continue;
}
// Construct proper ValidationResults in case any strings are in results.
const validationResults = results.map(x => resolveValidationResult(x, validator.name));
updateFieldAsync(fieldSelector, store, cancellationToken, state => {
const mutableValidationResults = state.validation.results;
mutableValidationResults.push(...validationResults);
});
}
updateFieldAsync(fieldSelector, store, cancellationToken, state => {
state.validation.currentValidation = undefined;
});
});
}
function constructValidatorHelpers(origin, validatorName) {
return {
error: (message, code) => ({
type: ValidationResultType.Error,
message: message,
code: code,
origin: origin,
validatorName: validatorName
}),
warning: (message, code) => ({
type: ValidationResultType.Warning,
message: message,
code: code,
origin: origin,
validatorName: validatorName
})
};
}
function updateFieldAsync(fieldSelector, store, cancellationToken, updater) {
// Check for cancellation right away.
if (cancellationToken.cancellationRequested) {
return;
}
setTimeout(() => {
// Check for cancellation again at the start of asynchronous scope.
if (cancellationToken.cancellationRequested) {
return;
}
store.update(helpers => {
const fieldState = helpers.selectField(fieldSelector);
if (fieldState == null) {
return;
}
updater(fieldState);
});
}, 0);
}
function resolveValidationResult(value, validatorName) {
if (typeof value !== "string") {
return value;
}
return {
message: value,
validatorName: validatorName,
type: ValidationResultType.Error,
origin: ValidationResultOrigin.Validation
};
}
function setFormErrors(state, errors) {
for (const key of Object.keys(errors)) {
const fieldError = errors[key];
const field = state.fields[key];
if (field == null || fieldError == null) {
continue;
}
if (typeof fieldError === "object" && !Array.isArray(fieldError)) {
setFormErrors(field, fieldError);
continue;
}
const validationResults = [];
for (const error of fieldError) {
let validationResult;
if (typeof error !== "string") {
validationResult = error;
}
else {
validationResult = {
message: error,
type: ValidationResultType.Error,
origin: ValidationResultOrigin.FormSubmit
};
}
validationResults.push(validationResult);
}
field.validation.results = validationResults;
}
}
function setFieldValidationResults(helpers, fieldSelector, errors) {
const fieldState = helpers.selectField(fieldSelector);
assertFieldIsDefined(fieldState);
fieldState.validation.results = errors.map(result => typeof result !== "string"
? result
: {
message: result,
type: ValidationResultType.Error
});
}
function resetFieldValidationResults(helpers, fieldSelector) {
const fieldState = helpers.selectField(fieldSelector);
assertFieldIsDefined(fieldState);
fieldState.validation.results = [];
}
const formsLogger = Debug("@reactway:forms");
const logger = formsLogger.extend("value-updater");
function ValueUpdaterFactory(helpers) {
const valueUpdater = {
id: "value",
updateFieldValue: (fieldId, value, selection) => {
var _a, _b;
const fieldState = helpers.selectField(fieldId);
assertFieldIsDefined(fieldState, fieldId);
if (!isInputFieldData(fieldState.data)) {
// TODO: Review
throw new Error("Not implemented.");
}
fieldState.validation.results = [];
const modifiers = fieldState.data.modifiers;
const modifiersKeys = Object.keys(modifiers);
if (modifiersKeys.length === 0) {
// No modifiers found, thus a value is set directly to currentValue.
fieldState.data.currentValue = value;
fieldState.data.selection = selection;
helpers.updateFieldStatus(fieldId, status => {
status.touched = true;
status.pristine = value === fieldState.data.initialValue;
});
return;
}
// Modifiers found, firing up the modifiers mechanism.
const previousParseValue = {
value: (_a = fieldState.data.transientValue) !== null && _a !== void 0 ? _a : fieldState.data.currentValue,
caretPosition: (_b = fieldState.data.selection) === null || _b === void 0 ? void 0 : _b.selectionStart
};
let newValue = value;
let transientValue = undefined;
let newCaretPosition = selection === null || selection === void 0 ? void 0 : selection.selectionStart;
for (const modifierKey of modifiersKeys) {
const modifier = modifiers[modifierKey];
if (modifier == null) {
throw new Error("Should never happen.");
}
const newParseValue = {
value: newValue,
caretPosition: newCaretPosition
};
const result = modifier.parse(newParseValue, previousParseValue);
newValue = result.currentValue;
// If modifier returned selection
if (result.caretPosition != null) {
// It becomes our newSelection.
newCaretPosition = result.caretPosition;
}
// If transientValue was is null or was already set before
if (transientValue != null || result.transientValue == null) {
// Do nothing.
continue;
}
// When first transientValue is encountered, it wins until it is resolved.
transientValue = result.transientValue;
}
let newSelection = undefined;
if (newCaretPosition != null) {
newSelection = {
selectionStart: newCaretPosition,
selectionEnd: newCaretPosition,
selectionDirection: "none"
};
}
fieldState.data.currentValue = newValue;
fieldState.data.transientValue = transientValue;
logger("Setting new selection to:", Object.assign({}, newSelection));
fieldState.data.selection = newSelection;
},
resetFieldValue: fieldId => {
const fieldState = helpers.selectField(fieldId);
assertFieldIsDefined(fieldState, fieldId);
if (!isInputFieldData(fieldState.data)) {
throw new Error("Only input field can be reset.");
}
valueUpdater.updateFieldValue(fieldId, fieldState.data.initialValue);
},
clearFieldValue: fieldId => {
const fieldState = helpers.selectField(fieldId);
assertFieldIsDefined(fieldState, fieldId);
if (!isInputFieldData(fieldState.data)) {
throw new Error("Only input field can be cleared.");
}
valueUpdater.updateFieldValue(fieldId, fieldState.data.defaultValue);
},
registerModifier: (fieldId, modifier) => {
const fieldState = helpers.selectField(fieldId);
assertFieldIsDefined(fieldState, fieldId);
if (!isInputFieldData(fieldState.data)) {
throw new Error("Not implemented.");
}
const id = shortid.generate();
const modifiers = fieldState.data.modifiers;
const mutableModifiers = modifiers;
mutableModifiers[id] = Object.assign(Object.assign({}, modifier), { id });
return id;
},
unregisterModifier: (fieldId, modifierId) => {
const fieldState = helpers.selectField(fieldId);
if (fieldState == null) {
return;
}
if (!isInputFieldData(fieldState.data)) {
throw new Error("Not implemented.");
}
const modifiers = fieldState.data.modifiers;
if (modifiers[modifierId] == null) {
// Gracefully return if the modifier is not found, no need to throw.
return;
}
const modifierOrderIndex = fieldState.data.modifiersOrder.findIndex(x => x === modifierId);
if (modifierOrderIndex === -1) {
return;
}
const mutableModifiersOrder = fieldState.data.modifiersOrder;
mutableModifiersOrder.splice(modifierOrderIndex, 1);
}
};
return valueUpdater;
}
function StatusUpdaterFactory(helpers, state) {
return {
id: "status",
updateFieldStatus: (fieldSelector, updater) => {
const fieldState = helpers.selectField(fieldSelector);
assertFieldIsDefined(fieldState, fieldSelector);
// eslint-disable-next-line @typescript-eslint/no-empty-function
const prevStatus = produce(fieldState.status, () => { });
let statusPatches;
const newStatus = produce(prevStatus, status => {
updater(status);
}, patches => {
statusPatches = patches;
});
updater(fieldState.status);
const statusChanged = (statusKey) => {
return statusPatches.find(x => x.path.includes(statusKey)) != null;
};
if (newStatus.touched && statusChanged("touched")) {
updateDependentStatusUpwards(state, helpers, fieldSelector, status => {
status.touched = true;
});
}
if (statusChanged("pristine")) {
updateDependentStatusUpwards(state, helpers, fieldSelector, status => {
status.pristine = newStatus.pristine;
});
}
if (statusChanged("readonly")) {
updateDependentStatusDownwards(fieldState, status => {
status.readonly = newStatus.readonly;
});
}
if (prevStatus === newStatus) {
return;
}
}
};
}
function updateDependentStatusDownwards(fieldState, updater) {
// TODO: Review
for (const key of Object.keys(fieldState.fields)) {
const child = fieldState.fields[key];
if (child == null) {
continue;
}
updater(child.status);
updateDependentStatusDownwards(child, updater);
}
}
function updateDependentStatusUpwards(state, helpers, fieldSelector, updater) {
const fieldState = helpers.selectField(fieldSelector);
assertFieldIsDefined(fieldState, fieldSelector);
// Update all parents (that have id) statuses.
let parentId = fieldSelector;
while ((parentId = helpers.getFieldParentId(parentId)) != null) {
const field = helpers.selectField(parentId);
assertFieldIsDefined(field, fieldSelector);
updater(field.status);
}
// Update form (upper-most) status too.
// TODO: Review if this is needed and/or FormSelector should be used.
updater(state.status);
}
function isPromise(candidate) {
return candidate.then != null && candidate.catch != null;
}
function generateFieldId(name, parentId) {
if (parentId == null) {
return name;
}
return `${parentId}${IdSeparator}${name}`;
}
function getFieldNameFromId(fieldId) {
const lastSeparatorIndex = fieldId.lastIndexOf(IdSeparator);
if (lastSeparatorIndex === -1) {
return fieldId;
}
return fieldId.slice(lastSeparatorIndex + IdSeparator.length);
}
function assertFieldIsDefined(field, fieldSelector) {
if (field == null) {
throw new Error(`Field '${String(fieldSelector)}' does not exist in a given state.`);
}
}
function assertUpdaterIsDefined(updater, updaterId) {
if (updater == null) {
throw new Error(`Updater '${updaterId}' does not exist.`);
}
}
function getDefaultState() {
return {
fields: {},
status: getDefaultStatuses(),
validation: getDefaultValidation()
};
}
function getDefaultStatuses() {
return {
disabled: false,
pristine: true,
touched: false,
readonly: false,
permanent: false
};
}
function getInputValues(defaultValue, initialValue, currentValue, transientValue) {
// Triple equals to `undefined`, because `null` might be a valid value.
if (initialValue === undefined) {
initialValue = defaultValue;
}
// Triple equals to `undefined`, because `null` might be a valid value.
if (currentValue === undefined) {
currentValue = initialValue;
}
return {
defaultValue,
initialValue,
currentValue,
transientValue
};
}
function getInitialInputData(defaultValue, initialValue, currentValue, transientValue) {
// Triple equals to `undefined`, because `null` might be a valid value.
if (initialValue === undefined) {
initialValue = defaultValue;
}
// Triple equals to `undefined`, because `null` might be a valid value.
if (currentValue === undefined) {
currentValue = initialValue;
}
return {
defaultValue,
initialValue,
currentValue,
transientValue,
modifiers: {},
modifiersOrder: []
};
}
function getDefaultUpdatersFactories() {
return {
validation: ValidationUpdaterFactory,
value: ValueUpdaterFactory,
status: StatusUpdaterFactory
};
}
function getDefaultValidation() {
return {
results: [],
validators: {},
validatorsOrder: []
};
}
function isInputFieldData(candidate) {
const inputValues = candidate;
return (inputValues.defaultValue !== undefined &&
inputValues.initialValue !== undefined &&
inputValues.currentValue !== undefined &&
inputValues.modifiers !== undefined &&
typeof inputValues.modifiers === "object" &&
Array.isArray(inputValues.modifiersOrder));
}
function constructStoreHelpers(state, fieldsCache) {
const cachedSelectField = fieldSelector => {
if (fieldSelector === FormSelector) {
return state;
}
const cachedField = fieldsCache[fieldSelector];
if (cachedField != null) {
return cachedField;
}
const selectedField = selectField(state, fieldSelector);
if (selectedField != null) {
fieldsCache[fieldSelector] = selectedField;
}
return selectedField;
};
const helpers = {
selectField: cachedSelectField,
selectFieldParent: fieldSelector => {
const parentId = getFieldParentId(fieldSelector);
if (parentId == null) {
return undefined;
}
return cachedSelectField(parentId);
},
getActiveFieldId: () => {
const formState = state;
return formState.data.activeFieldId;
},
getFieldParentId: getFieldParentId,
getFormValue: () => {
return state.getValue(state);
}
};
return helpers;
}
function constructUpdateStoreHelpers(updaters, draft, store, fieldsCache) {
const fieldStoreHelpers = constructStoreHelpers(draft, fieldsCache);
const updateStoreHelpers = Object.assign(Object.assign({}, fieldStoreHelpers), { registerField: (fieldId, initialFieldState) => {
registerField(draft, fieldId, initialFieldState);
}, unregisterField: id => {
unregisterField(draft, id);
}, setActiveFieldId: fieldId => {
const formState = draft;
formState.data.activeFieldId = fieldId;
}, updateFieldStatus: (fieldSelector, updater) => {
const statusUpdater = updateStoreHelpers.getUpdater("status");
statusUpdater.updateFieldStatus(fieldSelector, updater);
}, getUpdater: updaterId => {
return getUpdater(updateStoreHelpers, draft, store, updaters, updaterId);
}, enqueueUpdate: updater => {
setTimeout(() => store.update(updater), 0);
} });
return updateStoreHelpers;
}
function registerField(state, fieldId, initialFieldState) {
if (initialFieldState.computedValue && isInputFieldData(initialFieldState.data)) {
throw new Error(`Field ${fieldId} is marked to have computedValue, but also has data of an input field.`);
}
const fieldName = getFieldNameFromId(fieldId);
const parentField = selectRegistrationParent(state, fieldId);
if (parentField == null) {
throw new Error(`Parent for field '${fieldId}' to be registered on was not found.`);
}
const fieldState = parentField.fields[fieldName];
if (fieldState != null && fieldState.status.permanent === false) {
throw new Error(`Field '${fieldId}' has already been registered.`);
}
if (fieldState == null) {
// Make fields non-read-only
const mutableFields = parentField.fields;
// Add field into the state.
mutableFields[fieldName] = Object.assign(Object.assign(Object.assign({}, getDefaultState()), initialFieldState), { id: fieldId, name: fieldName, fields: {} });
}
}
function unregisterField(state, id) {
const parentField = selectFieldParent(state, id);
if (parentField == null) {
return;
}
const fieldName = getFieldNameFromId(id);
const field = parentField.fields[fieldName];
if (field == null) {
return;
}
if (field.status.permanent) {
return;
}
// Make fields non-read-only
const mutableFields = parentField.fields;
mutableFields[fieldName] = undefined;
}
function getUpdater(helpers, fieldState, store, updaters, updaterId) {
const factory = updaters[updaterId];
if (factory != null) {
return factory(helpers, fieldState, store);
}
return undefined;
}
function selectRegistrationParent(state, fieldId) {
const separatorIndex = fieldId.indexOf(IdSeparator);
if (separatorIndex === -1) {
return state;
}
else {
const firstChildName = fieldId.slice(0, separatorIndex);
const nextChildId = fieldId.slice(separatorIndex + IdSeparator.length);
// TODO: Review
const child = state.fields[firstChildName];
if (child == null) {
return undefined;
}
return selectRegistrationParent(child, nextChildId);
}
}
// TODO: Do we need recursion here?
function selectField(state, selector) {
if (selector === FormSelector) {
return state;
}
const fieldId = selector;
const firstSeparatorIndex = fieldId.indexOf(IdSeparator);
if (firstSeparatorIndex === -1) {
return state.fields[fieldId];
}
else {
const name = fieldId.slice(0, firstSeparatorIndex);
const nextFieldId = fieldId.slice(firstSeparatorIndex + IdSeparator.length);
const child = state.fields[name];
if (child == null) {
return undefined;
}
return selectField(child, nextFieldId);
}
}
function getFieldParentId(fieldSelector) {
if (fieldSelector === FormSelector) {
return undefined;
}
const lastSeparatorIndex = fieldSelector.lastIndexOf(IdSeparator);
if (lastSeparatorIndex === -1) {
return FormSelector;
}
return fieldSelector.slice(0, lastSeparatorIndex);
}
function selectFieldParent(state, fieldId) {
const parentId = getFieldParentId(fieldId);
if (parentId == null) {
return undefined;
}
return selectField(state, parentId);
}
class Store {
constructor(initialStateFactory, updatersFactories) {
// super();
this.emitter = new TinyEmitter();
this.handlers = {};
this.handlerIdsByFieldId = {};
this.count = 0;
this.fieldHandlersListener = (patches) => {
const fieldsKeys = Object.keys(this.handlerIdsByFieldId);
if (fieldsKeys.length === 0) {
return;
}
// Strongly type the key to catch an error if it changes.
const fieldsKey = "fields";
const paths = [];
patches.map(patch => {
const idPath = patch.path.filter(x => x !== fieldsKey).join(IdSeparator);
paths.push(idPath);
});
const fieldsToCall = [];
for (const fieldKey of fieldsKeys) {
// TODO: Is this check needed?
if (fieldsToCall.includes(fieldKey)) {
continue;
}
const fieldHandlersShouldBeCalled = paths.some(path => {
if (!path.startsWith(fieldKey)) {
return false;
}
return path.charAt(fieldKey.length) === IdSeparator;
});
if (!fieldHandlersShouldBeCalled) {
continue;
}
fieldsToCall.push(fieldKey);
}
const handlerIds = fieldsToCall
.map(fieldId => {
// We've accumulated the ids from this object, thus they exist.
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return this.handlerIdsByFieldId[fieldId];
})
.flatMap(x => x);
for (const handlerId of handlerIds) {
const handler = this.handlers[handlerId];
if (handler == null) {
continue;
}
handler(patches);
}
};
// eslint-disable-next-line @typescript-eslint/no-empty-function
this.state = produce(initialStateFactory(), () => { });
this.updaters = updatersFactories != null ? Object.assign({}, updatersFactories) : getDefaultUpdatersFactories();
this.emitter.addListener(this.fieldHandlersListener);
}
get state() {
return this._state;
}
set state(value) {
var _a;
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
if (window.debugState === true) {
const err = new Error();
/* eslint-disable no-console */
console.groupCollapsed("State being updated:", Object.assign({}, value));
console.log((_a = err.stack) === null || _a === void 0 ? void 0 : _a.split("\n")[3]);
console.groupEnd();
/* eslint-enable no-console */
}
this._state = value;
this._helpers = constructStoreHelpers(this._state, {});
}
get helpers() {
return this._helpers;
}
getState() {
return this.state;
}
addListener(handler, dependentFields) {
const handlerId = `h${this.count++}`;
this.handlers[handlerId] = handler;
if (dependentFields == null || dependentFields.length === 0) {
return this.emitter.addListener(patches => {
handler(patches);
});
}
const unregisterCallbacks = [];
for (const fieldId of dependentFields) {
let handlerIds = this.handlerIdsByFieldId[fieldId];
if (handlerIds == null) {
handlerIds = [];
this.handlerIdsByFieldId[fieldId] = handlerIds;
}
if (handlerIds.includes(handlerId)) {
continue;
}
// Register callback
handlerIds.push(handlerId);
const unregisterCallback = () => {
if (this.handlerIdsByFieldId[fieldId] == null) {
return;
}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion, no-shadow
const handlerIds = this.handlerIdsByFieldId[fieldId];
const index = handlerIds.indexOf(handlerId);
if (index === -1) {
return;
}
handlerIds.splice(index, 1);
};
unregisterCallbacks.push(unregisterCallback);
}
return () => {
for (const callback of unregisterCallbacks) {
callback();
}
};
}
update(updater) {
const patches = [];
const newState = produce(this.state, draft => {
updater(constructUpdateStoreHelpers(this.updaters, draft, this, {}), draft);
}, updatePatches => {
patches.push(...updatePatches);
});
if (this.state === newState) {
return;
}
this.state = newState;
this.emitter.emit(patches);
}
}
export { CancellationTokenImpl, FormSelector, FormsStores, IdSeparator, StatusUpdater, StatusUpdaterFactory, Store, ValidationResultOrigin, ValidationResultType, ValidationUpdater, ValidationUpdaterFactory, ValueUpdater, ValueUpdaterFactory, assertFieldIsDefined, assertUpdaterIsDefined, constructFieldHelpers, constructInputFieldHelpers, constructStoreHelpers, constructUpdateStoreHelpers, constructValidatorHelpers, formsLogger, generateFieldId, getDefaultState, getDefaultStatuses, getDefaultUpdatersFactories, getDefaultValidation, getFieldNameFromId, getFieldParentId, getInitialInputData, getInputValues, isInputFieldData, isPromise, selectField, selectFieldParent };

@@ -1,2 +0,30 @@

"use strict";function t(t){return t&&"object"==typeof t&&"default"in t?t.default:t}Object.defineProperty(exports,"__esModule",{value:!0});var e=require("@reactway/tiny-emitter"),i=t(require("immer")),n=t(require("shortid")),r=t(require("debug"));var s,a;(s=exports.ValidationResultType||(exports.ValidationResultType={}))[s.Error=0]="Error",s[s.Warning=1]="Warning",(a=exports.ValidationResultOrigin||(exports.ValidationResultOrigin={}))[a.Unknown=0]="Unknown",a[a.Validation=1]="Validation",a[a.FormSubmit=2]="FormSubmit";const o=Symbol("form");
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var tinyEmitter = require('@reactway/tiny-emitter');
var produce = _interopDefault(require('immer'));
var shortid = _interopDefault(require('shortid'));
var Debug = _interopDefault(require('debug'));
const ValueUpdater = "value";
const StatusUpdater = "status";
const ValidationUpdater = "validation";
// These comments originated in the v4 codebase and are kept for historical purpose:
(function (ValidationResultType) {
ValidationResultType[ValidationResultType["Error"] = 0] = "Error";
ValidationResultType[ValidationResultType["Warning"] = 1] = "Warning";
})(exports.ValidationResultType || (exports.ValidationResultType = {}));
(function (ValidationResultOrigin) {
ValidationResultOrigin[ValidationResultOrigin["Unknown"] = 0] = "Unknown";
ValidationResultOrigin[ValidationResultOrigin["Validation"] = 1] = "Validation";
ValidationResultOrigin[ValidationResultOrigin["FormSubmit"] = 2] = "FormSubmit";
})(exports.ValidationResultOrigin || (exports.ValidationResultOrigin = {}));
const IdSeparator = ".";
const FormSelector = Symbol("form");
/*! *****************************************************************************

@@ -16,2 +44,922 @@ Copyright (c) Microsoft Corporation. All rights reserved.

***************************************************************************** */
function l(t,e,i,n){return new(i||(i=Promise))((function(r,s){function a(t){try{l(n.next(t))}catch(t){s(t)}}function o(t){try{l(n.throw(t))}catch(t){s(t)}}function l(t){t.done?r(t.value):new i((function(e){e(t.value)})).then(a,o)}l((n=n.apply(t,e||[])).next())}))}class d{constructor(t,e){this.tokenName=e,this.cancelled=!1,this.cancellationCallback=t}get cancellationRequested(){return this.cancelled}cancel(){var t;this.cancelled||(this.cancelled=!0,null===(t=this.cancellationCallback)||void 0===t||t.call(this))}}class u extends e.TinyEmitter{constructor(){super(...arguments),this.storesRegistry={}}registerStore(t,e){if(null!=this.storesRegistry[t])throw new Error(`Form with formId "${t}" is already registered.`);this.storesRegistry[t]=e,this.emit()}unregisterStore(t){this.storesRegistry[t]=void 0,this.emit()}getStore(t){return this.storesRegistry[t]}getStoresIds(){return Object.keys(this.storesRegistry)}}const c=new class{setFormStoresHandler(t,e=!0){e&&null!=this.instance&&delete this.instance,this.instance=t}get registry(){var t;return this.instance=null!==(t=this.instance)&&void 0!==t?t:new u,this.instance}};function f(t,e){return{registerValidator:e=>i=>i.getUpdater("validation").registerValidator(t,e),unregisterValidator:e=>i=>{i.getUpdater("validation").unregisterValidator(t,e)},reportValidatorIndex:e.reportValidatorIndex}}function p(t,e,i){return{id:"validation",validateField:e=>l(this,void 0,void 0,(function*(){return function(t,e,i,n){return l(this,void 0,void 0,(function*(){const e=t.selectField(n);if(b(e,n),null!=e.validation.currentValidation&&e.validation.currentValidation.cancellationToken.cancel(),0===Object.keys(e.validation.validators).length)return;const r=[];for(const t of e.validation.validatorsOrder){const i=e.validation.validators[t];null!=i&&r.push(Object.assign({},i))}const s=e.getValue(e),a=new Date,o=new d(()=>{i.update(t=>{const e=t.selectField(n);if(null==e)return;const i=e.validation;null!=i.currentValidation&&i.currentValidation.started.getTime()===a.getTime()&&(i.currentValidation=void 0)})});e.validation.currentValidation={started:a,cancellationToken:o},e.validation.results=[];for(const t of r){if(!t.shouldValidate(s))continue;if(o.cancellationRequested)return;const e=t.validate(s,h(exports.ValidationResultOrigin.Validation,t.name));if(null==e)continue;let r=void 0;if(r=x(e)?yield e:e,0===r.length)continue;const a=r.map(e=>v(e,t.name));g(n,i,o,t=>{t.validation.results.push(...a)})}g(n,i,o,t=>{t.validation.currentValidation=void 0})}))}(t,0,i,e)})),registerValidator:(e,i)=>{const r=t.selectField(e);b(r,e);const s=n.generate();return r.validation.validators[s]=Object.assign(Object.assign({},i),{id:s}),r.validation.validatorsOrder.push(s),s},unregisterValidator:(e,i)=>{const n=t.selectField(e);if(null==n||null==n.validation.validators[i])return;delete n.validation.validators[i];const r=n.validation.validatorsOrder.findIndex(t=>t===i);-1!==r&&n.validation.validatorsOrder.splice(r,1)},setFormErrors:t=>{!function t(e,i){for(const n of Object.keys(i)){const r=i[n],s=e.fields[n];if(null==s||null==r)continue;if("object"==typeof r&&!Array.isArray(r)){t(s,r);continue}const a=[];for(const t of r){let e;e="string"!=typeof t?t:{message:t,type:exports.ValidationResultType.Error,origin:exports.ValidationResultOrigin.FormSubmit},a.push(e)}s.validation.results=a}}(e,t)}}}function h(t,e){return{error:(i,n)=>({type:exports.ValidationResultType.Error,message:i,code:n,origin:t,validatorName:e}),warning:(i,n)=>({type:exports.ValidationResultType.Warning,message:i,code:n,origin:t,validatorName:e})}}function g(t,e,i,n){i.cancellationRequested||setTimeout(()=>{i.cancellationRequested||e.update(e=>{const i=e.selectField(t);null!=i&&n(i)})},0)}function v(t,e){return"string"!=typeof t?t:{message:t,validatorName:e,type:exports.ValidationResultType.Error,origin:exports.ValidationResultOrigin.Validation}}const m=r("@reactway:forms");function V(t){const e={id:"value",updateFieldValue:(e,i,n)=>{var r,s;const a=t.selectField(e);if(b(a,e),!E(a.data))throw new Error("Not implemented.");const o=a.data.modifiers,l=Object.keys(o);if(0===l.length)return a.data.currentValue=i,a.data.selection=n,void t.updateFieldStatus(e,t=>{t.touched=!0,t.pristine=i===a.data.initialValue});const d={value:null!==(r=a.data.transientValue)&&void 0!==r?r:a.data.currentValue,caretPosition:null===(s=a.data.selection)||void 0===s?void 0:s.selectionStart};let u=i,c=void 0,f=null==n?void 0:n.selectionStart;for(const t of l){const e=o[t];if(null==e)throw new Error("Should never happen.");const i={value:u,caretPosition:f},n=e.parse(i,d);u=n.currentValue,null!=n.caretPosition&&(f=n.caretPosition),null==c&&null!=n.transientValue&&(c=n.transientValue)}let p=void 0;null!=f&&(p={selectionStart:f,selectionEnd:f,selectionDirection:"none"}),a.data.currentValue=u,a.data.transientValue=c,m.log("Setting new selection to:",Object.assign({},p)),a.data.selection=p},resetFieldValue:i=>{const n=t.selectField(i);if(b(n,i),!E(n.data))throw new Error("Only input field can be reset.");e.updateFieldValue(i,n.data.initialValue)},clearFieldValue:i=>{const n=t.selectField(i);if(b(n,i),!E(n.data))throw new Error("Only input field can be cleared.");e.updateFieldValue(i,n.data.defaultValue)},registerModifier:(e,i)=>{const r=t.selectField(e);if(b(r,e),!E(r.data))throw new Error("Not implemented.");const s=n.generate();return r.data.modifiers[s]=Object.assign(Object.assign({},i),{id:s}),s},unregisterModifier:(e,i)=>{const n=t.selectField(e);if(null==n)return;if(!E(n.data))throw new Error("Not implemented.");if(null==n.data.modifiers[i])return;const r=n.data.modifiersOrder.findIndex(t=>t===i);-1!==r&&n.data.modifiersOrder.splice(r,1)}};return e}function F(t,e){return{id:"status",updateFieldStatus:(n,r)=>{const s=t.selectField(n);b(s,n);const a=i(s.status,()=>{});let o;const l=i(a,t=>{r(t)},t=>{o=t});r(s.status);const d=t=>null!=o.find(e=>e.path.includes(t));l.touched&&d("touched")&&y(e,t,n,t=>{t.touched=!0}),d("pristine")&&y(e,t,n,t=>{t.pristine=l.pristine}),d("readonly")&&function t(e,i){for(const n of Object.keys(e.fields)){const r=e.fields[n];null!=r&&(i(r.status),t(r,i))}}(s,t=>{t.readonly=l.readonly})}}}function y(t,e,i,n){b(e.selectField(i),i);let r=i;for(;null!=(r=e.getFieldParentId(r));){const t=e.selectField(r);b(t,i),n(t.status)}n(t.status)}function x(t){return null!=t.then&&null!=t.catch}function O(t){const e=t.lastIndexOf(".");return-1===e?t:t.slice(e+".".length)}function b(t,e){if(null==t)throw new Error(`Field '${String(e)}' does not exist in a given state.`)}function w(){return{fields:{},status:{disabled:!1,pristine:!0,touched:!1,readonly:!1,permanent:!1},validation:{results:[],validators:{},validatorsOrder:[]}}}function I(){return{disabled:!1,pristine:!0,touched:!1,readonly:!1,permanent:!1}}function S(){return{validation:p,value:V,status:F}}function j(){return{results:[],validators:{},validatorsOrder:[]}}function E(t){const e=t;return void 0!==e.defaultValue&&void 0!==e.initialValue&&void 0!==e.currentValue&&void 0!==e.modifiers&&"object"==typeof e.modifiers&&Array.isArray(e.modifiersOrder)}function R(t,e){const i=i=>{if(i===o)return t;const n=e[i];if(null!=n)return n;const r=k(t,i);return null!=r&&(e[i]=r),r};return{selectField:i,selectFieldParent:t=>{const e=T(t);if(null!=e)return i(e)},getActiveFieldId:()=>t.data.activeFieldId,getFieldParentId:T,getFormValue:()=>t.getValue(t)}}function U(t,e,i,n){const r=R(e,n),s=Object.assign(Object.assign({},r),{registerField:(t,i)=>{!function(t,e,i){if(i.computedValue&&E(i.data))throw new Error(`Field ${e} is marked to have computedValue, but also has data of an input field.`);const n=O(e),r=function t(e,i){const n=i.indexOf(".");if(-1===n)return e;{const r=i.slice(0,n),s=i.slice(n+".".length),a=e.fields[r];if(null==a)return;return t(a,s)}}(t,e);if(null==r)throw new Error(`Parent for field '${e}' to be registered on was not found.`);const s=r.fields[n];if(null!=s&&!1===s.status.permanent)throw new Error(`Field '${e}' has already been registered.`);if(null==s){r.fields[n]=Object.assign(Object.assign(Object.assign({},{fields:{},status:{disabled:!1,pristine:!0,touched:!1,readonly:!1,permanent:!1},validation:{results:[],validators:{},validatorsOrder:[]}}),i),{id:e,name:n,fields:{}})}}(e,t,i)},unregisterField:t=>{!function(t,e){const i=P(t,e);if(null==i)return;const n=O(e),r=i.fields[n];if(null==r)return;if(r.status.permanent)return;i.fields[n]=void 0}(e,t)},setActiveFieldId:t=>{e.data.activeFieldId=t},updateFieldStatus:(t,e)=>{s.getUpdater("status").updateFieldStatus(t,e)},getUpdater:n=>function(t,e,i,n,r){const s=n[r];if(null!=s)return s(t,e,i);return}(s,e,i,t,n),enqueueUpdate:t=>{setTimeout(()=>i.update(t),0)}});return s}function k(t,e){if(e===o)return t;const i=e,n=i.indexOf(".");if(-1===n)return t.fields[i];{const e=i.slice(0,n),r=i.slice(n+".".length),s=t.fields[e];if(null==s)return;return k(s,r)}}function T(t){if(t===o)return;const e=t.lastIndexOf(".");return-1===e?o:t.slice(0,e)}function P(t,e){const i=T(e);if(null!=i)return k(t,i)}exports.CancellationTokenImpl=d,exports.FormSelector=o,exports.FormsStores=c,exports.IdSeparator=".",exports.StatusUpdater="status",exports.StatusUpdaterFactory=F,exports.Store=class{constructor(t,n){this.emitter=new e.TinyEmitter,this.handlers={},this.handlerIdsByFieldId={},this.count=0,this.fieldHandlersListener=t=>{const e=Object.keys(this.handlerIdsByFieldId);if(0===e.length)return;const i=[];t.map(t=>{const e=t.path.filter(t=>"fields"!==t).join(".");i.push(e)});const n=[];for(const t of e){if(n.includes(t))continue;i.some(e=>!!e.startsWith(t)&&"."===e.charAt(t.length))&&n.push(t)}const r=n.map(t=>this.handlerIdsByFieldId[t]).flatMap(t=>t);for(const e of r){const i=this.handlers[e];null!=i&&i(t)}},this.state=i(t(),()=>{}),this.updaters=null!=n?Object.assign({},n):S(),this.emitter.addListener(this.fieldHandlersListener)}get state(){return this._state}set state(t){var e;if(!0===window.debugState){const i=new Error;console.groupCollapsed("State being updated:",Object.assign({},t)),console.log(null===(e=i.stack)||void 0===e?void 0:e.split("\n")[3]),console.groupEnd()}this._state=t,this._helpers=R(this._state,{})}get helpers(){return this._helpers}getState(){return this.state}addListener(t,e){const i=`h${this.count++}`;if(this.handlers[i]=t,null==e||0===e.length)return this.emitter.addListener(e=>{t(e)});const n=[];for(const t of e){let e=this.handlerIdsByFieldId[t];if(null==e&&(e=[],this.handlerIdsByFieldId[t]=e),e.includes(i))continue;e.push(i);const r=()=>{if(null==this.handlerIdsByFieldId[t])return;const e=this.handlerIdsByFieldId[t],n=e.indexOf(i);-1!==n&&e.splice(n,1)};n.push(r)}return()=>{for(const t of n)t()}}update(t){const e=[],n=i(this.state,e=>{t(U(this.updaters,e,this,{}),e)},t=>{e.push(...t)});this.state!==n&&(this.state=n,this.emitter.emit(e))}},exports.ValidationUpdater="validation",exports.ValidationUpdaterFactory=p,exports.ValueUpdater="value",exports.ValueUpdaterFactory=V,exports.assertFieldIsDefined=b,exports.assertUpdaterIsDefined=function(t,e){if(null==t)throw new Error(`Updater '${e}' does not exist.`)},exports.constructFieldHelpers=f,exports.constructInputFieldHelpers=function(t,e){return Object.assign(Object.assign(Object.assign({},f(t,e)),e),{registerModifier:e=>i=>i.getUpdater("value").registerModifier(t,e),unregisterModifier:e=>i=>{i.getUpdater("value").unregisterModifier(t,e)}})},exports.constructStoreHelpers=R,exports.constructUpdateStoreHelpers=U,exports.constructValidatorHelpers=h,exports.formsLogger=m,exports.generateFieldId=function(t,e){return null==e?t:`${e}.${t}`},exports.getDefaultState=w,exports.getDefaultStatuses=I,exports.getDefaultUpdatersFactories=S,exports.getDefaultValidation=j,exports.getFieldNameFromId=O,exports.getFieldParentId=T,exports.getInitialInputData=function(t,e,i,n){return void 0===e&&(e=t),void 0===i&&(i=e),{defaultValue:t,initialValue:e,currentValue:i,transientValue:n,modifiers:{},modifiersOrder:[]}},exports.getInputValues=function(t,e,i,n){return void 0===e&&(e=t),void 0===i&&(i=e),{defaultValue:t,initialValue:e,currentValue:i,transientValue:n}},exports.isInputFieldData=E,exports.isPromise=x,exports.selectField=k,exports.selectFieldParent=P;
function __awaiter(thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
class CancellationTokenImpl {
constructor(cancellationCallback, tokenName) {
this.tokenName = tokenName;
this.cancelled = false;
this.cancellationCallback = cancellationCallback;
}
get cancellationRequested() {
return this.cancelled;
}
cancel() {
var _a;
if (this.cancelled) {
return;
}
this.cancelled = true;
(_a = this.cancellationCallback) === null || _a === void 0 ? void 0 : _a.call(this);
}
}
class StoresRegistry extends tinyEmitter.TinyEmitter {
constructor() {
super(...arguments);
this.storesRegistry = {};
}
registerStore(formId, store) {
if (this.storesRegistry[formId] != null) {
throw new Error(`Form with formId "${formId}" is already registered.`);
}
this.storesRegistry[formId] = store;
this.emit();
}
unregisterStore(formId) {
this.storesRegistry[formId] = undefined;
this.emit();
}
getStore(formId) {
return this.storesRegistry[formId];
}
getStoresIds() {
return Object.keys(this.storesRegistry);
}
}
class FormsStoresClass {
setFormStoresHandler(newHandler, disposeOldOne = true) {
if (disposeOldOne) {
if (this.instance != null) {
delete this.instance;
}
}
this.instance = newHandler;
}
get registry() {
var _a;
this.instance = (_a = this.instance) !== null && _a !== void 0 ? _a : new StoresRegistry();
return this.instance;
}
}
const FormsStores = new FormsStoresClass();
function constructFieldHelpers(fieldSelector, orderGuards) {
return {
registerValidator: validator => registerHelpers => {
const validationUpdater = registerHelpers.getUpdater("validation");
return validationUpdater.registerValidator(fieldSelector, validator);
},
unregisterValidator: validator => unregisterHelpers => {
const validationUpdater = unregisterHelpers.getUpdater("validation");
validationUpdater.unregisterValidator(fieldSelector, validator);
},
reportValidatorIndex: orderGuards.reportValidatorIndex
};
}
function constructInputFieldHelpers(fieldId, orderGuards) {
return Object.assign(Object.assign(Object.assign({}, constructFieldHelpers(fieldId, orderGuards)), orderGuards), { registerModifier: modifier => registerHelpers => {
const valueUpdater = registerHelpers.getUpdater("value");
return valueUpdater.registerModifier(fieldId, modifier);
}, unregisterModifier: modifierId => unregisterHelpers => {
const valueUpdater = unregisterHelpers.getUpdater("value");
valueUpdater.unregisterModifier(fieldId, modifierId);
} });
}
function ValidationUpdaterFactory(helpers, state, store) {
return {
id: "validation",
validateField: (fieldSelector) => __awaiter(this, void 0, void 0, function* () {
return validateField(helpers, state, store, fieldSelector);
}),
registerValidator: (fieldSelector, validator) => {
const fieldState = helpers.selectField(fieldSelector);
assertFieldIsDefined(fieldState, fieldSelector);
const id = shortid.generate();
const mutableValidators = fieldState.validation.validators;
mutableValidators[id] = Object.assign(Object.assign({}, validator), { id: id });
const mutableValidatorsOrder = fieldState.validation.validatorsOrder;
mutableValidatorsOrder.push(id);
return id;
},
unregisterValidator: (fieldId, validatorId) => {
const fieldState = helpers.selectField(fieldId);
if (fieldState == null || fieldState.validation.validators[validatorId] == null) {
// Gracefully return if fieldState or validator is not found, no need to throw.
return;
}
const mutableValidators = fieldState.validation.validators;
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete mutableValidators[validatorId];
const validatorOrderIndex = fieldState.validation.validatorsOrder.findIndex(x => x === validatorId);
if (validatorOrderIndex === -1) {
return;
}
const mutableValidatorsOrder = fieldState.validation.validatorsOrder;
mutableValidatorsOrder.splice(validatorOrderIndex, 1);
},
setFormErrors: errors => {
// TODO: Rename this updater.
setFormErrors(state, errors);
},
setFieldValidationResults: (fieldSelector, errors) => {
setFieldValidationResults(helpers, fieldSelector, errors);
},
resetFieldValidationResults: fieldSelector => {
resetFieldValidationResults(helpers, fieldSelector);
}
};
}
function validateField(helpers, _draft, store, fieldSelector) {
return __awaiter(this, void 0, void 0, function* () {
const fieldState = helpers.selectField(fieldSelector);
assertFieldIsDefined(fieldState, fieldSelector);
if (fieldState.validation.currentValidation != null) {
fieldState.validation.currentValidation.cancellationToken.cancel();
}
const validatorsKeys = Object.keys(fieldState.validation.validators);
if (validatorsKeys.length === 0) {
return;
}
// Copy validators because we're in an asynchronous context
// and their proxy into current state might be revoked by immer.
const validators = [];
for (const validatorId of fieldState.validation.validatorsOrder) {
const validator = fieldState.validation.validators[validatorId];
if (validator == null) {
// TODO: Should we throw if the validator is not found?
continue;
}
validators.push(Object.assign({}, validator));
}
const fieldValue = fieldState.getValue(fieldState);
// Indicate that the validation has started.
const validationStarted = new Date();
const cancellationToken = new CancellationTokenImpl(() => {
store.update(asyncHelpers => {
const asyncFieldState = asyncHelpers.selectField(fieldSelector);
if (asyncFieldState == null) {
return;
}
const validation = asyncFieldState.validation;
if (validation.currentValidation == null) {
return;
}
// Start times are different, thus a new validation is already in progress.
if (validation.currentValidation.started.getTime() !== validationStarted.getTime()) {
return;
}
validation.currentValidation = undefined;
});
});
// Set current validation and reset results synchronously
// for other validations to not clutter the results and find the cancellation token.
fieldState.validation.currentValidation = {
started: validationStarted,
cancellationToken: cancellationToken
};
fieldState.validation.results = [];
for (const validator of validators) {
if (!validator.shouldValidate(fieldValue)) {
continue;
}
// Check for cancellation before executing possibly costy validate function.
if (cancellationToken.cancellationRequested) {
return;
}
const validatorResult = validator.validate(fieldValue, constructValidatorHelpers(exports.ValidationResultOrigin.Validation, validator.name));
if (validatorResult == null) {
continue;
}
// Gather results.
let results = undefined;
if (!isPromise(validatorResult)) {
results = validatorResult;
}
else {
results = yield validatorResult;
}
if (results.length === 0) {
continue;
}
// Construct proper ValidationResults in case any strings are in results.
const validationResults = results.map(x => resolveValidationResult(x, validator.name));
updateFieldAsync(fieldSelector, store, cancellationToken, state => {
const mutableValidationResults = state.validation.results;
mutableValidationResults.push(...validationResults);
});
}
updateFieldAsync(fieldSelector, store, cancellationToken, state => {
state.validation.currentValidation = undefined;
});
});
}
function constructValidatorHelpers(origin, validatorName) {
return {
error: (message, code) => ({
type: exports.ValidationResultType.Error,
message: message,
code: code,
origin: origin,
validatorName: validatorName
}),
warning: (message, code) => ({
type: exports.ValidationResultType.Warning,
message: message,
code: code,
origin: origin,
validatorName: validatorName
})
};
}
function updateFieldAsync(fieldSelector, store, cancellationToken, updater) {
// Check for cancellation right away.
if (cancellationToken.cancellationRequested) {
return;
}
setTimeout(() => {
// Check for cancellation again at the start of asynchronous scope.
if (cancellationToken.cancellationRequested) {
return;
}
store.update(helpers => {
const fieldState = helpers.selectField(fieldSelector);
if (fieldState == null) {
return;
}
updater(fieldState);
});
}, 0);
}
function resolveValidationResult(value, validatorName) {
if (typeof value !== "string") {
return value;
}
return {
message: value,
validatorName: validatorName,
type: exports.ValidationResultType.Error,
origin: exports.ValidationResultOrigin.Validation
};
}
function setFormErrors(state, errors) {
for (const key of Object.keys(errors)) {
const fieldError = errors[key];
const field = state.fields[key];
if (field == null || fieldError == null) {
continue;
}
if (typeof fieldError === "object" && !Array.isArray(fieldError)) {
setFormErrors(field, fieldError);
continue;
}
const validationResults = [];
for (const error of fieldError) {
let validationResult;
if (typeof error !== "string") {
validationResult = error;
}
else {
validationResult = {
message: error,
type: exports.ValidationResultType.Error,
origin: exports.ValidationResultOrigin.FormSubmit
};
}
validationResults.push(validationResult);
}
field.validation.results = validationResults;
}
}
function setFieldValidationResults(helpers, fieldSelector, errors) {
const fieldState = helpers.selectField(fieldSelector);
assertFieldIsDefined(fieldState);
fieldState.validation.results = errors.map(result => typeof result !== "string"
? result
: {
message: result,
type: exports.ValidationResultType.Error
});
}
function resetFieldValidationResults(helpers, fieldSelector) {
const fieldState = helpers.selectField(fieldSelector);
assertFieldIsDefined(fieldState);
fieldState.validation.results = [];
}
const formsLogger = Debug("@reactway:forms");
const logger = formsLogger.extend("value-updater");
function ValueUpdaterFactory(helpers) {
const valueUpdater = {
id: "value",
updateFieldValue: (fieldId, value, selection) => {
var _a, _b;
const fieldState = helpers.selectField(fieldId);
assertFieldIsDefined(fieldState, fieldId);
if (!isInputFieldData(fieldState.data)) {
// TODO: Review
throw new Error("Not implemented.");
}
fieldState.validation.results = [];
const modifiers = fieldState.data.modifiers;
const modifiersKeys = Object.keys(modifiers);
if (modifiersKeys.length === 0) {
// No modifiers found, thus a value is set directly to currentValue.
fieldState.data.currentValue = value;
fieldState.data.selection = selection;
helpers.updateFieldStatus(fieldId, status => {
status.touched = true;
status.pristine = value === fieldState.data.initialValue;
});
return;
}
// Modifiers found, firing up the modifiers mechanism.
const previousParseValue = {
value: (_a = fieldState.data.transientValue) !== null && _a !== void 0 ? _a : fieldState.data.currentValue,
caretPosition: (_b = fieldState.data.selection) === null || _b === void 0 ? void 0 : _b.selectionStart
};
let newValue = value;
let transientValue = undefined;
let newCaretPosition = selection === null || selection === void 0 ? void 0 : selection.selectionStart;
for (const modifierKey of modifiersKeys) {
const modifier = modifiers[modifierKey];
if (modifier == null) {
throw new Error("Should never happen.");
}
const newParseValue = {
value: newValue,
caretPosition: newCaretPosition
};
const result = modifier.parse(newParseValue, previousParseValue);
newValue = result.currentValue;
// If modifier returned selection
if (result.caretPosition != null) {
// It becomes our newSelection.
newCaretPosition = result.caretPosition;
}
// If transientValue was is null or was already set before
if (transientValue != null || result.transientValue == null) {
// Do nothing.
continue;
}
// When first transientValue is encountered, it wins until it is resolved.
transientValue = result.transientValue;
}
let newSelection = undefined;
if (newCaretPosition != null) {
newSelection = {
selectionStart: newCaretPosition,
selectionEnd: newCaretPosition,
selectionDirection: "none"
};
}
fieldState.data.currentValue = newValue;
fieldState.data.transientValue = transientValue;
logger("Setting new selection to:", Object.assign({}, newSelection));
fieldState.data.selection = newSelection;
},
resetFieldValue: fieldId => {
const fieldState = helpers.selectField(fieldId);
assertFieldIsDefined(fieldState, fieldId);
if (!isInputFieldData(fieldState.data)) {
throw new Error("Only input field can be reset.");
}
valueUpdater.updateFieldValue(fieldId, fieldState.data.initialValue);
},
clearFieldValue: fieldId => {
const fieldState = helpers.selectField(fieldId);
assertFieldIsDefined(fieldState, fieldId);
if (!isInputFieldData(fieldState.data)) {
throw new Error("Only input field can be cleared.");
}
valueUpdater.updateFieldValue(fieldId, fieldState.data.defaultValue);
},
registerModifier: (fieldId, modifier) => {
const fieldState = helpers.selectField(fieldId);
assertFieldIsDefined(fieldState, fieldId);
if (!isInputFieldData(fieldState.data)) {
throw new Error("Not implemented.");
}
const id = shortid.generate();
const modifiers = fieldState.data.modifiers;
const mutableModifiers = modifiers;
mutableModifiers[id] = Object.assign(Object.assign({}, modifier), { id });
return id;
},
unregisterModifier: (fieldId, modifierId) => {
const fieldState = helpers.selectField(fieldId);
if (fieldState == null) {
return;
}
if (!isInputFieldData(fieldState.data)) {
throw new Error("Not implemented.");
}
const modifiers = fieldState.data.modifiers;
if (modifiers[modifierId] == null) {
// Gracefully return if the modifier is not found, no need to throw.
return;
}
const modifierOrderIndex = fieldState.data.modifiersOrder.findIndex(x => x === modifierId);
if (modifierOrderIndex === -1) {
return;
}
const mutableModifiersOrder = fieldState.data.modifiersOrder;
mutableModifiersOrder.splice(modifierOrderIndex, 1);
}
};
return valueUpdater;
}
function StatusUpdaterFactory(helpers, state) {
return {
id: "status",
updateFieldStatus: (fieldSelector, updater) => {
const fieldState = helpers.selectField(fieldSelector);
assertFieldIsDefined(fieldState, fieldSelector);
// eslint-disable-next-line @typescript-eslint/no-empty-function
const prevStatus = produce(fieldState.status, () => { });
let statusPatches;
const newStatus = produce(prevStatus, status => {
updater(status);
}, patches => {
statusPatches = patches;
});
updater(fieldState.status);
const statusChanged = (statusKey) => {
return statusPatches.find(x => x.path.includes(statusKey)) != null;
};
if (newStatus.touched && statusChanged("touched")) {
updateDependentStatusUpwards(state, helpers, fieldSelector, status => {
status.touched = true;
});
}
if (statusChanged("pristine")) {
updateDependentStatusUpwards(state, helpers, fieldSelector, status => {
status.pristine = newStatus.pristine;
});
}
if (statusChanged("readonly")) {
updateDependentStatusDownwards(fieldState, status => {
status.readonly = newStatus.readonly;
});
}
if (prevStatus === newStatus) {
return;
}
}
};
}
function updateDependentStatusDownwards(fieldState, updater) {
// TODO: Review
for (const key of Object.keys(fieldState.fields)) {
const child = fieldState.fields[key];
if (child == null) {
continue;
}
updater(child.status);
updateDependentStatusDownwards(child, updater);
}
}
function updateDependentStatusUpwards(state, helpers, fieldSelector, updater) {
const fieldState = helpers.selectField(fieldSelector);
assertFieldIsDefined(fieldState, fieldSelector);
// Update all parents (that have id) statuses.
let parentId = fieldSelector;
while ((parentId = helpers.getFieldParentId(parentId)) != null) {
const field = helpers.selectField(parentId);
assertFieldIsDefined(field, fieldSelector);
updater(field.status);
}
// Update form (upper-most) status too.
// TODO: Review if this is needed and/or FormSelector should be used.
updater(state.status);
}
function isPromise(candidate) {
return candidate.then != null && candidate.catch != null;
}
function generateFieldId(name, parentId) {
if (parentId == null) {
return name;
}
return `${parentId}${IdSeparator}${name}`;
}
function getFieldNameFromId(fieldId) {
const lastSeparatorIndex = fieldId.lastIndexOf(IdSeparator);
if (lastSeparatorIndex === -1) {
return fieldId;
}
return fieldId.slice(lastSeparatorIndex + IdSeparator.length);
}
function assertFieldIsDefined(field, fieldSelector) {
if (field == null) {
throw new Error(`Field '${String(fieldSelector)}' does not exist in a given state.`);
}
}
function assertUpdaterIsDefined(updater, updaterId) {
if (updater == null) {
throw new Error(`Updater '${updaterId}' does not exist.`);
}
}
function getDefaultState() {
return {
fields: {},
status: getDefaultStatuses(),
validation: getDefaultValidation()
};
}
function getDefaultStatuses() {
return {
disabled: false,
pristine: true,
touched: false,
readonly: false,
permanent: false
};
}
function getInputValues(defaultValue, initialValue, currentValue, transientValue) {
// Triple equals to `undefined`, because `null` might be a valid value.
if (initialValue === undefined) {
initialValue = defaultValue;
}
// Triple equals to `undefined`, because `null` might be a valid value.
if (currentValue === undefined) {
currentValue = initialValue;
}
return {
defaultValue,
initialValue,
currentValue,
transientValue
};
}
function getInitialInputData(defaultValue, initialValue, currentValue, transientValue) {
// Triple equals to `undefined`, because `null` might be a valid value.
if (initialValue === undefined) {
initialValue = defaultValue;
}
// Triple equals to `undefined`, because `null` might be a valid value.
if (currentValue === undefined) {
currentValue = initialValue;
}
return {
defaultValue,
initialValue,
currentValue,
transientValue,
modifiers: {},
modifiersOrder: []
};
}
function getDefaultUpdatersFactories() {
return {
validation: ValidationUpdaterFactory,
value: ValueUpdaterFactory,
status: StatusUpdaterFactory
};
}
function getDefaultValidation() {
return {
results: [],
validators: {},
validatorsOrder: []
};
}
function isInputFieldData(candidate) {
const inputValues = candidate;
return (inputValues.defaultValue !== undefined &&
inputValues.initialValue !== undefined &&
inputValues.currentValue !== undefined &&
inputValues.modifiers !== undefined &&
typeof inputValues.modifiers === "object" &&
Array.isArray(inputValues.modifiersOrder));
}
function constructStoreHelpers(state, fieldsCache) {
const cachedSelectField = fieldSelector => {
if (fieldSelector === FormSelector) {
return state;
}
const cachedField = fieldsCache[fieldSelector];
if (cachedField != null) {
return cachedField;
}
const selectedField = selectField(state, fieldSelector);
if (selectedField != null) {
fieldsCache[fieldSelector] = selectedField;
}
return selectedField;
};
const helpers = {
selectField: cachedSelectField,
selectFieldParent: fieldSelector => {
const parentId = getFieldParentId(fieldSelector);
if (parentId == null) {
return undefined;
}
return cachedSelectField(parentId);
},
getActiveFieldId: () => {
const formState = state;
return formState.data.activeFieldId;
},
getFieldParentId: getFieldParentId,
getFormValue: () => {
return state.getValue(state);
}
};
return helpers;
}
function constructUpdateStoreHelpers(updaters, draft, store, fieldsCache) {
const fieldStoreHelpers = constructStoreHelpers(draft, fieldsCache);
const updateStoreHelpers = Object.assign(Object.assign({}, fieldStoreHelpers), { registerField: (fieldId, initialFieldState) => {
registerField(draft, fieldId, initialFieldState);
}, unregisterField: id => {
unregisterField(draft, id);
}, setActiveFieldId: fieldId => {
const formState = draft;
formState.data.activeFieldId = fieldId;
}, updateFieldStatus: (fieldSelector, updater) => {
const statusUpdater = updateStoreHelpers.getUpdater("status");
statusUpdater.updateFieldStatus(fieldSelector, updater);
}, getUpdater: updaterId => {
return getUpdater(updateStoreHelpers, draft, store, updaters, updaterId);
}, enqueueUpdate: updater => {
setTimeout(() => store.update(updater), 0);
} });
return updateStoreHelpers;
}
function registerField(state, fieldId, initialFieldState) {
if (initialFieldState.computedValue && isInputFieldData(initialFieldState.data)) {
throw new Error(`Field ${fieldId} is marked to have computedValue, but also has data of an input field.`);
}
const fieldName = getFieldNameFromId(fieldId);
const parentField = selectRegistrationParent(state, fieldId);
if (parentField == null) {
throw new Error(`Parent for field '${fieldId}' to be registered on was not found.`);
}
const fieldState = parentField.fields[fieldName];
if (fieldState != null && fieldState.status.permanent === false) {
throw new Error(`Field '${fieldId}' has already been registered.`);
}
if (fieldState == null) {
// Make fields non-read-only
const mutableFields = parentField.fields;
// Add field into the state.
mutableFields[fieldName] = Object.assign(Object.assign(Object.assign({}, getDefaultState()), initialFieldState), { id: fieldId, name: fieldName, fields: {} });
}
}
function unregisterField(state, id) {
const parentField = selectFieldParent(state, id);
if (parentField == null) {
return;
}
const fieldName = getFieldNameFromId(id);
const field = parentField.fields[fieldName];
if (field == null) {
return;
}
if (field.status.permanent) {
return;
}
// Make fields non-read-only
const mutableFields = parentField.fields;
mutableFields[fieldName] = undefined;
}
function getUpdater(helpers, fieldState, store, updaters, updaterId) {
const factory = updaters[updaterId];
if (factory != null) {
return factory(helpers, fieldState, store);
}
return undefined;
}
function selectRegistrationParent(state, fieldId) {
const separatorIndex = fieldId.indexOf(IdSeparator);
if (separatorIndex === -1) {
return state;
}
else {
const firstChildName = fieldId.slice(0, separatorIndex);
const nextChildId = fieldId.slice(separatorIndex + IdSeparator.length);
// TODO: Review
const child = state.fields[firstChildName];
if (child == null) {
return undefined;
}
return selectRegistrationParent(child, nextChildId);
}
}
// TODO: Do we need recursion here?
function selectField(state, selector) {
if (selector === FormSelector) {
return state;
}
const fieldId = selector;
const firstSeparatorIndex = fieldId.indexOf(IdSeparator);
if (firstSeparatorIndex === -1) {
return state.fields[fieldId];
}
else {
const name = fieldId.slice(0, firstSeparatorIndex);
const nextFieldId = fieldId.slice(firstSeparatorIndex + IdSeparator.length);
const child = state.fields[name];
if (child == null) {
return undefined;
}
return selectField(child, nextFieldId);
}
}
function getFieldParentId(fieldSelector) {
if (fieldSelector === FormSelector) {
return undefined;
}
const lastSeparatorIndex = fieldSelector.lastIndexOf(IdSeparator);
if (lastSeparatorIndex === -1) {
return FormSelector;
}
return fieldSelector.slice(0, lastSeparatorIndex);
}
function selectFieldParent(state, fieldId) {
const parentId = getFieldParentId(fieldId);
if (parentId == null) {
return undefined;
}
return selectField(state, parentId);
}
class Store {
constructor(initialStateFactory, updatersFactories) {
// super();
this.emitter = new tinyEmitter.TinyEmitter();
this.handlers = {};
this.handlerIdsByFieldId = {};
this.count = 0;
this.fieldHandlersListener = (patches) => {
const fieldsKeys = Object.keys(this.handlerIdsByFieldId);
if (fieldsKeys.length === 0) {
return;
}
// Strongly type the key to catch an error if it changes.
const fieldsKey = "fields";
const paths = [];
patches.map(patch => {
const idPath = patch.path.filter(x => x !== fieldsKey).join(IdSeparator);
paths.push(idPath);
});
const fieldsToCall = [];
for (const fieldKey of fieldsKeys) {
// TODO: Is this check needed?
if (fieldsToCall.includes(fieldKey)) {
continue;
}
const fieldHandlersShouldBeCalled = paths.some(path => {
if (!path.startsWith(fieldKey)) {
return false;
}
return path.charAt(fieldKey.length) === IdSeparator;
});
if (!fieldHandlersShouldBeCalled) {
continue;
}
fieldsToCall.push(fieldKey);
}
const handlerIds = fieldsToCall
.map(fieldId => {
// We've accumulated the ids from this object, thus they exist.
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return this.handlerIdsByFieldId[fieldId];
})
.flatMap(x => x);
for (const handlerId of handlerIds) {
const handler = this.handlers[handlerId];
if (handler == null) {
continue;
}
handler(patches);
}
};
// eslint-disable-next-line @typescript-eslint/no-empty-function
this.state = produce(initialStateFactory(), () => { });
this.updaters = updatersFactories != null ? Object.assign({}, updatersFactories) : getDefaultUpdatersFactories();
this.emitter.addListener(this.fieldHandlersListener);
}
get state() {
return this._state;
}
set state(value) {
var _a;
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
if (window.debugState === true) {
const err = new Error();
/* eslint-disable no-console */
console.groupCollapsed("State being updated:", Object.assign({}, value));
console.log((_a = err.stack) === null || _a === void 0 ? void 0 : _a.split("\n")[3]);
console.groupEnd();
/* eslint-enable no-console */
}
this._state = value;
this._helpers = constructStoreHelpers(this._state, {});
}
get helpers() {
return this._helpers;
}
getState() {
return this.state;
}
addListener(handler, dependentFields) {
const handlerId = `h${this.count++}`;
this.handlers[handlerId] = handler;
if (dependentFields == null || dependentFields.length === 0) {
return this.emitter.addListener(patches => {
handler(patches);
});
}
const unregisterCallbacks = [];
for (const fieldId of dependentFields) {
let handlerIds = this.handlerIdsByFieldId[fieldId];
if (handlerIds == null) {
handlerIds = [];
this.handlerIdsByFieldId[fieldId] = handlerIds;
}
if (handlerIds.includes(handlerId)) {
continue;
}
// Register callback
handlerIds.push(handlerId);
const unregisterCallback = () => {
if (this.handlerIdsByFieldId[fieldId] == null) {
return;
}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion, no-shadow
const handlerIds = this.handlerIdsByFieldId[fieldId];
const index = handlerIds.indexOf(handlerId);
if (index === -1) {
return;
}
handlerIds.splice(index, 1);
};
unregisterCallbacks.push(unregisterCallback);
}
return () => {
for (const callback of unregisterCallbacks) {
callback();
}
};
}
update(updater) {
const patches = [];
const newState = produce(this.state, draft => {
updater(constructUpdateStoreHelpers(this.updaters, draft, this, {}), draft);
}, updatePatches => {
patches.push(...updatePatches);
});
if (this.state === newState) {
return;
}
this.state = newState;
this.emitter.emit(patches);
}
}
exports.CancellationTokenImpl = CancellationTokenImpl;
exports.FormSelector = FormSelector;
exports.FormsStores = FormsStores;
exports.IdSeparator = IdSeparator;
exports.StatusUpdater = StatusUpdater;
exports.StatusUpdaterFactory = StatusUpdaterFactory;
exports.Store = Store;
exports.ValidationUpdater = ValidationUpdater;
exports.ValidationUpdaterFactory = ValidationUpdaterFactory;
exports.ValueUpdater = ValueUpdater;
exports.ValueUpdaterFactory = ValueUpdaterFactory;
exports.assertFieldIsDefined = assertFieldIsDefined;
exports.assertUpdaterIsDefined = assertUpdaterIsDefined;
exports.constructFieldHelpers = constructFieldHelpers;
exports.constructInputFieldHelpers = constructInputFieldHelpers;
exports.constructStoreHelpers = constructStoreHelpers;
exports.constructUpdateStoreHelpers = constructUpdateStoreHelpers;
exports.constructValidatorHelpers = constructValidatorHelpers;
exports.formsLogger = formsLogger;
exports.generateFieldId = generateFieldId;
exports.getDefaultState = getDefaultState;
exports.getDefaultStatuses = getDefaultStatuses;
exports.getDefaultUpdatersFactories = getDefaultUpdatersFactories;
exports.getDefaultValidation = getDefaultValidation;
exports.getFieldNameFromId = getFieldNameFromId;
exports.getFieldParentId = getFieldParentId;
exports.getInitialInputData = getInitialInputData;
exports.getInputValues = getInputValues;
exports.isInputFieldData = isInputFieldData;
exports.isPromise = isPromise;
exports.selectField = selectField;
exports.selectFieldParent = selectFieldParent;

4

package.json
{
"name": "@reactway/forms-core",
"description": "React forms.",
"version": "0.0.0-canary.cd320b7",
"version": "0.0.0-canary.ded513d",
"author": "Reactway <dev@reactway.com> (https://github.com/reactway)",

@@ -14,3 +14,3 @@ "dependencies": {

"devDependencies": {
"@reactway-tools/rollup": "^0.0.0-canary.cd320b7",
"@reactway-tools/rollup": "^0.0.0-canary.ded513d",
"@reactway/eslint-config": "^1.0.1",

@@ -17,0 +17,0 @@ "@types/debug": "^4.1.5",

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc