Как установить расширенную подпорку для материала пользовательского интерфейса TreeView условно - PullRequest
0 голосов
/ 04 февраля 2020

Я пытаюсь создать компонент выбора дерева, подобный компоненту antd tree-select с использованием пользовательского интерфейса материала. У меня есть компоненты Material-UI TextField и TreeView один под другим. Изначально я хочу, чтобы древовидное представление свернулось, и пользователь должен иметь возможность вручную его развернуть. Но когда пользователь вводит некоторый текст в текстовое поле, я хочу, чтобы узлы, которые имеют подобный текст, были расширены. У меня есть код для поиска текста в дереве и получить идентификаторы узлов для соответствующих узлов. Существует реквизит, называемый expanded, который позволяет нам устанавливать список идентификаторов узлов, которые необходимо расширить. См. Приведенный ниже код.

import React from 'react';
import PropTypes from 'prop-types';
import { fade, makeStyles } from '@material-ui/core/styles';
import TreeView from '@material-ui/lab/TreeView';
import TreeItem from '@material-ui/lab/TreeItem';
import Typography from '@material-ui/core/Typography';
import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown';
import ArrowRightIcon from '@material-ui/icons/ArrowRight';
import TextField from '@material-ui/core/TextField';

const useTreeItemStyles = makeStyles(theme => ({
  root: {
    color: theme.palette.text.secondary,
    '&:focus > $content': {
      backgroundColor: `var(--tree-view-bg-color, ${theme.palette.grey[400]})`,
      color: 'var(--tree-view-color)',
    },
  },
  content: {
    color: theme.palette.text.secondary,
    paddingRight: theme.spacing(1),
    fontWeight: theme.typography.fontWeightMedium,
    '$expanded > &': {
      fontWeight: theme.typography.fontWeightRegular,
    },
  },
  group: {
    marginLeft: 12,
    borderLeft: `1px dashed ${fade(theme.palette.text.primary, 0.4)}`,
  },
  expanded: {},
  label: {
    fontWeight: 'inherit',
    color: 'inherit',
    width: 'auto'
  },
  labelRoot: {
    display: 'flex',
    alignItems: 'center',
    padding: theme.spacing(0.5, 0),
  },
  labelIcon: {
    marginRight: theme.spacing(1),
  },
  labelText: {
    fontWeight: 'inherit',
    flexGrow: 1,
  },
}));

const useStyles = makeStyles({
  root: {
    height: 264,
    flexGrow: 1,
    maxWidth: 400,
  },
});

const data = [
  {
    name: 'world',
    id: 'world',
    children: [
      {
        name: 'asia',
        id: 'asia',
        children: [
          {
            name: 'india',
            id: 'india',
            children: [
              {
                name: 'tamilnadu',
                id: 'tamilnadu',
                children: [
                  {
                    name: 'chennai',
                    id: 'chennai',
                    children: [
                      {
                        name: 'thiruvanmiyur',
                        id: 'thiruvanmiyur'
                      },
                      {
                        name: 'kelambakkam',
                        id: 'kelambakkam'
                      }
                    ]
                  },
                  {
                    name: 'madurai',
                    id: 'madurai',
                    children: [
                      {
                        name: 'mattuthavani',
                        id: 'mattuthavani'
                      }
                    ]
                  }
                ]
              },
              {
                name: 'andhrapradesh',
                id: 'andhrapradesh',
                children: [
                  {
                    name: 'vijayawada',
                    id: 'vijayawada',
                    children: [
                      {
                        name: 'satyanarayanapuram',
                        id: 'satyanarayanapuram'
                      }
                    ]
                  }
                ]
              },
              {
                name: 'telangana',
                id: 'telangana',
                children: [
                  {
                    name: 'hyderabad',
                    id: 'hyderabad',
                    children: [
                      {
                        name: 'dilsukhnagar',
                        id: 'dilsukhnagar'
                      }
                    ]
                  }
                ]
              }
            ]
          },
          {
            name: 'china',
            id: 'china',
            children: [
              {
                name: 'hubei',
                id: 'hubei',
                children: [
                  {
                    name: 'wuhan',
                    id: 'wuhan'
                  }
                ]
              }
            ]
          },
          {
            name: 'japan',
            id: 'japan',
            children: [
              {
                name: 'place honshu',
                id: 'honshu',
                children: [
                  {
                    name: 'tokyo',
                    id: 'tokyo'
                  }
                ]
              }
            ]
          }
        ]
      },
      {
        name: 'north america',
        id: 'northamerica',
        children: [
          {
            name: 'usa',
            id: 'usa',
            children: [
              {
                name: 'place california',
                id: 'california',
                children: [
                  {
                    name: 'losangeles',
                    id: 'losangeles',
                    children: [
                      {
                        name: 'hollywood',
                        id: 'hollywood'
                      }
                    ]
                  },
                  {
                    name: 'sanfrancisco',
                    id: 'sanfrancisco',
                    children: [
                      {
                        name: 'goldengate',
                        id: 'goldengate'
                      }
                    ]
                  }
                ]
              },
              {
                name: 'florida',
                id: 'florida',
                children: [
                  {
                    name: 'miami',
                    id: 'miami',
                    children: [
                      {
                        name: 'place Vizcaya',
                        id: 'Vizcaya'
                      }
                    ]
                  }
                ]
              }
            ]
          }
        ]
      }
    ]
  }
]

