Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
index.jsx316 linesDownload Raw Back to PgTreeView
1import { Checkbox } from '@mui/material';2import { makeStyles } from '@mui/styles';3import clsx from 'clsx';4import gettext from 'sources/gettext';5import React, { useEffect, useRef } from 'react';6import { Tree } from 'react-arborist';7import AutoSizer from 'react-virtualized-auto-sizer';8import ExpandMoreIcon from '@mui/icons-material/ExpandMore';9import ChevronRightIcon from '@mui/icons-material/ChevronRight';10import PropTypes from 'prop-types';11import IndeterminateCheckBoxIcon from '@mui/icons-material/IndeterminateCheckBox';12import EmptyPanelMessage from '../components/EmptyPanelMessage';13import CheckBoxIcon from '@mui/icons-material/CheckBox';14 15 16const useStyles = makeStyles((theme) => ({17  node: {18    display: 'inline-block',19    paddingLeft: '1.5rem',20    height: '100%',21  },22  checkboxStyle: {23    fill: theme.palette.primary.main24  },25  tree: {26    background: theme.palette.background.default,27    height: '100%',28    width: '100%',29    display: 'flex',30    flexDirection: 'column',31    flex: 1,32  },33  focusedNode: {34    background: theme.palette.primary.light,35  },36  leafNode: {37    marginLeft: '1.5rem'38  },39}));40 41export const PgTreeSelectionContext = React.createContext();42 43export default function PgTreeView({ data = [], hasCheckbox = false, selectionChange = null}) {44  let classes = useStyles();45  let treeData = data;46  const treeObj = useRef();47  const treeContainerRef = useRef();48  const [selectedCheckBoxNodes, setSelectedCheckBoxNodes] = React.useState([]);49 50  const onSelectionChange = () => {51    let selectedChNodes = treeObj.current.selectedNodes;52    if (hasCheckbox) {53      let selectedChildNodes = [];54 55      treeObj.current.selectedNodes.forEach((node) => {56        if(node.isInternal && !node.isOpen) {57          node.children.forEach((ch)=>{58            if(ch.data.isSelected && ch.isLeaf && !selectedChildNodes.includes(ch.id)) {59              selectedChildNodes.push(ch.id);60              selectedChNodes.push(ch);61            }62          });63        }64        selectedChildNodes.push(node.id);65      });66      setSelectedCheckBoxNodes(selectedChildNodes);67    }68 69    selectionChange?.(selectedChNodes);70  };71 72  return (<>73    { treeData.length > 0 ?74      <PgTreeSelectionContext.Provider value={selectedCheckBoxNodes}>75        <div ref={(containerRef) => treeContainerRef.current = containerRef} className={clsx(classes.tree)}>76          <AutoSizer>77            {({ width, height }) => (78              <Tree79                ref={(obj) => {80                  treeObj.current = obj;81                }}82                width={isNaN(width) ? 100 : width}83                height={isNaN(height) ? 100 : height}84                data={treeData}85                disableDrag={true}86                disableDrop={true}87                dndRootElement={treeContainerRef.current}88              >89                {90                  (props) => <Node onNodeSelectionChange={onSelectionChange} hasCheckbox={hasCheckbox} {...props}></Node>91                }92              </Tree>93            )}94          </AutoSizer>95        </div>96      </PgTreeSelectionContext.Provider>97      :98      <EmptyPanelMessage text={gettext('No objects are found to display')}/>99    }100  </>101  );102}103 104PgTreeView.propTypes = {105  data: PropTypes.array,106  selectionChange: PropTypes.func,107  hasCheckbox: PropTypes.bool,108};109 110function Node({ node, style, tree, hasCheckbox, onNodeSelectionChange}) {111  const classes = useStyles();112  const pgTreeSelCtx = React.useContext(PgTreeSelectionContext);113  const [isSelected, setIsSelected] = React.useState(pgTreeSelCtx.includes(node.id) || node.data?.isSelected);114  const [isIndeterminate, setIsIndeterminate] = React.useState(node?.parent.level==0);115 116  useEffect(()=>{117    setIsIndeterminate(node.data.isIndeterminate);118  }, [node?.data?.isIndeterminate]);119 120 121  useEffect(()=>{122    if(isSelected){123      if(!pgTreeSelCtx.includes(node.id)){124        tree.selectMulti(node.id);125        onNodeSelectionChange();126      }127    }128  }, [isSelected]);129 130 131  const onCheckboxSelection = (e) => {132    if (hasCheckbox) {133      setIsSelected(e.currentTarget.checked);134      node.data.isSelected = e.currentTarget.checked;135      if (e.currentTarget.checked) {136        node.selectMulti(node.id);137        if (!node.isLeaf) {138          node.data.isIndeterminate = false;139          selectAllChild(node, tree, 'checkbox', pgTreeSelCtx);140        } else if (node?.parent) {141          checkAndSelectParent(node);142        }143 144        if(node?.level == 0) {145          node.data.isIndeterminate = false;146        }147        node.focus();148      } else {149        node.deselect(node);150        if (!node.isLeaf) {151          deselectAllChild(node);152        }153 154        if(node?.parent){155          node.parent.data.isIndeterminate = false;156          delectPrentNode(node.parent);157        }158      }159    }160    tree.scrollTo(node.id, 'center');161    onNodeSelectionChange();162  };163 164  const onSelect = (e) => {165    node.focus();166    e.stopPropagation();167  };168 169  const onKeyDown = (e) => {170    if(e.code == 'Enter') {171      onSelect(e);172    }173  };174 175  return (176    <div style={style} className={clsx(node.isFocused ? classes.focusedNode : '')} onClick={onSelect} onKeyDown={onKeyDown}>177      <CollectionArrow node={node} tree={tree} selectedNodeIds={pgTreeSelCtx} />178      {179        hasCheckbox ? <Checkbox style={{ padding: 0 }} color="primary" className={clsx(!node.isInternal ? classes.leafNode: null)}180          checked={isSelected}181          checkedIcon={isIndeterminate  ? <IndeterminateCheckBoxIcon style={{height: '1.4rem'}} />: <CheckBoxIcon style={{height: '1.4rem'}} />}182          onChange={onCheckboxSelection}/> :183          <span className={clsx(node.data.icon)}></span>184      }185      <div className={clsx(node.data.icon, classes.node)}>{node.data.name}</div>186    </div>187  );188}189 190Node.propTypes = {191  node: PropTypes.object,192  style: PropTypes.any,193  tree: PropTypes.object,194  hasCheckbox: PropTypes.bool,195  onNodeSelectionChange: PropTypes.func196};197 198function CollectionArrow({ node, tree, selectedNodeIds }) {199  const toggleNode = () => {200    node.isInternal && node.toggle();201    if (node.isSelected && node.isOpen) {202      node.data.isSelected = true;203      selectAllChild(node, tree, 'expand', selectedNodeIds);204    }205  };206  return (207    <span onClick={toggleNode} onKeyDown={()=>{/* handled by parent */}}>208      {node.isInternal && node?.children.length > 0 ? <ToggleArrowIcon node={node} /> : null}209    </span>210  );211}212 213CollectionArrow.propTypes = {214  node: PropTypes.object,215  tree: PropTypes.object,216  selectedNodeIds: PropTypes.array217};218 219 220function ToggleArrowIcon({node}){221  return (<>{node.isOpen ? <ExpandMoreIcon /> : <ChevronRightIcon />}</>);222}223 224ToggleArrowIcon.propTypes = {225  node: PropTypes.object,226};227 228function checkAndSelectParent(chNode){229  let isAllChildSelected = true;230  chNode?.parent?.children?.forEach((child) => {231    if (!child.isSelected) {232      isAllChildSelected = false;233    }234  });235  if (chNode?.parent) {236    if (isAllChildSelected) {237      if (chNode.parent?.level == 0) {238        chNode.parent.data.isIndeterminate = true;239      } else {240        chNode.parent.data.isIndeterminate = false;241      }242      chNode.parent.selectMulti(chNode.parent.id);243    } else {244      chNode.parent.data.isIndeterminate = true;245      chNode.parent.selectMulti(chNode.parent.id);246    }247    chNode.parent.data.isSelected = true;248    checkAndSelectParent(chNode.parent);249  }250}251 252checkAndSelectParent.propTypes = {253  chNode: PropTypes.object254};255 256function delectPrentNode(chNode){257  if (chNode) {258    let isAnyChildSelected = false;259    chNode.children.forEach((childNode)=>{260      if(childNode.isSelected && !isAnyChildSelected){261        isAnyChildSelected = true;262      }263    });264    if(isAnyChildSelected){265      chNode.data.isSelected = true;266      chNode.data.isIndeterminate = true;267    } else {268      chNode.deselect(chNode);269      chNode.data.isSelected = false;270    }271  }272 273  if (chNode?.parent) {274    delectPrentNode(chNode.parent);275  }276}277 278function selectAllChild(chNode, tree, source, selectedNodeIds){279  let selectedChild = 0;280  chNode?.children?.forEach(child => {281 282    if(!child.isLeaf) {283      child.data.isIndeterminate = false;284    }285    if((source == 'expand' && selectedNodeIds.includes(child.id)) || source == 'checkbox') {286      child.data.isSelected = true;287      selectedChild += 1;288    }289    child.selectMulti(child.id);290 291    if (child?.children) {292      selectAllChild(child, tree, source, selectedNodeIds);293    }294  });295 296  if(selectedChild < chNode?.children.length ){297    chNode.data.isIndeterminate = true;298  } else {299    chNode.data.isIndeterminate = false;300  }301 302  if (chNode?.parent) {303    checkAndSelectParent(chNode);304  }305}306 307function deselectAllChild(chNode){308  chNode?.children.forEach(child => {309    child.deselect(child);310    child.data.isSelected = false;311    if (child?.children) {312      deselectAllChild(child);313    }314  });315}316 
codekingpro/portable-devtools · Team Ai