Code Monkey home page Code Monkey logo

react-folder-tree's Introduction

React Folder Tree

travis build codecov npm bundle size
npm npm GitHub top language

A versatile and customizable react treeview library. Features:
✅ custom icons
✅ custom event handlers
✅ inline add, modify, and delete tree nodes
✅ checkbox with half check (indeterminate check)
✅ read-only mode

It uses use-tree-state hook internally for convenient state management.

Quick Preview

folder-tree-demo

Live Demos

live demos and code examples can be found here


Basic Usage

🌀 install

$ yarn add react-folder-tree
$ npm install react-folder-tree --save

🌀 basic tree

import FolderTree, { testData } from 'react-folder-tree';
import 'react-folder-tree/dist/style.css';

const BasicTree = () => {
  const onTreeStateChange = (state, event) => console.log(state, event);

  return (
    <FolderTree
      data={ testData }
      onChange={ onTreeStateChange }
    />
  );
};

🌀 custom initial state

Initial tree state is an object that describes a nested tree node structure, which looks like:

{
  // reserved keys, can customize initial value
  name: 'root node',  
  checked (optional): 0 (unchecked, default) | 0.5 (half checked) | 1(checked),
  isOpen (optional): true (default) | false,
  children (optional): [array of treenode],

  // internal keys (auto generated), plz don't include them in the initial data
  path: [],    // path is an array of indexes to this node from root node
  _id: 0,

  // not reserved, can carry any extra info about this node
  nickname (optional): 'pikachu',
  url (optional): 'url of this node',
}

checked and isOpen status could be auto initialized by props initCheckedStatus and initOpenStatus. We can also provide data with custom checked and isOpen status, and set initCheckedStatus and initOpenStatus to 'custom'.

This example shows how to render a tree with custom initial state

const treeState = {
  name: 'root [half checked and opened]',
  checked: 0.5,   // half check: some children are checked
  isOpen: true,   // this folder is opened, we can see it's children
  children: [
    { name: 'children 1 [not checked]', checked: 0 },
    {
      name: 'children 2 [half checked and not opened]',
      checked: 0.5,
      isOpen: false,
      children: [
        { name: 'children 2-1 [not checked]', checked: 0 },
        { name: 'children 2-2 [checked]', checked: 1 },
      ],
    },
  ],
};

const CustomInitState = () => (
  <FolderTree
    data={ treeState }
    initCheckedStatus='custom'  // default: 0 [unchecked]
    initOpenStatus='custom'     // default: 'open'
  />
);

🌀 hate checkbox?

<FolderTree
  data={ treeState }
  showCheckbox={ false }    // default: true
/>

🌀 love indentation?

<FolderTree
  data={ treeState }
  indentPixels={ 99999 }    // default: 30
/>

🌀 read only?

we can use it as a classical view-only tree

<FolderTree
  data={ treeState }
  showCheckbox={ false }    // hiding checkbox is not required but recommended for better UX
  readOnly                  // <== here!!
/>

Advanced Usage

🔥 sync tree state

In order to perform more complex operations, we can sync and keep track of the current tree state outside.

This example shows how to download all selected files.

const SuperApp = () => {
  const [treeState, setTreeState] = useState(initState);
  const onTreeStateChange = (state, event) => setTreeState(state);

  const onDownload = () => downloadAllSelected(treeState);

  return (
    <>
      <FolderTree
        data={ initState }
        onChange={ onTreeStateChange }
      />
      <DownloadButton onClick={ onDownload } />
    </>
  );
};

🔥 custom icons

There are 9 icons and all of them are customizable.

  • FileIcon
  • FolderIcon
  • FolderOpenIcon
  • EditIcon
  • DeleteIcon
  • CancelIcon
  • CaretRightIcon
  • CaretDownIcon
  • OKIcon

This example shows how to customize the FileIcon (same interface for all other icons).

import { FaBitcoin } from 'react-icons/fa';

const BitcoinApp = () => {
  const FileIcon = ({ onClick: defaultOnClick, nodeData }) => {
    const {
      path,
      name,
      checked,
      isOpen,
      ...restData
    } = nodeData;

    // custom event handler
    const handleClick = () => {   
      doSthBad({ path, name, checked, isOpen, ...restData });

      defaultOnClick();
    };

    // custom Style
    return <FaBitcoin onClick={ handleClick } />;
  };

  return (
    <FolderTree
      data={ initState }
      iconComponents={{
        FileIcon,
        /* other custom icons ... */
      }}
    />
  );
};

🔥 hide icons / disable interaction

This usage is a subset of custom icons.

