Skip to content

Instantly share code, notes, and snippets.

@keyurparalkar
Created March 4, 2024 10:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save keyurparalkar/176a59c555022cc8b532dd325a34262c to your computer and use it in GitHub Desktop.
Save keyurparalkar/176a59c555022cc8b532dd325a34262c to your computer and use it in GitHub Desktop.
Recursive Tree component
import styled from "styled-components";
import { ReactElement, useState } from "react";
type Tree = {
id: string;
name: string;
icons?: JSX.Element[];
children?: Tree[];
};
type RecursiveTreeProps = {
tree: Tree;
};
interface CustomDetailsProps extends Omit<Tree, "id"> {
renderTree: ReactElement;
}
const StyledUnorderedList = styled.ul`
ul,
li {
list-style: none;
margin: 0;
padding: 0;
}
ul {
margin-left: 0.8rem;
}
`;
const StyledDetails = styled.details<{ hasIcons: boolean | undefined }>`
& summary {
text-align: left;
}
& summary.custom-icons {
display: flex;
align-items: center;
list-style: none;
& span {
margin-left: 0.525rem;
}
}
// Below styles are for the border around the details element ===================
${(props) =>
props?.hasIcons &&
`
& ul {
padding-left: 0.625rem;
border-left: 2px dashed white;
}
& ul li {
position: relative;
&::before {
content: "--";
display: block;
height: 20px;
width: 13px;
/* border: 2px solid red; */
position: absolute;
left: -1.5%;
}
}
`}// ============================================================================
`;
const CustomDetails = (props: CustomDetailsProps) => {
const { icons, name, renderTree } = props;
const [isOpen, setIsOpen] = useState(false);
const hasIcons = icons && icons.length > 0;
const handleDrawerOpen = (
e: React.SyntheticEvent<HTMLDetailsElement, ToggleEvent>
) => {
// This stops event bubbling;
e.stopPropagation();
setIsOpen(e.nativeEvent.newState === "open" ? true : false);
};
return (
<StyledDetails onToggle={handleDrawerOpen} hasIcons={hasIcons}>
<summary className={hasIcons ? "custom-icons" : undefined}>
{hasIcons && (isOpen ? icons[1] : icons[0])}
<span>{name}</span>
</summary>
{renderTree}
</StyledDetails>
);
};
const RecursiveTree = (props: RecursiveTreeProps) => {
const { tree } = props;
return (
<StyledUnorderedList>
{tree?.children &&
tree.children.map((item) => (
<li key={`key-${item.id}`}>
<CustomDetails
icons={item.icons}
name={item.name}
renderTree={<RecursiveTree tree={item} />}
/>
</li>
))}
</StyledUnorderedList>
);
};
export default RecursiveTree;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment