modified TagInput, for array an object
This commit is contained in:
parent
33f6220622
commit
354c653240
@ -1,32 +1,89 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
|
||||||
import { useFormContext } from "react-hook-form";
|
import { useFormContext } from "react-hook-form";
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { useDispatch } from "react-redux";
|
||||||
|
import { changeMaster } from "../../slices/localVariablesSlice";
|
||||||
|
|
||||||
const TagInput = ({
|
const TagInput = ({
|
||||||
label = "Tags",
|
label = "Tags",
|
||||||
name = "tags",
|
name = "tags",
|
||||||
placeholder = "Enter ... ",
|
placeholder = "Start typing to add...",
|
||||||
color = "#e9ecef",
|
color = "#e9ecef",
|
||||||
|
options = [],
|
||||||
}) => {
|
}) => {
|
||||||
const [tags, setTags] = useState([]);
|
const [tags, setTags] = useState([]);
|
||||||
const [input, setInput] = useState("");
|
const [input, setInput] = useState("");
|
||||||
const { setValue } = useFormContext();
|
const [suggestions, setSuggestions] = useState([]);
|
||||||
|
const { setValue, trigger } = useFormContext();
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setValue(name, tags); // sync to form when tags change
|
setValue(
|
||||||
}, [tags, name, setValue]);
|
name,
|
||||||
|
tags.map((tag) => ({
|
||||||
|
id: tag.id ?? null,
|
||||||
|
name: tag.name,
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
}, [ tags, name, setValue ] );
|
||||||
|
|
||||||
const addTag = (e) => {
|
useEffect(() => {
|
||||||
e.preventDefault();
|
if (input.trim() === "") {
|
||||||
const trimmed = input.trim();
|
setSuggestions([]);
|
||||||
if (trimmed !== "" && !tags.includes(trimmed)) {
|
} else {
|
||||||
setTags([...tags, trimmed]);
|
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("");
|
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 handleSuggestionClick = (suggestion) => {
|
||||||
const updated = tags.filter((_, i) => i !== removeIndex);
|
addTag(suggestion);
|
||||||
setTags(updated);
|
};
|
||||||
|
|
||||||
|
const removeTag = (indexToRemove) => {
|
||||||
|
const newTags = tags.filter((_, i) => i !== indexToRemove);
|
||||||
|
setTags(newTags);
|
||||||
|
setValue(name, newTags, { shouldValidate: true });
|
||||||
|
trigger(name);
|
||||||
};
|
};
|
||||||
|
|
||||||
const backgroundColor = color || "#f8f9fa";
|
const backgroundColor = color || "#f8f9fa";
|
||||||
@ -34,33 +91,73 @@ const TagInput = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<label htmlFor={name} className="form-label">{label}</label>
|
<label htmlFor={name} className="form-label">
|
||||||
<div className="form-control form-control-sm d-flex justify-content-start flex-wrap gap-1" style={{ minHeight: "12px" }}>
|
{label}
|
||||||
{tags.map((tag, i) => (
|
</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
|
<span
|
||||||
key={i}
|
key={index}
|
||||||
className="d-flex align-items-center"
|
className="d-flex align-items-center"
|
||||||
style={{
|
style={{
|
||||||
color: iconColor,
|
color: iconColor,
|
||||||
backgroundColor,
|
backgroundColor,
|
||||||
padding: "2px 3px",
|
padding: "2px 6px",
|
||||||
borderRadius: "2px"
|
borderRadius: "2px",
|
||||||
|
fontSize: "0.85rem",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{tag}
|
{tag.name}
|
||||||
<i className="bx bx-x bx-xs ms-1" onClick={() => removeTag(i)}></i>
|
<i
|
||||||
|
className="bx bx-x bx-xs ms-1"
|
||||||
|
onClick={() => removeTag(index)}
|
||||||
|
style={{ cursor: "pointer" }}
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
))}
|
))}
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="border-0 flex-grow-1"
|
|
||||||
value={input}
|
value={input}
|
||||||
onChange={(e) => setInput(e.target.value)}
|
onChange={(e) => setInput(e.target.value)}
|
||||||
onKeyDown={(e) => (e.key === "Enter" ? addTag(e) : null)}
|
onKeyDown={handleInputKeyDown}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
style={{ outline: "none", minWidth: "120px" }}
|
style={{
|
||||||
|
border: "none",
|
||||||
|
outline: "none",
|
||||||
|
flex: 1,
|
||||||
|
minWidth: "120px",
|
||||||
|
}}
|
||||||
|
onFocus={() => dispatch(changeMaster("Contact Tag"))}
|
||||||
/>
|
/>
|
||||||
</div>
|
</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",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{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>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user