For example, if we want to disable editing, we can hide EditIcon by passing in a dummy custom icon, so nothing will be rendered.

const EditIcon = (...args) => null;

A little more complex but more flexible way is to have extra node data, say editable, then build a custom icon that utilize this data

const EditIcon = ({ onClick: defaultOnClick, nodeData }) => {
  const { editable } = nodeData;

  // if this node is editable, render an EditIcon, otherwise render air
  return editable ? (<FaEdit onClick={ defaultOnClick } />) : null;

  // or render a 'not editable' icon
  return editable ? (<FaEdit onClick={ defaultOnClick } />) : (<DontEdit />));
};

🔥 custom onClick for node names

This example shows how to download the file when click on the node name.

const dataWithUrl = {
  name: 'secret crypto file',
  url: 'polkadot.network',      // wew can provide any custom data to the FolderTree!
  // ...
};

const onNameClick = ({ defaultOnClick, nodeData }) => {
  defaultOnClick();

  const {
    // internal data
    path, name, checked, isOpen, 
    // custom data
    url, ...whateverRest
  } = nodeData;

  download(url);
};

const Downloader = () => (
  <FolderTree
    data={ dataWithUrl }
    onNameClick={ onNameClick }
  />
);

APIs Summary

prop description type options
data initial tree state data (required) object N/A
initCheckedStatus initial check status of all nodes string 'unchecked' (default) | 'checked' | 'custom'
initOpenStatus initial open status of all treenodes string 'open' (default) | 'closed' | 'custom'
iconComponents custom icon components object ant design icons (default)
onChange callback when tree state changes function console.log (default)
onNameClick callback when click treenode name function open treenode inline toolbox (default)
indentPixels ident pixels of 1x level treenode number 30 (default)
showCheckbox show check box? bool true (default)
readOnly readOnly mode? can't click/check node bool false (default)

Bugs? Questions? Contributions?

Feel free to open an issue, or create a pull request!

react-folder-tree's People

Contributors

ianstorm avatar mboperator avatar shunjizhan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

react-folder-tree's Issues

On hover show the tool box

Feature request:

On hover show the inline tool box. Currently the tool box shows only when we click the node item.

Also press enter to add a name and rename the node item.

ReferenceError: self is not defined

Thanks for putting this package together; it's exactly what I need!

I have it loaded in and implemented so it 'works', however anytime I refresh my browser or navigate to the page I'm using the component on it throws me the following error:

ReferenceError: self is not defined
    at Object.<anonymous> (/Users/anthonyzupancic/Documents/GIT/git-connect/node_modules/react-folder-tree/dist/react-folder-tree.bundle.js:1:248)
    at Module._compile (node:internal/modules/cjs/loader:1097:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1151:10)
    at Module.load (node:internal/modules/cjs/loader:975:32)
    at Function.Module._load (node:internal/modules/cjs/loader:822:12)
    at Module.require (node:internal/modules/cjs/loader:999:19)
    at require (node:internal/modules/cjs/helpers:102:18)
    at Object.react-folder-tree (/Users/anthonyzupancic/Documents/GIT/git-connect/.next/server/pages/sync/create.js:955:18)
    at __webpack_require__ (/Users/anthonyzupancic/Documents/GIT/git-connect/.next/server/webpack-runtime.js:33:42)
    at eval (webpack-internal:///./src/components/forms/asset_navigation/FolderTree.tsx:7:75) {
  page: '/sync/create'
}

If I remove the component > reload my localhost > add the component/save => it works
Then I refresh my browser or navigate around and the error is thrown.

delete root folder

First of all, thanks a lot for this great job !!!

Is it possible to remove the root folder?

Or, in other words, can we define multiple root-level folders?

Thank you.

onNameClick nodeData not fetched

in tsx file, when i'm calling the function
const onNameClick = ({ defaultOnClick, nodeData }) => { console.log(nodeData)}

console is showing " undefined ".
pls tell me, how to solve this.

Add Custom new file icon and new folder icon

I would like to customize the new file icon and the folder icon, so that when I press them I can show my dialog window, instead than creating the default file named "new file" and the default folder.
Also, it would be great if the event state contains the path of the new file/folder created.
Is that possible?

Usage with remix

For any googlers who would like to use this project with remix:

import { ClientOnly } from 'remix-utils';
import type { Dispatch, ComponentType, LazyExoticComponent } from 'react';
import type { FolderTreeProps } from 'react-folder-tree';

const Tree: LazyExoticComponent<ComponentType<FolderTreeProps>> = lazy(
  async () => {
    const component = await import('react-folder-tree');
    return component.default as any;
  }
);

