ブラウザでエクスプローラーっぽくフォルダ構造を表示するツール


# はじめに

ブラウザ上でフォルダ情報を受け取って

エクスプローラーっぽくフォルダ表示したくなったので実装してみる。

(ただ、ファイル情報を階層とアイコンで表示するだけの機能。。。🤔)

# 対象

* フォルダがUploadできる端末(directory属性が有効であること)

# 今回作る成果物

こんな感じのアプリを作成する。

主な機能は

フォルダUpload

* 画面

![ブラウザでエクスプローラーっぽくフォルダ構造を表示する_01.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3777149/e915bc41-bc1b-491c-b702-0c7fe15f0bd4.jpeg)

フォルダ表示(Close)

![ブラウザでエクスプローラーっぽくフォルダ構造を表示する_02.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3777149/c3569644-d09f-d044-945c-c1b3099c9346.jpeg)

フォルダ表示(Open)

![ブラウザでエクスプローラーっぽくフォルダ構造を表示する_03.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3777149/47150aed-c367-639b-d030-08b904b3b545.jpeg)

多重フォルダ表示(Open)

![ブラウザでエクスプローラーっぽくフォルダ構造を表示する_04.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3777149/17fbdba3-8b22-3fe1-7d78-3ff666c84315.jpeg)

# プロジェクトの作成

今回はReactで実装していく。

Folderを表示するコンポーネントとFileを表示するコンポーネントを作成する。

extensionSVGMapを作ってPublic内のSVGIconと拡張子を紐づけて表示する。

(
  SVGIconは
  https://github.com/vscode-icons/vscode-icons
  を使用する。
)

```jsx:App.jsx

//略

  const MyFolder = ({ children, title }) => {
    const [isOpen, setIsOpen] = useState(false);
    return (
      <details onToggle={(e) => setIsOpen(e.target.open)}>
        <summary className='filebase'><img className='fileicon' src={isOpen ? './default_folder_opened.svg' : './default_folder.svg'} alt='folder' />{title}</summary>
        <div style={{ paddingLeft: '1em' }}>{children}</div>
      </details>
    );
  };

  const MyFile = ({ title }) => {
    const ext = title.split('.').pop();
    const extSVGname = extensionSVGMap[ext.toLowerCase()] || ext;
    const svgPath = `./file_type_${extSVGname}.svg`;
    return <div className='filebase'>  <img className='fileicon' src={svgPath} alt={ext} onError={(e) => { e.target.src = './default_file.svg'; }} />{title}</div>;
  };

//略

```


```javascript:extensionSVGMap.js
export const extensionSVGMap = {
  'jsx': 'reactjs',
  'tsx': 'reactts',
  'ts': 'typescript',
  'rs': 'rust',
  'rb': 'ruby',
  'py': 'python',
  'cs': 'csharp',
  'env': 'dotenv',
  'xlsx': 'excel',
  //...略
};

```

files が存在しない場合は、

"Folderを指定してください" というメッセージを表示する。

files が存在する場合は、

ツリーオブジェクトを再帰的に探索し、MyFile コンポーネントと MyFolder コンポーネントを使って描画する。

renderNode 関数でnode と name を引数として受け取ります。

node が文字列の場合、MyFile コンポーネントを返す。

node がオブジェクトの場合、MyFolder コンポーネントを返す。

（Object.entries でループし、renderNode 関数を再帰的に呼び出し、結果を MyFolder コンポーネントの子要素として追加する。）

```jsx:App.jsx

//略

  const renderTree = (files) => {
    if (!files) return <div>Folderを指定してください</div>;

    const tree = {};

    for (const file of files) {
      const pathParts = file.webkitRelativePath.split('/');
      let currentLevel = tree;

      for (let i = 0; i < pathParts.length - 1; i++) {
        const part = pathParts[i];
        if (!currentLevel[part]) {
          currentLevel[part] = {};
        }
        currentLevel = currentLevel[part];
      }
      currentLevel[pathParts.slice(-1)[0]] = file.name; 
    }

    const renderNode = (node, name) => {
      if (typeof node === 'string') {
        return <MyFile key={node} title={node} />;
      }
      return (
        <MyFolder key={name} title={name}>
          {Object.entries(node).map(([key, value]) => renderNode(value, key))}
        </MyFolder>
      );
    };

    return Object.entries(tree).map(([key, value]) => renderNode(value, key));
  };

//略

```

完成！


# 今回の成果物_demoURL/ソース

デモURL

https://shisojuice.github.io/FolderVisualizer/

ソース

https://github.com/shisojuice/FolderVisualizer


# まとめ
今回はブラウザでエクスプローラーっぽいフォルダ構造を表示してみたが、

ブラウザ上で、視覚情報としてフォルダ表示フォルダ展開したいときなどには役に立ちそう。💪

Uploadでdirectory属性を使うことで

フォルダー単位で扱えることを知らなかったので、良い経験になった。😊