├── src
├── index.css
├── index.js
├── App.test.js
├── App.css
├── logo.svg
├── registerServiceWorker.js
├── sample.data.js
└── App.js
├── public
├── favicon.ico
├── manifest.json
├── index.html
└── assets
│ └── css
│ └── style.css
├── .gitignore
├── package.json
└── README.md
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: sans-serif;
5 | }
6 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prodhan29/react-treeview-component/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | var React = require('react');
2 | var TreeViewCustomizable = require('./App');
3 |
4 | module.exports = {
5 | TreeViewCustomizable: TreeViewCustomizable
6 | }
7 |
--------------------------------------------------------------------------------
/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render( , div);
8 | });
9 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | animation: App-logo-spin infinite 20s linear;
7 | height: 80px;
8 | }
9 |
10 | .App-header {
11 | background-color: #222;
12 | height: 150px;
13 | padding: 20px;
14 | color: white;
15 | }
16 |
17 | .App-intro {
18 | font-size: large;
19 | }
20 |
21 | @keyframes App-logo-spin {
22 | from { transform: rotate(0deg); }
23 | to { transform: rotate(360deg); }
24 | }
25 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-treeview-customizable",
3 | "version": "0.1.11",
4 | "private": false,
5 | "main": "./src/index.js",
6 | "author": "Nahid hasan prodhan",
7 | "homepage": "https://prodhan29.github.io/react-treeview-component",
8 | "dependencies": {
9 | "gh-pages": "^1.0.0",
10 | "react": "^15.6.1",
11 | "react-dom": "^15.6.1"
12 | },
13 | "devDependencies": {
14 | "react-scripts": "1.0.8"
15 | },
16 | "scripts": {
17 | "predeploy": "npm run build",
18 | "deploy": "gh-pages -d build",
19 | "start": "react-scripts start",
20 | "build": "react-scripts build",
21 | "test": "react-scripts test --env=jsdom",
22 | "eject": "react-scripts eject"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React-treeview-component
2 |
3 | A react tree component where each node is consist of key-value pairs
4 |
5 | ### Installing
6 |
7 | ```
8 | npm i -S react-treeview-customizable
9 | ```
10 |
11 | ### Usage
12 |
13 | ```
14 | import React, { Component } from 'react';
15 | import Treeview from 'react-treeview-customizable';
16 |
17 | class App extends Component {
18 | render() {
19 | return (
20 |
21 |
22 |
23 | );
24 | }
25 | }
26 |
27 | export default App;
28 |
29 | ```
30 |
31 | ## Built With
32 |
33 | * react
34 | * css
35 | * html
36 |
37 | ## Authors
38 |
39 | * **Nahid hasan prodhan** [prodhan29](https://github.com/prodhan29)
40 |
41 | ## License
42 |
43 | This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details.
44 |
45 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
22 | React App
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | You need to enable JavaScript to run this app.
31 |
32 |
33 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/src/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/registerServiceWorker.js:
--------------------------------------------------------------------------------
1 | // In production, we register a service worker to serve assets from local cache.
2 |
3 | // This lets the app load faster on subsequent visits in production, and gives
4 | // it offline capabilities. However, it also means that developers (and users)
5 | // will only see deployed updates on the "N+1" visit to a page, since previously
6 | // cached resources are updated in the background.
7 |
8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
9 | // This link also includes instructions on opting out of this behavior.
10 |
11 | const isLocalhost = Boolean(
12 | window.location.hostname === 'localhost' ||
13 | // [::1] is the IPv6 localhost address.
14 | window.location.hostname === '[::1]' ||
15 | // 127.0.0.1/8 is considered localhost for IPv4.
16 | window.location.hostname.match(
17 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
18 | )
19 | );
20 |
21 | export default function register() {
22 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
23 | // The URL constructor is available in all browsers that support SW.
24 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
25 | if (publicUrl.origin !== window.location.origin) {
26 | // Our service worker won't work if PUBLIC_URL is on a different origin
27 | // from what our page is served on. This might happen if a CDN is used to
28 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
29 | return;
30 | }
31 |
32 | window.addEventListener('load', () => {
33 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
34 |
35 | if (!isLocalhost) {
36 | // Is not local host. Just register service worker
37 | registerValidSW(swUrl);
38 | } else {
39 | // This is running on localhost. Lets check if a service worker still exists or not.
40 | checkValidServiceWorker(swUrl);
41 | }
42 | });
43 | }
44 | }
45 |
46 | function registerValidSW(swUrl) {
47 | navigator.serviceWorker
48 | .register(swUrl)
49 | .then(registration => {
50 | registration.onupdatefound = () => {
51 | const installingWorker = registration.installing;
52 | installingWorker.onstatechange = () => {
53 | if (installingWorker.state === 'installed') {
54 | if (navigator.serviceWorker.controller) {
55 | // At this point, the old content will have been purged and
56 | // the fresh content will have been added to the cache.
57 | // It's the perfect time to display a "New content is
58 | // available; please refresh." message in your web app.
59 | console.log('New content is available; please refresh.');
60 | } else {
61 | // At this point, everything has been precached.
62 | // It's the perfect time to display a
63 | // "Content is cached for offline use." message.
64 | console.log('Content is cached for offline use.');
65 | }
66 | }
67 | };
68 | };
69 | })
70 | .catch(error => {
71 | console.error('Error during service worker registration:', error);
72 | });
73 | }
74 |
75 | function checkValidServiceWorker(swUrl) {
76 | // Check if the service worker can be found. If it can't reload the page.
77 | fetch(swUrl)
78 | .then(response => {
79 | // Ensure service worker exists, and that we really are getting a JS file.
80 | if (
81 | response.status === 404 ||
82 | response.headers.get('content-type').indexOf('javascript') === -1
83 | ) {
84 | // No service worker found. Probably a different app. Reload the page.
85 | navigator.serviceWorker.ready.then(registration => {
86 | registration.unregister().then(() => {
87 | window.location.reload();
88 | });
89 | });
90 | } else {
91 | // Service worker found. Proceed as normal.
92 | registerValidSW(swUrl);
93 | }
94 | })
95 | .catch(() => {
96 | console.log(
97 | 'No internet connection found. App is running in offline mode.'
98 | );
99 | });
100 | }
101 |
102 | export function unregister() {
103 | if ('serviceWorker' in navigator) {
104 | navigator.serviceWorker.ready.then(registration => {
105 | registration.unregister();
106 | });
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/public/assets/css/style.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0;
3 | padding: 0;
4 | box-sizing: border-box;
5 | }
6 | body {
7 | background: #fff;
8 | font-family: 'Roboto', sans-serif;
9 | }
10 | h3 {
11 | font-size: 15px;
12 | font-family: 'Montserrat', sans-serif;
13 | color: #222;
14 | margin: 0 0 15px;
15 | font-weight: 500;
16 | }
17 | /*------------------------ !group-dropdown --------------------------------------*/
18 | .tree{
19 | margin-top: 10px;
20 | position: relative;
21 | padding-left: 5px;
22 | }
23 | span.root {
24 | display: inline-block;
25 | background: #333;
26 | color: #fff;
27 | padding: 0 10px;
28 | line-height: 20px;
29 | margin-left: -5px;
30 | border-radius: 3px;
31 | text-transform: uppercase;
32 | font-size: 10px;
33 | font-weight: 500;
34 | }
35 | .tree ul{
36 | list-style: none;
37 | padding: 0 0 0 5px;
38 | }
39 | .tree ul li{
40 | padding: 7px 0 7px 15px;
41 | position: relative;
42 | -webkit-box-sizing: border-box;
43 | -moz-box-sizing: border-box;
44 | box-sizing: border-box;
45 | }
46 | .tree ul li:before {
47 | content: '';
48 | height: 1px;
49 | width: 10px;
50 | background-color: #b4b4b4;
51 | position: absolute;
52 | top: 16px;
53 | left: 0;
54 | margin: auto;
55 | }
56 | .tree ul li:after {
57 | content: '';
58 | width: 1px;
59 | height: 100%;
60 | background-color: #b4b4b4;
61 | position: absolute;
62 | top: 0;
63 | bottom: 0;
64 | left: 0;
65 | }
66 | .tree ul li:last-child {
67 | margin-bottom: 12px;
68 | }
69 | .tree ul li:last-child,
70 | .tree ul li:last-child:after{
71 | height: 16px;
72 | }
73 | .tree ul li .fa {
74 | color: #b4b4b4;
75 | cursor: pointer;
76 | display: inline-block;
77 | margin-right: 9px;
78 | }
79 | .tree ul li .fa:hover {
80 | color: #777;
81 | }
82 | .tree ul li .node {
83 | display: block;
84 | cursor: pointer;
85 | color: #737373;
86 | font-weight: 400;
87 | font-size: 13px;
88 | position: relative;
89 | z-index: 1;
90 | }
91 | .tree ul li .node:before {
92 | content: "";
93 | position: absolute;
94 | left: -5px;
95 | top: -5px;
96 | bottom: -5px;
97 | right: -5px;
98 | background-color: #f1f4f5;
99 | border-radius: 3px;
100 | z-index: -1;
101 | display: none;
102 | }
103 | .tree ul li .node:hover:before {
104 | display: block;
105 | }
106 | .tree ul li .node:hover{
107 | color:#333;
108 | text-decoration: none;
109 | }
110 | .node .actions {
111 | position: absolute;
112 | right: 0;
113 | background: #f1f4f5;
114 | display: none;
115 | top: 0;
116 | }
117 | .tree ul li .node:hover .actions {
118 | display: block;
119 | }
120 | .node .actions .fa {
121 | width: 16px;
122 | text-align: center;
123 | margin: 0;
124 | font-size: 10px;
125 | }
126 | .tree ul li .node.node_edit {
127 | z-index: 10;
128 | }
129 | .tree ul li .node.node_edit:before {
130 | background-color: #fff;
131 | display: block;
132 | border: 1px solid #71d371;
133 | box-shadow: 0 0 15px rgba(0, 0, 0, 0.2);
134 | }
135 | form.node_edit_form {
136 | position: relative;
137 | display: -webkit-flex;
138 | display: flex;
139 | height: 20px;
140 | }
141 | form.node_edit_form .field {
142 | flex-basis: 100px;
143 | position: relative;
144 | }
145 | form.node_edit_form .field input {
146 | line-height: 13px;
147 | height: 24px;
148 | padding: 0 10px 0 0;
149 | background-color: transparent;
150 | border: none;
151 | color: #000;
152 | position: relative;
153 | top: -3px;
154 | }
155 | form.node_edit_form .field.name input {
156 | width: 140px;
157 | }
158 | form.node_edit_form .field.code input {
159 | width: 60px;
160 | }
161 | form.node_edit_form .field.code:before {
162 | content: "";
163 | display: inline-block;
164 | width: 1px;
165 | position: absolute;
166 | left: -5px;
167 | top: -4px;
168 | bottom: -4px;
169 | background-color: #e1e5e8;
170 | }
171 | form.node_edit_form .field.action_node {
172 | position: absolute;
173 | top: -4px;
174 | right: -4px;
175 | bottom: -4px;
176 | width: 30px;
177 | background: #71d371;
178 | }
179 | form.node_edit_form .field.action_node .fa {
180 | width: 30px;
181 | display: block;
182 | height: 14px;
183 | text-align: center;
184 | line-height: 14px;
185 | color: #fff;
186 | font-size: 9px;
187 | }
188 | form.node_edit_form .field.action_node .fa.fa-close {
189 | background-color: #333;
190 | }
191 |
--------------------------------------------------------------------------------
/src/sample.data.js:
--------------------------------------------------------------------------------
1 | var treeData = {
2 | groupDropOptionId: 39,
3 | groupDropFieldId: 6,
4 | parentId: 0,
5 | name: 'Bangladesh',
6 | exportValue: 'BD',
7 | showChildren: true,
8 | editMode: false,
9 | children: [
10 | {
11 | groupDropOptionId: 40,
12 | groupDropFieldId: 6,
13 | parentId: 39,
14 | name: 'dhaka',
15 | exportValue: '1',
16 | showChildren: true,
17 | editMode: false,
18 | children: [
19 | {
20 | groupDropOptionId: 41,
21 | groupDropFieldId: 6,
22 | parentId: 40,
23 | name: 'gulshan',
24 | exportValue: '101',
25 | showChildren: false,
26 | editMode: false,
27 | children: []
28 | },
29 | {
30 | groupDropOptionId: 42,
31 | groupDropFieldId: 6,
32 | parentId: 40,
33 | name: 'mohakhali',
34 | exportValue: '102',
35 | showChildren: true,
36 | editMode: false,
37 | children: [
38 | {
39 | groupDropOptionId: 43,
40 | groupDropFieldId: 6,
41 | parentId: 42,
42 | name: 'shaheenbagh',
43 | exportValue: '20201',
44 | showChildren: false,
45 | editMode: false,
46 | children: []
47 | }
48 | ]
49 | },
50 | {
51 | groupDropOptionId: 44,
52 | groupDropFieldId: 6,
53 | parentId: 40,
54 | name: 'banani',
55 | exportValue: '103',
56 | showChildren: false,
57 | editMode: false,
58 | children: []
59 | },
60 | {
61 | groupDropOptionId: 45,
62 | groupDropFieldId: 6,
63 | parentId: 40,
64 | name: 'dhanmondi',
65 | exportValue: '204',
66 | showChildren: true,
67 | editMode: false,
68 | children: [
69 | {
70 | groupDropOptionId: 46,
71 | groupDropFieldId: 6,
72 | parentId: 45,
73 | name: 'jigatola',
74 | exportValue: '20401',
75 | showChildren: true,
76 | editMode: false,
77 | children: [
78 | {
79 | groupDropOptionId: 47,
80 | groupDropFieldId: 6,
81 | parentId: 46,
82 | name: 'japan bangladesh hospital !!!!!!!!',
83 | exportValue: '2040101',
84 | showChildren: false,
85 | editMode: false,
86 | children: []
87 | }
88 | ]
89 | },
90 | {
91 | groupDropOptionId: 48,
92 | groupDropFieldId: 6,
93 | parentId: 45,
94 | name: 'staff quater',
95 | exportValue: '12098',
96 | showChildren: false,
97 | editMode: false,
98 | children: []
99 | }
100 | ]
101 | },
102 | {
103 | groupDropOptionId: 49,
104 | groupDropFieldId: 6,
105 | parentId: 40,
106 | name: 'motijheel',
107 | exportValue: '205',
108 | showChildren: false,
109 | editMode: false,
110 | children: []
111 | }
112 | ]
113 | },
114 | {
115 | groupDropOptionId: 50,
116 | groupDropFieldId: 6,
117 | parentId: 39,
118 | name: 'chittagong',
119 | exportValue: '2',
120 | showChildren: true,
121 | editMode: false,
122 | children: [
123 | {
124 | groupDropOptionId: 51,
125 | groupDropFieldId: 6,
126 | parentId: 50,
127 | name: 'potenga',
128 | exportValue: '201',
129 | showChildren: false,
130 | editMode: false,
131 | children: []
132 | },
133 | {
134 | groupDropOptionId: 52,
135 | groupDropFieldId: 6,
136 | parentId: 50,
137 | name: 'bandarban',
138 | exportValue: '202',
139 | showChildren: false,
140 | editMode: false,
141 | children: []
142 | },
143 | {
144 | groupDropOptionId: 53,
145 | groupDropFieldId: 6,
146 | parentId: 50,
147 | name: 'cox\'s bazar',
148 | exportValue: '203',
149 | showChildren: false,
150 | editMode: false,
151 | children: []
152 | }
153 | ]
154 | },
155 | {
156 | groupDropOptionId: 54,
157 | groupDropFieldId: 6,
158 | parentId: 39,
159 | name: 'sylhet',
160 | exportValue: '3',
161 | showChildren: false,
162 | editMode: false,
163 | children: []
164 | }
165 | ]
166 | }
167 |
168 | export default treeData;
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import TreeData from './sample.data.js';
3 |
4 | export default class Treeview extends Component {
5 |
6 | constructor(props) {
7 | super(props);
8 | this.state = {
9 | data: TreeData,
10 | editableNode: ''
11 | }
12 | }
13 |
14 | addRoot = () => {
15 | let root = {
16 | name: '',
17 | exportValue: '',
18 | showChildren: true,
19 | editMode: true,
20 | children: []
21 | }
22 |
23 | this.setState({
24 | data: root
25 | });
26 | }
27 |
28 | handleEditChange = (e, value) => {
29 | value[e.target.name] = e.target.value;
30 | this.setState({ value });
31 | }
32 |
33 | deleteNode = (parent, index) => {
34 | parent.splice(index, 1);
35 | this.setState({ parent });
36 | }
37 |
38 | makeEditable = (value) => {
39 | this.state.editableNode = JSON.parse(JSON.stringify(value));
40 | value.editMode = true;
41 | this.setState({ value });
42 | }
43 |
44 | closeForm = (value, parent, index) => {
45 | if (value.name !== '' && value.exportValue !== '') {
46 | value.name = this.state.editableNode.name;
47 | value.exportValue = this.state.editableNode.exportValue;
48 | value.editMode = false;
49 | this.setState({ value });
50 | }
51 | else {
52 | console.log(index);
53 | parent.splice(index, 1);
54 | this.setState({ parent });
55 | }
56 | }
57 |
58 | doneEdit = (value) => {
59 | value.editMode = false;
60 | this.setState({ value });
61 | }
62 |
63 | toggleView = (ob) => {
64 | ob.showChildren = !ob.showChildren;
65 | this.setState({ ob });
66 | }
67 |
68 | addMember = (parent) => {
69 | let newChild = {
70 | name: '',
71 | exportValue: '',
72 | showChildren: false,
73 | editMode: true,
74 | children: []
75 | }
76 | parent.push(newChild);
77 | this.setState({ parent });
78 | }
79 |
80 | addChild = (node) => {
81 | node.showChildren = true;
82 | node.children.push({
83 | name: '',
84 | exportValue: '',
85 | showChildren: false,
86 | editMode: true,
87 | children: []
88 | });
89 | this.setState({ node });
90 | }
91 |
92 | nodeEditForm = (value, parent, index) => {
93 | return (
94 | { e.stopPropagation() }}>
95 |
117 |
118 | )
119 | }
120 |
121 | makeChildren = (node) => {
122 | if (typeof node === 'undefined' || node.length === 0) return null;
123 |
124 | let children;
125 | children = node.map((value, index)=> {
126 |
127 | let item = null;
128 |
129 | // A node has children and want to show her children
130 | if (value.children.length > 0 && value.showChildren) {
131 | let babies = this.makeChildren(value.children);
132 | let normalMode = (
133 |
134 | {value.name}
135 |
136 | { e.stopPropagation(); this.deleteNode(node, index) }}>
137 | { e.stopPropagation(); this.makeEditable(value) }}>
138 |
139 |
140 | )
141 | item = (
142 | { e.stopPropagation(); this.toggleView(value) }}>
143 | {(value.editMode) ? this.nodeEditForm(value, node, index) : normalMode}
144 | {babies}
145 |
146 | )
147 | }
148 |
149 | // A node has children but don't want to showing her children
150 | else if (value.children.length > 0 && !value.showChildren) {
151 | item = (
152 | { e.stopPropagation(); this.toggleView(value) }}>
153 | {value.name}
154 |
155 | );
156 | }
157 |
158 | // A node has no children
159 | else if (value.children.length === 0) {
160 | let normalMode = (
161 | {value.name}
162 |
163 | { e.stopPropagation(); this.addChild(value) }}>
164 | { e.stopPropagation(); this.makeEditable(value) }}>
165 | { e.stopPropagation(); this.deleteNode(node, index) }}>
166 |
167 |
168 | );
169 |
170 | item = (
171 | e.stopPropagation()}>
172 | {(value.editMode) ? this.nodeEditForm(value, node, index) : normalMode}
173 |
174 | );
175 | }
176 | return item;
177 | });
178 |
179 | return (
180 |
181 | {children}
182 |
183 | { e.stopPropagation();this.addMember(node) }}>
184 |
185 |
Add New
186 |
187 |
188 |
189 | );
190 | }
191 |
192 | getNodes = () => {
193 | if (typeof this.state.data.name === 'undefined') return null;
194 | let children = this.makeChildren(this.state.data.children);
195 | let root = (
196 | {this.state.data.name}
197 |
198 | { e.stopPropagation(); this.makeEditable(this.state.data) }}> edit
199 | { e.stopPropagation(); this.addChild(this.state.data) }}> Add_child
200 |
201 |
202 |
203 | )
204 | return (
205 |
206 | {(this.state.data.editMode) ? this.nodeEditForm(this.state.data) : root}
207 | {children}
208 |
209 | );
210 | }
211 |
212 | render() {
213 | return (
214 |
215 |
216 |
217 | {this.getNodes()}
218 |
219 |
220 |
221 | );
222 | }
223 | }
224 |
225 | //------------- Showing children ----------
226 | /*
227 |
240 | */
241 |
242 | //------------- Don't show children -------
243 | /*
244 |
245 | Mymenshingh (MYM)
246 | Rangpur (RAN)
247 |
248 | */
249 |
250 |
251 | // ---------------- Editing mode -----------
252 | /*
253 |
263 | */
264 |
265 |
--------------------------------------------------------------------------------