Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import React, { Component } from 'react';
import Editor, { createEditorStateWithText } from 'draft-js-plugins-editor';
// eslint-disable-next-line import/no-unresolved
import createInlineToolbarPlugin, { Separator } from 'draft-js-inline-toolbar-plugin';
// eslint-disable-next-line import/no-unresolved
import createLinkifyPlugin from 'draft-js-linkify-plugin'; // eslint-disable-line import/no-unresolved
import {
ItalicButton,
BoldButton,
Expand All @@ -15,9 +17,17 @@ import {
OrderedListButton,
BlockquoteButton,
CodeBlockButton,
} from 'draft-js-buttons'; // eslint-disable-line import/no-unresolved
AddLinkButton,
} from '../../../../../../draft-js-buttons/src/'; // eslint-disable-line import/no-unresolved
import editorStyles from './editorStyles.css';

let linkAddElement = null;
let inlineToolbarElement = null;

const addLink = () => {
linkAddElement.openPopover();
};

const inlineToolbarPlugin = createInlineToolbarPlugin({
structure: [
BoldButton,
Expand All @@ -32,10 +42,14 @@ const inlineToolbarPlugin = createInlineToolbarPlugin({
OrderedListButton,
BlockquoteButton,
CodeBlockButton,
]
AddLinkButton,
],
addLink,
});
const { InlineToolbar } = inlineToolbarPlugin;
const plugins = [inlineToolbarPlugin];
const linkifyPlugin = createLinkifyPlugin();
const { LinkAdd } = linkifyPlugin;
const plugins = [inlineToolbarPlugin, linkifyPlugin];
const text = 'In this editor a toolbar shows up once you select part of the text …';

export default class CustomInlineToolbarEditor extends Component {
Expand All @@ -56,14 +70,24 @@ export default class CustomInlineToolbarEditor extends Component {

render() {
return (
<div className={editorStyles.editor} onClick={this.focus}>
<Editor
<div>
<div className={editorStyles.editor} onClick={this.focus}>
<Editor
editorState={this.state.editorState}
onChange={this.onChange}
plugins={plugins}
ref={(element) => { this.editor = element; }}
/>
<InlineToolbar
ref={(element) => { inlineToolbarElement = element; }}
/>
</div>
<LinkAdd
ref={(element) => { linkAddElement = element; }}
editorState={this.state.editorState}
onChange={this.onChange}
plugins={plugins}
ref={(element) => { this.editor = element; }}
inlineToolbarElement={inlineToolbarElement}
/>
<InlineToolbar />
</div>
);
}
Expand Down
8 changes: 8 additions & 0 deletions draft-js-buttons/src/components/AddLinkButton/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import React from 'react';
import createLinkButton from '../../utils/createLinkButton';

export default createLinkButton({
children: (
<span>🔗</span>
),
});
2 changes: 2 additions & 0 deletions draft-js-buttons/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import AlignBlockCenterButton from './components/AlignBlockCenterButton';
import AlignBlockLeftButton from './components/AlignBlockLeftButton';
import AlignBlockRightButton from './components/AlignBlockRightButton';
import AddImageButton from './components/AddImageButton';
import AddLinkButton from './components/AddLinkButton';

export {
createBlockStyleButton,
Expand All @@ -36,4 +37,5 @@ export {
AlignBlockLeftButton,
AlignBlockRightButton,
AddImageButton,
AddLinkButton,
};
32 changes: 32 additions & 0 deletions draft-js-buttons/src/utils/createLinkButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React, { Component } from 'react';

export default ({ children }) => (
class linkButton extends Component {

activate = (event) => {
event.preventDefault();
event.stopPropagation();
this.props.addLink();
}

preventBubblingUp = (event) => { event.preventDefault(); }

render() {
const { theme } = this.props;
return (
<div
className={theme.buttonWrapper}
onMouseDown={this.preventBubblingUp}
>
<button
className={theme.button}
onClick={this.activate}
type="button"
children={children}
/>
</div>

);
}
}
);
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export default class Toolbar extends React.Component {
theme={theme.buttonStyles}
getEditorState={store.getItem('getEditorState')}
setEditorState={store.getItem('setEditorState')}
addLink={store.getItem('addLink')}
/>
))}
</div>
Expand Down
10 changes: 7 additions & 3 deletions draft-js-inline-toolbar-plugin/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,11 @@ import toolbarStyles from './toolbarStyles.css';
const createInlineToolbarPlugin = (config = {}) => {
const defaultTheme = { buttonStyles, toolbarStyles };

const store = createStore({
isVisible: false,
});
const defaultaddLink = undefined;

const {
theme = defaultTheme,
addLink = defaultaddLink,
structure = [
BoldButton,
ItalicButton,
Expand All @@ -28,6 +27,11 @@ const createInlineToolbarPlugin = (config = {}) => {
]
} = config;

const store = createStore({
isVisible: false,
addLink
});

const toolbarProps = {
store,
structure,
Expand Down
130 changes: 130 additions & 0 deletions draft-js-linkify-plugin/src/LinkAdd/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import linkifyIt from 'linkify-it';
import modifier from '../modifiers/addLink';
import styles from './styles.css';

const linkify = linkifyIt();

export default class LinkAdd extends Component {
// Start the popover closed
state = {
url: '',
open: false,
linkError: false
};

// When the popover is open and users click anywhere on the page,
// the popover should close
componentDidMount() {
document.addEventListener('click', this.closePopover);
}

componentWillUnmount() {
document.removeEventListener('click', this.closePopover);
}

onPopoverClick = () => {
this.preventNextClose = true;
}

onKeyDown(e) {
if (e.keyCode === 13) {
e.preventDefault();
e.stopPropagation();
this.addLink();
}
}

setPosition = (toolbarElement) => {
const position = {
top: toolbarElement.offsetTop,
left: toolbarElement.offsetLeft,
width: toolbarElement.offsetWidth,
transform: 'translate(-50%) scale(1)',
transition: 'transform 0.15s cubic-bezier(.3,1.2,.2,1)',
};
this.setState({ position });
}

openPopover = () => {
if (!this.state.open) {
this.preventNextClose = true;
// eslint-disable-next-line react/no-find-dom-node
const toolbarElement = ReactDOM.findDOMNode(this.props.inlineToolbarElement);
this.setPosition(toolbarElement);
setTimeout(() => {
setTimeout(() => this.inputElement.focus(), 0);
this.setState({ open: true, });
}, 0);
}
};

closePopover = () => {
if (!this.preventNextClose && this.state.open) {
this.setState({ open: false });
}

this.preventNextClose = false;
};

addLink = () => {
const { editorState, onChange } = this.props;
const { url } = this.state;
if (linkify.test(url)) {
this.setState({ linkError: false });
onChange(modifier(editorState, url));
this.closePopover();
} else {
this.setState({ linkError: true });
}
};

changeUrl = (evt) => {
this.setState({ url: evt.target.value });
}

render() {
const popoverClassName = this.state.open ?
styles.addLinkPopover :
styles.addLinkClosedPopover;

const inputClassName = this.state.linkError ?
`${styles.addLinkInput} ${styles.addLinkInputError}` :
styles.addLinkInput;

return (
<div className={styles.addLink}>
<div
className={popoverClassName}
onClick={this.onPopoverClick}
style={this.state.position}
>
<input
ref={(element) => { this.inputElement = element; }}
type="text"
placeholder="Paste the link url …"
className={inputClassName}
onChange={this.changeUrl}
onKeyDown={(e) => this.onKeyDown(e)}
value={this.state.url}
/>
<button
className={styles.addLinkConfirmButton}
type="button"
onClick={this.addLink}
>
+
</button>
<button
className={styles.addLinkConfirmButton}
type="button"
onClick={this.closePopover}
>
x
</button>
</div>
</div>
);
}
}
Loading