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!
I made some changes to the component so that the box displaying the selected values only appears when there are values present. Previously, the box was always visible, even when empty. Now, the box will only show up when there are items to display, making the UI cleaner and more adaptable to your specific use case
Thank you @enesien for the component.