본문 바로가기
개발/React

React Material - Mui 파일, 폴더 뷰 확장 / 선택하기 (Controlled Mui Tree View)

by 피로물든딸기 2023. 6. 12.
반응형

리액트 전체 링크

 

참고

- mui tree view

Mui Tree View로 파일, 폴더 뷰 만들기
Mui Tree View로 파일, 폴더 뷰 만들기 (with Node JS)

 

mui tree view의 Controlled tree view를 적용시켜보자.

 

위의 예제 코드는 다음과 같다.

import * as React from 'react';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
import TreeView from '@mui/lab/TreeView';
import TreeItem from '@mui/lab/TreeItem';

export default function ControlledTreeView() {
  const [expanded, setExpanded] = React.useState([]);
  const [selected, setSelected] = React.useState([]);

  const handleToggle = (event, nodeIds) => {
    setExpanded(nodeIds);
  };

  const handleSelect = (event, nodeIds) => {
    setSelected(nodeIds);
  };

  const handleExpandClick = () => {
    setExpanded((oldExpanded) =>
      oldExpanded.length === 0 ? ['1', '5', '6', '7'] : [],
    );
  };

  const handleSelectClick = () => {
    setSelected((oldSelected) =>
      oldSelected.length === 0 ? ['1', '2', '3', '4', '5', '6', '7', '8', '9'] : [],
    );
  };

  return (
    <Box sx={{ height: 270, flexGrow: 1, maxWidth: 400, overflowY: 'auto' }}>
      <Box sx={{ mb: 1 }}>
        <Button onClick={handleExpandClick}>
          {expanded.length === 0 ? 'Expand all' : 'Collapse all'}
        </Button>
        <Button onClick={handleSelectClick}>
          {selected.length === 0 ? 'Select all' : 'Unselect all'}
        </Button>
      </Box>
      <TreeView
        aria-label="controlled"
        defaultCollapseIcon={<ExpandMoreIcon />}
        defaultExpandIcon={<ChevronRightIcon />}
        expanded={expanded}
        selected={selected}
        onNodeToggle={handleToggle}
        onNodeSelect={handleSelect}
        multiSelect
      >
        <TreeItem nodeId="1" label="Applications">
          <TreeItem nodeId="2" label="Calendar" />
          <TreeItem nodeId="3" label="Chrome" />
          <TreeItem nodeId="4" label="Webstorm" />
        </TreeItem>
        <TreeItem nodeId="5" label="Documents">
          <TreeItem nodeId="6" label="MUI">
            <TreeItem nodeId="7" label="src">
              <TreeItem nodeId="8" label="index.js" />
              <TreeItem nodeId="9" label="tree-view.js" />
            </TreeItem>
          </TreeItem>
        </TreeItem>
      </TreeView>
    </Box>
  );
}

Mui Tree View로 파일, 폴더 뷰 만들기 (with Node JS)에서 localData에 경로가 있는 경우에 적용하였다.

import React, { useEffect, useState } from "react";

import TreeView from "@mui/lab/TreeView";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import ChevronRightIcon from "@mui/icons-material/ChevronRight";
import TreeItem from "@mui/lab/TreeItem";

let localData = {
  child: [
    {
      label: "D:",
      nodeId: 0,
      child: [
        {
          label: "github",
          nodeId: 1,
          child: [
            {
              label: "globfiles",
              nodeId: 2,
              child: [
                {
                  label: "abc1",
                  nodeId: 3,
                  child: [
                    { label: "abc1_jsonfile1.json", nodeId: 4 },
                    { label: "abc1_jsonfile2.json", nodeId: 5 },
                    { label: "abc1_textfile1.txt", nodeId: 6 },
                    { label: "abc1_textfile2.txt", nodeId: 7 },
                    {
                      label: "abc2",
                      nodeId: 8,
                      child: [
                        { label: "abc2_jsonfile.json", nodeId: 12 },
                        {
                          label: "abc3",
                          nodeId: 13,
                          child: [
                            { label: "abc3_jsonfile.json", nodeId: 14 },
                            { label: "abc3_textfile.txt", nodeId: 15 },
                          ],
                        },
                      ],
                    },
                    {
                      label: "abc2_2",
                      nodeId: 9,
                      child: [
                        { label: "abc2_2_jsonfile.json", nodeId: 10 },
                        { label: "abc2_2_textfile.txt", nodeId: 11 },
                      ],
                    },
                  ],
                },
                {
                  label: "def1",
                  nodeId: 16,
                  child: [
                    { label: "def_jsonfile1.json", nodeId: 17 },
                    { label: "def_jsonfile2.json", nodeId: 18 },
                    { label: "def_textfile1.txt", nodeId: 19 },
                    { label: "def_textfile2.txt", nodeId: 20 },
                  ],
                },
                {
                  label: "ghi1",
                  nodeId: 21,
                  child: [
                    {
                      label: "ghi2",
                      nodeId: 22,
                      child: [
                        { label: "ghi2_jsonfile1.json", nodeId: 23 },
                        { label: "ghi2_jsonfile2.json", nodeId: 24 },
                        { label: "ghi2_textfile1.txt", nodeId: 25 },
                        { label: "ghi2_textfile2.txt", nodeId: 26 },
                      ],
                    },
                  ],
                },
                { label: "jsonfile1.json", nodeId: 27 },
                { label: "jsonfile2.json", nodeId: 28 },
                { label: "textfile1.txt", nodeId: 29 },
                { label: "textfile2.txt", nodeId: 30 },
              ],
            },
          ],
        },
      ],
    },
  ],
}

