svelte-hook-form
Version:
A better version of form validation.
123 lines (106 loc) • 3 kB
text/typescript
import { get, type Readable } from "svelte/store";
import type { FieldState, FieldStore, ValidationResult, ValidationRule } from "./types";
export class FormField {
constructor(
private readonly store: FieldStore,
private readonly rules: ValidationRule[],
private readonly bail: boolean
) {}
/**
* Returns a readonly field state.
*
* @returns {Readable<FieldState>} state of the field.
*/
get state() {
return get(this.store);
}
/**
* Observe the field state.
*
* @returns {Readable<FieldState>} state of the field.
*
* @example
* ```svelte
* <script lang="ts">
* const state$ = watch("text");
* state$.subscribe((v) => {
* console.log("State =>", v);
* });
* </script>
* ```
*/
get watch(): Readable<FieldState> {
const { subscribe } = this.store;
return { subscribe };
}
/**
* Update the current field state.
*
* @param {Partial<FieldState>} state
*
* @example
* ```ts
* field.setState({ valid: true });
* ```
*/
setState(state: Partial<FieldState>) {
this.store.update((v) => ({ ...v, ...state }));
}
validate = (value: any = undefined): Promise<FieldState> => {
const promises: Promise<ValidationResult>[] = [];
const len = this.rules.length;
let newState = Object.assign(this.state, { errors: [] });
value = value || newState.value;
// If it's empty rules, it will always return `true`
if (len == 0) {
newState = Object.assign(newState, { value, valid: true });
this.setState(newState);
return Promise.resolve(newState);
}
newState = Object.assign(newState, {
// dirty: v.dirty ? true : soft ? false : true,
pending: true,
value
});
this.setState(newState);
// Setup validation rules and pass in field value
let i = 0;
for (i = 0; i < len; i++) {
const { validate, params } = this.rules[i];
promises.push(validate(value, params));
}
if (this.bail) {
return new Promise(async (resolve) => {
for (i = 0; i < len; i++) {
const result = await promises[i];
if (result !== true) {
newState = Object.assign(newState, {
pending: false,
errors: [result],
valid: false
});
this.setState(newState);
resolve(newState);
return;
}
}
newState = Object.assign(newState, {
pending: false,
valid: false
});
this.setState(newState);
resolve(newState);
});
}
return Promise.all(promises).then((result: ValidationResult[]) => {
const errors = <string[]>result.filter((v: ValidationResult) => v !== true);
newState = Object.assign(newState, {
pending: false,
errors,
valid: errors.length === 0
});
this.setState(newState);
return Promise.resolve(newState);
});
};
}