├── .nvmrc
├── .env
├── src
├── demo
│ ├── components
│ │ ├── App.css
│ │ ├── page-nav.css
│ │ ├── App.test.tsx
│ │ ├── __snapshots__
│ │ │ ├── Image.test.tsx.snap
│ │ │ ├── Snippet.test.tsx.snap
│ │ │ ├── Highlight.test.tsx.snap
│ │ │ ├── SentryBoundary.test.tsx.snap
│ │ │ ├── DemoEditor.test.tsx.snap
│ │ │ └── App.test.tsx.snap
│ │ ├── Snippet.css
│ │ ├── DemoEditor.css
│ │ ├── Image.tsx
│ │ ├── Highlight.test.tsx
│ │ ├── Snippet.tsx
│ │ ├── header.css
│ │ ├── Highlight.tsx
│ │ ├── Link.tsx
│ │ ├── SentryBoundary.test.tsx
│ │ ├── Image.test.tsx
│ │ ├── Snippet.test.tsx
│ │ ├── Link.test.tsx
│ │ ├── SentryBoundary.tsx
│ │ ├── App.tsx
│ │ ├── DemoEditor.tsx
│ │ └── DemoEditor.test.tsx
│ └── utils
│ │ ├── utilities.css
│ │ ├── layout.css
│ │ ├── elements.css
│ │ ├── typography.css
│ │ ├── objects.css
│ │ ├── DraftUtils.ts
│ │ └── DraftUtils.test.ts
├── lib
│ ├── api
│ │ ├── __snapshots__
│ │ │ └── copypaste.test.ts.snap
│ │ ├── conversion.ts
│ │ ├── conversion.test.ts
│ │ ├── lists.ts
│ │ ├── lists.test.ts
│ │ ├── copypaste.ts
│ │ └── copypaste.test.ts
│ ├── index.ts
│ └── index.test.ts
├── index.test.ts
├── index.tsx
└── setupTests.js
├── .eslintrc.js
├── .githooks
├── pre-push
├── commit-msg
├── pre-commit.6.lint.sh
├── pre-commit.8.test.sh
├── pre-commit.5.prettier.sh
├── pre-commit.0.whitespace.sh
├── deploy.sh
└── pre-commit
├── .eslintignore
├── public
├── favicon.ico
├── mstile-70x70.png
├── favicon-16x16.png
├── favicon-32x32.png
├── mstile-144x144.png
├── mstile-150x150.png
├── mstile-310x150.png
├── mstile-310x310.png
├── apple-touch-icon.png
├── wysiwyg-magic-wand.png
├── android-chrome-192x192.png
├── android-chrome-512x512.png
├── browserconfig.xml
├── site.webmanifest
├── favicon.svg
├── safari-pinned-tab.svg
└── index.html
├── .prettierignore
├── .github
├── repository-social-media.png
├── ISSUE_TEMPLATE
│ ├── config.yml
│ ├── feature_request.md
│ └── bug_report.md
├── PULL_REQUEST_TEMPLATE.md
├── workflows
│ └── ci.yml
└── renovate.json5
├── commitlint.config.js
├── .env.production
├── prettier.config.js
├── tsconfig.json
├── .editorconfig
├── docs
├── SECURITY.md
├── SUPPORT.md
├── CONTRIBUTING.md
├── CODE_OF_CONDUCT.md
└── flow-typed
│ └── npm
│ └── draftjs-conductor_v3.0.0.js
├── rollup.config.js
├── LICENSE
├── .gitignore
├── release.config.js
├── package.json
├── dangerfile.js
├── CHANGELOG.md
└── README.md
/.nvmrc:
--------------------------------------------------------------------------------
1 | 18
2 |
--------------------------------------------------------------------------------
/.env:
--------------------------------------------------------------------------------
1 | REACT_APP_THEME_COLOR="#002ea2"
2 | REACT_APP_RAVEN=""
3 |
--------------------------------------------------------------------------------
/src/demo/components/App.css:
--------------------------------------------------------------------------------
1 | h2 {
2 | margin-top: 3rem;
3 | }
4 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: "react-app",
3 | };
4 |
--------------------------------------------------------------------------------
/.githooks/pre-push:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | npx danger local --base main
4 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | *.min.js
3 | coverage/
4 | dist/
5 | es/
6 | *.bundle.js
7 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thibaudcolas/draftjs-conductor/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/public/mstile-70x70.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thibaudcolas/draftjs-conductor/HEAD/public/mstile-70x70.png
--------------------------------------------------------------------------------
/public/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thibaudcolas/draftjs-conductor/HEAD/public/favicon-16x16.png
--------------------------------------------------------------------------------
/public/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thibaudcolas/draftjs-conductor/HEAD/public/favicon-32x32.png
--------------------------------------------------------------------------------
/public/mstile-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thibaudcolas/draftjs-conductor/HEAD/public/mstile-144x144.png
--------------------------------------------------------------------------------
/public/mstile-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thibaudcolas/draftjs-conductor/HEAD/public/mstile-150x150.png
--------------------------------------------------------------------------------
/public/mstile-310x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thibaudcolas/draftjs-conductor/HEAD/public/mstile-310x150.png
--------------------------------------------------------------------------------
/public/mstile-310x310.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thibaudcolas/draftjs-conductor/HEAD/public/mstile-310x310.png
--------------------------------------------------------------------------------
/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thibaudcolas/draftjs-conductor/HEAD/public/apple-touch-icon.png
--------------------------------------------------------------------------------
/public/wysiwyg-magic-wand.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thibaudcolas/draftjs-conductor/HEAD/public/wysiwyg-magic-wand.png
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | *.min.js
3 | coverage/
4 | dist/
5 | *.bundle.js
6 | public/source-map-explorer.html
7 | build/
8 |
--------------------------------------------------------------------------------
/public/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thibaudcolas/draftjs-conductor/HEAD/public/android-chrome-192x192.png
--------------------------------------------------------------------------------
/public/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thibaudcolas/draftjs-conductor/HEAD/public/android-chrome-512x512.png
--------------------------------------------------------------------------------
/.github/repository-social-media.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thibaudcolas/draftjs-conductor/HEAD/.github/repository-social-media.png
--------------------------------------------------------------------------------
/.githooks/commit-msg:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | COMMIT_EDITMSG="$1"
4 | MESSAGE=$(cat $COMMIT_EDITMSG)
5 |
6 | npx commitlint -e "$COMMIT_EDITMSG"
7 |
--------------------------------------------------------------------------------
/commitlint.config.js:
--------------------------------------------------------------------------------
1 | // See https://github.com/marionebl/commitlint.
2 | module.exports = {
3 | extends: ["@commitlint/config-conventional"],
4 | };
5 |
--------------------------------------------------------------------------------
/src/demo/utils/utilities.css:
--------------------------------------------------------------------------------
1 | .u-text-center {
2 | text-align: center;
3 | }
4 |
5 | .u-wagtail {
6 | color: #358c8b;
7 | white-space: nowrap;
8 | }
9 |
--------------------------------------------------------------------------------
/.githooks/pre-commit.6.lint.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | if [ -n "$JS_STAGED" ];
4 | then
5 | npx eslint --cache --cache-location ./node_modules/.cache/ $JS_STAGED
6 | fi
7 |
--------------------------------------------------------------------------------
/src/demo/components/page-nav.css:
--------------------------------------------------------------------------------
1 | .page-nav {
2 | text-align: center;
3 | }
4 |
5 | @media only screen and (max-width: 480px) {
6 | .page-nav {
7 | text-align: left;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/.env.production:
--------------------------------------------------------------------------------
1 | REACT_APP_RAVEN=""
2 |
--------------------------------------------------------------------------------
/src/demo/components/App.test.tsx:
--------------------------------------------------------------------------------
1 | import { shallow } from "enzyme";
2 | import App from "./App";
3 |
4 | describe("App", () => {
5 | it("renders", () => {
6 | expect(shallow(
9 | `;
10 |
11 | exports[`Image renders 1`] = `
12 |
17 | `;
18 |
--------------------------------------------------------------------------------
/src/demo/utils/elements.css:
--------------------------------------------------------------------------------
1 | @import-normalize;
2 |
3 | *,
4 | *::before,
5 | *::after {
6 | box-sizing: inherit;
7 | }
8 |
9 | body {
10 | box-sizing: border-box;
11 | font-size: 1rem;
12 | color: #333;
13 | font-weight: normal;
14 | }
15 |
16 | img {
17 | max-width: 100%;
18 | }
19 |
20 | a {
21 | color: inherit;
22 | text-decoration: none;
23 | }
24 |
--------------------------------------------------------------------------------
/.githooks/pre-commit.5.prettier.sh:
--------------------------------------------------------------------------------
1 |
2 | #!/usr/bin/env bash
3 | # Format and re-stage fully staged files only.
4 |
5 | if [ -n "$PRETTIER_FULLY_STAGED" ];
6 | then
7 | npx prettier --cache --write $PRETTIER_FULLY_STAGED
8 | git add $PRETTIER_FULLY_STAGED
9 | fi
10 |
11 | if [ -n "$PRETTIER_STAGED" ];
12 | then
13 | npx prettier --cache --check $PRETTIER_STAGED
14 | fi
15 |
--------------------------------------------------------------------------------
/src/demo/components/Snippet.css:
--------------------------------------------------------------------------------
1 | .Snippet {
2 | font-size: 20px;
3 | border-radius: 6px;
4 | border: 1px solid gray;
5 | background-color: whitesmoke;
6 | padding: 40px;
7 | }
8 |
9 | .Snippet__text {
10 | font-size: 16px;
11 | font-weight: bold;
12 | border-radius: 6px;
13 | background-color: lightgrey;
14 | margin-top: 10px;
15 | padding: 40px;
16 | }
17 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | _Before_ submitting a pull request, please make sure to:
2 |
3 | 1. Create your branch from the up to date `main` branch.
4 | 2. If you've added code, add tests!
5 | 3. If you've changed APIs, update the documentation.
6 | 4. Ensure that:
7 |
8 | - The test suite passes, with 100% coverage (`npm run test:coverage`)
9 | - The linting passes (`npm run lint`, `npx flow`).
10 |
11 | Thank you!
12 |
--------------------------------------------------------------------------------
/src/demo/components/DemoEditor.css:
--------------------------------------------------------------------------------
1 | .DemoEditor {
2 | margin: 1rem 0;
3 | padding: 0.25rem;
4 | border: 1px solid black;
5 | background-color: white;
6 | }
7 |
8 | .DraftEditor-root {
9 | font-size: 1rem;
10 | line-height: 1.5;
11 | font-variant-ligatures: none;
12 | overflow: auto;
13 | }
14 |
15 | .public-DraftEditor-content,
16 | .public-DraftEditorPlaceholder-root,
17 | .EditorToolbar {
18 | padding: 0.25rem;
19 | }
20 |
--------------------------------------------------------------------------------
/.githooks/pre-commit.0.whitespace.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Check if this is the initial commit
4 | if git rev-parse --verify HEAD >/dev/null 2>&1
5 | then
6 | against=HEAD
7 | else
8 | against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
9 | fi
10 |
11 | # Use git diff-index to check for whitespace errors
12 | if ! git diff-index --check --cached $against
13 | then
14 | echo "Aborting commit due to whitespace errors."
15 | exit 1
16 | fi
17 |
--------------------------------------------------------------------------------
/src/demo/utils/typography.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica,
3 | Arial, sans-serif;
4 | }
5 |
6 | code {
7 | font-family: Monaco, Bitstream Vera Sans Mono, Lucida Console, Terminal,
8 | Consolas, Liberation Mono, DejaVu Sans Mono, Courier New, monospace;
9 | }
10 |
11 | h1,
12 | h2,
13 | h3,
14 | h4,
15 | h5,
16 | h6 {
17 | margin: 0 0 1rem;
18 | }
19 |
20 | h1,
21 | h2,
22 | h3 {
23 | line-height: 1.1;
24 | }
25 |
--------------------------------------------------------------------------------
/public/site.webmanifest:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Draft.js conductor",
3 | "short_name": "Conductor",
4 | "icons": [
5 | {
6 | "src": "android-chrome-192x192.png",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | },
10 | {
11 | "src": "android-chrome-512x512.png",
12 | "sizes": "512x512",
13 | "type": "image/png"
14 | }
15 | ],
16 | "theme_color": "#002EA2",
17 | "background_color": "#ffffff",
18 | "display": "standalone"
19 | }
20 |
--------------------------------------------------------------------------------
/src/lib/api/__snapshots__/copypaste.test.ts.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`copypaste copy/cut listener works 1`] = `"
17 | Oops. The editor just crashed. 18 |
19 |20 | Our team has been notified. You can provide us with more information if you want to. 21 |
22 |Oops. The editor just crashed.
38 |39 | Our team has been notified. You can provide us with more 40 | information if you want to. 41 |
42 |14 | Generated by :package::rocket: semantic-release 15 |
`; 16 | 17 | const SUCCESS_COMMENT = `:tada: This \${issue.pull_request ? 'pull request is included' : 'issue is fixed'} in v\${nextRelease.version}, available on npm: [${pkg.name}@\${nextRelease.version}](https://www.npmjs.com/package/${pkg.name}). 18 | 19 | ${COMMENT_POSTFIX} 20 | `; 21 | 22 | /** 23 | * See: 24 | * https://semantic-release.gitbook.io/semantic-release/ 25 | * https://github.com/semantic-release/npm 26 | * https://github.com/semantic-release/github 27 | * https://github.com/semantic-release/git 28 | * https://github.com/semantic-release/release-notes-generator 29 | * https://github.com/semantic-release/commit-analyzer 30 | * https://github.com/semantic-release/changelog 31 | */ 32 | module.exports = { 33 | branches: "main", 34 | tagFormat: "v${version}", 35 | npmPublish: true, 36 | tarballDir: "dist", 37 | assets: "dist/*.tgz", 38 | verifyConditions: [ 39 | "@semantic-release/changelog", 40 | "@semantic-release/npm", 41 | "@semantic-release/git", 42 | "@semantic-release/github", 43 | ], 44 | analyzeCommits: { 45 | preset: "angular", 46 | }, 47 | verifyRelease: [], 48 | generateNotes: ["@semantic-release/release-notes-generator"], 49 | prepare: [ 50 | { 51 | path: "@semantic-release/changelog", 52 | changelogFile: "CHANGELOG.md", 53 | changelogTitle: CHANGELOG_HEADER, 54 | }, 55 | { 56 | path: "@semantic-release/exec", 57 | cmd: "prettier --write CHANGELOG.md && rm -rf .git/hooks", 58 | }, 59 | "@semantic-release/npm", 60 | { 61 | path: "@semantic-release/git", 62 | message: 63 | "chore(release): v${nextRelease.version} [skip ci]\n\n${nextRelease.notes}", 64 | assets: [ 65 | "README.md", 66 | "CHANGELOG.md", 67 | "package.json", 68 | "package-lock.json", 69 | ], 70 | }, 71 | ], 72 | publish: [ 73 | "@semantic-release/npm", 74 | { 75 | path: "@semantic-release/github", 76 | assets: ["dist/*.tgz"], 77 | }, 78 | ], 79 | success: [ 80 | { 81 | path: "@semantic-release/github", 82 | successComment: SUCCESS_COMMENT, 83 | }, 84 | ], 85 | fail: ["@semantic-release/github"], 86 | }; 87 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "draftjs-conductor", 3 | "version": "3.0.0", 4 | "description": "📝✨ Little Draft.js helpers to make rich text editors “just work”", 5 | "author": "Thibaud Colas", 6 | "license": "MIT", 7 | "main": "dist/draftjs-conductor.cjs.js", 8 | "module": "dist/draftjs-conductor.esm.js", 9 | "types": "dist/draftjs-conductor.d.ts", 10 | "sideEffects": false, 11 | "keywords": [ 12 | "draftjs", 13 | "draft-js", 14 | "editor", 15 | "react", 16 | "wysiwyg", 17 | "rich text", 18 | "richtext", 19 | "rte" 20 | ], 21 | "repository": { 22 | "type": "git", 23 | "url": "https://github.com/thibaudcolas/draftjs-conductor.git" 24 | }, 25 | "bugs": { 26 | "url": "https://github.com/thibaudcolas/draftjs-conductor/issues" 27 | }, 28 | "homepage": "https://thibaudcolas.github.io/draftjs-conductor/", 29 | "files": [ 30 | "dist/*.js", 31 | "dist/*.d.ts" 32 | ], 33 | "browserslist": "> 1%, not IE 11", 34 | "jest": { 35 | "collectCoverageFrom": [ 36 | "src/lib/**/*.{js,jsx,ts,tsx}", 37 | "!
109 | Little Draft.js helpers to make rich text editors just work.
110 | Built for Draftail and Wagtail
123 |
235 | The default Draft.js copy-paste handlers lose a lot of the formatting 236 | when copy-pasting between Draft.js editors. While this might be ok for 237 | some use cases, sites with multiple editors on the same page need them 238 | to reliably support copy-paste. 239 |
240 |244 | By default, Draft.js only provides support for 5 list levels for 245 | bulleted and numbered lists. While this is often more than enough, 246 | some editors need to go further. This provides infinite list nesting 247 | styles. 248 |
249 |11 | The default Draft.js copy-paste handlers lose a lot of the formatting when copy-pasting between Draft.js editors. While this might be ok for some use cases, sites with multiple editors on the same page need them to reliably support copy-paste. 12 |
13 |112 | By default, Draft.js only provides support for 5 list levels for bulleted and numbered lists. While this is often more than enough, some editors need to go further. This provides infinite list nesting styles. 113 |
114 |` tags on paste – facebook/draft-js#523 (comment)](https://github.com/facebook/draft-js/issues/523#issuecomment-371098488)
73 |
74 | All of those problems can be fixed with this library, which overrides the `copy` and `cut` events to transfer more of the editor’s content, and introduces a function to use with the Draft.js [`handlePastedText`](https://draftjs.org/docs/api-reference-editor#handlepastedtext) to retrieve the pasted content.
75 |
76 | **This will paste all copied content, even if the target editor might not support it.** To ensure only supported content is retained, use filters like [draftjs-filters](https://github.com/thibaudcolas/draftjs-filters).
77 |
78 | Note: IE11 isn’t supported, as it doesn't support storing HTML in the clipboard, and we also use the [`Element.closest`](https://developer.mozilla.org/en-US/docs/Web/API/Element/closest) API.
79 |
80 | #### With draft.js 0.11 and above
81 |
82 | Here’s how to use the copy/cut override, and the paste handler:
83 |
84 | ```js
85 | import {
86 | onDraftEditorCopy,
87 | onDraftEditorCut,
88 | handleDraftEditorPastedText,
89 | } from "draftjs-conductor";
90 |
91 | class MyEditor extends Component {
92 | constructor(props: Props) {
93 | super(props);
94 |
95 | this.state = {
96 | editorState: EditorState.createEmpty(),
97 | };
98 |
99 | this.onChange = this.onChange.bind(this);
100 | this.handlePastedText = this.handlePastedText.bind(this);
101 | }
102 |
103 | onChange(nextState: EditorState) {
104 | this.setState({ editorState: nextState });
105 | }
106 |
107 | handlePastedText(
108 | text: string,
109 | html: string | null,
110 | editorState: EditorState,
111 | ) {
112 | let newState = handleDraftEditorPastedText(html, editorState);
113 |
114 | if (newState) {
115 | this.onChange(newState);
116 | return true;
117 | }
118 |
119 | return false;
120 | }
121 |
122 | render() {
123 | const { editorState } = this.state;
124 |
125 | return (
126 | Hello, world! Hello, world! Hello, world! Hello, world! Hello, world! Hello, world! Hello, world!
);
192 | });
193 |
194 | it("IMAGE", () => {
195 | const rawContentState = {
196 | entityMap: {
197 | 1: {
198 | type: "IMAGE",
199 | mutability: "IMMUTABLE",
200 | data: {
201 | src: "example.png",
202 | },
203 | },
204 | },
205 | blocks: [
206 | {
207 | key: "ccc",
208 | type: "atomic",
209 | text: " ",
210 | depth: 0,
211 | entityRanges: [
212 | {
213 | key: 1,
214 | offset: 0,
215 | length: 1,
216 | },
217 | ],
218 | inlineStyleRanges: [],
219 | },
220 | ],
221 | } as RawDraftContentState;
222 |
223 | const instance = mount