add InputRestriction support for reactiveValue.ts

This commit is contained in:
Maksim Skobaro 2025-02-08 03:33:42 +03:00
parent b1ea3a670e
commit a5695ccab6

View File

@ -1,11 +1,14 @@
import {action, computed, makeObservable, observable, reaction} from "mobx"; import {action, computed, makeObservable, observable, reaction} from "mobx";
import _ from "lodash";
export class ReactiveValue<T> { export class ReactiveValue<T> {
@observable private val: T; @observable private val: T;
@observable private isTouched: boolean = false; @observable private isTouched: boolean = false;
@observable private validators: ((value: T, field: string) => string)[] = []; @observable private validators: ((value: T, field: string) => string)[] = [];
@observable private inputRestrictions: ((value: T, field: string) => string)[] = [];
@observable private errors: string[] = []; @observable private errors: string[] = [];
@observable private inputRestrictionError: string;
@observable private fieldName: string; @observable private fieldName: string;
constructor(fireImmediately: boolean = false) { constructor(fireImmediately: boolean = false) {
@ -13,8 +16,10 @@ export class ReactiveValue<T> {
reaction(() => ({value: this.val, touched: this.isTouched}), () => { reaction(() => ({value: this.val, touched: this.isTouched}), () => {
if (!this.isTouched) { if (!this.isTouched) {
this.errors = []; this.errors = [];
this.inputRestrictionError = undefined;
return; return;
} }
this.errors = this.validators this.errors = this.validators
.map(validator => validator(this.val, this.fieldName)) .map(validator => validator(this.val, this.fieldName))
.filter(error => error && error.length > 0); .filter(error => error && error.length > 0);
@ -28,8 +33,23 @@ export class ReactiveValue<T> {
@action.bound @action.bound
set(value: T) { set(value: T) {
this.val = value;
this.isTouched = true; this.isTouched = true;
this.inputRestrictionError = undefined;
if (!_.isEmpty(value)) {
for (const restriction of this.inputRestrictions) {
const error = restriction(value, this.fieldName);
if (error) {
this.inputRestrictionError = error;
}
}
}
const lengthIsLessIfStringOrTrue = _.isString(value) && _.isString(this.val) ? value.length < this.val?.length : true;
if (!this.inputRestrictionError || lengthIsLessIfStringOrTrue) {
this.setAuto(value);
}
return this; return this;
} }
@ -56,23 +76,35 @@ export class ReactiveValue<T> {
@action.bound @action.bound
addValidator(validator: (value: T, field?: string) => string) { addValidator(validator: (value: T, field?: string) => string) {
this.validators.push(validator); if (validator) {
this.validators.push(validator);
}
return this;
}
@action.bound
addInputRestriction(restriction: (value: T) => string) {
if (restriction) {
this.inputRestrictions.push(restriction);
}
return this; return this;
} }
@computed @computed
get invalid() { get invalid() {
return this.errors.length > 0; return this.errors.length > 0 || !!this.inputRestrictionError;
} }
@computed @computed
get allErrors() { get allErrors() {
return this.errors; return [...this.errors, this.inputRestrictionError].filter(error => error);
} }
@computed @computed
get firstError() { get firstError() {
return this.errors[0]; return this.inputRestrictionError ?? this.errors[0];
} }
@action.bound @action.bound
@ -87,9 +119,10 @@ export class ReactiveValue<T> {
@action.bound @action.bound
clear() { clear() {
this.val = null; this.val = undefined;
this.isTouched = false; this.isTouched = false;
this.errors = []; this.errors = [];
this.inputRestrictionError = undefined;
} }
} }