diff --git a/.nvmrc b/.nvmrc
new file mode 100644
index 00000000..b6a7d89c
--- /dev/null
+++ b/.nvmrc
@@ -0,0 +1 @@
+16
diff --git a/config/webpack.config.js b/config/webpack.config.js
index b3851869..eba0ca92 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -20,6 +20,10 @@ module.exports = {
resolve: {
// our code can resolve 'xxx' instead of writing 'xxx.jsx'
extensions: ['*', '.js', '.jsx'],
+ fallback: {
+ 'react/jsx-runtime': 'react/jsx-runtime.js',
+ 'react/jsx-dev-runtime': 'react/jsx-dev-runtime.js',
+ },
},
module: {
// For every file that match regex in 'test', webpack pipes the code through to loaders
diff --git a/package.json b/package.json
index 3eba59b3..f3b7aaab 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "script-custom-react-folder-tree",
- "version": "5.0.11",
+ "version": "5.1.0",
"description": "customizable react folder tree library",
"main": "dist/react-folder-tree.bundle.js",
"typings": "index.d.ts",
@@ -43,7 +43,7 @@
"license": "MIT",
"private": false,
"scripts": {
- "prebuild": "del \"dist/\"",
+ "prebuild": "rimraf dist",
"build": "webpack --config config/webpack.prod.config.js",
"start": "cross-env NODE_ENV=development node scripts/dev-server.js",
"lint": "eslint src/ --ext .js,.jsx",
@@ -99,10 +99,14 @@
},
"peerDependencies": {
"react": "^16.8.0 || ^17",
+ "react-dnd": "^16.0.1",
+ "react-dnd-html5-backend": "^16.0.1",
"react-dom": "^16.8.0 || ^17"
},
"dependencies": {
"prop-types": "^15.7.2",
+ "react-dnd": "^16.0.1",
+ "react-dnd-html5-backend": "^16.0.1",
"react-icons": "^4.1.0",
"use-tree-state": "1.0.0"
}
diff --git a/src/components/FolderTree/FolderTree.jsx b/src/components/FolderTree/FolderTree.jsx
index ceb986a4..c868da00 100644
--- a/src/components/FolderTree/FolderTree.jsx
+++ b/src/components/FolderTree/FolderTree.jsx
@@ -1,4 +1,6 @@
import React from 'react';
+import { DndProvider } from 'react-dnd';
+import { HTML5Backend } from 'react-dnd-html5-backend';
import PropTypes from 'prop-types';
import useTreeState, {
testData,
@@ -28,6 +30,10 @@ const FolderTree = ({
debug = false,
searchData = null,
showSearchData = false,
+ dndConfig = {
+ onDrop: null,
+ backend: null,
+ },
}) => {
const options = {
initCheckedStatus,
@@ -56,6 +62,7 @@ const FolderTree = ({
debug,
searchData,
showSearchData,
+ dndConfig,
};
/* ----------
@@ -69,9 +76,11 @@ const FolderTree = ({
return (
-
-
-
+
+
+
+
+
);
};
@@ -104,6 +113,10 @@ FolderTree.propTypes = {
debug: PropTypes.bool,
searchData: PropTypes.object,
showSearchData: PropTypes.bool,
+ dndConfig: PropTypes.shape({
+ backend: PropTypes.func,
+ onDrop: PropTypes.func,
+ }),
};
export {
diff --git a/src/components/SandBox/SandBox.jsx b/src/components/SandBox/SandBox.jsx
index 4b8909e3..ea8da21c 100644
--- a/src/components/SandBox/SandBox.jsx
+++ b/src/components/SandBox/SandBox.jsx
@@ -1,4 +1,5 @@
import React, { useEffect, useState } from 'react';
+import PropTypes from 'prop-types';
import FolderTree, { testData } from '../FolderTree/FolderTree';
const makeId = (length) => {
@@ -63,7 +64,7 @@ const parseTestData = (node) => {
};
/* eslint-disable */
-const SandBox = () => {
+const SandBox = ({ dndConfig }) => {
const onTreeStateChange = (state, e) => console.log({ state, e });
const [tree, setTree] = useState(null);
@@ -79,6 +80,7 @@ const SandBox = () => {
!!tree && (
{
@@ -110,4 +112,11 @@ const SandBox = () => {
);
};
+SandBox.propTypes = {
+ dndConfig: PropTypes.shape({
+ backend: PropTypes.func,
+ onDrop: PropTypes.func,
+ }),
+};
+
export default SandBox;
diff --git a/src/components/TreeNode/TreeNode.jsx b/src/components/TreeNode/TreeNode.jsx
index ba9e20f7..150fd047 100644
--- a/src/components/TreeNode/TreeNode.jsx
+++ b/src/components/TreeNode/TreeNode.jsx
@@ -2,6 +2,8 @@ import React, {
useContext,
useState,
} from 'react';
+import { useDrag, useDrop } from 'react-dnd';
+import { NativeTypes } from 'react-dnd-html5-backend';
import PropTypes from 'prop-types';
import {
AiFillCaretRight,
@@ -25,6 +27,7 @@ import {
iconClassName,
getDefaultIcon,
} from '../../utils/iconUtils';
+import { FileTreeDragTypes } from '../../utils/dnd';
const TreeNode = ({
path,
@@ -51,6 +54,7 @@ const TreeNode = ({
onIconClick,
showCheckbox,
readOnly,
+ dndConfig,
searchData,
showSearchData,
@@ -58,6 +62,18 @@ const TreeNode = ({
debug,
} = useContext(ConfigContext);
+ const { onDrop } = dndConfig || {};
+
+ const [_, drop] = useDrop(() => ({
+ accept: [FileTreeDragTypes.TREE_NODE, NativeTypes.FILE],
+ drop: item => {
+ onDrop?.(item);
+ },
+ collect: monitor => ({
+ isOver: monitor.isOver(),
+ }),
+ }), [onDrop]);
+
const isFolder = !!children;
let matchCount = 0;
@@ -80,6 +96,14 @@ const TreeNode = ({
...restData,
};
+ const [__, drag] = useDrag({
+ type: FileTreeDragTypes.TREE_NODE,
+ item: nodeData,
+ collect: monitor => ({
+ isDragging: monitor.isDragging(),
+ }),
+ });
+
const level = path.length;
const offsetSize = !isFolder ? 0 : iconSize;
@@ -245,6 +269,7 @@ const TreeNode = ({
return (
<>
{children}
;
+
+jest.mock('react-dnd', () => ({
+ useDrag: jest.fn((...args) => [{ isDragging: false }, jest.fn()]),
+ useDrop: jest.fn((...args) => [{ isOver: false }, jest.fn()]),
+ DndProvider,
+}));
+
+jest.mock('react-dnd-html5-backend', () => ({
+ HTML5Backend: jest.fn(),
+}));
diff --git a/src/utils/dnd.js b/src/utils/dnd.js
new file mode 100644
index 00000000..71f4d51b
--- /dev/null
+++ b/src/utils/dnd.js
@@ -0,0 +1,7 @@
+import { HTML5Backend } from 'react-dnd-html5-backend';
+
+export const AppDndBackend = HTML5Backend;
+
+export const FileTreeDragTypes = {
+ TREE_NODE: 'TREE_NODE',
+};
diff --git a/yarn.lock b/yarn.lock
index 5cd87231..9c0a8d4c 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -910,6 +910,13 @@
dependencies:
regenerator-runtime "^0.13.4"
+"@babel/runtime@^7.9.2":
+ version "7.22.5"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.5.tgz#8564dd588182ce0047d55d7a75e93921107b57ec"
+ integrity sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA==
+ dependencies:
+ regenerator-runtime "^0.13.11"
+
"@babel/template@^7.14.5", "@babel/template@^7.3.3":
version "7.14.5"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.14.5.tgz#a9bc9d8b33354ff6e55a9c60d1109200a68974f4"
@@ -1152,6 +1159,21 @@
resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.15.tgz#6a9d143f7f4f49db2d782f9e1c8839a29b43ae23"
integrity sha512-15spi3V28QdevleWBNXE4pIls3nFZmBbUGrW9IVPwiQczuSb9n76TCB4bsk8TSel+I1OkHEdPhu5QKMfY6rQHA==
+"@react-dnd/asap@^5.0.1":
+ version "5.0.2"
+ resolved "https://registry.yarnpkg.com/@react-dnd/asap/-/asap-5.0.2.tgz#1f81f124c1cd6f39511c11a881cfb0f715343488"
+ integrity sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==
+
+"@react-dnd/invariant@^4.0.1":
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/@react-dnd/invariant/-/invariant-4.0.2.tgz#b92edffca10a26466643349fac7cdfb8799769df"
+ integrity sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==
+
+"@react-dnd/shallowequal@^4.0.1":
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz#d1b4befa423f692fa4abf1c79209702e7d8ae4b4"
+ integrity sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==
+
"@sinonjs/commons@^1.7.0":
version "1.8.3"
resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d"
@@ -2901,6 +2923,15 @@ discontinuous-range@1.0.0:
resolved "https://registry.yarnpkg.com/discontinuous-range/-/discontinuous-range-1.0.0.tgz#e38331f0844bba49b9a9cb71c771585aab1bc65a"
integrity sha1-44Mx8IRLukm5qctxx3FYWqsbxlo=
+dnd-core@^16.0.1:
+ version "16.0.1"
+ resolved "https://registry.yarnpkg.com/dnd-core/-/dnd-core-16.0.1.tgz#a1c213ed08961f6bd1959a28bb76f1a868360d19"
+ integrity sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==
+ dependencies:
+ "@react-dnd/asap" "^5.0.1"
+ "@react-dnd/invariant" "^4.0.1"
+ redux "^4.2.0"
+
dns-equal@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d"
@@ -3624,7 +3655,7 @@ extsprintf@^1.2.0:
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
-fast-deep-equal@^3.1.1:
+fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
@@ -4140,6 +4171,13 @@ he@^1.2.0:
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
+hoist-non-react-statics@^3.3.2:
+ version "3.3.2"
+ resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
+ integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
+ dependencies:
+ react-is "^16.7.0"
+
hosted-git-info@^2.1.4:
version "2.8.9"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
@@ -6802,6 +6840,24 @@ raw-body@2.4.0:
iconv-lite "0.4.24"
unpipe "1.0.0"
+react-dnd-html5-backend@^16.0.1:
+ version "16.0.1"
+ resolved "https://registry.yarnpkg.com/react-dnd-html5-backend/-/react-dnd-html5-backend-16.0.1.tgz#87faef15845d512a23b3c08d29ecfd34871688b6"
+ integrity sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==
+ dependencies:
+ dnd-core "^16.0.1"
+
+react-dnd@^16.0.1:
+ version "16.0.1"
+ resolved "https://registry.yarnpkg.com/react-dnd/-/react-dnd-16.0.1.tgz#2442a3ec67892c60d40a1559eef45498ba26fa37"
+ integrity sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==
+ dependencies:
+ "@react-dnd/invariant" "^4.0.1"
+ "@react-dnd/shallowequal" "^4.0.1"
+ dnd-core "^16.0.1"
+ fast-deep-equal "^3.1.3"
+ hoist-non-react-statics "^3.3.2"
+
"react-dom@^16.8.0 || ^17":
version "17.0.2"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23"
@@ -6821,7 +6877,7 @@ react-icons@^4.1.0:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
-react-is@^16.13.1, react-is@^16.8.1, react-is@^16.8.6:
+react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.6:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
@@ -6961,6 +7017,13 @@ redent@^1.0.0:
indent-string "^2.1.0"
strip-indent "^1.0.1"
+redux@^4.2.0:
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/redux/-/redux-4.2.1.tgz#c08f4306826c49b5e9dc901dee0452ea8fce6197"
+ integrity sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==
+ dependencies:
+ "@babel/runtime" "^7.9.2"
+
reflect.ownkeys@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz#749aceec7f3fdf8b63f927a04809e90c5c0b3460"
@@ -6978,6 +7041,11 @@ regenerate@^1.4.0:
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a"
integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==
+regenerator-runtime@^0.13.11:
+ version "0.13.11"
+ resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9"
+ integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==
+
regenerator-runtime@^0.13.4:
version "0.13.7"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55"