forked from NotNet/gluestick
96 lines
2.8 KiB
TypeScript
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>
|
|
);
|
|
}
|