├── .babelrc ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── __tests__ └── reactTreeFunctions.test.js ├── generateContents ├── index-html.js ├── react-generate-App-css.js ├── react-generate-app-test.js ├── react-generate-content.js ├── react-generate-index-css.js ├── react-generate-index.js ├── react-generate-logo-svg.js ├── react-generate-registerServiceWorker.js ├── react-generate-stateless-component.js ├── redux-generate-action-creators.js ├── redux-generate-components.js ├── redux-generate-container.js ├── redux-generate-reducers.js └── redux-index.js ├── index.html ├── index.js ├── main.js ├── node_modules ├── .bin │ ├── acorn │ ├── ansi-html │ ├── atob │ ├── babylon │ ├── browserslist │ ├── cssesc │ ├── csso │ ├── errno │ ├── escodegen │ ├── esgenerate │ ├── esparse │ ├── esvalidate │ ├── handlebars │ ├── he │ ├── html-minifier │ ├── import-local-fixture │ ├── internal-ip │ ├── is-ci │ ├── jest │ ├── jest-runtime │ ├── js-yaml │ ├── jsesc │ ├── json5 │ ├── loose-envify │ ├── miller-rabin │ ├── mime │ ├── mkdirp │ ├── multicast-dns │ ├── regjsparser │ ├── rimraf │ ├── sane │ ├── semver │ ├── sha.js │ ├── sshpk-conv │ ├── sshpk-sign │ ├── sshpk-verify │ ├── strip-indent │ ├── svgo │ ├── uglifyjs │ ├── uuid │ ├── watch │ ├── webpack │ ├── webpack-dev-server │ └── which └── react-sortable-tree │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── dist │ └── main.js │ ├── package.json │ ├── style.css │ └── style.css.map ├── package-lock.json ├── package.json ├── src ├── components │ ├── App.js │ ├── NotFound.js │ ├── react-interface.js │ ├── react-tree.js │ ├── redux-interface.js │ └── redux-tree.js ├── drawing.svg ├── gitHubIcon.png ├── reactLogo.png ├── reactVelocity.png ├── reactVelocity.svg ├── reactVelocity_black.svg └── reduxLogo.png ├── styles └── styles.css ├── test.js └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "env", 4 | "react" 5 | ], 6 | "plugins": [ 7 | "transform-class-properties", 8 | "transform-object-rest-spread" 9 | 10 | ] 11 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_STORE 2 | Thumbs.db 3 | .Trashes 4 | node_modules 5 | package-lock.json 6 | build 7 | node_modules 8 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM godronus/ubuntu-csx 2 | WORKDIR /app 3 | COPY . . 4 | RUN npm install 5 | RUN npm run build 6 | 7 | 8 | 9 | #FROM mhart/alpine-node 10 | #RUN yarn global add serve 11 | #WORKDIR /app 12 | #COPY --from=0 /app/build . 13 | #CMD [“serve”, “-p 80”, “-s”, “.”] 14 | EXPOSE 8080 15 | EXPOSE 3000 16 | CMD ["npm", "start" ] 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 apjs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Velocity 2 | 3 | ![](https://user-images.githubusercontent.com/34348924/37787797-0fce794c-2dbd-11e8-9843-40bd2256786d.gif) 4 | 5 | 6 | React Velocity is an application that allows you to visualize your React project's component hierarchy before exporting the requisite boilerplate code. The two main features allow you to create React-based and React/Redux-based production level projects. This tool enables you to convert between stateless and stateful, class-based components. Your export will include a 'src' folder containing all of the same files that exist in the src folder of 'create-react-app'. Additionally, you will have a 'components' folder that contains your stateful and stateless components. Feel free to replace the 'src' folder in create-react-app with your newly exported 'src' folder from React Velocity. 7 | 8 | Within the Redux feature of the app, you can add action creators to your actions folder, reducers to your reducers folder, and containers and components to your containers and components folders, respectively. The simple design makes it user-friendly and intuitive to use for developers who work with React or it may be used by those who are just beginning their journey building Single Page Applications. 9 | 10 | 11 | ## Usage 12 | 13 | To get started, fork -> clone -> npm install -> npm start the project onto your machine and run it in localhost and select one of the two features that you wish to work in. You may also visit [reactvelocity.com](http://reactvelocity.com) but it is recommended to use this master repo for the most recent version as there can be delays in the website update. The cyan page allows you to begin adding components for a React-based application and the purple page allows you to add a layer of Redux functionality. In React mode, you can toggle between stateful and stateless components. Redux mode allows you to add much more including reducers, actions, and containers all while preserving the ability to add functional and presentational components. 14 | 15 | 16 | ## Contributing 17 | 18 | Please submit issues/pull requests if you have feedback or message the React Velocity team to be added as a contributor: apjs.react.velocity@gmail.com and CC Paul (pauldubovsky@gmail.com) to the email. 19 | 20 | ## Authors 21 | 22 | * Alex Clifton (https://github.com/AGCB) 23 | * Paul Dubovsky (https://github.com/menothe) 24 | * Justin Yip (https://github.com/jeyip12) 25 | * Scott Bengtson (https://github.com/sunsnba) 26 | -------------------------------------------------------------------------------- /__tests__/reactTreeFunctions.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | I am copying all functions defined inside react-tree.js into this file to test. 3 | For our future selves..... if you decide to change functionality on any of 4 | these methods, you will have to then copy the code into the function definition 5 | of this file. 6 | It's not an ideal workflow, but at least you won't have to rewrite any of the tests:) 7 | 8 | Only the formatName() function is clear to test for me at this point. 9 | All of the others use this.setState() and pieces of RST. 10 | 11 | Be aware that our formatName() is in react-tree.js AND redux-tree.js 12 | So, if changes need to be made, they need to be made in 3 places. 13 | 1- formatName() in react-tree.js 14 | 2- formatName() in redux-tree.js 15 | 3- formatName() in reactTreeFunctions.test.js 16 | */ 17 | 18 | let formatName = (textField) => { 19 | let scrubbedResult = textField 20 | // Capitalize first letter of string. 21 | //| ^ = beginning of output | . = 1st char of str | 22 | .replace(/^./g, x => x.toUpperCase()) 23 | // Capitalize first letter of each word and removes spaces. 24 | //| \ = matches | \w = any alphanumeric | \S = single char except white space 25 | //| * = preceeding expression 0 or more times | + = preceeding expression 1 or more times | 26 | .replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1);}) 27 | .replace(/\ +/g, x => '') 28 | // Remove appending file extensions like .js or .json. 29 | //| \. = . in file extensions | $ = end of input | 30 | .replace(/\..+$/, ''); 31 | return scrubbedResult; 32 | } 33 | 34 | test('formatName Function', () => { 35 | //must capitalize first string 36 | expect(formatName('barney')).toBe('Barney'); 37 | //must remove spaces 38 | expect(formatName('Bar Bar')).toBe('BarBar'); 39 | //must capitalize first letter of each word 40 | expect(formatName('barney barry')).toBe('BarneyBarry'); 41 | //must remove file extensions like .js or .json 42 | expect(formatName('barney.js')).toBe('Barney'); 43 | //last wild test. 44 | expect(formatName('barny.js')).toBe('Barny'); 45 | }); 46 | 47 | 48 | 49 | 50 | /* 51 | Now for a test on our Export Button's function... 52 | 53 | */ 54 | 55 | function generateCode(data) { 56 | let filesToZip = {}; 57 | let keys = Object.keys(data); 58 | let code = ''; 59 | for (let i = 0; i < keys.length; i++) { 60 | code += "import React, { Component } from 'react';\n" 61 | if (data[keys[i]]) { 62 | for (let k=0; k < data[keys[i]].length; k++) { 63 | code += `import ${data[keys[i]][k]} from './${data[keys[i]][k]}';\n`; 64 | } 65 | } 66 | code += '\n'; 67 | code += `class ${keys[i]} extends Component {\n`; 68 | code += ' render() {\n'; 69 | code += ' return (\n'; 70 | code += '
\n'; 71 | if (data[keys[i]]) { 72 | for (let j=0; j < data[keys[i]].length; j++) { 73 | code += ` <${data[keys[i]][j]} />\n`; 74 | } 75 | } 76 | code += '
\n'; 77 | code += ' );\n'; 78 | code += ' };\n'; 79 | code += '}\n\n'; 80 | code += `export default ${keys[i]};`; 81 | filesToZip[keys[i]] = code; 82 | code = ''; 83 | } 84 | return filesToZip; 85 | } 86 | //sampleData... 87 | const treeData = [{title: 'App', children: [{title: 'Bengt', children: [{title: 'Einar'}]}, {title: 'Daniel'}]}]; 88 | const version2 = {"App":["Scott"],"Scott":["Justin"],"Justin":null,"Alex":null} 89 | test('Does the generateCode() function export anything at all', () => { 90 | expect(generateCode(treeData)).toBeDefined(); 91 | }); 92 | -------------------------------------------------------------------------------- /generateContents/index-html.js: -------------------------------------------------------------------------------- 1 | const generateIndexHTML = () => { 2 | let code = `\n`; 3 | code += `\n`; 4 | code += `\n`; 5 | code += ` \n`; 6 | code += ` React/Redux App\n`; 7 | code += `\n`; 8 | code += `\n`; 9 | code += `
React/Redux App
\n`; 10 | code += `\n`; 11 | code += ``; 12 | return code; 13 | } 14 | 15 | export default generateIndexHTML; 16 | -------------------------------------------------------------------------------- /generateContents/react-generate-App-css.js: -------------------------------------------------------------------------------- 1 | const generateAppCSS = () => { 2 | return ''; 3 | } 4 | 5 | export default generateAppCSS; 6 | -------------------------------------------------------------------------------- /generateContents/react-generate-app-test.js: -------------------------------------------------------------------------------- 1 | const generateAppTestJS = () => { 2 | let code = `import React from 'react';\n`; 3 | code += `import ReactDOM from 'react-dom';\n`; 4 | code += `import App from './App';\n\n`; 5 | code += `it('renders without crashing', () => {\n`; 6 | code += ` const div = document.createElement('div');\n`; 7 | code += ` ReactDOM.render(, div);\n`; 8 | code += ` ReactDOM.unmountComponentAtNode(div);\n`; 9 | code += `});`; 10 | return code; 11 | } 12 | 13 | export default generateAppTestJS; 14 | -------------------------------------------------------------------------------- /generateContents/react-generate-content.js: -------------------------------------------------------------------------------- 1 | const generateCode = data => { 2 | let filesToZip = {}; 3 | let keys = Object.keys(data); 4 | let code = ''; 5 | for (let i = 0; i < keys.length; i++) { 6 | let state = data[keys[i]][data[keys[i]].length - 1][0]; 7 | if (state === 'stateful') { 8 | code += "import React, { Component } from 'react';\n" 9 | if (data[keys[i]]) { 10 | for (let k=0; k < data[keys[i]].length - 1; k++) { 11 | if (keys[i] === 'App') { 12 | code += `import ${data[keys[i]][k]} from './components/${data[keys[i]][k]}';\n`; 13 | } else { 14 | if(data[keys[i]][k][0] === state) { 15 | } else { 16 | code += `import ${data[keys[i]][k]} from './${data[keys[i]][k]}';\n`; 17 | } 18 | } 19 | } 20 | } 21 | code += '\n'; 22 | code += `class ${keys[i]} extends Component {\n`; 23 | code += ' constructor(props) {\n'; 24 | code += ' super(props);\n'; 25 | code += ' this.state = {};\n'; 26 | code += ' }\n\n'; 27 | code += ' render() {\n'; 28 | code += ' return (\n'; 29 | code += '
\n'; 30 | if (data[keys[i]]) { 31 | for (let j=0; j < data[keys[i]].length - 1; j++) { 32 | if(data[keys[i]][j][0] === state) { 33 | } else { 34 | code += ` <${data[keys[i]][j]} />\n`; 35 | } 36 | } 37 | } 38 | code += '
\n'; 39 | code += ' );\n'; 40 | code += ' };\n'; 41 | code += '}\n\n'; 42 | code += `export default ${keys[i]};`; 43 | filesToZip[keys[i]] = code; 44 | code = ''; 45 | } 46 | } 47 | return filesToZip; 48 | } 49 | 50 | export default generateCode; 51 | -------------------------------------------------------------------------------- /generateContents/react-generate-index-css.js: -------------------------------------------------------------------------------- 1 | const generateIndexCSS = () => { 2 | let code = `body {\n`; 3 | code += ` margin: 0;\n`; 4 | code += ` padding: 0;\n`; 5 | code += ` font-family: sans-serif;\n`; 6 | code += `}`; 7 | return code; 8 | } 9 | 10 | export default generateIndexCSS; 11 | -------------------------------------------------------------------------------- /generateContents/react-generate-index.js: -------------------------------------------------------------------------------- 1 | const generateReactIndexJS = () => { 2 | let code = `import React from 'react';\n`; 3 | code += `import ReactDOM from 'react-dom';\n`; 4 | code += `import './index.css';\n`; 5 | code += `import App from './App';\n`; 6 | code += `import registerServiceWorker from './registerServiceWorker';\n\n`; 7 | code += `ReactDOM.render(, document.getElementById('root'));\n`; 8 | code += `registerServiceWorker();`; 9 | return code; 10 | } 11 | 12 | export default generateReactIndexJS; 13 | -------------------------------------------------------------------------------- /generateContents/react-generate-logo-svg.js: -------------------------------------------------------------------------------- 1 | const generateLogoSVG = () => { 2 | return ` 3 | 4 | 5 | 6 | 7 | 8 | 9 | ` 10 | } 11 | 12 | export default generateLogoSVG; 13 | -------------------------------------------------------------------------------- /generateContents/react-generate-registerServiceWorker.js: -------------------------------------------------------------------------------- 1 | const generateRegisterServiceWorker = () => { 2 | return `// In production, we register a service worker to serve assets from local cache. 3 | 4 | // This lets the app load faster on subsequent visits in production, and gives 5 | // it offline capabilities. However, it also means that developers (and users) 6 | // will only see deployed updates on the "N+1" visit to a page, since previously 7 | // cached resources are updated in the background. 8 | 9 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy. 10 | // This link also includes instructions on opting out of this behavior. 11 | 12 | const isLocalhost = Boolean( 13 | window.location.hostname === 'localhost' || 14 | // [::1] is the IPv6 localhost address. 15 | window.location.hostname === '[::1]' || 16 | // 127.0.0.1/8 is considered localhost for IPv4. 17 | window.location.hostname.match( 18 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 19 | ) 20 | ); 21 | 22 | export default function register() { 23 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 24 | // The URL constructor is available in all browsers that support SW. 25 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location); 26 | if (publicUrl.origin !== window.location.origin) { 27 | // Our service worker won't work if PUBLIC_URL is on a different origin 28 | // from what our page is served on. This might happen if a CDN is used to 29 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374 30 | return; 31 | } 32 | 33 | window.addEventListener('load', () => { 34 | const swUrl = `+ '`${process.env.PUBLIC_URL}/service-worker.js`;' + ` 35 | 36 | if (isLocalhost) { 37 | // This is running on localhost. Lets check if a service worker still exists or not. 38 | checkValidServiceWorker(swUrl); 39 | 40 | // Add some additional logging to localhost, pointing developers to the 41 | // service worker/PWA documentation. 42 | navigator.serviceWorker.ready.then(() => { 43 | console.log( 44 | 'This web app is being served cache-first by a service ' + 45 | 'worker. To learn more, visit https://goo.gl/SC7cgQ' 46 | ); 47 | }); 48 | } else { 49 | // Is not local host. Just register service worker 50 | registerValidSW(swUrl); 51 | } 52 | }); 53 | } 54 | } 55 | 56 | function registerValidSW(swUrl) { 57 | navigator.serviceWorker 58 | .register(swUrl) 59 | .then(registration => { 60 | registration.onupdatefound = () => { 61 | const installingWorker = registration.installing; 62 | installingWorker.onstatechange = () => { 63 | if (installingWorker.state === 'installed') { 64 | if (navigator.serviceWorker.controller) { 65 | // At this point, the old content will have been purged and 66 | // the fresh content will have been added to the cache. 67 | // It's the perfect time to display a "New content is 68 | // available; please refresh." message in your web app. 69 | console.log('New content is available; please refresh.'); 70 | } else { 71 | // At this point, everything has been precached. 72 | // It's the perfect time to display a 73 | // "Content is cached for offline use." message. 74 | console.log('Content is cached for offline use.'); 75 | } 76 | } 77 | }; 78 | }; 79 | }) 80 | .catch(error => { 81 | console.error('Error during service worker registration:', error); 82 | }); 83 | } 84 | 85 | function checkValidServiceWorker(swUrl) { 86 | // Check if the service worker can be found. If it can't reload the page. 87 | fetch(swUrl) 88 | .then(response => { 89 | // Ensure service worker exists, and that we really are getting a JS file. 90 | if ( 91 | response.status === 404 || 92 | response.headers.get('content-type').indexOf('javascript') === -1 93 | ) { 94 | // No service worker found. Probably a different app. Reload the page. 95 | navigator.serviceWorker.ready.then(registration => { 96 | registration.unregister().then(() => { 97 | window.location.reload(); 98 | }); 99 | }); 100 | } else { 101 | // Service worker found. Proceed as normal. 102 | registerValidSW(swUrl); 103 | } 104 | }) 105 | .catch(() => { 106 | console.log( 107 | 'No internet connection found. App is running in offline mode.' 108 | ); 109 | }); 110 | } 111 | 112 | export function unregister() { 113 | if ('serviceWorker' in navigator) { 114 | navigator.serviceWorker.ready.then(registration => { 115 | registration.unregister(); 116 | }); 117 | } 118 | } 119 | ` 120 | } 121 | 122 | export default generateRegisterServiceWorker; 123 | -------------------------------------------------------------------------------- /generateContents/react-generate-stateless-component.js: -------------------------------------------------------------------------------- 1 | export default function generatePresentationalComponent(data) { 2 | let filesToZip = {}; 3 | let keys = Object.keys(data); 4 | let code = ''; 5 | for (let i = 0; i < keys.length; i++) { 6 | if (keys[i] === 'action' || keys[i] === 'container/component' || keys[i] === 'reducer') { 7 | continue; 8 | } 9 | let state = data[keys[i]][data[keys[i]].length - 1][0]; 10 | if (state === 'stateless') { 11 | code += "import React from 'react';\n" 12 | if (data[keys[i]]) { 13 | for (let k=0; k < data[keys[i]].length-1; k++) { 14 | if(data[keys[i]][k][0] === state) { 15 | } else { 16 | code += `import ${data[keys[i]][k]} from './${data[keys[i]][k]}';\n`; 17 | } 18 | } 19 | } 20 | code += '\n'; 21 | code += `const ${keys[i]} = props => {\n`; 22 | code += ' return (\n'; 23 | code += '
\n'; 24 | if (data[keys[i]]) { 25 | for (let j=0; j < data[keys[i]].length - 1; j++) { 26 | if(data[keys[i]][j][0] === state) { 27 | } else { 28 | code += ` <${data[keys[i]][j]} />\n`; 29 | } 30 | } 31 | } 32 | code += '
\n'; 33 | code += ' );\n'; 34 | code += ' };\n\n'; 35 | code += `export default ${keys[i]};`; 36 | filesToZip[keys[i]] = code; 37 | code = ''; 38 | } 39 | } 40 | return filesToZip; 41 | } 42 | -------------------------------------------------------------------------------- /generateContents/redux-generate-action-creators.js: -------------------------------------------------------------------------------- 1 | const generateActionCreators = (data) => { 2 | let code = ''; 3 | for (let i = 0; i < data.length; i++) { 4 | if (data[i].parentNode) { 5 | if (data[i].parentNode.name === "action") { 6 | code += `export const ${data[i].node.name} = replace_payload => {\n`; 7 | code += ` return {\n`; 8 | code += ` type: '${data[i].node.type}',\n`; 9 | code += ` replace_payload\n`; 10 | code += ` }\n`; 11 | code += `}\n\n`; 12 | } 13 | } 14 | } 15 | return code; 16 | } 17 | 18 | export default generateActionCreators; 19 | -------------------------------------------------------------------------------- /generateContents/redux-generate-components.js: -------------------------------------------------------------------------------- 1 | export default function generateComponents(data) { 2 | let filesToZip = {}; 3 | let keys = Object.keys(data); 4 | let code = ''; 5 | for (let i = 0; i < keys.length; i++) { 6 | if (keys[i] === 'action' || keys[i] === 'container/component' || keys[i] === 'reducer') { 7 | continue; 8 | } 9 | let state = data[keys[i]][data[keys[i]].length - 1][0]; 10 | if (state === 'stateful') { 11 | code += "import React, { Component } from 'react';\n" 12 | if (data[keys[i]]) { 13 | for (let k=0; k < data[keys[i]].length - 1; k++) { 14 | code += `import ${data[keys[i]][k]} from './${data[keys[i]][k]}';\n`; 15 | } 16 | } 17 | code += '\n'; 18 | code += `class ${keys[i]} extends Component {\n`; 19 | code += ' constructor(props) {\n'; 20 | code += ' super(props);\n'; 21 | code += ' this.state = {};\n'; 22 | code += ' }\n\n'; 23 | code += ' render() {\n'; 24 | code += ' return (\n'; 25 | code += '
\n'; 26 | if (data[keys[i]]) { 27 | for (let j=0; j < data[keys[i]].length - 1; j++) { 28 | code += ` <${data[keys[i]][j]} />\n`; 29 | } 30 | } 31 | code += '
\n'; 32 | code += ' );\n'; 33 | code += ' };\n'; 34 | code += '}\n\n'; 35 | code += `export default ${keys[i]};`; 36 | filesToZip[keys[i]] = code; 37 | code = ''; 38 | } 39 | } 40 | return filesToZip; 41 | } 42 | -------------------------------------------------------------------------------- /generateContents/redux-generate-container.js: -------------------------------------------------------------------------------- 1 | const generateContainer = data => { 2 | let filesToZip = {}; 3 | let keys = Object.keys(data); 4 | let code = ''; 5 | for (let i = 0; i < keys.length; i++) { 6 | if (keys[i] === 'action' || keys[i] === 'container/component' || keys[i] === 'reducer') { 7 | continue; 8 | } 9 | let state = data[keys[i]][data[keys[i]].length - 1][0]; 10 | if (state === 'container') { 11 | code += "import React, { Component } from 'react';\n"; 12 | code += "import { connect } from 'react-redux';\n"; 13 | code += "import { bindActionCreators } from redux;\n"; 14 | code += "import * as actionCreators from './../actions/actionTypes';\n"; 15 | if (data[keys[i]]) { 16 | for (let k=0; k < data[keys[i]].length - 1; k++) { 17 | code += `import ${data[keys[i]][k]} from './${data[keys[i]][k]}';\n`; 18 | } 19 | } 20 | code += '\n'; 21 | code += `class ${keys[i]} extends Component {\n`; 22 | code += ' render() {\n'; 23 | code += ' return (\n'; 24 | code += '
\n'; 25 | if (data[keys[i]]) { 26 | for (let j=0; j < data[keys[i]].length - 1; j++) { 27 | code += ` <${data[keys[i]][j]} />\n`; 28 | } 29 | } 30 | code += '
\n'; 31 | code += ' );\n'; 32 | code += ' };\n'; 33 | code += '}\n\n'; 34 | code += "function mapStateToProps(state = {}) {\n"; 35 | code += ' return {prop: state.prop};\n'; 36 | code += '}\n\n'; 37 | code += 'function mapDispatchToProps(dispatch) {\n'; 38 | code += ' return {actions: bindActionCreators(actionCreators, dispatch)};\n'; 39 | code += '}\n\n'; 40 | code += `export default connect(mapStateToProps, mapDispatchToProps)(${keys[i]});`; 41 | filesToZip[keys[i]] = code; 42 | code = ''; 43 | } 44 | } 45 | return filesToZip; 46 | } 47 | 48 | export default generateContainer; 49 | -------------------------------------------------------------------------------- /generateContents/redux-generate-reducers.js: -------------------------------------------------------------------------------- 1 | const generateReducers = (data) => { 2 | let code = ''; 3 | for (let i = 0; i < data.length; i++) { 4 | if (data[i].node.componentType) { 5 | if (data[i].node.componentType === "Reducer") { 6 | code += `export function ${data[i].node.name}(state = {}, action) {\n`; 7 | code += ` switch (action.type) {\n`; 8 | if (data[i].node.case) { 9 | for (let j=0; j < data[i].node.case.length;j++) { 10 | code += ` case '${data[i].node.case[j]}':\n`; 11 | code += ` return Object.assign({}, state, {\n`; 12 | code += ` item: 'new item'\n`; 13 | code += ` });\n`; 14 | } 15 | } 16 | code += ` default:\n`; 17 | code += ` return state;\n` 18 | code += ` }\n`; 19 | code += `}\n\n`; 20 | } 21 | } 22 | } 23 | return code; 24 | } 25 | 26 | export default generateReducers; 27 | -------------------------------------------------------------------------------- /generateContents/redux-index.js: -------------------------------------------------------------------------------- 1 | const generateReduxIndexJS = () => { 2 | let code = `import React from 'react';\n`; 3 | code += `import { render } from 'react-dom';\n`; 4 | code += `import { Provider } from 'react-redux';\n`; 5 | code += `import { createStore } from 'redux';\n`; 6 | code += `import * as rootReducer from './reducers/reducers';\n`; 7 | code += `import App from './components/App';\n\n`; 8 | code += `const store = createStore(rootReducer);\n\n`; 9 | code += `render(\n`; 10 | code += ` \n`; 11 | code += ` \n`; 12 | code += ` ,\n`; 13 | code += ` document.getElementById('app')\n`; 14 | code += `)`; 15 | return code; 16 | } 17 | 18 | export default generateReduxIndexJS; 19 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | React Velocity 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | /* 2 | This is the file that renders directly to the index.html inside the App id. 3 | MuiThemeProvider is our Material UI boilerplate. 4 | */ 5 | import React from 'react'; 6 | import ReactDOM from 'react-dom'; 7 | import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'; 8 | import darkBaseTheme from 'material-ui/styles/baseThemes/darkBaseTheme'; 9 | import getMuiTheme from 'material-ui/styles/getMuiTheme'; 10 | import App from './src/components/App'; 11 | 12 | ReactDOM.render( 13 | 14 | 15 | , 16 | document.getElementById('app')); 17 | -------------------------------------------------------------------------------- /node_modules/.bin/acorn: -------------------------------------------------------------------------------- 1 | ../acorn/bin/acorn -------------------------------------------------------------------------------- /node_modules/.bin/ansi-html: -------------------------------------------------------------------------------- 1 | ../ansi-html/bin/ansi-html -------------------------------------------------------------------------------- /node_modules/.bin/atob: -------------------------------------------------------------------------------- 1 | ../atob/bin/atob.js -------------------------------------------------------------------------------- /node_modules/.bin/babylon: -------------------------------------------------------------------------------- 1 | ../babylon/bin/babylon.js -------------------------------------------------------------------------------- /node_modules/.bin/browserslist: -------------------------------------------------------------------------------- 1 | ../browserslist/cli.js -------------------------------------------------------------------------------- /node_modules/.bin/cssesc: -------------------------------------------------------------------------------- 1 | ../cssesc/bin/cssesc -------------------------------------------------------------------------------- /node_modules/.bin/csso: -------------------------------------------------------------------------------- 1 | ../csso/bin/csso -------------------------------------------------------------------------------- /node_modules/.bin/errno: -------------------------------------------------------------------------------- 1 | ../errno/cli.js -------------------------------------------------------------------------------- /node_modules/.bin/escodegen: -------------------------------------------------------------------------------- 1 | ../escodegen/bin/escodegen.js -------------------------------------------------------------------------------- /node_modules/.bin/esgenerate: -------------------------------------------------------------------------------- 1 | ../escodegen/bin/esgenerate.js -------------------------------------------------------------------------------- /node_modules/.bin/esparse: -------------------------------------------------------------------------------- 1 | ../esprima/bin/esparse.js -------------------------------------------------------------------------------- /node_modules/.bin/esvalidate: -------------------------------------------------------------------------------- 1 | ../esprima/bin/esvalidate.js -------------------------------------------------------------------------------- /node_modules/.bin/handlebars: -------------------------------------------------------------------------------- 1 | ../handlebars/bin/handlebars -------------------------------------------------------------------------------- /node_modules/.bin/he: -------------------------------------------------------------------------------- 1 | ../he/bin/he -------------------------------------------------------------------------------- /node_modules/.bin/html-minifier: -------------------------------------------------------------------------------- 1 | ../html-minifier/cli.js -------------------------------------------------------------------------------- /node_modules/.bin/import-local-fixture: -------------------------------------------------------------------------------- 1 | ../import-local/fixtures/cli.js -------------------------------------------------------------------------------- /node_modules/.bin/internal-ip: -------------------------------------------------------------------------------- 1 | ../internal-ip/cli.js -------------------------------------------------------------------------------- /node_modules/.bin/is-ci: -------------------------------------------------------------------------------- 1 | ../is-ci/bin.js -------------------------------------------------------------------------------- /node_modules/.bin/jest: -------------------------------------------------------------------------------- 1 | ../jest/bin/jest.js -------------------------------------------------------------------------------- /node_modules/.bin/jest-runtime: -------------------------------------------------------------------------------- 1 | ../jest-runtime/bin/jest-runtime.js -------------------------------------------------------------------------------- /node_modules/.bin/js-yaml: -------------------------------------------------------------------------------- 1 | ../js-yaml/bin/js-yaml.js -------------------------------------------------------------------------------- /node_modules/.bin/jsesc: -------------------------------------------------------------------------------- 1 | ../jsesc/bin/jsesc -------------------------------------------------------------------------------- /node_modules/.bin/json5: -------------------------------------------------------------------------------- 1 | ../json5/lib/cli.js -------------------------------------------------------------------------------- /node_modules/.bin/loose-envify: -------------------------------------------------------------------------------- 1 | ../loose-envify/cli.js -------------------------------------------------------------------------------- /node_modules/.bin/miller-rabin: -------------------------------------------------------------------------------- 1 | ../miller-rabin/bin/miller-rabin -------------------------------------------------------------------------------- /node_modules/.bin/mime: -------------------------------------------------------------------------------- 1 | ../mime/cli.js -------------------------------------------------------------------------------- /node_modules/.bin/mkdirp: -------------------------------------------------------------------------------- 1 | ../mkdirp/bin/cmd.js -------------------------------------------------------------------------------- /node_modules/.bin/multicast-dns: -------------------------------------------------------------------------------- 1 | ../multicast-dns/cli.js -------------------------------------------------------------------------------- /node_modules/.bin/regjsparser: -------------------------------------------------------------------------------- 1 | ../regjsparser/bin/parser -------------------------------------------------------------------------------- /node_modules/.bin/rimraf: -------------------------------------------------------------------------------- 1 | ../rimraf/bin.js -------------------------------------------------------------------------------- /node_modules/.bin/sane: -------------------------------------------------------------------------------- 1 | ../sane/src/cli.js -------------------------------------------------------------------------------- /node_modules/.bin/semver: -------------------------------------------------------------------------------- 1 | ../semver/bin/semver -------------------------------------------------------------------------------- /node_modules/.bin/sha.js: -------------------------------------------------------------------------------- 1 | ../sha.js/bin.js -------------------------------------------------------------------------------- /node_modules/.bin/sshpk-conv: -------------------------------------------------------------------------------- 1 | ../sshpk/bin/sshpk-conv -------------------------------------------------------------------------------- /node_modules/.bin/sshpk-sign: -------------------------------------------------------------------------------- 1 | ../sshpk/bin/sshpk-sign -------------------------------------------------------------------------------- /node_modules/.bin/sshpk-verify: -------------------------------------------------------------------------------- 1 | ../sshpk/bin/sshpk-verify -------------------------------------------------------------------------------- /node_modules/.bin/strip-indent: -------------------------------------------------------------------------------- 1 | ../strip-indent/cli.js -------------------------------------------------------------------------------- /node_modules/.bin/svgo: -------------------------------------------------------------------------------- 1 | ../svgo/bin/svgo -------------------------------------------------------------------------------- /node_modules/.bin/uglifyjs: -------------------------------------------------------------------------------- 1 | ../uglify-js/bin/uglifyjs -------------------------------------------------------------------------------- /node_modules/.bin/uuid: -------------------------------------------------------------------------------- 1 | ../uuid/bin/uuid -------------------------------------------------------------------------------- /node_modules/.bin/watch: -------------------------------------------------------------------------------- 1 | ../watch/cli.js -------------------------------------------------------------------------------- /node_modules/.bin/webpack: -------------------------------------------------------------------------------- 1 | ../webpack/bin/webpack.js -------------------------------------------------------------------------------- /node_modules/.bin/webpack-dev-server: -------------------------------------------------------------------------------- 1 | ../webpack-dev-server/bin/webpack-dev-server.js -------------------------------------------------------------------------------- /node_modules/.bin/which: -------------------------------------------------------------------------------- 1 | ../which/bin/which -------------------------------------------------------------------------------- /node_modules/react-sortable-tree/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | 6 | ## [2.0.1](https://github.com/fritz-c/react-sortable-tree/compare/v2.0.0...v2.0.1) (2018-02-10) 7 | 8 | 9 | ### Bug Fixes 10 | 11 | * restore highlight line appearance ([2c95205](https://github.com/fritz-c/react-sortable-tree/commit/2c95205)) 12 | 13 | 14 | 15 | 16 | # [2.0.0](https://github.com/fritz-c/react-sortable-tree/compare/v1.8.1...v2.0.0) (2018-02-10) 17 | 18 | 19 | ### BREAKING CHANGES 20 | 21 | * from v2.0.0 on, you must import the css for the 22 | component yourself, using `import 'react-sortable-tree/style.css';`. 23 | You only need to do this once in your application. 24 | 25 | 26 | 27 | 28 | ## [1.8.1](https://github.com/fritz-c/react-sortable-tree/compare/v1.8.0...v1.8.1) (2018-01-21) 29 | 30 | 31 | ### Bug Fixes 32 | 33 | * rename parentNode callback param to nextParentNode ([24bf39d](https://github.com/fritz-c/react-sortable-tree/commit/24bf39d)) 34 | 35 | 36 | 37 | 38 | # [1.8.0](https://github.com/fritz-c/react-sortable-tree/compare/v1.7.0...v1.8.0) (2018-01-21) 39 | 40 | 41 | ### Features 42 | 43 | * Parent node in onMoveNode callback ([537c6a4](https://github.com/fritz-c/react-sortable-tree/commit/537c6a4)) 44 | 45 | 46 | 47 | 48 | # [1.7.0](https://github.com/fritz-c/react-sortable-tree/compare/v1.6.0...v1.7.0) (2018-01-16) 49 | 50 | 51 | ### Features 52 | 53 | * add onDragStateChanged callback ([2caa9d1](https://github.com/fritz-c/react-sortable-tree/commit/2caa9d1)) 54 | 55 | onDragStateChanged is called when dragging begins and ends, so you can easily track the current state of dragging.
56 | Thanks to [@wuweiweiwu](https://github.com/wuweiweiwu) for the contribution! 57 | 58 | 59 | 60 | 61 | # [1.6.0](https://github.com/fritz-c/react-sortable-tree/compare/v1.5.5...v1.6.0) (2018-01-14) 62 | 63 | 64 | ### Features 65 | 66 | * add more parameters to rowHeight. Fixes [#199](https://github.com/fritz-c/react-sortable-tree/issues/199) ([8ff0ff2](https://github.com/fritz-c/react-sortable-tree/commit/8ff0ff2)) 67 | 68 | Thanks to [@wuweiweiwu](https://github.com/wuweiweiwu) for the contribution! 69 | 70 | 71 | 72 | 73 | ## [1.5.5](https://github.com/fritz-c/react-sortable-tree/compare/v1.5.4...v1.5.5) (2018-01-13) 74 | 75 | 76 | ### Bug Fixes 77 | 78 | * expand tree for searches on initial mount. fixes [#223](https://github.com/fritz-c/react-sortable-tree/issues/223) ([64a984a](https://github.com/fritz-c/react-sortable-tree/commit/64a984a)) 79 | 80 | 81 | 82 | 83 | ## [1.5.4](https://github.com/fritz-c/react-sortable-tree/compare/v1.5.3...v1.5.4) (2018-01-07) 84 | 85 | ### Bug Fixes 86 | 87 | * UglifyJS enabled to remove dead code, which had been causing issues with some builds. If the presence of UglifyJS causes issues in your production builds, please refer to https://github.com/fritz-c/react-sortable-tree#if-it-throws-typeerror-fn-is-not-a-function-errors-in-production 88 | 89 | 90 | 91 | 92 | ## [1.5.3](https://github.com/fritz-c/react-sortable-tree/compare/v1.5.2...v1.5.3) (2017-12-09) 93 | 94 | 95 | ### Bug Fixes 96 | 97 | * dragging past the bottom of the tree no longer slows down rendering ([3ce35f3](https://github.com/fritz-c/react-sortable-tree/commit/3ce35f3)) 98 | 99 | 100 | 101 | 102 | ## [1.5.2](https://github.com/fritz-c/react-sortable-tree/compare/v1.5.1...v1.5.2) (2017-11-28) 103 | 104 | 105 | ### Bug Fixes 106 | 107 | * correct positioning of full-width draggable rows ([00396d1](https://github.com/fritz-c/react-sortable-tree/commit/00396d1)) 108 | 109 | 110 | 111 | 112 | ## [1.5.1](https://github.com/fritz-c/react-sortable-tree/compare/v1.5.0...v1.5.1) (2017-11-28) 113 | 114 | 115 | ### Bug Fixes 116 | 117 | * prevent slowdown caused by invalid targetDepth when using maxDepth ([c21d4de](https://github.com/fritz-c/react-sortable-tree/commit/c21d4de)), closes [#194](https://github.com/fritz-c/react-sortable-tree/issues/194) 118 | 119 | 120 | 121 | 122 | # [1.5.0](https://github.com/fritz-c/react-sortable-tree/compare/v1.4.0...v1.5.0) (2017-10-29) 123 | 124 | 125 | ### Bug Fixes 126 | 127 | * Fix oblong collapse/expand button appearance on mobile safari ([62dfdec](https://github.com/fritz-c/react-sortable-tree/commit/62dfdec)) 128 | 129 | 130 | ### Features 131 | 132 | * enable the use of themes for simplified appearance customization ([d07c6a7](https://github.com/fritz-c/react-sortable-tree/commit/d07c6a7)) 133 | 134 | 135 | 136 | 137 | # [1.4.0](https://github.com/fritz-c/react-sortable-tree/compare/v1.3.1...v1.4.0) (2017-10-13) 138 | 139 | 140 | ### Features 141 | 142 | * Add path argument to onVisibilityToggle callback ([25cd134](https://github.com/fritz-c/react-sortable-tree/commit/25cd134)) 143 | 144 | 145 | 146 | 147 | ## [1.3.1](https://github.com/fritz-c/react-sortable-tree/compare/v1.3.0...v1.3.1) (2017-10-03) 148 | 149 | 150 | ### Bug Fixes 151 | 152 | * Allow react[@16](https://github.com/16) ([9a31a03](https://github.com/fritz-c/react-sortable-tree/commit/9a31a03)) 153 | 154 | 155 | 156 | 157 | # [1.3.0](https://github.com/fritz-c/react-sortable-tree/compare/v1.2.2...v1.3.0) (2017-09-20) 158 | 159 | 160 | ### Features 161 | 162 | * Provide more row parameters in rowHeight callback ([1b88b18](https://github.com/fritz-c/react-sortable-tree/commit/1b88b18)) 163 | 164 | 165 | 166 | 167 | ## [1.2.2](https://github.com/fritz-c/react-sortable-tree/compare/v1.2.1...v1.2.2) (2017-09-12) 168 | 169 | 170 | ### Bug Fixes 171 | 172 | * Specify version of react-dnd-html5-backend to avoid invalid package installs ([a09b611](https://github.com/fritz-c/react-sortable-tree/commit/a09b611)) 173 | 174 | 175 | 176 | 177 | ## [1.2.1](https://github.com/fritz-c/react-sortable-tree/compare/v1.2.0...v1.2.1) (2017-09-06) 178 | 179 | 180 | ### Bug Fixes 181 | 182 | * Allow children function in default renderer ([6f1dcac](https://github.com/fritz-c/react-sortable-tree/commit/6f1dcac)) 183 | 184 | 185 | 186 | 187 | # [1.2.0](https://github.com/fritz-c/react-sortable-tree/compare/v1.1.1...v1.2.0) (2017-08-12) 188 | 189 | 190 | ### Features 191 | 192 | * Add `shouldCopyOnOutsideDrop` prop to enable copying of nodes that leave the tree ([d6a9be9](https://github.com/fritz-c/react-sortable-tree/commit/d6a9be9)) 193 | 194 | 195 | 196 | 197 | ## [1.1.1](https://github.com/fritz-c/react-sortable-tree/compare/v1.1.0...v1.1.1) (2017-08-06) 198 | 199 | 200 | ### Bug Fixes 201 | 202 | * **tree-to-tree:** Fix node depth when dragging between trees ([323ccad](https://github.com/fritz-c/react-sortable-tree/commit/323ccad)) 203 | 204 | 205 | 206 | 207 | # [1.1.0](https://github.com/fritz-c/react-sortable-tree/compare/v1.0.0...v1.1.0) (2017-08-05) 208 | 209 | 210 | ### Features 211 | 212 | * **node-renderer:** Make title and subtitle insertable via props ([fff72c6](https://github.com/fritz-c/react-sortable-tree/commit/fff72c6)) 213 | 214 | 215 | 216 | 217 | # [1.0.0](https://github.com/fritz-c/react-sortable-tree/compare/v0.1.21...v1.0.0) (2017-08-05) 218 | 219 | 220 | ### Bug Fixes 221 | 222 | * External node offset was shifted ([d1ae0eb](https://github.com/fritz-c/react-sortable-tree/commit/d1ae0eb)) 223 | 224 | 225 | ### Code Refactoring 226 | 227 | * get rid of `dndWrapExternalSource` api ([d103e9f](https://github.com/fritz-c/react-sortable-tree/commit/d103e9f)) 228 | 229 | 230 | ### Features 231 | 232 | * **tree-to-tree:** Enable tree-to-tree drag-and-drop ([6986a23](https://github.com/fritz-c/react-sortable-tree/commit/6986a23)) 233 | * Display droppable placeholder element when tree is empty ([2cd371c](https://github.com/fritz-c/react-sortable-tree/commit/2cd371c)) 234 | * Add `prevPath` and `prevTreeIndex` to the `onMoveNode` callback ([6986a23](https://github.com/fritz-c/react-sortable-tree/commit/6986a23)) 235 | 236 | 237 | ### BREAKING CHANGES 238 | 239 | * Trees that are empty now display a placeholder element 240 | in their place instead of being simply empty. 241 | * `dndWrapExternalSource` api no longer exists. 242 | You can achieve the same functionality and more with react-dnd 243 | APIs, as demonstrated in the storybook example. 244 | 245 | 246 | 247 | 248 | ## [0.1.21](https://github.com/fritz-c/react-sortable-tree/compare/v0.1.20...v0.1.21) (2017-07-15) 249 | 250 | 251 | ### Bug Fixes 252 | 253 | * Remove console.log left in after development ([da27c47](https://github.com/fritz-c/react-sortable-tree/commit/da27c47)) 254 | 255 | 256 | 257 | See the GitHub [Releases](https://github.com/fritz-c/react-sortable-tree/releases) for information on updates. 258 | -------------------------------------------------------------------------------- /node_modules/react-sortable-tree/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Chris Fritz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /node_modules/react-sortable-tree/README.md: -------------------------------------------------------------------------------- 1 | # React Sortable Tree 2 | 3 | [![NPM](https://nodei.co/npm/react-sortable-tree.png)](https://npmjs.org/package/react-sortable-tree) 4 | 5 | [![tree200](https://cloud.githubusercontent.com/assets/4413963/18860410/26f64de8-84b8-11e6-9284-350308eed30a.png)](https://fritz-c.github.io/react-sortable-tree/) 6 | 7 | ### [Demo](https://fritz-c.github.io/react-sortable-tree/) 8 | 9 | [![demo](https://cloud.githubusercontent.com/assets/4413963/19334888/2be8261c-913a-11e6-9508-4b347ae114b4.gif)](https://fritz-c.github.io/react-sortable-tree/) 10 | 11 | ## Usage 12 | 13 | ```jsx 14 | import React, { Component } from 'react'; 15 | import SortableTree from 'react-sortable-tree'; 16 | import 'react-sortable-tree/style.css'; // This only needs to be imported once in your app 17 | 18 | export default class Tree extends Component { 19 | constructor(props) { 20 | super(props); 21 | 22 | this.state = { 23 | treeData: [{ title: 'Chicken', children: [{ title: 'Egg' }] }], 24 | }; 25 | } 26 | 27 | render() { 28 | return ( 29 |
30 | this.setState({ treeData })} 33 | /> 34 |
35 | ); 36 | } 37 | } 38 | ``` 39 | 40 | Find more examples in the [Storybook](https://fritz-c.github.io/react-sortable-tree/storybook/) 41 | 42 | Play with the code on an [example on CodeSandbox](https://codesandbox.io/s/wkxvy3z15w) 43 | 44 | ## Options 45 | 46 | | Prop | Type |
Description
| 47 | | :----------------------------- | :------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 48 | | treeData
_(required)_ | object[] | Tree data with the following keys:
`title` is the primary label for the node.
`subtitle` is a secondary label for the node.
`expanded` shows children of the node if true, or hides them if false. Defaults to false.
`children` is an array of child nodes belonging to the node.
**Example**: `[{title: 'main', subtitle: 'sub'}, { title: 'value2', expanded: true, children: [{ title: 'value3') }] }]` | 49 | | onChange
_(required)_ | func | Called whenever tree data changed. Just like with React input elements, you have to update your own component's data to see the changes reflected.
`( treeData: object[] ): void`
| 50 | | getNodeKey
_(recommended)_ | func | Specify the unique key used to identify each node and generate the `path` array passed in callbacks. With a setting of `getNodeKey={({ node }) => node.id}`, for example, in callbacks this will let you easily determine that the node with an `id` of `35` is (or has just become) a child of the node with an `id` of `12`, which is a child of ... and so on. It uses [`defaultGetNodeKey`](https://github.com/fritz-c/react-sortable-tree/blob/master/src/utils/default-handlers.js) by default, which returns the index in the tree (omitting hidden nodes).
`({ node: object, treeIndex: number }): string or number`
| 51 | | generateNodeProps | func | Generate an object with additional props to be passed to the node renderer. Use this for adding buttons via the `buttons` key, or additional `style` / `className` settings.
`({ node: object, path: number[] or string[], treeIndex: number, lowerSiblingCounts: number[], isSearchMatch: bool, isSearchFocus: bool }): object`
| 52 | | onMoveNode | func | Called after node move operation.
`({ treeData: object[], node: object, nextParentNode: object, prevPath: number[] or string[], prevTreeIndex: number, nextPath: number[] or string[], nextTreeIndex: number }): void`
| 53 | | onVisibilityToggle | func | Called after children nodes collapsed or expanded.
`({ treeData: object[], node: object, expanded: bool, path: number[] or string[] }): void`
| 54 | | onDragStateChanged | func | Called when a drag is initiated or ended.
`({ isDragging: bool, draggedNode: object }): void`
| 55 | | maxDepth | number | Maximum depth nodes can be inserted at. Defaults to infinite. | 56 | | canDrag | func or bool | Return false from callback to prevent node from dragging, by hiding the drag handle. Set prop to `false` to disable dragging on all nodes. Defaults to `true`.
`({ node: object, path: number[] or string[], treeIndex: number, lowerSiblingCounts: number[], isSearchMatch: bool, isSearchFocus: bool }): bool`
| 57 | | canDrop | func | Return false to prevent node from dropping in the given location.
`({ node: object, prevPath: number[] or string[], prevParent: object, prevTreeIndex: number, nextPath: number[] or string[], nextParent: object, nextTreeIndex: number }): bool`
| 58 | | theme | object | Set an all-in-one packaged appearance for the tree. See the [Themes](#themes) section for more information. | 59 | | searchMethod | func | The method used to search nodes. Defaults to [`defaultSearchMethod`](https://github.com/fritz-c/react-sortable-tree/blob/master/src/utils/default-handlers.js), which uses the `searchQuery` string to search for nodes with matching `title` or `subtitle` values. NOTE: Changing `searchMethod` will not update the search, but changing the `searchQuery` will.
`({ node: object, path: number[] or string[], treeIndex: number, searchQuery: any }): bool`
| 60 | | searchQuery | string or any | Used by the `searchMethod` to highlight and scroll to matched nodes. Should be a string for the default `searchMethod`, but can be anything when using a custom search. Defaults to `null`. | 61 | | searchFocusOffset | number | Outline the <`searchFocusOffset`>th node and scroll to it. | 62 | | searchFinishCallback | func | Get the nodes that match the search criteria. Used for counting total matches, etc.
`(matches: { node: object, path: number[] or string[], treeIndex: number }[]): void`
| 63 | | dndType | string | String value used by [react-dnd](http://react-dnd.github.io/react-dnd/docs-overview.html) (see overview at the link) for dropTargets and dragSources types. If not set explicitly, a default value is applied by react-sortable-tree for you for its internal use. **NOTE:** Must be explicitly set and the same value used in order for correct functioning of external nodes | 64 | | shouldCopyOnOutsideDrop | func or bool | Return true, or a callback returning true, and dropping nodes to react-dnd drop targets outside of the tree will not remove them from the tree. Defaults to `false`.
`({ node: object, prevPath: number[] or string[], prevTreeIndex: number, }): bool`
| 65 | | reactVirtualizedListProps | object | Custom properties to hand to the [react-virtualized list](https://github.com/bvaughn/react-virtualized/blob/master/docs/List.md#prop-types) | 66 | | style | object | Style applied to the container wrapping the tree (style defaults to `{height: '100%'}`) | 67 | | innerStyle | object | Style applied to the inner, scrollable container (for padding, etc.) | 68 | | className | string | Class name for the container wrapping the tree | 69 | | rowHeight | number or func | Used by react-virtualized. Defaults to `62`. Either a fixed row height (number) or a function that returns the height of a row given its index: `({ treeIndex: number, node: object, path: number[] or string[] }): number` | 70 | | slideRegionSize | number | Size in px of the region near the edges that initiates scrolling on dragover. Defaults to `100`. | 71 | | scaffoldBlockPxWidth | number | The width of the blocks containing the lines representing the structure of the tree. Defaults to `44`. | 72 | | isVirtualized | bool | Set to false to disable virtualization. Defaults to `true`. **NOTE**: Auto-scrolling while dragging, and scrolling to the `searchFocusOffset` will be disabled. | 73 | | nodeContentRenderer | any | Override the default component ([`NodeRendererDefault`](https://github.com/fritz-c/react-sortable-tree/blob/master/src/node-renderer-default.js)) for rendering nodes (but keep the scaffolding generator). This is a last resort for customization - most custom styling should be able to be solved with `generateNodeProps`, a `theme` or CSS rules. If you must use it, is best to copy the component in `node-renderer-default.js` to use as a base, and customize as needed. | 74 | | placeholderRenderer | any | Override the default placeholder component ([`PlaceholderRendererDefault`](https://github.com/fritz-c/react-sortable-tree/blob/master/src/placeholder-renderer-default.js)) which is displayed when the tree is empty. This is an advanced option, and in most cases should probably be solved with a `theme` or custom CSS instead. | 75 | 76 | ## Data Helper Functions 77 | 78 | Need a hand turning your flat data into nested tree data? 79 | Want to perform add/remove operations on the tree data without creating your own recursive function? 80 | Check out the helper functions exported from [`tree-data-utils.js`](https://github.com/fritz-c/react-sortable-tree/blob/master/src/utils/tree-data-utils.js). 81 | 82 | Notable among the available functions: 83 | 84 | * **`getTreeFromFlatData`**: Convert flat data (like that from a database) into nested tree data 85 | * **`getFlatDataFromTree`**: Convert tree data back to flat data 86 | * **`addNodeUnderParent`**: Add a node under the parent node at the given path 87 | * **`removeNode`**: For a given path, get the node at that path and the treeData with that node removed. 88 | * **`changeNodeAtPath`**: Modify the node object at the given path 89 | * **`map`**: Perform a change on every node in the tree 90 | * **`walk`**: Visit every node in the tree in order 91 | 92 | Documentation for each method is only available in the code at this time. You can also refer to the tests for simple usage examples. 93 | If your hobbies happen to include writing documentation, by all means submit a pull request. It would really help out. 94 | 95 | ## Themes 96 | 97 | Using the `theme` prop along with an imported theme module, you can easily override the default appearance with another standard one. 98 | 99 | ### Featured themes 100 | 101 | | ![File Explorer Theme](https://user-images.githubusercontent.com/4413963/32144502-1df1ae08-bcfd-11e7-8f63-8b836dace1a4.png) | Full Node Drag Theme | 102 | | :----------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------: | 103 | | **File Explorer** | **Full Node Drag** | 104 | | react-sortable-tree-theme-file-explorer | react-sortable-tree-theme-full-node-drag | 105 | | [Github](https://github.com/fritz-c/react-sortable-tree-theme-file-explorer) \| [NPM](https://www.npmjs.com/package/react-sortable-tree-theme-file-explorer) | [Github](https://github.com/fritz-c/react-sortable-tree-theme-full-node-drag) \| [NPM](https://www.npmjs.com/package/react-sortable-tree-theme-full-node-drag) | 106 | 107 | **Help Wanted** - As the themes feature has just been enabled, there are very few (only _two_ at the time of this writing) theme modules available. If you've customized the appearance of your tree to be especially cool or easy to use, I would be happy to feature it in this readme with a link to the Github repo and NPM page if you convert it to a theme. You can use my [file explorer theme repo](https://github.com/fritz-c/react-sortable-tree-theme-file-explorer) as a template to plug in your own stuff. 108 | 109 | ## Browser Compatibility 110 | 111 | | Browser | Works? | 112 | | :------ | :----- | 113 | | Chrome | Yes | 114 | | Firefox | Yes | 115 | | Safari | Yes | 116 | | IE 11 | Yes | 117 | 118 | ## Troubleshooting 119 | 120 | ### If it throws "TypeError: fn is not a function" errors in production 121 | 122 | This issue may be related to an ongoing incompatibility between UglifyJS and Webpack's behavior. See an explanation at [create-react-app#2376](https://github.com/facebookincubator/create-react-app/issues/2376). 123 | 124 | The simplest way to mitigate this issue is by adding `comparisons: false` to your Uglify config as seen here: https://github.com/facebookincubator/create-react-app/pull/2379/files 125 | 126 | ### If it doesn't work with other components that use react-dnd 127 | 128 | react-dnd only allows for one DragDropContext at a time (see: https://github.com/gaearon/react-dnd/issues/186). To get around this, you can import the context-less tree component via `SortableTreeWithoutDndContext`. 129 | 130 | ```js 131 | // before 132 | import SortableTree from 'react-sortable-tree'; 133 | 134 | // after 135 | import { SortableTreeWithoutDndContext as SortableTree } from 'react-sortable-tree'; 136 | ``` 137 | 138 | ## Contributing 139 | 140 | After cloning the repository and running `npm install` inside, you can use the following commands to develop and build the project. 141 | 142 | ```sh 143 | # Starts a webpack dev server that hosts a demo page with the component. 144 | # It uses react-hot-loader so changes are reflected on save. 145 | npm start 146 | 147 | # Start the storybook, which has several different examples to play with. 148 | # Also hot-reloaded. 149 | npm run storybook 150 | 151 | # Runs the library tests 152 | npm test 153 | 154 | # Lints the code with eslint 155 | npm run lint 156 | 157 | # Lints and builds the code, placing the result in the dist directory. 158 | # This build is necessary to reflect changes if you're 159 | # `npm link`-ed to this repository from another local project. 160 | npm run build 161 | ``` 162 | 163 | Pull requests are welcome! 164 | 165 | ## License 166 | 167 | MIT 168 | -------------------------------------------------------------------------------- /node_modules/react-sortable-tree/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "_args": [ 3 | [ 4 | "react-sortable-tree@2.0.1", 5 | "/home/lx/Desktop/SENIOR_PROJECT/apjs" 6 | ] 7 | ], 8 | "_from": "react-sortable-tree@2.0.1", 9 | "_id": "react-sortable-tree@2.0.1", 10 | "_inBundle": false, 11 | "_integrity": "sha512-exq0I9nXvLwpRwK+kTc3zAsfX/lxb5vYtScDXuveI2SvTCpLs7MevCLfB3NFik0y2QP72zPsZkvomz9cE31aHA==", 12 | "_location": "/react-sortable-tree", 13 | "_phantomChildren": {}, 14 | "_requested": { 15 | "type": "version", 16 | "registry": true, 17 | "raw": "react-sortable-tree@2.0.1", 18 | "name": "react-sortable-tree", 19 | "escapedName": "react-sortable-tree", 20 | "rawSpec": "2.0.1", 21 | "saveSpec": null, 22 | "fetchSpec": "2.0.1" 23 | }, 24 | "_requiredBy": [ 25 | "/" 26 | ], 27 | "_resolved": "https://registry.npmjs.org/react-sortable-tree/-/react-sortable-tree-2.0.1.tgz", 28 | "_spec": "2.0.1", 29 | "_where": "/home/lx/Desktop/SENIOR_PROJECT/apjs", 30 | "authors": [ 31 | "Chris Fritz" 32 | ], 33 | "browserslist": [ 34 | "IE 11", 35 | "last 2 versions", 36 | "> 1%" 37 | ], 38 | "bugs": { 39 | "url": "https://github.com/fritz-c/react-sortable-tree/issues" 40 | }, 41 | "dependencies": { 42 | "lodash.isequal": "^4.4.0", 43 | "prop-types": "^15.6.0", 44 | "react-dnd": "2.5.4", 45 | "react-dnd-html5-backend": "2.5.4", 46 | "react-dnd-scrollzone": "^4.0.0", 47 | "react-virtualized": "^9.18.5" 48 | }, 49 | "description": "Drag-and-drop sortable component for nested data and hierarchies", 50 | "devDependencies": { 51 | "@storybook/addon-options": "^3.3.12", 52 | "@storybook/addon-storyshots": "^3.3.12", 53 | "@storybook/react": "^3.3.12", 54 | "autoprefixer": "^7.2.6", 55 | "babel-cli": "^6.26.0", 56 | "babel-core": "^6.26.0", 57 | "babel-eslint": "^8.2.1", 58 | "babel-jest": "^22.2.2", 59 | "babel-loader": "^7.1.2", 60 | "babel-plugin-transform-object-rest-spread": "^6.26.0", 61 | "babel-polyfill": "^6.26.0", 62 | "babel-preset-env": "^1.6.1", 63 | "babel-preset-react": "^6.11.1", 64 | "codesandbox": "^1.1.14", 65 | "cross-env": "^5.1.3", 66 | "css-loader": "^0.28.9", 67 | "enzyme": "^3.3.0", 68 | "enzyme-adapter-react-16": "^1.1.1", 69 | "eslint": "^4.17.0", 70 | "eslint-config-airbnb": "^16.1.0", 71 | "eslint-config-prettier": "^2.9.0", 72 | "eslint-loader": "^1.9.0", 73 | "eslint-plugin-import": "^2.8.0", 74 | "eslint-plugin-jsx-a11y": "^6.0.3", 75 | "eslint-plugin-react": "^7.6.1", 76 | "extract-text-webpack-plugin": "^3.0.2", 77 | "file-loader": "^1.1.6", 78 | "gh-pages": "^1.1.0", 79 | "html-webpack-plugin": "^2.30.1", 80 | "jest": "^22.2.2", 81 | "jest-enzyme": "^4.2.0", 82 | "json-loader": "^0.5.4", 83 | "postcss-loader": "^2.1.0", 84 | "prettier": "^1.10.2", 85 | "react": "^16.2.0", 86 | "react-addons-shallow-compare": "^15.6.2", 87 | "react-dnd-test-backend": "^2.5.4", 88 | "react-dnd-touch-backend": "^0.3.20", 89 | "react-dom": "^16.2.0", 90 | "react-hot-loader": "^3.1.3", 91 | "react-sortable-tree-theme-file-explorer": "^1.1.2", 92 | "react-test-renderer": "^16.2.0", 93 | "rimraf": "^2.6.2", 94 | "style-loader": "^0.20.1", 95 | "webpack": "^3.10.0", 96 | "webpack-dev-server": "^2.11.1", 97 | "webpack-node-externals": "^1.6.0" 98 | }, 99 | "files": [ 100 | "dist", 101 | "style.css", 102 | "style.css.map" 103 | ], 104 | "homepage": "https://fritz-c.github.io/react-sortable-tree", 105 | "jest": { 106 | "setupTestFrameworkScriptFile": "./node_modules/jest-enzyme/lib/index.js", 107 | "setupFiles": [ 108 | "./test-config/shim.js", 109 | "./test-config/test-setup.js" 110 | ], 111 | "moduleFileExtensions": [ 112 | "js", 113 | "jsx", 114 | "json" 115 | ], 116 | "moduleDirectories": [ 117 | "node_modules" 118 | ], 119 | "moduleNameMapper": { 120 | "\\.(css|jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/__mocks__/fileMock.js" 121 | } 122 | }, 123 | "keywords": [ 124 | "react", 125 | "react-component" 126 | ], 127 | "license": "MIT", 128 | "main": "dist/main.js", 129 | "name": "react-sortable-tree", 130 | "peerDependencies": { 131 | "react": "^15.3.0 || ^16.0.0", 132 | "react-dom": "^15.3.0 || ^16.0.0" 133 | }, 134 | "repository": { 135 | "type": "git", 136 | "url": "git+https://github.com/fritz-c/react-sortable-tree.git" 137 | }, 138 | "scripts": { 139 | "build": "npm run clean && cross-env NODE_ENV=production TARGET=umd webpack --bail", 140 | "build-storybook": "cross-env NODE_ENV=production TARGET=demo build-storybook -o build/storybook", 141 | "build:demo": "npm run clean:demo && cross-env NODE_ENV=production TARGET=demo webpack --bail && npm run build-storybook", 142 | "clean": "rimraf dist style.css style.css.map", 143 | "clean:demo": "rimraf build", 144 | "deploy": "npm run build:demo && gh-pages -d build", 145 | "lint": "eslint src examples", 146 | "prepublishOnly": "npm run lint && npm run test && npm run build", 147 | "prettier": "prettier --single-quote --trailing-comma es5 --write \"{src,examples}/**/*.{js,css,md}\"", 148 | "start": "cross-env NODE_ENV=development TARGET=development webpack-dev-server --inline --hot", 149 | "storybook": "cross-env TARGET=development start-storybook -p ${PORT:-3001} -h 0.0.0.0", 150 | "test": "jest", 151 | "test:coverage": "jest --coverage && ./cc-test-reporter after-build --id=\"d957b5ff2f78e200b7cfa4e294ac1fe52c823c7d7c327628f897271dc72a874e\"", 152 | "test:watch": "jest --watchAll" 153 | }, 154 | "version": "2.0.1" 155 | } 156 | -------------------------------------------------------------------------------- /node_modules/react-sortable-tree/style.css: -------------------------------------------------------------------------------- 1 | /* Collection default theme */ 2 | 3 | .ReactVirtualized__Collection { 4 | } 5 | 6 | .ReactVirtualized__Collection__innerScrollContainer { 7 | } 8 | 9 | /* Grid default theme */ 10 | 11 | .ReactVirtualized__Grid { 12 | } 13 | 14 | .ReactVirtualized__Grid__innerScrollContainer { 15 | } 16 | 17 | /* Table default theme */ 18 | 19 | .ReactVirtualized__Table { 20 | } 21 | 22 | .ReactVirtualized__Table__Grid { 23 | } 24 | 25 | .ReactVirtualized__Table__headerRow { 26 | font-weight: 700; 27 | text-transform: uppercase; 28 | display: -webkit-box; 29 | display: -ms-flexbox; 30 | display: flex; 31 | -webkit-box-orient: horizontal; 32 | -webkit-box-direction: normal; 33 | -ms-flex-direction: row; 34 | flex-direction: row; 35 | -webkit-box-align: center; 36 | -ms-flex-align: center; 37 | align-items: center; 38 | } 39 | .ReactVirtualized__Table__row { 40 | display: -webkit-box; 41 | display: -ms-flexbox; 42 | display: flex; 43 | -webkit-box-orient: horizontal; 44 | -webkit-box-direction: normal; 45 | -ms-flex-direction: row; 46 | flex-direction: row; 47 | -webkit-box-align: center; 48 | -ms-flex-align: center; 49 | align-items: center; 50 | } 51 | 52 | .ReactVirtualized__Table__headerTruncatedText { 53 | display: inline-block; 54 | max-width: 100%; 55 | white-space: nowrap; 56 | text-overflow: ellipsis; 57 | overflow: hidden; 58 | } 59 | 60 | .ReactVirtualized__Table__headerColumn, 61 | .ReactVirtualized__Table__rowColumn { 62 | margin-right: 10px; 63 | min-width: 0px; 64 | } 65 | .ReactVirtualized__Table__rowColumn { 66 | text-overflow: ellipsis; 67 | white-space: nowrap; 68 | } 69 | 70 | .ReactVirtualized__Table__headerColumn:first-of-type, 71 | .ReactVirtualized__Table__rowColumn:first-of-type { 72 | margin-left: 10px; 73 | } 74 | .ReactVirtualized__Table__sortableHeaderColumn { 75 | cursor: pointer; 76 | } 77 | 78 | .ReactVirtualized__Table__sortableHeaderIconContainer { 79 | display: -webkit-box; 80 | display: -ms-flexbox; 81 | display: flex; 82 | -webkit-box-align: center; 83 | -ms-flex-align: center; 84 | align-items: center; 85 | } 86 | .ReactVirtualized__Table__sortableHeaderIcon { 87 | -webkit-box-flex: 0; 88 | -ms-flex: 0 0 24px; 89 | flex: 0 0 24px; 90 | height: 1em; 91 | width: 1em; 92 | fill: currentColor; 93 | } 94 | 95 | /* List default theme */ 96 | 97 | .ReactVirtualized__List { 98 | }.rst__node { 99 | min-width: 100%; 100 | white-space: nowrap; 101 | position: relative; 102 | text-align: left; 103 | } 104 | 105 | .rst__nodeContent { 106 | position: absolute; 107 | top: 0; 108 | bottom: 0; 109 | } 110 | 111 | /* ========================================================================== 112 | Scaffold 113 | 114 | Line-overlaid blocks used for showing the tree structure 115 | ========================================================================== */ 116 | .rst__lineBlock, 117 | .rst__absoluteLineBlock { 118 | height: 100%; 119 | position: relative; 120 | display: inline-block; 121 | } 122 | 123 | .rst__absoluteLineBlock { 124 | position: absolute; 125 | top: 0; 126 | } 127 | 128 | .rst__lineHalfHorizontalRight::before, 129 | .rst__lineFullVertical::after, 130 | .rst__lineHalfVerticalTop::after, 131 | .rst__lineHalfVerticalBottom::after { 132 | position: absolute; 133 | content: ''; 134 | background-color: black; 135 | } 136 | 137 | /** 138 | * +-----+ 139 | * | | 140 | * | +--+ 141 | * | | 142 | * +-----+ 143 | */ 144 | .rst__lineHalfHorizontalRight::before { 145 | height: 1px; 146 | top: 50%; 147 | right: 0; 148 | width: 50%; 149 | } 150 | 151 | /** 152 | * +--+--+ 153 | * | | | 154 | * | | | 155 | * | | | 156 | * +--+--+ 157 | */ 158 | .rst__lineFullVertical::after, 159 | .rst__lineHalfVerticalTop::after, 160 | .rst__lineHalfVerticalBottom::after { 161 | width: 1px; 162 | left: 50%; 163 | top: 0; 164 | height: 100%; 165 | } 166 | 167 | /** 168 | * +-----+ 169 | * | | | 170 | * | + | 171 | * | | 172 | * +-----+ 173 | */ 174 | .rst__lineHalfVerticalTop::after { 175 | height: 50%; 176 | } 177 | 178 | /** 179 | * +-----+ 180 | * | | 181 | * | + | 182 | * | | | 183 | * +-----+ 184 | */ 185 | .rst__lineHalfVerticalBottom::after { 186 | top: auto; 187 | bottom: 0; 188 | height: 50%; 189 | } 190 | 191 | /* Highlight line for pointing to dragged row destination 192 | ========================================================================== */ 193 | /** 194 | * +--+--+ 195 | * | | | 196 | * | | | 197 | * | | | 198 | * +--+--+ 199 | */ 200 | .rst__highlightLineVertical { 201 | z-index: 3; 202 | } 203 | .rst__highlightLineVertical::before { 204 | position: absolute; 205 | content: ''; 206 | background-color: #36c2f6; 207 | width: 8px; 208 | margin-left: -4px; 209 | left: 50%; 210 | top: 0; 211 | height: 100%; 212 | } 213 | @-webkit-keyframes arrow-pulse { 214 | 0% { 215 | -webkit-transform: translate(0, 0); 216 | transform: translate(0, 0); 217 | opacity: 0; 218 | } 219 | 30% { 220 | -webkit-transform: translate(0, 300%); 221 | transform: translate(0, 300%); 222 | opacity: 1; 223 | } 224 | 70% { 225 | -webkit-transform: translate(0, 700%); 226 | transform: translate(0, 700%); 227 | opacity: 1; 228 | } 229 | 100% { 230 | -webkit-transform: translate(0, 1000%); 231 | transform: translate(0, 1000%); 232 | opacity: 0; 233 | } 234 | } 235 | @keyframes arrow-pulse { 236 | 0% { 237 | -webkit-transform: translate(0, 0); 238 | transform: translate(0, 0); 239 | opacity: 0; 240 | } 241 | 30% { 242 | -webkit-transform: translate(0, 300%); 243 | transform: translate(0, 300%); 244 | opacity: 1; 245 | } 246 | 70% { 247 | -webkit-transform: translate(0, 700%); 248 | transform: translate(0, 700%); 249 | opacity: 1; 250 | } 251 | 100% { 252 | -webkit-transform: translate(0, 1000%); 253 | transform: translate(0, 1000%); 254 | opacity: 0; 255 | } 256 | } 257 | .rst__highlightLineVertical::after { 258 | content: ''; 259 | position: absolute; 260 | height: 0; 261 | margin-left: -4px; 262 | left: 50%; 263 | top: 0; 264 | border-left: 4px solid transparent; 265 | border-right: 4px solid transparent; 266 | border-top: 4px solid white; 267 | -webkit-animation: arrow-pulse 1s infinite linear both; 268 | animation: arrow-pulse 1s infinite linear both; 269 | } 270 | 271 | /** 272 | * +-----+ 273 | * | | 274 | * | +--+ 275 | * | | | 276 | * +--+--+ 277 | */ 278 | .rst__highlightTopLeftCorner::before { 279 | z-index: 3; 280 | content: ''; 281 | position: absolute; 282 | border-top: solid 8px #36c2f6; 283 | border-left: solid 8px #36c2f6; 284 | -webkit-box-sizing: border-box; 285 | box-sizing: border-box; 286 | height: calc(50% + 4px); 287 | top: 50%; 288 | margin-top: -4px; 289 | right: 0; 290 | width: calc(50% + 4px); 291 | } 292 | 293 | /** 294 | * +--+--+ 295 | * | | | 296 | * | | | 297 | * | +->| 298 | * +-----+ 299 | */ 300 | .rst__highlightBottomLeftCorner { 301 | z-index: 3; 302 | } 303 | .rst__highlightBottomLeftCorner::before { 304 | content: ''; 305 | position: absolute; 306 | border-bottom: solid 8px #36c2f6; 307 | border-left: solid 8px #36c2f6; 308 | -webkit-box-sizing: border-box; 309 | box-sizing: border-box; 310 | height: calc(100% + 4px); 311 | top: 0; 312 | right: 12px; 313 | width: calc(50% - 8px); 314 | } 315 | .rst__highlightBottomLeftCorner::after { 316 | content: ''; 317 | position: absolute; 318 | height: 0; 319 | right: 0; 320 | top: 100%; 321 | margin-top: -12px; 322 | border-top: 12px solid transparent; 323 | border-bottom: 12px solid transparent; 324 | border-left: 12px solid #36c2f6; 325 | } 326 | .rst__rowWrapper { 327 | padding: 10px 10px 10px 0; 328 | height: 100%; 329 | -webkit-box-sizing: border-box; 330 | box-sizing: border-box; 331 | } 332 | 333 | .rst__row { 334 | height: 100%; 335 | white-space: nowrap; 336 | display: -webkit-box; 337 | display: -ms-flexbox; 338 | display: flex; 339 | } 340 | .rst__row > * { 341 | -webkit-box-sizing: border-box; 342 | box-sizing: border-box; 343 | } 344 | 345 | /** 346 | * The outline of where the element will go if dropped, displayed while dragging 347 | */ 348 | .rst__rowLandingPad, 349 | .rst__rowCancelPad { 350 | border: none !important; 351 | -webkit-box-shadow: none !important; 352 | box-shadow: none !important; 353 | outline: none !important; 354 | } 355 | .rst__rowLandingPad > *, 356 | .rst__rowCancelPad > * { 357 | opacity: 0 !important; 358 | } 359 | .rst__rowLandingPad::before, 360 | .rst__rowCancelPad::before { 361 | background-color: lightblue; 362 | border: 3px dashed white; 363 | content: ''; 364 | position: absolute; 365 | top: 0; 366 | right: 0; 367 | bottom: 0; 368 | left: 0; 369 | z-index: -1; 370 | } 371 | 372 | /** 373 | * Alternate appearance of the landing pad when the dragged location is invalid 374 | */ 375 | .rst__rowCancelPad::before { 376 | background-color: #e6a8ad; 377 | } 378 | 379 | /** 380 | * Nodes matching the search conditions are highlighted 381 | */ 382 | .rst__rowSearchMatch { 383 | outline: solid 3px #0080ff; 384 | } 385 | 386 | /** 387 | * The node that matches the search conditions and is currently focused 388 | */ 389 | .rst__rowSearchFocus { 390 | outline: solid 3px #fc6421; 391 | } 392 | 393 | .rst__rowContents, 394 | .rst__rowLabel, 395 | .rst__rowToolbar, 396 | .rst__moveHandle, 397 | .rst__toolbarButton { 398 | display: inline-block; 399 | vertical-align: middle; 400 | } 401 | 402 | .rst__rowContents { 403 | position: relative; 404 | height: 100%; 405 | border: solid #bbb 1px; 406 | border-left: none; 407 | -webkit-box-shadow: 0 2px 2px -2px; 408 | box-shadow: 0 2px 2px -2px; 409 | padding: 25px 10px; 410 | border-radius: 10px; 411 | min-width: 230px; 412 | -webkit-box-flex: 1; 413 | -ms-flex: 1 0 auto; 414 | flex: 1 0 auto; 415 | display: -webkit-box; 416 | display: -ms-flexbox; 417 | display: flex; 418 | -webkit-box-align: center; 419 | -ms-flex-align: center; 420 | align-items: center; 421 | -webkit-box-pack: justify; 422 | -ms-flex-pack: justify; 423 | justify-content: space-between; 424 | background-color: rgba(1,1,1,.1); 425 | } 426 | 427 | .rst__rowContentsDragDisabled { 428 | border-left: solid #bbb 1px; 429 | } 430 | 431 | .rst__rowLabel { 432 | -webkit-box-flex: 0; 433 | -ms-flex: 0 1 auto; 434 | flex: 0 1 auto; 435 | padding-right: 20px; 436 | } 437 | 438 | .rst__rowToolbar { 439 | -webkit-box-flex: 0; 440 | -ms-flex: 0 1 auto; 441 | flex: 0 1 auto; 442 | display: -webkit-box; 443 | display: -ms-flexbox; 444 | display: flex; 445 | } 446 | 447 | .rst__moveHandle, 448 | .rst__loadingHandle { 449 | height: 100%; 450 | width: 44px; 451 | background: #424242 452 | url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MiIgaGVpZ2h0PSI0MiI+PGcgc3Ryb2tlPSIjRkZGIiBzdHJva2Utd2lkdGg9IjIuOSIgPjxwYXRoIGQ9Ik0xNCAxNS43aDE0LjQiLz48cGF0aCBkPSJNMTQgMjEuNGgxNC40Ii8+PHBhdGggZD0iTTE0IDI3LjFoMTQuNCIvPjwvZz4KPC9zdmc+') 453 | no-repeat center; 454 | border: solid #aaa 1px; 455 | -webkit-box-shadow: 0 2px 2px -2px; 456 | box-shadow: 0 2px 2px -2px; 457 | cursor: move; 458 | border-radius: 10px; 459 | z-index: 1; 460 | padding: 25px 25px; 461 | } 462 | 463 | .rst__loadingHandle { 464 | cursor: default; 465 | background: #d9d9d9; 466 | } 467 | 468 | @-webkit-keyframes pointFade { 469 | 0%, 470 | 19.999%, 471 | 100% { 472 | opacity: 0; 473 | } 474 | 20% { 475 | opacity: 1; 476 | } 477 | } 478 | 479 | @keyframes pointFade { 480 | 0%, 481 | 19.999%, 482 | 100% { 483 | opacity: 0; 484 | } 485 | 20% { 486 | opacity: 1; 487 | } 488 | } 489 | 490 | .rst__loadingCircle { 491 | width: 80%; 492 | height: 80%; 493 | margin: 10%; 494 | position: relative; 495 | } 496 | 497 | .rst__loadingCirclePoint { 498 | width: 100%; 499 | height: 100%; 500 | position: absolute; 501 | left: 0; 502 | top: 0; 503 | } 504 | .rst__loadingCirclePoint::before { 505 | content: ''; 506 | display: block; 507 | margin: 0 auto; 508 | width: 11%; 509 | height: 30%; 510 | background-color: #fff; 511 | border-radius: 30%; 512 | -webkit-animation: pointFade 800ms infinite ease-in-out both; 513 | animation: pointFade 800ms infinite ease-in-out both; 514 | } 515 | .rst__loadingCirclePoint:nth-of-type(1) { 516 | -webkit-transform: rotate(0deg); 517 | transform: rotate(0deg); 518 | } 519 | .rst__loadingCirclePoint:nth-of-type(7) { 520 | -webkit-transform: rotate(180deg); 521 | transform: rotate(180deg); 522 | } 523 | .rst__loadingCirclePoint:nth-of-type(1)::before, 524 | .rst__loadingCirclePoint:nth-of-type(7)::before { 525 | -webkit-animation-delay: -800ms; 526 | animation-delay: -800ms; 527 | } 528 | .rst__loadingCirclePoint:nth-of-type(2) { 529 | -webkit-transform: rotate(30deg); 530 | transform: rotate(30deg); 531 | } 532 | .rst__loadingCirclePoint:nth-of-type(8) { 533 | -webkit-transform: rotate(210deg); 534 | transform: rotate(210deg); 535 | } 536 | .rst__loadingCirclePoint:nth-of-type(2)::before, 537 | .rst__loadingCirclePoint:nth-of-type(8)::before { 538 | -webkit-animation-delay: -666ms; 539 | animation-delay: -666ms; 540 | } 541 | .rst__loadingCirclePoint:nth-of-type(3) { 542 | -webkit-transform: rotate(60deg); 543 | transform: rotate(60deg); 544 | } 545 | .rst__loadingCirclePoint:nth-of-type(9) { 546 | -webkit-transform: rotate(240deg); 547 | transform: rotate(240deg); 548 | } 549 | .rst__loadingCirclePoint:nth-of-type(3)::before, 550 | .rst__loadingCirclePoint:nth-of-type(9)::before { 551 | -webkit-animation-delay: -533ms; 552 | animation-delay: -533ms; 553 | } 554 | .rst__loadingCirclePoint:nth-of-type(4) { 555 | -webkit-transform: rotate(90deg); 556 | transform: rotate(90deg); 557 | } 558 | .rst__loadingCirclePoint:nth-of-type(10) { 559 | -webkit-transform: rotate(270deg); 560 | transform: rotate(270deg); 561 | } 562 | .rst__loadingCirclePoint:nth-of-type(4)::before, 563 | .rst__loadingCirclePoint:nth-of-type(10)::before { 564 | -webkit-animation-delay: -400ms; 565 | animation-delay: -400ms; 566 | } 567 | .rst__loadingCirclePoint:nth-of-type(5) { 568 | -webkit-transform: rotate(120deg); 569 | transform: rotate(120deg); 570 | } 571 | .rst__loadingCirclePoint:nth-of-type(11) { 572 | -webkit-transform: rotate(300deg); 573 | transform: rotate(300deg); 574 | } 575 | .rst__loadingCirclePoint:nth-of-type(5)::before, 576 | .rst__loadingCirclePoint:nth-of-type(11)::before { 577 | -webkit-animation-delay: -266ms; 578 | animation-delay: -266ms; 579 | } 580 | .rst__loadingCirclePoint:nth-of-type(6) { 581 | -webkit-transform: rotate(150deg); 582 | transform: rotate(150deg); 583 | } 584 | .rst__loadingCirclePoint:nth-of-type(12) { 585 | -webkit-transform: rotate(330deg); 586 | transform: rotate(330deg); 587 | } 588 | .rst__loadingCirclePoint:nth-of-type(6)::before, 589 | .rst__loadingCirclePoint:nth-of-type(12)::before { 590 | -webkit-animation-delay: -133ms; 591 | animation-delay: -133ms; 592 | } 593 | .rst__loadingCirclePoint:nth-of-type(7) { 594 | -webkit-transform: rotate(180deg); 595 | transform: rotate(180deg); 596 | } 597 | .rst__loadingCirclePoint:nth-of-type(13) { 598 | -webkit-transform: rotate(360deg); 599 | transform: rotate(360deg); 600 | } 601 | .rst__loadingCirclePoint:nth-of-type(7)::before, 602 | .rst__loadingCirclePoint:nth-of-type(13)::before { 603 | -webkit-animation-delay: 0ms; 604 | animation-delay: 0ms; 605 | } 606 | 607 | .rst__rowTitle { 608 | font-weight: bold; 609 | } 610 | 611 | .rst__rowTitleWithSubtitle { 612 | font-size: 85%; 613 | display: block; 614 | height: 0.8rem; 615 | } 616 | 617 | .rst__rowSubtitle { 618 | font-size: 70%; 619 | line-height: 1; 620 | } 621 | 622 | .rst__collapseButton, 623 | .rst__expandButton { 624 | -webkit-appearance: none; 625 | -moz-appearance: none; 626 | appearance: none; 627 | border: none; 628 | position: absolute; 629 | border-radius: 100%; 630 | -webkit-box-shadow: 0 0 0 1px #000; 631 | box-shadow: 0 0 0 1px #000; 632 | width: 16px; 633 | height: 16px; 634 | padding: 0; 635 | top: 50%; 636 | -webkit-transform: translate(-50%, -50%); 637 | transform: translate(-50%, -50%); 638 | cursor: pointer; 639 | } 640 | .rst__collapseButton:focus, 641 | .rst__expandButton:focus { 642 | outline: none; 643 | -webkit-box-shadow: 0 0 0 1px #000, 0 0 1px 3px #83bef9; 644 | box-shadow: 0 0 0 1px #000, 0 0 1px 3px #83bef9; 645 | } 646 | .rst__collapseButton:hover:not(:active), 647 | .rst__expandButton:hover:not(:active) { 648 | background-size: 24px; 649 | height: 20px; 650 | width: 20px; 651 | } 652 | 653 | .rst__collapseButton { 654 | background: #fff 655 | url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxOCIgaGVpZ2h0PSIxOCI+PGNpcmNsZSBjeD0iOSIgY3k9IjkiIHI9IjgiIGZpbGw9IiNGRkYiLz48ZyBzdHJva2U9IiM5ODk4OTgiIHN0cm9rZS13aWR0aD0iMS45IiA+PHBhdGggZD0iTTQuNSA5aDkiLz48L2c+Cjwvc3ZnPg==') 656 | no-repeat center; 657 | } 658 | 659 | .rst__expandButton { 660 | background: #fff 661 | url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxOCIgaGVpZ2h0PSIxOCI+PGNpcmNsZSBjeD0iOSIgY3k9IjkiIHI9IjgiIGZpbGw9IiNGRkYiLz48ZyBzdHJva2U9IiM5ODk4OTgiIHN0cm9rZS13aWR0aD0iMS45IiA+PHBhdGggZD0iTTQuNSA5aDkiLz48cGF0aCBkPSJNOSA0LjV2OSIvPjwvZz4KPC9zdmc+') 662 | no-repeat center; 663 | } 664 | 665 | /** 666 | * Line for under a node with children 667 | */ 668 | .rst__lineChildren { 669 | height: 100%; 670 | display: inline-block; 671 | position: absolute; 672 | } 673 | .rst__lineChildren::after { 674 | content: ''; 675 | position: absolute; 676 | background-color: black; 677 | width: 1px; 678 | left: 50%; 679 | bottom: 0; 680 | height: 10px; 681 | } 682 | .rst__placeholder { 683 | position: relative; 684 | height: 68px; 685 | max-width: 300px; 686 | padding: 10px; 687 | } 688 | .rst__placeholder, 689 | .rst__placeholder > * { 690 | -webkit-box-sizing: border-box; 691 | box-sizing: border-box; 692 | } 693 | .rst__placeholder::before { 694 | border: 3px dashed #d9d9d9; 695 | content: ''; 696 | position: absolute; 697 | top: 5px; 698 | right: 5px; 699 | bottom: 5px; 700 | left: 5px; 701 | z-index: -1; 702 | } 703 | 704 | /** 705 | * The outline of where the element will go if dropped, displayed while dragging 706 | */ 707 | .rst__placeholderLandingPad, 708 | .rst__placeholderCancelPad { 709 | border: none !important; 710 | -webkit-box-shadow: none !important; 711 | box-shadow: none !important; 712 | outline: none !important; 713 | } 714 | .rst__placeholderLandingPad *, 715 | .rst__placeholderCancelPad * { 716 | opacity: 0 !important; 717 | } 718 | .rst__placeholderLandingPad::before, 719 | .rst__placeholderCancelPad::before { 720 | background-color: lightblue; 721 | border-color: white; 722 | } 723 | 724 | /** 725 | * Alternate appearance of the landing pad when the dragged location is invalid 726 | */ 727 | .rst__placeholderCancelPad::before { 728 | background-color: #e6a8ad; 729 | } 730 | /** 731 | * Extra class applied to VirtualScroll through className prop 732 | */ 733 | .rst__virtualScrollOverride { 734 | overflow: auto !important; 735 | } 736 | .rst__virtualScrollOverride * { 737 | -webkit-box-sizing: border-box; 738 | box-sizing: border-box; 739 | } 740 | 741 | .ReactVirtualized__Grid__innerScrollContainer { 742 | overflow: visible !important; 743 | } 744 | 745 | .ReactVirtualized__Grid { 746 | outline: none; 747 | } 748 | 749 | /*# sourceMappingURL=style.css.map*/ 750 | -------------------------------------------------------------------------------- /node_modules/react-sortable-tree/style.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":[],"names":[],"mappings":"","file":"style.css","sourceRoot":""} -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-env", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --hot --host=0.0.0.0", 8 | "test": "jest", 9 | "build": "webpack" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "babel-core": "^6.26.0", 15 | "babel-loader": "^7.1.2", 16 | "babel-plugin-transform-class-properties": "^6.24.1", 17 | "babel-preset-env": "^1.6.1", 18 | "babel-preset-react": "^6.24.1", 19 | "css-loader": "^0.28.9", 20 | "html-webpack-plugin": "^3.0.6", 21 | "jszip": "^3.1.5", 22 | "material-ui": "^0.20.0", 23 | "material-ui-icons": "^1.0.0-beta.36", 24 | "normalize.css": "^8.0.0", 25 | "react": "^16.2.0", 26 | "react-dom": "^16.2.0", 27 | "react-ga": "^2.4.1", 28 | "react-router-dom": "^4.2.2", 29 | "react-sortable-tree": "^2.0.1", 30 | "style-loader": "^0.20.2", 31 | "webpack": "^3.11.0", 32 | "webpack-dev-server": "^2.11.1" 33 | }, 34 | "devDependencies": { 35 | "babel-plugin-transform-object-rest-spread": "^6.26.0", 36 | "babel-preset-es2015": "^6.24.1", 37 | "jest": "^22.4.2" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/components/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | //react-router-dom is react-router for browsers. 3 | import { BrowserRouter as Router, Route, Link } from "react-router-dom"; 4 | import ReactTree from './react-tree'; 5 | import ReduxTree from './redux-tree'; 6 | import ReactGA from 'react-ga'; 7 | import NotFound from './NotFound'; 8 | ReactGA.initialize('UA-116211451-1'); 9 | ReactGA.pageview(window.location.pathname + window.location.search); 10 | class App extends Component { 11 | render () { 12 | return ( 13 | 14 |
15 | 16 | 17 | 18 |
19 |
20 | ) 21 | } 22 | } 23 | 24 | export default App; 25 | -------------------------------------------------------------------------------- /src/components/NotFound.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const NotFound = () => ( 4 |
5 |

This isn't the page you were looking for...

6 | 7 |
8 | ); 9 | 10 | export default NotFound; 11 | -------------------------------------------------------------------------------- /src/components/react-interface.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import { Link } from "react-router-dom"; 3 | import AppBar from 'material-ui/AppBar'; 4 | import FlatButton from 'material-ui/FlatButton'; 5 | import {Card, CardActions} from 'material-ui/Card'; 6 | import Drawer from 'material-ui/Drawer'; 7 | import RaisedButton from 'material-ui/RaisedButton'; 8 | import TextField from 'material-ui/TextField'; 9 | import IconButton from 'material-ui/IconButton'; 10 | import Menu from 'material-ui/svg-icons/navigation/menu'; 11 | import Avatar from 'material-ui/Avatar'; 12 | import Chip from 'material-ui/Chip'; 13 | import FileDownload from 'material-ui/svg-icons/file/file-download'; 14 | import Settings from 'material-ui/svg-icons/action/settings'; 15 | import Dialog from 'material-ui/Dialog'; 16 | import {cyan200, cyan800, grey900, white} from 'material-ui/styles/colors'; 17 | 18 | const styles = { 19 | chip: { 20 | margin: 4, 21 | }, 22 | wrapper: { 23 | display: 'flex', 24 | flexWrap: 'wrap', 25 | }, 26 | }; 27 | 28 | const iconStyles = { 29 | marginRight: 24, 30 | }; 31 | 32 | class ReactInterface extends Component { 33 | state = { 34 | drawerOpen: false, 35 | dialogOpen: false, 36 | }; 37 | 38 | //Toggle = Drawer 39 | handleToggle = () => { this.setState({drawerOpen: !this.state.drawerOpen})}; 40 | //Change = Select Field 41 | handleChange = (event, index, value) => {this.setState({value})}; 42 | handleOpen = () => {this.setState({dialogOpen: true});}; 43 | handleClose = () => {this.setState({dialogOpen: false});}; 44 | 45 | render() { 46 | const actions = [ 47 | 52 | ]; 53 | const instructions = ['The component of ‘App’ is automatically generated, which may be used as the top-level parent component for your project.', 54 | 'In order to add children components to ‘App’, you can click on the ‘Add Child’ button to the far right of the ‘App’ component.', 55 | 'In case you delete the ‘App’ component, you can add another parent in the menu bar.', 56 | 'To delete a component, click on the ‘X’ that appears to the right of the ‘Add Child’ button. Deleting a parent node will also delete all of its children.', 57 | 'You may convert a stateless (or presentational) component to a stateful (or smart/class-based) component by toggling between the ‘stateless’ and ‘stateful’ buttons on the component.', 58 | 'Once you are satisfied with the structure of your project, click on the download button located at the top-right corner of the screen to export your components.', 59 | 'If you have any questions, please contact us at: apjs.react.velocity@gmail.com' 60 | ]; 61 | return ( 62 |
63 | } 65 | style={{ 66 | paddingTop: 10, 67 | backgroundColor: grey900, 68 | }} 69 | iconElementLeft={ 70 |
71 | 72 | 73 | 74 |
75 | } 76 | 77 | iconElementRight={ 78 | 79 | 80 | 81 | } 82 | /> 83 | 84 | 87 | 88 |
89 | 90 | 94 | 97 | Currently in React 98 | 99 | 100 | 101 | 105 | 108 | Change to Redux 109 | 110 | 111 | 116 | } 118 | color={cyan200} 119 | backgroundColor={white} /> 120 | Instructions 121 | 122 | 123 | 127 | 130 | Github 131 | 132 | 133 | 141 |
    142 | {instructions.map((instruction) =>
  • {instruction}
  • 143 | )} 144 |
