58 lines
1.3 KiB
TypeScript
58 lines
1.3 KiB
TypeScript
export function View(props: { title: string; body: string }) {
|
|
return `
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<title>${props.title} - Shelves</title>
|
|
</head>
|
|
<body>
|
|
${props.body}
|
|
<script src="/static/js/bundle.js" type="text/javascript" module></script>
|
|
</body>
|
|
</html>
|
|
`;
|
|
}
|
|
|
|
export type FormFieldType = 'text' | 'textarea' | 'file' | 'number';
|
|
export type FormFieldProps = {
|
|
name: string;
|
|
type: FormFieldType;
|
|
label: string;
|
|
error?: string;
|
|
required?: boolean;
|
|
};
|
|
export function FormField(props: FormFieldProps) {
|
|
const input = () => {
|
|
const attrs = `name='${props.name}' id="${props.name}" ${
|
|
(props.required ?? true) && "required aria-required='true'"
|
|
}`;
|
|
if (props.type === 'textarea') {
|
|
return `<textarea ${attrs}></textarea>`;
|
|
} else {
|
|
return `<input type="${props.type}" ${attrs}>`;
|
|
}
|
|
};
|
|
|
|
return `
|
|
<div class="form-input">
|
|
<label for="${props.name}">${props.label}</label>
|
|
${input()}
|
|
<div class="error" id="${props.name}-error" aria-live="polite">${
|
|
props.error ?? ''
|
|
}</div>
|
|
</div>
|
|
`;
|
|
}
|
|
export function Form(
|
|
props: { action: string; fields: FormFieldProps[]; hasFileData?: boolean },
|
|
) {
|
|
return `
|
|
<form action="${props.action}" method="POST" ${
|
|
props.hasFileData ? "enctype='multipart/form-data'" : ''
|
|
}>
|
|
${props.fields.map(FormField).join('')}
|
|
<input type="submit" value="Submit">
|
|
</form>
|
|
`;
|
|
}
|