Skip to content

Instantly share code, notes, and snippets.

@pradhumgp
Last active September 22, 2024 05:40
Show Gist options
  • Save pradhumgp/10ed9447415be5366123009ce925193d to your computer and use it in GitHub Desktop.
Save pradhumgp/10ed9447415be5366123009ce925193d to your computer and use it in GitHub Desktop.
import { useState, useEffect } from "react";
// Recursive Component to render the tree structure with collapsible and auto-expand functionality on search
const TreeNode = ({ node, isExpandedInitially, onItemClick }) => {
const [isExpanded, setIsExpanded] = useState(isExpandedInitially);
// Update the expand state when the initial expand prop changes (e.g., during search)
useEffect(() => {
setIsExpanded(isExpandedInitially);
}, [isExpandedInitially]);
// Determine the name of the current node (category, subcategory, etc.)
const nodeName =
node.name ||
node.category_name ||
node.sub_category_name ||
node.sub_sub_category_name;
// Check if there are children to render
const hasChildren =
node.categories ||
node.sub_categories ||
node.sub_sub_categories ||
node.sub_sub_sub_categories;
// Toggle the collapse/expand state
const handleExpandClick = (e) => {
e.stopPropagation(); // Prevent the click event from propagating to the node div
setIsExpanded(!isExpanded);
};
// Handle click event for the node
const handleNodeClick = () => {
if (onItemClick) {
onItemClick(node); // Call the provided onClick handler
}
};
return (
<li>
<div
style={{ cursor: hasChildren ? "pointer" : "default" }}
onClick={handleNodeClick}
>
{hasChildren && (
<span
style={{ marginRight: "8px", cursor: "pointer" }}
onClick={handleExpandClick}
>
{isExpanded ? "▼" : "▶"} {/* Arrow to indicate collapse/expand */}
</span>
)}
{nodeName}
</div>
{/* Recursively render child nodes if the current node is expanded */}
{isExpanded && hasChildren && (
<ul style={{ marginLeft: "20px" }}>
{node.categories && node.categories.map((child, index) => (
<TreeNode key={index} node={child} isExpandedInitially={isExpandedInitially} onItemClick={onItemClick} />
))}
{node.sub_categories && node.sub_categories.map((child, index) => (
<TreeNode key={index} node={child} isExpandedInitially={isExpandedInitially} onItemClick={onItemClick} />
))}
{node.sub_sub_categories && node.sub_sub_categories.map((child, index) => (
<TreeNode key={index} node={child} isExpandedInitially={isExpandedInitially} onItemClick={onItemClick} />
))}
{node.sub_sub_sub_categories && node.sub_sub_sub_categories.map((child, index) => (
<TreeNode key={index} node={child} isExpandedInitially={isExpandedInitially} onItemClick={onItemClick} />
))}
</ul>
)}
</li>
);
};
// Function to filter the tree based on the search query
const filterTree = (nodes, query) => {
if (!query) return nodes;
return nodes
.map(node => {
// Recursively filter through all levels
const filteredNode = { ...node };
const childrenKeys = ["categories", "sub_categories", "sub_sub_categories", "sub_sub_sub_categories"];
// If this node matches the query, we will include all its children
const nodeMatches = (node.name || node.category_name || node.sub_category_name || node.sub_sub_category_name || "").toLowerCase().includes(query.toLowerCase());
if (nodeMatches) {
return filteredNode; // Return the entire node with all its children
}
// Otherwise, recursively filter the children
childrenKeys.forEach(key => {
if (filteredNode[key]) {
filteredNode[key] = filterTree(filteredNode[key], query);
}
});
// If any of the children match, keep the node
const childrenMatch = childrenKeys.some(key => filteredNode[key] && filteredNode[key].length > 0);
if (childrenMatch) {
return filteredNode;
}
return null; // Remove the node if it and its children don't match
})
.filter(node => node !== null); // Filter out null nodes
};
const CatalogTree2 = ({catalogData}) => {
const [searchQuery, setSearchQuery] = useState("");
const [filteredData, setFilteredData] = useState(catalogData);
const handleSearch = (e) => {
const query = e.target.value;
setSearchQuery(query);
setFilteredData(filterTree(catalogData, query));
};
// Define the action to perform on item click
const handleItemClick = (node) => {
console.log("Clicked item:", node);
// Perform any action you want with the clicked node here
};
return (
<div>
<input
type="text"
placeholder="Search catalog..."
value={searchQuery}
onChange={handleSearch}
style={{ padding: "10px", width: "300px", marginBottom: "20px" }}
/>
<ul>
{filteredData.map((node, index) => (
<TreeNode key={index} node={node} isExpandedInitially={!!searchQuery} onItemClick={handleItemClick} />
))}
</ul>
</div>
);
};
export default CatalogTree2;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment