diff --git a/frontend/src/components/markdown-view/comments-list.js b/frontend/src/components/markdown-view/comments-list.js
deleted file mode 100644
index 0531ea6d7e..0000000000
--- a/frontend/src/components/markdown-view/comments-list.js
+++ /dev/null
@@ -1,305 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { processor } from '@seafile/seafile-editor/dist/utils/seafile-markdown2html';
-import { Button, Dropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap';
-import Loading from '../loading';
-import { gettext } from '../../utils/constants';
-import moment from 'moment';
-import '../../css/markdown-viewer/comments-list.css';
-
-const propTypes = {
- editorUtilities: PropTypes.object.isRequired,
- scrollToQuote: PropTypes.func.isRequired,
- getCommentsNumber: PropTypes.func.isRequired,
- commentsNumber: PropTypes.number.isRequired,
-};
-
-class CommentsList extends React.Component {
-
- constructor(props) {
- super(props);
- this.state = {
- commentsList: [],
- showResolvedComment: true,
- };
- }
-
- listComments = () => {
- this.props.editorUtilities.listComments().then((response) => {
- this.setState({
- commentsList: response.data.comments
- });
- });
- }
-
- handleCommentChange = (event) => {
- this.setState({
- comment: event.target.value,
- });
- }
-
- submitComment = () => {
- let comment = this.refs.commentTextarea.value;
- if (comment.trim().length > 0) {
- this.props.editorUtilities.postComment(comment.trim()).then((response) => {
- this.listComments();
- this.props.getCommentsNumber();
- });
- }
- this.refs.commentTextarea.value = '';
- }
-
- resolveComment = (event) => {
- this.props.editorUtilities.updateComment(event.target.id, 'true').then((response) => {
- this.listComments();
- });
- }
-
- deleteComment = (event) => {
- this.props.editorUtilities.deleteComment(event.target.id).then((response) => {
- this.props.getCommentsNumber();
- this.listComments();
- });
- }
-
- editComment = (commentID, newComment) => {
- this.props.editorUtilities.updateComment(commentID, null, null, newComment).then((res) => {
- this.props.getCommentsNumber();
- this.listComments();
- });
- }
-
- setQuoteText = (text) => {
- if (text.length > 0) {
- this.refs.commentTextarea.value = '> ' + text;
- }
- }
-
- scrollToQuote = (detail) => {
- this.props.scrollToQuote(detail);
- this.refs.commentTextarea.value = '';
- }
-
- toggleResolvedComment = () => {
- this.setState({
- showResolvedComment: !this.state.showResolvedComment
- });
- }
-
- componentWillMount() {
- this.listComments();
- }
-
- componentWillReceiveProps(nextProps) {
- if (this.props.commentsNumber !== nextProps.commentsNumber) {
- this.listComments();
- }
- }
-
- render() {
- return (
-
-
-
{gettext('Show resolved comments')}
-
-
-
-
-
- { (this.state.commentsList.length > 0 && this.props.commentsNumber > 0) &&
- this.state.commentsList.map((item, index = 0, arr) => {
- let oldTime = (new Date(item.created_at)).getTime();
- let time = moment(oldTime).format('YYYY-MM-DD HH:mm');
- return (
-
- );
- })
- }
- {(this.state.commentsList.length == 0 && this.props.commentsNumber > 0) && }
- { this.props.commentsNumber == 0 &&
- - {gettext('No comment yet.')}
- }
-
-
-
-
-
-
-
-
- );
- }
-}
-
-CommentsList.propTypes = propTypes;
-
-const CommentItempropTypes = {
- editorUtilities: PropTypes.object.isRequired,
- item: PropTypes.object,
- time: PropTypes.string,
- editComment: PropTypes.func,
- showResolvedComment: PropTypes.bool,
- deleteComment: PropTypes.func,
- resolveComment: PropTypes.func,
- commentsList: PropTypes.array,
- scrollToQuote: PropTypes.func.isRequired,
-};
-
-class CommentItem extends React.Component {
-
- constructor(props) {
- super(props);
- this.state = {
- dropdownOpen: false,
- html: '',
- quote: '',
- newComment: this.props.item.comment,
- editable: false,
- };
- }
-
- toggleDropDownMenu = () => {
- this.setState({
- dropdownOpen: !this.state.dropdownOpen,
- });
- }
-
- convertComment = (item) => {
- processor.process(item.comment).then(
- (result) => {
- let comment = String(result);
- this.setState({
- comment: comment
- });
- }
- );
- if (item.detail) {
- const quote = JSON.parse(item.detail).quote;
- processor.process(quote).then(
- (result) => {
- let quote = String(result);
- this.setState({
- quote: quote
- });
- }
- );
- }
- }
-
- toggleEditComment = () => {
- this.setState({
- editable: !this.state.editable
- });
- }
-
- updateComment = (event) => {
- const newComment = this.state.newComment;
- if (this.props.item.comment !== newComment) {
- this.props.editComment(event.target.id, newComment);
- }
- this.toggleEditComment();
- }
-
- handleCommentChange = (event) => {
- this.setState({
- newComment: event.target.value,
- });
- }
-
- scrollToQuote = () => {
- const position = JSON.parse(this.props.item.detail).position;
- this.props.scrollToQuote(position);
- }
-
- componentWillMount() {
- this.convertComment(this.props.item);
- }
-
- componentWillReceiveProps(nextProps) {
- this.convertComment(nextProps.item);
- }
-
- render() {
- const item = this.props.item;
- const { id, user_email, avatar_url, user_name, resolved } = item;
- if (item.resolved && !this.props.showResolvedComment) {
- return null;
- }
- if (this.state.editable) {
- return(
-
-
-

-
-
{user_name}
-
{this.props.time}
-
-
-
-
- {' '}
-
-
-
- );
- }
- return (
-
-
-

-
-
{user_name}
-
{this.props.time}
-
-
-
-
-
-
- {(user_email === this.props.editorUtilities.userName) &&
- {gettext('Delete')}
- }
- {(user_email === this.props.editorUtilities.userName) &&
- {gettext('Edit')}
- }
- {!resolved &&
- {gettext('Mark as resolved')}
- }
-
-
-
- {item.detail &&
-
-
-
- }
-
-
- );
- }
-}
-
-CommentItem.propTypes = CommentItempropTypes;
-
-export default CommentsList;
diff --git a/frontend/src/components/markdown-view/history-list.js b/frontend/src/components/markdown-view/history-list.js
index a38a7ffbf4..36066cfb46 100644
--- a/frontend/src/components/markdown-view/history-list.js
+++ b/frontend/src/components/markdown-view/history-list.js
@@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
import axios from 'axios';
import Loading from '../loading';
import moment from 'moment';
+import { gettext } from '../../utils/constants';
import '../../css/markdown-viewer/history-viewer.css';
const propTypes = {
@@ -97,6 +98,12 @@ class HistoryList extends React.Component {
render() {
return (
+
+
+
+
+
{gettext('History Versions')}
+
{this.state.historyList ?
this.state.historyList.map((item, index = 0, arr) => {
diff --git a/frontend/src/components/markdown-view/markdown-viewer-side-panel.js b/frontend/src/components/markdown-view/markdown-viewer-side-panel.js
deleted file mode 100644
index 03c24fde29..0000000000
--- a/frontend/src/components/markdown-view/markdown-viewer-side-panel.js
+++ /dev/null
@@ -1,164 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import classnames from 'classnames';
-import { Nav, NavItem, NavLink, TabContent, TabPane } from 'reactstrap';
-import HistoryList from './history-list';
-import CommentsList from './comments-list';
-import OutlineView from './outline';
-
-const URL = require('url-parse');
-
-const propTypes = {
- editorUtilities: PropTypes.object.isRequired,
- markdownContent: PropTypes.string.isRequired,
- commentsNumber: PropTypes.number.isRequired,
- viewer: PropTypes.object.isRequired,
- value: PropTypes.object.isRequired,
- activeTab: PropTypes.string.isRequired,
- showDiffViewer: PropTypes.func.isRequired,
- setDiffViewerContent: PropTypes.func.isRequired,
- reloadDiffContent: PropTypes.func.isRequired,
- tabItemClick: PropTypes.func.isRequired,
- getCommentsNumber: PropTypes.func.isRequired,
-};
-
-class MarkdownViewerSidePanel extends React.Component {
-
- constructor(props) {
- super(props);
- }
-
- tabItemClick = (tab) => {
- this.props.tabItemClick(tab);
- }
-
- showNavItem = (showTab) => {
- switch(showTab) {
- case 'outline':
- return (
- { this.tabItemClick('outline');}} >
-
- );
- case 'comments':
- return (
- {this.tabItemClick('comments');}}>
- {this.props.commentsNumber > 0 && {this.props.commentsNumber}
}
-
- );
- case 'history':
- return (
- { this.tabItemClick('history');}}>
-
- );
- }
- }
-
- renderNavItems = () => {
- return (
-
- );
- }
-
- scrollToNode = (node) => {
- let url = new URL(window.location.href);
- url.set('hash', 'user-content-' + node.text);
- window.location.href = url.toString();
- }
-
- findScrollContainer = (el, window) => {
- let parent = el.parentNode;
- const OVERFLOWS = ['auto', 'overlay', 'scroll'];
- let scroller;
- while (!scroller) {
- if (!parent.parentNode) break;
- const style = window.getComputedStyle(parent);
- const { overflowY } = style;
- if (OVERFLOWS.includes(overflowY)) {
- scroller = parent;
- break;
- }
- parent = parent.parentNode;
- }
- if (!scroller) {
- return window.document.body;
- }
- return scroller;
- }
-
- scrollToQuote = (path) => {
- if (!path) return;
- const win = window;
- if (path.length > 2) {
- // deal with code block or chart
- path[0] = path[0] > 1 ? path[0] - 1 : path[0] + 1;
- path = path.slice(0, 1);
- }
- let node = this.props.value.document.getNode(path);
- if (!node) {
- path = path.slice(0, 1);
- node = this.props.value.document.getNode(path);
- }
- if (node) {
- let element = win.document.querySelector(`[data-key="${node.key}"]`);
- while (element.tagName === 'CODE') {
- element = element.parentNode;
- }
- const scroller = this.findScrollContainer(element, win);
- const isWindow = scroller == win.document.body || scroller == win.document.documentElement;
- if (isWindow) {
- win.scrollTo(0, element.offsetTop);
- } else {
- scroller.scrollTop = element.offsetTop;
- }
- }
- }
-
- componentDidMount() {
- this.tabItemClick('outline');
- }
-
- render() {
- return (
-
- {this.renderNavItems()}
-
-
-
-
-
-
-
-
-
-
-
-
- );
- }
-}
-
-MarkdownViewerSidePanel.propTypes = propTypes;
-
-export default MarkdownViewerSidePanel;
diff --git a/frontend/src/components/markdown-view/outline.js b/frontend/src/components/markdown-view/outline.js
index f3a5b1aa81..c246742fbb 100644
--- a/frontend/src/components/markdown-view/outline.js
+++ b/frontend/src/components/markdown-view/outline.js
@@ -2,7 +2,6 @@ import React from 'react';
import PropTypes from 'prop-types';
import { gettext } from '../../utils/constants';
-
const OutlineItempropTypes = {
scrollToNode: PropTypes.func.isRequired,
node: PropTypes.object.isRequired,
@@ -41,7 +40,6 @@ const propTypes = {
scrollToNode: PropTypes.func.isRequired,
isViewer: PropTypes.bool.isRequired,
document: PropTypes.object.isRequired,
- editor: PropTypes.object.isRequired,
activeTitleIndex: PropTypes.number,
value: PropTypes.object,
};
@@ -56,13 +54,13 @@ class OutlineView extends React.PureComponent {
return (
+
{gettext('Contents')}
{headerList.size > 0 ?
headerList.map((node, index) => {
let active = (index === this.props.activeTitleIndex) ? ' active' : '';
return (
+ {
+ this.props.commentsNumber > 0 ?
+
+ :
+
diff --git a/frontend/src/css/comments-list.css b/frontend/src/css/comments-list.css
index b39d026f76..b5e90761c2 100644
--- a/frontend/src/css/comments-list.css
+++ b/frontend/src/css/comments-list.css
@@ -4,7 +4,8 @@
flex-direction: column;
width: 29%;
}
-.seafile-comment-title {
+.seafile-comment-title,
+.seafile-history-title {
border-bottom: 1px solid #e5e5e5;
min-height: 3em;
line-height: 3em;
@@ -12,15 +13,18 @@
display: flex;
background-color: #fafaf9;
}
-.seafile-comment-title .seafile-comment-title-text {
+.seafile-comment-title .seafile-comment-title-text,
+.seafile-history-title .seafile-history-title-text {
width: 100%;
text-align: center;
font-weight: 700;
}
-.seafile-comment-title .seafile-comment-title-close {
+.seafile-comment-title .seafile-comment-title-close,
+.seafile-history-title .seafile-history-title-close {
color: #b9b9b9;
}
-.seafile-comment-title .seafile-comment-title-close:hover {
+.seafile-comment-title .seafile-comment-title-close:hover,
+.seafile-history-title .seafile-history-title-close:hover {
color: #888;
}
.seafile-comment-toggle-resolved {
diff --git a/frontend/src/css/markdown-viewer/comments-list.css b/frontend/src/css/markdown-viewer/comments-list.css
deleted file mode 100644
index ef4843a3d3..0000000000
--- a/frontend/src/css/markdown-viewer/comments-list.css
+++ /dev/null
@@ -1,138 +0,0 @@
-.seafile-comment {
- background-color: #fff;
- display: flex;
- flex-direction: column;
- flex: 0 0 auto;
- min-height: 18.5em;
- z-index: 3;
- width: 380px;
-}
-.seafile-comment-title {
- border-bottom: 1px solid #e5e5e5;
- min-height: 2em;
- line-height: 2em;
- padding: 2px 1em;
- display: flex;
- flex-direction: row;
- justify-content: space-between;
- background-color: #fafaf9;
- position: absolute;
- z-index: 1;
- width: 30%;
-}
-.seafile-comment-title .seafile-comment-title-close {
- color: #b9b9b9;
-}
-.seafile-comment-title .seafile-comment-title-close:hover {
- color: #888;
-}
-.seafile-comment-list {
- height: calc(100% - 40px);
- overflow-y: auto;
- margin: 30px 0 120px;
-}
-.seafile-comment::-webkit-scrollbar {
- display: none;
-}
-.seafile-comment-list .comment-vacant {
- padding: 1em;
- text-align: center;
-}
-.seafile-comment-item {
- padding: 15px 10px;
- margin-bottom: 0;
-}
-.seafile-comment-item .seafile-comment-info {
- padding-bottom: 0.5em;
- height: 3em;
- display: flex;
- justify-content: flex-start;
-}
-.seafile-comment-item .seafile-comment-info .reviewer-info {
- padding-left: 10px;
-}
-.seafile-comment-item .seafile-comment-info .review-time {
- font-size: 10px;
- color: #777;
-}
-.seafile-comment-item .seafile-comment-info .seafile-comment-dropdown {
- margin-left: auto;
-}
-.seafile-comment-item .seafile-comment-info .seafile-comment-dropdown button {
- border: none;
- box-shadow: none;
- background-color: #fff;
-}
-.seafile-comment-item .seafile-comment-info .seafile-comment-dropdown .seafile-comment-dropdown-btn {
- color: #999;
- background-color: transparent;
-}
-.seafile-comment-item .seafile-comment-info .seafile-comment-dropdown:hover .seafile-comment-dropdown-btn {
- color: #555;
- background-color: transparent;
-}
-.seafile-comment-item .seafile-comment-info .seafile-comment-dropdown button:hover,
-.seafile-comment-item .seafile-comment-info .seafile-comment-dropdown button:focus {
- border: none;
- box-shadow: none;
- background-color: #eee;
-}
-.seafile-comment-item .seafile-comment-content {
- margin-left: 42px;
-}
-.seafile-comment-item .seafile-comment-content ol,
-.seafile-comment-item .seafile-comment-content ul,
-.seafile-comment-item .seafile-comment-content li {
- margin-left: 10px;
-}
-.seafile-comment-item .seafile-comment-content table,
-.seafile-comment-item .seafile-comment-content th,
-.seafile-comment-item .seafile-comment-content td {
- border: 1px solid #333;
-}
-.seafile-comment-item blockquote {
- cursor: pointer;
-}
-.seafile-comment-footer {
- background-color: #fafaf9;
- border-top: 1px solid #e5e5e5;
- min-height: 120px;
- position: absolute;
- bottom: 0;
- padding: 0;
- width: 30%;
-}
-.seafile-comment-footer .seafile-add-comment {
- margin: 10px 20px 5px 15px;
- height: 100%;
- width: 100%;
-}
-.seafile-add-comment .add-comment-input,
-.seafile-edit-comment .edit-comment-input {
- background-color: #fff;
- border: 1px solid #e6e6dd;
- padding: 5px;
- min-height: 70px;
- border-radius: 5px;
- width: 100%;
-}
-.seafile-add-comment .add-comment-input {
- height: calc(100% - 50px);
- width: 92%;
- max-height: 300px;
-}
-.seafile-comment-footer .seafile-add-comment .submit-comment {
- margin-top: 5px;
- width: 60px;
- height: 28px;
-}
-.seafile-comment-item-resolved {
- background-color: #e6ffed;
-}
-.seafile-comment-footer .seafile-add-comment .comment-btn,
-.seafile-edit-comment .comment-btn {
- height: 28px;
-}
-.seafile-edit-comment {
- margin-top: 10px;
-}
diff --git a/frontend/src/css/markdown-viewer/markdown-editor.css b/frontend/src/css/markdown-viewer/markdown-editor.css
index 1774e8beeb..788c235907 100644
--- a/frontend/src/css/markdown-viewer/markdown-editor.css
+++ b/frontend/src/css/markdown-viewer/markdown-editor.css
@@ -13,29 +13,52 @@
align-items: center;
}
.seafile-md-viewer-container {
- width: 70%;
+ width: 100%;
background-color: #fafaf9;
- overflow: hidden;
height: 100%;
-}
-.seafile-md-viewer-container:hover {
+ position: relative;
overflow: auto;
}
+.seafile-md-viewer-container.side-panel-on {
+ width: 70%;
+}
.seafile-md-viewer-slate {
flex: auto;
position: relative;
margin: 20px 40px;
+ margin-right: 30%;
}
.seafile-md-viewer-main {
- flex:auto;
- overflow:auto;
+ flex: auto;
+ overflow: auto;
background:#fafaf9;
width: 70%;
}
-.seafile-editor-outline .active {
+.seafile-md-viewer-slate.side-panel-on {
+ margin-right: 40px;
+}
+/* outline */
+.seafile-md-viewer .seafile-editor-outline {
+ background-color: #fafaf9;
+ margin: 40px;
+ border-left: 0;
+ width: 20%;
+ position: fixed;
+ top: 68px;
+ overflow-y: auto;
+ right: 5%;
+ z-index: 1;
+ height: 80%;
+}
+.seafile-md-viewer .seafile-editor-outline .active {
color: #eb8205;
border-left: 1px solid #eb8205;
}
+.seafile-md-viewer .seafile-editor-outline-heading {
+ padding: 7px 0;
+ border-bottom: 1px solid #eee;
+ color: #a0a0a0;
+}
.seafile-editor-outline .outline-h2, .seafile-editor-outline .outline-h3 {
height: 30px;
margin-left: 0;
@@ -52,71 +75,26 @@
}
/* side-panel */
.seafile-md-viewer-side-panel {
- height: 100%;
- overflow:hidden;
- user-select: none;
- width: 30%;
-}
-.seafile-md-viewer-side-panel .seafile-editor-outline {
- border-left: 0;
-}
-.seafile-md-viewer-side-panel:hover {
- overflow: auto;
-}
-.seafile-md-viewer-side-panel-heading {
- padding:7px 0;
- border-bottom: 1px solid #eee;
- color: #a0a0a0;
-}
-.seafile-md-viewer-side-panel-content {
- padding:8px 0;
- font-size: 0.875rem;
-}
-
-.seafile-md-viewer-side-panel {
- border-left: 1px solid #e6e6dd;
- background-color: #fff;
height: 100%;
overflow: hidden;
+ width: 30%;
+ position: fixed;
+ right: 0;
+ top: 87px;
}
-.seafile-md-viewer-side-panel .tab-content {
- height: calc(100% - 39px);
- overflow-y: auto;
- overflow-x: hidden;
+.seafile-md-viewer-side-panel .seafile-comment,
+.seafile-md-viewer-side-panel .seafile-history-side-panel {
+ width: 100%;
+ height: 100%;
}
-.seafile-md-viewer-side-panel .tab-content .outline {
- padding: 20px;
+.seafile-md-viewer-side-panel .seafile-comment .add-comment-input,
+.seafile-md-viewer-side-panel .seafile-comment .edit-comment-input {
+ background-color: #fff;
+ width: 100%;
}
-.seafile-md-viewer-side-panel .md-side-panel-nav {
- margin: 0;
+.seafile-md-viewer-side-panel .seafile-history-side-panel {
+ border-left: 1px solid #e6e6dd;
}
-.md-side-panel-nav .nav-item {
- width: 33.3%;
- padding-top: 4px;
-}
-.md-side-panel-nav .nav-item .nav-link {
- margin: 0 auto;
-}
-.md-side-panel-nav .nav-item i {
- padding: 0 8px;
- font-size: 1rem;
- width: 1rem;
-}
-.comments-number {
- font-size: 12px;
- width: 16px;
- height: 16px;
- border-radius: 8px;
- text-align: center;
- line-height: 16px;
- font-weight: 600;
- background-color: #fd9644;
- position: absolute;
- top: 10%;
- right: 30%;
- color: #fff;
-}
-
.seafile-viewer-comment-btn {
position: absolute;
top: 0;
@@ -131,11 +109,13 @@
cursor: pointer;
background-color: #eee;
}
-
+.seafile-md-viewer .seafile-comment .seafile-comment-footer {
+ min-height: 230px;
+}
+.seafile-md-viewer .seafile-comment-toggle-resolved {
+ width: 100%;
+}
@media (max-width:991.8px) {
- .seafile-md-viewer-side-panel {
- display:none;
- }
.seafile-editor-outline {
display: none;
}
@@ -143,9 +123,4 @@
width: calc(100% - 80px);
margin: 20px 40px;
}
-}
-@media (min-width:992px) {
- .seafile-md-viewer-side-panel {
- width: 30%;
- }
}
\ No newline at end of file
diff --git a/frontend/src/markdown-editor.js b/frontend/src/markdown-editor.js
index ec304bb465..d057de4150 100644
--- a/frontend/src/markdown-editor.js
+++ b/frontend/src/markdown-editor.js
@@ -18,13 +18,16 @@ import { serialize, deserialize } from '@seafile/seafile-editor/dist/utils/slate
import LocalDraftDialog from '@seafile/seafile-editor/dist/components/local-draft-dialog';
import DiffViewer from '@seafile/seafile-editor/dist/viewer/diff-viewer';
import MarkdownViewerToolbar from './components/toolbar/markdown-viewer-toolbar';
-import MarkdownViewerSidePanel from './components/markdown-view/markdown-viewer-side-panel';
+import HistoryList from './components/markdown-view/history-list';
+import CommentPanel from './components/file-view/comment-panel';
+import OutlineView from './components/markdown-view/outline';
import Loading from './components/loading';
import { findRange } from '@seafile/slate-react';
import './css/markdown-viewer/markdown-editor.css';
const CryptoJS = require('crypto-js');
+const URL = require('url-parse');
const { repoID, repoName, filePath, fileName, mode, draftID, isDraft, hasDraft } = window.app.pageOptions;
const { siteRoot, serviceUrl, seafileCollabServer } = window.app.config;
const userInfo = window.app.userInfo;
@@ -300,9 +303,11 @@ class MarkdownEditor extends React.Component {
collabUsers: userInfo ?
[{user: userInfo, is_editing: false}] : [],
commentsNumber: null,
- activeTab: 'outline',
loadingDiff: false,
value: null,
+ isShowComments: false,
+ isShowHistory: false,
+ isShowOutline: true,
};
if (this.state.collabServer) {
@@ -721,17 +726,8 @@ class MarkdownEditor extends React.Component {
});
}
- tabItemClick = (tab) => {
- if (this.state.activeTab !== tab) {
- this.setState({
- activeTab: tab
- });
- }
- }
-
setBtnPosition = (e) => {
- let isShowComments = this.state.activeTab === 'comments' ? true : false;
- if (!isShowComments) return;
+ if (!this.state.isShowComments) return;
const nativeSelection = window.getSelection();
if (!nativeSelection.rangeCount) {
this.range = null;
@@ -855,9 +851,95 @@ class MarkdownEditor extends React.Component {
return newNodes;
}
+ scrollToNode = (node) => {
+ let url = new URL(window.location.href);
+ url.set('hash', 'user-content-' + node.text);
+ window.location.href = url.toString();
+ }
+
+ findScrollContainer = (el, window) => {
+ let parent = el.parentNode;
+ const OVERFLOWS = ['auto', 'overlay', 'scroll'];
+ let scroller;
+ while (!scroller) {
+ if (!parent.parentNode) break;
+ const style = window.getComputedStyle(parent);
+ const { overflowY } = style;
+ if (OVERFLOWS.includes(overflowY)) {
+ scroller = parent;
+ break;
+ }
+ parent = parent.parentNode;
+ }
+ if (!scroller) {
+ return window.document.body;
+ }
+ return scroller;
+ }
+
+ scrollToQuote = (path) => {
+ if (!path) return;
+ const win = window;
+ if (path.length > 2) {
+ // deal with code block or chart
+ path[0] = path[0] > 1 ? path[0] - 1 : path[0] + 1;
+ path = path.slice(0, 1);
+ }
+ let node = this.state.value.document.getNode(path);
+ if (!node) {
+ path = path.slice(0, 1);
+ node = this.state.value.document.getNode(path);
+ }
+ if (node) {
+ let element = win.document.querySelector(`[data-key="${node.key}"]`);
+ while (element.tagName === 'CODE') {
+ element = element.parentNode;
+ }
+ const scroller = this.findScrollContainer(element, win);
+ const isWindow = scroller == win.document.body || scroller == win.document.documentElement;
+ if (isWindow) {
+ win.scrollTo(0, element.offsetTop);
+ } else {
+ scroller.scrollTop = element.offsetTop;
+ }
+ }
+ }
+
+ toggleHistory = () => {
+ if (this.state.isShowHistory) {
+ this.setState({
+ isShowHistory: false,
+ isShowOutline: true,
+ isShowComments: false,
+ });
+ } else {
+ this.setState({
+ isShowHistory: true,
+ isShowOutline: false,
+ isShowComments: false,
+ });
+ }
+ }
+
+ toggleCommentList = () => {
+ if (this.state.isShowComments) {
+ this.setState({
+ isShowHistory: false,
+ isShowOutline: true,
+ isShowComments: false,
+ });
+ } else {
+ this.setState({
+ isShowHistory: false,
+ isShowOutline: false,
+ isShowComments: true,
+ });
+ }
+ }
+
render() {
let component;
- let isShowComments = this.state.activeTab === 'comments' ? true : false;
+ let sidePanel = (this.state.isShowHistory || this.state.isShowComments) ? true : false;
if (this.state.loading) {
return (
@@ -882,11 +964,15 @@ class MarkdownEditor extends React.Component {
toggleShareLinkDialog={this.toggleShareLinkDialog}
onEdit={this.onEdit}
toggleNewDraft={editorUtilities.createDraftFile}
+ commentsNumber={this.state.commentsNumber}
+ toggleCommentList={this.toggleCommentList}
+ showFileHistory={true}
+ toggleHistory={this.toggleHistory}
/>
-
+
{
- this.state.activeTab === 'history' ?
+ this.state.isShowHistory ?
{ this.state.loadingDiff ?
@@ -899,30 +985,38 @@ class MarkdownEditor extends React.Component {
:
-
+
- {isShowComments &&
+ {this.state.isShowComments &&
}
}
+ {
+ this.state.isShowOutline &&
+
+ }
+
+
+ {this.state.isShowComments && }
+ {
+ this.state.isShowHistory &&
+
+ }
-
);
@@ -952,7 +1046,7 @@ class MarkdownEditor extends React.Component {
fileTagList={this.state.fileTagList}
deleteDraft={this.deleteDraft}
showDraftSaved={this.state.showDraftSaved}
- />;
+ />
}
return (