Note: i also made a package which you can use
https://www.npmjs.com/package/react-mentions-input-latest
code:
./MentionsInput.jsx
import React, { useEffect, useRef, useState } from "react";
import { setCaretToEnd, createSpanDiv } from "./utils";
import "./MentionsInput.css";
function MentionsInput(props) {
const { data: UsersList, trigger } = props;
const [showSuggestion, setShowSuggestion] = useState(false);
const main_text_area = document.getElementById("main-text-area");
const main_text_areaRef = useRef(null);
const handleSuggestionClick = (name, id) => {
createSpanDiv(name, main_text_area);
setCaretToEnd(main_text_areaRef?.current);
const childNodes = Array.from(main_text_areaRef?.current?.childNodes);
childNodes.forEach((node) => {
if (node.nodeType !== Node.TEXT_NODE) return;
const text = node.textContent;
const replacedText = text.replace(
new RegExp(`${trigger}(\\w+)`, "g"),
""
);
node.textContent = replacedText;
});
setShowSuggestion(false);
};
useEffect(() => {
if (!main_text_areaRef?.current) return;
const main_text_area = main_text_areaRef?.current;
const inputVal = document.getElementById("ow808");
main_text_area.setAttribute("tabindex", 1);
main_text_area.focus();
// Function to handle keyup event
const handleKeyUp = (e) => {
const inputText = e?.target?.innerText;
const atIndex = inputText.lastIndexOf(trigger);
const showSuggestion =
atIndex !== -1 &&
inputText.substring(atIndex + 1).split(" ")[0].length > 2;
setShowSuggestion(showSuggestion);
};
// Add event listener
inputVal.addEventListener("keyup", handleKeyUp);
// Cleanup function to remove event listener
return () => inputVal.removeEventListener("keyup", handleKeyUp);
}, [main_text_areaRef?.current]);
return (
<div className="h-screen w-full flex justify-center items-center bg-pink-100">
<div className="w-[400px] h-[20px]" id="ow808">
{showSuggestion && (
<section className="flex flex-col m-2 my-2 bg-white px-2 py-3 rounded-lg transition-all ease-in-out duration-500">
<div className="max-h-36 overflow-y-scroll">
{UsersList &&
UsersList.length > 0 &&
UsersList.map((user, index) => (
<div
className={`flex justify-start items-center p-2 space-x-1 cursor-pointer hover:bg-gray-200 ${
index === 4 || index === UsersList.length - 1
? ``
: `border-b-[1.5px]`
} transition-all ease-in-out duration-500 focus:bg-[#c4c4c4] `}
key={index}
onClick={() => {
handleSuggestionClick(user.name, user.id);
}}
>
<img
className="h-6 w-6 rounded-full object-cover"
src={user?.userAvatar}
alt={user?.name}
/>
<p className="overflow-x-hidden">{user.name}</p>
</div>
))}
</div>
</section>
)}
<div className="cnOaDb I9OJHe">
<div
className="border-2 border-gray-400 focus:outline-none px-3 py-2 rounded-lg"
role="textbox"
aria-multiline="true"
spellCheck="true"
id="main-text-area"
ref={main_text_areaRef}
contentEditable="true"
dir="ltr"
placeholder="Type Somehting here ...."
></div>
</div>
</div>
</div>
);
}
export default MentionsInput;
./utils.js
export function setCaretToEnd(element) {
element.focus();
const range = document.createRange();
const selection = window.getSelection();
range.selectNodeContents(element);
range.collapse(false);
selection.removeAllRanges();
selection.addRange(range);
}
export const UsersList = [
{
id: 235075,
name: "Nitin P",
userAvatar:
"https://cdn.pixabay.com/photo/2018/03/27/21/43/startup-3267505_640.jpg",
},
{
id: 473839,
name: "Nitin P",
userAvatar:
"https://cdn.pixabay.com/photo/2018/03/27/21/43/startup-3267505_640.jpg",
},
{
id: 2360589,
name: "Nitin Pahwa",
userAvatar:
"https://cdn.pixabay.com/photo/2018/03/27/21/43/startup-3267505_640.jpg",
},
{
id: 713607,
name: "Nitin Pal",
userAvatar:
"https://cdn.pixabay.com/photo/2018/03/27/21/43/startup-3267505_640.jpg",
},
{
id: 1496449,
name: "Nitin Pandey",
userAvatar:
"https://cdn.pixabay.com/photo/2018/03/27/21/43/startup-3267505_640.jpg",
},
];
export const createSpanDiv = (name, main_text_area) => {
var spanElement = document.createElement("span");
spanElement.textContent = name;
spanElement.classList = "text-indigo-500 font-bold mentioned-user";
spanElement.setAttribute("contenteditable", "false");
main_text_area.appendChild(spanElement);
};
./MentionsInput.css__
[contenteditable="true"]:empty:before {
content: attr(placeholder);
pointer-events: none;
display: block; /* For Firefox */
color: rgba(92, 86, 89, 0.497);
}
/* width */
::-webkit-scrollbar {
width: 7px;
}
/* Track */
::-webkit-scrollbar-track {
background: #888;
border-radius: 10px;
}
/* Handle */
::-webkit-scrollbar-thumb {
background: #5e5d5d;
border-radius: 5px;
}
/* Handle on hover */
::-webkit-scrollbar-thumb:hover {
background: #555;
}
use it like this
./App.js
import React, { useEffect, useState } from "react";
import {
BrowserRouter as Router,
Routes,
Route,
Navigate,
useNavigate,
} from "react-router-dom";
import MentionsInput from "./Components/MentionsInput";
import { UsersList } from "./Components/utils";
function App() {
return (
<Router>
<Routes>
<Route
path="/"
element={
<>
<MentionsInput data={UsersList} trigger="#" />
</>
}
/>
</Routes>
</Router>
);
}
export default App;
Top comments (1)
Library Not working. Perhaps, you may want to publish a working demo