{"id":13447457,"url":"https://github.com/brijeshb42/medium-draft","last_synced_at":"2026-01-29T15:10:59.954Z","repository":{"id":45717952,"uuid":"56425325","full_name":"brijeshb42/medium-draft","owner":"brijeshb42","description":"📝 A medium like Rich Text Editor built on draft-js with a focus on keyboard shortcuts.","archived":false,"fork":false,"pushed_at":"2026-01-15T12:31:51.000Z","size":943,"stargazers_count":1715,"open_issues_count":51,"forks_count":241,"subscribers_count":33,"default_branch":"master","last_synced_at":"2026-01-15T16:53:44.494Z","etag":null,"topics":["draft-js","editor","medium-editor","rich-text-editor"],"latest_commit_sha":null,"homepage":"https://bitwiser.in/medium-draft/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/brijeshb42.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-04-17T07:53:56.000Z","updated_at":"2026-01-15T12:31:56.000Z","dependencies_parsed_at":"2022-09-26T20:40:48.757Z","dependency_job_id":null,"html_url":"https://github.com/brijeshb42/medium-draft","commit_stats":null,"previous_names":[],"tags_count":26,"template":false,"template_full_name":null,"purl":"pkg:github/brijeshb42/medium-draft","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brijeshb42%2Fmedium-draft","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brijeshb42%2Fmedium-draft/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brijeshb42%2Fmedium-draft/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brijeshb42%2Fmedium-draft/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/brijeshb42","download_url":"https://codeload.github.com/brijeshb42/medium-draft/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brijeshb42%2Fmedium-draft/sbom","scorecard":{"id":253858,"data":{"date":"2025-08-11","repo":{"name":"github.com/brijeshb42/medium-draft","commit":"c5350bb76489fd4f2f90f1dc69db92224138d20a"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.4,"checks":[{"name":"Code-Review","score":5,"reason":"Found 5/9 approved changesets -- score normalized to 5","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":0,"reason":"project is archived","details":["Warn: Repository is archived."],"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 26 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"127 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-968p-4wvh-cqc8","Warn: Project is vulnerable to: GHSA-6chw-6frg-f759","Warn: Project is vulnerable to: GHSA-v88g-cgmw-v5xw","Warn: Project is vulnerable to: GHSA-whgm-jr23-g3j9","Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw","Warn: Project is vulnerable to: GHSA-fwr7-v2mv-hh25","Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92","Warn: Project is vulnerable to: GHSA-qwcr-r2fm-qrc7","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-cwfw-4gq5-mrqx","Warn: Project is vulnerable to: GHSA-g95f-p29q-9xw4","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-x9w5-v3q2-3rhw","Warn: Project is vulnerable to: GHSA-257v-vj4p-3w2h","Warn: Project is vulnerable to: GHSA-pxg6-pf52-xh8x","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-p28h-cc7q-c4fg","Warn: Project is vulnerable to: GHSA-9vvw-cc9w-f27h","Warn: Project is vulnerable to: GHSA-gxpj-cx7g-858c","Warn: Project is vulnerable to: GHSA-w573-4hg7-7wgq","Warn: Project is vulnerable to: GHSA-h6ch-v84p-w6p9","Warn: Project is vulnerable to: GHSA-3wcq-x3mq-6r9p","Warn: Project is vulnerable to: GHSA-vh7m-p724-62c2","Warn: Project is vulnerable to: GHSA-r9p9-mrjm-926w","Warn: Project is vulnerable to: GHSA-434g-2637-qmqr","Warn: Project is vulnerable to: GHSA-49q7-c7j4-3p7m","Warn: Project is vulnerable to: GHSA-977x-g7h5-7qgw","Warn: Project is vulnerable to: GHSA-f7q4-pwc6-w24p","Warn: Project is vulnerable to: GHSA-fc9h-whq2-v747","Warn: Project is vulnerable to: GHSA-vjh7-7g9h-fjfh","Warn: Project is vulnerable to: GHSA-4gmj-3p3h-gm8h","Warn: Project is vulnerable to: GHSA-6h5x-7c5m-7cr7","Warn: Project is vulnerable to: GHSA-rv95-896h-c2vc","Warn: Project is vulnerable to: GHSA-qw6h-vgh9-j6wx","Warn: Project is vulnerable to: GHSA-74fj-2j2h-c42q","Warn: Project is vulnerable to: GHSA-pw2r-vq6v-hr8c","Warn: Project is vulnerable to: GHSA-jchw-25xp-jwwc","Warn: Project is vulnerable to: GHSA-cxjh-pqwp-8mfp","Warn: Project is vulnerable to: GHSA-fjxv-7rqg-78g4","Warn: Project is vulnerable to: GHSA-8r6j-v8pm-fqw3","Warn: Project is vulnerable to: MAL-2023-462","Warn: Project is vulnerable to: GHSA-qh2h-chj9-jffq","Warn: Project is vulnerable to: GHSA-765h-qjxv-5f44","Warn: Project is vulnerable to: GHSA-f2jv-r9rf-7988","Warn: Project is vulnerable to: GHSA-43f8-2h32-f4cj","Warn: Project is vulnerable to: GHSA-6x33-pw7p-hmpq","Warn: Project is vulnerable to: GHSA-c7qv-q95q-8v27","Warn: Project is vulnerable to: GHSA-qqgx-2p2h-9c37","Warn: Project is vulnerable to: GHSA-78xj-cgh5-2h22","Warn: Project is vulnerable to: GHSA-2p57-rm9w-gvfp","Warn: Project is vulnerable to: GHSA-7r28-3m3f-r2pr","Warn: Project is vulnerable to: GHSA-r8j5-h5cx-65gg","Warn: Project is vulnerable to: GHSA-2pr6-76vf-7546","Warn: Project is vulnerable to: GHSA-8j8c-7jfh-h6hx","Warn: Project is vulnerable to: GHSA-896r-f27r-55mw","Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h","Warn: Project is vulnerable to: GHSA-6c8f-qphg-qjgp","Warn: Project is vulnerable to: GHSA-76p3-8jx3-jpfq","Warn: Project is vulnerable to: GHSA-3rfm-jhwj-7488","Warn: Project is vulnerable to: GHSA-hhq3-ff78-jv3g","Warn: Project is vulnerable to: GHSA-p6mc-m468-83gw","Warn: Project is vulnerable to: GHSA-29mw-wpgm-hmr9","Warn: Project is vulnerable to: GHSA-35jh-r3h4-6jhm","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv","Warn: Project is vulnerable to: GHSA-hxm2-r34f-qmc5","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-vh95-rmgr-6w4m","Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h","Warn: Project is vulnerable to: GHSA-w9mr-4mfr-499f","Warn: Project is vulnerable to: GHSA-r683-j2x4-v87g","Warn: Project is vulnerable to: GHSA-92xj-mqp7-vmcj","Warn: Project is vulnerable to: GHSA-wxgw-qj99-44c2","Warn: Project is vulnerable to: GHSA-5rrq-pxf6-6jx5","Warn: Project is vulnerable to: GHSA-8fr3-hfg3-gpgp","Warn: Project is vulnerable to: GHSA-gf8q-jrpm-jvxq","Warn: Project is vulnerable to: GHSA-2r2c-g63r-vccr","Warn: Project is vulnerable to: GHSA-cfm4-qjh2-4765","Warn: Project is vulnerable to: GHSA-x4jg-mjrx-434g","Warn: Project is vulnerable to: GHSA-9v62-24cr-58cx","Warn: Project is vulnerable to: GHSA-r8f7-9pfq-mjmv","Warn: Project is vulnerable to: GHSA-rp65-9cf3-cjxr","Warn: Project is vulnerable to: GHSA-76c9-3jph-rj3q","Warn: Project is vulnerable to: GHSA-hj48-42vr-x3v9","Warn: Project is vulnerable to: GHSA-9wv6-86v2-598j","Warn: Project is vulnerable to: GHSA-rhx6-c78j-4q9w","Warn: Project is vulnerable to: GHSA-h7cp-r72f-jxh6","Warn: Project is vulnerable to: GHSA-v62p-rq8g-8h59","Warn: Project is vulnerable to: GHSA-566m-qj78-rww5","Warn: Project is vulnerable to: GHSA-7fh5-64p2-3v2j","Warn: Project is vulnerable to: GHSA-hrpp-h998-j3pp","Warn: Project is vulnerable to: GHSA-p8p7-x288-28g6","Warn: Project is vulnerable to: GHSA-7mwh-4pqv-wmr8","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-m6fv-jmcg-4jfg","Warn: Project is vulnerable to: GHSA-cm22-4g7w-348p","Warn: Project is vulnerable to: GHSA-4g88-fppr-53pp","Warn: Project is vulnerable to: GHSA-4jqc-8m5r-9rpr","Warn: Project is vulnerable to: GHSA-c9g6-9335-x697","Warn: Project is vulnerable to: GHSA-j44m-qm6p-hp7m","Warn: Project is vulnerable to: GHSA-3jfq-g458-7qm9","Warn: Project is vulnerable to: GHSA-5955-9wpr-37jh","Warn: Project is vulnerable to: GHSA-f5x3-32g6-xq36","Warn: Project is vulnerable to: GHSA-r628-mhmh-qjhw","Warn: Project is vulnerable to: GHSA-9r2w-394v-53qc","Warn: Project is vulnerable to: GHSA-qq89-hq3f-393p","Warn: Project is vulnerable to: GHSA-52f5-9888-hmc6","Warn: Project is vulnerable to: GHSA-72xf-g2v4-qvf3","Warn: Project is vulnerable to: GHSA-7p7h-4mm5-852v","Warn: Project is vulnerable to: GHSA-662x-fhqg-9p8v","Warn: Project is vulnerable to: GHSA-394c-5j6w-4xmx","Warn: Project is vulnerable to: GHSA-78cj-fxph-m83p","Warn: Project is vulnerable to: GHSA-fhg7-m89q-25r3","Warn: Project is vulnerable to: GHSA-cf4h-3jhx-xvhq","Warn: Project is vulnerable to: GHSA-46c4-8wrp-j99v","Warn: Project is vulnerable to: GHSA-9m6j-fcg5-2442","Warn: Project is vulnerable to: GHSA-hh27-ffr2-f2jc","Warn: Project is vulnerable to: GHSA-rqff-837h-mm52","Warn: Project is vulnerable to: GHSA-8v38-pw62-9cw2","Warn: Project is vulnerable to: GHSA-hgjh-723h-mx2j","Warn: Project is vulnerable to: GHSA-jf5r-8hm2-f872","Warn: Project is vulnerable to: GHSA-wr3j-pwj9-hqq6","Warn: Project is vulnerable to: GHSA-cf66-xwfp-gvc4","Warn: Project is vulnerable to: GHSA-4v9v-hfq4-rm2v","Warn: Project is vulnerable to: GHSA-9jgg-88mc-972h","Warn: Project is vulnerable to: GHSA-g78m-2chm-r7qv","Warn: Project is vulnerable to: GHSA-c4w7-xm78-47vh","Warn: Project is vulnerable to: GHSA-p9pc-299p-vxgp"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-17T09:08:14.018Z","repository_id":45717952,"created_at":"2025-08-17T09:08:14.018Z","updated_at":"2025-08-17T09:08:14.018Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28880017,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-29T10:31:27.438Z","status":"ssl_error","status_checked_at":"2026-01-29T10:31:01.017Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["draft-js","editor","medium-editor","rich-text-editor"],"created_at":"2024-07-31T05:01:18.309Z","updated_at":"2026-01-29T15:10:59.931Z","avatar_url":"https://github.com/brijeshb42.png","language":"JavaScript","readme":"Latest development is going on in this [branch](https://github.com/brijeshb42/medium-draft/tree/djs-pl-ed-ts).\n\n# medium-draft - [demo](http://bitwiser.in/medium-draft/)\n\n[![npm version](https://badge.fury.io/js/medium-draft.svg)](https://www.npmjs.com/package/medium-draft) [![Build Status](https://travis-ci.org/brijeshb42/medium-draft.svg?branch=master)](https://travis-ci.org/brijeshb42/medium-draft)\n\nA medium like rich text editor built upon [draft-js](https://facebook.github.io/draft-js/) with an emphasis on eliminating mouse usage by adding relevant keyboard shortcuts.\n\n[Documentation](https://github.com/brijeshb42/medium-draft/wiki) in progress.\n\nInstall the beta version using\n\n`npm install medium-draft@beta`\n\n### Features\n\n- Focus on keyboard shortcuts and auto transform of text blocks.\n- Image addition with support for rich text captioning.\n- Minimize mouse usage.\n- Autolists.\n- Proper handling of `RETURN` presses.\n- It also has implementations of some custom blocks like:\n    - `caption` - Can be used as a caption for media blocks like image or video instead of nested `draft-js` instances for simplicity.\n    - `block-quote-caption` - Caption for `blockquote`s.\n    - `todo` - Todo text with a checkbox.\n- Easily customizable toolbar via `toolbarConfig` for the following block and inline styles. Defaults to all. Case sensitive.\n     - `block: ['ordered-list-item', 'unordered-list-item', 'blockquote', 'header-three', 'todo']`\n     - `inline: ['BOLD', 'ITALIC', 'UNDERLINE', 'hyperlink', 'HIGHLIGHT']`\n\n\n##### Following are the keyboard shortcuts to toggle block types (\u003ckbd\u003eAlt and CTRL\u003c/kbd\u003e for Windows/Linux and \u003ckbd\u003eOption and Command\u003c/kbd\u003e for OSX)\n*   \u003ckbd\u003eAlt/Option\u003c/kbd\u003e +\n\n    *   \u003ckbd\u003e1\u003c/kbd\u003e - Toggle Ordered list item\n    *   \u003ckbd\u003e*\u003c/kbd\u003e - Toggle Unordered list item\n    *   \u003ckbd\u003e#\u003c/kbd\u003e - Toggle Header-three.\n    *   \u003ckbd\u003e\u003c\u003c/kbd\u003e - Toggle Caption block.\n    *   \u003ckbd\u003e\u003e\u003c/kbd\u003e - Toggle unstyled or paragraph block.\n    *   \u003ckbd\u003eH\u003c/kbd\u003e - Highlight selection.\n\n##### Other Shortcuts\n\n* \u003ckbd\u003eCMD/CTRL\u003c/kbd\u003e + \u003ckbd\u003eK\u003c/kbd\u003e -\u003e Add Link\n* \u003ckbd\u003eCMD/CTRL\u003c/kbd\u003e + \u003ckbd\u003eSHIFT\u003c/kbd\u003e + \u003ckbd\u003eK\u003c/kbd\u003e -\u003e Remove link if cursor is inside a word with link.\n\n##### Editor level commands\n\nThese commands are not a part of the core editor but have been implemented in the example code that uses the `medium-draft` editor.\n\n*   \u003ckbd\u003eCommand/CTRL\u003c/kbd\u003e + \u003ckbd\u003eS\u003c/kbd\u003e - Save current data to `localstorage`.\n*   \u003ckbd\u003eAlt + Shift\u003c/kbd\u003e + \u003ckbd\u003eL\u003c/kbd\u003e - Load previously saved data from `localstorage`.\n\n##### Special characters while typing: While typing in an empty block, if the content matches one of the following, that particular block's type and look will be changed to the corresponding block specified below\n\n*   `--` - If current block is `blockquote`, it will be changed to `block-quote-caption`, else `caption`.\n*   `*.` `(An asterisk and a period)` - `unordered-list-item`.\n*   `*\u003cSPACE\u003e` `(An asterisk and a space)` - `unordered-list-item`.\n*   `-\u003cSPACE\u003e` `(A hyphen and a space)` - `unordered-list-item`.\n*   `1.` `(The number 1 and a period)` - `unordered-list-item`.\n*   `##` - `header-two`.\n*   `[]` - `todo`.\n*   `==` - `unstyled`.\n\n### Installation\n\n- **npm**.\n    - `npm install medium-draft`.\n    - `import Editor from 'medium-draft'`\n- **Browser**\n    - Include `\u003clink rel=\"stylesheet\" type=\"text/css\" href=\"https://unpkg.com/medium-draft/dist/medium-draft.css\"\u003e` in `\u003chead\u003e`\n    - Include `\u003cscript src=\"https://unpkg.com/medium-draft/dist/medium-draft.js\"\u003e\u003c/script\u003e`. **medium-draft** is available in the global object as `MediumDraft`.\n\n### Usage\n\n`medium-draft` sits on top of `draft-js` with some built in functionalities and blocks. Its API is almost the same as that of `draft-js`. You can take a look at [the demo editor's code](https://github.com/brijeshb42/medium-draft/tree/master/src/example.js) to see the implementation.\n\n#### CSS\n\nInclude the css that comes with the library in your HTML -\n```html\n\u003clink rel=\"stylesheet\" type=\"text/css\" href=\"https://unpkg.com/medium-draft/dist/medium-draft.css\"\u003e\n```\n\nIf you are using `webpack` for bundling, you can import the CSS like this in your JS code\n```javascript\nimport 'medium-draft/lib/index.css';\n```\n\nIf you are using `sideButtons`, you will also need to include the css for `font-awesome` -\n\n```html\n\u003clink rel=\"stylesheet\" href=\"//maxcdn.bootstrapcdn.com/font-awesome/4.6.1/css/font-awesome.min.css\"\u003e\n```\n\nor something equivalent.\n\n#### JS (ES6)\n\nAt the minimum, you need to provide `editorState` and `onChange` props, the same as `draft-js`.\n\n```javascript\n\nimport React from 'react';\nimport ReactDOM from 'react-dom';\n\n// if using webpack\n// import 'medium-draft/lib/index.css';\n\nimport {\n  Editor,\n  createEditorState,\n} from 'medium-draft';\n\nclass App extends React.Component {\n  constructor(props) {\n    super(props);\n\n    this.state = {\n      editorState: createEditorState(), // for empty content\n    };\n\n    /*\n    this.state = {\n      editorState: createEditorState(data), // with content\n    };\n    */\n\n    this.onChange = (editorState) =\u003e {\n      this.setState({ editorState });\n    };\n\n    this.refsEditor = React.createRef();\n\n  }\n\n  componentDidMount() {\n    this.refsEditor.current.focus();\n  }\n\n  render() {\n    const { editorState } = this.state;\n    return (\n      \u003cEditor\n        ref={this.refsEditor}\n        editorState={editorState}\n        onChange={this.onChange} /\u003e\n    );\n  }\n};\n\nReactDOM.render(\n  \u003cApp /\u003e,\n  document.getElementById('app')\n);\n```\n\n### Customizing side buttons\n\n`medium-draft`'s `Editor` accepts a prop called `sideButtons`. By default, there is only one (image) button, but you can add more. The `sideButtons` prop must be an array of objects with each object having the following signature:\n\n```js\n{\n  \"title\": \"unique-button-name\",\n  \"component\": ButtonComponent\n}\n```\n\nFor ex:\n\n```js\n{\n  \"title\": \"Image\",\n  \"component\": ImageSideButton\n}\n```\n\nExample code:\n\nRight now, the image button simply adds an image inside the editor using `URL.createObjectURL`. But if you would like to first upload the image to your server and then add that image to the editor, you can follow one of the 2 methods:\n\n1. Either extend the default `ImageSideButton` component that comes with `medium-draft`.\n\n2. Or create your own component with the complete functionality yourself.\n\nFor simplicity, we will follow the first method. If you study the [implementation](src/components/sides/image.js) of `ImageSideButton`, you will see an `onChange` method that receives the file chooser event where the seleced files are available as `event.target.files`. We will simply override this method as we don't want to customize anything else. Also note that each side button component receives `getEditorState` function (returns the draft `editorState`), `setEditorState(newEditorState)` function (sets the new editorState) and `close` function which you need to call manually to close the side buttons list:\n\n```javascript\nimport React from 'react';\nimport {\n  ImageSideButton,\n  Block,\n  addNewBlock,\n  createEditorState,\n  Editor,\n} from 'medium-draft';\nimport 'isomorphic-fetch';\n\nclass CustomImageSideButton extends ImageSideButton {\n\n  /*\n  We will only check for first file and also whether\n  it is an image or not.\n  */\n  onChange(e) {\n    const file = e.target.files[0];\n    if (file.type.indexOf('image/') === 0) {\n      // This is a post request to server endpoint with image as `image`\n      const formData = new FormData();\n      formData.append('image', file);\n      fetch('/your-server-endpoint', {\n        method: 'POST',\n        body: formData,\n      }).then((response) =\u003e {\n        if (response.status === 200) {\n          // Assuming server responds with\n          // `{ \"url\": \"http://example-cdn.com/image.jpg\"}`\n          return response.json().then(data =\u003e {\n            if (data.url) {\n              this.props.setEditorState(addNewBlock(\n                this.props.getEditorState(),\n                Block.IMAGE, {\n                  src: data.url,\n                }\n              ));\n            }\n          });\n        }\n      });\n    }\n    this.props.close();\n  }\n\n}\n\n// Now pass this component instead of default prop to Editor example above.\nclass App extends React.Component {\n  constructor(props) {\n    super(props);\n\n    this.sideButtons = [{\n      title: 'Image',\n      component: CustomImageSideButton,\n    }];\n\n    this.state = {\n      editorState: createEditorState(), // for empty content\n    };\n\n    /*\n    this.state = {\n      editorState: createEditorState(data), // with content\n    };\n    */\n\n    this.onChange = (editorState) =\u003e {\n      this.setState({ editorState });\n    };\n\n    this.refsEditor = React.createRef()\n\n  }\n\n  componentDidMount() {\n    this.refsEditor.current.focus();\n  }\n\n  render() {\n    const { editorState } = this.state;\n    return (\n      \u003cEditor\n        ref={this.refsEditor}\n        editorState={editorState}\n        onChange={this.onChange}\n        sideButtons={this.sideButtons}\n      /\u003e\n    );\n  }\n};\n```\n\n### Removing side buttons\n\nTo remove the side buttons entirely, so that the circular add button never appears, just pass an empty array:\n\n`sideButtons={[]}`\n\n### Customizing toolbar\n\nThere are three props you can use to customize the buttons in the toolbar that appears whenever you select text within the editor:\n\n- `blockButtons`\n- `inlineButtons`\n- `toolbarConfig`\n\nThe default block-level editor buttons are `['header-three', 'unordered-list-item', 'ordered-list-item', 'blockquote', 'todo']`, and the default inline editor buttons `['BOLD', 'ITALIC', 'UNDERLINE', 'HIGHLIGHT', 'hyperlink']`.\n\nFor example, if you want to keep the default block buttons and add a few more, you can do something like the following:\n\n```js\nimport { BLOCK_BUTTONS } from 'medium-draft';\n\nconst blockButtons = [{\n    label: 'H1',\n    style: 'header-one',\n    icon: 'header',\n    description: 'Heading 1',\n  },\n  {\n    label: 'H2',\n    style: 'header-two',\n    icon: 'header',\n    description: 'Heading 2',\n}].concat(BLOCK_BUTTONS);\n\n// in your component\n\u003cEditor blockButtons={blockButtons} ... /\u003e\n```\n\nIf you want to __remove__ some buttons or __reorder__ them, you could use functions like `array.slice` on the default `BLOCK_BUTTONS` and `INLINE_BUTTONS`, but this is probably more trouble than it's worth.\n\nFor this purpose it's better to use the `toolbarConfig` prop:\n\n```js\n// custom ordering for block and inline buttons, and removes some buttons\nconst toolbarConfig = {\n  block: ['unordered-list-item', 'header-one', 'header-three'],\n  inline: ['BOLD', 'UNDERLINE', 'hyperlink'],\n}\n\n\u003cEditor toolbarConfig={toolbarConfig} ... /\u003e\n```\n\nThe strings inside the `block` and `inline` arrays must match the `style` attribute inside `blockButtons` and `inlineButtons` arrays.\n\nTo summarize: if you need add, remove, and reorder buttons, it's probably easiest to use `blockButtons`, `inlineButtons`, and `toolbarConfig` together.\n\n#### Supply Your Own Toolbar\n\nIf the toolbar customization props aren't sufficient to get the behavior you want, you can inject your own toolbar with the `ToolbarComponent` prop.\n\nThis pattern is called [component injection](https://reactpatterns.github.io/Component-injection/). Your `ToolbarComponent` receives the same props as the default toolbar.\n\nIf you want to write your own toolbar component, a good place to start is [with the default component](https://github.com/brijeshb42/medium-draft/blob/master/src/components/toolbar.js).\n\n### Render data to HTML\n\nThe feature to export HTML is available from version `0.4.1` onwards.\n\n`medium-draft` uses [draft-convert](https://github.com/hubspot/draft-convert) (which in turn uses react-dom-server) to render `draft-js`'s `editorState` to HTML.\n\nThe exporter is not a part of the core library. If you want to use `medium-draft-exporter`, follow these steps -\n\n#### Browserify/webpack\n\n- `npm install draft-convert`.\n\n`draft-convert` is part of `peerDependencies` of `medium-draft`.\n\n##### Code\n\n```js\n  import mediumDraftExporter from 'medium-draft/lib/exporter';\n  const editorState = /* your draft editorState */;\n  const renderedHTML = mediumDraftExporter(editorState.getCurrentContent());\n  /* Use renderedHTML */\n```\n\n#### Browser\n\n- Add the following scripts before your js code.\n\n```html\n\u003cscript src=\"https://unpkg.com/react-dom@15.2.1/dist/react-dom-server.min.js\"\u003e\u003c/script\u003e\n\u003cscript src=\"https://unpkg.com/draft-convert@1.3.3/dist/draft-convert.min.js\"\u003e\u003c/script\u003e\n\u003cscript src=\"https://unpkg.com/medium-draft/dist/medium-draft-exporter.js\"\u003e\u003c/script\u003e\n```\n\nThe exporter is available as `MediumDraftExporter` global;\n\n- JS\n\n```js\nvar mediumDraftExporter = MediumDraftExporter.default;\nconst editorState = /* your draft editorState */;\nconst renderedHTML = mediumDraftExporter(editorState.getCurrentContent());\n/* Use renderedHTML */\n```\n\nThe `medium-draft-exporter` also comes with a preset CSS if you want to apply some basic styles to the rendered HTML.\n\n- In webpack, as part of your rendered HTML's page, use this-\n  ```js\n  import 'medium-draft/lib/basic.css'\n  ```\n\n- In browser, in your rendered html's page, you can include this stylesheet link\n  ```html\n  \u003clink rel=\"stylesheet\" type=\"text/css\" href=\"https://unpkg.com/medium-draft/dist/basic.css\"\u003e\n  ```\n\n### Load HTML exported using `medium-draft-exporter` to `editorState`\n\nThe feature to export HTML is available from version `0.5.3` onwards.\n\n`medium-draft` uses [draft-convert](https://github.com/hubspot/draft-convert) (which in turn uses react-dom-server) to render `draft-js`'s `editorState` to HTML.\n\nThe importer is not a part of the core library. If you want to use `medium-draft-importer`, follow these steps -\n\n#### Browserify/webpack\n\n- `npm install draft-convert`.\n\n`draft-convert` is part of `peerDependencies` of `medium-draft`.\n\n##### Code\n\n```js\n  import { convertToRaw } from 'draft-js';\n  import { createEditorState } from 'medium-draft';\n  import mediumDraftImporter from 'medium-draft/lib/importer';\n\n  const html = /* your previously exported html */;\n  const editorState = createEditorState(convertToRaw(mediumDraftImporter(html)));\n  // Use this editorState\n```\n\n#### Browser\n\n- Add the following scripts before your js code.\n\n```html\n\u003cscript src=\"https://unpkg.com/react-dom@15.2.1/dist/react-dom-server.min.js\"\u003e\u003c/script\u003e\n\u003cscript src=\"https://unpkg.com/draft-convert@1.3.3/dist/draft-convert.min.js\"\u003e\u003c/script\u003e\n\u003cscript src=\"https://unpkg.com/medium-draft/dist/medium-draft-importer.js\"\u003e\u003c/script\u003e\n```\n\nThe importer is available as `MediumDraftImporter` global;\n\n- JS\n\n```js\n  const { convertToRaw } = Draft;\n  const { createEditorState } = MediumDraft;\n  const mediumDraftImporter = MediumDraftImporter.default;\n  const html = /* your previously exported html */;\n  const editorState = createEditorState(convertToRaw(mediumDraftImporter(html)));\n  // Use this editorState\n```\n\n### Issues\n\n- [x] Write an exporter to export draft data to HTML specifically for `medium-draft`.\n- [ ] Figure out a way to show placeholder text for empty image captions.\n- [x] Currently, the toolbar that appears when text is selected needs to be fixed regarding its position in the viewport.\n\n### Developer\n\n- Clone this repo `git clone https://github.com/brijeshb42/medium-draft.git`.\n- Install node packages `npm install react react-dom draft-convert \u0026\u0026 npm install`.\n- Start local demo `npm run dev`. This will start a local server on port `8080`.\n- Build using `npm run build`.\n\n#### LICENSE\n\nMIT\n","funding_links":[],"categories":["JavaScript","Standalone Editors Built on Draft.js"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrijeshb42%2Fmedium-draft","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbrijeshb42%2Fmedium-draft","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrijeshb42%2Fmedium-draft/lists"}