├── .babelrc ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── .npmignore ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── LICENSE.txt ├── README.md ├── howto.png ├── howto2.png ├── jest.config.js ├── license.csv ├── package.json ├── src ├── Burger.css ├── Burger.js ├── Connector.css ├── Connector.js ├── Deserializer.js ├── DnDLeaf.js ├── ImageMenu.css ├── ImageMenu.js ├── ImgCanvas.css ├── ImgCanvas.js ├── ImgViewer.css ├── ImgViewer.js ├── InstructionMessage.js ├── Leaf.css ├── Leaf.js ├── LeafData.js ├── MainWindow.css ├── MainWindow.js ├── MarkDownTextBoxWrapper.js ├── Menu.css ├── Menu.js ├── MenuModal.css ├── MenuModal.js ├── PreviewLabels.css ├── PreviewLabels.js ├── PreviewList.js ├── PreviewMenu.css ├── PreviewMenu.js ├── PreviewPanel.css ├── PreviewPanel.js ├── PreviewSentence.js ├── Property.js ├── ReadOnlyLeaf.js ├── Reserved.js ├── StateProvider.js ├── Tree.css ├── Tree.js ├── images │ ├── backtoedit.png │ ├── camera.png │ ├── document.png │ ├── down.png │ ├── export.png │ ├── gopreview.png │ ├── gopreviewwhite.png │ ├── gotable.png │ ├── label.png │ ├── palette.png │ └── right.png ├── index.css └── index.js ├── test ├── CSSStub.js ├── Deserializer.test.js ├── LeafData.test.js ├── PNGStub.js ├── StateProvider.test.js ├── map.test.js ├── middleware.test.js ├── previewLabel.test.js ├── previewList.test.js ├── previewSentence.test.js ├── previewTree.test.js └── setupTests.js └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env" 4 | ], 5 | "plugins": [ 6 | "@babel/plugin-proposal-object-rest-spread", 7 | "@babel/plugin-transform-react-jsx" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | /test/__html__ 11 | 12 | # production 13 | /build 14 | 15 | # misc 16 | .DS_Store 17 | .env.local 18 | .env.development.local 19 | .env.test.local 20 | .env.production.local 21 | linkWithLocalReact.sh 22 | 23 | 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | yarn.lock 28 | package-lock.json 29 | 30 | # idea 31 | .idea -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src/ 2 | test/ 3 | .github/ 4 | .idea/ 5 | node_modules/ 6 | 7 | # misc 8 | .DS_Store 9 | .env.local 10 | .env.development.local 11 | .env.test.local 12 | .env.production.local 13 | linkWithLocalReact.sh 14 | 15 | 16 | npm-debug.log* 17 | yarn-debug.log* 18 | yarn-error.log* 19 | yarn.lock 20 | package-lock.json 21 | webpack.config.js 22 | .travis.yml 23 | .babelrc 24 | .gitignore 25 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "12.8.1" 4 | install: 5 | - npm install 6 | script: 7 | - npm run build 8 | branches: 9 | only: 10 | - master 11 | deploy: 12 | provider: npm 13 | email: ycuobu@gmail.com 14 | skip_cleanup: true 15 | tag: beta 16 | api_key: 17 | secure: vqZ2j2aiVKOoKc0M2fiiy8tQyASGu49+/9elinH1Wgdspjj9gsltPaGAkAGAGcR6v9AzA+KtjO3NwQ4VWToAPo35vp1yMRTGK5FQPXTZYcJJKoUUOEQkNz0KcFArk0h/vOU9DyJ16hR+1dPIyiiyeAZ/PYidSImPR17cbBVRoNbEiQVimJtYiGy4ImGbnG/654I1utsukbJ37Ekt1vTlFNdIXoXDCw3mcYi+aW/yrDTZE9ng0m/onoKnWm3pe2rxXfF+K/CKInmLv1toJzOyRCk9fG1vEUSf6gmM4RXYfMpBTGYTPks9z788BO+nfpG/SQ7hCoJaIUECIrFTru+Cs8igXIl7tsmIrcMqDNm/x67HUbRxe8XcoB0pO5uxbJePPzZAsof1TlgEogZTmUDx0HZLdQ7Ikrh3DEyIecGescKj15i+HuG4W68FUpMlyDRucdrTJugSVTwcxnykL3kcu7Zugu5/BlDtDc8VnKC/lWkdHolYoPwFoRJu2VW+noo+Uy5qASjU4tQ/GGOdFQX/mdc9i+Q5drcwt/yrofzIIGN8GpPGH+WCuJiCWVIbZx2si5yvK9M51cvAKZnmIpXoQx0tdIb6zmqyTdvZRK30euaZb8tbfFa20A5BZ0hsaSXH8RLhxrqqTNtZn4tSqyFtWlvbHxd9a+QrZyXlyYfwJnE= 18 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at ycuobu@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-present Nobuyuki Mukai 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![npm version](https://badge.fury.io/js/treemindmap.svg)](https://badge.fury.io/js/treemindmap) 2 | [![Build Status](https://travis-ci.org/3yaa3yaa/TreeMindMap.svg?branch=master)](https://travis-ci.org/3yaa3yaa/TreeMindMap) 3 | # Tree Mind Map 4 | This software is released under the MIT License, see LICENSE.txt. 5 | 6 | ## What is this? 7 | 8 | This is a NPM component of mind map tool written in React. 9 | https://www.npmjs.com/package/treemindmap 10 | 11 | ## What's it look like? 12 | Sample page -- https://treemindmap.netlify.com/ 13 | ![howto](https://github.com/3yaa3yaa/TreeMindMap/blob/master/howto.png) 14 | 15 | 16 | ## How to use this component? 17 | Use "" with parameters "initialState" and "stateHandler". 18 | * "initialState" : The first state to be load in the mindmap 19 | * "stateHandler" : This is triggered every time the state changed. 20 | 21 | For example, you can specify the values as below. 22 | 23 | ### ES6 24 | ``` 25 | import {Map} from 'treemindmap' 26 | ... 27 | render( 28 | { 31 | doSomething(state); 32 | }} 33 | />, 34 | document.getElementById('root') 35 | ); 36 | ``` 37 | 38 | ### ES5 39 | ``` 40 | const ReactDOM = require('react-dom'); 41 | const Render=require('treemindmap').Render; 42 | ... 43 | Render( 44 | initialdata, 45 | (state) => { 46 | doSomething(state); 47 | }, 48 | 'root', /* id of DOM */ 49 | ReactDOM 50 | ) 51 | ``` 52 | 53 | -------------------------------------------------------------------------------- /howto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/3yaa3yaa/TreeMindMap/7450413950a0ce32d0243309997e81fe27f1d766/howto.png -------------------------------------------------------------------------------- /howto2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/3yaa3yaa/TreeMindMap/7450413950a0ce32d0243309997e81fe27f1d766/howto2.png -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | setupFilesAfterEnv: ['/test/setupTests.js'], 3 | "moduleNameMapper":{ 4 | '^.+\\.(css|less)$': '/test/CSSStub.js', 5 | '^.+\\.(png)$': '/test/PNGStub.js' 6 | } 7 | } -------------------------------------------------------------------------------- /license.csv: -------------------------------------------------------------------------------- 1 | "module name","license","repository" 2 | "@3yaa3yaa/markdowntextbox@0.1.17","MIT","https://github.com/3yaa3yaa/markdowntextbox" 3 | "@babel/runtime@7.6.3","MIT","https://github.com/babel/babel/tree/master/packages/babel-runtime" 4 | "@types/hoist-non-react-statics@3.3.1","MIT","https://github.com/DefinitelyTyped/DefinitelyTyped" 5 | "@types/prop-types@15.7.3","MIT","https://github.com/DefinitelyTyped/DefinitelyTyped" 6 | "@types/react@16.9.5","MIT","https://github.com/DefinitelyTyped/DefinitelyTyped" 7 | "@webassemblyjs/ast@1.8.5","MIT","https://github.com/xtuc/webassemblyjs" 8 | "@webassemblyjs/floating-point-hex-parser@1.8.5","MIT","https://github.com/xtuc/webassemblyjs" 9 | "@webassemblyjs/helper-api-error@1.8.5","MIT","" 10 | "@webassemblyjs/helper-buffer@1.8.5","MIT","https://github.com/xtuc/webassemblyjs" 11 | "@webassemblyjs/helper-code-frame@1.8.5","MIT","https://github.com/xtuc/webassemblyjs" 12 | "@webassemblyjs/helper-fsm@1.8.5","ISC","" 13 | "@webassemblyjs/helper-module-context@1.8.5","MIT","https://github.com/xtuc/webassemblyjs" 14 | "@webassemblyjs/helper-wasm-bytecode@1.8.5","MIT","https://github.com/xtuc/webassemblyjs" 15 | "@webassemblyjs/helper-wasm-section@1.8.5","MIT","https://github.com/xtuc/webassemblyjs" 16 | "@webassemblyjs/ieee754@1.8.5","MIT","" 17 | "@webassemblyjs/leb128@1.8.5","MIT","" 18 | "@webassemblyjs/utf8@1.8.5","MIT","https://github.com/xtuc/webassemblyjs" 19 | "@webassemblyjs/wasm-edit@1.8.5","MIT","https://github.com/xtuc/webassemblyjs" 20 | "@webassemblyjs/wasm-gen@1.8.5","MIT","https://github.com/xtuc/webassemblyjs" 21 | "@webassemblyjs/wasm-opt@1.8.5","MIT","https://github.com/xtuc/webassemblyjs" 22 | "@webassemblyjs/wasm-parser@1.8.5","MIT","https://github.com/xtuc/webassemblyjs" 23 | "@webassemblyjs/wast-parser@1.8.5","MIT","https://github.com/xtuc/webassemblyjs" 24 | "@webassemblyjs/wast-printer@1.8.5","MIT","https://github.com/xtuc/webassemblyjs" 25 | "@xtuc/ieee754@1.2.0","BSD-3-Clause","https://github.com/feross/ieee754" 26 | "@xtuc/long@4.2.2","Apache-2.0","https://github.com/dcodeIO/long.js" 27 | "acorn@6.3.0","MIT","https://github.com/acornjs/acorn" 28 | "ajv-errors@1.0.1","MIT","https://github.com/epoberezkin/ajv-errors" 29 | "ajv-keywords@3.4.1","MIT","https://github.com/epoberezkin/ajv-keywords" 30 | "ajv@6.10.2","MIT","https://github.com/epoberezkin/ajv" 31 | "anymatch@2.0.0","ISC","https://github.com/micromatch/anymatch" 32 | "aproba@1.2.0","ISC","https://github.com/iarna/aproba" 33 | "arr-diff@4.0.0","MIT","https://github.com/jonschlinkert/arr-diff" 34 | "arr-flatten@1.1.0","MIT","https://github.com/jonschlinkert/arr-flatten" 35 | "arr-union@3.1.0","MIT","https://github.com/jonschlinkert/arr-union" 36 | "array-unique@0.3.2","MIT","https://github.com/jonschlinkert/array-unique" 37 | "asap@2.0.6","MIT","https://github.com/kriskowal/asap" 38 | "asn1.js@4.10.1","MIT","https://github.com/indutny/asn1.js" 39 | "assert@1.5.0","MIT","https://github.com/browserify/commonjs-assert" 40 | "assign-symbols@1.0.0","MIT","https://github.com/jonschlinkert/assign-symbols" 41 | "async-each@1.0.3","MIT","https://github.com/paulmillr/async-each" 42 | "atob@2.1.2","(MIT OR Apache-2.0)","git://git.coolaj86.com/coolaj86/atob.js" 43 | "balanced-match@1.0.0","MIT","https://github.com/juliangruber/balanced-match" 44 | "base64-arraybuffer@0.2.0","MIT","https://github.com/niklasvh/base64-arraybuffer" 45 | "base64-js@1.3.1","MIT","https://github.com/beatgammit/base64-js" 46 | "base@0.11.2","MIT","https://github.com/node-base/base" 47 | "big.js@5.2.2","MIT","https://github.com/MikeMcl/big.js" 48 | "binary-extensions@1.13.1","MIT","https://github.com/sindresorhus/binary-extensions" 49 | "bluebird@3.7.0","MIT","https://github.com/petkaantonov/bluebird" 50 | "bn.js@4.11.8","MIT","https://github.com/indutny/bn.js" 51 | "brace-expansion@1.1.11","MIT","https://github.com/juliangruber/brace-expansion" 52 | "braces@2.3.2","MIT","https://github.com/micromatch/braces" 53 | "brorand@1.1.0","MIT","https://github.com/indutny/brorand" 54 | "browserify-aes@1.2.0","MIT","https://github.com/crypto-browserify/browserify-aes" 55 | "browserify-cipher@1.0.1","MIT","https://github.com/crypto-browserify/browserify-cipher" 56 | "browserify-des@1.0.2","MIT","https://github.com/crypto-browserify/browserify-des" 57 | "browserify-rsa@4.0.1","MIT","https://github.com/crypto-browserify/browserify-rsa" 58 | "browserify-sign@4.0.4","ISC","https://github.com/crypto-browserify/browserify-sign" 59 | "browserify-zlib@0.2.0","MIT","https://github.com/devongovett/browserify-zlib" 60 | "buffer-from@1.1.1","MIT","https://github.com/LinusU/buffer-from" 61 | "buffer-xor@1.0.3","MIT","https://github.com/crypto-browserify/buffer-xor" 62 | "buffer@4.9.1","MIT","https://github.com/feross/buffer" 63 | "builtin-status-codes@3.0.0","MIT","https://github.com/bendrucker/builtin-status-codes" 64 | "cacache@12.0.3","ISC","https://github.com/npm/cacache" 65 | "cache-base@1.0.1","MIT","https://github.com/jonschlinkert/cache-base" 66 | "chokidar@2.1.8","MIT","https://github.com/paulmillr/chokidar" 67 | "chownr@1.1.3","ISC","https://github.com/isaacs/chownr" 68 | "chrome-trace-event@1.0.2","MIT","github.com:samccone/chrome-trace-event" 69 | "cipher-base@1.0.4","MIT","https://github.com/crypto-browserify/cipher-base" 70 | "class-utils@0.3.6","MIT","https://github.com/jonschlinkert/class-utils" 71 | "collection-visit@1.0.0","MIT","https://github.com/jonschlinkert/collection-visit" 72 | "commander@2.20.3","MIT","https://github.com/tj/commander.js" 73 | "commondir@1.0.1","MIT","https://github.com/substack/node-commondir" 74 | "component-emitter@1.3.0","MIT","https://github.com/component/emitter" 75 | "concat-map@0.0.1","MIT","https://github.com/substack/node-concat-map" 76 | "concat-stream@1.6.2","MIT","https://github.com/maxogden/concat-stream" 77 | "console-browserify@1.1.0","MIT","https://github.com/Raynos/console-browserify" 78 | "constants-browserify@1.0.0","MIT","https://github.com/juliangruber/constants-browserify" 79 | "copy-concurrently@1.0.5","ISC","https://github.com/npm/copy-concurrently" 80 | "copy-descriptor@0.1.1","MIT","https://github.com/jonschlinkert/copy-descriptor" 81 | "core-util-is@1.0.2","MIT","https://github.com/isaacs/core-util-is" 82 | "create-ecdh@4.0.3","MIT","https://github.com/crypto-browserify/createECDH" 83 | "create-hash@1.2.0","MIT","https://github.com/crypto-browserify/createHash" 84 | "create-hmac@1.1.7","MIT","https://github.com/crypto-browserify/createHmac" 85 | "crypto-browserify@3.12.0","MIT","https://github.com/crypto-browserify/crypto-browserify" 86 | "css-line-break@1.1.1","MIT","https://github.com/niklasvh/css-line-break" 87 | "csstype@2.6.7","MIT","https://github.com/frenic/csstype" 88 | "cyclist@1.0.1","MIT","https://github.com/mafintosh/cyclist" 89 | "date-now@0.1.4","MIT","https://github.com/Colingo/date-now" 90 | "debug@2.6.9","MIT","https://github.com/visionmedia/debug" 91 | "decode-uri-component@0.2.0","MIT","https://github.com/SamVerschueren/decode-uri-component" 92 | "define-property@0.2.5","MIT","https://github.com/jonschlinkert/define-property" 93 | "define-property@1.0.0","MIT","https://github.com/jonschlinkert/define-property" 94 | "define-property@2.0.2","MIT","https://github.com/jonschlinkert/define-property" 95 | "des.js@1.0.0","MIT","https://github.com/indutny/des.js" 96 | "diffie-hellman@5.0.3","MIT","https://github.com/crypto-browserify/diffie-hellman" 97 | "dnd-core@7.7.0","MIT","https://github.com/react-dnd/react-dnd" 98 | "domain-browser@1.2.0","MIT","https://github.com/bevry/domain-browser" 99 | "duplexify@3.7.1","MIT","https://github.com/mafintosh/duplexify" 100 | "elliptic@6.5.1","MIT","https://github.com/indutny/elliptic" 101 | "emojis-list@2.1.0","MIT","https://github.com/kikobeats/emojis-list" 102 | "end-of-stream@1.4.4","MIT","https://github.com/mafintosh/end-of-stream" 103 | "enhanced-resolve@4.1.1","MIT","https://github.com/webpack/enhanced-resolve" 104 | "errno@0.1.7","MIT","https://github.com/rvagg/node-errno" 105 | "eslint-scope@4.0.3","BSD-2-Clause","https://github.com/eslint/eslint-scope" 106 | "esrecurse@4.2.1","BSD-2-Clause","https://github.com/estools/esrecurse" 107 | "estraverse@4.3.0","BSD-2-Clause","https://github.com/estools/estraverse" 108 | "events@3.0.0","MIT","https://github.com/Gozala/events" 109 | "evp_bytestokey@1.0.3","MIT","https://github.com/crypto-browserify/EVP_BytesToKey" 110 | "expand-brackets@2.1.4","MIT","https://github.com/jonschlinkert/expand-brackets" 111 | "extend-shallow@2.0.1","MIT","https://github.com/jonschlinkert/extend-shallow" 112 | "extend-shallow@3.0.2","MIT","https://github.com/jonschlinkert/extend-shallow" 113 | "extglob@2.0.4","MIT","https://github.com/micromatch/extglob" 114 | "fast-deep-equal@2.0.1","MIT","https://github.com/epoberezkin/fast-deep-equal" 115 | "fast-json-stable-stringify@2.0.0","MIT","https://github.com/epoberezkin/fast-json-stable-stringify" 116 | "figgy-pudding@3.5.1","ISC","https://github.com/zkat/figgy-pudding" 117 | "file-saver@2.0.2","MIT","https://github.com/eligrey/FileSaver.js" 118 | "fill-range@4.0.0","MIT","https://github.com/jonschlinkert/fill-range" 119 | "find-cache-dir@2.1.0","MIT","https://github.com/avajs/find-cache-dir" 120 | "find-up@3.0.0","MIT","https://github.com/sindresorhus/find-up" 121 | "flush-write-stream@1.1.1","MIT","https://github.com/mafintosh/flush-write-stream" 122 | "for-in@1.0.2","MIT","https://github.com/jonschlinkert/for-in" 123 | "fragment-cache@0.2.1","MIT","https://github.com/jonschlinkert/fragment-cache" 124 | "from2@2.3.0","MIT","https://github.com/hughsk/from2" 125 | "fs-write-stream-atomic@1.0.10","ISC","https://github.com/npm/fs-write-stream-atomic" 126 | "fs.realpath@1.0.0","ISC","https://github.com/isaacs/fs.realpath" 127 | "get-value@2.0.6","MIT","https://github.com/jonschlinkert/get-value" 128 | "glob-parent@3.1.0","ISC","https://github.com/es128/glob-parent" 129 | "glob@7.1.4","ISC","https://github.com/isaacs/node-glob" 130 | "graceful-fs@4.2.2","ISC","https://github.com/isaacs/node-graceful-fs" 131 | "has-value@0.3.1","MIT","https://github.com/jonschlinkert/has-value" 132 | "has-value@1.0.0","MIT","https://github.com/jonschlinkert/has-value" 133 | "has-values@0.1.4","MIT","https://github.com/jonschlinkert/has-values" 134 | "has-values@1.0.0","MIT","https://github.com/jonschlinkert/has-values" 135 | "hash-base@3.0.4","MIT","https://github.com/crypto-browserify/hash-base" 136 | "hash.js@1.1.7","MIT","https://github.com/indutny/hash.js" 137 | "hmac-drbg@1.0.1","MIT","https://github.com/indutny/hmac-drbg" 138 | "hoist-non-react-statics@3.3.0","BSD-3-Clause","https://github.com/mridgway/hoist-non-react-statics" 139 | "html2canvas@1.0.0-rc.5","MIT","https://github.com/niklasvh/html2canvas" 140 | "https-browserify@1.0.0","MIT","https://github.com/substack/https-browserify" 141 | "ieee754@1.1.13","BSD-3-Clause","https://github.com/feross/ieee754" 142 | "iferr@0.1.5","MIT","https://github.com/shesek/iferr" 143 | "imurmurhash@0.1.4","MIT","https://github.com/jensyt/imurmurhash-js" 144 | "infer-owner@1.0.4","ISC","https://github.com/npm/infer-owner" 145 | "inflight@1.0.6","ISC","https://github.com/npm/inflight" 146 | "inherits@2.0.1","ISC","https://github.com/isaacs/inherits" 147 | "inherits@2.0.3","ISC","https://github.com/isaacs/inherits" 148 | "inherits@2.0.4","ISC","https://github.com/isaacs/inherits" 149 | "invariant@2.2.4","MIT","https://github.com/zertosh/invariant" 150 | "is-accessor-descriptor@0.1.6","MIT","https://github.com/jonschlinkert/is-accessor-descriptor" 151 | "is-accessor-descriptor@1.0.0","MIT","https://github.com/jonschlinkert/is-accessor-descriptor" 152 | "is-binary-path@1.0.1","MIT","https://github.com/sindresorhus/is-binary-path" 153 | "is-buffer@1.1.6","MIT","https://github.com/feross/is-buffer" 154 | "is-data-descriptor@0.1.4","MIT","https://github.com/jonschlinkert/is-data-descriptor" 155 | "is-data-descriptor@1.0.0","MIT","https://github.com/jonschlinkert/is-data-descriptor" 156 | "is-descriptor@0.1.6","MIT","https://github.com/jonschlinkert/is-descriptor" 157 | "is-descriptor@1.0.2","MIT","https://github.com/jonschlinkert/is-descriptor" 158 | "is-extendable@0.1.1","MIT","https://github.com/jonschlinkert/is-extendable" 159 | "is-extendable@1.0.1","MIT","https://github.com/jonschlinkert/is-extendable" 160 | "is-extglob@2.1.1","MIT","https://github.com/jonschlinkert/is-extglob" 161 | "is-glob@3.1.0","MIT","https://github.com/jonschlinkert/is-glob" 162 | "is-glob@4.0.1","MIT","https://github.com/micromatch/is-glob" 163 | "is-number@3.0.0","MIT","https://github.com/jonschlinkert/is-number" 164 | "is-plain-object@2.0.4","MIT","https://github.com/jonschlinkert/is-plain-object" 165 | "is-windows@1.0.2","MIT","https://github.com/jonschlinkert/is-windows" 166 | "is-wsl@1.1.0","MIT","https://github.com/sindresorhus/is-wsl" 167 | "isarray@1.0.0","MIT","https://github.com/juliangruber/isarray" 168 | "isobject@2.1.0","MIT","https://github.com/jonschlinkert/isobject" 169 | "isobject@3.0.1","MIT","https://github.com/jonschlinkert/isobject" 170 | "js-tokens@4.0.0","MIT","https://github.com/lydell/js-tokens" 171 | "json-parse-better-errors@1.0.2","MIT","https://github.com/zkat/json-parse-better-errors" 172 | "json-schema-traverse@0.4.1","MIT","https://github.com/epoberezkin/json-schema-traverse" 173 | "json5@1.0.1","MIT","https://github.com/json5/json5" 174 | "kind-of@3.2.2","MIT","https://github.com/jonschlinkert/kind-of" 175 | "kind-of@4.0.0","MIT","https://github.com/jonschlinkert/kind-of" 176 | "kind-of@5.1.0","MIT","https://github.com/jonschlinkert/kind-of" 177 | "kind-of@6.0.2","MIT","https://github.com/jonschlinkert/kind-of" 178 | "loader-runner@2.4.0","MIT","https://github.com/webpack/loader-runner" 179 | "loader-utils@1.2.3","MIT","https://github.com/webpack/loader-utils" 180 | "locate-path@3.0.0","MIT","https://github.com/sindresorhus/locate-path" 181 | "loose-envify@1.4.0","MIT","https://github.com/zertosh/loose-envify" 182 | "lru-cache@5.1.1","ISC","https://github.com/isaacs/node-lru-cache" 183 | "make-dir@2.1.0","MIT","https://github.com/sindresorhus/make-dir" 184 | "mamacro@0.0.3","MIT","" 185 | "map-cache@0.2.2","MIT","https://github.com/jonschlinkert/map-cache" 186 | "map-visit@1.0.0","MIT","https://github.com/jonschlinkert/map-visit" 187 | "md5.js@1.3.5","MIT","https://github.com/crypto-browserify/md5.js" 188 | "memory-fs@0.4.1","MIT","https://github.com/webpack/memory-fs" 189 | "memory-fs@0.5.0","MIT","https://github.com/webpack/memory-fs" 190 | "micromatch@3.1.10","MIT","https://github.com/micromatch/micromatch" 191 | "miller-rabin@4.0.1","MIT","https://github.com/indutny/miller-rabin" 192 | "minimalistic-assert@1.0.1","ISC","https://github.com/calvinmetcalf/minimalistic-assert" 193 | "minimalistic-crypto-utils@1.0.1","MIT","https://github.com/indutny/minimalistic-crypto-utils" 194 | "minimatch@3.0.4","ISC","https://github.com/isaacs/minimatch" 195 | "minimist@0.0.8","MIT","https://github.com/substack/minimist" 196 | "minimist@1.2.0","MIT","https://github.com/substack/minimist" 197 | "mississippi@3.0.0","BSD-2-Clause","https://github.com/maxogden/mississippi" 198 | "mixin-deep@1.3.2","MIT","https://github.com/jonschlinkert/mixin-deep" 199 | "mkdirp@0.5.1","MIT","https://github.com/substack/node-mkdirp" 200 | "move-concurrently@1.0.1","ISC","https://github.com/npm/move-concurrently" 201 | "ms@2.0.0","MIT","https://github.com/zeit/ms" 202 | "nanomatch@1.2.13","MIT","https://github.com/micromatch/nanomatch" 203 | "neo-async@2.6.1","MIT","https://github.com/suguru03/neo-async" 204 | "node-libs-browser@2.2.1","MIT","https://github.com/webpack/node-libs-browser" 205 | "normalize-path@2.1.1","MIT","https://github.com/jonschlinkert/normalize-path" 206 | "normalize-path@3.0.0","MIT","https://github.com/jonschlinkert/normalize-path" 207 | "object-assign@4.1.1","MIT","https://github.com/sindresorhus/object-assign" 208 | "object-copy@0.1.0","MIT","https://github.com/jonschlinkert/object-copy" 209 | "object-visit@1.0.1","MIT","https://github.com/jonschlinkert/object-visit" 210 | "object.pick@1.3.0","MIT","https://github.com/jonschlinkert/object.pick" 211 | "once@1.4.0","ISC","https://github.com/isaacs/once" 212 | "os-browserify@0.3.0","MIT","https://github.com/CoderPuppy/os-browserify" 213 | "p-limit@2.2.1","MIT","https://github.com/sindresorhus/p-limit" 214 | "p-locate@3.0.0","MIT","https://github.com/sindresorhus/p-locate" 215 | "p-try@2.2.0","MIT","https://github.com/sindresorhus/p-try" 216 | "pako@1.0.10","(MIT AND Zlib)","https://github.com/nodeca/pako" 217 | "parallel-transform@1.2.0","MIT","https://github.com/mafintosh/parallel-transform" 218 | "parse-asn1@5.1.5","ISC","https://github.com/crypto-browserify/parse-asn1" 219 | "pascalcase@0.1.1","MIT","https://github.com/jonschlinkert/pascalcase" 220 | "path-browserify@0.0.1","MIT","https://github.com/substack/path-browserify" 221 | "path-dirname@1.0.2","MIT","https://github.com/es128/path-dirname" 222 | "path-exists@3.0.0","MIT","https://github.com/sindresorhus/path-exists" 223 | "path-is-absolute@1.0.1","MIT","https://github.com/sindresorhus/path-is-absolute" 224 | "pbkdf2@3.0.17","MIT","https://github.com/crypto-browserify/pbkdf2" 225 | "pify@4.0.1","MIT","https://github.com/sindresorhus/pify" 226 | "pkg-dir@3.0.0","MIT","https://github.com/sindresorhus/pkg-dir" 227 | "posix-character-classes@0.1.1","MIT","https://github.com/jonschlinkert/posix-character-classes" 228 | "process-nextick-args@2.0.1","MIT","https://github.com/calvinmetcalf/process-nextick-args" 229 | "process@0.11.10","MIT","https://github.com/shtylman/node-process" 230 | "promise-inflight@1.0.1","ISC","https://github.com/iarna/promise-inflight" 231 | "prop-types@15.7.2","MIT","https://github.com/facebook/prop-types" 232 | "prr@1.0.1","MIT","https://github.com/rvagg/prr" 233 | "public-encrypt@4.0.3","MIT","https://github.com/crypto-browserify/publicEncrypt" 234 | "pump@2.0.1","MIT","https://github.com/mafintosh/pump" 235 | "pump@3.0.0","MIT","https://github.com/mafintosh/pump" 236 | "pumpify@1.5.1","MIT","https://github.com/mafintosh/pumpify" 237 | "punycode@1.3.2","MIT","https://github.com/bestiejs/punycode.js" 238 | "punycode@1.4.1","MIT","https://github.com/bestiejs/punycode.js" 239 | "punycode@2.1.1","MIT","https://github.com/bestiejs/punycode.js" 240 | "querystring-es3@0.2.1","MIT","https://github.com/mike-spainhower/querystring" 241 | "querystring@0.2.0","MIT","https://github.com/Gozala/querystring" 242 | "randombytes@2.1.0","MIT","https://github.com/crypto-browserify/randombytes" 243 | "randomfill@1.0.4","MIT","https://github.com/crypto-browserify/randomfill" 244 | "react-dnd-html5-backend@7.7.0","MIT","https://github.com/react-dnd/react-dnd" 245 | "react-dnd@7.7.0","MIT","https://github.com/react-dnd/react-dnd" 246 | "react-dom@16.10.2","MIT","https://github.com/facebook/react" 247 | "react-dom@16.11.0","MIT","https://github.com/facebook/react" 248 | "react-is@16.10.2","MIT","https://github.com/facebook/react" 249 | "react-is@16.11.0","MIT","https://github.com/facebook/react" 250 | "react-onclickoutside@6.9.0","MIT","https://github.com/Pomax/react-onclickoutside" 251 | "react-redux@6.0.1","MIT","https://github.com/reduxjs/react-redux" 252 | "react@16.10.2","MIT","https://github.com/facebook/react" 253 | "react@16.11.0","MIT","https://github.com/facebook/react" 254 | "readable-stream@2.3.6","MIT","https://github.com/nodejs/readable-stream" 255 | "readdirp@2.2.1","MIT","https://github.com/paulmillr/readdirp" 256 | "redux@4.0.4","MIT","https://github.com/reduxjs/redux" 257 | "regenerator-runtime@0.13.3","MIT","https://github.com/facebook/regenerator/tree/master/packages/regenerator-runtime" 258 | "regex-not@1.0.2","MIT","https://github.com/jonschlinkert/regex-not" 259 | "remove-trailing-separator@1.1.0","ISC","https://github.com/darsain/remove-trailing-separator" 260 | "repeat-element@1.1.3","MIT","https://github.com/jonschlinkert/repeat-element" 261 | "repeat-string@1.6.1","MIT","https://github.com/jonschlinkert/repeat-string" 262 | "resolve-url@0.2.1","MIT","https://github.com/lydell/resolve-url" 263 | "ret@0.1.15","MIT","https://github.com/fent/ret.js" 264 | "rimraf@2.7.1","ISC","https://github.com/isaacs/rimraf" 265 | "ripemd160@2.0.2","MIT","https://github.com/crypto-browserify/ripemd160" 266 | "run-queue@1.0.3","ISC","https://github.com/iarna/run-queue" 267 | "safe-buffer@5.1.2","MIT","https://github.com/feross/safe-buffer" 268 | "safe-regex@1.1.0","MIT","https://github.com/substack/safe-regex" 269 | "scheduler@0.16.2","MIT","https://github.com/facebook/react" 270 | "scheduler@0.17.0","MIT","https://github.com/facebook/react" 271 | "schema-utils@1.0.0","MIT","https://github.com/webpack-contrib/schema-utils" 272 | "semver@5.7.1","ISC","https://github.com/npm/node-semver" 273 | "serialize-javascript@1.9.1","BSD-3-Clause","https://github.com/yahoo/serialize-javascript" 274 | "set-value@2.0.1","MIT","https://github.com/jonschlinkert/set-value" 275 | "setimmediate@1.0.5","MIT","https://github.com/YuzuJS/setImmediate" 276 | "sha.js@2.4.11","(MIT AND BSD-3-Clause)","https://github.com/crypto-browserify/sha.js" 277 | "shallowequal@1.1.0","MIT","https://github.com/dashed/shallowequal" 278 | "snapdragon-node@2.1.1","MIT","https://github.com/jonschlinkert/snapdragon-node" 279 | "snapdragon-util@3.0.1","MIT","https://github.com/jonschlinkert/snapdragon-util" 280 | "snapdragon@0.8.2","MIT","https://github.com/jonschlinkert/snapdragon" 281 | "source-list-map@2.0.1","MIT","https://github.com/webpack/source-list-map" 282 | "source-map-resolve@0.5.2","MIT","https://github.com/lydell/source-map-resolve" 283 | "source-map-support@0.5.13","MIT","https://github.com/evanw/node-source-map-support" 284 | "source-map-url@0.4.0","MIT","https://github.com/lydell/source-map-url" 285 | "source-map@0.5.7","BSD-3-Clause","https://github.com/mozilla/source-map" 286 | "source-map@0.6.1","BSD-3-Clause","https://github.com/mozilla/source-map" 287 | "split-string@3.1.0","MIT","https://github.com/jonschlinkert/split-string" 288 | "ssri@6.0.1","ISC","https://github.com/zkat/ssri" 289 | "static-extend@0.1.2","MIT","https://github.com/jonschlinkert/static-extend" 290 | "stream-browserify@2.0.2","MIT","https://github.com/browserify/stream-browserify" 291 | "stream-each@1.2.3","MIT","https://github.com/mafintosh/stream-each" 292 | "stream-http@2.8.3","MIT","https://github.com/jhiesey/stream-http" 293 | "stream-shift@1.0.0","MIT","https://github.com/mafintosh/stream-shift" 294 | "string_decoder@1.1.1","MIT","https://github.com/nodejs/string_decoder" 295 | "symbol-observable@1.2.0","MIT","https://github.com/blesh/symbol-observable" 296 | "tapable@1.1.3","MIT","https://github.com/webpack/tapable" 297 | "terser-webpack-plugin@1.4.1","MIT","https://github.com/webpack-contrib/terser-webpack-plugin" 298 | "terser@4.3.8","BSD-2-Clause","https://github.com/terser/terser" 299 | "through2@2.0.5","MIT","https://github.com/rvagg/through2" 300 | "timers-browserify@2.0.11","MIT","https://github.com/jryans/timers-browserify" 301 | "to-arraybuffer@1.0.1","MIT","https://github.com/jhiesey/to-arraybuffer" 302 | "to-object-path@0.3.0","MIT","https://github.com/jonschlinkert/to-object-path" 303 | "to-regex-range@2.1.1","MIT","https://github.com/micromatch/to-regex-range" 304 | "to-regex@3.0.2","MIT","https://github.com/jonschlinkert/to-regex" 305 | "treemindmap@1.4.6","MIT","https://github.com/3yaa3yaa/TreeMindMap" 306 | "tslib@1.10.0","Apache-2.0","https://github.com/Microsoft/tslib" 307 | "tty-browserify@0.0.0","MIT","https://github.com/substack/tty-browserify" 308 | "typedarray@0.0.6","MIT","https://github.com/substack/typedarray" 309 | "union-value@1.0.1","MIT","https://github.com/jonschlinkert/union-value" 310 | "unique-filename@1.1.1","ISC","https://github.com/iarna/unique-filename" 311 | "unique-slug@2.0.2","ISC","https://github.com/iarna/unique-slug" 312 | "unset-value@1.0.0","MIT","https://github.com/jonschlinkert/unset-value" 313 | "upath@1.2.0","MIT","https://github.com/anodynos/upath" 314 | "uri-js@4.2.2","BSD-2-Clause","https://github.com/garycourt/uri-js" 315 | "urix@0.1.0","MIT","https://github.com/lydell/urix" 316 | "url@0.11.0","MIT","https://github.com/defunctzombie/node-url" 317 | "use@3.1.1","MIT","https://github.com/jonschlinkert/use" 318 | "util-deprecate@1.0.2","MIT","https://github.com/TooTallNate/util-deprecate" 319 | "util@0.10.3","MIT","https://github.com/defunctzombie/node-util" 320 | "util@0.11.1","MIT","https://github.com/defunctzombie/node-util" 321 | "vm-browserify@1.1.0","MIT","https://github.com/substack/vm-browserify" 322 | "watchpack@1.6.0","MIT","https://github.com/webpack/watchpack" 323 | "webpack-sources@1.4.3","MIT","https://github.com/webpack/webpack-sources" 324 | "webpack@4.41.1","MIT","https://github.com/webpack/webpack" 325 | "worker-farm@1.7.0","MIT","https://github.com/rvagg/node-worker-farm" 326 | "wrappy@1.0.2","ISC","https://github.com/npm/wrappy" 327 | "xtend@4.0.2","MIT","https://github.com/Raynos/xtend" 328 | "y18n@4.0.0","ISC","https://github.com/yargs/y18n" 329 | "yallist@3.1.1","ISC","https://github.com/isaacs/yallist" 330 | "yarn@1.19.1","BSD-2-Clause","https://github.com/yarnpkg/yarn" -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "treemindmap", 3 | "version": "1.8.0-beta.3", 4 | "description": "", 5 | "main": "build/bundle.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/3yaa3yaa/TreeMindMap.git" 9 | }, 10 | "scripts": { 11 | "test": "jest", 12 | "build": "webpack --mode production" 13 | }, 14 | "jest": { 15 | "moduleNameMapper": { 16 | "\\.(css|scss)$": "/node_modules/jest-css-modules" 17 | } 18 | }, 19 | "author": { 20 | "name": "Nobuyuki Mukai" 21 | }, 22 | "license": "MIT", 23 | "dependencies": { 24 | "@3yaa3yaa/markdowntextbox": "^0.4.0", 25 | "file-saver": "~2.0.5", 26 | "html2canvas": "^1.0.0-rc.7", 27 | "react-dnd": "^11.1.3", 28 | "react-dnd-html5-backend": "^11.1.3", 29 | "react-redux": "^7.2.2", 30 | "redux": "^4.0.5", 31 | "yarn": "^1.22.10", 32 | "crypto-js": "^4.0.0" 33 | }, 34 | "peerDependencies": { 35 | "react": "^16.14.0" 36 | }, 37 | "devDependencies": { 38 | "react": "^16.14.0", 39 | "@babel/cli": "^7.12.10", 40 | "@babel/core": "^7.12.10", 41 | "@babel/plugin-proposal-object-rest-spread": "^7.12.1", 42 | "@babel/plugin-transform-react-jsx": "^7.12.12", 43 | "@babel/preset-env": "^7.12.11", 44 | "babel-core": "^7.0.0-bridge.0", 45 | "babel-jest": "^26.6.3", 46 | "babel-loader": "^8.2.2", 47 | "css-loader": "^5.0.1", 48 | "enzyme": "^3.11.0", 49 | "enzyme-adapter-react-16": "^1.15.5", 50 | "eslint": "^7.16.0", 51 | "file-loader": "^6.2.0", 52 | "jest": "^26.6.3", 53 | "jest-css-modules": "^2.1.0", 54 | "style-loader": "^2.0.0", 55 | "url-loader": "^4.1.1", 56 | "webpack": "^5.11.1", 57 | "webpack-cli": "^4.3.0" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Burger.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #humberger { 5 | position: relative; 6 | margin:0px; 7 | padding: 0px; 8 | height: 12px; 9 | width: 20px; 10 | display: inline-block; 11 | box-sizing: border-box; 12 | } 13 | 14 | #humberger div { 15 | position: absolute; 16 | left: 0; 17 | height: 2px; 18 | width: 20px; 19 | background-color: aliceblue; 20 | border-radius: 2px; 21 | display: inline-block; 22 | box-sizing: border-box; 23 | } 24 | #humberger div:nth-of-type(1) { 25 | top: 0px; 26 | } 27 | #humberger div:nth-of-type(2) { 28 | top: 5px; 29 | } 30 | #humberger div:nth-of-type(3) { 31 | bottom: 0px; 32 | } -------------------------------------------------------------------------------- /src/Burger.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import './Burger.css'; 3 | 4 | class Burger extends Component { 5 | 6 | constructor(props) { 7 | super(props); 8 | } 9 | 10 | render() { 11 | return ( 12 |
13 |
14 |
15 |
16 |
17 | ); 18 | } 19 | } 20 | 21 | export default Burger; 22 | -------------------------------------------------------------------------------- /src/Connector.css: -------------------------------------------------------------------------------- 1 | .Connector-Table { 2 | /*position: absolute;*/ 3 | top:0%; 4 | bottom:0%; 5 | left:0px; 6 | right:0px; 7 | margin-top: 0px; 8 | margin-bottom: 0px; 9 | margin-left:0px; 10 | margin-right:0px; 11 | display: table; 12 | width: 30px; 13 | height:100%; 14 | table-layout: fixed; 15 | } 16 | 17 | .Connector-Row-top { 18 | height:auto; 19 | display: table-row; 20 | } 21 | 22 | 23 | .Connector-Row-bottom { 24 | height:100%; 25 | display: table-row; 26 | } 27 | 28 | .Connector-Single-TopLeft { 29 | display: table-cell; 30 | border-bottom:solid; 31 | border-bottom-width: 1px; 32 | height:5px; 33 | } 34 | 35 | .Connector-Single-TopRight { 36 | display: table-cell; 37 | border-bottom:solid; 38 | border-bottom-width: 1px; 39 | height:5px; 40 | } 41 | 42 | .Connector-Single-BottomLeft { 43 | display: table-cell; 44 | 45 | } 46 | 47 | .Connector-Single-BottomRight { 48 | list-style: none; 49 | display: table-cell; 50 | } 51 | 52 | 53 | .Connector-Vertical-TopLeft { 54 | display: table-cell; 55 | height:5px; 56 | } 57 | 58 | .Connector-Vertical-TopRight { 59 | display: table-cell; 60 | border-left: solid; 61 | border-left-width: 1px; 62 | height:5px; 63 | } 64 | 65 | .Connector-Vertical-BottomLeft { 66 | display: table-cell; 67 | } 68 | 69 | .Connector-Vertical-BottomRight { 70 | list-style: none; 71 | display: table-cell; 72 | border-left: solid; 73 | border-left-width: 1px; 74 | } 75 | 76 | 77 | .Connector-Top-TopLeft { 78 | display: table-cell; 79 | border-bottom:solid; 80 | border-bottom-width: 1px; 81 | height:5px; 82 | } 83 | 84 | .Connector-Top-TopRight { 85 | display: table-cell; 86 | border-bottom:solid; 87 | border-bottom-width: 1px; 88 | height:5px; 89 | } 90 | 91 | .Connector-Top-BottomLeft { 92 | display: table-cell; 93 | height:100%; 94 | 95 | } 96 | 97 | .Connector-Top-BottomRight { 98 | display: table-cell; 99 | border-left: solid; 100 | border-left-width: 1px; 101 | height:100%; 102 | } 103 | 104 | 105 | .Connector-Middle-TopLeft { 106 | display: table-cell; 107 | height:5px; 108 | 109 | } 110 | 111 | .Connector-Middle-TopRight { 112 | display: table-cell; 113 | border-left:solid; 114 | border-left-width: 1px; 115 | border-bottom:solid; 116 | border-bottom-width: 1px; 117 | height:5px; 118 | 119 | /*border-bottom-left-radius: 10px;*/ 120 | 121 | } 122 | 123 | .Connector-Middle-BottomLeft { 124 | display: table-cell; 125 | height:100%; 126 | 127 | } 128 | 129 | .Connector-Middle-BottomRight { 130 | list-style: none; 131 | display: table-cell; 132 | border-left:solid; 133 | border-left-width: 1px; 134 | height:100%; 135 | 136 | } 137 | 138 | 139 | 140 | .Connector-Bottom-TopLeft { 141 | display: table-cell; 142 | height:5px; 143 | } 144 | 145 | .Connector-Bottom-TopRight { 146 | display: table-cell; 147 | border-left:solid; 148 | border-left-width: 1px; 149 | border-bottom:solid; 150 | border-bottom-width: 1px; 151 | height:5px; 152 | } 153 | 154 | .Connector-Bottom-BottomLeft { 155 | display: table-cell; 156 | height:100%; 157 | } 158 | 159 | .Connector-Bottom-BottomRight { 160 | display: table-cell; 161 | height:100%; 162 | } 163 | -------------------------------------------------------------------------------- /src/Connector.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import "./Connector.css" 3 | 4 | 5 | // const canvasWidth = 30 6 | // const canvasHeight = 30 7 | 8 | class Connector extends Component { 9 | 10 | constructor(props) { 11 | super(props); 12 | } 13 | componentDidMount() { 14 | } 15 | 16 | 17 | _getTableStyle() 18 | { 19 | let isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1; 20 | let isEdge = navigator.userAgent.toLowerCase().indexOf('edge') > -1; 21 | if(isFirefox||isEdge) 22 | { 23 | return {position:"absolute"} 24 | } 25 | else 26 | { 27 | return {height:"100%"} 28 | } 29 | } 30 | 31 | _getSingleConnector() 32 | { 33 | return ( 34 |
35 |
36 |
 