export default function Page() {
    return (
      <ClientOnly fallback={'Loading...'}>
        {() => <Tree data={{ name: 'First' }} showCheckbox={false} />}
      </ClientOnly>
    )
}

Importing testData will not work. Your best is directly copying from https://github.com/shunjizhan/react-folder-tree/blob/master/src/utils/testData.js.

Thanks for the awesome project @shunjizhan!

Delete a "branch"

Hi,

When I perform tasks on any branch I can perfectly capture which of them I am referring to, with the "onChange".
But if I try to delete a "branch" (for now I have only tried it in folders) when accessing the "state" through the path of the "event" (these are the parameters that onChange returns and that I used in the other tasks ) the branch in question is gone so I can't use its information.

Is there a way to capture the data (path, "_id",... whatever) so I can tell which branch I was trying to delete before it disappears?

I know you already have something implemented in the line...

 handleCheck,
 handleRename,
 handleDelete,
 handleAddNode,
 handleToggleOpen,

but I have tried to use it by adding the following to the call to the component....

<FolderTree
handleDelete={() => {console.log("handleDelete!!!!")}}
...
/>

But it's not working,

can i use the handleDelete?
How ?

Thank you,

Ernesto

sort tree in ascending order

FR:
add function to show tree in ascending/descending order.
i have two json-data, wanted to compare and see different in different color like vs code explorer tree, deleted file in red, newly added file in green
and modified file contain both data and show in yellow color.

Screenshot 2022-06-19 200241

thanks

How can I add new custom params when I add a file or folder.

  • Is there a prop to access new file addition object when we add a file or folder?
  • I am not able customise initial state folder icons, where as initial state files can be customised.
  • How can I add new custom params when I add a file or folder which are present in other objects in initial tree state.
  • Is there a way to toggle the options when we click on each node, than opening for all the nodes?
  • When we create new file under a folder, paths are not updated. Hence paths of new file created and existing first file of the folder remains the same.(duplicate paths). Ideally new path has to be created for newly added file

Specify css for each TreeNode of each folder tree

I want to apply different css to each tree node with the property values ​​of the json data that goes into the data property.
(Different colors for each folder node)
Is there any way to implement this functionality?

Option to prevent allowing edits (read-only mode)

We can use onNameClick to prevent showing the edit UI when the name is clicked, but if they click the icon they'll see it anyways. We can then override the entire icon, but it would be nice to either simply have a prop that doesn't allow any editing, or also allow an onIconClick prop to override the onClick for icons without making an entire icon to do so.

custom initOpenStatus throws error..

when I set

initOpenStatus='custom' and set the corresponding isOpen property to true or false, I get the following error in the console:

custom open status is invalid! Fell back to all opened.

anything else I need to do to get this working? (using version 5.0.3)

thx - Ole

Update tree data externally?

What is the best way to update the data held in the tree "externally"? I have my data in state and when I setState on that my tree just goes blank. It works for the initial state. Is there a way to call the addNode and deleteNode? Thanks for any help.

Popup onNameclick

How I can implement pop up window to show file name on onNameclick/ hover
I have tried react -popup library but failed.
Const onNameclick=(nodeData) =>{ <> <Popup triggered={nodeData.name} position="right-centre"> <span>{nodeData.name}</span> </Popup> </> } ... <FolderTree data={testData} onNameclick={onNameclick}/> ....

border of name edition

when we are editing the name a black border with 2px appears, I try to edit the css through the detected structure...

`.editableNameContainer {

.EditableName {
	.editingName {
		input {
			&:focus {
				}
			}
		}
	}
}`

but I can't change it

someone knows ?

Thank you,

Ernesto

Add icon

How to add more icons to file other than rename, delete.
I wanted to add a custom icon to the file right side (TreeNodeToolBar) for more options.

Could not resolve dependency: React 18

Could not resolve dependency: React 18

npm ERR! Could not resolve dependency:
npm ERR! peer react@"^16.8.0 || ^17" from react-folder-tree@5.0.3
npm ERR! node_modules/react-folder-tree
npm ERR!   react-folder-tree@"*" from the root project

BUG: Component brokes if a data file has a key named `path`

