81 lines
1.9 KiB
TypeScript
81 lines
1.9 KiB
TypeScript
|
import {
|
||
|
Component,
|
||
|
createEffect,
|
||
|
createSignal,
|
||
|
createUniqueId,
|
||
|
onMount,
|
||
|
Show,
|
||
|
} from "solid-js";
|
||
|
import formStyles from "./form.module.css";
|
||
|
|
||
|
export type TextFieldProps = {
|
||
|
label?: string;
|
||
|
helperText?: string;
|
||
|
type?: "text" | "password";
|
||
|
onChange?: (value: string) => void;
|
||
|
onInput?: (value: string) => void;
|
||
|
inputId?: string;
|
||
|
error?: boolean;
|
||
|
required?: boolean;
|
||
|
name?: string;
|
||
|
};
|
||
|
|
||
|
const TextField: Component<TextFieldProps> = (props) => {
|
||
|
let input: HTMLInputElement;
|
||
|
let field: HTMLDivElement;
|
||
|
const [hasContent, setHasContent] = createSignal(false);
|
||
|
const altInputId = createUniqueId();
|
||
|
|
||
|
createEffect(() => {
|
||
|
if (hasContent()) {
|
||
|
field.classList.add("float-label");
|
||
|
} else {
|
||
|
field.classList.remove("float-label");
|
||
|
}
|
||
|
});
|
||
|
|
||
|
onMount(() => {
|
||
|
setHasContent(input.value.length > 0);
|
||
|
});
|
||
|
|
||
|
const onInputChange = (e: { currentTarget: HTMLInputElement }) => {
|
||
|
const value = (e.currentTarget as HTMLInputElement).value;
|
||
|
setHasContent(value.length > 0);
|
||
|
props.onInput?.(value);
|
||
|
};
|
||
|
|
||
|
const inputId = () => props.inputId ?? altInputId;
|
||
|
|
||
|
const fieldClass = () => {
|
||
|
const cls = [formStyles.textfield];
|
||
|
if (typeof props.helperText !== "undefined") {
|
||
|
cls.push(formStyles.withHelperText);
|
||
|
}
|
||
|
if (props.error) {
|
||
|
cls.push(formStyles.error);
|
||
|
}
|
||
|
return cls.join(" ");
|
||
|
};
|
||
|
|
||
|
return (
|
||
|
<div ref={field!} class={fieldClass()}>
|
||
|
<label for={inputId()}>{props.label}</label>
|
||
|
<input
|
||
|
ref={input!}
|
||
|
id={inputId()}
|
||
|
type={props.type ?? "text"}
|
||
|
onInput={onInputChange}
|
||
|
onChange={(e) => props.onChange?.(e.currentTarget.value)}
|
||
|
placeholder=""
|
||
|
required={props.required}
|
||
|
name={props.name}
|
||
|
/>
|
||
|
<Show when={typeof props.helperText !== "undefined"}>
|
||
|
<span class={formStyles.helperText}>{props.helperText}</span>
|
||
|
</Show>
|
||
|
</div>
|
||
|
);
|
||
|
};
|
||
|
|
||
|
export default TextField;
|