145 |
146 |
147 |
148 |
149 | this.setState({drawerOpen})} 154 | > 155 | 156 | 157 | 169 | 170 | 178 | 179 | 180 |
181 | ); 182 | } 183 | } 184 | export default ReactInterface; 185 | -------------------------------------------------------------------------------- /src/components/react-tree.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { render } from 'react-dom'; 3 | import 'react-sortable-tree/style.css'; 4 | import SortableTree, { addNodeUnderParent ,removeNodeAtPath, changeNodeAtPath, getFlatDataFromTree } from 'react-sortable-tree'; 5 | import MenuItem from 'material-ui/MenuItem'; 6 | import {cyan100, grey800, white} from 'material-ui/styles/colors'; 7 | import ReactInterface from './react-interface'; 8 | import IconButton from 'material-ui/IconButton'; 9 | import generateCode from '../../generateContents/react-generate-content'; 10 | import generatePresentationalComponent from '../../generateContents/react-generate-stateless-component'; 11 | import generateAppCSS from '../../generateContents/react-generate-App-css'; 12 | import generateAppTestJS from '../../generateContents/react-generate-app-test'; 13 | import generateIndexCSS from '../../generateContents/react-generate-index-css'; 14 | import generateReactIndexJS from '../../generateContents/react-generate-index'; 15 | import generateLogoSVG from '../../generateContents/react-generate-logo-svg'; 16 | import generateRegisterServiceWorker from '../../generateContents/react-generate-registerServiceWorker'; 17 | import JSZip from 'jszip'; 18 | 19 | const zip = new JSZip(); 20 | 21 | class ReactTree extends Component { 22 | constructor(props) { 23 | super(props); 24 | /* 25 | treeData is boilerplate from react-sortable-tree that deals with the tree's 26 | organization. treeData is an array of deeply nested objects. 27 | */ 28 | this.state = { 29 | treeData: [{ 30 | name: 'App', 31 | //Parent is for canDrop and dictates that parents CANT be drag/dropped. 32 | parent: true, 33 | //isStateful defaults App component only to "stateful". 34 | isStateful: true, 35 | }], 36 | textFieldValue: '', 37 | /* 38 | flattenedArray takes the treeData and a helper function called 39 | getFlatDataFromTree and returns treeData as a flattened Array. 40 | */ 41 | flattenedArray: [], 42 | /* 43 | The empty string for error is for all MaterialUI text fields so that 44 | they don't automatically return an error message. 45 | */ 46 | error: '', 47 | /* 48 | Version2 is an object with key value pairs where the key is the name of 49 | the file we are zipping and the value is the code belonging to that file. 50 | */ 51 | version2: {}, 52 | }; 53 | this.stateful = this.stateful.bind(this); 54 | this.formatName = this.formatName.bind(this); 55 | this.handleTextFieldChange = this.handleTextFieldChange.bind(this); 56 | this.concatNewComponent = this.concatNewComponent.bind(this); 57 | this.updateFlattenedData = this.updateFlattenedData.bind(this); 58 | this.onButtonPress = this.onButtonPress.bind(this); 59 | this.onKeyPress = this.onKeyPress.bind(this); 60 | this.createCodeForGenerateContent = this.createCodeForGenerateContent.bind(this); 61 | this.handleExport = this.handleExport.bind(this); 62 | this.exportZipFiles = this.exportZipFiles.bind(this); 63 | } 64 | 65 | /* 66 | stateful() generates an "isStateful" property on all components except App 67 | and defaults the stateful/stateless button to "stateless". 68 | */ 69 | stateful(node,path,getNodeKey) { 70 | if (!('isStateful' in node)) { 71 | this.setState(state => ({ 72 | treeData: changeNodeAtPath({ 73 | treeData: state.treeData, 74 | path, 75 | getNodeKey, 76 | newNode: { ...node, isStateful:false } 77 | }) 78 | })) 79 | } 80 | } 81 | /* 82 | formatName() is for sanitization of user input for naming react components 83 | using industry standard practice. 84 | */ 85 | formatName(textField) { 86 | let scrubbedResult = textField 87 | // Capitalize first letter of string. 88 | //| ^ = beginning of output | . = 1st char of str | 89 | .replace(/^./g, x => x.toUpperCase()) 90 | // Capitalize first letter of each word and removes spaces. 91 | //| \ = matches | \w = any alphanumeric | \S = single char except white space 92 | //| * = preceeding expression 0 or more times | + = preceeding expression 1 or more times | 93 | .replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1);}) 94 | .replace(/\ +/g, x => '') 95 | // Remove appending file extensions like .js or .json. 96 | //| \. = . in file extensions | $ = end of input | 97 | .replace(/\..+$/, ''); 98 | return scrubbedResult; 99 | } 100 | 101 | handleTextFieldChange(e){ 102 | this.setState({ 103 | textFieldValue: e.target.value, 104 | }); 105 | } 106 | 107 | /* 108 | concatNewComponent() is run when a user clicks "add child" or hits "Enter" when 109 | inside the textfield. It adds a new node to treeData with the name of the 110 | value of the textfield. If there is nothing written in the textfield, it 111 | returns an error message. 112 | */ 113 | concatNewComponent() { 114 | if(this.state.textFieldValue !== '') { 115 | this.setState(state => ({ 116 | treeData: state.treeData.concat({ 117 | name: this.formatName(this.state.textFieldValue), 118 | }), 119 | error: "", 120 | })) 121 | } else {(this.setState(state => ({ 122 | error: "This field is required" 123 | }) 124 | ))} 125 | } 126 | 127 | /* 128 | Code Generation is not based off of treeData, it is based off of a flattenedArray. 129 | updateFlattenedData() makes sure that flattenedArray is up to date with treeData. 130 | textFieldValue is set to an empty string to reset the textField after you hit 131 | "Enter" or "Add Child". 132 | */ 133 | updateFlattenedData() { 134 | const getNodeKey = ({ treeIndex }) => treeIndex; 135 | const flatteningNestedArray = getFlatDataFromTree({treeData: this.state.treeData, getNodeKey}); 136 | this.setState(state => ({ 137 | flattenedArray: flatteningNestedArray, 138 | textFieldValue: '', 139 | })) 140 | } 141 | 142 | /* onButtonPress() adds a new node to the treeData. concatNewComponent has to 143 | finish before updateFlattenedData() to ensure that updateFlattenedData knows 144 | about the new node. To ensure this, we put updateFlattenedData on a short 145 | time-out. Refer to onButtonPress for onKeyPress. 146 | */ 147 | onButtonPress(){ 148 | this.concatNewComponent(); 149 | /* using setTimeout breaks binding, so use a variable to store this to give 150 | to the function when it runs 151 | */ 152 | const that = this; 153 | setTimeout(function(){that.updateFlattenedData()},100); 154 | }; 155 | 156 | onKeyPress(e) { 157 | if(e.key == 'Enter') { 158 | this.concatNewComponent(); 159 | const that = this; 160 | setTimeout(function(){that.updateFlattenedData()},100); 161 | } 162 | } 163 | /* 164 | For exporting zipped js files, we need an array of subArrays 165 | flattenedVar comes from react-sortable-tree. 166 | */ 167 | createCodeForGenerateContent() { 168 | const getNodeKey = ({ treeIndex }) => treeIndex; 169 | const flatteningNestedArray = getFlatDataFromTree({treeData: this.state.treeData, getNodeKey}); 170 | let flattenedVar = flatteningNestedArray; 171 | /* 172 | version1 is an array of sub-arrays. Within each array, the last element 173 | is the parent of all previous elements. 174 | */ 175 | let version1 = []; 176 | /* For version2, the key is the parent. Value is all of the children 177 | stored as elements in an array. 178 | */ 179 | let version2 = {}; 180 | 181 | for(let i = 0; i {that.handleExport()}, 100); 261 | } 262 | 263 | render() { 264 | 265 | const getNodeKey = ({ treeIndex }) => treeIndex; 266 | const flattenedArray = getFlatDataFromTree({treeData: this.state.treeData, getNodeKey}); 267 | let isStateful = true; 268 | const canDrop = ({ node, nextParent, prevPath, nextPath }) => { 269 | if (node.parent) { 270 | return false; 271 | } 272 | return true; 273 | }; 274 | 275 | return ( 276 |
279 | 290 |
291 | this.setState({ treeData })} 294 | canDrop={canDrop} 295 | generateNodeProps={({ node, path }) => ({ 296 | title: ( 297 | { 301 | const name = event.target.value; 302 | this.setState(state => ({ 303 | treeData: changeNodeAtPath({ 304 | treeData: state.treeData, 305 | path, 306 | getNodeKey, 307 | newNode: { ...node, name }, 308 | }), 309 | })); 310 | }} 311 | /> 312 | ), 313 | // stateful: this.stateful(node,path,getNodeKey), 314 | buttons: [ 315 | , 332 | , 354 | , 372 | ], 373 | })} 374 | /> 375 |
376 |
377 | ); 378 | } 379 | } 380 | 381 | export default ReactTree; 382 | -------------------------------------------------------------------------------- /src/components/redux-interface.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import { Link } from "react-router-dom"; 3 | import AppBar from 'material-ui/AppBar'; 4 | import FlatButton from 'material-ui/FlatButton'; 5 | import {Card, CardActions, CardTitle, CardText} from 'material-ui/Card'; 6 | import Drawer from 'material-ui/Drawer'; 7 | import MenuItem from 'material-ui/MenuItem'; 8 | import RaisedButton from 'material-ui/RaisedButton'; 9 | import TextField from 'material-ui/TextField'; 10 | import SelectField from 'material-ui/SelectField'; 11 | import {RadioButton, RadioButtonGroup} from 'material-ui/RadioButton'; 12 | import IconButton from 'material-ui/IconButton'; 13 | import Menu from 'material-ui/svg-icons/navigation/menu'; 14 | import Avatar from 'material-ui/Avatar'; 15 | import Chip from 'material-ui/Chip'; 16 | import FileDownload from 'material-ui/svg-icons/file/file-download'; 17 | import Settings from 'material-ui/svg-icons/action/settings'; 18 | import Dialog from 'material-ui/Dialog'; 19 | import {deepPurple200, deepPurple800, grey800, grey900, white} from 'material-ui/styles/colors'; 20 | 21 | const styles = { 22 | chip: { 23 | margin: 4, 24 | }, 25 | wrapper: { 26 | display: 'flex', 27 | flexWrap: 'wrap', 28 | }, 29 | block: { 30 | maxWidth: 50, 31 | }, 32 | radioButton: { 33 | marginBottom: 16, 34 | }, 35 | }; 36 | 37 | const iconStyles = { 38 | marginRight: 24, 39 | }; 40 | 41 | const cardStyle = { 42 | backgroundColor: grey900 43 | } 44 | 45 | class ReduxInterface extends Component { 46 | 47 | constructor(props) { 48 | super(props); 49 | this.state = { 50 | drawerOpen: false, 51 | dialogOpen: false, 52 | }; 53 | this.handleToggle = this.handleToggle.bind(this); 54 | this.handleOpen = this.handleOpen.bind(this); 55 | this.handleClose = this.handleClose.bind(this); 56 | } 57 | 58 | //Toggle = Drawer 59 | handleToggle(){ this.setState({drawerOpen: !this.state.drawerOpen})}; 60 | handleOpen(){this.setState({dialogOpen: true});}; 61 | handleClose(){this.setState({dialogOpen: false});}; 62 | 63 | render() { 64 | const actions = [ 65 | 70 | ]; 71 | 72 | return ( 73 |
74 | } 76 | style={{ 77 | paddingTop: 10, 78 | backgroundColor: grey900, 79 | }} 80 | iconElementLeft={ 81 |
82 | 83 | 84 | 85 |
86 | } 87 | iconElementRight={ 88 | 89 | 90 | 91 | } 92 | /> 93 | 96 | 97 |
98 | 99 | 103 | 106 | Currently in Redux 107 | 108 | 109 | 110 | 114 | 117 | Change to React 118 | 119 | 120 | 125 | } 127 | color={deepPurple200} 128 | backgroundColor={white} /> 129 | Instructions 130 | 131 | 132 | 136 | 139 | Github 140 | 141 | 142 | 150 |
    151 |
  • As indicated on the top three nodes named ‘action’, ‘reducer’, and ‘container/component’, please do not remove these nodes. They are placeholders for your project’s folder structure.
  • 152 |

    153 |
  • To begin, open the menu drawer by clicking on the horizontal lines in the top-left corner of the page. Please be sure that the appropriate file type is selected before you add a file.
  • 154 |

    155 |
  • If you wish to add an action creator, make sure that ‘Action’ is selected, type the name of that action in the ‘Action: Name’ input field and click ‘Add File’.
  • 156 |

    157 |
  • **IMPORTANT NOTE: A user created 'action' must be a child of the pre-generated 'action' component.
  • 158 |

    159 |
  • To add a reducer, select ‘Reducer’ from the menu drop-down, type the reducer’s name in the input field, and add all the cases for that reducer in the ‘Reducer: Case’ input field.
  • 160 |

    161 |
  • **IMPORTANT NOTE: You may add multiple cases for a single reducer, but please be sure that the cases are comma-separated.
  • 162 |

    163 |
  • Add a container by selecting the container option in the drop-down and title the container in the input field. You may add stateful and stateless components by selecting the component drop-down option and specifying on the bottom of the menu whether the component is stateful or stateless.
  • 164 |

    165 |
  • **IMPORTANT NOTE: WHEN YOU ADD A FILE, DRAG THE FILE TO ITS APPROPRIATE FOLDER ON THE PAGE AND NEST IT INSIDE ITS APPROPRIATE FOLDER.
  • 166 |

    167 |
  • For example, if you created an action creator, drag and drop the newly created file under the default ‘action’ node on the page. Once you have determined your project structure, you can export your files by clicking on the download button in the top-right corner of the screen.
  • 168 |

    169 |
  • If you have any questions, please contact us at: apjs.react.velocity@gmail.com
  • 170 |
171 |
172 |
173 |
174 |
175 | this.setState({drawerOpen})} 180 | > 181 | 182 | 183 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 215 | 216 | 217 | 218 | 219 | 231 | 232 | 233 | 234 | 235 | 247 | 248 | 249 | 250 | 251 | 263 | 264 | 265 | 266 | 267 | 279 | {this.props.radioButtonChecked(e)}}> 280 | 286 | 292 | 293 | 294 | 295 | 296 | 297 | 305 | 306 | 307 | 308 |
309 | ); 310 | } 311 | } 312 | export default ReduxInterface; 313 | -------------------------------------------------------------------------------- /src/components/redux-tree.js: -------------------------------------------------------------------------------- 1 | //add an onclick to the export button to update the flattened array 2 | //that way the flattenedata array is up to date 3 | 4 | 5 | import React, { Component } from 'react'; 6 | import { render } from 'react-dom'; 7 | import 'react-sortable-tree/style.css'; 8 | import SortableTree, { addNodeUnderParent ,removeNodeAtPath, changeNodeAtPath, getFlatDataFromTree } from 'react-sortable-tree'; 9 | import MenuItem from 'material-ui/MenuItem'; 10 | import ReduxInterface from './redux-interface'; 11 | import generateReduxIndexJS from './../../generateContents/redux-index'; 12 | import generateIndexHTML from './../../generateContents/index-html'; 13 | import generateActionCreators from './../../generateContents/redux-generate-action-creators'; 14 | import generateReducers from './../../generateContents/redux-generate-reducers'; 15 | import generateComponents from './../../generateContents/redux-generate-components'; 16 | import generatePresentationalComponent from './../../generateContents/react-generate-stateless-component'; 17 | import generateContainer from './../../generateContents/redux-generate-container'; 18 | import {deepPurple100, grey800, white} from 'material-ui/styles/colors'; 19 | import JSZip from 'jszip'; 20 | const zip = new JSZip(); 21 | 22 | class ReduxTree extends Component { 23 | constructor(props) { 24 | super(props); 25 | 26 | this.state = { 27 | treeData: [ 28 | { name: 'action', defaultType: '', parent: true, id: 'action'}, 29 | { name: 'reducer', defaultType: '', parent: true, id: 'reducer'}, 30 | { name: 'container/component', defaultType: '', parent: true, expanded: true, id: 'container/component', children: [ { name: 'App', componentType: 'Container', parent: true } ]}], 31 | value: 'Action', 32 | actionName: '', 33 | actionType: '', 34 | reducerName: '', 35 | reducerCase: '', 36 | componentName: '', 37 | containerName: '', 38 | actionError: '', 39 | reducerNameError: '', 40 | reducerCaseError: '', 41 | componentNameError: '', 42 | containerNameError: '', 43 | flattenedArray: [], 44 | version2: {}, 45 | parents: [], 46 | radioButtonState: false, 47 | }; 48 | this.camelCaseFormat = this.camelCaseFormat.bind(this); 49 | this.capitalizeFirstLetterOfEachWord = this.capitalizeFirstLetterOfEachWord.bind(this); 50 | this.allCapSnakeCaseFormat = this.allCapSnakeCaseFormat.bind(this); 51 | this.actionHandleTextFieldChange = this.actionHandleTextFieldChange.bind(this); 52 | this.reducerNameHandleTextFieldChange = this.reducerNameHandleTextFieldChange.bind(this); 53 | this.reducerCaseHandleTextFieldChange = this.reducerCaseHandleTextFieldChange.bind(this); 54 | this.componentNameHandleTextFieldChange = this.componentNameHandleTextFieldChange.bind(this); 55 | this.containerNameHandleTextFieldChange = this.containerNameHandleTextFieldChange.bind(this); 56 | this.chooseFileType = this.chooseFileType.bind(this); 57 | this.concatNewComponent = this.concatNewComponent.bind(this); 58 | this.updateFlattenedData = this.updateFlattenedData.bind(this); 59 | this.onButtonPress = this.onButtonPress.bind(this); 60 | this.onKeyPress = this.onKeyPress.bind(this); 61 | this.createCodeForGenerateContent = this.createCodeForGenerateContent.bind(this); 62 | this.handleExport = this.handleExport.bind(this); 63 | this.exportZipFiles = this.exportZipFiles.bind(this); 64 | this.toggleStateButton = this.toggleStateButton.bind(this); 65 | this.handleChangeSelectField = this.handleChangeSelectField.bind(this); 66 | this.reducerCaseToArray = this.reducerCaseToArray.bind(this); 67 | this.radioButtonChecked = this.radioButtonChecked.bind(this); 68 | } 69 | 70 | camelCaseFormat(textField) { 71 | let scrubbedResult = textField 72 | // Capitalize first letter of each word and removes spaces. 73 | //| \ = matches | \w = any alphanumeric | \S = single char except white space 74 | //| * = preceeding expression 0 or more times | + = preceeding expression 1 or more times | 75 | .replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1);}) 76 | .replace(/\ +/g, x => '') 77 | // Capitalize first letter of string. 78 | //| ^ = beginning of output | . = 1st char of str | 79 | .replace(/^./g, x => x.toLowerCase()) 80 | // Remove appending file extensions like .js or .json. 81 | //| \. = . in file extensions | $ = end of input | 82 | .replace(/\..+$/, ''); 83 | return scrubbedResult; 84 | } 85 | 86 | capitalizeFirstLetterOfEachWord(textField) { 87 | let scrubbedResult = textField 88 | .replace(/^./g, x => x.toUpperCase()) 89 | .replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1);}) 90 | .replace(/\ +/g, x => '') 91 | .replace(/\..+$/, ''); 92 | return scrubbedResult; 93 | } 94 | 95 | allCapSnakeCaseFormat(textField) { 96 | let scrubbedResult = textField 97 | .replace(/\w\S*/g, function(txt){return '_' + txt.substr(0);}) 98 | .replace(/\ +/g, x => '') 99 | .replace(/^_/g, x => '') 100 | .replace(/\w/g, x => x.toUpperCase()) 101 | return scrubbedResult; 102 | } 103 | 104 | reducerCaseToArray(textfield){ 105 | let splitTextField = textfield.split(/,/) 106 | let scrubbedArray = splitTextField.map(ele => { 107 | return this.allCapSnakeCaseFormat(ele) 108 | }) 109 | return scrubbedArray; 110 | } 111 | 112 | actionHandleTextFieldChange(e){ 113 | this.setState({ 114 | actionName: e.target.value, 115 | actionType: e.target.value, 116 | 117 | }); 118 | } 119 | 120 | reducerNameHandleTextFieldChange(e){ 121 | this.setState({ 122 | reducerName: e.target.value, 123 | }); 124 | } 125 | 126 | reducerCaseHandleTextFieldChange(e){ 127 | this.setState({ 128 | reducerCase: e.target.value, 129 | }); 130 | } 131 | 132 | componentNameHandleTextFieldChange(e){ 133 | this.setState({ 134 | componentName: e.target.value, 135 | }); 136 | } 137 | 138 | containerNameHandleTextFieldChange(e){ 139 | this.setState({ 140 | containerName: e.target.value, 141 | }); 142 | } 143 | 144 | chooseFileType() { 145 | if (this.state.value === "Action") { 146 | return "Action" 147 | } 148 | if (this.state.value === "Reducer") { 149 | return "Reducer" 150 | } 151 | if (this.state.value === "Container") { 152 | return "Container" 153 | } 154 | if (this.state.value === "Component") { 155 | return "Component" 156 | } 157 | } 158 | 159 | concatNewComponent() { 160 | if(this.state.actionName !== '' && this.state.actionType !== '' && this.state.value === 'Action' ) { 161 | this.setState(state => ({ 162 | treeData: state.treeData.concat({ 163 | name: this.camelCaseFormat(this.state.actionName), 164 | type: this.allCapSnakeCaseFormat(this.state.actionType), 165 | componentType: this.chooseFileType(), 166 | }), 167 | actionError: "", 168 | })) 169 | } else if(this.state.reducerName !== '' && this.state.reducerCase !== '' && this.state.value === 'Reducer' ) { 170 | this.setState(state => ({ 171 | treeData: state.treeData.concat({ 172 | name: this.camelCaseFormat(this.state.reducerName), 173 | case: this.reducerCaseToArray(this.state.reducerCase), 174 | componentType: this.chooseFileType(), 175 | }), 176 | reducerNameError: "", 177 | reducerCaseError: "", 178 | })) 179 | } else if(this.state.componentName !== '' && this.state.value === 'Component' ) { 180 | this.setState(state => ({ 181 | treeData: state.treeData.concat({ 182 | name: this.capitalizeFirstLetterOfEachWord(this.state.componentName), 183 | isStateful: this.state.radioButtonState, 184 | componentType: this.chooseFileType(), 185 | }), 186 | componentNameError: "", 187 | })) 188 | } else if(this.state.containerName !== '' && this.state.value === 'Container' ) { 189 | this.setState(state => ({ 190 | treeData: state.treeData.concat({ 191 | name: this.capitalizeFirstLetterOfEachWord(this.state.containerName), 192 | componentType: this.chooseFileType(), 193 | }), 194 | containerNameError: "", 195 | })) 196 | } else if (this.state.value === 'Action'){( 197 | this.setState(state => ({ 198 | actionError: "This field is required." 199 | }) 200 | ))} else if (this.state.value === 'Reducer'){( 201 | this.setState(state => ({ 202 | reducerNameError: "These fields are required.", 203 | reducerCaseError: "These fields are required." 204 | }) 205 | ))} else if (this.state.value === 'Component'){( 206 | this.setState(state => ({ 207 | componentNameError: "This field is required.", 208 | }) 209 | ))} else if (this.state.value === 'Container'){( 210 | this.setState(state => ({ 211 | containerNameError: "This field is required.", 212 | }) 213 | ))} 214 | } 215 | 216 | updateFlattenedData() { 217 | const getNodeKey = ({ treeIndex }) => treeIndex; 218 | const flatteningNestedArray = getFlatDataFromTree({treeData: this.state.treeData, getNodeKey}); 219 | this.setState(state => ({ 220 | flattenedArray: flatteningNestedArray, 221 | actionName: '', 222 | actionType: '', 223 | reducerName: '', 224 | reducerCase: '', 225 | componentName: '', 226 | containerName: '', 227 | })) 228 | } 229 | 230 | onButtonPress(){ 231 | this.concatNewComponent(); 232 | // using setTimeout breaks binding, so use a variable to store this to give to the function when it runs 233 | const that = this; 234 | setTimeout(function(){that.updateFlattenedData()},100); 235 | }; 236 | 237 | onKeyPress(e) { 238 | if(e.key === 'Enter') { 239 | this.concatNewComponent(); 240 | // using setTimeout breaks binding, so use a variable to store this to give to the function when it runs 241 | const that = this; 242 | setTimeout(function(){that.updateFlattenedData()},100); 243 | } 244 | } 245 | 246 | createCodeForGenerateContent() { 247 | const getNodeKey = ({ treeIndex }) => treeIndex; 248 | const flatteningNestedArray = getFlatDataFromTree({treeData: this.state.treeData, getNodeKey}); 249 | let flattenedVar = flatteningNestedArray; 250 | let version1 = []; 251 | let version2 = {}; 252 | for(let i = 0; i treeIndex; 305 | const flattenedArray = getFlatDataFromTree({treeData: this.state.treeData, getNodeKey}); 306 | const index = generateReduxIndexJS(); 307 | const html = generateIndexHTML(); 308 | const actions = generateActionCreators(flattenedArray); 309 | const reducers = generateReducers(flattenedArray); 310 | const stateful = generateComponents(version2); 311 | const stateless = generatePresentationalComponent(version2); 312 | const components = {...stateful, ...stateless}; 313 | const containers = generateContainer(version2); 314 | let fileNames = Object.keys(components); 315 | let fileNamesContainers = Object.keys(containers); 316 | zip.file('index.js', index, {base64: false}); 317 | zip.file('index.html', html, {base64: false}); 318 | zip.folder('actions').file('actionTypes.js', actions , {base64: false}); 319 | zip.folder('reducers').file('reducers.js', reducers , {base64: false}); 320 | for (let i=0; i {that.handleExport()}, 100); 339 | } 340 | 341 | toggleStateButton() { 342 | this.setState(prevState => ({ 343 | isToggleOn: !prevState.isToggleOn 344 | })); 345 | } 346 | 347 | handleChangeSelectField(event, index, value) { 348 | this.setState({ 349 | value, 350 | actionError: '', 351 | reducerNameError: '', 352 | reducerCaseError: '', 353 | componentNameError: '', 354 | containerNameError: '', 355 | }) 356 | }; 357 | 358 | radioButtonChecked(e) { 359 | this.setState({ 360 | radioButtonState: e.target.value, 361 | }); 362 | } 363 | 364 | render() { 365 | const getNodeKey = ({ treeIndex }) => treeIndex; 366 | const flattenedArray = getFlatDataFromTree({treeData: this.state.treeData, getNodeKey}); 367 | const canDrop = ({ node, nextParent, prevPath, nextPath }) => { 368 | if (node.parent) { 369 | return false; 370 | } 371 | return true; 372 | }; 373 | return ( 374 |
377 | 404 |
405 | this.setState({ treeData })} 408 | canDrop={canDrop} 409 | generateNodeProps={({ node, path }) => ({ 410 | title: ( 411 | { 415 | const name = event.target.value; 416 | this.setState(state => ({ 417 | treeData: changeNodeAtPath({ 418 | treeData: state.treeData, 419 | path, 420 | getNodeKey, 421 | newNode: { ...node, name}, 422 | }), 423 | })); 424 | }} 425 | /> 426 | ), 427 | buttons: [ 428 | , 437 | , 455 | ], 456 | })} 457 | /> 458 |
459 |
460 | ); 461 | } 462 | } 463 | 464 | export default ReduxTree; 465 | -------------------------------------------------------------------------------- /src/drawing.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 41 | 43 | 44 | 46 | image/svg+xml 47 | 49 | 50 | 51 | 52 | 53 | 58 | 75 | 82 | 89 | 96 | 103 | 110 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /src/gitHubIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apjs/ReactVelocity/d4ee8a1ebb0af454f44df8d907e1daf9283e03e5/src/gitHubIcon.png -------------------------------------------------------------------------------- /src/reactLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apjs/ReactVelocity/d4ee8a1ebb0af454f44df8d907e1daf9283e03e5/src/reactLogo.png -------------------------------------------------------------------------------- /src/reactVelocity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apjs/ReactVelocity/d4ee8a1ebb0af454f44df8d907e1daf9283e03e5/src/reactVelocity.png -------------------------------------------------------------------------------- /src/reactVelocity.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 25 | 30 | 34 | 39 | 45 | 49 | 54 | 60 | 64 | 65 | 69 | 74 | 78 | 79 | 83 | 88 | 92 | 93 | 97 | 102 | 106 | 107 | 111 | 116 | 120 | 121 | 125 | 130 | 134 | 135 | 139 | 144 | 148 | 149 | 153 | 158 | 162 | 163 | 167 | 172 | 176 | 177 | 181 | 186 | 190 | 191 | 192 | 213 | 215 | 216 | 218 | image/svg+xml 219 | 221 | 222 | 223 | 224 | 225 | 230 | REACT 241 | ELOCI Y 252 | 258 | 265 | 271 | 278 | 285 | 292 | 299 | 306 | 307 | 308 | -------------------------------------------------------------------------------- /src/reactVelocity_black.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 25 | 30 | 34 | 39 | 45 | 49 | 54 | 60 | 64 | 69 | 75 | 79 | 80 | 84 | 89 | 93 | 98 | 104 | 108 | 109 | 113 | 118 | 122 | 127 | 133 | 137 | 138 | 142 | 147 | 151 | 156 | 162 | 166 | 167 | 171 | 176 | 180 | 185 | 191 | 195 | 196 | 200 | 205 | 209 | 214 | 220 | 224 | 225 | 229 | 234 | 238 | 243 | 249 | 253 | 254 | 258 | 263 | 267 | 272 | 278 | 282 | 283 | 287 | 292 | 296 | 301 | 307 | 311 | 312 | 316 | 321 | 325 | 330 | 336 | 340 | 341 | 342 | 363 | 365 | 366 | 368 | image/svg+xml 369 | 371 | 372 | 373 | 374 | 375 | 380 | REACT 391 | ELOCI Y 402 | 408 | 415 | 421 | 428 | 435 | 442 | 449 | 456 | 463 | 464 | 465 | -------------------------------------------------------------------------------- /src/reduxLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apjs/ReactVelocity/d4ee8a1ebb0af454f44df8d907e1daf9283e03e5/src/reduxLogo.png -------------------------------------------------------------------------------- /styles/styles.css: -------------------------------------------------------------------------------- 1 | /* font-family: 'Roboto', sans-serif; */ 2 | 3 | /* 4 | This is a sample style just to show that we have This 5 | file connected to our index.html \*/ 6 | body { 7 | margin:0; 8 | } 9 | 10 | .logo { 11 | padding-top: 8px; 12 | } 13 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apjs/ReactVelocity/d4ee8a1ebb0af454f44df8d907e1daf9283e03e5/test.js -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const htmlWebpackPlugin = require('html-webpack-plugin'); 3 | 4 | 5 | const htmlWebpackConfig = new htmlWebpackPlugin({ 6 | template: __dirname + '/index.html', 7 | filename: 'index.html', 8 | inject: 'body' 9 | }); 10 | 11 | module.exports = { 12 | entry: __dirname + '/main.js', 13 | output: { 14 | path: __dirname + '/build', 15 | filename: 'bundle.js' 16 | }, 17 | module: { 18 | rules: [ 19 | { 20 | test: /\.js?$/, 21 | loader: 'babel-loader', 22 | exclude: /node_modules/, 23 | query: { 24 | presets: [ 'env', 'react' ] 25 | } 26 | }, 27 | { 28 | test: /.css$/, 29 | use: ['style-loader', 'css-loader'], 30 | }, 31 | ] 32 | }, 33 | plugins: [ htmlWebpackConfig], 34 | devServer: { 35 | inline: true, 36 | port: 8080, 37 | contentBase: __dirname + '/build', 38 | historyApiFallback: { 39 | index: '/' 40 | } 41 | } 42 | }; 43 | --------------------------------------------------------------------------------