gluestick/src/components/Input.tsx
2023-04-28 22:31:38 -04:00

96 lines
2.8 KiB
TypeScript

import { Field, FieldProps, FieldAttributes, FormikProps } from "formik";
import React, { LabelHTMLAttributes } from "react";
import styles from "./Input.module.css";
import classnames from "classnames";
type CustomInputProps<T> = {
customRender?: (fieldProps: FieldProps) => React.ReactNode;
customOnChange?: (
event: React.ChangeEvent<HTMLInputElement>,
form: FormikProps<T>
) => void;
};
export function Label({
children,
...props
}: LabelHTMLAttributes<HTMLLabelElement>) {
return (
<label className={classnames(styles.label, props.className)} {...props}>
{children}
</label>
);
}
export function Hint({
children,
...props
}: LabelHTMLAttributes<HTMLLabelElement>) {
return (
<label className={classnames(styles.hint, props.className)} {...props}>
{children}
</label>
);
}
export default function Input<T>(
props: CustomInputProps<T> &
FieldAttributes<{ hint?: string; label?: string; disabled?: boolean }>
) {
const generatedId = React.useId();
return (
<div className={classnames("form-row", styles.formRow)}>
{props.label ? <Label htmlFor={generatedId}>{props.label}</Label> : null}
<Field id={generatedId} {...props}>
{(fieldProps: FieldProps) => {
let { field, meta, form } = fieldProps;
let textAfterField =
meta.touched && meta.error ? (
<p className={styles.error}>{meta.error}</p>
) : (
props.hint && <p className={styles.hint}>{props.hint}</p>
);
// <input type="file"> in React is always uncontrolled, so we have to hardcode
// the value to "" if it's a file picker
const inputFields =
props.type === "file"
? (() => {
let clonedField = Object.assign({}, field);
delete clonedField.value;
return clonedField;
})()
: field;
return (
<>
{props.customRender == null ? (
<input
type={props.type}
placeholder={props.placeholder}
disabled={props.disabled}
title={props.title}
{...inputFields}
onChange={(event) => {
console.log(event);
if (props.customOnChange) {
console.log("using custom on change");
props.customOnChange(event, form);
} else {
form.setFieldValue(field.name, event.currentTarget.value);
}
}}
/>
) : (
props.customRender(fieldProps)
)}
{textAfterField}
</>
);
}}
</Field>
</div>
);
}