mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-09 02:42:47 +00:00
replace tree with index content (#2819)
This commit is contained in:
33
frontend/src/components/index-viewer.js
Normal file
33
frontend/src/components/index-viewer.js
Normal file
@@ -0,0 +1,33 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import MarkdownViewer from '@seafile/seafile-editor/dist/viewer/markdown-viewer';
|
||||
|
||||
const viewerPropTypes = {
|
||||
onLinkClick: PropTypes.func,
|
||||
onContentRendered: PropTypes.func.isRequired,
|
||||
indexContent: PropTypes.string,
|
||||
};
|
||||
|
||||
class IndexContentViewer extends React.Component {
|
||||
|
||||
onLinkClick = (event) => {
|
||||
event.preventDefault();
|
||||
this.props.onLinkClick(event);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="markdown-content">
|
||||
<MarkdownViewer
|
||||
markdownContent={this.props.indexContent}
|
||||
onContentRendered={this.props.onContentRendered}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
IndexContentViewer.propTypes = viewerPropTypes;
|
||||
|
||||
export default IndexContentViewer;
|
@@ -34,6 +34,9 @@
|
||||
.wiki-pages-container .tree-view {
|
||||
padding-left:0;
|
||||
}
|
||||
.wiki-pages-container .article {
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.wiki-md-viewer-rendered-content {
|
||||
padding: 30px 0 0;
|
||||
@@ -176,7 +179,18 @@ img[src=""] {
|
||||
.markdown-content a {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
.wiki-side-nav .markdown-content a {
|
||||
color: #212529;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.index-edit {
|
||||
position: absolute;
|
||||
right: 0.25rem;
|
||||
top: 0.25rem;
|
||||
}
|
||||
|
||||
.wiki-main-panel .markdown-content .ml-2 {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import React, { Component } from 'react';
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { gettext, siteRoot, logoPath, mediaUrl, siteTitle, logoWidth, logoHeight } from '../../utils/constants';
|
||||
import { gettext, siteRoot, logoPath, mediaUrl, siteTitle, logoWidth, logoHeight, repoID } from '../../utils/constants';
|
||||
import TreeView from '../../components/tree-view/tree-view';
|
||||
import NodeMenu from '../../components/tree-view/node-menu';
|
||||
import MenuControl from '../../components/menu-control';
|
||||
@@ -8,6 +8,7 @@ import Delete from '../../components/dialog/delete-dialog';
|
||||
import Rename from '../../components/dialog/rename-dialog';
|
||||
import CreateFolder from '../../components/dialog/create-folder-dialog';
|
||||
import CreateFile from '../../components/dialog/create-file-dialog';
|
||||
import IndexContentViewer from '../../components/index-viewer';
|
||||
|
||||
const propTypes = {
|
||||
changedNode: PropTypes.object,
|
||||
@@ -21,6 +22,11 @@ const propTypes = {
|
||||
onDeleteNode: PropTypes.func.isRequired,
|
||||
onAddFileNode: PropTypes.func.isRequired,
|
||||
onAddFolderNode: PropTypes.func.isRequired,
|
||||
onLinkClick: PropTypes.func,
|
||||
hasIndex: PropTypes.bool.isRequired,
|
||||
indexContent: PropTypes.string,
|
||||
indexPath: PropTypes.string,
|
||||
indexPermission: PropTypes.string,
|
||||
};
|
||||
|
||||
class SidePanel extends Component {
|
||||
@@ -173,6 +179,15 @@ class SidePanel extends Component {
|
||||
this.setState({showRename: !this.state.showRename});
|
||||
}
|
||||
|
||||
onEditClick = (e) => {
|
||||
e.preventDefault();
|
||||
window.location.href= siteRoot + 'lib/' + repoID + '/file' + this.props.indexPath + '?mode=edit';
|
||||
}
|
||||
|
||||
onContentRendered = () => {
|
||||
// todo
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className={`side-panel wiki-side-panel ${this.props.closeSideBar ? '': 'left-zero'}`}>
|
||||
@@ -183,71 +198,90 @@ class SidePanel extends Component {
|
||||
<a title="Close" aria-label="Close" onClick={this.closeSide} className="sf2-icon-x1 sf-popover-close side-panel-close action-icon d-md-none "></a>
|
||||
</div>
|
||||
<div id="side-nav" className="wiki-side-nav" role="navigation">
|
||||
<h3
|
||||
className="wiki-pages-heading"
|
||||
onMouseEnter={this.onMouseEnter}
|
||||
onMouseLeave={this.onMouseLeave}
|
||||
>
|
||||
{gettext('Pages')}
|
||||
<div className="heading-icon">
|
||||
<MenuControl
|
||||
isShow={this.state.isMenuIconShow}
|
||||
onClick={this.onHeadingMenuClick}
|
||||
/>
|
||||
</div>
|
||||
</h3>
|
||||
<div className="wiki-pages-container">
|
||||
{this.props.treeData &&
|
||||
<TreeView
|
||||
currentPath={this.props.currentPath}
|
||||
treeData={this.props.treeData}
|
||||
currentNode={this.state.currentNode}
|
||||
isNodeItemFrezee={this.state.isNodeItemFrezee}
|
||||
onNodeClick={this.onNodeClick}
|
||||
onShowContextMenu={this.onShowContextMenu}
|
||||
onDirCollapse={this.props.onDirCollapse}
|
||||
/>
|
||||
}
|
||||
{this.state.isShowMenu &&
|
||||
<NodeMenu
|
||||
menuPosition={this.state.menuPosition}
|
||||
currentNode={this.state.currentNode}
|
||||
toggleAddFile={this.toggleAddFile}
|
||||
toggleAddFolder={this.toggleAddFolder}
|
||||
toggleRename={this.toggleRename}
|
||||
toggleDelete={this.toggleDelete}
|
||||
/>
|
||||
}
|
||||
{this.state.showDelete &&
|
||||
<Delete
|
||||
currentNode={this.state.currentNode}
|
||||
handleSubmit={this.onDeleteNode}
|
||||
toggleCancel={this.deleteCancel}
|
||||
/>
|
||||
}
|
||||
{this.state.showFile &&
|
||||
<CreateFile
|
||||
fileType={'.md'}
|
||||
parentPath={this.state.currentNode.path}
|
||||
onAddFile={this.onAddFileNode}
|
||||
addFileCancel={this.addFileCancel}
|
||||
/>
|
||||
}
|
||||
{this.state.showFolder &&
|
||||
<CreateFolder
|
||||
parentPath={this.state.currentNode.path}
|
||||
onAddFolder={this.onAddFolderNode}
|
||||
addFolderCancel={this.addFolderCancel}
|
||||
/>
|
||||
}
|
||||
{this.state.showRename &&
|
||||
<Rename
|
||||
currentNode={this.state.currentNode}
|
||||
onRename={this.onRenameNode}
|
||||
toggleCancel={this.renameCancel}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
{this.props.hasIndex ?
|
||||
<Fragment>
|
||||
<h3 className="wiki-pages-heading">
|
||||
{gettext('Contents')}
|
||||
{this.props.indexPermission === 'rw' &&
|
||||
<button className="btn btn-secondary operation-item index-edit" title="Edit Index" onClick={this.onEditClick}>{gettext('Edit')}</button>
|
||||
}
|
||||
</h3>
|
||||
<div className="wiki-pages-container">
|
||||
<IndexContentViewer
|
||||
onLinkClick={this.props.onLinkClick}
|
||||
onContentRendered={this.onContentRendered}
|
||||
indexContent={this.props.indexContent}
|
||||
/>
|
||||
</div>
|
||||
</Fragment> :
|
||||
<Fragment>
|
||||
<h3
|
||||
className="wiki-pages-heading"
|
||||
onMouseEnter={this.onMouseEnter}
|
||||
onMouseLeave={this.onMouseLeave}
|
||||
>
|
||||
{gettext('Pages')}
|
||||
<div className="heading-icon">
|
||||
<MenuControl
|
||||
isShow={this.state.isMenuIconShow}
|
||||
onClick={this.onHeadingMenuClick}
|
||||
/>
|
||||
</div>
|
||||
</h3>
|
||||
<div className="wiki-pages-container">
|
||||
{this.props.treeData &&
|
||||
<TreeView
|
||||
currentPath={this.props.currentPath}
|
||||
treeData={this.props.treeData}
|
||||
currentNode={this.state.currentNode}
|
||||
isNodeItemFrezee={this.state.isNodeItemFrezee}
|
||||
onNodeClick={this.onNodeClick}
|
||||
onShowContextMenu={this.onShowContextMenu}
|
||||
onDirCollapse={this.props.onDirCollapse}
|
||||
/>
|
||||
}
|
||||
{this.state.isShowMenu &&
|
||||
<NodeMenu
|
||||
menuPosition={this.state.menuPosition}
|
||||
currentNode={this.state.currentNode}
|
||||
toggleAddFile={this.toggleAddFile}
|
||||
toggleAddFolder={this.toggleAddFolder}
|
||||
toggleRename={this.toggleRename}
|
||||
toggleDelete={this.toggleDelete}
|
||||
/>
|
||||
}
|
||||
{this.state.showDelete &&
|
||||
<Delete
|
||||
currentNode={this.state.currentNode}
|
||||
handleSubmit={this.onDeleteNode}
|
||||
toggleCancel={this.deleteCancel}
|
||||
/>
|
||||
}
|
||||
{this.state.showFile &&
|
||||
<CreateFile
|
||||
fileType={'.md'}
|
||||
parentPath={this.state.currentNode.path}
|
||||
onAddFile={this.onAddFileNode}
|
||||
addFileCancel={this.addFileCancel}
|
||||
/>
|
||||
}
|
||||
{this.state.showFolder &&
|
||||
<CreateFolder
|
||||
parentPath={this.state.currentNode.path}
|
||||
onAddFolder={this.onAddFolderNode}
|
||||
addFolderCancel={this.addFolderCancel}
|
||||
/>
|
||||
}
|
||||
{this.state.showRename &&
|
||||
<Rename
|
||||
currentNode={this.state.currentNode}
|
||||
onRename={this.onRenameNode}
|
||||
toggleCancel={this.renameCancel}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
</Fragment>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@@ -30,7 +30,11 @@ class Wiki extends Component {
|
||||
permission: '',
|
||||
isFileLoading: false,
|
||||
changedNode: null,
|
||||
isViewFileState: true
|
||||
isViewFileState: true,
|
||||
hasIndex: false,
|
||||
indexContent: '',
|
||||
indexPath: '',
|
||||
indexPermission: '',
|
||||
};
|
||||
window.onpopstate = this.onpopstate;
|
||||
}
|
||||
@@ -39,6 +43,23 @@ class Wiki extends Component {
|
||||
this.initWikiData(initialPath);
|
||||
}
|
||||
|
||||
getIndexContent = (files) => {
|
||||
files.some(file => {
|
||||
if (file.type === 'file' && file.name === 'index.md') {
|
||||
let filePath = Utils.joinPath(file.parent_path, file.name);
|
||||
editorUtilities.getWikiFileContent(slug, filePath).then((res) => {
|
||||
this.setState({
|
||||
hasIndex: true,
|
||||
indexContent: res.data.content,
|
||||
indexPath: filePath,
|
||||
indexPermission: res.data.permission,
|
||||
});
|
||||
return;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
initWikiData(filePath){
|
||||
this.setState({isFileLoading: true});
|
||||
editorUtilities.getFiles().then((files) => {
|
||||
@@ -68,6 +89,7 @@ class Wiki extends Component {
|
||||
let fileUrl = siteRoot + 'wikis/' + slug + filePath + hash;
|
||||
window.history.pushState({urlPath: fileUrl, filePath: filePath}, filePath, fileUrl);
|
||||
}
|
||||
this.getIndexContent(files);
|
||||
}, () => {
|
||||
this.setState({
|
||||
isLoadFailed: true
|
||||
@@ -81,6 +103,7 @@ class Wiki extends Component {
|
||||
.then(res => {
|
||||
this.setState({
|
||||
content: res.data.content,
|
||||
isViewFileState: true,
|
||||
latestContributor: res.data.latest_contributor,
|
||||
lastModified: moment.unix(res.data.last_modified).fromNow(),
|
||||
permission: res.data.permission,
|
||||
@@ -95,7 +118,7 @@ class Wiki extends Component {
|
||||
}
|
||||
|
||||
onLinkClick = (event) => {
|
||||
const url = event.target.href;
|
||||
const url = event.path[2].href;
|
||||
if (this.isInternalMarkdownLink(url)) {
|
||||
let path = this.getPathFromInternalMarkdownLink(url);
|
||||
this.initMainPanelData(path);
|
||||
@@ -450,7 +473,7 @@ class Wiki extends Component {
|
||||
}
|
||||
|
||||
isInternalDirLink(url) {
|
||||
var re = new RegExp(siteRoot + '#[a-z\-]*?/lib/' + repoID + '/.*');
|
||||
var re = new RegExp(siteRoot + 'library/' + repoID + '/.*');
|
||||
return re.test(url);
|
||||
}
|
||||
|
||||
@@ -462,11 +485,12 @@ class Wiki extends Component {
|
||||
}
|
||||
|
||||
getPathFromInternalDirLink(url) {
|
||||
var re = new RegExp(siteRoot + '#[a-z\-]*?/lib/' + repoID + '(/.*)');
|
||||
var re = new RegExp(siteRoot + 'library/' + repoID + '(/.*)');
|
||||
var array = re.exec(url);
|
||||
var path = decodeURIComponent(array[1]);
|
||||
|
||||
var dirPath = path.substring(1);
|
||||
var index = path.substring(1).indexOf('/');
|
||||
var dirPath = path.substring(index + 1);
|
||||
re = new RegExp('(^/.*)');
|
||||
if (re.test(dirPath)) {
|
||||
path = dirPath;
|
||||
@@ -492,6 +516,11 @@ class Wiki extends Component {
|
||||
onRenameNode={this.onRenameNode}
|
||||
onDeleteNode={this.onDeleteNode}
|
||||
onDirCollapse={this.onDirCollapse}
|
||||
onLinkClick={this.onLinkClick}
|
||||
hasIndex={this.state.hasIndex}
|
||||
indexContent={this.state.indexContent}
|
||||
indexPath={this.state.indexPath}
|
||||
indexPermission={this.state.indexPermission}
|
||||
/>
|
||||
<MainPanel
|
||||
content={this.state.content}
|
||||
|
Reference in New Issue
Block a user