Files
monie-web/src/components/demo.FormComponents.tsx

128 lines
3.4 KiB
TypeScript

import { useStore } from '@tanstack/react-form'
import { useFieldContext, useFormContext } from '#/hooks/demo.form-context'
export function SubscribeButton({ label }: { label: string }) {
const form = useFormContext()
return (
<form.Subscribe selector={(state) => state.isSubmitting}>
{(isSubmitting) => (
<button
type="submit"
disabled={isSubmitting}
className="px-6 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 transition-colors disabled:opacity-50"
>
{label}
</button>
)}
</form.Subscribe>
)
}
function ErrorMessages({
errors,
}: {
errors: Array<string | { message: string }>
}) {
return (
<>
{errors.map((error) => (
<div
key={typeof error === 'string' ? error : error.message}
className="text-red-500 mt-1 font-bold"
>
{typeof error === 'string' ? error : error.message}
</div>
))}
</>
)
}
export function TextField({
label,
placeholder,
}: {
label: string
placeholder?: string
}) {
const field = useFieldContext<string>()
const errors = useStore(field.store, (state) => state.meta.errors)
return (
<div>
<label htmlFor={label} className="block font-bold mb-1 text-xl">
{label}
<input
value={field.state.value}
placeholder={placeholder}
onBlur={field.handleBlur}
onChange={(e) => field.handleChange(e.target.value)}
className="w-full px-4 py-2 rounded-md border border-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500"
/>
</label>
{field.state.meta.isTouched && <ErrorMessages errors={errors} />}
</div>
)
}
export function TextArea({
label,
rows = 3,
}: {
label: string
rows?: number
}) {
const field = useFieldContext<string>()
const errors = useStore(field.store, (state) => state.meta.errors)
return (
<div>
<label htmlFor={label} className="block font-bold mb-1 text-xl">
{label}
<textarea
value={field.state.value}
onBlur={field.handleBlur}
rows={rows}
onChange={(e) => field.handleChange(e.target.value)}
className="w-full px-4 py-2 rounded-md border border-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500"
/>
</label>
{field.state.meta.isTouched && <ErrorMessages errors={errors} />}
</div>
)
}
export function Select({
label,
values,
}: {
label: string
values: Array<{ label: string; value: string }>
placeholder?: string
}) {
const field = useFieldContext<string>()
const errors = useStore(field.store, (state) => state.meta.errors)
return (
<div>
<label htmlFor={label} className="block font-bold mb-1 text-xl">
{label}
</label>
<select
name={field.name}
value={field.state.value}
onBlur={field.handleBlur}
onChange={(e) => field.handleChange(e.target.value)}
className="w-full px-4 py-2 rounded-md border border-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500"
>
{values.map((value) => (
<option key={value.value} value={value.value}>
{value.label}
</option>
))}
</select>
{field.state.meta.isTouched && <ErrorMessages errors={errors} />}
</div>
)
}