| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| import { styled, alpha } from "@mui/material/styles"; |
| import * as React from "react"; |
| import { |
| TreeItem, |
| TreeItemProps, |
| useTreeItemState, |
| TreeItemContentProps |
| } from "@mui/x-tree-view/TreeItem"; |
| import clsx from "clsx"; |
| import { SimpleTreeView } from "@mui/x-tree-view"; |
| import { Typography } from "@components/muiComponents"; |
| import { CircularProgress, Stack } from "@mui/material"; |
| import { TreeNode } from "@models/treeStructureType"; |
| import FolderOutlinedIcon from "@mui/icons-material/FolderOutlined"; |
| import InsertDriveFileOutlinedIcon from "@mui/icons-material/InsertDriveFileOutlined"; |
| import { isEmpty } from "@utils/Utils"; |
| import { toast } from "react-toastify"; |
| |
| const CustomContentRoot = styled("div")(({ theme }) => ({ |
| WebkitTapHighlightColor: "#0E8173", |
| "&&:hover, &&.Mui-disabled, &&.Mui-focused, &&.Mui-selected, &&.Mui-selected.Mui-focused, &&.Mui-selected:hover": |
| { |
| backgroundColor: "transparent" |
| }, |
| ".MuiTreeItem-contentBar": { |
| position: "absolute", |
| width: "100%", |
| height: 24, |
| left: 0 |
| }, |
| ".MuiTreeItem-iconContainer": { |
| zIndex: 9 |
| }, |
| "&:hover .MuiTreeItem-contentBar": { |
| backgroundColor: theme.palette.action.hover, |
| "@media (hover: none)": { |
| backgroundColor: "transparent" |
| } |
| }, |
| "&.Mui-disabled .MuiTreeItem-contentBar": { |
| opacity: theme.palette.action.disabledOpacity, |
| backgroundColor: "transparent" |
| }, |
| "&.Mui-focused .MuiTreeItem-contentBar": { |
| backgroundColor: theme.palette.action.focus |
| }, |
| "&.Mui-selected .MuiTreeItem-contentBar": { |
| backgroundColor: "#beebff" |
| }, |
| "&.Mui-selected:hover .MuiTreeItem-contentBar": { |
| backgroundColor: alpha( |
| theme.palette.primary.main, |
| theme.palette.action.selectedOpacity + theme.palette.action.hoverOpacity |
| ), |
| |
| "@media (hover: none)": { |
| backgroundColor: alpha( |
| theme.palette.primary.main, |
| theme.palette.action.selectedOpacity |
| ) |
| } |
| }, |
| "&.Mui-selected.Mui-focused .MuiTreeItem-contentBar": { |
| backgroundColor: "#beebff" |
| } |
| })); |
| |
| const CustomContent = React.forwardRef(function CustomContent( |
| props: TreeItemContentProps, |
| ref |
| ) { |
| const { |
| className, |
| classes, |
| label, |
| itemId, |
| icon: iconProp, |
| expansionIcon, |
| displayIcon |
| } = props; |
| |
| const { |
| disabled, |
| expanded, |
| selected, |
| focused, |
| handleExpansion, |
| handleSelection, |
| preventSelection |
| } = useTreeItemState(itemId); |
| |
| const icon = iconProp || expansionIcon || displayIcon; |
| const handleMouseDown = ( |
| event: React.MouseEvent<HTMLDivElement, MouseEvent> |
| ) => { |
| event.stopPropagation(); |
| preventSelection(event); |
| }; |
| |
| const handleClick = (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => { |
| event.stopPropagation(); |
| event.preventDefault(); |
| handleExpansion(event); |
| handleSelection(event); |
| }; |
| |
| return ( |
| <CustomContentRoot |
| className={clsx(className, classes.root, { |
| "Mui-expanded": expanded, |
| "Mui-selected": selected, |
| "Mui-focused": focused, |
| "Mui-disabled": disabled |
| })} |
| onClick={(e) => { |
| handleClick(e); |
| }} |
| onMouseDown={handleMouseDown} |
| ref={ref as React.Ref<HTMLDivElement>} |
| > |
| <div className="MuiTreeItem-contentBar" /> |
| <div className={classes.iconContainer}>{icon}</div> |
| <Typography component="div" className={classes.label}> |
| {label} |
| </Typography> |
| </CustomContentRoot> |
| ); |
| }); |
| |
| const CustomTreeItem = React.forwardRef(function CustomTreeItem( |
| props: TreeItemProps, |
| ref: React.Ref<HTMLLIElement> |
| ) { |
| return ( |
| <TreeItem |
| sx={{ |
| "& .MuiTreeItem-label": { |
| fontWeight: "400 !important", |
| fontSize: "1rem !important", |
| lineHeight: "24px !important", |
| color: "rgba(0,0,0,0.8)" |
| }, |
| |
| "& .MuiTreeItem-content svg": { |
| color: "rgba(0,0,0,0.8)", |
| fontSize: "18px !important" |
| } |
| }} |
| ContentComponent={CustomContent} |
| {...props} |
| ref={ref} |
| /> |
| ); |
| }); |
| |
| const FormTreeView: React.FC<{ |
| treeData: TreeNode[]; |
| treeName: string; |
| loader?: boolean; |
| onNodeSelect?: any; |
| searchTerm: string; |
| }> = ({ treeData, treeName, loader, onNodeSelect, searchTerm }) => { |
| const [expandedItems, setExpandedItems] = React.useState<string[]>([]); |
| const toastId: any = React.useRef(null); |
| |
| React.useEffect(() => { |
| const handleExpandClick = () => { |
| setExpandedItems(() => [treeName]); |
| }; |
| handleExpandClick(); |
| }, []); |
| |
| const handleExpandedItemsChange = ( |
| _event: React.SyntheticEvent, |
| itemIds: string[] |
| ) => { |
| setExpandedItems(itemIds); |
| }; |
| |
| const filteredData = treeData.filter((node) => { |
| return ( |
| node.label.toLowerCase().includes(searchTerm.toLowerCase()) || |
| (node.children && |
| node.children.some((child) => |
| child.label.toLowerCase().includes(searchTerm.toLowerCase()) |
| )) |
| ); |
| }); |
| |
| const highlightText = (text: string) => { |
| if (!searchTerm) return text; |
| |
| const parts = text.split(new RegExp(`(${searchTerm})`, "gi")); |
| return parts.map((part, index) => |
| part.toLowerCase() === searchTerm.toLowerCase() ? ( |
| <span key={index} style={{ color: "red", fontWeight: "bold" }}> |
| {part} |
| </span> |
| ) : ( |
| part |
| ) |
| ); |
| }; |
| const renderTreeItem = (node: TreeNode) => |
| node?.id && ( |
| <CustomTreeItem |
| key={node.id} |
| itemId={ |
| node.types != "parent" |
| ? node.id === "No Records Found" |
| ? node.id |
| : `${node.id}@${node?.parent}` |
| : node.id |
| } |
| label={ |
| <div |
| onClick={(_event: React.MouseEvent<HTMLElement>) => { |
| if (node.id === "No Records Found") { |
| toast.dismiss(toastId.current); |
| toastId.current = toast.info("No terms present"); |
| return; |
| } |
| if (node.types == "parent" && isEmpty(node.children)) { |
| toast.dismiss(toastId.current); |
| toastId.current = toast.warning(`No ${treeName}`); |
| return; |
| } |
| }} |
| className="custom-treeitem-label" |
| > |
| {node.id != "No Records Found" && node.types == "parent" ? ( |
| <FolderOutlinedIcon className="custom-treeitem-icon" /> |
| ) : ( |
| <InsertDriveFileOutlinedIcon /> |
| )} |
| <span className="tree-item-label">{highlightText(node.label)}</span> |
| </div> |
| } |
| > |
| {node.children && node.children.map((child) => renderTreeItem(child))} |
| </CustomTreeItem> |
| ); |
| |
| return ( |
| <> |
| <Stack className="sidebar-tree-box"> |
| <SimpleTreeView |
| expandedItems={expandedItems} |
| onExpandedItemsChange={handleExpandedItemsChange} |
| aria-label="customized" |
| defaultExpandedItems={[treeName]} |
| className="sidebar-treeview" |
| onItemFocus={(_e, itemId) => { |
| if (itemId.indexOf("@") == -1) { |
| return; |
| } |
| onNodeSelect(itemId); |
| }} |
| > |
| <TreeItem |
| itemId={treeName} |
| label={ |
| <Stack |
| display="flex" |
| alignItems="center" |
| flexDirection="row" |
| sx={{ borderBottom: "1px solid rgba(25,255,255,0.1)" }} |
| className="tree-item-parent-label" |
| > |
| <Stack flexGrow={1}> |
| <span>{treeName}</span> |
| </Stack> |
| </Stack> |
| } |
| sx={{ |
| "& .MuiTreeItem-label": { |
| fontWeight: "bold !important", |
| fontSize: "1rem !important", |
| lineHeight: "24px !important", |
| color: "#38bb9b" |
| }, |
| "& .MuiTreeItem-content svg": { |
| color: "#38bb9b", |
| fontSize: "20px !important" |
| } |
| }} |
| > |
| {loader ? ( |
| <Stack className="tree-item-loader-box"> |
| <CircularProgress className="tree-item-loader" /> |
| </Stack> |
| ) : ( |
| filteredData.map((node: TreeNode) => renderTreeItem(node)) |
| )} |
| </TreeItem> |
| </SimpleTreeView> |
| </Stack> |
| </> |
| ); |
| }; |
| |
| export default FormTreeView; |