Page MenuHomeSealhub

field.ts
No OneTemporary

field.ts

import { is, predicates } from "@sealcode/ts-predicates";
import { BaseContext } from "koa";
import { FormDataValue } from "..";
export type FormFieldValidationResponse = { valid: boolean; message: string };
export type FormFieldValidationFn<ValueType> = (
ctx: BaseContext,
value: ValueType,
field: FormField<ValueType>
) => Promise<FormFieldValidationResponse>;
export type FieldParsedValue<T> = T extends FormField<infer R> ? R : never;
export abstract class FormField<ParsedValue extends unknown = unknown> {
name: string;
constructor(
public required: boolean = false,
public validator: FormFieldValidationFn<ParsedValue> = async () => ({
valid: true,
message: "",
})
) {}
init(fieldname: string): void {
this.name = fieldname;
}
public async _validate(
ctx: BaseContext,
value: ParsedValue
): Promise<FormFieldValidationResponse> {
if (
this.required &&
(value == "" || value == null || value == undefined)
) {
return { valid: false, message: "This field is required" };
}
return this.validator(ctx, value, this);
}
public abstract getEmptyValue(): ParsedValue;
abstract parse(raw_value: FormDataValue): Promise<ParsedValue>;
async getValue(
ctx: BaseContext,
all_form_values: Record<string, FormDataValue>
): Promise<{
valid: boolean;
message: string;
parsed: ParsedValue;
raw: FormDataValue;
}> {
const parsed = all_form_values[this.name]
? await this.parse(all_form_values[this.name])
: this.getEmptyValue();
const { valid, message } = await this._validate(ctx, parsed);
return { parsed, valid, message, raw: all_form_values[this.name] };
}
}
export class SimpleFormField extends FormField<string> {
async parse(raw_value: FormDataValue): Promise<string> {
return raw_value.toString();
}
public getEmptyValue(): string {
return "";
}
}
export class PickFromListField extends FormField<string> {
constructor(
public required: boolean = false,
public generateOptions: (
ctx: BaseContext
) => Promise<Record<string, string> | { [i: string]: string }>,
public customValidation: (
ctx: BaseContext,
value: unknown,
instance: PickFromListField
) => Promise<FormFieldValidationResponse> = (ctx, value, instance) =>
instance.valueInList(ctx, value)
) {
super(required, (ctx, value) =>
this.customValidation(ctx, value, this)
);
}
async valueInList(
ctx: BaseContext,
value: unknown
): Promise<FormFieldValidationResponse> {
const options = await this.generateOptions(ctx);
if (!is(value, predicates.string)) {
return { valid: false, message: "not a string" };
}
if (!Object.keys(options).includes(value)) {
return {
valid: false,
message: `"${value}" is not one of the options`,
};
}
return { valid: true, message: "" };
}
async parse(raw_value: FormDataValue) {
return raw_value.toString();
}
public getEmptyValue(): string {
return "";
}
}
export class ChekboxedListField extends FormField<Record<string, "on">> {
constructor(
public required: boolean = false,
public generateOptions: (
ctx: BaseContext
) => Promise<Record<string, string> | { [i: string]: string }>,
public isVisible: (ctx: BaseContext) => Promise<boolean> = () =>
Promise.resolve(true)
) {
super(required, (ctx, value) => this.isValueValid(ctx, value));
}
private async isValueValid(
_: BaseContext,
value: unknown
): Promise<FormFieldValidationResponse> {
if (is(value, predicates.string)) {
return { valid: false, message: "you need an array" };
}
if (is(value, predicates.null)) {
return { valid: false, message: "you need an array" };
}
return { valid: true, message: "" };
}
async parse(raw_value: FormDataValue): Promise<Record<string, "on">> {
if (is(raw_value, predicates.object)) {
return Object.fromEntries(
Object.entries(raw_value).filter(([, value]) => value === "on")
) as Record<string, "on">;
} else {
return {};
}
}
public getEmptyValue(): Record<string, "on"> {
return {};
}
}
export class NumberField extends FormField<number | null> {
constructor(required: boolean) {
super(required, (_, value) => this.isValueValid(_, value));
}
private async isValueValid(_: BaseContext, value: unknown) {
if (
(is(value, predicates.string) &&
!isNaN(parseFloat(value)) &&
parseFloat(value).toString() == value.trim()) ||
is(value, predicates.number) ||
((is(value, predicates.undefined) || value == "") && !this.required)
) {
return { valid: true, message: "" };
}
return { valid: false, message: "Proszę wprowadzić liczbę" };
}
async parse(raw_value: string): Promise<number> {
return parseFloat(raw_value);
}
public getEmptyValue(): number | null {
return null;
}
}

File Metadata

Mime Type
text/x-java
Expires
Sun, Nov 2, 21:34 (1 d, 12 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1030530
Default Alt Text
field.ts (4 KB)

Event Timeline