aboutsummaryrefslogtreecommitdiff
path: root/packages/db/test/fixtures/ticketing-example/src/components/Form.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'packages/db/test/fixtures/ticketing-example/src/components/Form.tsx')
-rw-r--r--packages/db/test/fixtures/ticketing-example/src/components/Form.tsx119
1 files changed, 119 insertions, 0 deletions
diff --git a/packages/db/test/fixtures/ticketing-example/src/components/Form.tsx b/packages/db/test/fixtures/ticketing-example/src/components/Form.tsx
new file mode 100644
index 000000000..f393d8281
--- /dev/null
+++ b/packages/db/test/fixtures/ticketing-example/src/components/Form.tsx
@@ -0,0 +1,119 @@
+// Generated by simple:form
+
+import { navigate } from 'astro:transitions/client';
+import {
+ type FieldErrors,
+ type FormState,
+ type FormValidator,
+ formNameInputProps,
+ getInitialFormState,
+ toSetValidationErrors,
+ toTrackAstroSubmitStatus,
+ toValidateField,
+ validateForm,
+} from 'simple:form';
+import { type ComponentProps, createContext, useContext, useState } from 'react';
+
+export function useCreateFormContext(validator: FormValidator, fieldErrors?: FieldErrors) {
+ const initial = getInitialFormState({ validator, fieldErrors });
+ const [formState, setFormState] = useState<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({
+ children,
+ validator,
+ context,
+ fieldErrors,
+ name,
+ ...formProps
+}: {
+ validator: FormValidator;
+ context?: FormContextType;
+ fieldErrors?: FieldErrors;
+} & Omit<ComponentProps<'form'>, 'method' | 'onSubmit'>) {
+ const formContext = context ?? useCreateFormContext(validator, fieldErrors);
+
+ return (
+ <FormContext.Provider value={formContext}>
+ <form
+ {...formProps}
+ method="POST"
+ onSubmit={async (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ const formData = new FormData(e.currentTarget);
+ formContext.set((formState) => ({
+ ...formState,
+ isSubmitPending: true,
+ submitStatus: 'validating',
+ }));
+ const parsed = await validateForm({ formData, validator });
+ if (parsed.data) {
+ navigate(formProps.action ?? '', { formData });
+ return formContext.trackAstroSubmitStatus();
+ }
+
+ formContext.setValidationErrors(parsed.fieldErrors);
+ }}
+ >
+ {name ? <input {...formNameInputProps} value={name} /> : null}
+ {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?`
+ );
+ }
+
+ const { hasErroredOnce, validationErrors, validator } = fieldState;
+ return (
+ <>
+ <input
+ onBlur={async (e) => {
+ const value = e.target.value;
+ if (value === '') return;
+ formContext.validateField(inputProps.name, value, validator);
+ }}
+ onChange={async (e) => {
+ if (!hasErroredOnce) return;
+ const value = e.target.value;
+ formContext.validateField(inputProps.name, value, validator);
+ }}
+ {...inputProps}
+ />
+ {validationErrors?.map((e) => (
+ <p className="error" key={e}>
+ {e}
+ </p>
+ ))}
+ </>
+ );
+}