diff --git a/app/components/Editor/components/Link.js b/app/components/Editor/components/Link.js
index 54302eb60..57a99feb6 100644
--- a/app/components/Editor/components/Link.js
+++ b/app/components/Editor/components/Link.js
@@ -43,7 +43,7 @@ export default function Link({
);
} else {
return (
-
+
{children}
);
diff --git a/app/components/Editor/components/Toolbar/Toolbar.js b/app/components/Editor/components/Toolbar/Toolbar.js
index 9e5e16b2f..5721ed8f9 100644
--- a/app/components/Editor/components/Toolbar/Toolbar.js
+++ b/app/components/Editor/components/Toolbar/Toolbar.js
@@ -4,17 +4,29 @@ import { observable } from 'mobx';
import { observer } from 'mobx-react';
import { Portal } from 'react-portal';
import { Editor, findDOMNode } from 'slate-react';
-import { Value } from 'slate';
+import { Node, Value } from 'slate';
import styled from 'styled-components';
import _ from 'lodash';
import FormattingToolbar from './components/FormattingToolbar';
import LinkToolbar from './components/LinkToolbar';
+function getLinkInSelection(value): any {
+ try {
+ const selectedLinks = value.document
+ .getInlinesAtRange(value.selection)
+ .filter(node => node.type === 'link');
+ if (selectedLinks.size) {
+ return selectedLinks.first();
+ }
+ } catch (err) {
+ // It's okay.
+ }
+}
+
@observer
export default class Toolbar extends Component {
@observable active: boolean = false;
- @observable focused: boolean = false;
- @observable link: ?React$Element;
+ @observable link: ?Node;
@observable top: string = '';
@observable left: string = '';
@@ -33,35 +45,24 @@ export default class Toolbar extends Component {
this.update();
};
- handleFocus = () => {
- this.focused = true;
+ hideLinkToolbar = () => {
+ this.link = undefined;
};
- handleBlur = () => {
- this.focused = false;
+ showLinkToolbar = (ev: SyntheticEvent) => {
+ ev.preventDefault();
+ ev.stopPropagation();
+
+ const link = getLinkInSelection(this.props.value);
+ this.link = link;
};
- get linkInSelection(): any {
- const { value } = this.props;
-
- try {
- const selectedLinks = value.document
- .getInlinesAtRange(value.selection)
- .filter(node => node.type === 'link');
- if (selectedLinks.size) {
- return selectedLinks.first();
- }
- } catch (err) {
- // It's okay.
- }
- }
-
update = () => {
const { value } = this.props;
- const link = this.linkInSelection;
+ const link = getLinkInSelection(value);
if (value.isBlurred || (value.isCollapsed && !link)) {
- if (this.active && !this.focused) {
+ if (this.active && !this.link) {
this.active = false;
this.link = undefined;
this.top = '';
@@ -78,17 +79,20 @@ export default class Toolbar extends Component {
if (value.startBlock.type.match(/code/)) return;
this.active = true;
- this.focused = !!link;
- this.link = link;
+ this.link = this.link || link;
const padding = 16;
const selection = window.getSelection();
- const range = selection.getRangeAt(0);
- const rect = link
- ? findDOMNode(link).getBoundingClientRect()
- : range.getBoundingClientRect();
+ let rect;
- if (rect.top === 0 && rect.left === 0) {
+ if (link) {
+ rect = findDOMNode(link).getBoundingClientRect();
+ } else if (selection.rangeCount > 0) {
+ const range = selection.getRangeAt(0);
+ rect = range.getBoundingClientRect();
+ }
+
+ if (!rect || (rect.top === 0 && rect.left === 0)) {
return;
}
@@ -117,11 +121,11 @@ export default class Toolbar extends Component {
) : (
)}
diff --git a/app/components/Editor/components/Toolbar/components/DocumentResult.js b/app/components/Editor/components/Toolbar/components/DocumentResult.js
index 68aa6f9af..6f0c83e0e 100644
--- a/app/components/Editor/components/Toolbar/components/DocumentResult.js
+++ b/app/components/Editor/components/Toolbar/components/DocumentResult.js
@@ -25,10 +25,12 @@ function DocumentResult({ document, ...rest }: Props) {
const ListItem = styled.a`
display: flex;
align-items: center;
- height: 24px;
- padding: 4px 8px 4px 0;
+ height: 28px;
+ padding: 6px 8px 6px 0;
color: ${color.white};
font-size: 15px;
+ overflow: hidden;
+ white-space: nowrap;
i {
visibility: hidden;
diff --git a/app/components/Editor/components/Toolbar/components/FormattingToolbar.js b/app/components/Editor/components/Toolbar/components/FormattingToolbar.js
index 77fd7839a..dcf21e074 100644
--- a/app/components/Editor/components/Toolbar/components/FormattingToolbar.js
+++ b/app/components/Editor/components/Toolbar/components/FormattingToolbar.js
@@ -14,7 +14,7 @@ import StrikethroughIcon from 'components/Icon/StrikethroughIcon';
class FormattingToolbar extends Component {
props: {
editor: Editor,
- onCreateLink: () => void,
+ onCreateLink: SyntheticEvent => void,
};
/**
@@ -48,15 +48,15 @@ class FormattingToolbar extends Component {
this.props.editor.change(change => change.setBlock(type));
};
- onCreateLink = (ev: SyntheticEvent) => {
+ handleCreateLink = (ev: SyntheticEvent) => {
ev.preventDefault();
ev.stopPropagation();
const data = { href: '' };
- this.props.editor.change(change =>
- change.wrapInline({ type: 'link', data })
- );
- this.props.onCreateLink();
+ this.props.editor.change(change => {
+ change.wrapInline({ type: 'link', data });
+ this.props.onCreateLink(ev);
+ });
};
renderMarkButton = (type: string, IconClass: Function) => {
@@ -93,7 +93,7 @@ class FormattingToolbar extends Component {
{this.renderBlockButton('heading1', Heading1Icon)}
{this.renderBlockButton('heading2', Heading2Icon)}
-
+
diff --git a/app/components/Editor/components/Toolbar/components/LinkToolbar.js b/app/components/Editor/components/Toolbar/components/LinkToolbar.js
index d3209e8cc..afe84be60 100644
--- a/app/components/Editor/components/Toolbar/components/LinkToolbar.js
+++ b/app/components/Editor/components/Toolbar/components/LinkToolbar.js
@@ -1,9 +1,10 @@
// @flow
import React, { Component } from 'react';
-import ReactDOM from 'react-dom';
+import { findDOMNode } from 'react-dom';
import { observable, action } from 'mobx';
import { observer, inject } from 'mobx-react';
import { withRouter } from 'react-router-dom';
+import { Node } from 'slate';
import { Editor } from 'slate-react';
import styled from 'styled-components';
import ArrowKeyNavigation from 'boundless-arrow-key-navigation';
@@ -19,12 +20,13 @@ import Flex from 'shared/components/Flex';
@keydown
@observer
class LinkToolbar extends Component {
+ wrapper: HTMLSpanElement;
input: HTMLElement;
firstDocument: HTMLElement;
props: {
editor: Editor,
- link: Object,
+ link: Node,
documents: DocumentsStore,
onBlur: () => void,
};
@@ -34,10 +36,35 @@ class LinkToolbar extends Component {
@observable resultIds: string[] = [];
@observable searchTerm: ?string = null;
- componentWillMount() {
+ componentDidMount() {
this.isEditing = !!this.props.link.data.get('href');
+ setImmediate(() =>
+ window.addEventListener('click', this.handleOutsideMouseClick)
+ );
}
+ componentWillUnmount() {
+ window.removeEventListener('click', this.handleOutsideMouseClick);
+ }
+
+ handleOutsideMouseClick = (ev: SyntheticMouseEvent) => {
+ const element = findDOMNode(this.wrapper);
+
+ if (
+ !element ||
+ (ev.target instanceof HTMLElement && element.contains(ev.target)) ||
+ (ev.button && ev.button !== 0)
+ ) {
+ return;
+ }
+
+ if (this.input.value) {
+ this.props.onBlur();
+ } else {
+ this.removeLink();
+ }
+ };
+
@action
search = async () => {
this.isFetching = true;
@@ -70,7 +97,7 @@ class LinkToolbar extends Component {
case 40: // down
ev.preventDefault();
if (this.firstDocument) {
- const element = ReactDOM.findDOMNode(this.firstDocument);
+ const element = findDOMNode(this.firstDocument);
if (element instanceof HTMLElement) element.focus();
}
break;
@@ -90,16 +117,6 @@ class LinkToolbar extends Component {
this.resultIds = [];
};
- onBlur = () => {
- if (!this.resultIds.length) {
- if (this.input.value) {
- this.props.onBlur();
- } else {
- this.removeLink();
- }
- }
- };
-
removeLink = () => {
this.save('');
};
@@ -110,13 +127,15 @@ class LinkToolbar extends Component {
};
save = (href: string) => {
+ const { editor, link } = this.props;
href = href.trim();
- this.props.editor.change(change => {
+ editor.change(change => {
if (href) {
change.setInline({ type: 'link', data: { href } });
- } else {
- change.unwrapInline('link');
+ } else if (link) {
+ change.unwrapInlineByKey(link.key);
}
+ change.deselect();
this.props.onBlur();
});
};
@@ -126,17 +145,17 @@ class LinkToolbar extends Component {
};
render() {
- const href = this.props.link.data.get('href');
+ const { link } = this.props;
+ const href = link && link.data.get('href');
const hasResults = this.resultIds.length > 0;
return (
-
+ (this.wrapper = ref)}>
(this.input = ref)}
defaultValue={href}
placeholder="Search or paste a linkā¦"
- onBlur={this.onBlur}
onKeyDown={this.onKeyDown}
onChange={this.onChange}
autoFocus