37 |
 
38 |
39 |
40 |
 
41 |
 
42 |
43 |
44 | ) 45 | } 46 | 47 | _getVerticalConnector() 48 | { 49 | return ( 50 |
51 |
52 |
 
53 |
 
54 |
55 |
56 |
 
57 |
 
58 |
59 |
60 | ) 61 | } 62 | 63 | _getTopConnector() 64 | { 65 | return ( 66 |
67 |
68 |
 
69 |
 
70 |
71 |
72 |
 
73 |
 
74 |
75 |
76 | ) 77 | } 78 | 79 | _getMiddleConnector() 80 | { 81 | return ( 82 |
83 |
84 |
 
85 |
 
86 |
87 |
88 |
 
89 |
 
90 |
91 |
92 | ) 93 | } 94 | 95 | 96 | _getBottomConnector() 97 | { 98 | return ( 99 |
100 |
101 |
 
102 |
 
103 |
104 |
105 |
 
106 |
 
107 |
108 |
109 | ) 110 | } 111 | render() { 112 | switch(this.props.mode) 113 | { 114 | case "SINGLE": 115 | return this._getSingleConnector() 116 | case "VERTICAL": 117 | return this._getVerticalConnector() 118 | case "TOP": 119 | return this._getTopConnector() 120 | case "MIDDLE": 121 | return this._getMiddleConnector() 122 | case "BOTTOM": 123 | return this._getBottomConnector() 124 | default: 125 | return
 
126 | } 127 | } 128 | } 129 | export default Connector; 130 | -------------------------------------------------------------------------------- /src/Deserializer.js: -------------------------------------------------------------------------------- 1 | import LeafData from "./LeafData"; 2 | import Property from "./Property"; 3 | import sha256 from 'crypto-js/sha256'; 4 | 5 | 6 | export default class Deserializer 7 | { 8 | constructor(text) 9 | { 10 | this.text=text; 11 | this.data=this.convertInitialData(this.text); 12 | } 13 | 14 | convertInitialData(givenData) 15 | { 16 | if (givenData==null) 17 | { 18 | return null; 19 | } 20 | else 21 | { 22 | let parsedData = typeof(givenData)=="string" ? JSON.parse(givenData) : givenData; 23 | let root; 24 | let property; 25 | if("root" in parsedData) 26 | { 27 | root=this.convertToLeafData(parsedData.root); 28 | } 29 | else 30 | { 31 | root=new LeafData(); 32 | } 33 | if("property" in parsedData) 34 | { 35 | property=this.convertToProperty(parsedData.property); 36 | } 37 | else 38 | { 39 | property=new Property(); 40 | } 41 | property.initialTreeHash = sha256(JSON.stringify(root)).toString(); 42 | 43 | return {root: root, property: property}; 44 | 45 | } 46 | } 47 | 48 | convertToLeafData(data) 49 | { 50 | let out=new LeafData(data.id, data.description, [], data.imgs, data.color); 51 | 52 | for(let child of data.children) 53 | { 54 | out.children.push(this.convertToLeafData(child)); 55 | } 56 | return out; 57 | } 58 | 59 | convertToProperty(data) 60 | { 61 | return Property.getNewObject(data); 62 | } 63 | } -------------------------------------------------------------------------------- /src/DnDLeaf.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { DragSource, DropTarget } from 'react-dnd' 3 | import Leaf from "./Leaf"; 4 | 5 | class DnDLeaf extends Component { 6 | 7 | constructor(props) { 8 | super(props); 9 | } 10 | 11 | render() { 12 | const { connectDragSource, connectDropTarget } = this.props; 13 | return ( 14 | connectDropTarget(connectDragSource( 15 |
16 | 27 |
28 | )) 29 | ); 30 | } 31 | } 32 | 33 | const dragSpec = { 34 | beginDrag: (props) => { return props.leafdata }, 35 | endDrag: (props,monitor,component)=>{ 36 | let dragged=monitor.getItem() 37 | let dropped=monitor.getDropResult() 38 | if(dropped!=null) 39 | { 40 | if(dragged.id!=dropped.id) 41 | { 42 | component.props.move(dragged.id, dropped.id); 43 | } 44 | } 45 | } 46 | } 47 | 48 | const dropSpec = { 49 | drop: (props, monitor, component)=> { 50 | return props.leafdata; 51 | } 52 | } 53 | 54 | 55 | function collectDrag(connect, monitor) { 56 | return { 57 | connectDragSource: connect.dragSource(), 58 | isDragging: monitor.isDragging() 59 | } 60 | } 61 | 62 | function collectDrop(connect, monitor) { 63 | return { 64 | connectDropTarget: connect.dropTarget(), 65 | isOver: monitor.isOver() 66 | } 67 | } 68 | 69 | DnDLeaf=DropTarget("leaf", dropSpec, collectDrop)(DnDLeaf) 70 | DnDLeaf=DragSource("leaf", dragSpec, collectDrag)(DnDLeaf) 71 | 72 | export default DnDLeaf ; 73 | -------------------------------------------------------------------------------- /src/ImageMenu.css: -------------------------------------------------------------------------------- 1 | 2 | .ImageMenu { 3 | display: block; 4 | width: 30px; 5 | } 6 | 7 | .ImageMenu-Label{ 8 | vertical-align: top; 9 | } 10 | 11 | .ImageMenu-Item { 12 | vertical-align: top; 13 | height: 1px; 14 | visibility: hidden; 15 | } 16 | 17 | 18 | .ImageMenu-Canvas{ 19 | display: none; 20 | } 21 | 22 | 23 | .ImageMenu-Label { 24 | display: block; 25 | } 26 | .ImageMenu-Img { 27 | display: inline-block; 28 | width:30px; 29 | } 30 | 31 | .ImageMenu-ButtonLabel { 32 | display: inline-block; 33 | color: whitesmoke; 34 | padding-top: 0px; 35 | padding-left: 3px; 36 | vertical-align: top; 37 | } -------------------------------------------------------------------------------- /src/ImageMenu.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import './ImageMenu.css'; 3 | import previewimg from './images/gopreviewwhite.png'; 4 | import downimg from './images/down.png'; 5 | import rightimg from './images/right.png'; 6 | import cameraimg from './images/camera.png'; 7 | import paletteimg from './images/palette.png'; 8 | import ImgCanvas from "./ImgCanvas"; 9 | import Property from "./Property"; 10 | 11 | export default class ImageMenu extends Component { 12 | 13 | constructor(props) { 14 | super(props); 15 | this.state={ 16 | imgcanvas:""} 17 | } 18 | 19 | 20 | colorPickHandler(e) 21 | { 22 | //e.preventDefault() 23 | let color=e.target.value 24 | let newleaf=this.props.leafdata 25 | newleaf.color=color 26 | this.props.edit(newleaf) 27 | } 28 | 29 | fileChangeHandler(e) 30 | { 31 | //e.preventDefault() 32 | let file = e.target.files[0] 33 | if(file!=undefined){ 34 | let reader = new FileReader() 35 | reader.onloadend = () => { 36 | let img=new Image() 37 | img.onload=()=>{ 38 | let w= 1024 39 | let ratio= w /img.width 40 | let h=img.height*ratio 41 | this.setState( {imgcanvas: 42 | { 46 | ctx.drawImage(img,0,0,w,h); 47 | let newleaf=this.props.leafdata 48 | let imagedata=canvas.toDataURL("image/jpeg") 49 | if (newleaf.imgs==null) 50 | {newleaf.imgs = [imagedata]} 51 | else 52 | {newleaf.imgs.push([imagedata])} 53 | this.props.edit(newleaf) 54 | } 55 | } 56 | > 57 | }) 58 | this.setState({imgcanvas:""}) 59 | } 60 | img.src=reader.result 61 | 62 | } 63 | reader.readAsDataURL(file) 64 | } 65 | } 66 | 67 | downButtonStyle() { 68 | if(this.props.leafdata.id===0) 69 | { 70 | return {display:"none"} 71 | } 72 | else 73 | { 74 | return {} 75 | } 76 | } 77 | 78 | render() { 79 | return
80 | 83 | {this.props.changePreviewMode(Property.previewMode().Tree)}} /> 85 | 86 | 87 | 90 | {this.fileChangeHandler(e)}} /> 92 | {this.state.imgcanvas} 93 | 94 | 97 | {this.colorPickHandler(e)}} /> 99 | 100 | 103 | {this.props.addChild(this.props.leafdata.id)}} /> 105 | 106 | 109 | {this.props.addSibling(this.props.leafdata.id)}} /> 111 | 112 |
113 | } 114 | } -------------------------------------------------------------------------------- /src/ImgCanvas.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .ImgCanvas-canvas { 4 | display: none; 5 | } 6 | -------------------------------------------------------------------------------- /src/ImgCanvas.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import './ImgCanvas.css' 3 | 4 | class ImgCanvas extends Component { 5 | 6 | constructor(props) { 7 | super(props); 8 | this.canvasRef=React.createRef() 9 | } 10 | 11 | componentDidMount() { 12 | let canvas = this.canvasRef; 13 | let ctx=canvas.getContext('2d') 14 | this.props.setImage(ctx,canvas) 15 | //this.props.updateLeaf(this.canvasRef.toDataURL("image/jpeg",0.1)) 16 | } 17 | 18 | render() { 19 | return {this.canvasRef=e}}> 20 | } 21 | } 22 | 23 | 24 | export default ImgCanvas; 25 | -------------------------------------------------------------------------------- /src/ImgViewer.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .ImgViewer { 4 | width:auto; 5 | } 6 | 7 | 8 | .ImgViewer-div { 9 | width: auto; 10 | margin:5px; 11 | 12 | } 13 | 14 | .ImgViewer-img { 15 | width: 180px; 16 | margin:5px; 17 | } 18 | -------------------------------------------------------------------------------- /src/ImgViewer.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import './ImgViewer.css'; 3 | 4 | class ImgViewer extends Component { 5 | 6 | constructor(props) { 7 | super(props); 8 | } 9 | 10 | _generateImgs(imgs) 11 | { 12 | return imgs.map((img)=>
) 13 | } 14 | 15 | 16 | _getDOM() 17 | { 18 | if (this.props.leafdata.imgs!=null) 19 | { 20 | return ( 21 |
22 | {this._generateImgs(this.props.leafdata.imgs)} 23 |
) 24 | } 25 | else 26 | { 27 | return
28 | } 29 | 30 | 31 | } 32 | 33 | render() { 34 | return ( 35 | this._getDOM() 36 | ); 37 | } 38 | } 39 | 40 | export default ImgViewer; 41 | -------------------------------------------------------------------------------- /src/InstructionMessage.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Property from "./Property"; 3 | 4 | 5 | export default class InstructionMessage extends Component { 6 | 7 | constructor(props) { 8 | super(props); 9 | this.previewMode=this.props.previewMode; 10 | } 11 | 12 | getContent() 13 | { 14 | switch (this.previewMode) { 15 | case Property.previewMode().Label: 16 | return
17 |
No Labels!
18 |
Try adding #label on your Mind Map!
19 |
20 | default: 21 | return
22 |
No content!
23 |
Start adding items on your Mind Map!
24 |
25 | } 26 | } 27 | 28 | getRandomImage() 29 | { 30 | let items = ['🥺','👻','👽','🙈','🙉','🙊','👺','👾','🤖']; 31 | let random = Math.floor( Math.random() * items.length ); 32 | return items[random]; 33 | } 34 | 35 | 36 | render() { 37 | return
38 |
{this.getRandomImage()}
39 | {this.getContent()} 40 |
41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /src/Leaf.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .Leaf-Row{ 4 | position: static; 5 | display: table-row; 6 | vertical-align: middle; 7 | } 8 | 9 | .Leaf-Columns{ 10 | position: static; 11 | display: table-cell; 12 | margin:0px; 13 | vertical-align: top; 14 | } 15 | 16 | .Leaf-Command { 17 | display:none; 18 | } 19 | .Leaf-Command-Label{ 20 | margin:0px; 21 | padding:0px; 22 | font-size: small; 23 | color: green; 24 | border:none; 25 | border-color: whitesmoke; 26 | border-width:1px; 27 | } 28 | -------------------------------------------------------------------------------- /src/Leaf.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import './Leaf.css'; 3 | import ImgViewer from './ImgViewer' 4 | import ImageMenu from "./ImageMenu"; 5 | import MarkDownTextBoxWrapper from "./MarkDownTextBoxWrapper"; 6 | import StateProvider from './StateProvider'; 7 | import LeafData from "./LeafData"; 8 | 9 | class Leaf extends Component { 10 | 11 | constructor(props) { 12 | super(props); 13 | this.leafTextAreaRef=React.createRef() 14 | this.leafRef=React.createRef() 15 | this.state={flag:true}; 16 | this.textValueBuffer=""; 17 | 18 | } 19 | 20 | edit() 21 | { 22 | let newleaf= LeafData.getNewObject(this.props.leafdata); 23 | newleaf.description= this.textValueBuffer; 24 | this.props.edit(newleaf); 25 | } 26 | 27 | 28 | keyDownHandler(e) { 29 | switch (e.keyCode) { 30 | case 13: //Enter 31 | if (e.shiftKey != true && this.props.leafdata.id!=0) { 32 | e.preventDefault(); 33 | this.props.addSibling(this.props.leafdata.id); 34 | } 35 | break; 36 | case 9: //Tab 37 | if (e.shiftKey!=true) 38 | { 39 | e.preventDefault() 40 | this.props.addChild(this.props.leafdata.id) 41 | } 42 | else 43 | { 44 | e.preventDefault() 45 | this.props.walk(StateProvider.whereToMove().LEVELUP); 46 | } 47 | break; 48 | case 46: //Delete 49 | e.preventDefault() 50 | this.props.delete(this.props.leafdata.id) 51 | break; 52 | case 38: //UP 53 | 54 | if (e.shiftKey!=true) 55 | { 56 | e.preventDefault() 57 | this.props.walk(StateProvider.whereToMove().UP); 58 | } 59 | break; 60 | case 40: //Down 61 | if (e.shiftKey!=true) 62 | { 63 | e.preventDefault(); 64 | this.props.walk(StateProvider.whereToMove().DOWN); 65 | break; 66 | } 67 | } 68 | } 69 | 70 | leafUpdateHandler(e) 71 | { 72 | this.textValueBuffer=e.target.value; 73 | this.edit() 74 | } 75 | 76 | stateChange() 77 | { 78 | this.setState({flag: this.state.flag===false}) 79 | } 80 | 81 | _getLeafStyle() 82 | { 83 | let color=this.props.leafdata.color; 84 | if(color==undefined){color="silver"}; 85 | 86 | return { 87 | textAlign: "left", 88 | backgroundColor: color, 89 | padding: "10px", 90 | marginTop: "1px", 91 | marginBottom: "1px", 92 | marginLeft:"0px", 93 | marginRight:"0px", 94 | borderRadius:"10px", 95 | resize: "both" 96 | } 97 | } 98 | 99 | _getMenuVisibility() 100 | { 101 | if(this._getIsFocused()) 102 | { 103 | return {display: "block" } 104 | } 105 | else 106 | { 107 | return {display: "none"} 108 | } 109 | } 110 | 111 | _getIsFocused() 112 | { 113 | if(this.props.leafdata.id==this.props.focusId) 114 | { 115 | return true; 116 | } 117 | else 118 | { 119 | return false; 120 | } 121 | } 122 | 123 | _getDescriptionStyle() 124 | { 125 | return {minWidth: "100px", maxWidth: "200px"}; 126 | } 127 | 128 | 129 | 130 | _getDOM(){ 131 | return ( 132 |
this.keyDownHandler(e)} 134 | style={this._getLeafStyle()} 135 | ref={(e)=>{this.leafRef=e}} > 136 |
137 |
{this.props.jump(this.props.leafdata.id)}} 139 | > 140 |
141 |
142 | this.leafUpdateHandler(e)} 144 | focus={this._getIsFocused()} 145 | descriptionStyle={this._getDescriptionStyle()} 146 | textAreaStyle={{height:"170px",fontFamily:"sans-serif", fontSize:"100%"}} 147 | /> 148 |
149 |
150 |
151 |
152 |
153 | {this.props.edit(leaf);this.stateChange();}} 155 | addChild={this.props.addChild} 156 | addSibling={this.props.addSibling} 157 | changePreviewMode={this.props.changePreviewMode} 158 | style={this._getMenuVisibility()}/> 159 |
160 |
161 |
162 | 163 | ) 164 | } 165 | 166 | render() { 167 | return ( 168 | this._getDOM() 169 | ); 170 | } 171 | } 172 | 173 | export default Leaf ; 174 | -------------------------------------------------------------------------------- /src/LeafData.js: -------------------------------------------------------------------------------- 1 | export default class LeafData{ 2 | constructor(id=0, description="", children=[], imgs=[],color="silver") 3 | { 4 | this.id=id; 5 | this.description=description; 6 | this.children=children; 7 | this.imgs=imgs; 8 | this.color=color; 9 | } 10 | 11 | static getNewObject(rawdata) 12 | { 13 | try{ 14 | return new LeafData(rawdata.id, rawdata.description, rawdata.children, rawdata.imgs, rawdata.color) 15 | }catch(e) 16 | { 17 | console.error("failed to generate new leaf object :" + e.message) 18 | 19 | } 20 | } 21 | 22 | isLeafDataClass() 23 | { 24 | return true; 25 | } 26 | 27 | getLeaf(id) 28 | { 29 | if(this.id===id) 30 | { 31 | return this; 32 | } 33 | else 34 | { 35 | for(let leaf of this.children) 36 | { 37 | let child=leaf.getLeaf(id) 38 | if(child!=null) 39 | { 40 | return child; 41 | } 42 | } 43 | } 44 | return null; 45 | } 46 | 47 | isNullObject() 48 | { 49 | if(this.children.length===0 && this.description==="" && this.imgs.length===0) 50 | { 51 | return true; 52 | } 53 | else 54 | { 55 | return false; 56 | } 57 | } 58 | 59 | getParent(id) 60 | { 61 | if(this.children.find(l => l.id===id) != undefined) 62 | { 63 | return this; 64 | } 65 | else 66 | { 67 | for(let l of this.children) 68 | { 69 | let parent=l.getParent(id); 70 | if(parent!=null) 71 | { 72 | return parent; 73 | } 74 | } 75 | } 76 | return null; 77 | } 78 | 79 | 80 | getSiblings(id) 81 | { 82 | return this.getParent(id).children; 83 | } 84 | 85 | getElderBrother(id) 86 | { 87 | let brothers=this.getSiblings(id); 88 | let elderbrother=null; 89 | for(let brother of brothers) 90 | { 91 | if(brother.id===id){return elderbrother}; 92 | elderbrother=brother; 93 | } 94 | return null; 95 | } 96 | 97 | isLastRecord(id) 98 | { 99 | if(this.getYoungerBrother(id)===null) 100 | { 101 | return true; 102 | } 103 | else 104 | { 105 | return false; 106 | } 107 | } 108 | 109 | getYoungerBrother(id) 110 | { 111 | let brothers=this.getSiblings(id); 112 | let wasMe=false; 113 | for(let brother of brothers) 114 | { 115 | if(wasMe){return brother}; 116 | if(brother.id===id){wasMe=true}; 117 | } 118 | return null; 119 | } 120 | 121 | 122 | 123 | getChildren( id) 124 | { 125 | let l = this.getLeaf(id); 126 | if(l===null) 127 | { 128 | return []; 129 | } 130 | else 131 | { 132 | return l.children; 133 | } 134 | } 135 | 136 | getAllFamilyMembers() 137 | { 138 | let out=[this]; 139 | out=out.concat(this.getAllChildren()); 140 | return out; 141 | } 142 | 143 | 144 | getAllChildren() 145 | { 146 | let out=[]; 147 | this._setChildrenInArray(this.id, out) 148 | return out; 149 | } 150 | 151 | 152 | _setChildrenInArray(id, out) 153 | { 154 | let current = this.getLeaf(id); 155 | if(id!=this.id) 156 | { 157 | out.push(current); 158 | } 159 | current.children.forEach((child)=>{this._setChildrenInArray(child.id, out)}); 160 | } 161 | 162 | filterAndSortLeafs(leafs, parentid) 163 | { 164 | let out=[]; 165 | if(Array.isArray(leafs) && leafs.length>0) 166 | { 167 | let bigbrother = leafs.filter((leaf)=>{return (leaf.parentid==parentid && leaf.elderbrotherid==0)})[0]; 168 | StateProvider.recursivelyGetSiblings(leafs, bigbrother, out); 169 | return out; 170 | } 171 | else 172 | { 173 | return null; 174 | } 175 | } 176 | 177 | getRandomId() 178 | { 179 | let chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; 180 | let str = ''; 181 | for (let i = 0; i < 8; i++) { 182 | str += chars.charAt(Math.floor(Math.random() * chars.length)); 183 | } 184 | return str; 185 | } 186 | 187 | getNewId() 188 | { 189 | const random = this.getRandomId() 190 | return this.getLeaf(random)===null ? random : this.getNewId() 191 | // return this.getLatestId()+1; 192 | } 193 | 194 | getLatestId() 195 | { 196 | let children=this.getAllChildren() 197 | if(children.length>0) 198 | { 199 | return children.sort((a,b)=>{return b.id-a.id})[0].id; 200 | } 201 | else 202 | { 203 | return 0; 204 | } 205 | } 206 | 207 | labelExists(label) 208 | { 209 | let regexp= new RegExp('#' + label+ '(:|\r\n|\n|\r| |$)','g'); 210 | if(regexp.test(this.description)) 211 | { 212 | return true; 213 | } 214 | else 215 | { 216 | return false; 217 | } 218 | } 219 | 220 | codeExists() 221 | { 222 | let regexp=/```([\n\r]|.)*```/; 223 | if(regexp.test(this.description)) 224 | { 225 | return true; 226 | } 227 | else 228 | { 229 | return false; 230 | } 231 | } 232 | 233 | 234 | getLabelValues(label) 235 | { 236 | let regexp; 237 | if(label==="") 238 | { 239 | regexp= new RegExp('#([^ ]+?)(\r\n|\n|\r| |$)','g') 240 | } 241 | else 242 | { 243 | regexp= new RegExp('#' + label+ ':([^ ]+?)(\r\n|\n|\r| |$)','g') 244 | }; 245 | return [...this.description.matchAll(regexp)].map(el=>{return el[1]}); 246 | } 247 | 248 | getLabelFieldsOfChildren() 249 | { 250 | let items=[this]; 251 | items=items.concat(this.getAllChildren()); 252 | let array=[]; 253 | if(items===null){return array}; 254 | for(let item of items) 255 | { 256 | let regexp = new RegExp('#([^ ]+?)(\r\n|\n|\r| |$)','g'); 257 | array=array.concat([...item.description.matchAll(regexp)].map(el=>{return el[1]})); 258 | } 259 | array=array.map(el=>{return el.replace(/:.*/,'')}); //Remove Value 260 | array=array.filter(el=>!isFinite(el)) //Remove Numbers 261 | array=array.filter((x, i, self) => self.indexOf(x) === i); //Remove Duplicates 262 | return array; 263 | } 264 | 265 | getNumericValuesOfChildren(label="") 266 | { 267 | return this.getChildrenValues(label).filter(el=>isFinite(el)); 268 | } 269 | 270 | getChildrenValues(label="") 271 | { 272 | let items=[this]; 273 | items=items.concat(this.getAllChildren()); 274 | let array=[]; 275 | if(items===null){return array}; 276 | for(let item of items) 277 | { 278 | array=array.concat(item.getLabelValues(label)); 279 | } 280 | return array; 281 | } 282 | 283 | 284 | 285 | sumLabelsOfChildren(label) 286 | { 287 | let array=this.getNumericValuesOfChildren(label); 288 | let reducer=(acc, cur)=>{ 289 | return acc+parseFloat(cur); 290 | }; 291 | return array.reduce(reducer,0); 292 | } 293 | 294 | countLabelsOfChildren(label) 295 | { 296 | let array=this.getNumericValuesOfChildren(label); 297 | return array.length; 298 | } 299 | 300 | meanLabelsOfChildren(label) 301 | { 302 | return Math.round((this.sumLabelsOfChildren(label)/this.countLabelsOfChildren(label)) * 10) / 10 // 出力:123.5 303 | } 304 | 305 | } -------------------------------------------------------------------------------- /src/MainWindow.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | .MainWindow { 5 | position: relative; 6 | } 7 | 8 | .MainWindow-Preview { 9 | position: absolute; 10 | z-index: 1; 11 | } 12 | 13 | .MainWindow-Content { 14 | position: absolute; 15 | z-index: 0; 16 | } 17 | -------------------------------------------------------------------------------- /src/MainWindow.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Tree from "./Tree"; 3 | import PreviewPanel from "./PreviewPanel"; 4 | import Property from "./Property"; 5 | import './MainWindow.css' 6 | 7 | class MainWindow extends Component { 8 | 9 | constructor(props) { 10 | super(props); 11 | this.mainRef = React.createRef(); 12 | this.mainContentRef = React.createRef(); 13 | this.height='0px'; 14 | } 15 | 16 | _getPreviewScreen() 17 | { 18 | if(this.props.property.previewMode!=Property.previewMode().none) 19 | { 20 | return 24 | } 25 | } 26 | 27 | _getTreeStyle() { 28 | if (this.props.property.previewMode != Property.previewMode().none) { 29 | return {filter: "blur(10px)", 30 | opacity: "0.7", 31 | position: "fixed", 32 | overflow:"hidden"} 33 | } 34 | } 35 | 36 | _getMainStyle() 37 | { 38 | return {height: this.height}; 39 | } 40 | 41 | 42 | render() { 43 | return
44 |
45 | {this._getPreviewScreen()} 46 |
47 |
{this.mainContentRef=e}} 49 | style={this._getTreeStyle()}> 50 | 64 |
65 |
66 | } 67 | } 68 | 69 | export default MainWindow; 70 | -------------------------------------------------------------------------------- /src/MarkDownTextBoxWrapper.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import './Leaf.css'; 3 | import { MarkdownTextBox } from '@3yaa3yaa/markdowntextbox'; 4 | import ReservedList from "./Reserved"; 5 | 6 | export default class MarkDownTextBoxWrapper extends Component { 7 | 8 | constructor(props) { 9 | super(props); 10 | this.reservedList = new ReservedList((label)=>{return this.props.leafdata.sumLabelsOfChildren(label)} 11 | , (label)=>{return this.props.leafdata.countLabelsOfChildren(label)}); 12 | } 13 | 14 | 15 | render() { 16 | return ( 17 | 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Menu.css: -------------------------------------------------------------------------------- 1 | 2 | .Menu { 3 | position: relative; 4 | } 5 | 6 | .Menu-Command { 7 | display:none; 8 | } 9 | 10 | 11 | .Menu-Command-Burger{ 12 | float: left; 13 | margin:0px; 14 | padding:0px; 15 | font-size: small; 16 | color: darkgray; 17 | border:none; 18 | border-color: whitesmoke; 19 | border-width:1px; 20 | } 21 | -------------------------------------------------------------------------------- /src/Menu.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import './Menu.css'; 3 | import Burger from './Burger' 4 | import MenuModal from './MenuModal' 5 | import onClickOutside from 'react-onclickoutside'; 6 | 7 | class Menu extends Component { 8 | 9 | constructor(props) { 10 | super(props); 11 | this.state={modalDisplay:false} 12 | } 13 | 14 | _closeModal() { 15 | this.setState({modalDisplay:false}) 16 | } 17 | 18 | 19 | _switchModal() 20 | { 21 | let nextState=(this.state.modalDisplay === false) 22 | this.setState({modalDisplay:nextState}) 23 | } 24 | _getModal() 25 | { 26 | return {this._closeModal()}}/> 32 | } 33 | 34 | 35 | _getPositionStyle(event) 36 | { 37 | if(this.state.modalDisplay==false) 38 | { 39 | const out={display:"none"} 40 | return out; 41 | } 42 | const out={ 43 | left: 20 , 44 | top: 0 45 | } 46 | return out; 47 | } 48 | 49 | handleClickOutside() { 50 | this._closeModal() 51 | } 52 | 53 | 54 | render() { 55 | return ( 56 |
57 | 62 | {this._getModal()} 63 |
64 | ); 65 | } 66 | } 67 | 68 | export default onClickOutside(Menu) ; 69 | -------------------------------------------------------------------------------- /src/MenuModal.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .MenuModal { 4 | position: absolute; 5 | width:auto; 6 | min-width: 120px; 7 | height:auto; 8 | background-color: whitesmoke; 9 | z-index: 1; 10 | border-radius:10px; 11 | filter: drop-shadow(1px 1px 1px rgba(0,0,0,0.6)); 12 | } 13 | 14 | .MenuModal-Command { 15 | display:block; 16 | margin-left: 20px; 17 | } 18 | .MenuModal-Command-Label{ 19 | margin:10px; 20 | padding:0px; 21 | font-size: small; 22 | color: darkgray; 23 | border:none; 24 | border-color: beige; 25 | border-width:1px; 26 | } 27 | 28 | .MenuModal-canvas{ 29 | display: none; 30 | } 31 | 32 | .MenuModal-Command-Burger{ 33 | float: left; 34 | margin:0px; 35 | padding:0px; 36 | font-size: small; 37 | color: darkgray; 38 | border:none; 39 | border-color: beige; 40 | border-width:1px; 41 | } 42 | -------------------------------------------------------------------------------- /src/MenuModal.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import './MenuModal.css'; 3 | import ImgCanvas from './ImgCanvas' 4 | 5 | class MenuModal extends Component { 6 | 7 | constructor(props) { 8 | super(props); 9 | this.state={ 10 | imgcanvas:""} 11 | } 12 | 13 | colorPickHandler(e) 14 | { 15 | //e.preventDefault() 16 | let color=e.target.value 17 | let newleaf=this.props.leafdata 18 | newleaf.color=color 19 | this.props.edit(newleaf) 20 | } 21 | 22 | fileChangeHander(e) 23 | { 24 | //e.preventDefault() 25 | let file = e.target.files[0] 26 | let reader = new FileReader() 27 | reader.onloadend = () => { 28 | let img=new Image() 29 | img.onload=()=>{ 30 | let w= 200 31 | let ratio= w /img.width 32 | let h=img.height*ratio 33 | this.setState( {imgcanvas: 34 | { 38 | ctx.drawImage(img,0,0,w,h); 39 | let newleaf=this.props.leafdata 40 | let imagedata=canvas.toDataURL("image/jpeg") 41 | if (newleaf.imgs==null) 42 | {newleaf.imgs = [imagedata]} 43 | else 44 | {newleaf.imgs.push([imagedata])} 45 | this.props.edit(newleaf) 46 | } 47 | } 48 | > 49 | }) 50 | this.setState({imgcanvas:""}) 51 | } 52 | img.src=reader.result 53 | 54 | } 55 | reader.readAsDataURL(file) 56 | } 57 | 58 | _getModal() 59 | { 60 | return( 61 |
{ this.menuModalRef=e}}> 62 | 64 | {this.fileChangeHander(e);this.props.closeModal()}}> 65 |
66 | 67 | {this.colorPickHandler(e);this.props.closeModal()}}> 68 | 69 |
70 | 71 | {this.props.addChild(this.props.leafdata.id);this.props.closeModal()}}> 72 |
73 | 74 | {this.props.addSibling(this.props.leafdata.id);this.props.closeModal()}}> 75 |
76 |
77 | ) 78 | } 79 | 80 | render() { 81 | //return this._getModal() 82 | return
{this._getModal()}{this.state.imgcanvas}
83 | } 84 | } 85 | 86 | export default MenuModal; 87 | -------------------------------------------------------------------------------- /src/PreviewLabels.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .PreviewLabel-Items { 4 | display: inline-block; 5 | margin: 5px; 6 | vertical-align: top; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /src/PreviewLabels.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import './PreviewLabels.css' 3 | import ReadOnlyLeaf from "./ReadOnlyLeaf"; 4 | import InstructionMessage from "./InstructionMessage"; 5 | import Property from "./Property"; 6 | 7 | export default class PreviewLabels extends Component { 8 | 9 | constructor(props) { 10 | super(props); 11 | this.leafdata=this.props.leafdata; 12 | this.fields=this.props.leafdata.getLabelFieldsOfChildren(); 13 | } 14 | 15 | getSummary(field) 16 | { 17 | let count = this.leafdata.countLabelsOfChildren(field); 18 | let sum=this.leafdata.sumLabelsOfChildren(field); 19 | let mean=this.leafdata.meanLabelsOfChildren(field); 20 | if(sum>0 && count>1) 21 | { 22 | return
23 |
{`Count: ${count}`}
24 |
{`Total: ${sum}`}
25 |
{`Average: ${mean}`}
26 |
27 |
28 | } 29 | else 30 | { 31 | return ""; 32 | } 33 | } 34 | 35 | getContent() 36 | { 37 | if(this.fields.length>0) 38 | { 39 | return this.fields.map((field, fieldid)=>{ 40 | let items=this.leafdata.getAllFamilyMembers().map((child, itemid)=>{ 41 | if(child.labelExists(field)) 42 | { 43 | return
46 | 47 |
; 48 | } 49 | else 50 | { 51 | return null; 52 | } 53 | }).filter((item)=>{return item!=null}); 54 | return

{field}

{items}{this.getSummary(field)}
55 | }) 56 | } 57 | else 58 | { 59 | return 60 | } 61 | } 62 | 63 | 64 | render() { 65 | return
{this.getContent()}
66 | } 67 | } 68 | 69 | -------------------------------------------------------------------------------- /src/PreviewList.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Property from "./Property"; 3 | import { MarkdownTextBox } from '@3yaa3yaa/markdowntextbox'; 4 | import ImgViewer from "./ImgViewer"; 5 | import InstructionMessage from "./InstructionMessage"; 6 | 7 | export default class PreviewList extends Component { 8 | 9 | constructor(props) { 10 | super(props); 11 | this.leafdata=this.props.leafdata; 12 | this.fields=this.props.leafdata.getLabelFieldsOfChildren(); 13 | } 14 | 15 | getHeader() 16 | { 17 | let array = ["Content","Images"]; 18 | array= array.concat(this.fields); 19 | array = array.map((el,index)=>{return
{el}
}) 20 | 21 | return
{array}
; 22 | } 23 | 24 | removeLabelAndFunction(text) 25 | { 26 | let out=text; 27 | out=out.replace(/#[^ ]+?(\n|\r|\r\n| |$)/g,''); 28 | out=out.replace(/=[^ ]+?(\n|\r|\r\n| |$)/g,''); 29 | return out; 30 | } 31 | 32 | getContent() 33 | { 34 | let array = [this.leafdata]; 35 | array=array.concat(this.leafdata.getAllChildren(this.leafdata.id)); 36 | return array.map((item,rownum)=>{ 37 | let cells=[]; 39 | cells.push() 40 | for(let field of this.fields) 41 | { 42 | let val=""; 43 | if(item.labelExists(field)) 44 | { 45 | let tmp = item.getLabelValues(field); 46 | if(tmp.length>0) 47 | { 48 | val = tmp; 49 | } 50 | else 51 | { 52 | val = '\u2705'; 53 | } 54 | } 55 | else 56 | { 57 | val = ''; 58 | } 59 | cells.push(val); 60 | } 61 | 62 | cells=cells.map((el,colnum)=>{return
{el}
}); 66 | return
{cells}
}); 67 | } 68 | 69 | GetPreviewListStyle() { 70 | return {display: "table", 71 | width:"auto", 72 | borderStyle: "solid", 73 | borderWidth: "0.3px", 74 | borderColor: "darkslategray", 75 | marginTop: "15px", 76 | marginRight:"15px"} 77 | } 78 | 79 | 80 | GetPreviewListHeaderStyle() { 81 | return {display: "table-header-group", 82 | fontWeight: "bold"} 83 | } 84 | 85 | GetPreviewListRowStyle() { 86 | return {display: "table-row"} 87 | } 88 | 89 | GetPreviewListCellStyle() { 90 | return {display: "table-cell", 91 | verticalAlign: "top", 92 | textAlign: "left", 93 | maxWidth:"600px", 94 | padding: "3px", 95 | borderStyle: "solid", 96 | borderWidth: "0.3px", 97 | borderColor: "darkslategray"} 98 | } 99 | 100 | getContentOrInstruction() 101 | { 102 | if(this.leafdata.isNullObject()) 103 | { 104 | return 105 | } 106 | else 107 | { 108 | return
109 | {this.getHeader()} 110 | {this.getContent()} 111 |
112 | } 113 | } 114 | 115 | 116 | render() { 117 | return this.getContentOrInstruction() 118 | } 119 | } 120 | 121 | -------------------------------------------------------------------------------- /src/PreviewMenu.css: -------------------------------------------------------------------------------- 1 | 2 | .PreviewMenu { 3 | margin-left: 10px; 4 | margin-top: 0px; 5 | margin-right: 10px; 6 | } 7 | 8 | .PreviewMenu-Item { 9 | visibility: hidden; 10 | height: 3px; 11 | margin: 0px; 12 | } 13 | 14 | .PreviewMenu-buttons{ 15 | list-style: none; 16 | } 17 | 18 | 19 | .PreviewMenu-button{ 20 | vertical-align: top; 21 | height:40px; 22 | list-style: none; 23 | } 24 | 25 | .html2canvas-container { width: 3000px !important; height: 3000px !important; } 26 | 27 | 28 | .PreviewMenu-Img { 29 | display: inline-block; 30 | width:30px; 31 | height: 30px; 32 | border-radius: 5px; 33 | /*border-style: solid;*/ 34 | background-color: lightgray; 35 | /*border-color: silver;*/ 36 | vertical-align: top; 37 | } 38 | 39 | .PreviewMenu-ButtonLabel { 40 | display: inline-block; 41 | color: gray; 42 | padding-top: 3px; 43 | padding-left: 5px; 44 | vertical-align: center; 45 | } -------------------------------------------------------------------------------- /src/PreviewMenu.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import './PreviewMenu.css'; 3 | import Property from "./Property"; 4 | import backToEditImg from './images/backtoedit.png' 5 | import goPreviewImg from './images/gopreview.png' 6 | import exportImg from './images/export.png' 7 | import tableImg from './images/gotable.png' 8 | import documentImg from './images/document.png' 9 | import labelimg from "./images/label.png" 10 | 11 | export default class PreviewMenu extends Component { 12 | 13 | constructor(props) { 14 | super(props); 15 | this.export=this.props.export; 16 | this.changePreviewMode=this.props.changePreviewMode; 17 | } 18 | 19 | getCommandButton(previewMode, buttonimg, text, onClick) 20 | { 21 | const id="PreviewMenu-" + text; 22 | return
  • 23 | 27 | {onClick(e)}} /> 29 |
  • 30 | } 31 | 32 | getCommandLabelStyle(previewMode) 33 | { 34 | if(previewMode===this.props.previewMode) 35 | { 36 | return {fontWeight:"bold"} 37 | } 38 | } 39 | 40 | getButtons() 41 | { 42 | return
      43 | {this.getCommandButton(Property.previewMode().none, backToEditImg,"Back" 44 | , () => {this.changePreviewMode(Property.previewMode().none)})} 45 | {this.getCommandButton(Property.previewMode().Tree, goPreviewImg,"Preview" 46 | , () => {this.changePreviewMode(Property.previewMode().Tree)})} 47 | {this.getCommandButton(Property.previewMode().Sentence, documentImg,"Composition" 48 | , () => {this.changePreviewMode(Property.previewMode().Sentence)})} 49 | {this.getCommandButton(Property.previewMode().Label, labelimg,"Labels" 50 | , () => {this.changePreviewMode(Property.previewMode().Label)})} 51 | {this.getCommandButton(Property.previewMode().List, tableImg,"List" 52 | , () => {this.changePreviewMode(Property.previewMode().List)})} 53 | {this.getCommandButton(Property.previewMode().none, exportImg,"Export" 54 | , () => {this.export()})} 55 |
    ; 56 | } 57 | 58 | render() { 59 | return
    60 | {this.getButtons()} 61 |
    62 | } 63 | } -------------------------------------------------------------------------------- /src/PreviewPanel.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .PreviewPanel { 4 | display:table-row; 5 | width:100%; 6 | min-width: 500px; 7 | height:auto; 8 | min-width: 400px; 9 | border-radius:10px; 10 | filter: drop-shadow(1px 1px 1px rgba(0,0,0,0.6)); 11 | overflow: auto; 12 | } 13 | 14 | .PreviewPanel-Menu { 15 | display: table-cell; 16 | margin-top: 5px; 17 | width: 175px; 18 | min-width: 175px; 19 | vertical-align: top; 20 | /*position: fixed;*/ 21 | margin-right: 0px; 22 | } 23 | .PreviewPanel-Main { 24 | display:table-cell; 25 | width: fit-content; 26 | margin-top: 4px; 27 | vertical-align: top; 28 | margin-left: 5px; 29 | } 30 | -------------------------------------------------------------------------------- /src/PreviewPanel.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import './PreviewPanel.css'; 3 | import Property from "./Property"; 4 | import Tree from './Tree' 5 | import PreviewMenu from "./PreviewMenu"; 6 | import PreviewLabels from "./PreviewLabels"; 7 | import html2canvas from "html2canvas"; 8 | import { saveAs } from "file-saver"; 9 | import PreviewSentence from "./PreviewSentence"; 10 | import PreviewList from "./PreviewList"; 11 | 12 | class PreviewPanel extends Component { 13 | 14 | constructor(props) { 15 | super(props); 16 | this.leafdata=this.props.leafdata; 17 | this.changePreviewMode=this.props.changePreviewMode; 18 | this.previewRef = React.createRef(); 19 | } 20 | 21 | getContent() 22 | { 23 | switch(this.props.previewMode) 24 | { 25 | case Property.previewMode().Tree: 26 | let props = new Property(); 27 | props.isReadOnly=Property.readOnlyLevel().hardReadOnly; 28 | return ; 29 | case Property.previewMode().List: 30 | return ; 31 | case Property.previewMode().Label: 32 | return ; 33 | case Property.previewMode().Sentence: 34 | return ; 35 | default: 36 | return ""; 37 | } 38 | } 39 | 40 | doExport() 41 | { 42 | switch (this.props.previewMode) { 43 | case Property.previewMode().Sentence: 44 | case Property.previewMode().Label: 45 | case Property.previewMode().List: 46 | let blob=new Blob([this.previewRef.current.innerHTML], {type: "text/plain;charset=utf-8"}) 47 | saveAs(blob,"treemindmap.html"); 48 | break; 49 | default: 50 | html2canvas(this.previewRef.current).then(function(canvas) { 51 | canvas.toBlob((blob => {saveAs(blob,'treemindmap.png')})); 52 | }); 53 | break; 54 | } 55 | } 56 | 57 | getPreviewMainStyle() 58 | { 59 | switch (this.props.previewMode) { 60 | case Property.previewMode().Sentence: 61 | case Property.previewMode().Label: 62 | case Property.previewMode().List: 63 | return {width:"100%"}; 64 | default: 65 | return {width:"fit-content"}; 66 | } 67 | } 68 | 69 | 70 | render() { 71 | return
    72 |
    73 | {this.doExport()}} 74 | changePreviewMode={this.changePreviewMode} 75 | previewMode={this.props.previewMode} 76 | /> 77 |
    78 |
    79 | {this.getContent()} 80 |
    81 |
    82 | } 83 | } 84 | 85 | export default PreviewPanel; 86 | -------------------------------------------------------------------------------- /src/PreviewSentence.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Property from "./Property"; 3 | import MarkDownTextBoxWrapper from "./MarkDownTextBoxWrapper"; 4 | import ImgViewer from "./ImgViewer"; 5 | import InstructionMessage from "./InstructionMessage"; 6 | import LeafData from "./LeafData"; 7 | 8 | export default class PreviewSentence extends Component { 9 | 10 | constructor(props) { 11 | super(props); 12 | this.leafdata=this.props.leafdata; 13 | this.fields=this.props.leafdata.getLabelFieldsOfChildren(); 14 | } 15 | 16 | getContent() 17 | { 18 | if(this.leafdata.isNullObject()) 19 | { 20 | return 21 | } 22 | else 23 | { 24 | let out=[]; 25 | this.fillContents(this.leafdata,1, out); 26 | return out; 27 | } 28 | } 29 | 30 | fillContents(leafdata, depth, out) 31 | { 32 | if(leafdata.description!="") 33 | { 34 | out.push(this.getTag(depth,leafdata)); 35 | } 36 | if(leafdata.imgs.length>0) 37 | { 38 | out.push(); 39 | } 40 | leafdata.children.forEach((child)=>this.fillContents(child, depth+1, out)) 41 | } 42 | 43 | removeHeader(text) 44 | { 45 | return text.replace(/h[1-7]\./g,''); 46 | } 47 | 48 | firstlineHasSpecialCharacter(text) { 49 | let re = /^[^\n]*(```|#|\=sum|\=count|\=mean)/g; 50 | return re.test(text); 51 | } 52 | 53 | 54 | 55 | getTag(depth,leafdata) 56 | { 57 | let outputdata = LeafData.getNewObject(leafdata); 58 | outputdata.description=this.removeHeader(leafdata.description) 59 | if(outputdata.children.length>0 && depth<7 && !this.firstlineHasSpecialCharacter(outputdata.description)) { 60 | outputdata.description = 'h' + depth + '.' + outputdata.description; 61 | } 62 | return
    63 | 64 |
    ; 65 | 66 | } 67 | 68 | 69 | render() { 70 | return
    71 | {this.getContent()} 72 |
    73 | } 74 | } 75 | 76 | -------------------------------------------------------------------------------- /src/Property.js: -------------------------------------------------------------------------------- 1 | export default class Property{ 2 | constructor(focusId=0, 3 | isReadOnly=Property.readOnlyLevel().canEdit, 4 | previewMode=Property.previewMode().none, 5 | initialTreeHash="") 6 | { 7 | this.focusId=focusId; 8 | this.isReadOnly=isReadOnly; 9 | this.previewMode=previewMode; 10 | this.initialTreeHash=initialTreeHash; 11 | } 12 | 13 | static readOnlyLevel() 14 | { 15 | return { 16 | canEdit:0, 17 | softReadOnly:1, 18 | hardReadOnly:2 19 | } 20 | } 21 | 22 | static previewMode() 23 | { 24 | return { 25 | none:0, 26 | Tree:1, 27 | List:2, 28 | Label:3, 29 | Sentence:4 30 | } 31 | } 32 | 33 | 34 | static getNewObject(rawdata) 35 | { 36 | try{ 37 | return new Property(rawdata.focusId, rawdata.isReadOnly, rawdata.previewMode, rawdata.initialTreeHash) 38 | }catch(e) 39 | { 40 | return new Property(); 41 | console.error("failed to generate new property object :" + e.message) 42 | } 43 | } 44 | 45 | 46 | 47 | } -------------------------------------------------------------------------------- /src/ReadOnlyLeaf.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Leaf from "./Leaf"; 3 | 4 | export default class ReadOnlyLeaf extends Component { 5 | 6 | constructor(props) { 7 | super(props); 8 | } 9 | 10 | render() { 11 | return ( 12 |
    13 | {}} 16 | addChild={()=>{}} 17 | delete={()=>{}} 18 | addSibling={()=>{}} 19 | move={()=>{}} 20 | walk={()=>{}} 21 | jump={()=>{}} 22 | changePreviewMode={()=>{}} 23 | /> 24 |
    25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Reserved.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Reserved } from '@3yaa3yaa/markdowntextbox' 3 | 4 | 5 | export default class ReservedList 6 | { 7 | constructor(callback_sum, callback_count) 8 | { 9 | let arr=[]; 10 | this.callback_sum=callback_sum; 11 | this.callback_count=callback_count; 12 | arr.push(new Reserved('#',[" ","\n","\r\n","\r"], (node, key)=>{return this.getTagJSX(node, key)},"label(:value)")) 13 | arr.push(new Reserved('=count(',[")"], (node, key)=>{return this.getCountJSX(node, key)},"label")) 14 | arr.push(new Reserved('=sum(',[")"], (node, key)=>{return this.getSumJSX(node, key)},"label")) 15 | arr.push(new Reserved('=mean(',[")"], (node, key)=>{return this.getMeanJSX(node, key)},"label")) 16 | this.items=arr; 17 | } 18 | 19 | getMeanJSX(text, key){ 20 | if(text===null || text===undefined){text=""}; 21 | return
    {this.callback_sum(text)/this.callback_count(text)}
    ; 22 | } 23 | 24 | getSumJSX(text, key) 25 | { 26 | if(text===null || text===undefined){text=""}; 27 | return
    {this.callback_sum(text)}
    ; 28 | } 29 | 30 | getCountJSX(text, key) 31 | { 32 | if(text===null || text===undefined){text=""}; 33 | return
    {this.callback_count(text)}
    ; 34 | } 35 | 36 | 37 | getTagJSX(text, key) 38 | { 39 | return
    {text}
    ; 40 | } 41 | 42 | getTagStyle() 43 | { 44 | return { 45 | display:"block", 46 | textAlign: "justify", 47 | verticalAlign:"middle", 48 | backgroundColor: "#FFDDFF", 49 | fontsize:"8px", 50 | paddingLeft: "10px", 51 | paddingRight: "10px", 52 | borderBottomLeftRadius:"20px", 53 | borderTopLeftRadius:"20px", 54 | borderBottomRightRadius:"20px", 55 | borderTopRightRadius:"20px", 56 | width: "fit-content", 57 | marginBottom:"2px", 58 | // height: "10px", 59 | color: "#FF00FF" 60 | } 61 | } 62 | 63 | getBoldTagStyle() 64 | { 65 | let tagstyle= this.getTagStyle(); 66 | tagstyle= Object.assign(tagstyle, {fontWeight:"bold"}) 67 | return tagstyle; 68 | } 69 | } 70 | 71 | 72 | -------------------------------------------------------------------------------- /src/StateProvider.js: -------------------------------------------------------------------------------- 1 | import LeafData from './LeafData' 2 | import Property from "./Property"; 3 | 4 | class StateProvider 5 | { 6 | // Action 7 | static deleteAction(id) 8 | { 9 | return { type: 'delete',id } 10 | } 11 | static addRootAction() 12 | { 13 | return { type: 'addRoot' } 14 | } 15 | static addSiblingAction(id) 16 | { 17 | return { type: 'addSibling', id } 18 | } 19 | static addChildAction(id) 20 | { 21 | return { type: 'addChild', id } 22 | } 23 | static editAction(leaf) 24 | { 25 | return { type: 'edit', leaf } 26 | } 27 | 28 | static moveAction(from, to) 29 | { 30 | return { type: 'move', from, to } 31 | } 32 | 33 | static walkAction(whereTo) 34 | { 35 | return { type: 'walk', whereTo } 36 | } 37 | 38 | static jumpAction(id) 39 | { 40 | return { type: 'jump', id } 41 | } 42 | 43 | static changeModeAction(mode) 44 | { 45 | return { type: 'changeMode', mode } 46 | } 47 | 48 | static changePreviewModeAction(mode) 49 | { 50 | return { type: 'changePreviewMode', mode } 51 | } 52 | 53 | 54 | // Reducer 55 | static leafReducer(state = { root: new LeafData(), property: new Property() }, action) { 56 | try{ 57 | let result; 58 | if(state.property.isReadOnly===Property.readOnlyLevel().canEdit || action.type==="changeMode" || action.type==="changePreviewMode") 59 | { 60 | switch (action.type) { 61 | case 'delete': 62 | result= StateProvider.delete(state, action.id,state.property.focusId); 63 | break; 64 | case 'addRoot': 65 | result= StateProvider.addRoot(state ,state.property.focusId,state.property.focusId); 66 | break; 67 | case 'addSibling': 68 | let youngerbrothers=state.root.getYoungerBrother(state.property.focusId); 69 | if(youngerbrothers===null) 70 | {result= StateProvider.addSibling(state ,action.id)} 71 | else 72 | {result = StateProvider.walk(state, state.property.focusId, StateProvider.whereToMove().DOWN)}; 73 | break; 74 | case 'addChild': 75 | let children = state.root.getChildren(state.property.focusId); 76 | if(children.length===0) 77 | {result = StateProvider.addChild(state ,action.id)} 78 | else 79 | {result = StateProvider.walk(state, state.property.focusId, StateProvider.whereToMove().LEVELDOWN)}; 80 | break; 81 | case 'edit': 82 | result= StateProvider.edit(state ,action.leaf,state.property.focusId); 83 | break; 84 | case 'move': 85 | result= StateProvider.move(state, action.from, action.to ,state.property.focusId); 86 | break; 87 | case 'walk': 88 | result = StateProvider.walk(state, state.property.focusId, action.whereTo); 89 | break; 90 | case 'jump': 91 | result = StateProvider.jump(state, action.id); 92 | break; 93 | case 'changeMode': 94 | result = StateProvider.changeMode(state, action.mode); 95 | break; 96 | case 'changePreviewMode': 97 | result = StateProvider.changePreviewMode(state, action.mode); 98 | break; 99 | default: 100 | result= state; 101 | break; 102 | } 103 | return result; 104 | } 105 | else 106 | { 107 | return state; 108 | } 109 | 110 | }catch(e) 111 | { 112 | console.error("Failed to update state: "+ e.message) 113 | return state; 114 | } 115 | 116 | } 117 | 118 | //Reducer implementation 119 | static addRoot(state,id,focusId) 120 | { 121 | if (state.root!=null) 122 | { 123 | return {root:state.root, property: state.property} 124 | } 125 | else 126 | { 127 | let leaf = new LeafData(0, "", []); 128 | let property = Property.getNewObject(state.property); 129 | property.focusId=leaf.id; 130 | return { root: leaf, property: property} 131 | } 132 | } 133 | 134 | static addChild(state, id) 135 | { 136 | let current=state.root.getLeaf(id); 137 | let leaf=new LeafData(state.root.getNewId(), "", []); 138 | current.children=current.children.concat(leaf); 139 | 140 | let property = Property.getNewObject(state.property); 141 | property.focusId=leaf.id; 142 | 143 | return { root: LeafData.getNewObject(state.root), property: property } 144 | } 145 | 146 | static addSibling(state, id) 147 | { 148 | let parent=state.root.getParent(id); 149 | if(parent===null) 150 | { 151 | return { root: state.root, property: state.property } 152 | } 153 | else 154 | { 155 | let leaf=new LeafData(state.root.getNewId()); 156 | parent.children=parent.children.concat(leaf); 157 | 158 | let property = Property.getNewObject(state.property); 159 | property.focusId=leaf.id; 160 | return { root: LeafData.getNewObject(state.root), property:property } 161 | } 162 | } 163 | 164 | static edit(state, newleaf, focusId) 165 | { 166 | let leaf = state.root.getLeaf(newleaf.id); 167 | leaf.description=newleaf.description; 168 | leaf.imgs=newleaf.imgs; 169 | leaf.color=newleaf.color; 170 | return { root: state.root , property:state.property} 171 | } 172 | 173 | static move(state, from, to, focusId) 174 | { 175 | let current=state.root.getLeaf(from); 176 | let currentParent = state.root.getParent(from); 177 | let destination = state.root.getLeaf(to); 178 | if(destination!=null && current!=null && currentParent!=null) 179 | { 180 | if(currentParent.id!=destination.id) 181 | { 182 | let newleaf = LeafData.getNewObject(current); 183 | destination.children=destination.children.concat(newleaf); 184 | currentParent.children=currentParent.children.filter(child=>child.id!=from); 185 | } 186 | } 187 | return { root: LeafData.getNewObject(state.root) , property:state.property} 188 | } 189 | 190 | 191 | static delete(state, id, focusId) 192 | { 193 | if(state.root!=null && state.root.id!=id) 194 | { 195 | let parent=state.root.getParent(id); 196 | parent.children=parent.children.filter(child=>child.id!=id); 197 | 198 | let property = Property.getNewObject(state.property); 199 | property.focusId=parent.id; 200 | 201 | return {root: LeafData.getNewObject(state.root), property: property} 202 | } 203 | else 204 | { 205 | return {root: state.root , property:state.property} 206 | } 207 | } 208 | 209 | 210 | static whereToMove() 211 | { 212 | return { 213 | LEVELDOWN: "LEVELDOWN", 214 | LEVELUP: "LEVELUP", 215 | DOWN: "DOWN", 216 | UP: "UP" 217 | } 218 | } 219 | 220 | 221 | static walk(state, focusId, whereToMove) 222 | { 223 | let destination = StateProvider.whereToMove(); 224 | let moveTo=""; 225 | switch (whereToMove) { 226 | case destination.UP: 227 | let elderBrother=state.root.getElderBrother(focusId); 228 | if(elderBrother!=null){moveTo=elderBrother.id} 229 | else{moveTo=focusId}; 230 | break; 231 | case destination.DOWN: 232 | let youngerBrother=state.root.getYoungerBrother(focusId); 233 | if(youngerBrother!=null){moveTo=youngerBrother.id} 234 | else{moveTo=focusId}; 235 | break; 236 | case destination.LEVELUP: 237 | let parent=state.root.getParent(focusId); 238 | if(parent!=null){moveTo=parent.id} 239 | else {moveTo=focusId}; 240 | break; 241 | case destination.LEVELDOWN: 242 | let children = state.root.getChildren(focusId); 243 | if(children.length>0){moveTo=children[0].id} 244 | else{moveTo=focusId} 245 | break; 246 | default: 247 | moveTo=focusId; 248 | break; 249 | } 250 | let property=Property.getNewObject(state.property) 251 | property.focusId=moveTo; 252 | 253 | return { root: state.root , property:property} 254 | } 255 | 256 | static jump(state, focusId) 257 | { 258 | let property=Property.getNewObject(state.property) 259 | property.focusId=focusId; 260 | return { root: state.root , property: property} 261 | } 262 | 263 | static changeMode(state, mode) 264 | { 265 | let property=Property.getNewObject(state.property) 266 | property.isReadOnly=mode; 267 | return { root: state.root , property: property} 268 | } 269 | 270 | 271 | static changePreviewMode(state, mode) 272 | { 273 | let property=Property.getNewObject(state.property) 274 | property.previewMode=mode; 275 | 276 | if(mode!=Property.previewMode().none) 277 | { 278 | property.isReadOnly=Property.readOnlyLevel().softReadOnly; 279 | } 280 | else 281 | { 282 | property.isReadOnly=Property.readOnlyLevel().canEdit; 283 | } 284 | return { root: state.root , property: property} 285 | } 286 | 287 | 288 | // Map Redux state to component props 289 | static mapStateToProps(state) { 290 | return { 291 | root: state.root, 292 | property: state.property 293 | } 294 | } 295 | 296 | // Map Redux actions to component props 297 | static mapDispatchToProps(dispatch) { 298 | return { 299 | delete: (id) => dispatch(StateProvider.deleteAction(id)), 300 | addRoot: () => dispatch(StateProvider.addRootAction()), 301 | addSibling: (id) => dispatch(StateProvider.addSiblingAction(id)), 302 | addChild: (id) => dispatch(StateProvider.addChildAction(id)), 303 | edit: (leaf) => dispatch(StateProvider.editAction(leaf)), 304 | move: (from, to) => dispatch(StateProvider.moveAction(from, to)), 305 | walk: (whereTo) => dispatch(StateProvider.walkAction(whereTo)), 306 | jump: (id) => dispatch(StateProvider.jumpAction(id)), 307 | changeMode: (mode) => dispatch(StateProvider.changeModeAction(mode)), 308 | changePreviewMode: (mode) => dispatch(StateProvider.changePreviewModeAction(mode)) 309 | } 310 | } 311 | } 312 | 313 | export default StateProvider; -------------------------------------------------------------------------------- /src/Tree.css: -------------------------------------------------------------------------------- 1 | 2 | ul { 3 | padding-left: 0px; 4 | } 5 | 6 | .Tree { 7 | display: flex; 8 | flex-direction: row; 9 | flex-wrap: nowrap; 10 | margin-top: 15px; 11 | } 12 | 13 | 14 | 15 | .Tree-Element { 16 | display: table ; 17 | list-style: none; 18 | } 19 | 20 | 21 | .Tree-Trunk { 22 | position:relative; 23 | list-style: none; 24 | display: table-cell; 25 | vertical-align: top; 26 | height:5px; 27 | min-width: 30px; 28 | } 29 | 30 | .Tree-Trunk-Sub { 31 | list-style: none; 32 | display: table-cell; 33 | vertical-align: top; 34 | height:10px; 35 | } 36 | 37 | 38 | .Tree-Branch { 39 | list-style: none; 40 | display: table-cell; 41 | } 42 | /* 43 | .Tree-Connector { 44 | position: center; 45 | width: 30px; 46 | height: 30px; 47 | margin-top: 15px; 48 | margin-left: 0px; 49 | border-left: 0px solid #5bc0de; 50 | border-top: 3px solid #5bc0de; 51 | vertical-align: middle; 52 | border-right: 8px solid #5bc0de; 53 | box-sizing: border-box; 54 | }*/ -------------------------------------------------------------------------------- /src/Tree.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import './Tree.css'; 3 | import DnDLeaf from "./DnDLeaf"; 4 | import Connector from "./Connector" 5 | import StateProvider from "./StateProvider" 6 | import { DndProvider } from "react-dnd"; 7 | import {HTML5Backend} from "react-dnd-html5-backend"; 8 | import Property from "./Property"; 9 | 10 | class Tree extends Component { 11 | 12 | constructor(props) { 13 | super(props); 14 | } 15 | 16 | _getFocusId() 17 | { 18 | if(this.props.property.isReadOnly!=Property.readOnlyLevel().canEdit) 19 | { 20 | return -1; 21 | } 22 | else 23 | { 24 | return this.props.property.focusId; 25 | } 26 | } 27 | 28 | safeExec(callback) 29 | { 30 | if(typeof callback === 'function') 31 | { 32 | return callback 33 | } 34 | else 35 | { 36 | return ()=>{return ""} 37 | } 38 | } 39 | 40 | _getLeaf(leaf) 41 | { 42 | return 53 | } 54 | 55 | _getConnector(leaf) 56 | { 57 | // let arr = [] 58 | // return arr; 59 | return ; 60 | } 61 | // 62 | // _getConnectorSub(originalleaf,rootleaf,result) 63 | // { 64 | // StateProvider.filterAndSortLeafs(this.props.root,rootleaf.id).slice(1).forEach( 65 | // (leaf)=>{ 66 | // result.push() 67 | // this._getConnectorSub(originalleaf,leaf,result) 68 | // }) 69 | // } 70 | 71 | _getVertical(leaf) 72 | { 73 | if(this._isLastRecord(leaf,this.props.root)) 74 | { 75 | return "" 76 | } 77 | else 78 | { 79 | return "VERTICAL" 80 | } 81 | } 82 | 83 | 84 | 85 | _formatLeaf(dataarr) { 86 | let out = [] 87 | if(dataarr!=null) 88 | { 89 | dataarr.forEach((leaf)=>{ 90 | out.push(( 91 |
      92 |
    • 93 | {this._getConnector(leaf)} 94 |
    • 95 |
    • 96 |
        97 |
      • 98 | {this._getLeaf(leaf)} 99 |
      • 100 |
      101 |
    • 102 |
    • 103 | {this._formatLeaf(leaf.getChildren(leaf.id))} 104 |
    • 105 |
    106 | )) 107 | }) 108 | } 109 | return
      {out}
    110 | } 111 | 112 | _getMode(root, id) 113 | { 114 | let out="" 115 | let parent=root.getParent(id); 116 | if(parent===null) 117 | {out=""} 118 | else 119 | { 120 | let filtered=parent.children; 121 | if(id==filtered[0].id) 122 | { 123 | if (filtered.length==1) 124 | {out="SINGLE"} 125 | else 126 | {out = "TOP"} 127 | } 128 | else 129 | { 130 | if(id==filtered[filtered.length-1].id) 131 | {out = "BOTTOM"} 132 | else 133 | {out="MIDDLE"} 134 | } 135 | } 136 | return out 137 | } 138 | 139 | _getTree() 140 | { 141 | return this._formatLeaf([this.props.root]) 142 | } 143 | 144 | 145 | render() { 146 | return ( 147 | 148 |
    149 | {this._getTree()} 150 |
    151 |
    152 | ); 153 | } 154 | } 155 | 156 | //Tree=DragDropContext(HTML5Backend)(Tree) 157 | export default Tree; 158 | -------------------------------------------------------------------------------- /src/images/backtoedit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/3yaa3yaa/TreeMindMap/7450413950a0ce32d0243309997e81fe27f1d766/src/images/backtoedit.png -------------------------------------------------------------------------------- /src/images/camera.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/3yaa3yaa/TreeMindMap/7450413950a0ce32d0243309997e81fe27f1d766/src/images/camera.png -------------------------------------------------------------------------------- /src/images/document.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/3yaa3yaa/TreeMindMap/7450413950a0ce32d0243309997e81fe27f1d766/src/images/document.png -------------------------------------------------------------------------------- /src/images/down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/3yaa3yaa/TreeMindMap/7450413950a0ce32d0243309997e81fe27f1d766/src/images/down.png -------------------------------------------------------------------------------- /src/images/export.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/3yaa3yaa/TreeMindMap/7450413950a0ce32d0243309997e81fe27f1d766/src/images/export.png -------------------------------------------------------------------------------- /src/images/gopreview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/3yaa3yaa/TreeMindMap/7450413950a0ce32d0243309997e81fe27f1d766/src/images/gopreview.png -------------------------------------------------------------------------------- /src/images/gopreviewwhite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/3yaa3yaa/TreeMindMap/7450413950a0ce32d0243309997e81fe27f1d766/src/images/gopreviewwhite.png -------------------------------------------------------------------------------- /src/images/gotable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/3yaa3yaa/TreeMindMap/7450413950a0ce32d0243309997e81fe27f1d766/src/images/gotable.png -------------------------------------------------------------------------------- /src/images/label.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/3yaa3yaa/TreeMindMap/7450413950a0ce32d0243309997e81fe27f1d766/src/images/label.png -------------------------------------------------------------------------------- /src/images/palette.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/3yaa3yaa/TreeMindMap/7450413950a0ce32d0243309997e81fe27f1d766/src/images/palette.png -------------------------------------------------------------------------------- /src/images/right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/3yaa3yaa/TreeMindMap/7450413950a0ce32d0243309997e81fe27f1d766/src/images/right.png -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 13 | monospace; 14 | } 15 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './index.css'; 3 | import StateProvider from './StateProvider'; 4 | import { Provider, connect } from 'react-redux' 5 | import {compose,createStore,applyMiddleware } from "redux"; 6 | import Deserializer from "./Deserializer"; 7 | import MainWindow from "./MainWindow"; 8 | 9 | // Connected Component 10 | const App = connect( 11 | StateProvider.mapStateToProps, 12 | StateProvider.mapDispatchToProps 13 | )(MainWindow) 14 | 15 | 16 | 17 | export const Map=props=>{ 18 | const { initialState, stateHandler, middleware } = props; 19 | let store; 20 | let deserializer=new Deserializer(initialState); 21 | let convertedInitialState=deserializer.data; 22 | 23 | const finalCreateStore = middleware ? compose( 24 | applyMiddleware(middleware) 25 | )(createStore): createStore; 26 | 27 | if(convertedInitialState == null) 28 | { 29 | // store = createStore(StateProvider.leafReducer) 30 | store = finalCreateStore(StateProvider.leafReducer) 31 | } 32 | else 33 | { 34 | // store = createStore(StateProvider.leafReducer, convertedInitialState) 35 | store = finalCreateStore(StateProvider.leafReducer, convertedInitialState) 36 | } 37 | if(stateHandler!=null) 38 | { 39 | store.subscribe(()=>stateHandler(store.getState())) 40 | } 41 | return ( 42 | 43 | 44 | 45 | ) 46 | } 47 | 48 | export function Render(initialState, stateHandler, elementId,ReactDOM, middleware){ 49 | ReactDOM.render( 50 | , 53 | document.getElementById(elementId) 54 | ) 55 | 56 | } 57 | 58 | // If you want your app to work offline and load faster, you can change 59 | // unregister() to register() below. Note this comes with some pitfalls. 60 | // Learn more about service workers: http://bit.ly/CRA-PWA 61 | //serviceWorker.unregister(); 62 | -------------------------------------------------------------------------------- /test/CSSStub.js: -------------------------------------------------------------------------------- 1 | module.exports = {}; 2 | -------------------------------------------------------------------------------- /test/Deserializer.test.js: -------------------------------------------------------------------------------- 1 | import Deserializer from '../src/Deserializer' 2 | import LeafData from "../src/LeafData"; 3 | 4 | describe('A suite', function() { 5 | 6 | 7 | it('should deserialize properly', function(){ 8 | let test ="{\"root\":{\"id\":0,\"description\":\"\",\"children\":[{\"id\":1,\"description\":\"\",\"children\":[{\"id\":2,\"description\":\"\",\"children\":[],\"imgs\":[],\"color\":\"silver\"},{\"id\":3,\"description\":\"\",\"children\":[],\"imgs\":[],\"color\":\"silver\"}],\"imgs\":[],\"color\":\"silver\"}],\"imgs\":[],\"color\":\"silver\"},\"focusId\":3}" 9 | let expected0=new LeafData(0); 10 | let expected1=new LeafData(1); 11 | let expected2=new LeafData(2); 12 | let expected3=new LeafData(3); 13 | expected0.children.push(expected1); 14 | expected1.children.push(expected2); 15 | expected1.children.push(expected3); 16 | 17 | let deserialized= new Deserializer(test); 18 | 19 | expect(deserialized.data.root instanceof LeafData).toBe(true); 20 | expect(deserialized.data.root).toEqual(expected0); 21 | }) 22 | 23 | }); 24 | -------------------------------------------------------------------------------- /test/LeafData.test.js: -------------------------------------------------------------------------------- 1 | import LeafData from "../src/LeafData"; 2 | 3 | describe('A suite', function() { 4 | 5 | it('should collect tree data properly', function(){ 6 | let data0= new LeafData(0,"data0",[]); 7 | let data1= new LeafData(1,"data1",[]); 8 | let data2= new LeafData(2,"data2",[]); 9 | let data3= new LeafData(3,"data3",[]); 10 | let data4= new LeafData(4,"data4",[]); 11 | let data5= new LeafData(5,"data5",[]); 12 | let data6= new LeafData(6,"data6",[]); 13 | let data7= new LeafData(7,"data7",[]); 14 | 15 | data0.children.push(data1); 16 | data0.children.push(data2); 17 | data1.children.push(data3); 18 | data2.children.push(data4); 19 | data4.children.push(data5); 20 | data5.children.push(data6); 21 | data4.children.push(data7); 22 | 23 | expect(data0.getLeaf(0)).toEqual(data0); 24 | expect(data0.getLeaf(1)).toEqual(data1); 25 | expect(data0.getLeaf(2)).toEqual(data2); 26 | expect(data0.getLeaf(3)).toEqual(data3); 27 | expect(data0.getLeaf(4)).toEqual(data4); 28 | expect(data0.getLeaf(5)).toEqual(data5); 29 | expect(data0.getLeaf(6)).toEqual(data6); 30 | 31 | expect(data1.getAllChildren()).toEqual([data3]); 32 | expect(data2.getAllChildren().sort((a,b)=>{return a.id-b.id})).toEqual([data4, data5,data6,data7]); 33 | expect(data0.getAllChildren().sort((a,b)=>{return a.id-b.id})).toEqual([data1,data2,data3,data4,data5,data6,data7]); 34 | expect(/^.{8}$/.test(data0.getNewId())).toEqual(true); 35 | expect(data0.getParent(6)).toEqual(data5) 36 | expect(data0.getSiblings(7)).toEqual([data5,data7]) 37 | }) 38 | 39 | 40 | it('should get Parents correctly', function(){ 41 | let data0= new LeafData(0,"data0",[]); 42 | let data1= new LeafData(1,"data1",[]); 43 | let data2= new LeafData(2,"data2",[]); 44 | let data3= new LeafData(3,"data3",[]); 45 | let data4= new LeafData(4,"data4",[]); 46 | 47 | data0.children.push(data1); 48 | data0.children.push(data2); 49 | data1.children.push(data3); 50 | data2.children.push(data4); 51 | 52 | expect(data0.getParent(1)).toEqual(data0); 53 | expect(data0.getParent(2)).toEqual(data0); 54 | expect(data0.getParent(3)).toEqual(data1); 55 | expect(data0.getParent(4)).toEqual(data2); 56 | }) 57 | 58 | 59 | it('should calculate correctly', function(){ 60 | let data0= new LeafData(0,"#10",[]); 61 | let data1= new LeafData(1,"abc#10",[]); 62 | let data2= new LeafData(2,"abc#10 def",[]); 63 | let data3= new LeafData(3,"abc#10.5",[]); 64 | 65 | data0.children.push(data1); 66 | data1.children.push(data2); 67 | data2.children.push(data3); 68 | 69 | expect(data0.sumLabelsOfChildren()).toEqual(40.5); 70 | expect(data1.sumLabelsOfChildren()).toEqual(30.5); 71 | expect(data2.sumLabelsOfChildren()).toEqual(20.5); 72 | expect(data3.sumLabelsOfChildren()).toEqual(10.5); 73 | 74 | expect(data0.countLabelsOfChildren()).toEqual(4); 75 | expect(data1.countLabelsOfChildren()).toEqual(3); 76 | expect(data2.countLabelsOfChildren()).toEqual(2); 77 | expect(data3.countLabelsOfChildren()).toEqual(1); 78 | 79 | 80 | expect(data0.getLabelFieldsOfChildren()).toEqual([]); 81 | }) 82 | 83 | it('should calculate multiple tags correctly', function(){ 84 | let data0= new LeafData(0,"#10 #10",[]); 85 | let data1= new LeafData(1,"abc#10 #10",[]); 86 | let data2= new LeafData(2,"abc#10 def #10",[]); 87 | let data3= new LeafData(3,"abc#10.5 #10",[]); 88 | 89 | data0.children.push(data1); 90 | data1.children.push(data2); 91 | data2.children.push(data3); 92 | 93 | 94 | expect(data0.sumLabelsOfChildren()).toEqual(80.5); 95 | expect(data1.sumLabelsOfChildren()).toEqual(60.5); 96 | expect(data2.sumLabelsOfChildren()).toEqual(40.5); 97 | expect(data3.sumLabelsOfChildren()).toEqual(20.5); 98 | 99 | expect(data0.countLabelsOfChildren()).toEqual(8); 100 | expect(data1.countLabelsOfChildren()).toEqual(6); 101 | expect(data2.countLabelsOfChildren()).toEqual(4); 102 | expect(data3.countLabelsOfChildren()).toEqual(2); 103 | 104 | expect(data0.getLabelFieldsOfChildren()).toEqual([]); 105 | }) 106 | 107 | it('should calculate label values correctly', function(){ 108 | let data0= new LeafData(0,"#test:10",[]); 109 | let data1= new LeafData(1,"abc#test:10",[]); 110 | let data2= new LeafData(2,"abc#test:10",[]); 111 | let data3= new LeafData(3,"abc#test:10.5",[]); 112 | let data4= new LeafData(4,"#test",[]); 113 | 114 | data0.children.push(data1); 115 | data1.children.push(data2); 116 | data2.children.push(data3); 117 | data3.children.push(data4); 118 | ; 119 | expect(data0.sumLabelsOfChildren("test")).toEqual(40.5); 120 | expect(data1.sumLabelsOfChildren("test")).toEqual(30.5); 121 | expect(data2.sumLabelsOfChildren("test")).toEqual(20.5); 122 | expect(data3.sumLabelsOfChildren("test")).toEqual(10.5); 123 | expect(data4.sumLabelsOfChildren("test")).toEqual(0); 124 | 125 | expect(data0.countLabelsOfChildren("test")).toEqual(4); 126 | expect(data1.countLabelsOfChildren("test")).toEqual(3); 127 | expect(data2.countLabelsOfChildren("test")).toEqual(2); 128 | expect(data3.countLabelsOfChildren("test")).toEqual(1); 129 | expect(data4.countLabelsOfChildren("test")).toEqual(0); 130 | 131 | 132 | expect(data0.getLabelFieldsOfChildren()).toEqual(["test"]); 133 | }) 134 | 135 | 136 | it('should returns proper fields', function(){ 137 | let data0= new LeafData(0,"#test1:10 #test2:100",[]); 138 | let data1= new LeafData(1,"abc#test1:10 #test2:100",[]); 139 | let data2= new LeafData(2,"abc#test1:10 #test2:100",[]); 140 | let data3= new LeafData(3,"abc#test1:10.5 #test2:100.5",[]); 141 | let data4= new LeafData(4,"#test3",[]); 142 | 143 | data0.children.push(data1); 144 | data1.children.push(data2); 145 | data2.children.push(data3); 146 | data3.children.push(data4); 147 | 148 | expect(data0.sumLabelsOfChildren("test1")).toEqual(40.5); 149 | expect(data1.sumLabelsOfChildren("test1")).toEqual(30.5); 150 | expect(data2.sumLabelsOfChildren("test1")).toEqual(20.5); 151 | expect(data3.sumLabelsOfChildren("test1")).toEqual(10.5); 152 | expect(data4.sumLabelsOfChildren("test1")).toEqual(0); 153 | 154 | expect(data0.sumLabelsOfChildren("test2")).toEqual(400.5); 155 | expect(data1.sumLabelsOfChildren("test2")).toEqual(300.5); 156 | expect(data2.sumLabelsOfChildren("test2")).toEqual(200.5); 157 | expect(data3.sumLabelsOfChildren("test2")).toEqual(100.5); 158 | expect(data4.sumLabelsOfChildren("test2")).toEqual(0); 159 | 160 | 161 | expect(data0.countLabelsOfChildren("test1")).toEqual(4); 162 | expect(data1.countLabelsOfChildren("test1")).toEqual(3); 163 | expect(data2.countLabelsOfChildren("test1")).toEqual(2); 164 | expect(data3.countLabelsOfChildren("test1")).toEqual(1); 165 | expect(data4.countLabelsOfChildren("test1")).toEqual(0); 166 | 167 | expect(data0.countLabelsOfChildren("test2")).toEqual(4); 168 | expect(data1.countLabelsOfChildren("test2")).toEqual(3); 169 | expect(data2.countLabelsOfChildren("test2")).toEqual(2); 170 | expect(data3.countLabelsOfChildren("test2")).toEqual(1); 171 | expect(data4.countLabelsOfChildren("test2")).toEqual(0); 172 | 173 | expect(data0.getLabelFieldsOfChildren()).toEqual(["test1","test2","test3"]); 174 | 175 | expect(data0.getLabelValues("test1")).toEqual(["10"]); 176 | expect(data0.getLabelValues("test2")).toEqual(["100"]); 177 | expect(data3.getLabelValues("test1")).toEqual(["10.5"]); 178 | expect(data3.getLabelValues("test2")).toEqual(["100.5"]); 179 | expect(data4.getLabelValues("test1")).toEqual([]); 180 | expect(data4.getLabelValues("test2")).toEqual([]); 181 | expect(data4.getLabelValues("test3")).toEqual([]); 182 | 183 | 184 | expect(data0.labelExists("test1")).toEqual(true); 185 | expect(data0.labelExists("test2")).toEqual(true); 186 | expect(data0.labelExists("test3")).toEqual(false); 187 | expect(data1.labelExists("test1")).toEqual(true); 188 | expect(data1.labelExists("test2")).toEqual(true); 189 | expect(data1.labelExists("test3")).toEqual(false); 190 | expect(data2.labelExists("test1")).toEqual(true); 191 | expect(data2.labelExists("test2")).toEqual(true); 192 | expect(data2.labelExists("test3")).toEqual(false); 193 | expect(data3.labelExists("test1")).toEqual(true); 194 | expect(data3.labelExists("test2")).toEqual(true); 195 | expect(data3.labelExists("test3")).toEqual(false); 196 | expect(data4.labelExists("test1")).toEqual(false); 197 | expect(data4.labelExists("test2")).toEqual(false); 198 | expect(data4.labelExists("test3")).toEqual(true); 199 | 200 | }) 201 | 202 | 203 | it('should calculate multiple tags correctly', function(){ 204 | let data0= new LeafData(0,"#10\n#10",[]); 205 | let data1= new LeafData(1,"#abc #10\n#10",[]); 206 | let data2= new LeafData(2,"#abc #10 #def\n#10",[]); 207 | let data3= new LeafData(3,"#abc #10.5\n#10",[]); 208 | 209 | data0.children.push(data1); 210 | data1.children.push(data2); 211 | data2.children.push(data3); 212 | 213 | expect(data0.sumLabelsOfChildren()).toEqual(80.5); 214 | expect(data1.sumLabelsOfChildren()).toEqual(60.5); 215 | expect(data2.sumLabelsOfChildren()).toEqual(40.5); 216 | expect(data3.sumLabelsOfChildren()).toEqual(20.5); 217 | 218 | expect(data0.countLabelsOfChildren()).toEqual(8); 219 | expect(data1.countLabelsOfChildren()).toEqual(6); 220 | expect(data2.countLabelsOfChildren()).toEqual(4); 221 | expect(data3.countLabelsOfChildren()).toEqual(2); 222 | 223 | 224 | expect(data0.getLabelFieldsOfChildren()).toEqual(["abc","def"]); 225 | }) 226 | 227 | it('should calculate multiple tags correctly simple version', function(){ 228 | let data0= new LeafData(0,"#10\n#10",[]); 229 | 230 | 231 | expect(data0.getLabelValues("")).toEqual(["10","10"]); 232 | 233 | expect(data0.sumLabelsOfChildren()).toEqual(20); 234 | expect(data0.countLabelsOfChildren()).toEqual(2); 235 | }) 236 | 237 | 238 | it('should know it has code block', function(){ 239 | let data0= new LeafData(0,"```\ntest\n```",[]); 240 | let data1= new LeafData(0,"```\ntest\n",[]); 241 | let data2= new LeafData(0,"test",[]); 242 | let data3= new LeafData(0,"```\ntest\n``",[]); 243 | let data4= new LeafData(0,"```\ntest``\ntest\n```",[]); 244 | let data5= new LeafData(0,"test\n```\ntest``\ntest\n```\ntest",[]); 245 | 246 | 247 | expect(data0.codeExists()).toEqual(true); 248 | expect(data1.codeExists()).toEqual(false); 249 | expect(data2.codeExists()).toEqual(false); 250 | expect(data3.codeExists()).toEqual(false); 251 | expect(data4.codeExists()).toEqual(true); 252 | expect(data5.codeExists()).toEqual(true); 253 | }) 254 | 255 | 256 | 257 | }); 258 | -------------------------------------------------------------------------------- /test/PNGStub.js: -------------------------------------------------------------------------------- 1 | module.exports = {}; 2 | -------------------------------------------------------------------------------- /test/StateProvider.test.js: -------------------------------------------------------------------------------- 1 | import LeafData from "../src/LeafData"; 2 | import Property from "../src/Property"; 3 | import StateProvider from "../src/StateProvider"; 4 | 5 | describe('A suite', function() { 6 | 7 | it('should add root properly', function(){ 8 | let tested = StateProvider.addRoot({root:null, property:new Property()}, 0, 0) 9 | expect(tested.root).toEqual(new LeafData(0,"",[])); 10 | }) 11 | 12 | it('should add children properly', function(){ 13 | let tested = StateProvider.addRoot({root:null, property:new Property()}, 0, 0) 14 | tested=StateProvider.addChild(tested,0); 15 | 16 | // let expected1=new LeafData(0,"",[]); 17 | // let expected2=new LeafData(1,"",[]); 18 | // expected1.children.push(expected2); 19 | expect(tested.root.id).toEqual(0); 20 | expect(tested.root.description).toEqual(""); 21 | expect(/^.{8}$/.test(tested.root.children[0].id) ).toBe(true); 22 | }) 23 | 24 | it('cannot add siblings below the root', function(){ 25 | let tested = StateProvider.addRoot({root:null, property:new Property()}, 0, 0) 26 | tested=StateProvider.addSibling(tested,0); 27 | 28 | let expected1=new LeafData(0); 29 | expect(tested.root).toEqual(expected1); 30 | }) 31 | 32 | 33 | 34 | 35 | 36 | }); 37 | -------------------------------------------------------------------------------- /test/map.test.js: -------------------------------------------------------------------------------- 1 | import { Map } from "../src/index"; 2 | import React from 'react'; 3 | import { shallow, mount, render } from 'enzyme'; 4 | import LeafData from "../src/LeafData"; 5 | import Property from "../src/Property"; 6 | 7 | 8 | describe('A suite', function() { 9 | 10 | it('should add node properly', function(){ 11 | const wrapper=mount(); 12 | expect(wrapper.find('.Leaf').length).toBe(1); 13 | wrapper.find('[id="0-menu-right"]').simulate("click"); 14 | expect(wrapper.find('.Leaf').length).toBe(2); 15 | }); 16 | 17 | it('should edit value properly', function(){ 18 | const wrapper=mount(); 19 | wrapper.find('.Leaf').find('.text').simulate("change", { target: { value: "foo" }}); 20 | expect(wrapper.find('.Leaf').at(0).text()).toEqual(expect.stringContaining("foo")); 21 | }); 22 | 23 | it('should edit value properly to an existing map', function(){ 24 | let data0= new LeafData(0,"first level:\n=sum()",[]); 25 | let data1= new LeafData(1,"second level:\n=count()",[]); 26 | let data2= new LeafData(2,"third level:\n#10.5",[]); 27 | let data3= new LeafData(3,"fourth level:\n#20.5",[]); 28 | let data4= new LeafData(4,"fifth level:\n#30.5",[]); 29 | data0.children.push(data1); 30 | data1.children.push(data2); 31 | data2.children.push(data3); 32 | data3.children.push(data4); 33 | let prop=new Property(); 34 | 35 | const wrapper=mount(); 36 | wrapper.find('.Leaf').at(0).find('.text').simulate("change", { target: { value: "foo" }}); 37 | expect(wrapper.find('.Leaf').at(0).text()).toEqual(expect.stringContaining("foo")); 38 | expect(wrapper.find('.Leaf').at(1).text()).toEqual(expect.stringContaining("second")); 39 | }); 40 | 41 | }); 42 | 43 | 44 | -------------------------------------------------------------------------------- /test/middleware.test.js: -------------------------------------------------------------------------------- 1 | import { Map } from "../src/index"; 2 | import React from 'react'; 3 | import { shallow, mount, render } from 'enzyme'; 4 | import LeafData from "../src/LeafData"; 5 | import Property from "../src/Property"; 6 | 7 | const middleware = store => next => action => { 8 | next(action); 9 | // console.log("middleware called" + JSON.stringify(action)) 10 | setTimeout(()=> console.log("middleware call every 1 sec") , 1000) 11 | }; 12 | 13 | describe('A suite', function() { 14 | 15 | jest.useFakeTimers(); 16 | it('should execute middleware call', function(){ 17 | const wrapper=mount(); 18 | wrapper.find('.Leaf').find('.text').simulate("change", { target: { value: "foo" }}); 19 | expect(wrapper.find('.Leaf').at(0).text()).toEqual(expect.stringContaining("foo")); 20 | jest.runAllTimers() 21 | }); 22 | 23 | 24 | }); 25 | 26 | 27 | -------------------------------------------------------------------------------- /test/previewLabel.test.js: -------------------------------------------------------------------------------- 1 | import PreviewLabels from "../src/PreviewLabels"; 2 | import LeafData from "../src/LeafData"; 3 | import React from 'react'; 4 | import { shallow, mount, render } from 'enzyme'; 5 | 6 | 7 | describe('A suite', function() { 8 | 9 | it('should calculate numbers properly', function(){ 10 | 11 | const sel = id => `[testkey="${id}"]`; 12 | let data0= new LeafData(0,"first level:\n=sum()",[]); 13 | let data1= new LeafData(1,"second level:\n=count()",[]); 14 | let data2= new LeafData(2,"third level:\n#10.5",[]); 15 | let data3= new LeafData(3,"fourth level:\n#20.5",[]); 16 | let data4= new LeafData(4,"fifth level:\n#30.5",[]); 17 | 18 | data0.children.push(data1); 19 | data1.children.push(data2); 20 | data2.children.push(data3); 21 | data3.children.push(data4); 22 | 23 | const wrapper=mount(); 24 | expect(wrapper.exists(sel('hasnodata'))).toEqual(true); 25 | }); 26 | 27 | 28 | it('should recognize label exists properly', function(){ 29 | let data0= new LeafData(0,"first level",[]); 30 | let data1= new LeafData(1,"second level",[]); 31 | let data2= new LeafData(2,"third level:\n#label1",[]); 32 | let data3= new LeafData(3,"fourth level:\n#label2",[]); 33 | let data4= new LeafData(4,"fifth level:\n#label3",[]); 34 | 35 | data0.children.push(data1); 36 | data1.children.push(data2); 37 | data2.children.push(data3); 38 | data3.children.push(data4); 39 | 40 | 41 | const sel = id => `[testkey="${id}"]`; 42 | const wrapper=mount(); 43 | //console.log(wrapper.debug()); 44 | 45 | expect(wrapper.exists(sel('hasnodata'))).toEqual(false); 46 | expect(wrapper.find(sel('val-0')).at(0).text()).toEqual(expect.stringContaining("label1")); 47 | //expect(wrapper.exists(sel('val-0-0'))).toEqual(true); 48 | expect(wrapper.find(sel('val-0')).find('.description').text()).toEqual('third level:\nlabel1'); 49 | 50 | 51 | expect(wrapper.find(sel('val-1')).at(0).text()).toEqual(expect.stringContaining("label2")); 52 | expect(wrapper.find(sel('val-1')).find('.description').text()).toEqual('fourth level:\nlabel2'); 53 | 54 | 55 | expect(wrapper.find(sel('val-2')).at(0).text()).toEqual(expect.stringContaining("label3")); 56 | expect(wrapper.find(sel('val-2')).find('.description').text()).toEqual('fifth level:\nlabel3'); 57 | 58 | expect(wrapper.exists(sel('val-3'))).toEqual(false); 59 | 60 | }); 61 | 62 | it('should calculate label values properly', function(){ 63 | let data0= new LeafData(0,"first level:\n=sum(label)",[]); 64 | let data1= new LeafData(1,"second level:\n=count(label)",[]); 65 | let data2= new LeafData(2,"third level:\n#label:10.5",[]); 66 | let data3= new LeafData(3,"fourth level:\n#label:20.5",[]); 67 | let data4= new LeafData(4,"fifth level:\n#label:30.5",[]); 68 | 69 | data0.children.push(data1); 70 | data1.children.push(data2); 71 | data2.children.push(data3); 72 | data3.children.push(data4); 73 | 74 | 75 | const sel = id => `[testkey="${id}"]`; 76 | const wrapper=mount(); 77 | //console.log(wrapper.debug()); 78 | expect(wrapper.exists(sel('hasnodata'))).toEqual(false); 79 | expect(wrapper.find(sel('val-0')).at(0).text()).toEqual(expect.stringContaining("label")); 80 | 81 | expect(wrapper.find(sel('val-0')).find('.description').at(0).text()).toEqual('third level:\nlabel:10.5'); 82 | expect(wrapper.find(sel('val-0')).find('.description').at(1).text()).toEqual('fourth level:\nlabel:20.5'); 83 | expect(wrapper.find(sel('val-0')).find('.description').at(2).text()).toEqual('fifth level:\nlabel:30.5'); 84 | 85 | expect(wrapper.exists(sel('val-1'))).toEqual(false); 86 | 87 | }); 88 | 89 | }); 90 | 91 | 92 | -------------------------------------------------------------------------------- /test/previewList.test.js: -------------------------------------------------------------------------------- 1 | import PreviewList from "../src/PreviewList"; 2 | import LeafData from "../src/LeafData"; 3 | import React from 'react'; 4 | import { shallow, mount, render } from 'enzyme'; 5 | 6 | 7 | describe('A suite', function() { 8 | 9 | it('should display items properly', function(){ 10 | let data0= new LeafData(0,"data0",[]); 11 | let data1= new LeafData(1,"data1",[]); 12 | let data2= new LeafData(2,"data2",[]); 13 | let data3= new LeafData(3,"data3",[]); 14 | let data4= new LeafData(4,"data4",[]); 15 | let data5= new LeafData(5,"data5",[]); 16 | let data6= new LeafData(6,"data6",[]); 17 | let data7= new LeafData(7,"data7",[]); 18 | 19 | data0.children.push(data1); 20 | data1.children.push(data3); 21 | data0.children.push(data2); 22 | data2.children.push(data4); 23 | data4.children.push(data7); 24 | data4.children.push(data5); 25 | data5.children.push(data6); 26 | 27 | const wrapper=mount(); 28 | //console.log(wrapper.debug()); 29 | expect(wrapper.find('.description').at(0).text()).toBe("data0"); 30 | expect(wrapper.find('.description').at(1).text()).toBe("data1"); 31 | expect(wrapper.find('.description').at(2).text()).toBe("data3"); 32 | expect(wrapper.find('.description').at(3).text()).toBe("data2"); 33 | expect(wrapper.find('.description').at(4).text()).toBe("data4"); 34 | expect(wrapper.find('.description').at(5).text()).toBe("data7"); 35 | expect(wrapper.find('.description').at(6).text()).toBe("data5"); 36 | expect(wrapper.find('.description').at(7).text()).toBe("data6"); 37 | }); 38 | 39 | 40 | it('should calculate numbers properly', function(){ 41 | 42 | const sel = id => `[testkey="${id}"]`; 43 | let data0= new LeafData(0,"first level:\n=sum()",[]); 44 | let data1= new LeafData(1,"second level:\n=count()",[]); 45 | let data2= new LeafData(2,"third level:\n#10.5",[]); 46 | let data3= new LeafData(3,"fourth level:\n#20.5",[]); 47 | let data4= new LeafData(4,"fifth level:\n#30.5",[]); 48 | 49 | data0.children.push(data1); 50 | data1.children.push(data2); 51 | data2.children.push(data3); 52 | data3.children.push(data4); 53 | 54 | const wrapper=mount(); 55 | //console.log(wrapper.debug()); 56 | expect(wrapper.find(sel('val-0-0')).at(0).text()).toEqual(expect.stringContaining("first level")); 57 | expect(wrapper.find(sel('val-0-1')).at(0).text()).toEqual(''); 58 | 59 | expect(wrapper.find(sel('val-1-0')).at(0).text()).toEqual(expect.stringContaining("second level")); 60 | expect(wrapper.find(sel('val-1-1')).at(0).text()).toEqual(''); 61 | 62 | 63 | expect(wrapper.find(sel('val-2-0')).at(0).text()).toEqual(expect.stringContaining("third level")); 64 | expect(wrapper.find(sel('val-2-1')).at(0).text()).toEqual(''); 65 | 66 | 67 | expect(wrapper.find(sel('val-3-0')).at(0).text()).toEqual(expect.stringContaining("fourth level")); 68 | expect(wrapper.find(sel('val-3-1')).at(0).text()).toEqual(''); 69 | 70 | 71 | expect(wrapper.find(sel('val-4-0')).at(0).text()).toEqual(expect.stringContaining("fifth level")); 72 | expect(wrapper.find(sel('val-4-1')).at(0).text()).toEqual(''); 73 | 74 | }); 75 | 76 | 77 | it('should recognize label exists properly', function(){ 78 | let data0= new LeafData(0,"first level",[]); 79 | let data1= new LeafData(1,"second level",[]); 80 | let data2= new LeafData(2,"third level:\n#label1",[]); 81 | let data3= new LeafData(3,"fourth level:\n#label2",[]); 82 | let data4= new LeafData(4,"fifth level:\n#label3",[]); 83 | 84 | data0.children.push(data1); 85 | data1.children.push(data2); 86 | data2.children.push(data3); 87 | data3.children.push(data4); 88 | 89 | 90 | const sel = id => `[testkey="${id}"]`; 91 | const wrapper=mount(); 92 | //console.log(wrapper.debug()); 93 | expect(wrapper.find(sel('val-0-0')).at(0).text()).toEqual(expect.stringContaining("first level")); 94 | expect(wrapper.find(sel('val-0-1')).at(0).text()).toEqual(''); 95 | expect(wrapper.find(sel('val-0-2')).at(0).text()).toEqual(''); 96 | expect(wrapper.find(sel('val-0-3')).at(0).text()).toEqual(''); 97 | expect(wrapper.find(sel('val-0-4')).at(0).text()).toEqual(''); 98 | 99 | expect(wrapper.find(sel('val-1-0')).at(0).text()).toEqual(expect.stringContaining("second level")); 100 | expect(wrapper.find(sel('val-1-1')).at(0).text()).toEqual(''); 101 | expect(wrapper.find(sel('val-1-2')).at(0).text()).toEqual(''); 102 | expect(wrapper.find(sel('val-1-3')).at(0).text()).toEqual(''); 103 | expect(wrapper.find(sel('val-1-4')).at(0).text()).toEqual(''); 104 | 105 | 106 | expect(wrapper.find(sel('val-2-0')).at(0).text()).toEqual(expect.stringContaining("third level")); 107 | expect(wrapper.find(sel('val-2-1')).at(0).text()).toEqual(''); 108 | expect(wrapper.find(sel('val-2-2')).at(0).text()).toEqual('\u2705'); 109 | expect(wrapper.find(sel('val-2-3')).at(0).text()).toEqual(''); 110 | expect(wrapper.find(sel('val-2-4')).at(0).text()).toEqual(''); 111 | 112 | 113 | 114 | expect(wrapper.find(sel('val-3-0')).at(0).text()).toEqual(expect.stringContaining("fourth level")); 115 | expect(wrapper.find(sel('val-3-1')).at(0).text()).toEqual(''); 116 | expect(wrapper.find(sel('val-3-2')).at(0).text()).toEqual(''); 117 | expect(wrapper.find(sel('val-3-3')).at(0).text()).toEqual('\u2705'); 118 | expect(wrapper.find(sel('val-3-4')).at(0).text()).toEqual(''); 119 | 120 | 121 | expect(wrapper.find(sel('val-4-0')).at(0).text()).toEqual(expect.stringContaining("fifth level")); 122 | expect(wrapper.find(sel('val-4-1')).at(0).text()).toEqual(''); 123 | expect(wrapper.find(sel('val-4-2')).at(0).text()).toEqual(''); 124 | expect(wrapper.find(sel('val-4-3')).at(0).text()).toEqual(''); 125 | expect(wrapper.find(sel('val-4-4')).at(0).text()).toEqual('\u2705'); 126 | }); 127 | 128 | it('should calculate label values properly', function(){ 129 | let data0= new LeafData(0,"first level:\n=sum(label)",[]); 130 | let data1= new LeafData(1,"second level:\n=count(label)",[]); 131 | let data2= new LeafData(2,"third level:\n#label:10.5",[]); 132 | let data3= new LeafData(3,"fourth level:\n#label:20.5",[]); 133 | let data4= new LeafData(4,"fifth level:\n#label:30.5",[]); 134 | 135 | data0.children.push(data1); 136 | data1.children.push(data2); 137 | data2.children.push(data3); 138 | data3.children.push(data4); 139 | 140 | 141 | const sel = id => `[testkey="${id}"]`; 142 | const wrapper=mount(); 143 | //console.log(wrapper.debug()); 144 | expect(wrapper.find(sel('val-0-0')).at(0).text()).toEqual(expect.stringContaining("first level")); 145 | expect(wrapper.find(sel('val-0-1')).at(0).text()).toEqual(''); 146 | expect(wrapper.find(sel('val-0-2')).at(0).text()).toEqual(''); 147 | 148 | expect(wrapper.find(sel('val-1-0')).at(0).text()).toEqual(expect.stringContaining("second level")); 149 | expect(wrapper.find(sel('val-1-1')).at(0).text()).toEqual(''); 150 | expect(wrapper.find(sel('val-1-2')).at(0).text()).toEqual(''); 151 | 152 | 153 | expect(wrapper.find(sel('val-2-0')).at(0).text()).toEqual(expect.stringContaining("third level")); 154 | expect(wrapper.find(sel('val-2-1')).at(0).text()).toEqual(''); 155 | expect(wrapper.find(sel('val-2-2')).at(0).text()).toEqual('10.5'); 156 | 157 | 158 | 159 | expect(wrapper.find(sel('val-3-0')).at(0).text()).toEqual(expect.stringContaining("fourth level")); 160 | expect(wrapper.find(sel('val-3-1')).at(0).text()).toEqual(''); 161 | expect(wrapper.find(sel('val-3-2')).at(0).text()).toEqual('20.5'); 162 | 163 | 164 | expect(wrapper.find(sel('val-4-0')).at(0).text()).toEqual(expect.stringContaining("fifth level")); 165 | expect(wrapper.find(sel('val-4-1')).at(0).text()).toEqual(''); 166 | expect(wrapper.find(sel('val-4-2')).at(0).text()).toEqual('30.5'); 167 | }); 168 | 169 | }); 170 | 171 | 172 | -------------------------------------------------------------------------------- /test/previewSentence.test.js: -------------------------------------------------------------------------------- 1 | import PreviewSentence from "../src/PreviewSentence"; 2 | import LeafData from "../src/LeafData"; 3 | import React from 'react'; 4 | import { shallow, mount, render } from 'enzyme'; 5 | 6 | 7 | describe('A suite', function() { 8 | 9 | it('should display header properly', function(){ 10 | let data0= new LeafData(0,"data0",[]); 11 | let data1= new LeafData(1,"h1.data1",[]); 12 | let data2= new LeafData(2,"data2",[]); 13 | let data3= new LeafData(3,"data3h1.",[]); 14 | let data4= new LeafData(4,"h3.data4",[]); 15 | let data5= new LeafData(5,"data5",[]); 16 | let data6= new LeafData(6,"data6",[]); 17 | let data7= new LeafData(7,"data7",[]); 18 | 19 | data0.children.push(data1); 20 | data1.children.push(data3); 21 | data0.children.push(data2); 22 | data2.children.push(data4); 23 | data4.children.push(data7); 24 | data4.children.push(data5); 25 | data5.children.push(data6); 26 | 27 | const wrapper=mount(); 28 | //console.log(wrapper.debug()); 29 | expect(wrapper.find('h1').at(0).text()).toBe("data0"); 30 | expect(wrapper.find('h2').at(0).text()).toBe("data1"); 31 | expect(wrapper.find('h2').at(1).text()).toBe("data2"); 32 | expect(wrapper.find('h3').at(0).text()).toBe("data4"); 33 | expect(wrapper.find('h4').at(0).text()).toBe("data5"); 34 | }); 35 | 36 | 37 | it('should calculate numbers properly', function(){ 38 | let data0= new LeafData(0,"first level:\n=sum()",[]); 39 | let data1= new LeafData(1,"second level:\n=count()",[]); 40 | let data2= new LeafData(2,"third level:\n=mean()",[]); 41 | let data3= new LeafData(3,"fourth level:\n#10.5",[]); 42 | let data4= new LeafData(4,"fifth level:\n#20.5",[]); 43 | let data5= new LeafData(5,"sixth level:\n#30.5",[]); 44 | 45 | data0.children.push(data1); 46 | data1.children.push(data2); 47 | data2.children.push(data3); 48 | data3.children.push(data4); 49 | data4.children.push(data5); 50 | 51 | const wrapper=mount(); 52 | //console.log(wrapper.debug()); 53 | expect(wrapper.find('.PreviewSentence-Paragraph .description').at(0).text()).toEqual(expect.stringContaining("61.5")); 54 | expect(wrapper.find('.PreviewSentence-Paragraph .description').at(1).text()).toEqual(expect.stringContaining("3")); 55 | expect(wrapper.find('.PreviewSentence-Paragraph .description').at(2).text()).toEqual(expect.stringContaining("20.5")); 56 | expect(wrapper.find('.PreviewSentence-Paragraph .description').at(3).text()).toEqual(expect.stringContaining("10.5")); 57 | expect(wrapper.find('.PreviewSentence-Paragraph .description').at(4).text()).toEqual(expect.stringContaining("20.5")); 58 | expect(wrapper.find('.PreviewSentence-Paragraph .description').at(5).text()).toEqual(expect.stringContaining("30.5")); 59 | }); 60 | 61 | 62 | 63 | it('should calculate labels properly', function(){ 64 | let data0= new LeafData(0,"first level:\n=sum(label)",[]); 65 | let data1= new LeafData(1,"second level:\n=count(label)",[]); 66 | let data2= new LeafData(2,"third level:\n=mean(label)",[]); 67 | let data3= new LeafData(3,"fourth level:\n#label:10.5",[]); 68 | let data4= new LeafData(4,"fifth level:\n#label:20.5",[]); 69 | let data5= new LeafData(5,"sixth level:\n#label:30.5",[]); 70 | 71 | data0.children.push(data1); 72 | data1.children.push(data2); 73 | data2.children.push(data3); 74 | data3.children.push(data4); 75 | data4.children.push(data5); 76 | 77 | const wrapper=mount(); 78 | //console.log(wrapper.debug()); 79 | expect(wrapper.find('.PreviewSentence-Paragraph .description').at(0).text()).toEqual(expect.stringContaining("61.5")); 80 | expect(wrapper.find('.PreviewSentence-Paragraph .description').at(1).text()).toEqual(expect.stringContaining("3")); 81 | expect(wrapper.find('.PreviewSentence-Paragraph .description').at(2).text()).toEqual(expect.stringContaining("20.5")); 82 | expect(wrapper.find('.PreviewSentence-Paragraph .description').at(3).text()).toEqual(expect.stringContaining("10.5")); 83 | expect(wrapper.find('.PreviewSentence-Paragraph .description').at(4).text()).toEqual(expect.stringContaining("20.5")); 84 | expect(wrapper.find('.PreviewSentence-Paragraph .description').at(5).text()).toEqual(expect.stringContaining("30.5")); 85 | }); 86 | 87 | 88 | it('should calculate numbers on first level properly', function(){ 89 | let data0= new LeafData(0,"=sum()",[]); 90 | let data1= new LeafData(1,"=count()",[]); 91 | let data2= new LeafData(2,"=mean()",[]); 92 | let data3= new LeafData(3,"#10.5",[]); 93 | let data4= new LeafData(4,"#20.5",[]); 94 | let data5= new LeafData(5,"#30.5",[]); 95 | 96 | data0.children.push(data1); 97 | data1.children.push(data2); 98 | data2.children.push(data3); 99 | data3.children.push(data4); 100 | data4.children.push(data5); 101 | 102 | const wrapper=mount(); 103 | //console.log(wrapper.debug()); 104 | expect(wrapper.find('.PreviewSentence-Paragraph .description').at(0).text()).toEqual(expect.stringContaining("61.5")); 105 | expect(wrapper.find('.PreviewSentence-Paragraph .description').at(1).text()).toEqual(expect.stringContaining("3")); 106 | expect(wrapper.find('.PreviewSentence-Paragraph .description').at(2).text()).toEqual(expect.stringContaining("20.5")); 107 | expect(wrapper.find('.PreviewSentence-Paragraph .description').at(3).text()).toEqual(expect.stringContaining("10.5")); 108 | expect(wrapper.find('.PreviewSentence-Paragraph .description').at(4).text()).toEqual(expect.stringContaining("20.5")); 109 | expect(wrapper.find('.PreviewSentence-Paragraph .description').at(5).text()).toEqual(expect.stringContaining("30.5")); 110 | }); 111 | 112 | 113 | it('should calculate numbers on first level properly', function(){ 114 | let data0= new LeafData(0,"=sum(label)",[]); 115 | let data1= new LeafData(1,"=count(label)",[]); 116 | let data2= new LeafData(2,"=mean(label)",[]); 117 | let data3= new LeafData(3,"#label:10.5",[]); 118 | let data4= new LeafData(4,"#label:20.5",[]); 119 | let data5= new LeafData(5,"#label:30.5",[]); 120 | 121 | data0.children.push(data1); 122 | data1.children.push(data2); 123 | data2.children.push(data3); 124 | data3.children.push(data4); 125 | data4.children.push(data5); 126 | 127 | const wrapper=mount(); 128 | //console.log(wrapper.debug()); 129 | expect(wrapper.find('.PreviewSentence-Paragraph .description').at(0).text()).toEqual(expect.stringContaining("61.5")); 130 | expect(wrapper.find('.PreviewSentence-Paragraph .description').at(1).text()).toEqual(expect.stringContaining("3")); 131 | expect(wrapper.find('.PreviewSentence-Paragraph .description').at(2).text()).toEqual(expect.stringContaining("20.5")); 132 | expect(wrapper.find('.PreviewSentence-Paragraph .description').at(3).text()).toEqual(expect.stringContaining("10.5")); 133 | expect(wrapper.find('.PreviewSentence-Paragraph .description').at(4).text()).toEqual(expect.stringContaining("20.5")); 134 | expect(wrapper.find('.PreviewSentence-Paragraph .description').at(5).text()).toEqual(expect.stringContaining("30.5")); 135 | }); 136 | 137 | 138 | it('should calculate numbers on first level properly', function(){ 139 | let data0= new LeafData(0,"test=sum()",[]); 140 | let data1= new LeafData(1,"test=count()",[]); 141 | let data2= new LeafData(2,"test=mean()",[]); 142 | let data3= new LeafData(3,"test#10.5",[]); 143 | let data4= new LeafData(4,"test#20.5",[]); 144 | let data5= new LeafData(5,"test#30.5",[]); 145 | 146 | data0.children.push(data1); 147 | data1.children.push(data2); 148 | data2.children.push(data3); 149 | data3.children.push(data4); 150 | data4.children.push(data5); 151 | 152 | const wrapper=mount(); 153 | //console.log(wrapper.debug()); 154 | expect(wrapper.find('.PreviewSentence-Paragraph .description').at(0).text()).toEqual(expect.stringContaining("61.5")); 155 | expect(wrapper.find('.PreviewSentence-Paragraph .description').at(1).text()).toEqual(expect.stringContaining("3")); 156 | expect(wrapper.find('.PreviewSentence-Paragraph .description').at(2).text()).toEqual(expect.stringContaining("20.5")); 157 | expect(wrapper.find('.PreviewSentence-Paragraph .description').at(3).text()).toEqual(expect.stringContaining("10.5")); 158 | expect(wrapper.find('.PreviewSentence-Paragraph .description').at(4).text()).toEqual(expect.stringContaining("20.5")); 159 | expect(wrapper.find('.PreviewSentence-Paragraph .description').at(5).text()).toEqual(expect.stringContaining("30.5")); 160 | }); 161 | 162 | 163 | it('should calculate numbers on first level properly', function(){ 164 | let data0= new LeafData(0,"test=sum(label)",[]); 165 | let data1= new LeafData(1,"test=count(label)",[]); 166 | let data2= new LeafData(2,"test=mean(label)",[]); 167 | let data3= new LeafData(3,"test#label:10.5",[]); 168 | let data4= new LeafData(4,"test#label:20.5",[]); 169 | let data5= new LeafData(5,"test#label:30.5",[]); 170 | 171 | data0.children.push(data1); 172 | data1.children.push(data2); 173 | data2.children.push(data3); 174 | data3.children.push(data4); 175 | data4.children.push(data5); 176 | 177 | const wrapper=mount(); 178 | //console.log(wrapper.debug()); 179 | expect(wrapper.find('.PreviewSentence-Paragraph .description').at(0).text()).toEqual(expect.stringContaining("61.5")); 180 | expect(wrapper.find('.PreviewSentence-Paragraph .description').at(1).text()).toEqual(expect.stringContaining("3")); 181 | expect(wrapper.find('.PreviewSentence-Paragraph .description').at(2).text()).toEqual(expect.stringContaining("20.5")); 182 | expect(wrapper.find('.PreviewSentence-Paragraph .description').at(3).text()).toEqual(expect.stringContaining("10.5")); 183 | expect(wrapper.find('.PreviewSentence-Paragraph .description').at(4).text()).toEqual(expect.stringContaining("20.5")); 184 | expect(wrapper.find('.PreviewSentence-Paragraph .description').at(5).text()).toEqual(expect.stringContaining("30.5")); 185 | }); 186 | 187 | }); 188 | 189 | 190 | -------------------------------------------------------------------------------- /test/previewTree.test.js: -------------------------------------------------------------------------------- 1 | import Tree from "../src/Tree"; 2 | import LeafData from "../src/LeafData"; 3 | import React from 'react'; 4 | import { shallow, mount, render } from 'enzyme'; 5 | import Property from "../src/Property"; 6 | 7 | 8 | describe('A suite', function() { 9 | 10 | it('should calculate numbers properly', function(){ 11 | let data0= new LeafData(0,"first level:\n=sum()",[]); 12 | let data1= new LeafData(1,"second level:\n=count()",[]); 13 | let data2= new LeafData(2,"third level:\n#10.5",[]); 14 | let data3= new LeafData(3,"fourth level:\n#20.5",[]); 15 | let data4= new LeafData(4,"fifth level:\n#30.5",[]); 16 | 17 | data0.children.push(data1); 18 | data1.children.push(data2); 19 | data2.children.push(data3); 20 | data3.children.push(data4); 21 | 22 | let prop=new Property(); 23 | const wrapper=mount(); 24 | 25 | expect(wrapper.find('.description').at(0).text()).toEqual(expect.stringContaining("61.5")); 26 | expect(wrapper.find('.description').at(1).text()).toEqual(expect.stringContaining("3")); 27 | expect(wrapper.find('.description').at(2).text()).toEqual(expect.stringContaining("10.5")); 28 | expect(wrapper.find('.description').at(3).text()).toEqual(expect.stringContaining("20.5")); 29 | expect(wrapper.find('.description').at(4).text()).toEqual(expect.stringContaining("30.5")); 30 | 31 | let fs = require("fs"); 32 | const dir='./test/__html__' 33 | if (!fs.existsSync(dir)){ 34 | fs.mkdirSync(dir); 35 | fs.writeFileSync("./test/__html__/previewTree.test.multiple.labels.html", wrapper.html()); 36 | } 37 | }); 38 | 39 | }); 40 | 41 | 42 | -------------------------------------------------------------------------------- /test/setupTests.js: -------------------------------------------------------------------------------- 1 | import Enzyme from 'enzyme' 2 | import EnzymeAdapter from 'enzyme-adapter-react-16' 3 | Enzyme.configure({ 4 | adapter: new EnzymeAdapter(), 5 | disableLifecycleMethods: true 6 | }) -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | 2 | var path = require('path'); 3 | module.exports = { 4 | entry: './src/index.js', 5 | output: { 6 | path: path.resolve(__dirname, 'build'), 7 | filename: 'bundle.js', 8 | libraryTarget: 'commonjs2' 9 | }, 10 | module: { 11 | rules: [ 12 | { 13 | test: /\.js$/, 14 | include: path.resolve(__dirname, 'src'), 15 | use: { 16 | loader: 'babel-loader', 17 | options: { 18 | presets: ['@babel/preset-env'], 19 | } 20 | } 21 | }, 22 | { 23 | test: /\.css/, 24 | include: path.resolve(__dirname, 'src'), 25 | use: [ 26 | 'style-loader', 27 | { 28 | loader: 'css-loader', 29 | options: { 30 | url: false 31 | }, 32 | } 33 | ] 34 | }, 35 | { 36 | test: /\.(png|jpe?g|gif)$/i, 37 | include: path.resolve(__dirname, 'src'), 38 | use: { 39 | loader: 'url-loader', 40 | options: { 41 | limit: 10000, 42 | }, 43 | }, 44 | }, 45 | ] 46 | }, 47 | externals: { 48 | 'react': 'commonjs react' 49 | } 50 | }; --------------------------------------------------------------------------------