Skip to content
This repository was archived by the owner on Oct 11, 2022. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,58 @@ A [DraftJS] plugin for supporting Markdown syntax shortcuts in DraftJS. This plu
npm i --save draft-js-markdown-plugin
```

## Options
The `draft-js-markdown-plugin` is configurable. Just pass a config object. Here are the available options:


### `renderLanguageSelect`

```js
renderLanguageSelect = ({
// Array of language options
options: Array<{ label, value }>,
// Callback to select an option
onChange: (selectedValue: string) => void,
// Value of selected option
selectedValue: string,
// Label of selected option
selectedLabel: string
}) => React.Node
```

Code blocks render a select to switch syntax highlighting - `renderLanguageSelect` is a render function that lets you override how this is rendered.

#### Example:

```
import createMarkdownPlugin from 'draft-js-markdown-plugin';

const renderLanguageSelect = ({ options, onChange, selectedValue }) => (
<select value={selectedValue} onChange={onChange}>
{options.map(({ label, value }) => (
<option key={value} value={value}>
{label}
</option>
))}
</select>
);

const markdownPlugin = createMarkdownPlugin({ renderLanguageSelect })
```

### `languages`
Dictionary for languages available to code block switcher

#### Example:

```js
const languages = {
js: 'JavaScript'
}

const markdownPlugin = createMarkdownPlugin({ languages })
```

## Usage

```js
Expand Down
37 changes: 36 additions & 1 deletion demo/client/components/DemoEditor/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,42 @@ const prismPlugin = createPrismPlugin({
prism: Prism,
});

const plugins = [prismPlugin, createMarkdownShortcutsPlugin()];
const renderLanguageSelect = ({
options,
onChange,
selectedValue,
selectedLabel,
}) => (
<div className={styles.switcherContainer}>
<div className={styles.switcher}>
<select
className={styles.switcherSelect}
value={selectedValue}
onChange={onChange}
>
{options.map(({ label, value }) => (
<option key={value} value={value}>
{label}
</option>
))}
</select>
<div className={styles.switcherLabel}>
{selectedLabel} {String.fromCharCode(9662)}
</div>
</div>
</div>
);

const languages = {
js: "JavaScript",
kotlin: "Kotlin",
mathml: "MathML",
};

const plugins = [
prismPlugin,
createMarkdownShortcutsPlugin({ renderLanguageSelect }),
];

const initialEditorState = EditorState.createWithContent(
convertFromRaw(initialState)
Expand Down
34 changes: 34 additions & 0 deletions demo/client/components/DemoEditor/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,37 @@
height: 100%;
width: 100%;
}

pre {
background: #f9f9f9;
padding: 0.2em 0.5em;
position: relative;
}

.switcherContainer {
position: absolute;
text-align: right;
bottom: -23px;
right: 3px;
}

.switcher {
display: inline-block;
font-family: sans-serif;
color: #ccc;
letter-spacing: 0.05em;
font-size: 12px;
padding: 0.3em;
cursor: pointer;
position: relative;
}

.switcherSelect {
position: absolute;
top: 0;
cursor: pointer;
opacity: 0;
left: 0;
width: 100%;
height: 100%;
}
16 changes: 9 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"clean": "node_modules/.bin/rimraf lib; node_modules/.bin/rimraf demo/public",
"dev": "node_modules/.bin/babel-node ./demo/server.js",
"test": "jest",
"precommit": "lint-staged"
"precommit": "jest && lint-staged"
},
"repository": {
"type": "git",
Expand Down Expand Up @@ -65,6 +65,7 @@
"flow-bin": "^0.36.0",
"history": "^2.0.0",
"husky": "^0.14.3",
"jest": "^21.1.0",
"jsdom": "^9.8.3",
"lint-staged": "^4.2.1",
"mocha": "^3.2.0",
Expand All @@ -81,30 +82,31 @@
"react-dom": "^15.4.1",
"react-github-corner": "^2.0.0",
"react-github-fork-ribbon": "^0.4.4",
"react-test-renderer": "^16.2.0",
"rimraf": "^2.5.4",
"sinon": "^1.17.6",
"static-site-generator-webpack-plugin": "^3.1.0",
"style-loader": "^0.13.1",
"url-loader": "^0.5.7",
"webpack": "^1.13.3",
"webpack-dev-middleware": "^1.8.4",
"webpack-hot-middleware": "^2.13.2",
"jest": "^21.1.0",
"react-test-renderer": "^16.2.0"
"webpack-hot-middleware": "^2.13.2"
},
"peerDependencies": {
"draft-js-plugins-editor": "~2.0.0-rc.1 || 2.0.0-rc2 || 2.0.0-rc1 || 2.0.0-beta12",
"react": "^15.0.0",
"react-dom": "^15.0.0"
"react-dom": "^15.0.0",
"react-portal": "^4.1.4"
},
"contributors": [
"Atsushi Nagase <[email protected]>"
],
"dependencies": {
"decorate-component-with-props": "^1.0.2",
"draft-js": "~0.10.1",
"draft-js": "^0.10.4",
"draft-js-checkable-list-item": "^2.0.5",
"draft-js-prism-plugin": "^0.1.1",
"immutable": "~3.7.4"
"immutable": "~3.7.4",
"react-click-outside": "^3.0.1"
}
}
131 changes: 131 additions & 0 deletions src/components/Code/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import React, { PureComponent } from "react";
import { Map } from "immutable";
import { EditorState, EditorBlock, Modifier } from "draft-js";
import enhanceWithClickOutside from "react-click-outside";

