diff --git a/app/components/ScrollToAnchor.js b/app/components/ScrollToAnchor.js
new file mode 100644
index 000000000..349ab8762
--- /dev/null
+++ b/app/components/ScrollToAnchor.js
@@ -0,0 +1,23 @@
+// @flow
+import * as React from 'react';
+import { withRouter } from 'react-router-dom';
+
+class ScrollToAnchor extends React.Component<*> {
+ componentDidUpdate(prevProps) {
+ if (this.props.location.hash === prevProps.location.hash) return;
+ if (window.location.hash === '') return;
+
+ // Delay on timeout to ensure that the DOM is updated first
+ setImmediate(() => {
+ const id = window.location.hash.replace('#', '');
+ const element = document.getElementById(id);
+ if (element) element.scrollIntoView();
+ });
+ }
+
+ render() {
+ return this.props.children;
+ }
+}
+
+export default withRouter(ScrollToAnchor);
diff --git a/app/index.js b/app/index.js
index d0f085aae..8a92ce64c 100644
--- a/app/index.js
+++ b/app/index.js
@@ -33,6 +33,7 @@ import Error404 from 'scenes/Error404';
import ErrorBoundary from 'components/ErrorBoundary';
import ScrollToTop from 'components/ScrollToTop';
+import ScrollToAnchor from 'components/ScrollToAnchor';
import Layout from 'components/Layout';
import Auth from 'components/Auth';
import RouteSidebarHidden from 'components/RouteSidebarHidden';
@@ -62,84 +63,94 @@ if (element) {
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/scenes/Document/Document.js b/app/scenes/Document/Document.js
index f1cddbc35..839595122 100644
--- a/app/scenes/Document/Document.js
+++ b/app/scenes/Document/Document.js
@@ -231,6 +231,12 @@ class DocumentScene extends React.Component {
};
onClickLink = (href: string) => {
+ // on page hash
+ if (href[0] === '#') {
+ window.location.href = href;
+ return;
+ }
+
if (isInternalUrl(href)) {
// relative
let navigateTo = href;
@@ -239,7 +245,7 @@ class DocumentScene extends React.Component {
if (href[0] !== '/') {
try {
const url = new URL(href);
- navigateTo = url.pathname;
+ navigateTo = url.pathname + url.hash;
} catch (err) {
navigateTo = href;
}
@@ -251,6 +257,10 @@ class DocumentScene extends React.Component {
}
};
+ onShowToast = (message: string) => {
+ this.props.ui.showToast(message, 'success');
+ };
+
render() {
const { location, match } = this.props;
const Editor = this.editorComponent;
@@ -328,6 +338,7 @@ class DocumentScene extends React.Component {
onChange={this.onChange}
onSave={this.onSave}
onCancel={this.onDiscard}
+ onShowToast={this.onShowToast}
readOnly={!this.isEditing}
toc
/>
diff --git a/package.json b/package.json
index 83f1c8efe..c3689af4d 100644
--- a/package.json
+++ b/package.json
@@ -158,7 +158,7 @@
"react-waypoint": "^7.3.1",
"redis": "^2.6.2",
"redis-lock": "^0.1.0",
- "rich-markdown-editor": "2.0.11",
+ "rich-markdown-editor": "3.1.1",
"safestart": "1.1.0",
"sequelize": "4.28.6",
"sequelize-cli": "^2.7.0",
diff --git a/yarn.lock b/yarn.lock
index 5c1f403ec..1036fee73 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -8858,9 +8858,9 @@ retry-axios@0.3.2, retry-axios@^0.3.2:
version "0.3.2"
resolved "https://registry.yarnpkg.com/retry-axios/-/retry-axios-0.3.2.tgz#5757c80f585b4cc4c4986aa2ffd47a60c6d35e13"
-rich-markdown-editor@2.0.11:
- version "2.0.11"
- resolved "https://registry.yarnpkg.com/rich-markdown-editor/-/rich-markdown-editor-2.0.11.tgz#209fd57ad8f61244e09cbe167ba64fc2c3b37da6"
+rich-markdown-editor@3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/rich-markdown-editor/-/rich-markdown-editor-3.1.1.tgz#ee97b3ca87836c960de0912b852df32c8054873d"
dependencies:
"@tommoor/slate-drop-or-paste-images" "^0.8.1"
babel-plugin-transform-async-to-generator "^6.24.1"