nested-comments-react
Version:
A simple react component for nested comments
227 lines (206 loc) • 6.54 kB
JSX
import React from "react";
import { useState, useEffect } from "react";
import ReplyBox from "../ReplyBox/ReplyBox";
import "./NestedComments.css";
import { v4 as uuid } from "uuid";
import ReplyIcon from "../Svg/ReplyIcon";
export function NestedComments({
data = [],
commentAdded = () => {},
userName,
paginationSize = 2,
userId,
}) {
const PARENT_ROOT = "*";
let [treeData, setTreeData] = useState({ children: [], id: PARENT_ROOT });
let childSet = new Set();
let [mapColors, setMapColors] = useState({});
const getRandomColor = (userId) => {
let mapColorsCopy = { ...mapColors };
if (userId !== undefined && mapColorsCopy.userId !== undefined)
return mapColors.userId;
var color = "#";
for (var i = 0; i < 6; i++) {
color += Math.floor(Math.random() * 10);
}
if (userId !== undefined) setMapColors({ ...mapColors, userId: color });
return color;
};
const createTree = (src, mapRelations) => {
if (!mapRelations.has(src.childId)) {
let obj = {
id: src.childId,
name: src.name,
comment: src.comment,
color: src.color !== undefined ? src.color : getRandomColor(src.userId),
children: [],
};
return obj;
} else {
let children = [];
let obj = {
id: src.childId,
name: src.name,
comment: src.comment,
color: src.color !== undefined ? src.color : getRandomColor(src.userId),
children,
};
mapRelations.get(src.childId).forEach((ele) => {
children.push(createTree(ele, mapRelations));
});
return obj;
}
};
const createMap = (data) => {
let mapRelations = new Map();
data.forEach((ele) => {
childSet.add(ele.childId);
if (mapRelations.has(ele.parentId)) {
mapRelations.set(ele.parentId, [
...mapRelations.get(ele.parentId),
ele,
]);
} else {
mapRelations.set(ele.parentId, [ele]);
}
});
return mapRelations;
};
const getRootId = () => {
let res = new Set();
data.forEach((ele) => {
if (!childSet.has(ele.parentId)) {
res.add(ele.parentId);
}
});
return [...res];
};
useEffect(() => {
let mapRelations = createMap(data);
let res = [];
let rootIds = getRootId();
if (rootIds.length > 0) {
for (let rootId of rootIds) {
mapRelations.get(rootId).forEach((ele, index) => {
res.push(createTree(mapRelations.get(rootId)[index], mapRelations));
});
}
}
let nestedData = { children: [...res], id: PARENT_ROOT };
setTreeData(nestedData);
}, []);
let Recurse = ({ root, commentAdded }) => {
let [showComments, setShowComments] = useState(false);
let [showTextBox, setShowTextBox] = useState(false);
let [treeKey, setTreeKey] = useState("");
let [currLimit, setCurrLimit] = useState(paginationSize);
const cancel = () => {
setShowTextBox(false);
};
const onReply = (reply) => {
const unique_id = uuid();
const small_id = unique_id.slice(0, 8);
root.children.unshift({
id: small_id,
name: userName,
comment: reply,
color: getRandomColor(userId),
children: [],
});
setShowComments(true);
setShowTextBox(false);
setTreeKey(small_id);
commentAdded({
parentId: root.id == "*" ? uuid().slice(0, 8) : root.id,
childId: small_id,
comment: reply,
name: userName,
userId: userId,
});
};
return (
<div>
{root.id === PARENT_ROOT && (
<ReplyBox onReply={(reply) => onReply(reply)} isRoot={true} />
)}
<div className={root.id !== PARENT_ROOT ? "recursive-align" : ""}>
{root.id !== PARENT_ROOT && (
<div style={{ display: "flex" }}>
<div
className="circle"
style={{ backgroundColor: root.color }}
title={root.name}
>
<p className="circle-inner">
{root && root.name && root.name.substring(0, 2)}
</p>
</div>{" "}
<div className="nested-comment">{root && root.comment}</div>
</div>
)}
{root.id !== PARENT_ROOT && (
<div className="recursive-reply-button-div">
<div className="reply-img-div">
<ReplyIcon className="reply-icon" />
</div>
<div>
<p
onClick={() => setShowTextBox(!showTextBox)}
className="recursive-reply-button"
>
Reply
</p>
</div>
</div>
)}
{showTextBox && (
<div className="recursive-reply-box-align">
<ReplyBox
onReply={(reply) => onReply(reply)}
cancel={() => cancel()}
></ReplyBox>
</div>
)}
{((showComments && root) || root.id === PARENT_ROOT) &&
root.children.slice(0, currLimit).map((ele) => {
return (
<span key={ele.id}>
<div>
<Recurse root={ele} commentAdded={commentAdded} />
</div>
</span>
);
})}
{!showComments &&
root.id !== PARENT_ROOT &&
root.children &&
root.children.length != 0 && (
<p
className="recursive-view-replies-button"
onClick={() => setShowComments(true)}
>
view replies
</p>
)}
{(showComments || root.id === PARENT_ROOT) &&
root.children.length != PARENT_ROOT &&
currLimit < root.children.length && (
<p
className="show-more-button"
onClick={() => {
setCurrLimit(currLimit + paginationSize);
}}
>
show more
</p>
)}
</div>
</div>
);
};
return (
<div>
<Recurse root={treeData} commentAdded={commentAdded} />
</div>
);
}