This takes part of the work being done in #96 that was reverted but still useful. Note Tailwind and Fresh weren't upgraded because there's no security vulnerability in either, and I have found the new versions to be worse in performance. Thos will likely stay at those fixed versions going forward.
187 lines
4.2 KiB
TypeScript
187 lines
4.2 KiB
TypeScript
export interface FormField {
|
|
name: string;
|
|
label: string;
|
|
value?: string | null;
|
|
overrideValue?: string;
|
|
description?: string;
|
|
placeholder?: string;
|
|
type:
|
|
| 'text'
|
|
| 'email'
|
|
| 'tel'
|
|
| 'url'
|
|
| 'date'
|
|
| 'datetime-local'
|
|
| 'number'
|
|
| 'range'
|
|
| 'select'
|
|
| 'textarea'
|
|
| 'checkbox'
|
|
| 'hidden'
|
|
| 'password';
|
|
step?: string;
|
|
max?: string;
|
|
min?: string;
|
|
rows?: string;
|
|
options?: {
|
|
label: string;
|
|
value: string;
|
|
}[];
|
|
checked?: boolean;
|
|
multiple?: boolean;
|
|
required?: boolean;
|
|
disabled?: boolean;
|
|
readOnly?: boolean;
|
|
extraClasses?: string;
|
|
}
|
|
|
|
export function getFormDataField(formData: FormData, field: string) {
|
|
return ((formData.get(field) || '') as string).trim();
|
|
}
|
|
|
|
export function getFormDataFieldArray(formData: FormData, field: string) {
|
|
return ((formData.getAll(field) || []) as string[]).map((value) => value.trim());
|
|
}
|
|
|
|
export function generateFieldHtml(
|
|
field: FormField,
|
|
formData: FormData,
|
|
) {
|
|
let value = field.overrideValue ||
|
|
(field.multiple ? getFormDataFieldArray(formData, field.name) : getFormDataField(formData, field.name)) ||
|
|
field.value;
|
|
|
|
if (typeof field.overrideValue !== 'undefined') {
|
|
value = field.overrideValue;
|
|
}
|
|
|
|
if (field.type === 'hidden') {
|
|
return generateInputHtml(field, value);
|
|
}
|
|
|
|
return (
|
|
<fieldset class={`block mb-4 ${field.extraClasses || ''}`}>
|
|
<label class='text-slate-300 block pb-1' for={`field_${field.name}`}>{field.label}</label>
|
|
{generateInputHtml(field, value)}
|
|
{field.description
|
|
? (
|
|
<aside class={`text-sm text-slate-400 p-2 ${field.type === 'checkbox' ? 'inline' : ''}`}>
|
|
{field.description}
|
|
</aside>
|
|
)
|
|
: null}
|
|
</fieldset>
|
|
);
|
|
}
|
|
|
|
function generateInputHtml(
|
|
{
|
|
name,
|
|
placeholder,
|
|
type,
|
|
options,
|
|
step,
|
|
max,
|
|
min,
|
|
rows,
|
|
checked,
|
|
multiple,
|
|
disabled,
|
|
required,
|
|
readOnly,
|
|
}: FormField,
|
|
value?: string | string[] | null,
|
|
) {
|
|
const additionalAttributes: Record<string, string | number | boolean> = {};
|
|
|
|
if (typeof step !== 'undefined') {
|
|
additionalAttributes.step = parseInt(step, 10);
|
|
}
|
|
if (typeof max !== 'undefined') {
|
|
additionalAttributes.max = parseInt(max, 10);
|
|
}
|
|
if (typeof min !== 'undefined') {
|
|
additionalAttributes.min = parseInt(min, 10);
|
|
}
|
|
if (typeof rows !== 'undefined') {
|
|
additionalAttributes.rows = parseInt(rows, 10);
|
|
}
|
|
if (checked === true && type === 'checkbox' && value) {
|
|
additionalAttributes.checked = true;
|
|
}
|
|
if (multiple === true) {
|
|
additionalAttributes.multiple = true;
|
|
}
|
|
if (required === true) {
|
|
additionalAttributes.required = true;
|
|
}
|
|
if (disabled === true) {
|
|
additionalAttributes.disabled = true;
|
|
}
|
|
if (readOnly === true) {
|
|
additionalAttributes.readonly = true;
|
|
}
|
|
|
|
if (type === 'select') {
|
|
return (
|
|
<select class='mt-1 input-field' id={`field_${name}`} name={name} {...additionalAttributes}>
|
|
{options?.map((option) => (
|
|
<option
|
|
value={option.value}
|
|
selected={option.value === value || (multiple && (value || [])?.includes(option.value))}
|
|
>
|
|
{option.label}
|
|
</option>
|
|
))}
|
|
</select>
|
|
);
|
|
}
|
|
|
|
if (type === 'textarea') {
|
|
return (
|
|
<textarea
|
|
class='mt-1 input-field'
|
|
id={`field_${name}`}
|
|
name={name}
|
|
rows={6}
|
|
placeholder={placeholder}
|
|
{...additionalAttributes}
|
|
>
|
|
{(value as string) || ''}
|
|
</textarea>
|
|
);
|
|
}
|
|
|
|
if (type === 'checkbox') {
|
|
return (
|
|
<input id={`field_${name}`} name={name} type={type} value={value as string || ''} {...additionalAttributes} />
|
|
);
|
|
}
|
|
|
|
if (type === 'password') {
|
|
return (
|
|
<input
|
|
class='mt-1 input-field'
|
|
id={`field_${name}`}
|
|
name={name}
|
|
type={type}
|
|
placeholder={placeholder || ''}
|
|
value=''
|
|
{...additionalAttributes}
|
|
/>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<input
|
|
class='mt-1 input-field'
|
|
id={`field_${name}`}
|
|
name={name}
|
|
type={type}
|
|
placeholder={placeholder || ''}
|
|
value={value as string || ''}
|
|
{...additionalAttributes}
|
|
/>
|
|
);
|
|
}
|