├── .storybook ├── addons.js ├── config.js └── webpack.config.js ├── src ├── index.js ├── viewer.js └── editor.js ├── .prettierrc ├── babel.config.js ├── .eslintrc.js ├── docs ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md └── COMMIT_MESSAGE_CONVENTION.md ├── .gitignore ├── LICENSE ├── stories ├── viewer │ ├── viewer.stories.js │ └── dummyData.js └── editor │ └── editor.stories.js ├── webpack.config.js ├── package.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md └── README.md /.storybook/addons.js: -------------------------------------------------------------------------------- 1 | import '@storybook/addon-knobs/register'; 2 | import '@storybook/addon-actions/register'; 3 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import Editor from './editor'; 2 | import Viewer from './viewer'; 3 | 4 | export {Editor, Viewer}; 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth" : 100, 3 | "singleQuote" : true, 4 | "bracketSpacing" : false, 5 | "arrowParens": "always" 6 | } 7 | -------------------------------------------------------------------------------- /.storybook/config.js: -------------------------------------------------------------------------------- 1 | import {configure} from '@storybook/react'; 2 | 3 | // automatically import all files ending in *.stories.js 4 | const req = require.context('../stories', true, /.stories.js$/); 5 | function loadStories() { 6 | req.keys().forEach((filename) => req(filename)); 7 | } 8 | 9 | configure(loadStories, module); 10 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = (api) => { 2 | api.cache(true); 3 | 4 | return { 5 | presets: [ 6 | [ 7 | '@babel/preset-env', 8 | { 9 | modules: false, 10 | useBuiltIns: 'entry' 11 | } 12 | ], 13 | '@babel/preset-react' 14 | ], 15 | plugins: [ 16 | '@babel/plugin-proposal-class-properties', 17 | '@babel/plugin-proposal-object-rest-spread' 18 | ] 19 | }; 20 | }; 21 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: 'babel-eslint', 3 | parserOptions: { 4 | ecmaVersion: 7, 5 | sourceType: 'module', 6 | ecmaFeatures: { 7 | jsx: true 8 | } 9 | }, 10 | extends: ['tui/es6', 'plugin:react/recommended', 'plugin:prettier/recommended'], 11 | plugins: ['react', 'prettier'], 12 | rules: { 13 | 'react/prop-types': 0 14 | }, 15 | settings: { 16 | react: { 17 | version: 'detect' 18 | } 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /docs/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | ## Version 11 | 12 | 13 | ## Test Environment 14 | 15 | 16 | ## Current Behavior 17 | 19 | 20 | ```js 21 | // Write example code 22 | ``` 23 | 24 | ## Expected Behavior 25 | 26 | -------------------------------------------------------------------------------- /.storybook/webpack.config.js: -------------------------------------------------------------------------------- 1 | // you can use this file to add your custom webpack plugins, loaders and anything you like. 2 | // This is just the basic way to add additional webpack configurations. 3 | // For more information refer the docs: https://storybook.js.org/configurations/custom-webpack-config 4 | 5 | // IMPORTANT 6 | // When you add this file, we won't add the default configurations which is similar 7 | // to "React Create App". This only has babel loader to load JavaScript. 8 | module.exports = { 9 | plugins: [], 10 | module: { 11 | rules: [ 12 | { 13 | test: /\.css$/, 14 | use: ['style-loader', 'css-loader'] 15 | }, 16 | { 17 | test: /\.(jpg|png|gif)$/, 18 | use: ['file-loader'] 19 | } 20 | ] 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Compiled binary addons (http://nodejs.org/api/addons.html) 17 | build/Release 18 | 19 | # Dependency directory 20 | node_modules 21 | 22 | # Bower Components 23 | bower_components 24 | lib 25 | 26 | # IDEA 27 | .idea 28 | *.iml 29 | 30 | # Window 31 | Thumbs.db 32 | Desktop.ini 33 | 34 | # MAC 35 | .DS_Store 36 | 37 | # SVN 38 | .svn 39 | 40 | # eclipse 41 | .project 42 | .metadata 43 | 44 | # build 45 | build 46 | 47 | # etc 48 | *.swp 49 | etc 50 | temp 51 | api 52 | doc 53 | report 54 | karma.conf.local.js 55 | .tern-project 56 | .tern-port 57 | *.vim 58 | .\#* 59 | .vscode/ 60 | dist/ 61 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 NHN Entertainment Corp. 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /stories/viewer/viewer.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import 'codemirror/lib/codemirror.css'; 3 | import 'highlight.js/styles/github.css'; 4 | import 'tui-editor/dist/tui-editor.css'; 5 | import 'tui-editor/dist/tui-editor-contents.min.css'; 6 | 7 | import 'tui-editor/dist/tui-editor-extScrollSync' 8 | import 'tui-editor/dist/tui-editor-extColorSyntax' 9 | import 'tui-editor/dist/tui-editor-extUML' 10 | import 'tui-editor/dist/tui-editor-extChart' 11 | import 'tui-editor/dist/tui-editor-extTable' 12 | 13 | import {storiesOf} from '@storybook/react'; 14 | import {withKnobs} from '@storybook/addon-knobs'; 15 | import {basicViewerDummy} from './dummyData'; 16 | import {Viewer} from '../../src/index'; 17 | 18 | const stories = storiesOf('Viewer', module).addDecorator(withKnobs); 19 | 20 | stories.add('basic', () => { 21 | const {content} = basicViewerDummy; 22 | 23 | return ( 24 | 42 | ); 43 | }); 44 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const config = { 4 | entry: './src/index.js', 5 | output: { 6 | filename: 'toastui-react-editor.js', 7 | path: path.resolve(__dirname, 'dist'), 8 | libraryTarget: 'commonjs2' 9 | }, 10 | externals: { 11 | 'tui-editor': { 12 | commonjs: 'tui-editor', 13 | commonjs2: 'tui-editor' 14 | }, 15 | 'tui-editor/dist/tui-editor-Viewer': { 16 | commonjs: 'tui-editor/dist/tui-editor-Viewer', 17 | commonjs2: 'tui-editor/dist/tui-editor-Viewer' 18 | }, 19 | react: { 20 | commonjs: 'react', 21 | commonjs2: 'react' 22 | }, 23 | jquery: { 24 | commonjs: 'jquery', 25 | commonjs2: 'jquery' 26 | }, 27 | 'highlight.js': { 28 | commonjs: 'highlight.js', 29 | commonjs2: 'highlight.js' 30 | }, 31 | 'markdown-it': { 32 | commonjs: 'markdown-it', 33 | commonjs2: 'markdown-it' 34 | }, 35 | 'to-mark': { 36 | commonjs: 'to-mark', 37 | commonjs2: 'to-mark' 38 | } 39 | }, 40 | module: { 41 | rules: [ 42 | { 43 | test: /\.js$/, 44 | include: [path.resolve(__dirname, 'src')], 45 | use: { 46 | loader: 'babel-loader', 47 | options: { 48 | presets: ['@babel/preset-env'] 49 | } 50 | } 51 | } 52 | ] 53 | } 54 | }; 55 | 56 | module.exports = () => config; 57 | -------------------------------------------------------------------------------- /src/viewer.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Viewer from 'tui-editor/dist/tui-editor-Viewer'; 3 | 4 | export default class ViewerComponent extends React.Component { 5 | rootEl = React.createRef(); 6 | 7 | viewerInst = null; 8 | 9 | getRootElement() { 10 | return this.rootEl.current; 11 | } 12 | 13 | getInstance() { 14 | return this.viewerInst; 15 | } 16 | 17 | bindEventHandlers(props, prevProps) { 18 | Object.keys(this.props) 19 | .filter((key) => /on[A-Z][a-zA-Z]+/.test(key)) 20 | .forEach((key) => { 21 | const eventName = key[2].toLowerCase() + key.slice(3); 22 | // For 23 | if(prevProps && prevProps[key] !== props[key]) { 24 | this.viewerInst.off(eventName); 25 | } 26 | this.viewerInst.on(eventName, props[key]); 27 | }); 28 | } 29 | 30 | componentDidMount() { 31 | this.viewerInst = new Viewer({ 32 | el: this.rootEl.current, 33 | ...this.props 34 | }); 35 | 36 | this.bindEventHandlers(this.props); 37 | } 38 | 39 | shouldComponentUpdate(nextProps) { 40 | const currentValue = this.props.initialValue; 41 | const nextValue = nextProps.initialValue; 42 | 43 | if (currentValue !== nextValue) { 44 | this.getInstance().setValue(nextValue); 45 | } 46 | 47 | this.bindEventHandlers(nextProps, this.props); 48 | 49 | return false; 50 | } 51 | 52 | render() { 53 | return
; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/editor.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Editor from 'tui-editor'; 3 | 4 | export default class extends React.Component { 5 | rootEl = React.createRef(); 6 | editorInst = null; 7 | 8 | getRootElement() { 9 | return this.rootEl.current; 10 | } 11 | 12 | getInstance() { 13 | return this.editorInst; 14 | } 15 | 16 | bindEventHandlers(props, prevProps) { 17 | Object.keys(props) 18 | .filter((key) => /on[A-Z][a-zA-Z]+/.test(key)) 19 | .forEach((key) => { 20 | const eventName = key[2].toLowerCase() + key.slice(3); 21 | // For 22 | if(prevProps && prevProps[key] !== props[key]) { 23 | this.editorInst.off(eventName); 24 | } 25 | this.editorInst.on(eventName, props[key]); 26 | }); 27 | } 28 | 29 | componentDidMount() { 30 | this.editorInst = new Editor({ 31 | el: this.rootEl.current, 32 | ...this.props 33 | }); 34 | 35 | this.bindEventHandlers(this.props); 36 | } 37 | 38 | shouldComponentUpdate(nextProps) { 39 | const instance = this.getInstance(); 40 | const {height, previewStyle} = nextProps; 41 | 42 | if (this.props.height !== height) { 43 | instance.height(height); 44 | } 45 | 46 | if (this.props.previewStyle !== previewStyle) { 47 | instance.changePreviewStyle(previewStyle); 48 | } 49 | 50 | this.bindEventHandlers(nextProps, this.props); 51 | 52 | return false; 53 | } 54 | 55 | render() { 56 | return
; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /docs/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 20 | 21 | 27 | 28 | ### Please check if the PR fulfills these requirements 29 | - [ ] It's the right issue type on the title 30 | - [ ] When resolving a specific issue, it's referenced in the PR's title (e.g. `fix #xxx[,#xxx]`, where "xxx" is the issue number) 31 | - [ ] The commit message follows our guidelines 32 | - [ ] Tests for the changes have been added (for bug fixes/features) 33 | - [ ] Docs have been added/updated (for bug fixes/features) 34 | - [ ] It does not introduce a breaking change or has a description of the breaking change 35 | 36 | ### Description 37 | 38 | 39 | 40 | --- 41 | Thank you for your contribution to TOAST UI product. 🎉 😘 ✨ 42 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@toast-ui/react-editor", 3 | "version": "1.0.1", 4 | "description": "TOAST UI Editor for React", 5 | "main": "dist/toastui-react-editor.js", 6 | "scripts": { 7 | "lint": "eslint src/**", 8 | "build": "webpack -p --progress", 9 | "storybook": "start-storybook -p 6006", 10 | "build-storybook": "build-storybook" 11 | }, 12 | "homepage": "https://github.com/nhn/toast-ui.react-editor", 13 | "bugs": "https://github.com/nhn/toast-ui.react-editor/issues", 14 | "author": "NHN. FE Development Lab ", 15 | "repository": "https://github.com/nhn/toast-ui.react-editor.git", 16 | "license": "MIT", 17 | "browserslist": "last 2 versions, ie 9", 18 | "peerDependencies": { 19 | "react": "^16.0.0" 20 | }, 21 | "devDependencies": { 22 | "@babel/core": "^7.2.2", 23 | "@babel/plugin-proposal-class-properties": "^7.2.3", 24 | "@babel/plugin-proposal-object-rest-spread": "^7.2.0", 25 | "@babel/preset-env": "^7.2.3", 26 | "@babel/preset-react": "^7.0.0", 27 | "@storybook/addon-actions": "^4.1.7", 28 | "@storybook/addon-knobs": "^4.1.7", 29 | "@storybook/addon-links": "^4.1.7", 30 | "@storybook/addons": "^4.1.7", 31 | "@storybook/react": "^4.1.7", 32 | "babel-eslint": "^10.0.1", 33 | "babel-loader": "^8.0.5", 34 | "css-loader": "^2.1.0", 35 | "eslint": "^5.12.1", 36 | "eslint-config-prettier": "^3.6.0", 37 | "eslint-config-tui": "^2.1.0", 38 | "eslint-plugin-prettier": "^3.0.1", 39 | "eslint-plugin-react": "^7.12.4", 40 | "file-loader": "^3.0.1", 41 | "prettier": "^1.16.0", 42 | "react": "^16.7.0", 43 | "react-dom": "^16.7.0", 44 | "storybook": "^1.0.0", 45 | "style-loader": "^0.23.1", 46 | "webpack": "^4.29.0", 47 | "webpack-cli": "^3.2.1", 48 | "webpack-dev-server": "^3.1.14" 49 | }, 50 | "dependencies": { 51 | "tui-editor": "^1.3.0" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /stories/viewer/dummyData.js: -------------------------------------------------------------------------------- 1 | export const basicViewerDummy = { 2 | content: [ 3 | '![image](https://cloud.githubusercontent.com/assets/389021/16107646/9729e556-33d8-11e6-933f-5b09fa3a53bb.png)', 4 | '# Heading 1', 5 | '## Heading 2', 6 | '### Heading 3', 7 | '#### Heading 4', 8 | '##### Heading 5', 9 | '###### Heading 6', 10 | ' code block', 11 | '```js', 12 | 'console.log("fenced code block");', 13 | '```', 14 | '
**HTML block**
', 15 | '* list', 16 | ' * list indented', 17 | '1. ordered', 18 | '2. list', 19 | ' 1. ordered list', 20 | ' 2. indented', 21 | '', 22 | '- [ ] task', 23 | '- [x] list completed', 24 | '', 25 | '[link](https://nhn.github.io/tui.editor/)', 26 | '> block quote', 27 | '---', 28 | 'horizontal line', 29 | '***', 30 | '`code`, *italic*, **bold**, ~~strikethrough~~, Red color', 31 | '|table|head|', 32 | '|---|---|', 33 | '|table|body|', 34 | '```uml', 35 | 'partition Conductor {', 36 | ' (*) --> "Climbs on Platform"', 37 | ' --> === S1 ===', 38 | ' --> Bows', 39 | '}', 40 | '', 41 | 'partition Audience #LightSkyBlue {', 42 | ' === S1 === --> Applauds', 43 | '}', 44 | '', 45 | 'partition Conductor {', 46 | ' Bows --> === S2 ===', 47 | ' --> WavesArmes', 48 | ' Applauds --> === S2 ===', 49 | '}', 50 | '', 51 | 'partition Orchestra #CCCCEE {', 52 | ' WavesArmes --> Introduction', 53 | ' --> "Play music"', 54 | '}', 55 | '```', 56 | '```chart', 57 | ',category1,category2', 58 | 'Jan,21,23', 59 | 'Feb,31,17', 60 | '', 61 | 'type: column', 62 | 'title: Monthly Revenue', 63 | 'x.title: Amount', 64 | 'y.title: Month', 65 | 'y.min: 1', 66 | 'y.max: 40', 67 | 'y.suffix: $', 68 | '```' 69 | ].join('\n') 70 | }; 71 | -------------------------------------------------------------------------------- /docs/COMMIT_MESSAGE_CONVENTION.md: -------------------------------------------------------------------------------- 1 | # Commit Message Convention 2 | 3 | ## Commit Message Format 4 | 5 | ``` 6 | : Short description (fix #1234) 7 | 8 | Logger description here if necessary 9 | 10 | BREAKING CHANGE: only contain breaking change 11 | ``` 12 | * Any line of the commit message cannot be longer 100 characters! 13 | 14 | ## Revert 15 | ``` 16 | revert: commit 17 | 18 | This reverts commit 19 | More description if needed 20 | ``` 21 | 22 | ## Type 23 | Must be one of the following: 24 | 25 | * **feat**: A new feature 26 | * **fix**: A bug fix 27 | * **docs**: Documentation only changes 28 | * **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc) 29 | * **refactor**: A code change that neither fixes a bug nor adds a feature 30 | * **perf**: A code change that improves performance 31 | * **test**: Adding missing or correcting existing tests 32 | * **chore**: Changes to the build process or auxiliary tools and libraries such as documentation generation 33 | 34 | ## Subject 35 | * use the imperative, __present__ tense: "change" not "changed" nor "changes" 36 | * don't capitalize the first letter 37 | * no dot (.) at the end 38 | * reference GitHub issues at the end. If the commit doesn’t completely fix the issue, then use `(refs #1234)` instead of `(fixes #1234)`. 39 | 40 | ## Body 41 | 42 | * use the imperative, __present__ tense: "change" not "changed" nor "changes". 43 | * the motivation for the change and contrast this with previous behavior. 44 | 45 | ## BREAKING CHANGE 46 | * This commit contains breaking change(s). 47 | * start with the word BREAKING CHANGE: with a space or two newlines. The rest of the commit message is then used for this. 48 | 49 | This convention is based on [AngularJS](https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#commits) and [ESLint](https://eslint.org/docs/developer-guide/contributing/pull-requests#step2) 50 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | Contributor Covenant Code of Conduct 2 | Our Pledge 3 | 4 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. 5 | Our Standards 6 | 7 | Examples of behavior that contributes to creating a positive environment include: 8 | 9 | Using welcoming and inclusive language 10 | Being respectful of differing viewpoints and experiences 11 | Gracefully accepting constructive criticism 12 | Focusing on what is best for the community 13 | Showing empathy towards other community members 14 | 15 | Examples of unacceptable behavior by participants include: 16 | 17 | The use of sexualized language or imagery and unwelcome sexual attention or advances 18 | Trolling, insulting/derogatory comments, and personal or political attacks 19 | Public or private harassment 20 | Publishing others' private information, such as a physical or electronic address, without explicit permission 21 | Other conduct which could reasonably be considered inappropriate in a professional setting 22 | 23 | Our Responsibilities 24 | 25 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 26 | 27 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 28 | Scope 29 | 30 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 31 | Enforcement 32 | 33 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at dl_javascript@nhn.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 34 | 35 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 36 | Attribution 37 | 38 | This Code of Conduct is adapted from the Contributor Covenant, version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 39 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to TOAST UI 2 | 3 | First off, thanks for taking the time to contribute! 🎉 😘 ✨ 4 | 5 | The following is a set of guidelines for contributing to TOAST UI. These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request. 6 | 7 | ## Reporting Bugs 8 | Bugs are tracked as GitHub issues. Search the list and try reproduce on [demo][demo] before you create an issue. When you create an issue, please provide the following information by filling in the template. 9 | 10 | Explain the problem and include additional details to help maintainers reproduce the problem: 11 | 12 | * **Use a clear and descriptive title** for the issue to identify the problem. 13 | * **Describe the exact steps which reproduce the problem** in as many details as possible. Don't just say what you did, but explain how you did it. For example, if you moved the cursor to the end of a line, explain if you used a mouse or a keyboard. 14 | * **Provide specific examples to demonstrate the steps.** Include links to files or GitHub projects, or copy/pasteable snippets, which you use in those examples. If you're providing snippets on the issue, use Markdown code blocks. 15 | * **Describe the behavior you observed after following the steps** and point out what exactly is the problem with that behavior. 16 | * **Explain which behavior you expected to see instead and why.** 17 | * **Include screenshots and animated GIFs** which show you following the described steps and clearly demonstrate the problem. 18 | 19 | ## Suggesting Enhancements 20 | In case you want to suggest for TOAST UI Editor, please follow this guideline to help maintainers and the community understand your suggestion. 21 | Before creating suggestions, please check [issue list](https://github.nhnent.com/fe/tui.editor/labels/feature%20request) if there's already a request. 22 | 23 | Create an issue and provide the following information: 24 | 25 | * **Use a clear and descriptive title** for the issue to identify the suggestion. 26 | * **Provide a step-by-step description of the suggested enhancement** in as many details as possible. 27 | * **Provide specific examples to demonstrate the steps.** Include copy/pasteable snippets which you use in those examples, as Markdown code blocks. 28 | * **Include screenshots and animated GIFs** which helps demonstrate the steps or point out the part of TOAST UI Editor which the suggestion is related to. 29 | * **Explain why this enhancement would be useful** to most TOAST UI users. 30 | * **List some other text editors or applications where this enhancement exists.** 31 | 32 | ## First Code Contribution 33 | 34 | Unsure where to begin contributing to TOAST UI? You can start by looking through these `document`, `good first issue` and `help wanted` issues: 35 | 36 | * **document issues**: issues which should be reviewed or improved. 37 | * **good first issues**: issues which should only require a few lines of code, and a test or two. 38 | * **help wanted issues**: issues which should be a bit more involved than beginner issues. 39 | 40 | ## Pull Requests 41 | 42 | ### Development WorkFlow 43 | - Set up your development environment 44 | - Make change from a right branch 45 | - Be sure the code passes `npm run lint`, `npm run test` 46 | - Make a pull request 47 | 48 | ### Development environment 49 | - Prepare your machine node and it's packages installed. 50 | - Checkout our repository 51 | - Install dependencies by `npm install && bower install` 52 | - Start webpack-dev-server by `npm run serve` 53 | 54 | ### Make changes 55 | #### Checkout a branch 56 | - **master**: PR Base branch. 57 | - **production**: lastest release branch with distribution files. never make a PR on this 58 | - **gh-pages**: API docs, examples and demo 59 | 60 | #### Check Code Style 61 | Run `npm run eslint` and make sure all the tests pass. 62 | 63 | #### Test 64 | Run `npm run test` and verify all the tests pass. 65 | If you are adding new commands or features, they must include tests. 66 | If you are changing functionality, update the tests if you need to. 67 | 68 | #### Commit 69 | Follow our [commit message conventions](./docs/COMMIT_MESSAGE_CONVENTION.md). 70 | 71 | ### Yes! Pull request 72 | Make your pull request, then describe your changes. 73 | #### Title 74 | Follow other PR title format on below. 75 | ``` 76 | : Short Description (fix #111) 77 | : Short Description (fix #123, #111, #122) 78 | : Short Description (ref #111) 79 | ``` 80 | * capitalize first letter of Type 81 | * use present tense: 'change' not 'changed' or 'changes' 82 | 83 | #### Description 84 | If it has related to issues, add links to the issues(like `#123`) in the description. 85 | Fill in the [Pull Request Template](./docs/PULL_REQUEST_TEMPLATE.md) by check your case. 86 | 87 | ## Code of Conduct 88 | This project and everyone participating in it is governed by the [Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior to dl_javascript@nhn.com. 89 | 90 | > This Guide is base on [atom contributing guide](https://github.com/atom/atom/blob/master/CONTRIBUTING.md), [CocoaPods](http://guides.cocoapods.org/contributing/contribute-to-cocoapods.html) and [ESLint](http://eslint.org/docs/developer-guide/contributing/pull-requests) 91 | [demo]:http://ui.toast.com/tui-chart 92 | -------------------------------------------------------------------------------- /stories/editor/editor.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import 'codemirror/lib/codemirror.css'; 3 | import 'highlight.js/styles/github.css'; 4 | import 'tui-editor/dist/tui-editor.min.css'; 5 | import 'tui-editor/dist/tui-editor-contents.min.css'; 6 | 7 | import 'tui-editor/dist/tui-editor-extScrollSync'; 8 | import 'tui-editor/dist/tui-editor-extColorSyntax'; 9 | import 'tui-editor/dist/tui-editor-extUML'; 10 | import 'tui-editor/dist/tui-editor-extChart'; 11 | import 'tui-editor/dist/tui-editor-extTable'; 12 | 13 | import {storiesOf} from '@storybook/react'; 14 | import {withKnobs} from '@storybook/addon-knobs'; 15 | import {basicViewerDummy} from '../viewer/dummyData'; 16 | import {Editor} from '../../src/index'; 17 | 18 | const stories = storiesOf('Editor', module).addDecorator(withKnobs); 19 | 20 | stories.add('demo', () => { 21 | const {content} = basicViewerDummy; 22 | 23 | return ( 24 | 45 | ); 46 | }); 47 | 48 | stories.add('customize toolbar', () => { 49 | return ( 50 | 78 | ); 79 | }); 80 | 81 | stories.add('customize feature', () => { 82 | class Story extends React.Component { 83 | ref = React.createRef(); 84 | 85 | handleClick = () => { 86 | this.ref.current.getInstance().exec('Bold'); 87 | }; 88 | 89 | render() { 90 | return ( 91 | <> 92 | 99 | 100 | 101 | ); 102 | } 103 | } 104 | 105 | return ; 106 | }); 107 | 108 | stories.add('i18n', () => ( 109 | 116 | )); 117 | 118 | stories.add('setValue programmatically', () => { 119 | class Story extends React.Component { 120 | ref = React.createRef(); 121 | 122 | handleClick = () => { 123 | const {content} = basicViewerDummy; 124 | 125 | this.ref.current.getInstance().setValue(content); 126 | }; 127 | 128 | render() { 129 | return ( 130 | <> 131 | 152 | 153 | 154 | ); 155 | } 156 | } 157 | 158 | return ; 159 | }); 160 | 161 | stories.add('dynamically change react state', () => { 162 | class Story extends React.Component { 163 | ref = React.createRef(); 164 | state = { 165 | content: '', 166 | height: 400, 167 | previewStyle: 'vertical', 168 | editType: 'markdown' 169 | }; 170 | 171 | handleChange = () => { 172 | const value = this.ref.current.getInstance().getValue(); 173 | 174 | this.setState({ 175 | ...this.state, 176 | content: value 177 | }); 178 | }; 179 | 180 | toggleEditType = () => { 181 | this.setState((prevState) => ({ 182 | ...prevState, 183 | editType: prevState.editType === 'markdown' ? 'wysiwyg' : 'markdown' 184 | })); 185 | }; 186 | 187 | togglePreviewStyle = () => { 188 | this.setState((prevState) => ({ 189 | ...prevState, 190 | previewStyle: prevState.previewStyle === 'vertical' ? 'tab' : 'vertical' 191 | })); 192 | }; 193 | 194 | changeHeight = () => { 195 | this.setState((prevState) => ({ 196 | ...prevState, 197 | height: prevState.height + 100 198 | })); 199 | }; 200 | 201 | render() { 202 | return ( 203 | <> 204 | 226 | 227 | 228 | 229 | 230 | ); 231 | } 232 | } 233 | 234 | return ; 235 | }); 236 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ⚠️Notice: This repository is deprecated️️️️️ 2 | 3 | TOAST UI Editor React Wrapper has been managed separately from the TOAST UI Editor repository. As a result of the distribution of these issues, we decided to deprecated each wrapper repository and manage repository as a [mono-repo](https://en.wikipedia.org/wiki/Monorepo) from the [TOAST UI Editor repository](https://github.com/nhn/tui.editor). 4 | 5 | From now on, please submit issues or contributings related to TOAST UI React Wrapper to [TOAST UI Editor repository](https://github.com/nhn/tui.editor). Thank you🙂. 6 | 7 | # TOAST UI Editor for React 8 | 9 | > This is a React component wrapping [TOAST UI Editor](https://github.com/nhn/tui.editor). 10 | 11 | [![github version](https://img.shields.io/github/release/nhn/toast-ui.react-editor.svg)](https://github.com/nhn/toast-ui.react-editor/releases/latest) 12 | [![npm version](https://img.shields.io/npm/v/@toast-ui/react-editor.svg)](https://www.npmjs.com/package/@toast-ui/react-editor) 13 | [![license](https://img.shields.io/github/license/nhn/toast-ui.react-editor.svg)](https://github.com/nhn/toast-ui.react-editor/blob/master/LICENSE) 14 | [![PRs welcome](https://img.shields.io/badge/PRs-welcome-ff69b4.svg)](https://github.com/nhn/toast-ui.react-editor/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) 15 | [![code with hearth by NHN](https://img.shields.io/badge/%3C%2F%3E%20with%20%E2%99%A5%20by-NHN-ff1414.svg)](https://github.com/nhn) 16 | 17 | ## 🚩 Table of Contents 18 | * [Collect statistics on the use of open source](#collect-statistics-on-the-use-of-open-source) 19 | * [Install](#-install) 20 | * [Using npm](#using-npm) 21 | * [Usage](#-usage) 22 | * [Import](#import) 23 | * [Props](#props) 24 | * [Instance Methods](#instance-methods) 25 | * [Getting the root element](#getting-the-root-element) 26 | * [Events](#events) 27 | * [Pull Request Steps](#-pull-request-steps) 28 | * [Documents](#-documents) 29 | * [Contributing](#-contributing) 30 | * [License](#-license) 31 | 32 | ## Collect statistics on the use of open source 33 | 34 | React Wrapper of TOAST UI Editor applies Google Analytics (GA) to collect statistics on the use of open source, in order to identify how widely TOAST UI Editor is used throughout the world. It also serves as important index to determine the future course of projects. location.hostname (e.g. > “ui.toast.com") is to be collected and the sole purpose is nothing but to measure statistics on the usage. To disable GA, use the `usageStatistics` props like the example below. 35 | 36 | ```js 37 | 41 | ``` 42 | 43 | Or, import `tui-code-snippet.js` (**v1.4.0** or **later**) and then immediately write the options as follows: 44 | ```js 45 | tui.usageStatistics = false; 46 | ``` 47 | 48 | ## 💾 Install 49 | 50 | ### Using npm 51 | 52 | ```sh 53 | npm install --save @toast-ui/react-editor 54 | ``` 55 | 56 | ## 🔡 Usage 57 | 58 | ### Import 59 | 60 | You can use Toast UI Editor for React as a ECMAScript module or a CommonJS module. As this module does not contain CSS files, you should import `tui-editor.css`, `tui-editor-contents.css` from `tui-editor` and `codemirror.css` from `codemirror.css` in the script. 61 | 62 | * Using ECMAScript module 63 | 64 | ```js 65 | import 'codemirror/lib/codemirror.css'; 66 | import 'tui-editor/dist/tui-editor.min.css'; 67 | import 'tui-editor/dist/tui-editor-contents.min.css'; 68 | import { Editor } from '@toast-ui/react-editor' 69 | ``` 70 | 71 | * Using CommonJS module 72 | 73 | ```js 74 | require('codemirror/lib/codemirror.css'); 75 | require('tui-editor/dist/tui-editor.min.css'); 76 | require('tui-editor/dist/tui-editor-contents.min.css'); 77 | const Editor = require('@toast-ui/react-editor'); 78 | ``` 79 | 80 | ### Props 81 | 82 | [All the options of the TOAST UI Editor](https://nhn.github.io/tui.editor/api/latest/ToastUIEditor.html#ToastUIEditor) are supported in the form of props. 83 | 84 | ```js 85 | const MyComponent = () => ( 86 | 107 | ); 108 | ``` 109 | 110 | ### Instance Methods 111 | 112 | For using [instance methods of TOAST UI Editor](https://nhn.github.io/tui.editor/api/latest/ToastUIEditor.html#.defineExtension), first thing to do is creating Refs of wrapper component using [`createRef()`](https://reactjs.org/docs/refs-and-the-dom.html#creating-refs). But the wrapper component does not provide a way to call instance methods of TOAST UI Editor directly. Instead, you can call `getInstance()` method of the wrapper component to get the instance, and call the methods on it. 113 | 114 | ```js 115 | class MyComponent extends React.Component { 116 | editorRef = React.createRef(); 117 | 118 | handleClick = () => { 119 | this.editorRef.current.getInstance().exec('Bold'); 120 | }; 121 | 122 | render() { 123 | return ( 124 | <> 125 | 132 | 133 | 134 | ); 135 | } 136 | } 137 | ``` 138 | 139 | ### Getting the root element 140 | 141 | An instance of the wrapper component also provides a handy method for getting the root element. If you want to manipulate the root element directly, you can call `getRootElement` to get the element. 142 | 143 | ```js 144 | class MyComponent extends React.Component { 145 | editorRef = React.createRef(); 146 | 147 | handleClickButton = () => { 148 | this.editorRef.current.getRootElement().classList.add('my-editor-root'); 149 | } 150 | 151 | render() { 152 | return ( 153 | <> 154 | 161 | 162 | 163 | ); 164 | } 165 | } 166 | ``` 167 | 168 | ### Events 169 | [All the events of TOAST UI Editor](https://nhn.github.io/tui.editor/api/latest/ToastUIEditor.html#focus) are supported in the form of `on[EventName]` props. The first letter of each event name should be capitalized. For example, for using `focus` event you can use `onFocus` prop like the example below. 170 | 171 | ```js 172 | class MyComponent extends React.Component { 173 | handleFocus = () => { 174 | console.log('focus!!'); 175 | } 176 | 177 | render() { 178 | return ( 179 | 188 | ); 189 | } 190 | } 191 | ``` 192 | 193 | ## 🔧 Pull Request Steps 194 | 195 | TOAST UI products are open source, so you can create a pull request(PR) after you fix issues. 196 | Run npm scripts and develop yourself with the following process. 197 | 198 | ### Setup 199 | 200 | Fork `master` branch into your personal repository. 201 | Clone it to local computer. Install node modules. 202 | Before starting development, you should check to have any errors. 203 | 204 | ``` sh 205 | $ git clone https://github.com/{your-personal-repo}/[[repo name]].git 206 | $ cd [[repo name]] 207 | $ npm install 208 | ``` 209 | 210 | ### Develop 211 | 212 | Let's start development! 213 | 214 | ### Pull Request 215 | 216 | Before PR, check to test lastly and then check any errors. 217 | If it has no error, commit and then push it! 218 | 219 | For more information on PR's step, please see links of Contributing section. 220 | 221 | ## 💬 Contributing 222 | * [Code of Conduct](https://github.com/nhn/toast-ui.react-editor/blob/master/CODE_OF_CONDUCT.md) 223 | * [Contributing guideline](https://github.com/nhn/toast-ui.react-editor/blob/master/CONTRIBUTING.md) 224 | * [Commit convention](https://github.com/nhn/toast-ui.react-editor/blob/master/docs/COMMIT_MESSAGE_CONVENTION.md) 225 | 226 | ## 📜 License 227 | This software is licensed under the [MIT](./LICENSE) © [NHN.](https://github.com/nhn) 228 | --------------------------------------------------------------------------------