function StyledTreeItem(props) {
  const { labelText, ...other } = props;
  const classes = useTreeItemStyles();
  return (
    <TreeItem
      label={
        <div className={classes.labelRoot}>
          <Typography variant="body2" className={classes.labelText}>
            {labelText}
          </Typography>
        </div>
      }
      classes={{
        root: classes.root,
        content: classes.content,
        expanded: classes.expanded,
        group: classes.group,
        label: classes.label
      }}
      {...other}
    />
  );
}

StyledTreeItem.propTypes = {
  bgColor: PropTypes.string,
  color: PropTypes.string,
  labelIcon: PropTypes.elementType,
  labelInfo: PropTypes.string,
  labelText: PropTypes.string.isRequired,
};

const filterFunc = (value, searchTerm) => value.toLowerCase().includes(searchTerm);

export default function PlaceTreeView() {
  const classes = useStyles();
  const [searchTerm, setSearchTerm] = React.useState('');
  const [expandNodes, setExpandNodes] = React.useState([]);
  const [options, setOptions] = React.useState(data);
  const handleSearchTermChange = event => {
    setSearchTerm(event.target.value);
    searchTree(event.target.value);
  }

  const getTreeItemsFromData = treeItems => {
    return treeItems.map(treeItemData => {
      let children = undefined;
      if (treeItemData.children && treeItemData.children.length > 0) {
        children = getTreeItemsFromData(treeItemData.children);
      }
      return (
        <StyledTreeItem
          key={treeItemData.id}
          nodeId={treeItemData.id}
          labelText={treeItemData.name}
          children={children}
          highlight={filterFunc(treeItemData.name, searchTerm)}
        />
      );
    });
  };

  const searchTree = searchTerm => {
    searchTerm = searchTerm.toLowerCase().trim();
    if(searchTerm === '') {
      return data;
    }
    let nodesToExpand = [];
    function dig(list) {
      return list.map(treeNode => {
        const { children } = treeNode;
        const match = filterFunc(treeNode.name, searchTerm);
        const childList = dig(children || [], match);
        if(match || childList.length) {
          nodesToExpand.push(treeNode.id);
          return {
            ...treeNode,
            children: childList
          };
        }
        return null;
      })
      .filter(node => node);
    }
    setExpandNodes(nodesToExpand);
    setOptions(dig(data));
  }

  let treeViewProps = {};
  if(searchTerm.trim() !== '') {
    treeViewProps = { expanded: expandNodes }
  }

  console.log('treeviewprops', treeViewProps);
  return (
      <div style={{margin: '20px', display: 'flex', flexDirection: 'column'}}>
        <TextField style={{width: '200px', marginBottom: '10px'}} id="standard-basic" label="Search place" onChange={handleSearchTermChange} />
        <TreeView
          className={classes.root}
          defaultCollapseIcon={<ArrowDropDownIcon />}
          defaultExpandIcon={<ArrowRightIcon />}
          // expanded={expandNodes}
          {...treeViewProps}
          defaultEndIcon={<div style={{ width: 24 }} />}
        >
          {getTreeItemsFromData(options)}
        </TreeView>
      </div>
    );
  }

Если я контролирую компонент TreeView и устанавливаю expanded=[] в качестве исходного состояния. Тогда он не позволяет пользователям расширяться вручную в начале, когда в текстовом поле нет текста. Если я установлю expanded=[list of all nodes in tree] в качестве начального состояния, то он покажет все узлы, развернутые по умолчанию. Но я этого не хочу. Я хочу, чтобы он был первоначально свернут до root, а затем позволил пользователю вручную расширить узлы. Поэтому я попытался сделать условную опору expanded. Но затем я получаю эту ошибку

Material-UI: A component is changing an uncontrolled TreeView to be controlled. Elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled TreeView element for the lifetime of the component.

Что я могу сделать, чтобы избежать этой ошибки и установить условие условно? Или есть какой-то другой способ добиться того, чего я хочу достичь?

1 Ответ

1 голос
/ 04 февраля 2020

Возможно, вы не установили обратный вызов, когда пытаетесь управлять им. (То же, что и value и onChange) в TextField * Документ API TreeView здесь
Вы можете найти обратный вызов onNodeToggle
Установить, что это решит эту проблему.

...