modified TagInput, for array an object

This commit is contained in:
Pramod Mahajan 2025-05-16 19:08:41 +05:30
parent 1c1a1da8a0
commit 7cfe04de2e

View File

@ -1,32 +1,89 @@
import React, { useState, useEffect } from "react";
import { useFormContext } from "react-hook-form";
import React, { useEffect, useState } from "react";
import { useDispatch } from "react-redux";
import { changeMaster } from "../../slices/localVariablesSlice";
const TagInput = ({
label = "Tags",
name = "tags",
placeholder = "Enter ... ",
placeholder = "Start typing to add...",
color = "#e9ecef",
options = [],
}) => {
const [tags, setTags] = useState([]);
const [input, setInput] = useState("");
const { setValue } = useFormContext();
const [suggestions, setSuggestions] = useState([]);
const { setValue, trigger } = useFormContext();
const dispatch = useDispatch();
useEffect(() => {
setValue(name, tags); // sync to form when tags change
}, [tags, name, setValue]);
setValue(
name,
tags.map((tag) => ({
id: tag.id ?? null,
name: tag.name,
}))
);
}, [ tags, name, setValue ] );
const addTag = (e) => {
e.preventDefault();
const trimmed = input.trim();
if (trimmed !== "" && !tags.includes(trimmed)) {
setTags([...tags, trimmed]);
setInput("");
useEffect(() => {
if (input.trim() === "") {
setSuggestions([]);
} else {
const filtered = options?.filter(
(opt) =>
opt?.name?.toLowerCase()?.includes(input?.toLowerCase()) &&
!tags?.some((tag) => tag?.name === opt?.name)
);
setSuggestions(filtered);
}
}, [input, options, tags]);
const addTag = async ( tagObj ) =>
{
if (!tags.some((tag) => tag.id === tagObj.id)) {
const cleanedTag = {
id: tagObj.id ?? null,
name: tagObj.name,
};
const newTags = [...tags, cleanedTag];
setTags(newTags);
setValue(name, newTags, { shouldValidate: true }); // only id + name
await trigger(name);
setInput("");
setSuggestions([]);
}
};
const handleInputKeyDown = (e) => {
if (e.key === "Enter" && input.trim() !== "") {
e.preventDefault();
const existing = options.find(
(opt) => opt.name.toLowerCase() === input.trim().toLowerCase()
);
const newTag = existing
? existing
: {
id: null,
name: input.trim(),
description: input.trim(),
};
addTag(newTag); // Call async function (not awaiting because it's UI input)
} else if (e.key === "Backspace" && input === "") {
setTags((prev) => prev.slice(0, -1));
}
};
const removeTag = (removeIndex) => {
const updated = tags.filter((_, i) => i !== removeIndex);
setTags(updated);
const handleSuggestionClick = (suggestion) => {
addTag(suggestion);
};
const removeTag = (indexToRemove) => {
const newTags = tags.filter((_, i) => i !== indexToRemove);
setTags(newTags);
setValue(name, newTags, { shouldValidate: true });
trigger(name);
};
const backgroundColor = color || "#f8f9fa";
@ -34,32 +91,72 @@ const TagInput = ({
return (
<>
<label htmlFor={name} className="form-label">{label}</label>
<div className="form-control form-control-sm d-flex justify-content-start flex-wrap gap-1" style={{ minHeight: "12px" }}>
{tags.map((tag, i) => (
<span
key={i}
className="d-flex align-items-center"
<label htmlFor={name} className="form-label">
{label}
</label>
<div
className="form-control form-control-sm p-1"
style={{ minHeight: "38px", position: "relative" }}
>
<div className="d-flex flex-wrap align-items-center gap-1">
{tags.map((tag, index) => (
<span
key={index}
className="d-flex align-items-center"
style={{
color: iconColor,
backgroundColor,
padding: "2px 6px",
borderRadius: "2px",
fontSize: "0.85rem",
}}
>
{tag.name}
<i
className="bx bx-x bx-xs ms-1"
onClick={() => removeTag(index)}
style={{ cursor: "pointer" }}
/>
</span>
))}
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyDown={handleInputKeyDown}
placeholder={placeholder}
style={{
color: iconColor,
backgroundColor,
padding: "2px 3px",
borderRadius: "2px"
border: "none",
outline: "none",
flex: 1,
minWidth: "120px",
}}
onFocus={() => dispatch(changeMaster("Contact Tag"))}
/>
</div>
{suggestions.length > 0 && (
<ul
className="list-group position-absolute mt-1 bg-white w-50 shadow-sm"
style={{
zIndex: 1000,
maxHeight: "150px",
overflowY: "auto",
}}
>
{tag}
<i className="bx bx-x bx-xs ms-1" onClick={() => removeTag(i)}></i>
</span>
))}
<input
type="text"
className="border-0 flex-grow-1"
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyDown={(e) => (e.key === "Enter" ? addTag(e) : null)}
placeholder={placeholder}
style={{ outline: "none", minWidth: "120px" }}
/>
{suggestions.map((sugg, i) => (
<li
key={i}
className="dropdown-item p-1 hoverBox"
onClick={() => handleSuggestionClick(sugg)}
style={{ cursor: "pointer", fontSize: "0.875rem" }}
>
{sugg.name}
</li>
))}
</ul>
)}
</div>
</>
);