const alias = {
javascript: "js",
jsx: "js",
};

const CodeSwitchContainer = enhanceWithClickOutside(
class SwitchContainer extends PureComponent {
handleClickOutside() {
this.props.onClickOutside();
}

render() {
return (
<div contentEditable={false} onClick={this.props.onClick}>
{this.props.children}
</div>
);
}
}
);

class CodeBlock extends PureComponent {
state = {
isOpen: false,
};

onChange = ev => {
ev.preventDefault();
ev.stopPropagation();
const blockKey = this.props.block.getKey();
const {
getEditorState,
setReadOnly,
setEditorState,
} = this.props.blockProps;

const editorState = getEditorState();
const selection = editorState.getSelection();
const language = ev.currentTarget.value;
const blockSelection = selection.merge({
anchorKey: blockKey,
focusKey: blockKey,
});

let content = editorState.getCurrentContent();
content = Modifier.mergeBlockData(
content,
blockSelection,
Map({ language })
);
setReadOnly(false);

const newEditorState = EditorState.push(
editorState,
content,
"change-block-data"
);

setEditorState(EditorState.forceSelection(newEditorState, selection));
};

cancelClicks = event => event.preventDefault();

onSelectClick = event => {
const { setReadOnly } = this.props.blockProps;
event.stopPropagation();
setReadOnly(true);
};

onClickOutside = () => {
const {
getEditorState,
setReadOnly,
setEditorState,
} = this.props.blockProps;

setReadOnly(false);

const editorState = getEditorState();
const selection = editorState.getSelection();

setEditorState(EditorState.forceSelection(editorState, selection));
};

render() {
const {
languages,
renderLanguageSelect,
language: _language,
} = this.props.blockProps;

const language = alias[_language] || _language;
const selectedLabel = languages[language];
const selectedValue = language;

const options = Object.keys(languages).reduce(
(acc, val) => [
...acc,
{
label: languages[val],
value: val,
},
],
[]
);

return (
<div>
<EditorBlock {...this.props} />
<CodeSwitchContainer
onClickOutside={this.onClickOutside}
onClick={this.onSelectClick}
>
{renderLanguageSelect({
selectedLabel,
selectedValue,
onChange: this.onChange,
options,
})}
</CodeSwitchContainer>
</div>
);
}
}

export default CodeBlock;
2 changes: 2 additions & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ export const inlineMatchers = {
CODE: [/`([^`]+)`$/g],
STRIKETHROUGH: [/~~([^(?:~~)]+)~~$/g],
};

export const CODE_BLOCK_TYPE = "code-block";
4 changes: 2 additions & 2 deletions src/decorators/image/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import createImageStrategy from "./imageStrategy";
import Image from "../../components/Image";

const createImageDecorator = (config, store) => ({
strategy: createImageStrategy(config, store),
const createImageDecorator = () => ({
strategy: createImageStrategy(),
component: Image,
});

Expand Down
4 changes: 2 additions & 2 deletions src/decorators/link/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import createLinkStrategy from "./linkStrategy";
import Link from "../../components/Link";

const createLinkDecorator = (config, store) => ({
strategy: createLinkStrategy(config, store),
const createLinkDecorator = () => ({
strategy: createLinkStrategy(),
component: Link,
});

Expand Down
Loading