Skip to content

Commit

Permalink
feat(form): add solid-js template
Browse files Browse the repository at this point in the history
  • Loading branch information
dsnjunior committed Jan 3, 2024
1 parent 32411bb commit 326c98f
Show file tree
Hide file tree
Showing 6 changed files with 371 additions and 0 deletions.
1 change: 1 addition & 0 deletions packages/form/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"astro": "^4.0.7",
"preact": "^10.19.3",
"react": "^18.0.0",
"solid-js": "^1.8.7",
"typescript": "^5.3.3",
"zod": "^3.22.4"
},
Expand Down
5 changes: 5 additions & 0 deletions packages/form/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ const frameworks = [
label: "Preact",
templateDir: "preact",
},
{
value: "solid-js",
label: "SolidJS",
templateDir: "solid-js",
},
] as const;

type Framework = (typeof frameworks)[number];
Expand Down
135 changes: 135 additions & 0 deletions packages/form/templates/solid-js/Form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// Generated by simple:form

import {
type ComponentProps,
For,
Show,
createContext,
createSignal,
useContext,
} from "solid-js";
import {
type FieldErrors,
type FormState,
type FormValidator,
formNameInputProps,
getInitialFormState,
toSetValidationErrors,
toTrackAstroSubmitStatus,
toValidateField,
validateForm,
} from "simple:form";

export function useCreateFormContext(
validator: FormValidator,
fieldErrors?: FieldErrors,
) {
const initial = getInitialFormState({ validator, fieldErrors });
const [formState, setFormState] = createSignal<FormState>(initial);
return {
value: formState,
set: setFormState,
setValidationErrors: toSetValidationErrors(setFormState),
validateField: toValidateField(setFormState),
trackAstroSubmitStatus: toTrackAstroSubmitStatus(setFormState),
};
}

export function useFormContext() {
const formContext = useContext(FormContext);
if (!formContext) {
throw new Error(
"Form context not found. `useFormContext()` should only be called from children of a <Form> component.",
);
}
return formContext;
}

type FormContextType = ReturnType<typeof useCreateFormContext>;

const FormContext = createContext<FormContextType | undefined>(undefined);

export function Form(
props: {
validator: FormValidator;
context?: FormContextType;
fieldErrors?: FieldErrors;
} & Omit<ComponentProps<"form">, "method" | "onSubmit">,
) {
const formContext =
props.context ?? useCreateFormContext(props.validator, props.fieldErrors);

return (
<FormContext.Provider value={formContext}>
<form
{...props}
method="post"
onSubmit={async (e) => {
const formData = new FormData(e.currentTarget);
formContext.set((formState) => ({
...formState,
isSubmitPending: true,
submitStatus: "validating",
}));
const parsed = await validateForm({
formData,
validator: props.validator,
});
if (parsed.data) {
return formContext.trackAstroSubmitStatus();
}

e.preventDefault();
e.stopPropagation();
formContext.setValidationErrors(parsed.fieldErrors);
}}
>
<Show when={props.name}>
{(name) => <input {...formNameInputProps} value={name()} />}
</Show>
{props.children}
</form>
</FormContext.Provider>
);
}

export function Input(inputProps: ComponentProps<"input"> & { name: string }) {
const formContext = useFormContext();
const fieldState = () => formContext.value().fields[inputProps.name];
if (!fieldState()) {
throw new Error(
`Input "${inputProps.name}" not found in form. Did you use the <Form> component?`,
);
}

return (
<Show when={fieldState()}>
{(state) => (
<>
<input
onBlur={(e) => {
const value = e.target.value;
if (value === "") return;
formContext.validateField(
inputProps.name,
value,
state().validator,
);
}}
onInput={(e) => {
if (!state().hasErroredOnce) return;
const value = e.target.value;
formContext.validateField(
inputProps.name,
value,
state().validator,
);
}}
{...inputProps}
/>
<For each={state().validationErrors}>{(e) => <p>{e}</p>}</For>
</>
)}
</Show>
);
}
1 change: 1 addition & 0 deletions packages/form/templates/solid-js/env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/// <reference path="../../types.d.ts" />
7 changes: 7 additions & 0 deletions packages/form/templates/solid-js/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"extends": "astro/tsconfigs/strictest",
"compilerOptions": {
"jsx": "preserve",
"jsxImportSource": "solid-js"
}
}
Loading

0 comments on commit 326c98f

Please sign in to comment.