mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-11 20:01:10 +00:00
12.0 refactor wiki folder (#6290)
* 01 remove wiki folder * remove folder * 03 remove wiki folder css * 04 optimise codes
This commit is contained in:
@@ -1,24 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
|
||||||
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
|
||||||
viewBox="0 0 32 32" style="enable-background:new 0 0 32 32;" xml:space="preserve">
|
|
||||||
<style type="text/css">
|
|
||||||
.st0{fill:#FFFFFF;}
|
|
||||||
.st1{fill:#949494;}
|
|
||||||
</style>
|
|
||||||
<g>
|
|
||||||
<path id="path-1" class="st0" d="M28.8,12.8H3.2v-8c0-0.9,0.5-1.6,1.4-1.6H10c0.6,0,1.2,0.3,1.4,0.9l1.8,3.7
|
|
||||||
c0.5,1.1,1.7,1.8,2.9,1.8H27c0.9,0,1.8,0.7,1.8,1.6V12.8z M28.8,27.2c0,0.9-0.9,1.6-1.8,1.6H4.6c-0.9,0-1.4-0.7-1.4-1.6V16h25.6
|
|
||||||
V27.2z M28.6,6.4H17.8c-1.2,0-2.3-0.7-2.9-1.8l-1.4-2.9C13,0.7,11.9,0,10.6,0H3C1.3,0,0,1.4,0,3.2v25.6C0,30.6,1.3,32,3,32h25.6
|
|
||||||
c1.8,0,3.4-1.4,3.4-3.2V9.6C32,7.8,30.4,6.4,28.6,6.4L28.6,6.4z"/>
|
|
||||||
</g>
|
|
||||||
<title>folder</title>
|
|
||||||
<g id="folder">
|
|
||||||
<g>
|
|
||||||
<path id="path-1_1_" class="st1" d="M28.8,12.8H3.2v-8c0-0.9,0.5-1.6,1.4-1.6H10c0.6,0,1.2,0.3,1.4,0.9l1.8,3.7
|
|
||||||
c0.5,1.1,1.7,1.8,2.9,1.8H27c0.9,0,1.8,0.7,1.8,1.6V12.8z M28.8,27.2c0,0.9-0.9,1.6-1.8,1.6H4.6c-0.9,0-1.4-0.7-1.4-1.6V16h25.6
|
|
||||||
V27.2z M28.6,6.4H17.8c-1.2,0-2.3-0.7-2.9-1.8l-1.4-2.9C13,0.7,11.9,0,10.6,0H3C1.3,0,0,1.4,0,3.2v25.6C0,30.6,1.3,32,3,32h25.6
|
|
||||||
c1.8,0,3.4-1.4,3.4-3.2V9.6C32,7.8,30.4,6.4,28.6,6.4L28.6,6.4z"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 1.3 KiB |
@@ -1,4 +0,0 @@
|
|||||||
const FOLDER = 'folder';
|
|
||||||
const PAGE = 'page';
|
|
||||||
|
|
||||||
export { FOLDER, PAGE };
|
|
@@ -15,14 +15,11 @@
|
|||||||
padding-bottom: 0.5rem;
|
padding-bottom: 0.5rem;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
max-height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wiki-nav .page-folder {
|
.wiki-nav .can-drop::after,
|
||||||
position: relative;
|
.wiki-nav .can-drop-top::after {
|
||||||
}
|
|
||||||
|
|
||||||
.wiki-nav .page-folder.can-drop::after,
|
|
||||||
.wiki-nav .page-folder.can-drop-top::after {
|
|
||||||
content: '';
|
content: '';
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 1px;
|
height: 1px;
|
||||||
@@ -32,7 +29,16 @@
|
|||||||
background-color: #666;
|
background-color: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wiki-nav .page-folder .page-folder-children {
|
.wiki-nav .can-drop::after {
|
||||||
|
top: unset;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wiki-nav .can-drop-top::after {
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wiki-nav .page-children {
|
||||||
transition: height 0.25s ease-in-out 0s;
|
transition: height 0.25s ease-in-out 0s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,16 +50,6 @@
|
|||||||
border-bottom: 1px solid #666;
|
border-bottom: 1px solid #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wiki-nav .page-folder.can-drop::after {
|
|
||||||
top: unset;
|
|
||||||
bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wiki-nav .page-folder.can-drop-top::after {
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wiki-nav .page-folder-wrapper,
|
|
||||||
.wiki-nav .wiki-page-item {
|
.wiki-nav .wiki-page-item {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -72,7 +68,6 @@
|
|||||||
background-color: #EDEDEA;
|
background-color: #EDEDEA;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wiki-nav .page-folder-wrapper:hover,
|
|
||||||
.wiki-nav .wiki-page-item:hover {
|
.wiki-nav .wiki-page-item:hover {
|
||||||
background-color: #EFEFED;
|
background-color: #EFEFED;
|
||||||
}
|
}
|
||||||
@@ -81,7 +76,6 @@
|
|||||||
background-color: #E6E6E4;
|
background-color: #E6E6E4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wiki-nav .folder-main,
|
|
||||||
.wiki-nav .wiki-page-item-main {
|
.wiki-nav .wiki-page-item-main {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -90,15 +84,6 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wiki-nav .icon-expand-folder {
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 12px;
|
|
||||||
transform: scale(0.8);
|
|
||||||
color: #b5b5b5;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wiki-nav .folder-content,
|
|
||||||
.wiki-nav .wiki-page-content {
|
.wiki-nav .wiki-page-content {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
padding-right: 8px;
|
padding-right: 8px;
|
||||||
@@ -109,29 +94,25 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wiki-nav .in-folder .wiki-page-content {
|
.wiki-nav .wiki-page-content {
|
||||||
padding-left: calc(12 * 0.8px + 0.5rem);
|
padding-left: calc(12 * 0.8px + 0.5rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
.wiki-nav .folder-content:hover,
|
|
||||||
.wiki-nav .wiki-page-content:hover {
|
.wiki-nav .wiki-page-content:hover {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wiki-nav .folder-content .dtable-font,
|
|
||||||
.wiki-nav .wiki-page-content .dtable-font {
|
.wiki-nav .wiki-page-content .dtable-font {
|
||||||
margin-right: 6px;
|
margin-right: 6px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wiki-nav .folder-content .folder-name,
|
|
||||||
.wiki-nav .wiki-page-content .wiki-page-title {
|
.wiki-nav .wiki-page-content .wiki-page-title {
|
||||||
height: 40px;
|
height: 40px;
|
||||||
line-height: 40px;
|
line-height: 40px;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wiki-nav .folder-operation-dropdownmenu,
|
|
||||||
.wiki-nav .more-wiki-page-operation {
|
.wiki-nav .more-wiki-page-operation {
|
||||||
width: 24px;
|
width: 24px;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
@@ -141,37 +122,31 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.wiki-nav .wiki-add-page-btn:hover,
|
.wiki-nav .wiki-add-page-btn:hover,
|
||||||
.wiki-nav .folder-operation:hover,
|
|
||||||
.wiki-nav .more-wiki-page-operation:hover {
|
.wiki-nav .more-wiki-page-operation:hover {
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
background-color: #DFDFDD;
|
background-color: #DFDFDD;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wiki-nav .wiki-page-item .sf3-font.sf3-font-enlarge,
|
.wiki-nav .wiki-page-item .sf3-font.sf3-font-enlarge,
|
||||||
.wiki-nav .page-folder-wrapper .folder-operation-dropdownmenu .seafile-multicolor-icon-more-level,
|
|
||||||
.wiki-nav .wiki-page-item .more-wiki-page-operation .seafile-multicolor-icon-more-level {
|
.wiki-nav .wiki-page-item .more-wiki-page-operation .seafile-multicolor-icon-more-level {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wiki-nav .wiki-page-item:hover .sf3-font.sf3-font-enlarge,
|
.wiki-nav .wiki-page-item:hover .sf3-font.sf3-font-enlarge,
|
||||||
.wiki-nav .page-folder-wrapper:hover .folder-operation-dropdownmenu .seafile-multicolor-icon-more-level,
|
|
||||||
.wiki-nav .wiki-page-item:hover .more-wiki-page-operation .seafile-multicolor-icon-more-level {
|
.wiki-nav .wiki-page-item:hover .more-wiki-page-operation .seafile-multicolor-icon-more-level {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wiki-nav .folder-operation-dropdownmenu:hover,
|
|
||||||
.wiki-nav .more-wiki-page-operation:hover {
|
.wiki-nav .more-wiki-page-operation:hover {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wiki-nav .folder-operation-dropdownmenu .dtable-font,
|
|
||||||
.wiki-nav .more-wiki-page-operation .dtable-font {
|
.wiki-nav .more-wiki-page-operation .dtable-font {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wiki-nav .folder-list .page-folder.fold-freezed .btn-folder-operation,
|
|
||||||
.wiki-nav .wiki-page-item.wiki-page-freezed .sf3-font.sf3-font-enlarge,
|
.wiki-nav .wiki-page-item.wiki-page-freezed .sf3-font.sf3-font-enlarge,
|
||||||
.wiki-nav .wiki-page-item.wiki-page-freezed .seafile-multicolor-icon-more-level {
|
.wiki-nav .wiki-page-item.wiki-page-freezed .seafile-multicolor-icon-more-level {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
@@ -254,69 +229,6 @@
|
|||||||
margin: 0.2rem 0;
|
margin: 0.2rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* folders dropdown */
|
|
||||||
.wiki-nav .more-wiki-page-operation .btn-move-to-folder {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wiki-nav .more-wiki-page-operation .move-to-folders-toggle {
|
|
||||||
opacity: 0;
|
|
||||||
width: 0;
|
|
||||||
min-width: 0;
|
|
||||||
margin-left: -12px;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wiki-nav .more-wiki-page-operation .folders-dropdown-menu {
|
|
||||||
margin-top: -16px;
|
|
||||||
margin-left: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wiki-nav .folders-dropdown-menu .dropdown-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wiki-nav .more-wiki-page-operation .btn-move-to-folder:focus .dtable-icon-insert-right {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wiki-nav .folders-dropdown {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wiki-nav .folders-dropdown-toggle {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wiki-nav .folders-dropdown .item-text {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wiki-nav .folders-dropdown .dropdown-menu {
|
|
||||||
max-height: 300px;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wiki-nav .folders-dropdown .dropdown-menu .folder-name {
|
|
||||||
display: inline-block;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wiki-nav .folders-dropdown .icon-dropdown-toggle {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
width: 12px;
|
|
||||||
height: 12px;
|
|
||||||
margin-right: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wiki-nav .folders-dropdown .icon-dropdown-toggle .item-icon {
|
|
||||||
transform: scale(0.8);
|
|
||||||
}
|
|
||||||
|
|
||||||
.wiki-nav .wiki-page-item.page-can-drop::after,
|
.wiki-nav .wiki-page-item.page-can-drop::after,
|
||||||
.wiki-nav .wiki-page-item.page-can-drop-top::after {
|
.wiki-nav .wiki-page-item.page-can-drop-top::after {
|
||||||
content: '';
|
content: '';
|
||||||
@@ -351,40 +263,15 @@
|
|||||||
|
|
||||||
.wiki-nav,
|
.wiki-nav,
|
||||||
.wiki-nav .wiki-page-item .sf3-font.sf3-font-enlarge:hover,
|
.wiki-nav .wiki-page-item .sf3-font.sf3-font-enlarge:hover,
|
||||||
.wiki-nav .wiki-page-item .seafile-multicolor-icon-more-level:hover,
|
.wiki-nav .wiki-page-item .seafile-multicolor-icon-more-level:hover {
|
||||||
.wiki-nav .page-folder .seafile-multicolor-icon-more-level:hover,
|
|
||||||
.wiki-nav .icon-expand-folder:hover {
|
|
||||||
color: #212529;
|
color: #212529;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wiki-nav .wiki-page-item .sf3-font.sf3-font-enlarge,
|
.wiki-nav .wiki-page-item .sf3-font.sf3-font-enlarge,
|
||||||
.wiki-nav .wiki-page-item .seafile-multicolor-icon-more-level,
|
.wiki-nav .wiki-page-item .seafile-multicolor-icon-more-level {
|
||||||
.wiki-nav .page-folder .seafile-multicolor-icon-more-level,
|
|
||||||
.wiki-nav .icon-expand-folder {
|
|
||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wiki-nav .page-folder-wrapper.can-drop-top::before {
|
|
||||||
content: '';
|
|
||||||
width: 100%;
|
|
||||||
height: 1px;
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
background-color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wiki-nav .page-folder-wrapper.can-drop::after {
|
|
||||||
content: '';
|
|
||||||
width: 100%;
|
|
||||||
height: 1px;
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
background-color: #666;
|
|
||||||
top: unset;
|
|
||||||
bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.svg-item {
|
.svg-item {
|
||||||
width: 1em;
|
width: 1em;
|
||||||
height: 1em;
|
height: 1em;
|
||||||
|
@@ -103,15 +103,8 @@ class Wiki extends Component {
|
|||||||
|
|
||||||
getFirstPageId = (config) => {
|
getFirstPageId = (config) => {
|
||||||
if (!config || !Array.isArray(config.navigation)) return '';
|
if (!config || !Array.isArray(config.navigation)) return '';
|
||||||
for (let i = 0; i < config.navigation.length; i++) {
|
const firstPage = config.navigation[0] || {};
|
||||||
const item = config.navigation[i] || {};
|
return firstPage.id;
|
||||||
if (item.type === 'page') {
|
|
||||||
return item.id;
|
|
||||||
}
|
|
||||||
if (item.type === 'folder' && item.children[0]) {
|
|
||||||
return item.children[0].id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
getSdocFileContent = (docUuid, accessToken) => {
|
getSdocFileContent = (docUuid, accessToken) => {
|
||||||
|
@@ -1,8 +0,0 @@
|
|||||||
export default class Folder {
|
|
||||||
constructor(object) {
|
|
||||||
this.type = 'folder';
|
|
||||||
this.id = object.id;
|
|
||||||
this.name = object.name;
|
|
||||||
this.children = Array.isArray(object.children) ? object.children : [];
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,21 +1,37 @@
|
|||||||
import Page from './page';
|
import Page from './page';
|
||||||
import Folder from './folder';
|
|
||||||
|
|
||||||
export default class WikiConfig {
|
export default class WikiConfig {
|
||||||
constructor(object) {
|
constructor(object) {
|
||||||
this.version = object.version || 1;
|
const { version = 1, pages = [], navigation = [] } = object;
|
||||||
this.navigation = (Array.isArray(object.navigation) ? object.navigation : []).map(item => {
|
this.version = version;
|
||||||
if (item.type === 'folder') {
|
this.pages = pages.map(page => new Page(page));
|
||||||
return new Folder(item);
|
this.navigation = navigation.filter(item => {
|
||||||
} else if (item.type === 'page') {
|
return item.type === 'page';
|
||||||
return {
|
});
|
||||||
id: item.id,
|
// Render pages in folder to navigation root
|
||||||
type: item.type,
|
const page_id_map = {};
|
||||||
children: item.children || [],
|
this.pages.forEach(page => {
|
||||||
};
|
page_id_map[page.id] = false;
|
||||||
|
});
|
||||||
|
function traversePage(node) {
|
||||||
|
if (!node) return;
|
||||||
|
if (node.id) {
|
||||||
|
page_id_map[node.id] = true;
|
||||||
}
|
}
|
||||||
return null;
|
if (Array.isArray(node.children)) {
|
||||||
}).filter(item => !!item);
|
node.children.forEach(child => traversePage(child));
|
||||||
this.pages = Array.isArray(object.pages) ? object.pages.map(page => new Page(page)) : [];
|
}
|
||||||
|
}
|
||||||
|
traversePage({ children: this.navigation });
|
||||||
|
for (let key in page_id_map) {
|
||||||
|
if (page_id_map[key] === false) {
|
||||||
|
const page = this.pages.find(item => item.id === key);
|
||||||
|
this.navigation.push({
|
||||||
|
id: page.id,
|
||||||
|
type: 'page',
|
||||||
|
children: page.children || [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -7,14 +7,9 @@ import toaster from '../../components/toast';
|
|||||||
import Loading from '../../components/loading';
|
import Loading from '../../components/loading';
|
||||||
import WikiNav from './wiki-nav/index';
|
import WikiNav from './wiki-nav/index';
|
||||||
import PageUtils from './wiki-nav/page-utils';
|
import PageUtils from './wiki-nav/page-utils';
|
||||||
import NewFolderDialog from './wiki-nav/new-folder-dialog';
|
|
||||||
import AddNewPageDialog from './wiki-nav/add-new-page-dialog';
|
|
||||||
import WikiNavFooter from './wiki-nav/wiki-nav-footer';
|
|
||||||
import { generateUniqueId, isObjectNotEmpty } from './utils';
|
import { generateUniqueId, isObjectNotEmpty } from './utils';
|
||||||
import Folder from './models/folder';
|
|
||||||
import Page from './models/page';
|
import Page from './models/page';
|
||||||
import wikiAPI from '../../utils/wiki-api';
|
import wikiAPI from '../../utils/wiki-api';
|
||||||
import { FOLDER } from './constant';
|
|
||||||
import { Utils } from '../../utils/utils';
|
import { Utils } from '../../utils/utils';
|
||||||
import WikiExternalOperations from './wiki-external-operations';
|
import WikiExternalOperations from './wiki-external-operations';
|
||||||
|
|
||||||
@@ -37,10 +32,6 @@ class SidePanel extends Component {
|
|||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
|
||||||
isShowNewFolderDialog: false,
|
|
||||||
isShowAddNewPageDialog: false,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
confirmDeletePage = (pageId) => {
|
confirmDeletePage = (pageId) => {
|
||||||
@@ -50,7 +41,7 @@ class SidePanel extends Component {
|
|||||||
config.pages.splice(index, 1);
|
config.pages.splice(index, 1);
|
||||||
PageUtils.deletePage(navigation, pageId);
|
PageUtils.deletePage(navigation, pageId);
|
||||||
this.props.saveWikiConfig(config);
|
this.props.saveWikiConfig(config);
|
||||||
// TODO: To delete a page, do you need to delete all subpages at once (requires a new API)
|
// TODO: delete a page, then delete all subpages
|
||||||
wikiAPI.deleteWiki2Page(wikiId, pageId);
|
wikiAPI.deleteWiki2Page(wikiId, pageId);
|
||||||
if (config.pages.length > 0) {
|
if (config.pages.length > 0) {
|
||||||
this.props.setCurrentPage(config.pages[0].id);
|
this.props.setCurrentPage(config.pages[0].id);
|
||||||
@@ -72,7 +63,7 @@ class SidePanel extends Component {
|
|||||||
const navigation = config.navigation;
|
const navigation = config.navigation;
|
||||||
const pageId = generateUniqueId(navigation);
|
const pageId = generateUniqueId(navigation);
|
||||||
const newPage = new Page({ id: pageId, name, icon, path, docUuid });
|
const newPage = new Page({ id: pageId, name, icon, path, docUuid });
|
||||||
this.addPage(newPage, this.current_folder_id, successCallback, errorCallback, jumpToNewPage);
|
this.addPage(newPage, '', successCallback, errorCallback, jumpToNewPage);
|
||||||
};
|
};
|
||||||
|
|
||||||
duplicatePage = async (fromPageConfig, successCallback, errorCallback) => {
|
duplicatePage = async (fromPageConfig, successCallback, errorCallback) => {
|
||||||
@@ -102,240 +93,32 @@ class SidePanel extends Component {
|
|||||||
this.props.saveWikiConfig(config, onSuccess, errorCallback);
|
this.props.saveWikiConfig(config, onSuccess, errorCallback);
|
||||||
};
|
};
|
||||||
|
|
||||||
movePage = ({ moved_page_id, target_page_id, source_page_folder_id, target_page_folder_id, move_position }) => {
|
movePage = ({ moved_page_id, target_page_id, move_position }) => {
|
||||||
let config = deepCopy(this.props.config);
|
let config = deepCopy(this.props.config);
|
||||||
let { navigation } = config;
|
let { navigation } = config;
|
||||||
PageUtils.movePage(navigation, moved_page_id, target_page_id, source_page_folder_id, target_page_folder_id, move_position);
|
PageUtils.movePage(navigation, moved_page_id, target_page_id, move_position);
|
||||||
config.navigation = navigation;
|
config.navigation = navigation;
|
||||||
this.props.saveWikiConfig(config);
|
this.props.saveWikiConfig(config);
|
||||||
};
|
};
|
||||||
|
|
||||||
movePageOut = (moved_page_id, source_id, target_id, move_position) => {
|
renderWikiNav = () => {
|
||||||
let config = deepCopy(this.props.config);
|
|
||||||
let { navigation } = config;
|
|
||||||
PageUtils.movePageOut(navigation, moved_page_id, source_id, target_id, move_position);
|
|
||||||
config.navigation = navigation;
|
|
||||||
this.props.saveWikiConfig(config);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create a new folder in the root directory (not supported to create a new subfolder in the folder)
|
|
||||||
addPageFolder = (folder_data, parent_folder_id) => {
|
|
||||||
const { config } = this.props;
|
|
||||||
const { navigation } = config;
|
|
||||||
let folder_id = generateUniqueId(navigation);
|
|
||||||
let newFolder = new Folder({ id: folder_id, ...folder_data });
|
|
||||||
// No parent folder, directly add to the root directory
|
|
||||||
if (!parent_folder_id) {
|
|
||||||
config.navigation.push(newFolder);
|
|
||||||
} else { // Recursively find the parent folder and add
|
|
||||||
navigation.forEach(item => {
|
|
||||||
if (item.type === FOLDER) {
|
|
||||||
this._addFolder(item, newFolder, parent_folder_id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this.props.saveWikiConfig(config);
|
|
||||||
};
|
|
||||||
|
|
||||||
_addFolder(folder, newFolder, parent_folder_id) {
|
|
||||||
if (folder.id === parent_folder_id) {
|
|
||||||
folder.children.push(newFolder);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
folder.children.forEach(item => {
|
|
||||||
if (item.type === FOLDER) {
|
|
||||||
this._addFolder(item, newFolder, parent_folder_id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onModifyFolder = (folder_id, folder_data) => {
|
|
||||||
const { config } = this.props;
|
|
||||||
const { navigation } = config;
|
|
||||||
PageUtils.modifyFolder(navigation, folder_id, folder_data);
|
|
||||||
config.navigation = navigation;
|
|
||||||
this.props.saveWikiConfig(config);
|
|
||||||
};
|
|
||||||
|
|
||||||
onDeleteFolder = (page_folder_id) => {
|
|
||||||
const { config } = this.props;
|
|
||||||
const { navigation, pages } = config;
|
|
||||||
PageUtils.deleteFolder(navigation, pages, page_folder_id);
|
|
||||||
// TODO: delete all pages inside the folder, A new API is required
|
|
||||||
config.navigation = navigation;
|
|
||||||
this.props.saveWikiConfig(config);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Drag a folder to the front and back of another folder
|
|
||||||
onMoveFolder = (moved_folder_id, target_id, move_position) => {
|
|
||||||
const { config } = this.props;
|
|
||||||
const { navigation } = config;
|
|
||||||
let updatedNavigation = deepCopy(navigation);
|
|
||||||
|
|
||||||
// Get the moved folder first and delete the original location
|
|
||||||
let moved_folder;
|
|
||||||
let moved_folder_index = PageUtils.getFolderIndexById(updatedNavigation, moved_folder_id);
|
|
||||||
if (moved_folder_index === -1) {
|
|
||||||
updatedNavigation.forEach(item => {
|
|
||||||
if (item.type === FOLDER) {
|
|
||||||
moved_folder_index = PageUtils.getFolderIndexById(item.children, moved_folder_id);
|
|
||||||
if (moved_folder_index > -1) {
|
|
||||||
moved_folder = item.children[moved_folder_index];
|
|
||||||
item.children.splice(moved_folder_index, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
moved_folder = updatedNavigation[moved_folder_index];
|
|
||||||
updatedNavigation.splice(moved_folder_index, 1);
|
|
||||||
}
|
|
||||||
let indexOffset = 0;
|
|
||||||
if (move_position === 'move_below') {
|
|
||||||
indexOffset++;
|
|
||||||
}
|
|
||||||
// Get the location of the release
|
|
||||||
let target_folder_index = PageUtils.getFolderIndexById(updatedNavigation, target_id);
|
|
||||||
if (target_folder_index === -1) {
|
|
||||||
updatedNavigation.forEach(item => {
|
|
||||||
if (item.type === FOLDER) {
|
|
||||||
target_folder_index = PageUtils.getFolderIndexById(item.children, target_id);
|
|
||||||
if (target_folder_index > -1) {
|
|
||||||
item.children.splice(target_folder_index + indexOffset, 0, moved_folder);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// not changed
|
|
||||||
updatedNavigation = navigation;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
updatedNavigation.splice(target_folder_index + indexOffset, 0, moved_folder);
|
|
||||||
}
|
|
||||||
config.navigation = updatedNavigation;
|
|
||||||
this.props.saveWikiConfig(config);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Not support yet: Move a folder into another folder
|
|
||||||
moveFolderToFolder = (moved_folder_id, target_id) => {
|
|
||||||
let { config } = this.props;
|
|
||||||
let { navigation } = config;
|
|
||||||
|
|
||||||
// Find the folder and move it to this new folder
|
|
||||||
let target_folder = PageUtils.getFolderById(navigation, target_id);
|
|
||||||
if (!target_folder) {
|
|
||||||
toaster.danger('Only_support_two_level_folders');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let moved_folder;
|
|
||||||
let moved_folder_index = PageUtils.getFolderIndexById(navigation, moved_folder_id);
|
|
||||||
|
|
||||||
// The original directory is in the root directory
|
|
||||||
if (moved_folder_index > -1) {
|
|
||||||
moved_folder = PageUtils.getFolderById(navigation, moved_folder_id);
|
|
||||||
// If moved folder There are other directories under the ID, and dragging is not supported
|
|
||||||
if (moved_folder.children.some(item => item.type === FOLDER)) {
|
|
||||||
toaster.danger('Only_support_two_level_folders');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
target_folder.children.push(moved_folder);
|
|
||||||
navigation.splice(moved_folder_index, 1);
|
|
||||||
} else { // The original directory is not in the root directory
|
|
||||||
navigation.forEach(item => {
|
|
||||||
if (item.type === FOLDER) {
|
|
||||||
let moved_folder_index = PageUtils.getFolderIndexById(item.children, moved_folder_id);
|
|
||||||
if (moved_folder_index > -1) {
|
|
||||||
moved_folder = item.children[moved_folder_index];
|
|
||||||
target_folder.children.push(moved_folder);
|
|
||||||
item.children.splice(moved_folder_index, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
config.navigation = navigation;
|
|
||||||
this.props.saveWikiConfig(config);
|
|
||||||
};
|
|
||||||
|
|
||||||
onToggleAddFolder = () => {
|
|
||||||
this.setState({ isShowNewFolderDialog: !this.state.isShowNewFolderDialog });
|
|
||||||
};
|
|
||||||
|
|
||||||
openAddPageDialog = (folder_id) => {
|
|
||||||
this.current_folder_id = folder_id;
|
|
||||||
this.setState({ isShowAddNewPageDialog: true });
|
|
||||||
};
|
|
||||||
|
|
||||||
closeAddNewPageDialog = () => {
|
|
||||||
this.current_folder_id = null;
|
|
||||||
this.setState({ isShowAddNewPageDialog: false });
|
|
||||||
};
|
|
||||||
|
|
||||||
onSetFolderId = (folder_id) => {
|
|
||||||
this.current_folder_id = folder_id;
|
|
||||||
};
|
|
||||||
|
|
||||||
renderFolderView = () => {
|
|
||||||
const { config, onUpdatePage } = this.props;
|
const { config, onUpdatePage } = this.props;
|
||||||
const { pages, navigation } = config;
|
const { pages, navigation } = config;
|
||||||
return (
|
return (
|
||||||
<div className="wiki2-pages-container">
|
<div className="wiki2-pages-container">
|
||||||
<WikiNav
|
{isObjectNotEmpty(config) &&
|
||||||
isEditMode={isWiki2}
|
<WikiNav
|
||||||
navigation={navigation}
|
isEditMode={isWiki2}
|
||||||
pages={pages}
|
navigation={navigation}
|
||||||
onToggleAddPage={this.openAddPageDialog}
|
pages={pages}
|
||||||
onDeletePage={this.confirmDeletePage}
|
onDeletePage={this.confirmDeletePage}
|
||||||
onUpdatePage={onUpdatePage}
|
onUpdatePage={onUpdatePage}
|
||||||
setCurrentPage={this.props.setCurrentPage}
|
setCurrentPage={this.props.setCurrentPage}
|
||||||
onMovePage={this.movePage}
|
onMovePage={this.movePage}
|
||||||
movePageOut={this.movePageOut}
|
|
||||||
onToggleAddFolder={this.onToggleAddFolder}
|
|
||||||
onModifyFolder={this.onModifyFolder}
|
|
||||||
onDeleteFolder={this.onDeleteFolder}
|
|
||||||
onMoveFolder={this.onMoveFolder}
|
|
||||||
moveFolderToFolder={this.moveFolderToFolder}
|
|
||||||
onAddNewPage={this.onAddNewPage}
|
|
||||||
duplicatePage={this.duplicatePage}
|
|
||||||
onSetFolderId={this.onSetFolderId}
|
|
||||||
currentPageId={this.props.currentPageId}
|
|
||||||
addPageInside={this.addPageInside}
|
|
||||||
/>
|
|
||||||
{this.state.isShowNewFolderDialog &&
|
|
||||||
<NewFolderDialog
|
|
||||||
onAddFolder={this.addPageFolder}
|
|
||||||
onToggleAddFolderDialog={this.onToggleAddFolder}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
{this.state.isShowAddNewPageDialog &&
|
|
||||||
<AddNewPageDialog
|
|
||||||
toggle={this.closeAddNewPageDialog}
|
|
||||||
onAddNewPage={this.onAddNewPage}
|
onAddNewPage={this.onAddNewPage}
|
||||||
title={gettext('Add page')}
|
duplicatePage={this.duplicatePage}
|
||||||
/>
|
currentPageId={this.props.currentPageId}
|
||||||
}
|
addPageInside={this.addPageInside}
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
renderNoFolder = () => {
|
|
||||||
return (
|
|
||||||
<div className="wiki2-pages-container">
|
|
||||||
{isWiki2 &&
|
|
||||||
<WikiNavFooter
|
|
||||||
onToggleAddPage={this.openAddPageDialog}
|
|
||||||
onToggleAddFolder={this.onToggleAddFolder}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
{this.state.isShowNewFolderDialog &&
|
|
||||||
<NewFolderDialog
|
|
||||||
onAddFolder={this.addPageFolder}
|
|
||||||
onToggleAddFolderDialog={this.onToggleAddFolder}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
{this.state.isShowAddNewPageDialog &&
|
|
||||||
<AddNewPageDialog
|
|
||||||
toggle={this.closeAddNewPageDialog}
|
|
||||||
onAddNewPage={this.onAddNewPage}
|
|
||||||
title={gettext('Add page')}
|
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@@ -364,7 +147,7 @@ class SidePanel extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { isLoading, config } = this.props;
|
const { isLoading } = this.props;
|
||||||
return (
|
return (
|
||||||
<div className={`wiki2-side-panel${this.props.closeSideBar ? '' : ' left-zero'}`}>
|
<div className={`wiki2-side-panel${this.props.closeSideBar ? '' : ' left-zero'}`}>
|
||||||
<div className="wiki2-side-panel-top">
|
<div className="wiki2-side-panel-top">
|
||||||
@@ -377,7 +160,7 @@ class SidePanel extends Component {
|
|||||||
</UncontrolledTooltip>
|
</UncontrolledTooltip>
|
||||||
</div>
|
</div>
|
||||||
<div className="wiki2-side-nav">
|
<div className="wiki2-side-nav">
|
||||||
{isLoading ? <Loading /> : (isObjectNotEmpty(config) ? this.renderFolderView() : this.renderNoFolder())}
|
{isLoading ? <Loading /> : this.renderWikiNav()}
|
||||||
</div>
|
</div>
|
||||||
<WikiExternalOperations onAddWikiPage={this.handleAddNewPage.bind(false)} />
|
<WikiExternalOperations onAddWikiPage={this.handleAddNewPage.bind(false)} />
|
||||||
</div>
|
</div>
|
||||||
|
@@ -46,7 +46,7 @@ function WikiTopNav({ config, currentPageId }) {
|
|||||||
return (
|
return (
|
||||||
<Fragment key={item.id}>
|
<Fragment key={item.id}>
|
||||||
<div className='wiki2-top-nav-item d-flex'>
|
<div className='wiki2-top-nav-item d-flex'>
|
||||||
<NavItemIcon symbol={item.type === 'folder' ? 'wiki-folder' : 'file'} disable={true} />
|
<NavItemIcon symbol={'file'} disable={true} />
|
||||||
{item.name}
|
{item.name}
|
||||||
</div>
|
</div>
|
||||||
{index !== paths.length - 1 && <div>/</div>}
|
{index !== paths.length - 1 && <div>/</div>}
|
||||||
|
@@ -1,52 +0,0 @@
|
|||||||
import React, { Component } from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { Dropdown, DropdownMenu, DropdownItem, DropdownToggle } from 'reactstrap';
|
|
||||||
import { gettext } from '../../../utils/constants';
|
|
||||||
|
|
||||||
class AddPageDropdownMenu extends Component {
|
|
||||||
|
|
||||||
toggle = event => {
|
|
||||||
this.onStopPropagation(event);
|
|
||||||
this.props.toggleDropdown();
|
|
||||||
};
|
|
||||||
|
|
||||||
addPage = event => {
|
|
||||||
this.onStopPropagation(event);
|
|
||||||
this.props.onToggleAddPage(null);
|
|
||||||
};
|
|
||||||
|
|
||||||
onToggleAddFolder = event => {
|
|
||||||
this.onStopPropagation(event);
|
|
||||||
this.props.onToggleAddFolder();
|
|
||||||
};
|
|
||||||
|
|
||||||
onStopPropagation = event => {
|
|
||||||
event && event.nativeEvent && event.nativeEvent.stopImmediatePropagation();
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<Dropdown isOpen toggle={this.toggle}>
|
|
||||||
<DropdownToggle caret></DropdownToggle>
|
|
||||||
<DropdownMenu container="body" className='dtable-dropdown-menu large mt-0'>
|
|
||||||
<DropdownItem onClick={this.addPage}>
|
|
||||||
<i className="sf3-font sf3-font-file" />
|
|
||||||
<span className='item-text'>{gettext('Add page')}</span>
|
|
||||||
</DropdownItem>
|
|
||||||
<DropdownItem onClick={this.onToggleAddFolder}>
|
|
||||||
<i className="sf3-font sf3-font-folder" />
|
|
||||||
<span className='item-text'>{gettext('Add folder')}</span>
|
|
||||||
</DropdownItem>
|
|
||||||
</DropdownMenu>
|
|
||||||
</Dropdown>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AddPageDropdownMenu.propTypes = {
|
|
||||||
toggleDropdown: PropTypes.func,
|
|
||||||
onToggleAddPage: PropTypes.func,
|
|
||||||
onToggleAddFolder: PropTypes.func,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AddPageDropdownMenu;
|
|
@@ -1,2 +0,0 @@
|
|||||||
export const DRAGGED_FOLDER_MODE = 'wiki-folder';
|
|
||||||
export const DRAGGED_PAGE_MODE = 'wiki-page';
|
|
@@ -1,103 +0,0 @@
|
|||||||
import { DragSource, DropTarget } from 'react-dnd';
|
|
||||||
import { DRAGGED_FOLDER_MODE, DRAGGED_PAGE_MODE } from '../constant';
|
|
||||||
import FolderItem from './folder-item';
|
|
||||||
|
|
||||||
const dragSource = {
|
|
||||||
beginDrag(props, monitor) {
|
|
||||||
const { folderIndex, folder } = props;
|
|
||||||
return {
|
|
||||||
idx: folderIndex,
|
|
||||||
data: folder,
|
|
||||||
mode: DRAGGED_FOLDER_MODE,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
endDrag(props, monitor) {
|
|
||||||
const sourceRow = monitor.getItem();
|
|
||||||
const didDrop = monitor.didDrop();
|
|
||||||
let targetRow = {};
|
|
||||||
if (!didDrop) {
|
|
||||||
return { sourceRow, targetRow };
|
|
||||||
}
|
|
||||||
},
|
|
||||||
isDragging(props, monitor) {
|
|
||||||
const { folderIndex: currentIndex, draggedPage } = props;
|
|
||||||
const { idx } = draggedPage;
|
|
||||||
return idx > currentIndex;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const dropTarget = {
|
|
||||||
drop(props, monitor) {
|
|
||||||
const sourceRow = monitor.getItem();
|
|
||||||
const { folder: targetFolder } = props;
|
|
||||||
const targetFolderId = targetFolder.id;
|
|
||||||
const className = props.getClassName();
|
|
||||||
if (!className) return;
|
|
||||||
let move_position;
|
|
||||||
if (className.includes('can-drop')) {
|
|
||||||
move_position = 'move_below';
|
|
||||||
}
|
|
||||||
if (className.includes('can-drop-top')) {
|
|
||||||
move_position = 'move_above';
|
|
||||||
}
|
|
||||||
let moveInto = className.includes('dragged-page-over');
|
|
||||||
|
|
||||||
// 1. drag source is page
|
|
||||||
if (sourceRow.mode === DRAGGED_PAGE_MODE) {
|
|
||||||
const sourceFolderId = sourceRow.folderId;
|
|
||||||
const draggedPageId = sourceRow.data.id;
|
|
||||||
// 1.1 move page into folder
|
|
||||||
if (moveInto) {
|
|
||||||
props.onMovePage({
|
|
||||||
moved_page_id: draggedPageId,
|
|
||||||
target_page_id: null,
|
|
||||||
source_page_folder_id: sourceFolderId,
|
|
||||||
target_page_folder_id: targetFolderId,
|
|
||||||
move_position,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
} else { // 1.2 Drag the page above or below the folder
|
|
||||||
props.movePageOut(draggedPageId, sourceFolderId, targetFolderId, move_position);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 2. drag source is folder
|
|
||||||
if (sourceRow.mode === DRAGGED_FOLDER_MODE) {
|
|
||||||
const draggedFolderId = sourceRow.data.id;
|
|
||||||
// 2.0 If dragged folder and target folder are the same folder, return
|
|
||||||
if (targetFolderId === draggedFolderId) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 2.1 Do not support drag folder into another folder
|
|
||||||
if (moveInto) {
|
|
||||||
// props.moveFolderToFolder(draggedFolderId, targetFolderId);
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
// 2.2 Drag folder above or below another folder
|
|
||||||
props.onMoveFolder(draggedFolderId, targetFolderId, move_position);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 3. Drag other dom
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const dragCollect = (connect, monitor) => ({
|
|
||||||
connectDragSource: connect.dragSource(),
|
|
||||||
connectDragPreview: connect.dragPreview(),
|
|
||||||
isDragging: monitor.isDragging(),
|
|
||||||
});
|
|
||||||
|
|
||||||
const dropCollect = (connect, monitor) => ({
|
|
||||||
connectDropTarget: connect.dropTarget(),
|
|
||||||
isOver: monitor.isOver(),
|
|
||||||
canDrop: monitor.canDrop(),
|
|
||||||
draggedPage: monitor.getItem(),
|
|
||||||
connect,
|
|
||||||
monitor,
|
|
||||||
});
|
|
||||||
|
|
||||||
export default DropTarget('WikiNav', dropTarget, dropCollect)(
|
|
||||||
DragSource('WikiNav', dragSource, dragCollect)(FolderItem)
|
|
||||||
);
|
|
@@ -1,284 +0,0 @@
|
|||||||
import React, { Component } from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import classnames from 'classnames';
|
|
||||||
import FolderOperationDropdownMenu from './folder-operation-dropdownmenu';
|
|
||||||
import DraggedPageItem from '../pages/dragged-page-item';
|
|
||||||
import DraggedFolderItem from './dragged-folder-item';
|
|
||||||
import NameEditPopover from '../../common/name-edit-popover';
|
|
||||||
import NavItemIcon from '../../common/nav-item-icon';
|
|
||||||
|
|
||||||
class FolderItem extends Component {
|
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
const { name, icon } = props.folder;
|
|
||||||
this.state = {
|
|
||||||
isEditing: false,
|
|
||||||
icon: icon || '',
|
|
||||||
name: name || '',
|
|
||||||
isMouseEnter: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleExpand = (e) => {
|
|
||||||
e.nativeEvent.stopImmediatePropagation();
|
|
||||||
this.props.toggleExpand(this.props.folder.id);
|
|
||||||
this.forceUpdate();
|
|
||||||
};
|
|
||||||
|
|
||||||
onClickFolderChildren = (e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
e.nativeEvent.stopImmediatePropagation();
|
|
||||||
};
|
|
||||||
|
|
||||||
openFolderEditor = () => {
|
|
||||||
this.setState({ isEditing: true });
|
|
||||||
};
|
|
||||||
|
|
||||||
closeFolderEditor = () => {
|
|
||||||
if (this.state.isEditing) {
|
|
||||||
const { name, icon } = this.state;
|
|
||||||
this.props.onModifyFolder(this.props.folder.id, { name, icon });
|
|
||||||
this.setState({ isEditing: false });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
onChangeName = (name) => {
|
|
||||||
this.setState({ name });
|
|
||||||
};
|
|
||||||
|
|
||||||
onChangeIcon = (icon) => {
|
|
||||||
this.setState({ icon });
|
|
||||||
};
|
|
||||||
|
|
||||||
changeItemFreeze = (isFreeze) => {
|
|
||||||
this.isFreeze = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
renderFolder = (folder, index, pagesLength, isOnlyOnePage, id_page_map) => {
|
|
||||||
const { isEditMode, pages, pathStr } = this.props;
|
|
||||||
const { id: folderId } = folder;
|
|
||||||
return (
|
|
||||||
<DraggedFolderItem
|
|
||||||
key={`page-folder-${folderId}`}
|
|
||||||
isEditMode={isEditMode}
|
|
||||||
folder={folder}
|
|
||||||
folderIndex={index}
|
|
||||||
pagesLength={pagesLength}
|
|
||||||
isOnlyOnePage={isOnlyOnePage}
|
|
||||||
id_page_map={id_page_map}
|
|
||||||
renderFolderMenuItems={this.props.renderFolderMenuItems}
|
|
||||||
toggleExpand={this.props.toggleExpand}
|
|
||||||
onToggleAddPage={this.props.onToggleAddPage}
|
|
||||||
onModifyFolder={this.props.onModifyFolder}
|
|
||||||
onDeleteFolder={this.props.onDeleteFolder}
|
|
||||||
onMoveFolder={this.props.onMoveFolder}
|
|
||||||
setCurrentPage={this.props.setCurrentPage}
|
|
||||||
onUpdatePage={this.props.onUpdatePage}
|
|
||||||
duplicatePage={this.props.duplicatePage}
|
|
||||||
onSetFolderId={this.props.onSetFolderId}
|
|
||||||
onDeletePage={this.props.onDeletePage}
|
|
||||||
onMovePageToFolder={this.props.onMovePageToFolder}
|
|
||||||
onMovePage={this.props.onMovePage}
|
|
||||||
moveFolderToFolder={this.props.moveFolderToFolder}
|
|
||||||
pages={pages}
|
|
||||||
pathStr={pathStr + '-' + folderId}
|
|
||||||
setClassName={this.props.setClassName}
|
|
||||||
getClassName={this.props.getClassName}
|
|
||||||
movePageOut={this.props.movePageOut}
|
|
||||||
layerDragProps={this.props.layerDragProps}
|
|
||||||
getFoldState={this.props.getFoldState}
|
|
||||||
currentPageId={this.props.currentPageId}
|
|
||||||
addPageInside={this.props.addPageInside}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
renderPage = (page, index, pagesLength, isOnlyOnePage) => {
|
|
||||||
const { isEditMode, pages, folder, pathStr } = this.props;
|
|
||||||
const id = page.id;
|
|
||||||
if (!pages.find(item => item.id === id)) return;
|
|
||||||
return (
|
|
||||||
<DraggedPageItem
|
|
||||||
key={id}
|
|
||||||
pagesLength={pagesLength}
|
|
||||||
isOnlyOnePage={isOnlyOnePage}
|
|
||||||
infolder={false}
|
|
||||||
page={Object.assign({}, pages.find(item => item.id === id), page)}
|
|
||||||
pageIndex={index}
|
|
||||||
folderId={folder.id}
|
|
||||||
isEditMode={isEditMode}
|
|
||||||
renderFolderMenuItems={this.props.renderFolderMenuItems}
|
|
||||||
duplicatePage={this.props.duplicatePage}
|
|
||||||
onSetFolderId={this.props.onSetFolderId}
|
|
||||||
setCurrentPage={this.props.setCurrentPage}
|
|
||||||
onUpdatePage={this.props.onUpdatePage}
|
|
||||||
onDeletePage={this.props.onDeletePage}
|
|
||||||
onMovePageToFolder={(targetFolderId) => {
|
|
||||||
this.props.onMovePageToFolder(folder.id, page.id, targetFolderId);
|
|
||||||
}}
|
|
||||||
onMovePage={this.props.onMovePage}
|
|
||||||
onMoveFolder={this.props.onMoveFolder}
|
|
||||||
pages={pages}
|
|
||||||
pathStr={pathStr + '-' + page.id}
|
|
||||||
currentPageId={this.props.currentPageId}
|
|
||||||
addPageInside={this.props.addPageInside}
|
|
||||||
getFoldState={this.props.getFoldState}
|
|
||||||
toggleExpand={this.props.toggleExpand}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
getFolderClassName = (layerDragProps, state) => {
|
|
||||||
if (!state || ! layerDragProps || !layerDragProps.clientOffset) {
|
|
||||||
return 'page-folder-wrapper';
|
|
||||||
}
|
|
||||||
let y = layerDragProps.clientOffset.y;
|
|
||||||
let top = this.foldWrapprRef.getBoundingClientRect().y;
|
|
||||||
let className = '';
|
|
||||||
// middle
|
|
||||||
if (top + 10 < y && y < top + 30) {
|
|
||||||
className += ' dragged-page-over ';
|
|
||||||
}
|
|
||||||
// top
|
|
||||||
if (top + 10 > y) {
|
|
||||||
className += ' can-drop-top ';
|
|
||||||
}
|
|
||||||
// bottom
|
|
||||||
if (top + 30 < y) {
|
|
||||||
className += ' can-drop ';
|
|
||||||
}
|
|
||||||
this.props.setClassName(className);
|
|
||||||
return className + 'page-folder-wrapper';
|
|
||||||
};
|
|
||||||
|
|
||||||
getFolderChildrenHeight = () => {
|
|
||||||
const folded = this.props.getFoldState(this.props.folder.id);
|
|
||||||
if (folded) return 0;
|
|
||||||
return 'auto';
|
|
||||||
};
|
|
||||||
|
|
||||||
onMouseEnter = () => {
|
|
||||||
this.setState({ isMouseEnter: true });
|
|
||||||
};
|
|
||||||
|
|
||||||
onMouseLeave = () => {
|
|
||||||
this.setState({ isMouseEnter: false });
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
connectDropTarget, connectDragPreview, connectDragSource, isOver, canDrop,
|
|
||||||
isEditMode, folder, pagesLength, id_page_map, isOnlyOnePage, layerDragProps,
|
|
||||||
} = this.props;
|
|
||||||
const { isEditing } = this.state;
|
|
||||||
const { id: folderId, name, children } = folder;
|
|
||||||
const folded = this.props.getFoldState(folderId);
|
|
||||||
let navItemId = `folder-item-${folderId}`;
|
|
||||||
let fn = isEditMode ? connectDragSource : (argu) => {argu;};
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={classnames('page-folder', { 'readonly': !isEditMode })}
|
|
||||||
ref={ref => this.foldRef = ref}
|
|
||||||
onClick={this.toggleExpand}
|
|
||||||
>
|
|
||||||
{fn(connectDropTarget(
|
|
||||||
connectDragPreview(
|
|
||||||
<div
|
|
||||||
className={this.getFolderClassName(layerDragProps, isOver && canDrop)}
|
|
||||||
ref={ref => this.foldWrapprRef = ref}
|
|
||||||
onMouseEnter={this.onMouseEnter}
|
|
||||||
onMouseLeave={this.onMouseLeave}
|
|
||||||
>
|
|
||||||
<div className='folder-main'>
|
|
||||||
<div
|
|
||||||
className='folder-content'
|
|
||||||
style={{ marginLeft: `${(this.props.pathStr.split('-').length - 1) * 16}px` }}
|
|
||||||
id={navItemId}
|
|
||||||
>
|
|
||||||
{this.state.isMouseEnter ?
|
|
||||||
<div className='nav-item-icon'>
|
|
||||||
<i className={`sf3-font-down sf3-font ${folded ? 'rotate-270' : ''}`}></i>
|
|
||||||
</div>
|
|
||||||
:
|
|
||||||
<NavItemIcon symbol={'wiki-folder'} disable={true} />
|
|
||||||
}
|
|
||||||
<span className='folder-name text-truncate' title={name}>{name}</span>
|
|
||||||
{isEditing &&
|
|
||||||
<NameEditPopover
|
|
||||||
oldName={this.state.name}
|
|
||||||
targetId={navItemId}
|
|
||||||
onChangeName={this.onChangeName}
|
|
||||||
toggleEditor={this.closeFolderEditor}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{isEditMode &&
|
|
||||||
<FolderOperationDropdownMenu
|
|
||||||
changeItemFreeze={this.changeItemFreeze}
|
|
||||||
openFolderEditor={this.openFolderEditor}
|
|
||||||
onDeleteFolder={this.props.onDeleteFolder}
|
|
||||||
onToggleAddPage={this.props.onToggleAddPage}
|
|
||||||
folderId={folderId}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
))
|
|
||||||
}
|
|
||||||
<div
|
|
||||||
className="page-folder-children"
|
|
||||||
style={{ height: this.getFolderChildrenHeight() }}
|
|
||||||
onClick={this.onClickFolderChildren}
|
|
||||||
>
|
|
||||||
{!folded && children &&
|
|
||||||
children.map((item, index) => {
|
|
||||||
return item.type === 'folder' ? this.renderFolder(item, index, pagesLength, isOnlyOnePage, id_page_map) : this.renderPage(item, index, pagesLength, isOnlyOnePage);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FolderItem.propTypes = {
|
|
||||||
isEditMode: PropTypes.bool,
|
|
||||||
folder: PropTypes.object,
|
|
||||||
folderIndex: PropTypes.number,
|
|
||||||
pagesLength: PropTypes.number,
|
|
||||||
id_page_map: PropTypes.object,
|
|
||||||
isOver: PropTypes.bool,
|
|
||||||
canDrop: PropTypes.bool,
|
|
||||||
isDragging: PropTypes.bool,
|
|
||||||
connectDropTarget: PropTypes.func,
|
|
||||||
connectDragPreview: PropTypes.func,
|
|
||||||
connectDragSource: PropTypes.func,
|
|
||||||
renderFolderMenuItems: PropTypes.func,
|
|
||||||
duplicatePage: PropTypes.func,
|
|
||||||
onSetFolderId: PropTypes.func,
|
|
||||||
toggleExpand: PropTypes.func,
|
|
||||||
onToggleAddPage: PropTypes.func,
|
|
||||||
onModifyFolder: PropTypes.func,
|
|
||||||
onDeleteFolder: PropTypes.func,
|
|
||||||
setCurrentPage: PropTypes.func,
|
|
||||||
onUpdatePage: PropTypes.func,
|
|
||||||
onDeletePage: PropTypes.func,
|
|
||||||
onMovePageToFolder: PropTypes.func,
|
|
||||||
onMovePage: PropTypes.func,
|
|
||||||
isOnlyOnePage: PropTypes.bool,
|
|
||||||
pages: PropTypes.array,
|
|
||||||
onMoveFolder: PropTypes.func,
|
|
||||||
moveFolderToFolder: PropTypes.func,
|
|
||||||
pathStr: PropTypes.string,
|
|
||||||
setClassName: PropTypes.func,
|
|
||||||
getClassName: PropTypes.func,
|
|
||||||
movePageOut: PropTypes.func,
|
|
||||||
layerDragProps: PropTypes.object,
|
|
||||||
getFoldState: PropTypes.func,
|
|
||||||
currentPageId: PropTypes.string,
|
|
||||||
addPageInside: PropTypes.func,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default FolderItem;
|
|
@@ -1,75 +0,0 @@
|
|||||||
import React, { Component } from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { Dropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap';
|
|
||||||
import { gettext } from '../../../../utils/constants';
|
|
||||||
import Icon from '../../../../components/icon';
|
|
||||||
|
|
||||||
export default class FolderOperationDropdownMenu extends Component {
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
changeItemFreeze: PropTypes.func,
|
|
||||||
openFolderEditor: PropTypes.func,
|
|
||||||
onDeleteFolder: PropTypes.func,
|
|
||||||
onToggleAddPage: PropTypes.func,
|
|
||||||
folderId: PropTypes.string,
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
isMenuShow: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
onDropdownToggle = (e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
const isMenuShow = !this.state.isMenuShow;
|
|
||||||
this.props.changeItemFreeze(isMenuShow);
|
|
||||||
this.setState({ isMenuShow });
|
|
||||||
};
|
|
||||||
|
|
||||||
openFolderEditor = (evt) => {
|
|
||||||
evt.nativeEvent.stopImmediatePropagation();
|
|
||||||
this.props.openFolderEditor();
|
|
||||||
};
|
|
||||||
|
|
||||||
onDeleteFolder = (evt) => {
|
|
||||||
evt.nativeEvent.stopImmediatePropagation();
|
|
||||||
this.props.onDeleteFolder(this.props.folderId);
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Dropdown
|
|
||||||
isOpen={this.state.isMenuShow}
|
|
||||||
toggle={this.onDropdownToggle}
|
|
||||||
className="folder-operation-dropdownmenu"
|
|
||||||
>
|
|
||||||
<DropdownToggle tag="span" data-toggle="dropdown" aria-expanded={this.state.isMenuShow}>
|
|
||||||
<Icon symbol={'more-level'}/>
|
|
||||||
</DropdownToggle>
|
|
||||||
<DropdownMenu
|
|
||||||
className="dtable-dropdown-menu large"
|
|
||||||
flip={false}
|
|
||||||
modifiers={{ preventOverflow: { boundariesElement: document.body } }}
|
|
||||||
positionFixed={true}
|
|
||||||
>
|
|
||||||
<DropdownItem onClick={this.props.onToggleAddPage.bind(this, this.props.folderId)}>
|
|
||||||
<i className="sf3-font sf3-font-file" />
|
|
||||||
<span className="item-text">{gettext('Add page')}</span>
|
|
||||||
</DropdownItem>
|
|
||||||
<DropdownItem onClick={this.openFolderEditor}>
|
|
||||||
<i className="sf3-font sf3-font-rename" />
|
|
||||||
<span className="item-text">{gettext('Modify name')}</span>
|
|
||||||
</DropdownItem>
|
|
||||||
<DropdownItem onMouseDown={this.onDeleteFolder}>
|
|
||||||
<i className="sf3-font sf3-font-delete1" />
|
|
||||||
<span className="item-text">{gettext('Delete folder')}</span>
|
|
||||||
</DropdownItem>
|
|
||||||
</DropdownMenu>
|
|
||||||
</Dropdown>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,98 +0,0 @@
|
|||||||
import React, { Component } from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { Modal, ModalHeader, ModalBody, ModalFooter, Form, FormGroup, Label, Input, Button, Alert } from 'reactstrap';
|
|
||||||
import { gettext } from '../../../utils/constants';
|
|
||||||
|
|
||||||
export default class NewFolderDialog extends Component {
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
onAddFolder: PropTypes.func,
|
|
||||||
onToggleAddFolderDialog: PropTypes.func,
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
folderName: '',
|
|
||||||
errMessage: '',
|
|
||||||
iconClassName: '',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
document.addEventListener('keydown', this.onHotKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
document.removeEventListener('keydown', this.onHotKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
onHotKey = (e) => {
|
|
||||||
if (e.keyCode === 13) {
|
|
||||||
e.preventDefault();
|
|
||||||
this.handleSubmit();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
handleChange = (event) => {
|
|
||||||
let { folderName } = this.state;
|
|
||||||
let value = event.target.value;
|
|
||||||
if (value === folderName) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.setState({ folderName: value });
|
|
||||||
};
|
|
||||||
|
|
||||||
handleSubmit = () => {
|
|
||||||
let { folderName, iconClassName } = this.state;
|
|
||||||
if (!folderName) {
|
|
||||||
this.setState({ errMessage: gettext('Name_is_required') });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.props.onAddFolder({ name: folderName, icon: iconClassName });
|
|
||||||
this.props.onToggleAddFolderDialog();
|
|
||||||
};
|
|
||||||
|
|
||||||
toggle = () => {
|
|
||||||
this.props.onToggleAddFolderDialog();
|
|
||||||
};
|
|
||||||
|
|
||||||
onIconChange = (className) => {
|
|
||||||
this.setState({ iconClassName: className });
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { folderName, errMessage } = this.state;
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
isOpen={true}
|
|
||||||
toggle={this.toggle}
|
|
||||||
autoFocus={false}
|
|
||||||
className="new-folder-dialog"
|
|
||||||
>
|
|
||||||
<ModalHeader toggle={this.toggle}>{gettext('New folder')}</ModalHeader>
|
|
||||||
<ModalBody>
|
|
||||||
<Form>
|
|
||||||
<FormGroup>
|
|
||||||
<Label for="folderName">{gettext('Name')}</Label>
|
|
||||||
<Input
|
|
||||||
id="folderName"
|
|
||||||
value={folderName}
|
|
||||||
innerRef={input => {
|
|
||||||
this.newInput = input;
|
|
||||||
}}
|
|
||||||
onChange={this.handleChange}
|
|
||||||
autoFocus={true}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
</Form>
|
|
||||||
{errMessage && <Alert color="danger" className="mt-2">{errMessage}</Alert>}
|
|
||||||
</ModalBody>
|
|
||||||
<ModalFooter>
|
|
||||||
<Button color="secondary" onClick={this.toggle}>{gettext('Cancel')}</Button>
|
|
||||||
<Button color="primary" onClick={this.handleSubmit}>{gettext('Submit')}</Button>
|
|
||||||
</ModalFooter>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,10 +1,16 @@
|
|||||||
import { FOLDER, PAGE } from '../constant';
|
class NewPage {
|
||||||
|
constructor(id) {
|
||||||
|
this.id = id;
|
||||||
|
this.type = 'page';
|
||||||
|
this.children = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default class PageUtils {
|
export default class PageUtils {
|
||||||
|
|
||||||
static addPage(navigation, page_id, parentId) {
|
static addPage(navigation, page_id, parentId) {
|
||||||
if (!parentId) {
|
if (!parentId) {
|
||||||
navigation.push({ id: page_id, type: PAGE });
|
navigation.push(new NewPage(page_id));
|
||||||
} else {
|
} else {
|
||||||
navigation.forEach(item => {
|
navigation.forEach(item => {
|
||||||
this._addPageRecursion(page_id, item, parentId);
|
this._addPageRecursion(page_id, item, parentId);
|
||||||
@@ -17,10 +23,10 @@ export default class PageUtils {
|
|||||||
item.children = [];
|
item.children = [];
|
||||||
}
|
}
|
||||||
if (item.id === parentId) {
|
if (item.id === parentId) {
|
||||||
item.children.push({ id: page_id, type: PAGE });
|
item.children.push(new NewPage(page_id));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
item.children.forEach(item => {
|
item.children && item.children.forEach(item => {
|
||||||
this._addPageRecursion(page_id, item, parentId);
|
this._addPageRecursion(page_id, item, parentId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -43,7 +49,7 @@ export default class PageUtils {
|
|||||||
item.children.splice(pageIndex, 1);
|
item.children.splice(pageIndex, 1);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
item.children.forEach(item => {
|
item.children && item.children.forEach(item => {
|
||||||
this._deletePageRecursion(item, page_id);
|
this._deletePageRecursion(item, page_id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -57,76 +63,7 @@ export default class PageUtils {
|
|||||||
return pages.findIndex(page => page.id === pageId);
|
return pages.findIndex(page => page.id === pageId);
|
||||||
};
|
};
|
||||||
|
|
||||||
static getFolderById = (list, folder_id) => {
|
|
||||||
if (!folder_id || !Array.isArray(list)) return null;
|
|
||||||
return list.find(item => item.type === FOLDER && item.id === folder_id);
|
|
||||||
};
|
|
||||||
|
|
||||||
static getFolderIndexById = (list, folder_id) => {
|
|
||||||
if (!folder_id || !Array.isArray(list)) return -1;
|
|
||||||
return list.findIndex(folder => folder.id === folder_id);
|
|
||||||
};
|
|
||||||
|
|
||||||
static modifyFolder(navigation, folder_id, folder_data) {
|
|
||||||
navigation.forEach(item => {
|
|
||||||
if (item.type === FOLDER) {
|
|
||||||
this._modifyFolder(item, folder_id, folder_data);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static _modifyFolder(folder, folder_id, folder_data) {
|
|
||||||
if (folder.id === folder_id) {
|
|
||||||
for (let key in folder_data) {
|
|
||||||
folder[key] = folder_data[key];
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
folder.children.forEach(item => {
|
|
||||||
if (item.type === FOLDER) {
|
|
||||||
this._modifyFolder(item, folder_id, folder_data);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static deleteFolder(navigation, pages, folder_id) {
|
|
||||||
// delete folder and pages within it
|
|
||||||
const folderIndex = this.getFolderIndexById(navigation, folder_id);
|
|
||||||
if (folderIndex > -1) {
|
|
||||||
this._deletePagesInFolder(pages, navigation[folderIndex]);
|
|
||||||
navigation.splice(folderIndex, 1);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// delete subfolder and pages within it
|
|
||||||
navigation.forEach(item => {
|
|
||||||
if (item.type === FOLDER) {
|
|
||||||
const folderIndex = this.getFolderIndexById(item.children, folder_id);
|
|
||||||
if (folderIndex > -1) {
|
|
||||||
const subfolder = item.children[folderIndex];
|
|
||||||
this._deletePagesInFolder(pages, subfolder);
|
|
||||||
item.children.splice(folderIndex, 1);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static _deletePagesInFolder(pages, folder) {
|
|
||||||
folder.children.forEach(item => {
|
|
||||||
if (item.type === FOLDER) {
|
|
||||||
this._deletePagesInFolder(pages, item);
|
|
||||||
}
|
|
||||||
if (item.type === PAGE) {
|
|
||||||
let index = this.getPageIndexById(item.id, pages);
|
|
||||||
pages.splice(index, 1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static insertPage(navigation, page_id, target_page_id, target_id, move_position) {
|
static insertPage(navigation, page_id, target_page_id, target_id, move_position) {
|
||||||
// 1. No folder, insert page in root directory
|
|
||||||
if (!target_id) {
|
if (!target_id) {
|
||||||
let insertIndex = target_page_id ? navigation.findIndex(item => item.id === target_page_id) : -1;
|
let insertIndex = target_page_id ? navigation.findIndex(item => item.id === target_page_id) : -1;
|
||||||
if (insertIndex < 0) {
|
if (insertIndex < 0) {
|
||||||
@@ -136,34 +73,12 @@ export default class PageUtils {
|
|||||||
if (move_position === 'move_below') {
|
if (move_position === 'move_below') {
|
||||||
insertIndex++;
|
insertIndex++;
|
||||||
}
|
}
|
||||||
navigation.splice(insertIndex, 0, { id: page_id, type: PAGE });
|
navigation.splice(insertIndex, 0, new NewPage(page_id));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 2. If there is a folder, find it and insert it
|
|
||||||
navigation.forEach(item => {
|
|
||||||
if (item.type === FOLDER) {
|
|
||||||
this._insertPageIntoFolder(item, page_id, target_page_id, target_id, move_position);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static _insertPageIntoFolder(folder, page_id, target_page_id, target_id, move_position) {
|
static movePage(navigation, moved_page_id, target_page_id, move_position) {
|
||||||
if (folder.id === target_id) {
|
|
||||||
let insertIndex = target_page_id ? folder.children.findIndex(item => item.id === target_page_id) : -1;
|
|
||||||
if (move_position === 'move_below') {
|
|
||||||
insertIndex++;
|
|
||||||
}
|
|
||||||
folder.children.splice(insertIndex, 0, { id: page_id, type: PAGE });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
folder.children.forEach(item => {
|
|
||||||
if (item.type === FOLDER) {
|
|
||||||
this._insertPageIntoFolder(item, page_id, target_page_id, target_id, move_position);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static movePage(navigation, moved_page_id, target_page_id, source_id, target_id, move_position) {
|
|
||||||
let movedPage = null;
|
let movedPage = null;
|
||||||
function _cutPageRecursion(item, page_id) {
|
function _cutPageRecursion(item, page_id) {
|
||||||
if (!item || !Array.isArray(item.children) || movedPage) return;
|
if (!item || !Array.isArray(item.children) || movedPage) return;
|
||||||
@@ -171,7 +86,7 @@ export default class PageUtils {
|
|||||||
if (pageIndex > -1) {
|
if (pageIndex > -1) {
|
||||||
movedPage = item.children.splice(pageIndex, 1)[0];
|
movedPage = item.children.splice(pageIndex, 1)[0];
|
||||||
} else {
|
} else {
|
||||||
item.children.forEach(item => {
|
item.children && item.children.forEach(item => {
|
||||||
_cutPageRecursion(item, page_id);
|
_cutPageRecursion(item, page_id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -195,7 +110,7 @@ export default class PageUtils {
|
|||||||
item.children.splice(insertIndex, 0, movedPage);
|
item.children.splice(insertIndex, 0, movedPage);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
item.children.forEach(item => {
|
item.children && item.children.forEach(item => {
|
||||||
_insertPageRecursion(item, page_id, target_page_id, target_id, move_position);
|
_insertPageRecursion(item, page_id, target_page_id, target_id, move_position);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -217,58 +132,6 @@ export default class PageUtils {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
_cutPage(navigation, moved_page_id);
|
_cutPage(navigation, moved_page_id);
|
||||||
_insertPage(navigation, moved_page_id, target_page_id, target_id, move_position);
|
_insertPage(navigation, moved_page_id, target_page_id, target_page_id, move_position);
|
||||||
}
|
|
||||||
|
|
||||||
// move Page Outside Folder
|
|
||||||
static movePageOut(navigation, moved_page_id, source_id, target_id, move_position) {
|
|
||||||
let movedPage = null;
|
|
||||||
function getFolderIndexById(list, folder_id) {
|
|
||||||
if (!folder_id || !Array.isArray(list)) return -1;
|
|
||||||
return list.findIndex(folder => folder.id === folder_id);
|
|
||||||
}
|
|
||||||
// Move the page to the top or bottom of the folder
|
|
||||||
function _insertPage(navigation, page_id, target_id, move_position) {
|
|
||||||
let indexOffset = 0;
|
|
||||||
if (move_position === 'move_below') {
|
|
||||||
indexOffset++;
|
|
||||||
}
|
|
||||||
let folder_index = getFolderIndexById(navigation, target_id);
|
|
||||||
if (folder_index > -1) {
|
|
||||||
navigation.splice(folder_index + indexOffset, 0, movedPage);
|
|
||||||
} else {
|
|
||||||
navigation.forEach((item) => {
|
|
||||||
if (item.type === FOLDER) {
|
|
||||||
let folder_index = getFolderIndexById(item.children, target_id);
|
|
||||||
if (folder_index > -1) {
|
|
||||||
item.children.splice(folder_index + indexOffset, 0, movedPage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function _cutPageRecursion(item, page_id) {
|
|
||||||
if (!item || !Array.isArray(item.children) || movedPage) return;
|
|
||||||
let pageIndex = item.children.findIndex(item => item.id === page_id);
|
|
||||||
if (pageIndex > -1) {
|
|
||||||
movedPage = item.children.splice(pageIndex, 1)[0];
|
|
||||||
} else {
|
|
||||||
item.children.forEach(item => {
|
|
||||||
_cutPageRecursion(item, page_id);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function _cutPage(navigation, page_id) {
|
|
||||||
const pageIndex = navigation.findIndex(item => item.id === page_id);
|
|
||||||
if (pageIndex > -1) {
|
|
||||||
movedPage = navigation.splice(pageIndex, 1)[0];
|
|
||||||
} else {
|
|
||||||
navigation.forEach(item => {
|
|
||||||
_cutPageRecursion(item, page_id);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_cutPage(navigation, moved_page_id, source_id);
|
|
||||||
_insertPage(navigation, moved_page_id, target_id, move_position);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
import { DragSource, DropTarget } from 'react-dnd';
|
import { DragSource, DropTarget } from 'react-dnd';
|
||||||
import { DRAGGED_FOLDER_MODE, DRAGGED_PAGE_MODE } from '../constant';
|
|
||||||
import PageItem from './page-item';
|
import PageItem from './page-item';
|
||||||
|
|
||||||
const dragSource = {
|
const dragSource = {
|
||||||
@@ -7,8 +6,7 @@ const dragSource = {
|
|||||||
return {
|
return {
|
||||||
idx: props.pageIndex,
|
idx: props.pageIndex,
|
||||||
data: { ...props.page, index: props.pageIndex },
|
data: { ...props.page, index: props.pageIndex },
|
||||||
folderId: props.folderId,
|
mode: 'wiki-page',
|
||||||
mode: DRAGGED_PAGE_MODE,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
endDrag(props, monitor) {
|
endDrag(props, monitor) {
|
||||||
@@ -28,48 +26,22 @@ const dragSource = {
|
|||||||
|
|
||||||
const dropTarget = {
|
const dropTarget = {
|
||||||
drop(props, monitor) {
|
drop(props, monitor) {
|
||||||
const sourceRow = monitor.getItem();
|
const dragSource = monitor.getItem();
|
||||||
// 1 drag page
|
if (dragSource.mode === 'wiki-page') {
|
||||||
if (sourceRow.mode === DRAGGED_PAGE_MODE) {
|
const { pageIndex: targetIndex, page: targetPage } = props;
|
||||||
const { infolder, pageIndex: targetIndex, page: targetPage, folderId: targetFolderId } = props;
|
const draggedPageId = dragSource.data.id;
|
||||||
const sourceFolderId = sourceRow.folderId;
|
|
||||||
const draggedPageId = sourceRow.data.id;
|
|
||||||
const targetPageId = targetPage.id;
|
const targetPageId = targetPage.id;
|
||||||
|
|
||||||
if (draggedPageId !== targetPageId) {
|
if (draggedPageId !== targetPageId) {
|
||||||
const sourceIndex = sourceRow.idx;
|
const sourceIndex = dragSource.idx;
|
||||||
let move_position;
|
const move_position = sourceIndex > targetIndex ? 'move_above' : 'move_below';
|
||||||
if (infolder) {
|
|
||||||
move_position = 'move_below';
|
|
||||||
} else {
|
|
||||||
move_position = sourceIndex > targetIndex ? 'move_above' : 'move_below';
|
|
||||||
}
|
|
||||||
|
|
||||||
props.onMovePage({
|
props.onMovePage({
|
||||||
moved_page_id: draggedPageId,
|
moved_page_id: draggedPageId,
|
||||||
target_page_id: targetPageId,
|
target_page_id: targetPageId,
|
||||||
source_page_folder_id: sourceFolderId,
|
|
||||||
target_page_folder_id: targetFolderId,
|
|
||||||
move_position,
|
move_position,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 1 drag folder
|
|
||||||
if (sourceRow.mode === DRAGGED_FOLDER_MODE) {
|
|
||||||
const { pageIndex: targetIndex, page: targetPage } = props;
|
|
||||||
const draggedFolderId = sourceRow.data.id;
|
|
||||||
const targetPageId = targetPage.id;
|
|
||||||
const sourceIndex = sourceRow.idx;
|
|
||||||
// Drag the parent folder to the child page, return
|
|
||||||
if (props.pathStr.split('-').includes(draggedFolderId)) return;
|
|
||||||
props.onMoveFolder(
|
|
||||||
draggedFolderId,
|
|
||||||
targetPageId,
|
|
||||||
sourceIndex > targetIndex ? 'move_above' : 'move_below',
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -11,24 +11,15 @@ export default class PageDropdownMenu extends Component {
|
|||||||
page: PropTypes.object.isRequired,
|
page: PropTypes.object.isRequired,
|
||||||
pages: PropTypes.array,
|
pages: PropTypes.array,
|
||||||
pagesLength: PropTypes.number,
|
pagesLength: PropTypes.number,
|
||||||
folderId: PropTypes.string,
|
|
||||||
canDelete: PropTypes.bool,
|
|
||||||
canDuplicate: PropTypes.bool,
|
|
||||||
renderFolderMenuItems: PropTypes.func,
|
|
||||||
toggle: PropTypes.func,
|
toggle: PropTypes.func,
|
||||||
toggleNameEditor: PropTypes.func,
|
toggleNameEditor: PropTypes.func,
|
||||||
duplicatePage: PropTypes.func,
|
duplicatePage: PropTypes.func,
|
||||||
onSetFolderId: PropTypes.func,
|
|
||||||
onDeletePage: PropTypes.func,
|
onDeletePage: PropTypes.func,
|
||||||
onMovePageToFolder: PropTypes.func,
|
|
||||||
isOnlyOnePage: PropTypes.bool,
|
isOnlyOnePage: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
|
||||||
isShowMenu: false,
|
|
||||||
};
|
|
||||||
this.pageNameMap = this.calculateNameMap();
|
this.pageNameMap = this.calculateNameMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,9 +32,6 @@ export default class PageDropdownMenu extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
onDropdownToggle = (evt) => {
|
onDropdownToggle = (evt) => {
|
||||||
if (evt.target && this.foldersDropdownToggle && this.foldersDropdownToggle.contains(evt.target)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
evt.stopPropagation();
|
evt.stopPropagation();
|
||||||
this.props.toggle();
|
this.props.toggle();
|
||||||
};
|
};
|
||||||
@@ -58,22 +46,8 @@ export default class PageDropdownMenu extends Component {
|
|||||||
this.props.onDeletePage();
|
this.props.onDeletePage();
|
||||||
};
|
};
|
||||||
|
|
||||||
onMovePageToFolder = (targetFolderId) => {
|
|
||||||
this.props.onMovePageToFolder(targetFolderId);
|
|
||||||
};
|
|
||||||
|
|
||||||
onRemoveFromFolder = (evt) => {
|
|
||||||
evt.nativeEvent.stopImmediatePropagation();
|
|
||||||
this.props.onMovePageToFolder(null);
|
|
||||||
};
|
|
||||||
|
|
||||||
onToggleFoldersMenu = () => {
|
|
||||||
this.setState({ isShowMenu: !this.state.isShowMenu });
|
|
||||||
};
|
|
||||||
|
|
||||||
duplicatePage = () => {
|
duplicatePage = () => {
|
||||||
const { page, folderId } = this.props;
|
const { page } = this.props;
|
||||||
this.props.onSetFolderId(folderId);
|
|
||||||
this.props.duplicatePage({ from_page_id: page.id }, () => {}, this.duplicatePageFailure);
|
this.props.duplicatePage({ from_page_id: page.id }, () => {}, this.duplicatePageFailure);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -81,14 +55,6 @@ export default class PageDropdownMenu extends Component {
|
|||||||
toaster.danger(gettext('Failed_to_duplicate_page'));
|
toaster.danger(gettext('Failed_to_duplicate_page'));
|
||||||
};
|
};
|
||||||
|
|
||||||
showMenu = () => {
|
|
||||||
this.setState({ isShowMenu: true });
|
|
||||||
};
|
|
||||||
|
|
||||||
hideMenu = () => {
|
|
||||||
this.setState({ isShowMenu: false });
|
|
||||||
};
|
|
||||||
|
|
||||||
handleCopyLink = () => {
|
handleCopyLink = () => {
|
||||||
const { page } = this.props;
|
const { page } = this.props;
|
||||||
const wikiLink = getWikPageLink(page.id);
|
const wikiLink = getWikPageLink(page.id);
|
||||||
@@ -109,10 +75,7 @@ export default class PageDropdownMenu extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const { pagesLength, isOnlyOnePage } = this.props;
|
||||||
folderId, canDelete, canDuplicate, renderFolderMenuItems, pagesLength, isOnlyOnePage,
|
|
||||||
} = this.props;
|
|
||||||
const folderMenuItems = renderFolderMenuItems && renderFolderMenuItems({ currentFolderId: folderId, onMovePageToFolder: this.onMovePageToFolder });
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
@@ -135,63 +98,16 @@ export default class PageDropdownMenu extends Component {
|
|||||||
<i className="sf3-font sf3-font-rename" />
|
<i className="sf3-font sf3-font-rename" />
|
||||||
<span className="item-text">{gettext('Modify name')}</span>
|
<span className="item-text">{gettext('Modify name')}</span>
|
||||||
</DropdownItem>
|
</DropdownItem>
|
||||||
{canDuplicate &&
|
<DropdownItem onClick={this.duplicatePage}>
|
||||||
<DropdownItem onClick={this.duplicatePage}>
|
<i className="sf3-font sf3-font-copy1" />
|
||||||
<i className="sf3-font sf3-font-copy1" />
|
<span className="item-text">{gettext('Duplicate page')}</span>
|
||||||
<span className="item-text">{gettext('Duplicate page')}</span>
|
</DropdownItem>
|
||||||
</DropdownItem>
|
{(isOnlyOnePage || pagesLength === 1) ? '' : (
|
||||||
}
|
|
||||||
{(isOnlyOnePage || pagesLength === 1 || !canDelete) ? '' : (
|
|
||||||
<DropdownItem onClick={this.onDeletePage}>
|
<DropdownItem onClick={this.onDeletePage}>
|
||||||
<i className="sf3-font sf3-font-delete1" />
|
<i className="sf3-font sf3-font-delete1" />
|
||||||
<span className="item-text">{gettext('Delete page')}</span>
|
<span className="item-text">{gettext('Delete page')}</span>
|
||||||
</DropdownItem>
|
</DropdownItem>
|
||||||
)}
|
)}
|
||||||
{folderId &&
|
|
||||||
<DropdownItem onClick={this.onRemoveFromFolder}>
|
|
||||||
<i className="sf3-font sf3-font-move" />
|
|
||||||
<span className="item-text">{gettext('Remove from folder')}</span>
|
|
||||||
</DropdownItem>
|
|
||||||
}
|
|
||||||
{renderFolderMenuItems && folderMenuItems.length > 0 &&
|
|
||||||
<DropdownItem
|
|
||||||
className="pr-2 btn-move-to-folder"
|
|
||||||
tag="div"
|
|
||||||
onClick={(evt) => {
|
|
||||||
evt.stopPropagation();
|
|
||||||
evt.nativeEvent.stopImmediatePropagation();
|
|
||||||
this.showMenu();
|
|
||||||
}}
|
|
||||||
onMouseEnter={this.showMenu}
|
|
||||||
onMouseLeave={this.hideMenu}
|
|
||||||
>
|
|
||||||
<Dropdown
|
|
||||||
className="folders-dropdown"
|
|
||||||
direction="right"
|
|
||||||
isOpen={this.state.isShowMenu}
|
|
||||||
toggle={this.onToggleFoldersMenu}
|
|
||||||
>
|
|
||||||
<div className="folders-dropdown-toggle" ref={ref => this.foldersDropdownToggle = ref}>
|
|
||||||
<i className="sf3-font sf3-font-move" />
|
|
||||||
<span className="item-text">{gettext('Move to')}</span>
|
|
||||||
<span className="icon-dropdown-toggle">
|
|
||||||
<i className="sf3-font-down sf3-font rotate-270"></i>
|
|
||||||
</span>
|
|
||||||
<DropdownToggle className="move-to-folders-toggle"></DropdownToggle>
|
|
||||||
</div>
|
|
||||||
{this.state.isShowMenu &&
|
|
||||||
<DropdownMenu
|
|
||||||
className="folders-dropdown-menu"
|
|
||||||
flip={false}
|
|
||||||
modifiers={{ preventOverflow: { boundariesElement: document.body } }}
|
|
||||||
positionFixed={true}
|
|
||||||
>
|
|
||||||
{folderMenuItems}
|
|
||||||
</DropdownMenu>
|
|
||||||
}
|
|
||||||
</Dropdown>
|
|
||||||
</DropdownItem>
|
|
||||||
}
|
|
||||||
< hr className='divider' />
|
< hr className='divider' />
|
||||||
<DropdownItem onClick={this.handleOpenInNewTab}>
|
<DropdownItem onClick={this.handleOpenInNewTab}>
|
||||||
<i className='sf3-font sf3-font-open-in-new-tab' />
|
<i className='sf3-font sf3-font-open-in-new-tab' />
|
||||||
|
@@ -113,20 +113,20 @@ class PageItem extends Component {
|
|||||||
window.seafile['docUuid'] = docUuid;
|
window.seafile['docUuid'] = docUuid;
|
||||||
};
|
};
|
||||||
|
|
||||||
getFolderChildrenHeight = () => {
|
getPageChildrenHeight = () => {
|
||||||
const folded = this.props.getFoldState(this.props.page.id);
|
const folded = this.props.getFoldState(this.props.page.id);
|
||||||
if (folded) return 0;
|
if (folded) return 0;
|
||||||
return 'auto';
|
return 'auto';
|
||||||
};
|
};
|
||||||
|
|
||||||
onClickFolderChildren = (e) => {
|
onClickPageChildren = (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.nativeEvent.stopImmediatePropagation();
|
e.nativeEvent.stopImmediatePropagation();
|
||||||
};
|
};
|
||||||
|
|
||||||
renderPage = (page, index, pagesLength, isOnlyOnePage) => {
|
renderPage = (page, index, pagesLength, isOnlyOnePage) => {
|
||||||
if (!page) return;
|
if (!page) return;
|
||||||
const { isEditMode, pages, folderId, pathStr } = this.props;
|
const { isEditMode, pages, pathStr } = this.props;
|
||||||
const id = page.id;
|
const id = page.id;
|
||||||
if (!pages.find(item => item.id === id)) return;
|
if (!pages.find(item => item.id === id)) return;
|
||||||
return (
|
return (
|
||||||
@@ -134,22 +134,14 @@ class PageItem extends Component {
|
|||||||
key={id}
|
key={id}
|
||||||
pagesLength={pagesLength}
|
pagesLength={pagesLength}
|
||||||
isOnlyOnePage={isOnlyOnePage}
|
isOnlyOnePage={isOnlyOnePage}
|
||||||
infolder={false}
|
|
||||||
page={Object.assign({}, pages.find(item => item.id === id), page)}
|
page={Object.assign({}, pages.find(item => item.id === id), page)}
|
||||||
pageIndex={index}
|
pageIndex={index}
|
||||||
folderId={folderId}
|
|
||||||
isEditMode={isEditMode}
|
isEditMode={isEditMode}
|
||||||
renderFolderMenuItems={this.props.renderFolderMenuItems}
|
|
||||||
duplicatePage={this.props.duplicatePage}
|
duplicatePage={this.props.duplicatePage}
|
||||||
onSetFolderId={this.props.onSetFolderId}
|
|
||||||
setCurrentPage={this.props.setCurrentPage}
|
setCurrentPage={this.props.setCurrentPage}
|
||||||
onUpdatePage={this.props.onUpdatePage}
|
onUpdatePage={this.props.onUpdatePage}
|
||||||
onDeletePage={this.props.onDeletePage}
|
onDeletePage={this.props.onDeletePage}
|
||||||
onMovePageToFolder={(targetFolderId) => {
|
|
||||||
this.props.onMovePageToFolder(folderId, page.id, targetFolderId);
|
|
||||||
}}
|
|
||||||
onMovePage={this.props.onMovePage}
|
onMovePage={this.props.onMovePage}
|
||||||
onMoveFolder={this.props.onMoveFolder}
|
|
||||||
pages={pages}
|
pages={pages}
|
||||||
pathStr={pathStr + '-' + page.id}
|
pathStr={pathStr + '-' + page.id}
|
||||||
currentPageId={this.props.currentPageId}
|
currentPageId={this.props.currentPageId}
|
||||||
@@ -173,21 +165,14 @@ class PageItem extends Component {
|
|||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
connectDragSource, connectDragPreview, connectDropTarget, isOver, canDrop, isDragging,
|
connectDragSource, connectDragPreview, connectDropTarget, isOver, canDrop, isDragging,
|
||||||
infolder, page, pagesLength, isEditMode, folderId, isOnlyOnePage, pathStr,
|
page, pagesLength, isEditMode, isOnlyOnePage, pathStr,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const { isShowNameEditor, pageName, isSelected } = this.state;
|
const { isShowNameEditor, pageName, isSelected } = this.state;
|
||||||
const isOverPage = isOver && canDrop;
|
const isOverPage = isOver && canDrop;
|
||||||
if (isSelected) this.setDocUuid(page.docUuid);
|
if (isSelected) this.setDocUuid(page.docUuid);
|
||||||
|
|
||||||
let pageCanDropTop;
|
let pageCanDropTop = isOverPage && isDragging;
|
||||||
let pageCanDrop;
|
let pageCanDrop = isOverPage && !isDragging;
|
||||||
if (infolder) {
|
|
||||||
pageCanDropTop = false;
|
|
||||||
pageCanDrop = isOverPage;
|
|
||||||
} else {
|
|
||||||
pageCanDropTop = isOverPage && isDragging;
|
|
||||||
pageCanDrop = isOverPage && !isDragging;
|
|
||||||
}
|
|
||||||
let navItemId = `page-editor-${page.id}`;
|
let navItemId = `page-editor-${page.id}`;
|
||||||
let fn = isEditMode ? connectDragSource : (argu) => {argu;};
|
let fn = isEditMode ? connectDragSource : (argu) => {argu;};
|
||||||
let childNumber = Array.isArray(page.children) ? page.children.length : 0;
|
let childNumber = Array.isArray(page.children) ? page.children.length : 0;
|
||||||
@@ -246,16 +231,10 @@ class PageItem extends Component {
|
|||||||
pages={this.props.pages}
|
pages={this.props.pages}
|
||||||
pagesLength={pagesLength}
|
pagesLength={pagesLength}
|
||||||
isOnlyOnePage={isOnlyOnePage}
|
isOnlyOnePage={isOnlyOnePage}
|
||||||
folderId={folderId}
|
|
||||||
canDelete={true}
|
|
||||||
canDuplicate={true}
|
|
||||||
toggle={this.toggleDropdown}
|
toggle={this.toggleDropdown}
|
||||||
renderFolderMenuItems={this.props.renderFolderMenuItems}
|
|
||||||
toggleNameEditor={this.toggleNameEditor}
|
toggleNameEditor={this.toggleNameEditor}
|
||||||
duplicatePage={this.props.duplicatePage}
|
duplicatePage={this.props.duplicatePage}
|
||||||
onSetFolderId={this.props.onSetFolderId}
|
|
||||||
onDeletePage={this.openDeleteDialog}
|
onDeletePage={this.openDeleteDialog}
|
||||||
onMovePageToFolder={this.props.onMovePageToFolder}
|
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@@ -283,9 +262,9 @@ class PageItem extends Component {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
<div
|
<div
|
||||||
className="page-folder-children"
|
className="page-children"
|
||||||
style={{ height: this.getFolderChildrenHeight() }}
|
style={{ height: this.getPageChildrenHeight() }}
|
||||||
onClick={this.onClickFolderChildren}
|
onClick={this.onClickPageChildren}
|
||||||
>
|
>
|
||||||
{page.children &&
|
{page.children &&
|
||||||
page.children.map((item, index) => {
|
page.children.map((item, index) => {
|
||||||
@@ -304,26 +283,19 @@ PageItem.propTypes = {
|
|||||||
isDragging: PropTypes.bool,
|
isDragging: PropTypes.bool,
|
||||||
draggedPage: PropTypes.object,
|
draggedPage: PropTypes.object,
|
||||||
isEditMode: PropTypes.bool,
|
isEditMode: PropTypes.bool,
|
||||||
infolder: PropTypes.bool,
|
|
||||||
page: PropTypes.object,
|
page: PropTypes.object,
|
||||||
folder: PropTypes.object,
|
|
||||||
pages: PropTypes.array,
|
pages: PropTypes.array,
|
||||||
pageIndex: PropTypes.number,
|
pageIndex: PropTypes.number,
|
||||||
folderId: PropTypes.string,
|
|
||||||
pagesLength: PropTypes.number,
|
pagesLength: PropTypes.number,
|
||||||
connectDragSource: PropTypes.func,
|
connectDragSource: PropTypes.func,
|
||||||
connectDragPreview: PropTypes.func,
|
connectDragPreview: PropTypes.func,
|
||||||
connectDropTarget: PropTypes.func,
|
connectDropTarget: PropTypes.func,
|
||||||
renderFolderMenuItems: PropTypes.func,
|
|
||||||
duplicatePage: PropTypes.func,
|
duplicatePage: PropTypes.func,
|
||||||
onSetFolderId: PropTypes.func,
|
|
||||||
setCurrentPage: PropTypes.func,
|
setCurrentPage: PropTypes.func,
|
||||||
onUpdatePage: PropTypes.func,
|
onUpdatePage: PropTypes.func,
|
||||||
onDeletePage: PropTypes.func,
|
onDeletePage: PropTypes.func,
|
||||||
onMovePageToFolder: PropTypes.func,
|
|
||||||
onMovePage: PropTypes.func,
|
onMovePage: PropTypes.func,
|
||||||
isOnlyOnePage: PropTypes.bool,
|
isOnlyOnePage: PropTypes.bool,
|
||||||
onMoveFolder: PropTypes.func,
|
|
||||||
pathStr: PropTypes.string,
|
pathStr: PropTypes.string,
|
||||||
currentPageId: PropTypes.string,
|
currentPageId: PropTypes.string,
|
||||||
addPageInside: PropTypes.func,
|
addPageInside: PropTypes.func,
|
||||||
|
@@ -1,48 +0,0 @@
|
|||||||
import React, { Component } from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import CommonAddTool from '../../../components/common/common-add-tool';
|
|
||||||
import AddPageDropdownMenu from './add-page-dropdownmenu';
|
|
||||||
import { gettext } from '../../../utils/constants';
|
|
||||||
|
|
||||||
class WikiNavFooter extends Component {
|
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
isShowDropdownMenu: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleDropdown = (event) => {
|
|
||||||
event && event.stopPropagation();
|
|
||||||
this.setState({ isShowDropdownMenu: !this.state.isShowDropdownMenu });
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<div className='wiki-nav-footer'>
|
|
||||||
<div className='add-wiki-page-wrapper'>
|
|
||||||
<CommonAddTool
|
|
||||||
className='add-wiki-page-btn'
|
|
||||||
callBack={this.toggleDropdown}
|
|
||||||
footerName={gettext('Add page or folder')}
|
|
||||||
/>
|
|
||||||
{this.state.isShowDropdownMenu &&
|
|
||||||
<AddPageDropdownMenu
|
|
||||||
toggleDropdown={this.toggleDropdown}
|
|
||||||
onToggleAddPage={this.props.onToggleAddPage}
|
|
||||||
onToggleAddFolder={this.props.onToggleAddFolder}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WikiNavFooter.propTypes = {
|
|
||||||
onToggleAddPage: PropTypes.func,
|
|
||||||
onToggleAddFolder: PropTypes.func,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default WikiNavFooter;
|
|
@@ -1,11 +1,8 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { DropdownItem } from 'reactstrap';
|
|
||||||
import { DropTarget, DragLayer } from 'react-dnd';
|
import { DropTarget, DragLayer } from 'react-dnd';
|
||||||
import html5DragDropContext from './html5DragDropContext';
|
import html5DragDropContext from './html5DragDropContext';
|
||||||
import DraggedFolderItem from './folders/dragged-folder-item';
|
|
||||||
import DraggedPageItem from './pages/dragged-page-item';
|
import DraggedPageItem from './pages/dragged-page-item';
|
||||||
import WikiNavFooter from './wiki-nav-footer';
|
|
||||||
import { repoID } from '../../../utils/constants';
|
import { repoID } from '../../../utils/constants';
|
||||||
|
|
||||||
import '../css/wiki-nav.css';
|
import '../css/wiki-nav.css';
|
||||||
@@ -17,26 +14,17 @@ class WikiNav extends Component {
|
|||||||
navigation: PropTypes.array,
|
navigation: PropTypes.array,
|
||||||
pages: PropTypes.array,
|
pages: PropTypes.array,
|
||||||
onTogglePinViewList: PropTypes.func,
|
onTogglePinViewList: PropTypes.func,
|
||||||
onToggleAddPage: PropTypes.func,
|
|
||||||
onToggleAddFolder: PropTypes.func,
|
|
||||||
onModifyFolder: PropTypes.func,
|
|
||||||
onDeleteFolder: PropTypes.func,
|
|
||||||
onMoveFolder: PropTypes.func,
|
|
||||||
setCurrentPage: PropTypes.func,
|
setCurrentPage: PropTypes.func,
|
||||||
onUpdatePage: PropTypes.func,
|
onUpdatePage: PropTypes.func,
|
||||||
onDeletePage: PropTypes.func,
|
onDeletePage: PropTypes.func,
|
||||||
onMovePage: PropTypes.func,
|
onMovePage: PropTypes.func,
|
||||||
moveFolderToFolder: PropTypes.func,
|
|
||||||
movePageOut: PropTypes.func,
|
|
||||||
duplicatePage: PropTypes.func,
|
duplicatePage: PropTypes.func,
|
||||||
onSetFolderId: PropTypes.func,
|
|
||||||
currentPageId: PropTypes.string,
|
currentPageId: PropTypes.string,
|
||||||
addPageInside: PropTypes.func,
|
addPageInside: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.folderClassNameCache = '';
|
|
||||||
this.idFoldedStatusMap = this.getFoldedFromLocal();
|
this.idFoldedStatusMap = this.getFoldedFromLocal();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,145 +37,63 @@ class WikiNav extends Component {
|
|||||||
window.localStorage.setItem(`wiki-folded-${repoID}`, JSON.stringify(items));
|
window.localStorage.setItem(`wiki-folded-${repoID}`, JSON.stringify(items));
|
||||||
};
|
};
|
||||||
|
|
||||||
getFoldState = (folderId) => {
|
getFoldState = (pageId) => {
|
||||||
return this.idFoldedStatusMap[folderId];
|
return this.idFoldedStatusMap[pageId];
|
||||||
};
|
};
|
||||||
|
|
||||||
toggleExpand = (folderId) => {
|
toggleExpand = (pageId) => {
|
||||||
const idFoldedStatusMap = this.getFoldedFromLocal();
|
const idFoldedStatusMap = this.getFoldedFromLocal();
|
||||||
if (idFoldedStatusMap[folderId]) {
|
if (idFoldedStatusMap[pageId]) {
|
||||||
delete idFoldedStatusMap[folderId];
|
delete idFoldedStatusMap[pageId];
|
||||||
} else {
|
} else {
|
||||||
idFoldedStatusMap[folderId] = true;
|
idFoldedStatusMap[pageId] = true;
|
||||||
}
|
}
|
||||||
this.saveFoldedToLocal(idFoldedStatusMap);
|
this.saveFoldedToLocal(idFoldedStatusMap);
|
||||||
this.idFoldedStatusMap = idFoldedStatusMap;
|
this.idFoldedStatusMap = idFoldedStatusMap;
|
||||||
};
|
};
|
||||||
|
|
||||||
onMovePageToFolder = (source_page_folder_id, moved_page_id, target_page_folder_id) => {
|
renderPage = (page, index, pagesLength, isOnlyOnePage, id_page_map, layerDragProps) => {
|
||||||
this.props.onMovePage({
|
|
||||||
moved_page_id,
|
|
||||||
source_page_folder_id,
|
|
||||||
target_page_folder_id,
|
|
||||||
target_page_id: null,
|
|
||||||
move_position: 'move_below'
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
renderFolderMenuItems = ({ currentFolderId, onMovePageToFolder }) => {
|
|
||||||
// folder lists (in the root directory)
|
|
||||||
const { navigation } = this.props;
|
|
||||||
let renderFolders = navigation.filter(item => item.type === 'folder' && item.id !== currentFolderId);
|
|
||||||
return renderFolders.map(folder => {
|
|
||||||
const { id, name } = folder;
|
|
||||||
return (
|
|
||||||
<DropdownItem key={`move-to-folder-${id}`} onClick={onMovePageToFolder.bind(this, id)}>
|
|
||||||
<span className="folder-name text-truncate" title={name}>{name}</span>
|
|
||||||
</DropdownItem>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
setClassName = (name) => {
|
|
||||||
this.folderClassNameCache = name;
|
|
||||||
};
|
|
||||||
|
|
||||||
getClassName = () => {
|
|
||||||
return this.folderClassNameCache;
|
|
||||||
};
|
|
||||||
|
|
||||||
renderFolder = (folder, index, pagesLength, isOnlyOnePage, id_page_map, layerDragProps) => {
|
|
||||||
const { isEditMode, pages } = this.props;
|
|
||||||
const folderId = folder.id;
|
|
||||||
return (
|
|
||||||
<DraggedFolderItem
|
|
||||||
key={`page-folder-${folderId}`}
|
|
||||||
isEditMode={isEditMode}
|
|
||||||
folder={folder}
|
|
||||||
folderIndex={index}
|
|
||||||
pagesLength={pagesLength}
|
|
||||||
isOnlyOnePage={isOnlyOnePage}
|
|
||||||
id_page_map={id_page_map}
|
|
||||||
renderFolderMenuItems={this.renderFolderMenuItems}
|
|
||||||
toggleExpand={this.toggleExpand}
|
|
||||||
onToggleAddPage={this.props.onToggleAddPage}
|
|
||||||
onDeleteFolder={this.props.onDeleteFolder}
|
|
||||||
onMoveFolder={this.props.onMoveFolder}
|
|
||||||
setCurrentPage={this.props.setCurrentPage}
|
|
||||||
onUpdatePage={this.props.onUpdatePage}
|
|
||||||
duplicatePage={this.props.duplicatePage}
|
|
||||||
onSetFolderId={this.props.onSetFolderId}
|
|
||||||
onDeletePage={this.props.onDeletePage}
|
|
||||||
onMovePageToFolder={this.onMovePageToFolder}
|
|
||||||
onMovePage={this.props.onMovePage}
|
|
||||||
pages={pages}
|
|
||||||
moveFolderToFolder={this.props.moveFolderToFolder}
|
|
||||||
pathStr={folderId}
|
|
||||||
layerDragProps={layerDragProps}
|
|
||||||
setClassName={this.setClassName}
|
|
||||||
getClassName={this.getClassName}
|
|
||||||
movePageOut={this.props.movePageOut}
|
|
||||||
onModifyFolder={this.props.onModifyFolder}
|
|
||||||
getFoldState={this.getFoldState}
|
|
||||||
currentPageId={this.props.currentPageId}
|
|
||||||
addPageInside={this.props.addPageInside}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
renderPage = (page, index, pagesLength, isOnlyOnePage, id_page_map) => {
|
|
||||||
const { isEditMode, pages } = this.props;
|
const { isEditMode, pages } = this.props;
|
||||||
const id = page.id;
|
const id = page.id;
|
||||||
if (!pages.find(item => item.id === id)) return;
|
if (!pages.find(item => item.id === id)) return;
|
||||||
const folderId = null; // Pages in the root directory, no folders, use null
|
|
||||||
return (
|
return (
|
||||||
<DraggedPageItem
|
<DraggedPageItem
|
||||||
key={id}
|
key={id}
|
||||||
pagesLength={pagesLength}
|
pagesLength={pagesLength}
|
||||||
isOnlyOnePage={isOnlyOnePage}
|
isOnlyOnePage={isOnlyOnePage}
|
||||||
infolder={false}
|
|
||||||
page={Object.assign({}, pages.find(item => item.id === id), page)}
|
page={Object.assign({}, pages.find(item => item.id === id), page)}
|
||||||
pages={pages}
|
pages={pages}
|
||||||
pageIndex={index}
|
pageIndex={index}
|
||||||
folderId={folderId}
|
|
||||||
isEditMode={isEditMode}
|
isEditMode={isEditMode}
|
||||||
renderFolderMenuItems={this.renderFolderMenuItems}
|
|
||||||
duplicatePage={this.props.duplicatePage}
|
duplicatePage={this.props.duplicatePage}
|
||||||
onSetFolderId={this.props.onSetFolderId}
|
|
||||||
setCurrentPage={this.props.setCurrentPage}
|
setCurrentPage={this.props.setCurrentPage}
|
||||||
onUpdatePage={this.props.onUpdatePage}
|
onUpdatePage={this.props.onUpdatePage}
|
||||||
onDeletePage={this.props.onDeletePage}
|
onDeletePage={this.props.onDeletePage}
|
||||||
onMovePageToFolder={(targetFolderId) => {
|
|
||||||
this.onMovePageToFolder(folderId, page.id, targetFolderId);
|
|
||||||
}}
|
|
||||||
onMovePage={this.props.onMovePage}
|
onMovePage={this.props.onMovePage}
|
||||||
onMoveFolder={this.props.onMoveFolder}
|
|
||||||
pathStr={page.id}
|
pathStr={page.id}
|
||||||
currentPageId={this.props.currentPageId}
|
currentPageId={this.props.currentPageId}
|
||||||
addPageInside={this.props.addPageInside}
|
addPageInside={this.props.addPageInside}
|
||||||
getFoldState={this.getFoldState}
|
getFoldState={this.getFoldState}
|
||||||
toggleExpand={this.toggleExpand}
|
toggleExpand={this.toggleExpand}
|
||||||
|
id_page_map={id_page_map}
|
||||||
|
layerDragProps={layerDragProps}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
renderStructureBody = React.forwardRef((layerDragProps, ref) => {
|
renderStructureBody = React.forwardRef((layerDragProps, ref) => {
|
||||||
const { navigation, pages, isEditMode } = this.props;
|
const { navigation, pages } = this.props;
|
||||||
let isOnlyOnePage = false;
|
let isOnlyOnePage = false;
|
||||||
if (pages.length === 1) {
|
if (pages.length === 1) {
|
||||||
isOnlyOnePage = true;
|
isOnlyOnePage = true;
|
||||||
}
|
}
|
||||||
const pagesLength = pages.length;
|
|
||||||
let id_page_map = {};
|
let id_page_map = {};
|
||||||
pages.forEach(page => id_page_map[page.id] = page);
|
pages.forEach(page => id_page_map[page.id] = page);
|
||||||
const style = { maxHeight: isEditMode ? 'calc(100% - 40px)' : '100%' };
|
|
||||||
return (
|
return (
|
||||||
<div className='wiki-nav-body' style={style}>
|
<div className='wiki-nav-body'>
|
||||||
{navigation.map((item, index) => {
|
{navigation.map((item, index) => {
|
||||||
return item.type === 'folder' ?
|
return this.renderPage(item, index, pages.length, isOnlyOnePage, id_page_map, layerDragProps);
|
||||||
this.renderFolder(item, index, pagesLength, isOnlyOnePage, id_page_map, layerDragProps) :
|
|
||||||
this.renderPage(item, index, pagesLength, isOnlyOnePage, id_page_map);
|
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -211,12 +117,6 @@ class WikiNav extends Component {
|
|||||||
return (
|
return (
|
||||||
<div className='wiki-nav'>
|
<div className='wiki-nav'>
|
||||||
<StructureBody />
|
<StructureBody />
|
||||||
{(this.props.isEditMode) &&
|
|
||||||
<WikiNavFooter
|
|
||||||
onToggleAddPage={this.props.onToggleAddPage}
|
|
||||||
onToggleAddFolder={this.props.onToggleAddFolder}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user