mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-02 15:38:15 +00:00
Repair wiki view bug (#2304)
* repair scroll bug and href bug * add response for (delete,rename,new) sidebar node * repair scroll bug * add null markdown handler * modify delete response logic * repair style of the content * optimized code
This commit is contained in:
committed by
Daniel Pan
parent
0555c059c3
commit
842342dbc9
@@ -2,7 +2,7 @@ import React, { Component } from 'react';
|
|||||||
import TreeView from './tree-view/tree-view';
|
import TreeView from './tree-view/tree-view';
|
||||||
import { siteRoot, logoPath, mediaUrl, siteTitle, logoWidth, logoHeight } from './constance';
|
import { siteRoot, logoPath, mediaUrl, siteTitle, logoWidth, logoHeight } from './constance';
|
||||||
import Tree from './tree-view/tree';
|
import Tree from './tree-view/tree';
|
||||||
import Node from './tree-view/node'
|
import { Node } from './tree-view/node'
|
||||||
import NodeMenu from './menu-component/node-menu';
|
import NodeMenu from './menu-component/node-menu';
|
||||||
import MenuControl from './menu-component/node-menu-control';
|
import MenuControl from './menu-component/node-menu-control';
|
||||||
const gettext = window.gettext;
|
const gettext = window.gettext;
|
||||||
@@ -82,13 +82,40 @@ class SidePanel extends Component {
|
|||||||
|
|
||||||
onAddFolderNode = (dirPath) => {
|
onAddFolderNode = (dirPath) => {
|
||||||
this.props.editorUtilities.createDir(dirPath).then(res => {
|
this.props.editorUtilities.createDir(dirPath).then(res => {
|
||||||
this.initializeTreeData()
|
let tree = this.state.tree_data.copy();
|
||||||
|
let index = dirPath.lastIndexOf("/");
|
||||||
|
let name = dirPath.substring(index+1);
|
||||||
|
let parentPath = dirPath.substring(0, index);
|
||||||
|
if (!parentPath) {
|
||||||
|
parentPath = "/";
|
||||||
|
}
|
||||||
|
let node = new Node({name : name, type: "dir", isExpanded: false, children: []});
|
||||||
|
let parentNode = tree.getNodeByPath(parentPath);
|
||||||
|
tree.addChildToNode(parentNode, node);
|
||||||
|
tree.setOneNodeToActived({node});
|
||||||
|
this.setState({
|
||||||
|
tree_data: tree
|
||||||
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
onAddFileNode = (filePath) => {
|
onAddFileNode = (filePath) => {
|
||||||
this.props.editorUtilities.createFile(filePath).then(res => {
|
this.props.editorUtilities.createFile(filePath).then(res => {
|
||||||
this.initializeTreeData()
|
let tree = this.state.tree_data.copy();
|
||||||
|
let index = filePath.lastIndexOf("/");
|
||||||
|
let name = filePath.substring(index+1);
|
||||||
|
let parentPath = filePath.substring(0, index);
|
||||||
|
if (!parentPath) {
|
||||||
|
parentPath = "/";
|
||||||
|
}
|
||||||
|
let node = new Node({name : name, type: "file", isExpanded: false, children: []});
|
||||||
|
let parentNode = tree.getNodeByPath(parentPath);
|
||||||
|
tree.addChildToNode(parentNode, node);
|
||||||
|
tree.setOneNodeToActived({node});
|
||||||
|
this.setState({
|
||||||
|
tree_data: tree
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,17 +125,16 @@ class SidePanel extends Component {
|
|||||||
let filePath = node.path;
|
let filePath = node.path;
|
||||||
if (type === 'file') {
|
if (type === 'file') {
|
||||||
this.props.editorUtilities.renameFile(filePath, newName).then(res => {
|
this.props.editorUtilities.renameFile(filePath, newName).then(res => {
|
||||||
this.initializeTreeData()
|
|
||||||
if (this.isModifyCurrentFile()) {
|
if (this.isModifyCurrentFile()) {
|
||||||
node.name = newName;
|
node.name = newName;
|
||||||
this.props.onFileClick(null, node);
|
this.props.onFileClick(null, node);
|
||||||
|
this.changeActivedNode({node});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type === 'dir') {
|
if (type === 'dir') {
|
||||||
this.props.editorUtilities.renameDir(filePath, newName).then(res => {
|
this.props.editorUtilities.renameDir(filePath, newName).then(res => {
|
||||||
this.initializeTreeData();
|
|
||||||
if (this.isModifyContainsCurrentFile()) {
|
if (this.isModifyContainsCurrentFile()) {
|
||||||
let currentNode = this.state.currentNode;
|
let currentNode = this.state.currentNode;
|
||||||
let nodePath = encodeURI(currentNode.path);
|
let nodePath = encodeURI(currentNode.path);
|
||||||
@@ -118,6 +144,7 @@ class SidePanel extends Component {
|
|||||||
if(node){
|
if(node){
|
||||||
currentNode.name = newName;
|
currentNode.name = newName;
|
||||||
this.props.onFileClick(null, node);
|
this.props.onFileClick(null, node);
|
||||||
|
this.changeActivedNode({node})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -129,15 +156,11 @@ class SidePanel extends Component {
|
|||||||
let filePath = currentNode.path;
|
let filePath = currentNode.path;
|
||||||
let type = currentNode.type;
|
let type = currentNode.type;
|
||||||
if (type === 'file') {
|
if (type === 'file') {
|
||||||
this.props.editorUtilities.deleteFile(filePath).then(res => {
|
this.props.editorUtilities.deleteFile(filePath);
|
||||||
this.initializeTreeData();
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type === 'dir') {
|
if (type === 'dir') {
|
||||||
this.props.editorUtilities.deleteDir(filePath).then(res => {
|
this.props.editorUtilities.deleteDir(filePath);
|
||||||
this.initializeTreeData();
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let isCurrentFile = false;
|
let isCurrentFile = false;
|
||||||
@@ -147,23 +170,39 @@ class SidePanel extends Component {
|
|||||||
isCurrentFile = this.isModifyCurrentFile();
|
isCurrentFile = this.isModifyCurrentFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let tree = this.state.tree_data.copy();
|
||||||
|
tree.removeNodeFromTree(currentNode);
|
||||||
|
|
||||||
if (isCurrentFile) {
|
if (isCurrentFile) {
|
||||||
let homeNode = this.getHomeNode();
|
let homeNode = this.getHomeNode();
|
||||||
this.props.onFileClick(null, homeNode);
|
this.props.onFileClick(null, homeNode);
|
||||||
|
tree.setNoneNodeActived();
|
||||||
|
this.setState({tree_data: tree})
|
||||||
|
} else {
|
||||||
|
this.setState({tree_data: tree})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
changeActivedNode(node) {
|
||||||
|
let tree = this.state.tree_data.copy();
|
||||||
|
tree.setOneNodeToActived(node);
|
||||||
|
this.setState({
|
||||||
|
tree_data: tree
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
isModifyCurrentFile() {
|
isModifyCurrentFile() {
|
||||||
let name = this.state.currentNode.name;
|
let name = this.state.currentNode.name;
|
||||||
let pathname = window.location.pathname;
|
let pathname = window.location.pathname;
|
||||||
let currentName = pathname.slice(pathname.lastIndexOf("/") + 1);
|
let currentName = pathname.slice(pathname.lastIndexOf("/") + 1);
|
||||||
return name === currentName;
|
return encodeURI(name) === currentName;
|
||||||
}
|
}
|
||||||
|
|
||||||
isModifyContainsCurrentFile() {
|
isModifyContainsCurrentFile() {
|
||||||
let pathname = window.location.pathname;
|
let pathname = window.location.pathname;
|
||||||
let nodePath = this.state.currentNode.path;
|
let nodePath = this.state.currentNode.path;
|
||||||
if (pathname.indexOf(nodePath)) {
|
|
||||||
|
if (pathname.indexOf(encodeURI(nodePath)) > -1) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@@ -44,13 +44,17 @@ class MarkdownViewerContent extends React.Component {
|
|||||||
|
|
||||||
class MarkdownViewer extends React.Component {
|
class MarkdownViewer extends React.Component {
|
||||||
|
|
||||||
state = {
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
renderingContent: true,
|
renderingContent: true,
|
||||||
renderingOutline: true,
|
renderingOutline: true,
|
||||||
html: '',
|
html: '',
|
||||||
outlineTreeRoot: null,
|
outlineTreeRoot: null,
|
||||||
navItems: [],
|
navItems: [],
|
||||||
activeId: 0
|
activeId: 0
|
||||||
|
};
|
||||||
|
this.activeIdFromOutLine = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
scrollToNode(node) {
|
scrollToNode(node) {
|
||||||
@@ -60,12 +64,13 @@ class MarkdownViewer extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
scrollHandler = (event) => {
|
scrollHandler = (event) => {
|
||||||
|
var currentId = '';
|
||||||
|
if (!this.activeIdFromOutLine) {
|
||||||
var target = event.target || event.srcElement;
|
var target = event.target || event.srcElement;
|
||||||
var markdownContainer = this.refs.markdownContainer;
|
var markdownContainer = this.refs.markdownContainer;
|
||||||
var headingList = markdownContainer.querySelectorAll('[id^="user-content"]');
|
var headingList = markdownContainer.querySelectorAll('[id^="user-content"]');
|
||||||
var top = target.scrollTop;
|
var top = target.scrollTop;
|
||||||
var defaultOffset = markdownContainer.offsetTop;
|
var defaultOffset = markdownContainer.offsetTop;
|
||||||
var currentId = '';
|
|
||||||
for (let i = 0; i < headingList.length; i++) {
|
for (let i = 0; i < headingList.length; i++) {
|
||||||
let heading = headingList[i];
|
let heading = headingList[i];
|
||||||
if (heading.tagName === 'H1') {
|
if (heading.tagName === 'H1') {
|
||||||
@@ -77,6 +82,10 @@ class MarkdownViewer extends React.Component {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
currentId = this.activeIdFromOutLine;
|
||||||
|
this.activeIdFromOutLine = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (currentId !== this.state.activeId) {
|
if (currentId !== this.state.activeId) {
|
||||||
this.setState({
|
this.setState({
|
||||||
@@ -85,6 +94,10 @@ class MarkdownViewer extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleNavItemClick = (activeId) => {
|
||||||
|
this.activeIdFromOutLine = activeId;
|
||||||
|
}
|
||||||
|
|
||||||
setContent(markdownContent) {
|
setContent(markdownContent) {
|
||||||
let that = this;
|
let that = this;
|
||||||
|
|
||||||
@@ -118,6 +131,10 @@ class MarkdownViewer extends React.Component {
|
|||||||
navItems: navItems,
|
navItems: navItems,
|
||||||
activeId: currentId
|
activeId: currentId
|
||||||
})
|
})
|
||||||
|
}else {
|
||||||
|
_this.setState({
|
||||||
|
navItems: []
|
||||||
|
})
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -35,6 +35,141 @@ class Tree {
|
|||||||
node.children.splice(insertIndex, 0, child);
|
node.children.splice(insertIndex, 0, child);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeChildNode(node, child) {
|
||||||
|
let children = node.children;
|
||||||
|
let removeNode = null;
|
||||||
|
let index = null;
|
||||||
|
for (let i = 0; i < children.length; i++) {
|
||||||
|
if (child.path === children[i].path) {
|
||||||
|
removeNode = children[i];
|
||||||
|
index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
child.parent = null;
|
||||||
|
node.children.splice(index, 1);
|
||||||
|
return removeNode ? removeNode : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
addNodeToTree(node) {
|
||||||
|
let parentNode = this.getNodeParentFromTree(node);
|
||||||
|
this.addChildToNode(parentNode, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeNodeFromTree(node) {
|
||||||
|
let parentNode = this.getNodeParentFromTree(node);
|
||||||
|
this.removeChildNode(parentNode, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
getNodeParentFromTree(node) {
|
||||||
|
let parentNode = node.parent;
|
||||||
|
let findNode = null;
|
||||||
|
function cb(node) {
|
||||||
|
if(parentNode.path === node.path){
|
||||||
|
findNode = node;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.traverseBF(cb);
|
||||||
|
return findNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
getNodeByPath(path) {
|
||||||
|
let findNode = null;
|
||||||
|
function cb(node){
|
||||||
|
if (node.path === path) {
|
||||||
|
findNode = node;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.traverseBF(cb);
|
||||||
|
return findNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
traverseDF(callback) {
|
||||||
|
let stack = [];
|
||||||
|
let found = false;
|
||||||
|
stack.unshift(this.root);
|
||||||
|
let currentNode = stack.shift();
|
||||||
|
while (!found && currentNode) {
|
||||||
|
found = callback(currentNode) == true ? true : false;
|
||||||
|
if (!found) {
|
||||||
|
stack.unshift(...currentNode.children);
|
||||||
|
currentNode = stack.shift();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
traverseBF(callback) {
|
||||||
|
let queue = [];
|
||||||
|
let found = false;
|
||||||
|
queue.push(this.root);
|
||||||
|
let currentNode = queue.shift();
|
||||||
|
while (!found && currentNode) {
|
||||||
|
found = callback(currentNode) === true ? true : false;
|
||||||
|
if (!found) {
|
||||||
|
queue.push(...currentNode.children);
|
||||||
|
currentNode = queue.shift();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setOneNodeToActived({node}) {
|
||||||
|
this.setNoneNodeActived();
|
||||||
|
let root = this.root;
|
||||||
|
root.isExpanded = true;
|
||||||
|
let layer2 = root.hasChildren() ? root.children : null; // direct to replace root child;
|
||||||
|
let isLayer2 = false;
|
||||||
|
for (let i = 0; i < layer2.length; i++) {
|
||||||
|
if (node.path === layer2[i].path) {
|
||||||
|
isLayer2 = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isLayer2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let replaceNode = null;
|
||||||
|
let needReplacedNode = null;
|
||||||
|
while (node.parent) {
|
||||||
|
let flag = false;
|
||||||
|
node.parent.isExpanded = true;
|
||||||
|
for (let i = 0; i < layer2.length; i++) {
|
||||||
|
if (node.parent.path === layer2[i].path) {
|
||||||
|
replaceNode = node.parent;
|
||||||
|
needReplacedNode = layer2[i];
|
||||||
|
flag = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (flag) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
node = node.parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.removeChildNode(root, needReplacedNode);
|
||||||
|
this.addChildToNode(root, replaceNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
setNoneNodeActived() {
|
||||||
|
function setNodeToDeactived(node) {
|
||||||
|
if (node.isExpanded) {
|
||||||
|
node.isExpanded = false;
|
||||||
|
if (node.hasChildren()) {
|
||||||
|
let children = node.children;
|
||||||
|
children.forEach(function(child) {
|
||||||
|
setNodeToDeactived(child);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setNodeToDeactived(this.root);
|
||||||
|
this.root.isExpanded = true; // default to show;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* parse tree from javascript object
|
* parse tree from javascript object
|
||||||
|
@@ -3,8 +3,8 @@ import React from 'react';
|
|||||||
class WikiOutlineItem extends React.Component {
|
class WikiOutlineItem extends React.Component {
|
||||||
|
|
||||||
handleNavItemClick = () => {
|
handleNavItemClick = () => {
|
||||||
var index = this.props.item.key;
|
var activeId = this.props.item.id;
|
||||||
this.props.handleNavItemClick(index)
|
this.props.handleNavItemClick(activeId)
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@@ -32,14 +32,6 @@ class WikiOutline extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleNavItemClick = (index) => {
|
|
||||||
if (index !== this.state.activeIndex) {
|
|
||||||
this.setState({
|
|
||||||
activeIndex : index
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
let _this = this;
|
let _this = this;
|
||||||
let activeId = nextProps.activeId;
|
let activeId = nextProps.activeId;
|
||||||
@@ -49,13 +41,12 @@ class WikiOutline extends React.Component {
|
|||||||
let flag = false;
|
let flag = false;
|
||||||
let item = navItems[i];
|
let item = navItems[i];
|
||||||
if (item.id === activeId && item.key !== _this.state.activeIndex) {
|
if (item.id === activeId && item.key !== _this.state.activeIndex) {
|
||||||
let direction = item.key > _this.state.activeIndex ? "down" : "up";
|
|
||||||
let currentTop = parseInt(_this.state.scrollTop);
|
|
||||||
let scrollTop = 0;
|
let scrollTop = 0;
|
||||||
if (item.key > 20 && direction === "down") {
|
if (item.key > 20) {
|
||||||
scrollTop = currentTop - 27 + "px";
|
scrollTop = - (item.key - 20)*27 + "px";
|
||||||
} else if (currentTop < 0 && direction === "up") {
|
if (parseInt(scrollTop) > 0) { // handle scroll quickly;
|
||||||
scrollTop = currentTop + 27 + "px";
|
scrollTop = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_this.setState({
|
_this.setState({
|
||||||
activeIndex : item.key,
|
activeIndex : item.key,
|
||||||
@@ -79,7 +70,7 @@ class WikiOutline extends React.Component {
|
|||||||
key={item.key}
|
key={item.key}
|
||||||
item={item}
|
item={item}
|
||||||
activeIndex={this.state.activeIndex}
|
activeIndex={this.state.activeIndex}
|
||||||
handleNavItemClick={this.handleNavItemClick}
|
handleNavItemClick={this.props.handleNavItemClick}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
@@ -64,6 +64,7 @@ img[src=""] {
|
|||||||
flex: 1 0 80%;
|
flex: 1 0 80%;
|
||||||
display:flex;
|
display:flex;
|
||||||
flex-direction:column;
|
flex-direction:column;
|
||||||
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wiki-side-panel {
|
.wiki-side-panel {
|
||||||
@@ -72,9 +73,13 @@ img[src=""] {
|
|||||||
flex-direction:column;
|
flex-direction:column;
|
||||||
overflow:hidden;
|
overflow:hidden;
|
||||||
}
|
}
|
||||||
|
.cur-view-main {
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.cur-view-container {
|
.cur-view-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cur-view-container .markdown-container {
|
.cur-view-container .markdown-container {
|
||||||
@@ -83,19 +88,21 @@ img[src=""] {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cur-view-container .markdown-content {
|
.cur-view-container .markdown-content {
|
||||||
flex: 1;
|
width: calc(100% - 160px);
|
||||||
width: calc(100% - 200px);
|
|
||||||
padding-right: 40px;
|
padding-right: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cur-view-container .markdown-outline {
|
.cur-view-container .markdown-outline {
|
||||||
position: sticky;
|
position: fixed;
|
||||||
|
padding-right: 18px;
|
||||||
|
top: 97px;
|
||||||
|
right: 0;
|
||||||
width: 200px;
|
width: 200px;
|
||||||
padding: 0 18px;
|
overflow: hidden;
|
||||||
top: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.wiki-hide {
|
.wiki-hide {
|
||||||
@@ -107,6 +114,7 @@ img[src=""] {
|
|||||||
padding-right: 40px;
|
padding-right: 40px;
|
||||||
}
|
}
|
||||||
.cur-view-container .markdown-content {
|
.cur-view-container .markdown-content {
|
||||||
|
width: 100%;
|
||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
}
|
}
|
||||||
.cur-view-container .markdown-outline {
|
.cur-view-container .markdown-outline {
|
||||||
|
Reference in New Issue
Block a user