import { writable } from 'svelte/store'; import { z } from 'zod'; export type FormInput = { value: T; error: string | null; }; type FormInputs = { [K in keyof T]: FormInput; }; export function createForm>(schema: T, initialValues: z.infer) { // Create a writable store for the inputs const inputsStore = writable>>(initializeInputs(initialValues)); function initializeInputs(initialValues: z.infer): FormInputs> { const inputs: FormInputs> = {} as FormInputs>; for (const key in initialValues) { if (Object.prototype.hasOwnProperty.call(initialValues, key)) { inputs[key as keyof z.infer] = { value: initialValues[key as keyof z.infer], error: null }; } } return inputs; } function validate() { let success = true; inputsStore.update((inputs) => { // Extract values from inputs to validate against the schema const values = Object.fromEntries( Object.entries(inputs).map(([key, input]) => [key, input.value]) ); const result = schema.safeParse(values); if (!result.success) { success = false; for (const input of Object.keys(inputs)) { const error = result.error.errors.find((e) => e.path[0] === input); if (error) { inputs[input as keyof z.infer].error = error.message; } else { inputs[input as keyof z.infer].error = null; } } } else { for (const input of Object.keys(inputs)) { inputs[input as keyof z.infer].error = null; } } return inputs; }); return success ? data() : null; } function data() { let values: z.infer | null = null; inputsStore.subscribe((inputs) => { values = Object.fromEntries( Object.entries(inputs).map(([key, input]) => [key, input.value]) ) as z.infer; })(); return values; } function reset() { inputsStore.update((inputs) => { for (const input of Object.keys(inputs)) { inputs[input as keyof z.infer] = { value: initialValues[input as keyof z.infer], error: null }; } return inputs; }); } return { schema, inputs: inputsStore, data, validate, reset }; }