const TreeViewExample2 = () => {
  const [treeInfo, setTreeInfo] = useState({});

  /*
  let tmpTreeInfo = {};
  let nodeId = 0;
  const appendChild = (arr, info) => {
    if (arr.child === undefined) arr.child = [];
    if (arr.child.findIndex((item) => item.label === info) === -1) {
      arr.child.push({ label: info, nodeId });
      nodeId++;
    }
  };

  const makeDirectories = (directories) => {
    tmpTreeInfo = {};
    for (let d of directories) {
      let split = d.split("/");
      let len = split.length;
      let current = tmpTreeInfo;

      for (let i = 0; i < len; i++) {
        appendChild(current, split[i]);
        current = current.child.find((item) => item.label === split[i]);
      }
    }

    console.log(tmpTreeInfo);
    setTreeInfo(tmpTreeInfo);
  };
  */

  const getFiles = () => {
    setTreeInfo(localData);
    return;
    // let server = `http://192.168.55.120:3002`;
    // let path = `D:\\github\\globfiles\\**`;
    // fetch(`${server}/useGlob?path=${path}`)
    //   .then((res) => res.json())
    //   .then((data) => makeDirectories(data.findPath));
  };

  const makeTreeItem = (info, parent) => {
    if (info.child === undefined) return;

    return info.child.map((item, idx) => (
      <TreeItem
        key={idx}
        nodeId={item.nodeId.toString()}
        label={item.label}
        onClick={() => console.log(`${parent}/${item.label}`)}
      >
        {makeTreeItem(item, `${parent}/${item.label}`)}
      </TreeItem>
    ));
  };

  useEffect(() => {
    getFiles();
  }, []);

  return (
    <div>
      {/* <button onClick={getFiles}>test</button> */}
      <TreeView
        aria-label="file system navigator"
        defaultCollapseIcon={<ExpandMoreIcon />}
        defaultExpandIcon={<ChevronRightIcon />}
        //sx={{ height: 240, flexGrow: 1, maxWidth: 400, overflowY: "auto" }}
      >
        {makeTreeItem(treeInfo, "")}
      </TreeView>
    </div>
  );
};

export default TreeViewExample2;

 

 

const TreeViewExample2 = () => {
  const [treeInfo, setTreeInfo] = useState({});

  const [expanded, setExpanded] = React.useState([]);
  const [selected, setSelected] = React.useState([]);

 

먼저 예시에서 주어진 메서드를 그대로 붙여넣자.

  const handleToggle = (event, nodeIds) => {
    setExpanded(nodeIds);
  };

  const handleSelect = (event, nodeIds) => {
    setSelected(nodeIds);
  };

  const handleExpandClick = () => {
    setExpanded((oldExpanded) =>
      oldExpanded.length === 0 ? ['1', '5', '6', '7'] : [],
    );
  };

  const handleSelectClick = () => {
    setSelected((oldSelected) =>
      oldSelected.length === 0 ? ['1', '2', '3', '4', '5', '6', '7', '8', '9'] : [],
    );
  };

 

버튼을 추가하고, expanded, selected, onNodeToggle, onNodeSelect를 추가한다.

import Box from '@mui/material/Box';
import Button from '@mui/material/Button';

...

  return (
    <div>
      {/* <button onClick={getFiles}>test</button> */}
      <Box sx={{ mb: 1 }}>
        <Button onClick={handleExpandClick}>
          {expanded.length === 0 ? 'Expand all' : 'Collapse all'}
        </Button>
        <Button onClick={handleSelectClick}>
          {selected.length === 0 ? 'Select all' : 'Unselect all'}
        </Button>
      </Box>
      <TreeView
        expanded={expanded}
        selected={selected}
        onNodeToggle={handleToggle}
        onNodeSelect={handleSelect}
        aria-label="file system navigator"
        defaultCollapseIcon={<ExpandMoreIcon />}
        defaultExpandIcon={<ChevronRightIcon />}
        //sx={{ height: 240, flexGrow: 1, maxWidth: 400, overflowY: "auto" }}
      >
        {makeTreeItem(treeInfo, "")}
      </TreeView>
    </div>
  );

 

예시에서 주어지는 expanded는 1, 5, 6, 7 node만 펼친다.

oldExpanded.length === 0 ? ['1', '5', '6', '7'] : [],

 

여기에서는 nodeId로 전체 노드 개수를 알 수 있다.

  let tmpTreeInfo = {};
  let nodeId = 0;
  const appendChild = (arr, info) => {
    if (arr.child === undefined) arr.child = [];
    if (arr.child.findIndex((item) => item.label === info) === -1) {
      arr.child.push({ label: info, nodeId });
      nodeId++;
    }
  };

 

그러나 실제 node와 연동하지는 않고, localData를 보고 경로를 만들고 있다.

localData에서 nodeId가 가장 큰 값은 30이다.

let localData = {
  child: [
  
  ...
  
                },
                { label: "jsonfile1.json", nodeId: 27 },
                { label: "jsonfile2.json", nodeId: 28 },
                { label: "textfile1.txt", nodeId: 29 },
                { label: "textfile2.txt", nodeId: 30 },
              ],
            },
          ],
        },
      ],
    },
  ],
};

 

따라서 handleExpandClick을 다음과 같이 수정한다. 

참고로 expanded에 들어갈 배열은 문자열이어야 한다.

  const handleExpandClick = () => {
    let fullExpanded = [];
    for(let i = 0; i <= 30; i++) fullExpanded.push(i.toString());

    setExpanded((oldExpanded) =>
      oldExpanded.length === 0 ? fullExpanded : []
    );
  };

 

이제 버튼을 누르면 정상적으로 동작하는 것을 알 수 있다.

SELECT ALL는 기존에 정의된 노드가 모두 클릭되는 것을 알 수 있다.

 

결과는 링크에서 확인하자.

반응형

댓글