Add/Remove link

This commit is contained in:
Cássio Zen 2016-08-06 18:16:30 -03:00
parent 72b8e14f07
commit 5fa551dcb2
4 changed files with 66 additions and 4 deletions

View File

@ -78,6 +78,6 @@
"react-portal": "^2.2.1", "react-portal": "^2.2.1",
"selection-position": "^1.0.0", "selection-position": "^1.0.0",
"slate": "^0.11.0", "slate": "^0.11.0",
"slate-markdown-serializer": "^0.1.3" "slate-markdown-serializer": "^0.1.5"
} }
} }

View File

@ -38,6 +38,7 @@ class MarkdownControl extends React.Component {
this.handleDocumentChange = this.handleDocumentChange.bind(this); this.handleDocumentChange = this.handleDocumentChange.bind(this);
this.handleMarkStyleClick = this.handleMarkStyleClick.bind(this); this.handleMarkStyleClick = this.handleMarkStyleClick.bind(this);
this.handleBlockStyleClick = this.handleBlockStyleClick.bind(this); this.handleBlockStyleClick = this.handleBlockStyleClick.bind(this);
this.handleInlineClick = this.handleInlineClick.bind(this);
this.handleBlockTypeClick = this.handleBlockTypeClick.bind(this); this.handleBlockTypeClick = this.handleBlockTypeClick.bind(this);
this.handleKeyDown = this.handleKeyDown.bind(this); this.handleKeyDown = this.handleKeyDown.bind(this);
this.calculateMenuPositions = this.calculateMenuPositions.bind(this); this.calculateMenuPositions = this.calculateMenuPositions.bind(this);
@ -150,6 +151,42 @@ class MarkdownControl extends React.Component {
this.setState({ state }); this.setState({ state });
} }
/**
* When clicking a link, if the selection has a link in it, remove the link.
* Otherwise, add a new link with an href and text.
*
* @param {Event} e
*/
handleInlineClick(type, isActive) {
let { state } = this.state;
if (type === 'link') {
if (!state.isExpanded) return;
if (isActive) {
state = state
.transform()
.unwrapInline('link')
.apply();
}
else {
const href = window.prompt('Enter the URL of the link:', 'http://www.');
state = state
.transform()
.wrapInline({
type: 'link',
data: { href }
})
.collapseToEnd()
.apply();
}
}
this.setState({ state });
}
handleBlockTypeClick(type) { handleBlockTypeClick(type) {
let { state } = this.state; let { state } = this.state;
@ -208,7 +245,9 @@ class MarkdownControl extends React.Component {
position={this.menuPositions.stylesMenu} position={this.menuPositions.stylesMenu}
marks={this.state.state.marks} marks={this.state.state.marks}
blocks={this.state.state.blocks} blocks={this.state.state.blocks}
inlines={this.state.state.inlines}
onClickMark={this.handleMarkStyleClick} onClickMark={this.handleMarkStyleClick}
onClickInline={this.handleInlineClick}
onClickBlock={this.handleBlockStyleClick} onClickBlock={this.handleBlockStyleClick}
/> />
); );

View File

@ -16,8 +16,10 @@ export default class StylesMenu extends Component {
this.hasBlock = this.hasBlock.bind(this); this.hasBlock = this.hasBlock.bind(this);
this.renderMarkButton = this.renderMarkButton.bind(this); this.renderMarkButton = this.renderMarkButton.bind(this);
this.renderBlockButton = this.renderBlockButton.bind(this); this.renderBlockButton = this.renderBlockButton.bind(this);
this.renderLinkButton = this.renderLinkButton.bind(this);
this.updateMenuPosition = this.updateMenuPosition.bind(this); this.updateMenuPosition = this.updateMenuPosition.bind(this);
this.handleMarkClick = this.handleMarkClick.bind(this); this.handleMarkClick = this.handleMarkClick.bind(this);
this.handleInlineClick = this.handleInlineClick.bind(this);
this.handleBlockClick = this.handleBlockClick.bind(this); this.handleBlockClick = this.handleBlockClick.bind(this);
this.handleOpen = this.handleOpen.bind(this); this.handleOpen = this.handleOpen.bind(this);
} }
@ -54,6 +56,10 @@ export default class StylesMenu extends Component {
const { blocks } = this.props; const { blocks } = this.props;
return blocks.some(node => node.type == type); return blocks.some(node => node.type == type);
} }
hasLinks(type) {
const { inlines } = this.props;
return inlines.some(inline => inline.type == 'link');
}
handleMarkClick(e, type) { handleMarkClick(e, type) {
e.preventDefault(); e.preventDefault();
@ -70,6 +76,21 @@ export default class StylesMenu extends Component {
); );
} }
handleInlineClick(e, type, isActive) {
e.preventDefault();
this.props.onClickInline(type, isActive);
}
renderLinkButton() {
const isActive = this.hasLinks();
const onMouseDown = e => this.handleInlineClick(e, 'link', isActive);
return (
<span className={styles.button} onMouseDown={onMouseDown} data-active={isActive}>
<Icon type="link"/>
</span>
);
}
handleBlockClick(e, type) { handleBlockClick(e, type) {
e.preventDefault(); e.preventDefault();
const isActive = this.hasBlock(type); const isActive = this.hasBlock(type);
@ -103,6 +124,7 @@ export default class StylesMenu extends Component {
{this.renderMarkButton('bold', 'bold')} {this.renderMarkButton('bold', 'bold')}
{this.renderMarkButton('italic', 'italic')} {this.renderMarkButton('italic', 'italic')}
{this.renderMarkButton('code', 'code')} {this.renderMarkButton('code', 'code')}
{this.renderLinkButton()}
{this.renderBlockButton('heading1', 'h1')} {this.renderBlockButton('heading1', 'h1')}
{this.renderBlockButton('heading2', 'h2')} {this.renderBlockButton('heading2', 'h2')}
{this.renderBlockButton('block-quote', 'quote-left')} {this.renderBlockButton('block-quote', 'quote-left')}
@ -122,6 +144,8 @@ StylesMenu.propTypes = {
}), }),
marks: PropTypes.object.isRequired, marks: PropTypes.object.isRequired,
blocks: PropTypes.object.isRequired, blocks: PropTypes.object.isRequired,
inlines: PropTypes.object.isRequired,
onClickBlock: PropTypes.func.isRequired, onClickBlock: PropTypes.func.isRequired,
onClickMark: PropTypes.func.isRequired onClickMark: PropTypes.func.isRequired,
onClickInline: PropTypes.func.isRequired
}; };

View File

@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import Block from './Block'; import Block from './Block';
import BlockStatic from './BlockStatic'; import BlockStatic from './BlockStatic';
import { Icon } from '../../UI';
/* eslint react/prop-types: 0, react/no-multi-comp: 0 */ /* eslint react/prop-types: 0, react/no-multi-comp: 0 */
@ -24,7 +23,7 @@ export const NODES = {
'link': (props) => { 'link': (props) => {
const { data } = props.node; const { data } = props.node;
const href = data.get('href'); const href = data.get('href');
return <span><a {...props.attributes} href={href}>{props.children}</a><Icon type="link"/></span>; return <a {...props.attributes} href={href}>{props.children}</a>;
}, },
'image': (props) => { 'image': (props) => {
const { node } = props; const { node } = props;