A react tag input field component using shadcn, like one you see when adding keywords to a video on YouTube. Usable with Form
or standalone.
const [values, setValues] = useState<string[]>([])
...
<InputTags value={values} onChange={setValues} />
<FormField
control={form.control}
name="data_points"
render={({ field }) => (
<FormItem>
<FormLabel>Add Data Point(s)</FormLabel>
<FormControl>
<InputTags {...field} />
</FormControl>
<FormDescription>
...
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Input, InputProps } from "@/components/ui/input";
import { XIcon } from "lucide-react";
import { Dispatch, SetStateAction, forwardRef, useState } from "react";
type InputTagsProps = InputProps & {
value: string[];
onChange: Dispatch<SetStateAction<string[]>>;
};
export const InputTags = forwardRef<HTMLInputElement, InputTagsProps>(
({ value, onChange, ...props }, ref) => {
const [pendingDataPoint, setPendingDataPoint] = useState("");
const addPendingDataPoint = () => {
if (pendingDataPoint) {
const newDataPoints = new Set([...value, pendingDataPoint]);
onChange(Array.from(newDataPoints));
setPendingDataPoint("");
}
};
return (
<>
<div className="flex">
<Input
value={pendingDataPoint}
onChange={(e) => setPendingDataPoint(e.target.value)}
onKeyDown={(e) => {
if (e.key === "Enter") {
e.preventDefault();
addPendingDataPoint();
} else if (e.key === "," || e.key === " ") {
e.preventDefault();
addPendingDataPoint();
}
}}
className="rounded-r-none"
{...props}
ref={ref}
/>
<Button
type="button"
variant="secondary"
className="rounded-l-none border border-l-0"
onClick={addPendingDataPoint}
>
Add
</Button>
</div>
<div className="border rounded-md min-h-[2.5rem] overflow-y-auto p-2 flex gap-2 flex-wrap items-center">
{value.map((item, idx) => (
<Badge key={idx} variant="secondary">
{item}
<button
type="button"
className="w-3 ml-2"
onClick={() => {
onChange(value.filter((i) => i !== item));
}}
>
<XIcon className="w-3" />
</button>
</Badge>
))}
</div>
</>
);
}
);
Godspeed!
@aryanbhat One note regarding your change. This will have the effect of changing the form height when the field appears and in many cases when you have other things under the form, this may not be deisrable.
I would make the function optional with a default value in order to preserve backward compatibility.
Just a suggestion.