I am currently looking for a way to add types to the custom events emitted from the custom action to be used in use directive
I am creating. Here is the snippet.
import { z } from "zod";
const acceptedNodeNames = [
//
"INPUT",
"SELECT",
"TEXTAREA",
];
// prettier-ignore
type FormField =
| HTMLInputElement
| HTMLSelectElement
| HTMLTextAreaElement;
type SubmitConfig = {
schema: {
[key: string]: z.Schema;
};
};
export function form(node: HTMLFormElement, { schema }: SubmitConfig) {
// store fields that needs to be validated in array
const fields = Array.from(node.children).filter(
(child) => acceptedNodeNames.includes(child.nodeName) && (child as FormField).name in schema,
) as FormField[];
function handleInput(this: FormField) {
const { name, value } = this;
const result = schema[name].safeParse(value);
if (result.success) {
this.removeAttribute("aria-invalid");
} else {
this.setAttribute("aria-invalid", "");
}
}
// attach field listeners
for (const field of fields) {
field.addEventListener("input", handleInput);
}
function handleSubmit(e: SubmitEvent) {
e.preventDefault();
const input = fields.reduce(
(obj, field) => ({
...obj,
[field.name]: field.value,
}),
{},
);
const result = z.object(schema).safeParse(input);
if (result.success) {
const event = new CustomEvent("okay", { detail: { ...result } });
node.dispatchEvent(event);
} else {
const errors = JSON.parse(result.error.message);
for (const error of errors) {
const field = fields.find(({ name }) => name === error.path[0]);
if (!field) continue;
field.setAttribute("aria-invalid", "");
}
const event = new CustomEvent("fail", { detail: { errors } });
node.dispatchEvent(event);
}
}
// attach form submit handler
node.addEventListener("submit", handleSubmit);
return {
destroy() {
node.removeEventListener("submit", handleSubmit);
for (const field of fields) {
field.removeEventListener("input", handleInput);
}
},
};
}
It basically just validates form field and uses zod
underneath. This is how I would like to use it.
<form
novalidate
method="POST"
use:form={{
schema: {
email: z.string().email(),
},
}}
on:okay={(e) => {
console.log(e.detail);
}}
on:fail={(e) => {
console.log(e.detail);
}}
>
...
</form>
This code is already working, but my only problem is that it doesn't have types and it got some annoying squiggly lines in vscode
. Is there a way at the moment to declare a type to those custom events similar to adding types in svelte components like so
interface $$Events {
change: CustomEvent<{
page: number;
pageSize: number;
}>;
}
Thanks!