56 lines
2.1 KiB
TypeScript
56 lines
2.1 KiB
TypeScript
"use client";
|
|
|
|
import { useRef, useState, type ComponentProps } from "react";
|
|
import { XIcon } from "lucide-react";
|
|
|
|
import { Input } from "@/components/ui/input";
|
|
import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput } from "@/components/ui/input-group";
|
|
|
|
export function ClearableInput({ className, ...props }: ComponentProps<typeof Input>) {
|
|
const editableText = ["text", "search", "email", "url", "tel"].includes(props.type ?? "text");
|
|
if (!editableText) return <Input className={className} {...props} />;
|
|
return <InputGroup className={className}><ClearableInputGroupInput {...props} /></InputGroup>;
|
|
}
|
|
|
|
export function ClearableInputGroupInput({ ref, onChange, onValueChange, ...props }: ComponentProps<typeof Input>) {
|
|
const inputRef = useRef<HTMLInputElement | null>(null);
|
|
const [uncontrolledValue, setUncontrolledValue] = useState(props.defaultValue ?? "");
|
|
const value = props.value ?? uncontrolledValue;
|
|
|
|
return (
|
|
<>
|
|
<InputGroupInput
|
|
{...props}
|
|
ref={(node) => {
|
|
inputRef.current = node;
|
|
if (typeof ref === "function") return ref(node);
|
|
if (ref) ref.current = node;
|
|
}}
|
|
onChange={(event) => {
|
|
setUncontrolledValue(event.target.value);
|
|
onValueChange?.(event.target.value);
|
|
onChange?.(event);
|
|
}}
|
|
/>
|
|
{String(value).length > 0 && !props.disabled && !props.readOnly ? (
|
|
<InputGroupAddon align="inline-end">
|
|
<InputGroupButton
|
|
size="icon-xs"
|
|
aria-label={`ล้าง${props["aria-label"] ?? props.placeholder ?? "ข้อความ"}`}
|
|
onClick={() => {
|
|
const input = inputRef.current;
|
|
if (!input) return;
|
|
// Use the native setter so React receives the same change as typing.
|
|
Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value")?.set?.call(input, "");
|
|
input.dispatchEvent(new Event("input", { bubbles: true }));
|
|
input.focus();
|
|
}}
|
|
>
|
|
<XIcon />
|
|
</InputGroupButton>
|
|
</InputGroupAddon>
|
|
) : null}
|
|
</>
|
|
);
|
|
}
|