Component brokes if a data file has a key named path

 {
        "name": "Проверка",
        "title": "Проверка",
        "path": "c:/PROJECTS/FileDATA/FileDATA/data",
        "fullname": "c:/PROJECTS/FileDATA/FileDATA/data/Проверка",
        "isDirectory": true,
        "children": [
            {
                "name": "159.pdf",
                "title": "159",
                "path": "c:/PROJECTS/FileDATA/FileDATA/data/Проверка",
                "fullname": "c:/PROJECTS/FileDATA/FileDATA/data/Проверка/159.pdf",
                "isDirectory": false
            },

Trigger caret click on name click

for folders, it is common to allow for both the label click and the caret click to expand / contract the folder contents. Is there a way to do this?

Library not work on NextJs

As I inspected, it is caused by:

  • Using style-loader in webpack config to include scss file, causing 'document is not defined' error
    localhost_3000_read2

  • The global object using self so it can not be used on Node.js, causing 'self is not defined' error

So to fix these in my opinion, we will have to change the webpack config and also usage of this library:

ReferenceError: self is not defined

I am trying to use it in Next.js project and i am getting this error:

Server Error
ReferenceError: self is not defined

This error happened while generating the page. Any console logs will be displayed in the terminal window.
Source
.next\server\pages\page2.js (1:0) @ Object.react-folder-tree

1 | module.exports = require("react-folder-tree");;
Call Stack
webpack_require
.next\server\webpack-runtime.js (21:0)

Per tree item events

Hey, thanks for cool component!

Is there a way to attach some new logic to single elements when mouse events occur?
For example, I would like to add a tooltip with different contents on top of items when I hover each.

Mirroring the events on to another folder tree

Hi,

I am trying to create a folder tree diff view. I want my users to navigate one tree and if path is found in another tree open it programmatically.

So we have foldertree1 and foldertree2, when a user click the arrow to open the tree, foldertree2 will be alos open the same path. In the screenshot, when I click green , I want to programmatically open 2nd tree (indicated by red arrow).

Screenshot 2021-12-13 at 2 26 23 PM

Please advice.

edit name directly

On the other hand, would it be possible that when I click on the edit name icon, the focus is already placed at the end of said name and allows me to write directly?

custom css for selected file, folder

I'd like to highlight the file name being edited currently.

I tried the below css but it didn't work. Could you please guide me on how to add my own css styles.

.TreeNodde .TreeNodeToolBar {
  background-color: gray;
}

.TreeNodde .editingName {
  background-color: blue;
}

Expand docs for reserved keys and internal data structure

Hi!
I noticed that the docs mention the reserved keys that you can use to define the nodes. But I think it is missing information on what custom keys you cannot use.
I noticed this because I had a custom key called path, but it killed the file tree (rendering was all messy) and threw a bunch of Uncaught TypeError: Cannot read property 'children' of undefined.
Then I realized that both keys path and _id are used internally.
I'm suggesting you add that to the readme as well, could help avoid confusion.

Great work on the library though. Thanks!

Use custom icons per file/folder

Is there a way to use custom icons per file folder preferably defining them within the initData itself, like this:-

const initData = {
name: "node",
isOpen: true,
children: [],
icon: SomeComponent
}

Not able to maintain "isOpen" status.

The question is simple. I am trying to set a dictionary with existing "isOpen" keys, so that I can control which nodes are open/closed.

This is the basic example where I set a dict with "isOpen" keys to false, however, whenever I refresh it the tree renders all nodes expanded.

import FolderTree from "react-folder-tree";
import my-data from "./data-test.json";
import React, { useState} from "react";

const BasicTree = () => {
  const [treeState, setTreeState] = useState(my-data);
  console.log(treeState);
  return (
    <>
      <FolderTree data={treeState} />
    </>
  );
};

export default BasicTree;

This comes from a more complex setup, where I receive a JSON from an api and set the new directoryTree using useState. The problem is that the FolderTree does not preserve the open/close status from the previous render, so now I am reading the previous status and manually setting the "isOpen" attributes to maintain that state. However, it still does not work. It renders the whole tree open. Minimal example:

import React, { useState, useEffect, useRef } from "react";
import readValue from "./api";
import FolderTree from "react-folder-tree";

const MyDirectory = () => {
  const { dirTree, setDirTree } = useState();
 
  const onValueReceived = (value) => {
    console.log("Value recived: " + JSON.stringify(value));
    setDirTree(value);
  };

  const onTreeStateChange = (state, event) => {};

  const onNameClick = ({ defaultOnClick, nodeData }) => {
    defaultOnClick();
    const {
      path,
      name,
      checked,
      isOpen,
      // custom data
    } = nodeData;

   
    readValue(name)
      .then((value) => onValueReceived(value))
      .catch((e) => {console.log(e)});
  };


  return( {
    dirTree ? (
      <FolderTree
        data={dirTree}
        onNameClick={onNameClick}
        onChange={onTreeStateChange}
      />
    ) : (
      <></>
    );});

export default MyDirectory;

My question is:
How can I preserve the open/closed state of the nodes when updating the tree state?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.