mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-09 02:42:47 +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;
|
||||
overflow: auto;
|
||||
user-select: none;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
.wiki-nav .page-folder {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.wiki-nav .page-folder.can-drop::after,
|
||||
.wiki-nav .page-folder.can-drop-top::after {
|
||||
.wiki-nav .can-drop::after,
|
||||
.wiki-nav .can-drop-top::after {
|
||||
content: '';
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
@@ -32,7 +29,16 @@
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -44,16 +50,6 @@
|
||||
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 {
|
||||
position: relative;
|
||||
display: flex;
|
||||
@@ -72,7 +68,6 @@
|
||||
background-color: #EDEDEA;
|
||||
}
|
||||
|
||||
.wiki-nav .page-folder-wrapper:hover,
|
||||
.wiki-nav .wiki-page-item:hover {
|
||||
background-color: #EFEFED;
|
||||
}
|
||||
@@ -81,7 +76,6 @@
|
||||
background-color: #E6E6E4;
|
||||
}
|
||||
|
||||
.wiki-nav .folder-main,
|
||||
.wiki-nav .wiki-page-item-main {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
@@ -90,15 +84,6 @@
|
||||
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 {
|
||||
height: 100%;
|
||||
padding-right: 8px;
|
||||
@@ -109,29 +94,25 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.wiki-nav .in-folder .wiki-page-content {
|
||||
.wiki-nav .wiki-page-content {
|
||||
padding-left: calc(12 * 0.8px + 0.5rem);
|
||||
}
|
||||
|
||||
.wiki-nav .folder-content:hover,
|
||||
.wiki-nav .wiki-page-content:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.wiki-nav .folder-content .dtable-font,
|
||||
.wiki-nav .wiki-page-content .dtable-font {
|
||||
margin-right: 6px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.wiki-nav .folder-content .folder-name,
|
||||
.wiki-nav .wiki-page-content .wiki-page-title {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.wiki-nav .folder-operation-dropdownmenu,
|
||||
.wiki-nav .more-wiki-page-operation {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
@@ -141,37 +122,31 @@
|
||||
}
|
||||
|
||||
.wiki-nav .wiki-add-page-btn:hover,
|
||||
.wiki-nav .folder-operation:hover,
|
||||
.wiki-nav .more-wiki-page-operation:hover {
|
||||
border-radius: 3px;
|
||||
background-color: #DFDFDD;
|
||||
}
|
||||
|
||||
.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 {
|
||||
cursor: pointer;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.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 {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.wiki-nav .folder-operation-dropdownmenu:hover,
|
||||
.wiki-nav .more-wiki-page-operation:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.wiki-nav .folder-operation-dropdownmenu .dtable-font,
|
||||
.wiki-nav .more-wiki-page-operation .dtable-font {
|
||||
font-size: 14px;
|
||||
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 .seafile-multicolor-icon-more-level {
|
||||
opacity: 1;
|
||||
@@ -254,69 +229,6 @@
|
||||
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-top::after {
|
||||
content: '';
|
||||
@@ -351,40 +263,15 @@
|
||||
|
||||
.wiki-nav,
|
||||
.wiki-nav .wiki-page-item .sf3-font.sf3-font-enlarge: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 {
|
||||
.wiki-nav .wiki-page-item .seafile-multicolor-icon-more-level:hover {
|
||||
color: #212529;
|
||||
}
|
||||
|
||||
.wiki-nav .wiki-page-item .sf3-font.sf3-font-enlarge,
|
||||
.wiki-nav .wiki-page-item .seafile-multicolor-icon-more-level,
|
||||
.wiki-nav .page-folder .seafile-multicolor-icon-more-level,
|
||||
.wiki-nav .icon-expand-folder {
|
||||
.wiki-nav .wiki-page-item .seafile-multicolor-icon-more-level {
|
||||
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 {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
|
@@ -103,15 +103,8 @@ class Wiki extends Component {
|
||||
|
||||
getFirstPageId = (config) => {
|
||||
if (!config || !Array.isArray(config.navigation)) return '';
|
||||
for (let i = 0; i < config.navigation.length; i++) {
|
||||
const item = config.navigation[i] || {};
|
||||
if (item.type === 'page') {
|
||||
return item.id;
|
||||
}
|
||||
if (item.type === 'folder' && item.children[0]) {
|
||||
return item.children[0].id;
|
||||
}
|
||||
}
|
||||
const firstPage = config.navigation[0] || {};
|
||||
return firstPage.id;
|
||||
};
|
||||
|
||||
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 Folder from './folder';
|
||||
|
||||
export default class WikiConfig {
|
||||
constructor(object) {
|
||||
this.version = object.version || 1;
|
||||
this.navigation = (Array.isArray(object.navigation) ? object.navigation : []).map(item => {
|
||||
if (item.type === 'folder') {
|
||||
return new Folder(item);
|
||||
} else if (item.type === 'page') {
|
||||
return {
|
||||
id: item.id,
|
||||
type: item.type,
|
||||
children: item.children || [],
|
||||
};
|
||||
const { version = 1, pages = [], navigation = [] } = object;
|
||||
this.version = version;
|
||||
this.pages = pages.map(page => new Page(page));
|
||||
this.navigation = navigation.filter(item => {
|
||||
return item.type === 'page';
|
||||
});
|
||||
// Render pages in folder to navigation root
|
||||
const page_id_map = {};
|
||||
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;
|
||||
}).filter(item => !!item);
|
||||
this.pages = Array.isArray(object.pages) ? object.pages.map(page => new Page(page)) : [];
|
||||
if (Array.isArray(node.children)) {
|
||||
node.children.forEach(child => traversePage(child));
|
||||
}
|
||||
}
|
||||
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 WikiNav from './wiki-nav/index';
|
||||
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 Folder from './models/folder';
|
||||
import Page from './models/page';
|
||||
import wikiAPI from '../../utils/wiki-api';
|
||||
import { FOLDER } from './constant';
|
||||
import { Utils } from '../../utils/utils';
|
||||
import WikiExternalOperations from './wiki-external-operations';
|
||||
|
||||
@@ -37,10 +32,6 @@ class SidePanel extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isShowNewFolderDialog: false,
|
||||
isShowAddNewPageDialog: false,
|
||||
};
|
||||
}
|
||||
|
||||
confirmDeletePage = (pageId) => {
|
||||
@@ -50,7 +41,7 @@ class SidePanel extends Component {
|
||||
config.pages.splice(index, 1);
|
||||
PageUtils.deletePage(navigation, pageId);
|
||||
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);
|
||||
if (config.pages.length > 0) {
|
||||
this.props.setCurrentPage(config.pages[0].id);
|
||||
@@ -72,7 +63,7 @@ class SidePanel extends Component {
|
||||
const navigation = config.navigation;
|
||||
const pageId = generateUniqueId(navigation);
|
||||
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) => {
|
||||
@@ -102,240 +93,32 @@ class SidePanel extends Component {
|
||||
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 { 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;
|
||||
this.props.saveWikiConfig(config);
|
||||
};
|
||||
|
||||
movePageOut = (moved_page_id, source_id, target_id, move_position) => {
|
||||
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 = () => {
|
||||
renderWikiNav = () => {
|
||||
const { config, onUpdatePage } = this.props;
|
||||
const { pages, navigation } = config;
|
||||
return (
|
||||
<div className="wiki2-pages-container">
|
||||
<WikiNav
|
||||
isEditMode={isWiki2}
|
||||
navigation={navigation}
|
||||
pages={pages}
|
||||
onToggleAddPage={this.openAddPageDialog}
|
||||
onDeletePage={this.confirmDeletePage}
|
||||
onUpdatePage={onUpdatePage}
|
||||
setCurrentPage={this.props.setCurrentPage}
|
||||
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}
|
||||
{isObjectNotEmpty(config) &&
|
||||
<WikiNav
|
||||
isEditMode={isWiki2}
|
||||
navigation={navigation}
|
||||
pages={pages}
|
||||
onDeletePage={this.confirmDeletePage}
|
||||
onUpdatePage={onUpdatePage}
|
||||
setCurrentPage={this.props.setCurrentPage}
|
||||
onMovePage={this.movePage}
|
||||
onAddNewPage={this.onAddNewPage}
|
||||
title={gettext('Add page')}
|
||||
/>
|
||||
}
|
||||
</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')}
|
||||
duplicatePage={this.duplicatePage}
|
||||
currentPageId={this.props.currentPageId}
|
||||
addPageInside={this.addPageInside}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
@@ -364,7 +147,7 @@ class SidePanel extends Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { isLoading, config } = this.props;
|
||||
const { isLoading } = this.props;
|
||||
return (
|
||||
<div className={`wiki2-side-panel${this.props.closeSideBar ? '' : ' left-zero'}`}>
|
||||
<div className="wiki2-side-panel-top">
|
||||
@@ -377,7 +160,7 @@ class SidePanel extends Component {
|
||||
</UncontrolledTooltip>
|
||||
</div>
|
||||
<div className="wiki2-side-nav">
|
||||
{isLoading ? <Loading /> : (isObjectNotEmpty(config) ? this.renderFolderView() : this.renderNoFolder())}
|
||||
{isLoading ? <Loading /> : this.renderWikiNav()}
|
||||
</div>
|
||||
<WikiExternalOperations onAddWikiPage={this.handleAddNewPage.bind(false)} />
|
||||
</div>
|
||||
|
@@ -46,7 +46,7 @@ function WikiTopNav({ config, currentPageId }) {
|
||||
return (
|
||||
<Fragment key={item.id}>
|
||||
<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}
|
||||
</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 {
|
||||
|
||||
static addPage(navigation, page_id, parentId) {
|
||||
if (!parentId) {
|
||||
navigation.push({ id: page_id, type: PAGE });
|
||||
navigation.push(new NewPage(page_id));
|
||||
} else {
|
||||
navigation.forEach(item => {
|
||||
this._addPageRecursion(page_id, item, parentId);
|
||||
@@ -17,10 +23,10 @@ export default class PageUtils {
|
||||
item.children = [];
|
||||
}
|
||||
if (item.id === parentId) {
|
||||
item.children.push({ id: page_id, type: PAGE });
|
||||
item.children.push(new NewPage(page_id));
|
||||
return true;
|
||||
}
|
||||
item.children.forEach(item => {
|
||||
item.children && item.children.forEach(item => {
|
||||
this._addPageRecursion(page_id, item, parentId);
|
||||
});
|
||||
}
|
||||
@@ -43,7 +49,7 @@ export default class PageUtils {
|
||||
item.children.splice(pageIndex, 1);
|
||||
return true;
|
||||
}
|
||||
item.children.forEach(item => {
|
||||
item.children && item.children.forEach(item => {
|
||||
this._deletePageRecursion(item, page_id);
|
||||
});
|
||||
}
|
||||
@@ -57,76 +63,7 @@ export default class PageUtils {
|
||||
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) {
|
||||
// 1. No folder, insert page in root directory
|
||||
if (!target_id) {
|
||||
let insertIndex = target_page_id ? navigation.findIndex(item => item.id === target_page_id) : -1;
|
||||
if (insertIndex < 0) {
|
||||
@@ -136,34 +73,12 @@ export default class PageUtils {
|
||||
if (move_position === 'move_below') {
|
||||
insertIndex++;
|
||||
}
|
||||
navigation.splice(insertIndex, 0, { id: page_id, type: PAGE });
|
||||
navigation.splice(insertIndex, 0, new NewPage(page_id));
|
||||
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) {
|
||||
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) {
|
||||
static movePage(navigation, moved_page_id, target_page_id, move_position) {
|
||||
let movedPage = null;
|
||||
function _cutPageRecursion(item, page_id) {
|
||||
if (!item || !Array.isArray(item.children) || movedPage) return;
|
||||
@@ -171,7 +86,7 @@ export default class PageUtils {
|
||||
if (pageIndex > -1) {
|
||||
movedPage = item.children.splice(pageIndex, 1)[0];
|
||||
} else {
|
||||
item.children.forEach(item => {
|
||||
item.children && item.children.forEach(item => {
|
||||
_cutPageRecursion(item, page_id);
|
||||
});
|
||||
}
|
||||
@@ -195,7 +110,7 @@ export default class PageUtils {
|
||||
item.children.splice(insertIndex, 0, movedPage);
|
||||
return;
|
||||
}
|
||||
item.children.forEach(item => {
|
||||
item.children && item.children.forEach(item => {
|
||||
_insertPageRecursion(item, page_id, target_page_id, target_id, move_position);
|
||||
});
|
||||
}
|
||||
@@ -217,58 +132,6 @@ export default class PageUtils {
|
||||
});
|
||||
}
|
||||
_cutPage(navigation, moved_page_id);
|
||||
_insertPage(navigation, moved_page_id, target_page_id, target_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);
|
||||
_insertPage(navigation, moved_page_id, target_page_id, target_page_id, move_position);
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,4 @@
|
||||
import { DragSource, DropTarget } from 'react-dnd';
|
||||
import { DRAGGED_FOLDER_MODE, DRAGGED_PAGE_MODE } from '../constant';
|
||||
import PageItem from './page-item';
|
||||
|
||||
const dragSource = {
|
||||
@@ -7,8 +6,7 @@ const dragSource = {
|
||||
return {
|
||||
idx: props.pageIndex,
|
||||
data: { ...props.page, index: props.pageIndex },
|
||||
folderId: props.folderId,
|
||||
mode: DRAGGED_PAGE_MODE,
|
||||
mode: 'wiki-page',
|
||||
};
|
||||
},
|
||||
endDrag(props, monitor) {
|
||||
@@ -28,48 +26,22 @@ const dragSource = {
|
||||
|
||||
const dropTarget = {
|
||||
drop(props, monitor) {
|
||||
const sourceRow = monitor.getItem();
|
||||
// 1 drag page
|
||||
if (sourceRow.mode === DRAGGED_PAGE_MODE) {
|
||||
const { infolder, pageIndex: targetIndex, page: targetPage, folderId: targetFolderId } = props;
|
||||
const sourceFolderId = sourceRow.folderId;
|
||||
const draggedPageId = sourceRow.data.id;
|
||||
const dragSource = monitor.getItem();
|
||||
if (dragSource.mode === 'wiki-page') {
|
||||
const { pageIndex: targetIndex, page: targetPage } = props;
|
||||
const draggedPageId = dragSource.data.id;
|
||||
const targetPageId = targetPage.id;
|
||||
|
||||
if (draggedPageId !== targetPageId) {
|
||||
const sourceIndex = sourceRow.idx;
|
||||
let move_position;
|
||||
if (infolder) {
|
||||
move_position = 'move_below';
|
||||
} else {
|
||||
move_position = sourceIndex > targetIndex ? 'move_above' : 'move_below';
|
||||
}
|
||||
|
||||
const sourceIndex = dragSource.idx;
|
||||
const move_position = sourceIndex > targetIndex ? 'move_above' : 'move_below';
|
||||
props.onMovePage({
|
||||
moved_page_id: draggedPageId,
|
||||
target_page_id: targetPageId,
|
||||
source_page_folder_id: sourceFolderId,
|
||||
target_page_folder_id: targetFolderId,
|
||||
move_position,
|
||||
});
|
||||
}
|
||||
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,
|
||||
pages: PropTypes.array,
|
||||
pagesLength: PropTypes.number,
|
||||
folderId: PropTypes.string,
|
||||
canDelete: PropTypes.bool,
|
||||
canDuplicate: PropTypes.bool,
|
||||
renderFolderMenuItems: PropTypes.func,
|
||||
toggle: PropTypes.func,
|
||||
toggleNameEditor: PropTypes.func,
|
||||
duplicatePage: PropTypes.func,
|
||||
onSetFolderId: PropTypes.func,
|
||||
onDeletePage: PropTypes.func,
|
||||
onMovePageToFolder: PropTypes.func,
|
||||
isOnlyOnePage: PropTypes.bool,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isShowMenu: false,
|
||||
};
|
||||
this.pageNameMap = this.calculateNameMap();
|
||||
}
|
||||
|
||||
@@ -41,9 +32,6 @@ export default class PageDropdownMenu extends Component {
|
||||
};
|
||||
|
||||
onDropdownToggle = (evt) => {
|
||||
if (evt.target && this.foldersDropdownToggle && this.foldersDropdownToggle.contains(evt.target)) {
|
||||
return;
|
||||
}
|
||||
evt.stopPropagation();
|
||||
this.props.toggle();
|
||||
};
|
||||
@@ -58,22 +46,8 @@ export default class PageDropdownMenu extends Component {
|
||||
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 = () => {
|
||||
const { page, folderId } = this.props;
|
||||
this.props.onSetFolderId(folderId);
|
||||
const { page } = this.props;
|
||||
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'));
|
||||
};
|
||||
|
||||
showMenu = () => {
|
||||
this.setState({ isShowMenu: true });
|
||||
};
|
||||
|
||||
hideMenu = () => {
|
||||
this.setState({ isShowMenu: false });
|
||||
};
|
||||
|
||||
handleCopyLink = () => {
|
||||
const { page } = this.props;
|
||||
const wikiLink = getWikPageLink(page.id);
|
||||
@@ -109,10 +75,7 @@ export default class PageDropdownMenu extends Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
folderId, canDelete, canDuplicate, renderFolderMenuItems, pagesLength, isOnlyOnePage,
|
||||
} = this.props;
|
||||
const folderMenuItems = renderFolderMenuItems && renderFolderMenuItems({ currentFolderId: folderId, onMovePageToFolder: this.onMovePageToFolder });
|
||||
const { pagesLength, isOnlyOnePage } = this.props;
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
@@ -135,63 +98,16 @@ export default class PageDropdownMenu extends Component {
|
||||
<i className="sf3-font sf3-font-rename" />
|
||||
<span className="item-text">{gettext('Modify name')}</span>
|
||||
</DropdownItem>
|
||||
{canDuplicate &&
|
||||
<DropdownItem onClick={this.duplicatePage}>
|
||||
<i className="sf3-font sf3-font-copy1" />
|
||||
<span className="item-text">{gettext('Duplicate page')}</span>
|
||||
</DropdownItem>
|
||||
}
|
||||
{(isOnlyOnePage || pagesLength === 1 || !canDelete) ? '' : (
|
||||
<DropdownItem onClick={this.duplicatePage}>
|
||||
<i className="sf3-font sf3-font-copy1" />
|
||||
<span className="item-text">{gettext('Duplicate page')}</span>
|
||||
</DropdownItem>
|
||||
{(isOnlyOnePage || pagesLength === 1) ? '' : (
|
||||
<DropdownItem onClick={this.onDeletePage}>
|
||||
<i className="sf3-font sf3-font-delete1" />
|
||||
<span className="item-text">{gettext('Delete page')}</span>
|
||||
</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' />
|
||||
<DropdownItem onClick={this.handleOpenInNewTab}>
|
||||
<i className='sf3-font sf3-font-open-in-new-tab' />
|
||||
|
@@ -113,20 +113,20 @@ class PageItem extends Component {
|
||||
window.seafile['docUuid'] = docUuid;
|
||||
};
|
||||
|
||||
getFolderChildrenHeight = () => {
|
||||
getPageChildrenHeight = () => {
|
||||
const folded = this.props.getFoldState(this.props.page.id);
|
||||
if (folded) return 0;
|
||||
return 'auto';
|
||||
};
|
||||
|
||||
onClickFolderChildren = (e) => {
|
||||
onClickPageChildren = (e) => {
|
||||
e.stopPropagation();
|
||||
e.nativeEvent.stopImmediatePropagation();
|
||||
};
|
||||
|
||||
renderPage = (page, index, pagesLength, isOnlyOnePage) => {
|
||||
if (!page) return;
|
||||
const { isEditMode, pages, folderId, pathStr } = this.props;
|
||||
const { isEditMode, pages, pathStr } = this.props;
|
||||
const id = page.id;
|
||||
if (!pages.find(item => item.id === id)) return;
|
||||
return (
|
||||
@@ -134,22 +134,14 @@ class PageItem extends Component {
|
||||
key={id}
|
||||
pagesLength={pagesLength}
|
||||
isOnlyOnePage={isOnlyOnePage}
|
||||
infolder={false}
|
||||
page={Object.assign({}, pages.find(item => item.id === id), page)}
|
||||
pageIndex={index}
|
||||
folderId={folderId}
|
||||
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(folderId, page.id, targetFolderId);
|
||||
}}
|
||||
onMovePage={this.props.onMovePage}
|
||||
onMoveFolder={this.props.onMoveFolder}
|
||||
pages={pages}
|
||||
pathStr={pathStr + '-' + page.id}
|
||||
currentPageId={this.props.currentPageId}
|
||||
@@ -173,21 +165,14 @@ class PageItem extends Component {
|
||||
render() {
|
||||
const {
|
||||
connectDragSource, connectDragPreview, connectDropTarget, isOver, canDrop, isDragging,
|
||||
infolder, page, pagesLength, isEditMode, folderId, isOnlyOnePage, pathStr,
|
||||
page, pagesLength, isEditMode, isOnlyOnePage, pathStr,
|
||||
} = this.props;
|
||||
const { isShowNameEditor, pageName, isSelected } = this.state;
|
||||
const isOverPage = isOver && canDrop;
|
||||
if (isSelected) this.setDocUuid(page.docUuid);
|
||||
|
||||
let pageCanDropTop;
|
||||
let pageCanDrop;
|
||||
if (infolder) {
|
||||
pageCanDropTop = false;
|
||||
pageCanDrop = isOverPage;
|
||||
} else {
|
||||
pageCanDropTop = isOverPage && isDragging;
|
||||
pageCanDrop = isOverPage && !isDragging;
|
||||
}
|
||||
let pageCanDropTop = isOverPage && isDragging;
|
||||
let pageCanDrop = isOverPage && !isDragging;
|
||||
let navItemId = `page-editor-${page.id}`;
|
||||
let fn = isEditMode ? connectDragSource : (argu) => {argu;};
|
||||
let childNumber = Array.isArray(page.children) ? page.children.length : 0;
|
||||
@@ -246,16 +231,10 @@ class PageItem extends Component {
|
||||
pages={this.props.pages}
|
||||
pagesLength={pagesLength}
|
||||
isOnlyOnePage={isOnlyOnePage}
|
||||
folderId={folderId}
|
||||
canDelete={true}
|
||||
canDuplicate={true}
|
||||
toggle={this.toggleDropdown}
|
||||
renderFolderMenuItems={this.props.renderFolderMenuItems}
|
||||
toggleNameEditor={this.toggleNameEditor}
|
||||
duplicatePage={this.props.duplicatePage}
|
||||
onSetFolderId={this.props.onSetFolderId}
|
||||
onDeletePage={this.openDeleteDialog}
|
||||
onMovePageToFolder={this.props.onMovePageToFolder}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
@@ -283,9 +262,9 @@ class PageItem extends Component {
|
||||
))
|
||||
}
|
||||
<div
|
||||
className="page-folder-children"
|
||||
style={{ height: this.getFolderChildrenHeight() }}
|
||||
onClick={this.onClickFolderChildren}
|
||||
className="page-children"
|
||||
style={{ height: this.getPageChildrenHeight() }}
|
||||
onClick={this.onClickPageChildren}
|
||||
>
|
||||
{page.children &&
|
||||
page.children.map((item, index) => {
|
||||
@@ -304,26 +283,19 @@ PageItem.propTypes = {
|
||||
isDragging: PropTypes.bool,
|
||||
draggedPage: PropTypes.object,
|
||||
isEditMode: PropTypes.bool,
|
||||
infolder: PropTypes.bool,
|
||||
page: PropTypes.object,
|
||||
folder: PropTypes.object,
|
||||
pages: PropTypes.array,
|
||||
pageIndex: PropTypes.number,
|
||||
folderId: PropTypes.string,
|
||||
pagesLength: PropTypes.number,
|
||||
connectDragSource: PropTypes.func,
|
||||
connectDragPreview: PropTypes.func,
|
||||
connectDropTarget: PropTypes.func,
|
||||
renderFolderMenuItems: PropTypes.func,
|
||||
duplicatePage: PropTypes.func,
|
||||
onSetFolderId: PropTypes.func,
|
||||
setCurrentPage: PropTypes.func,
|
||||
onUpdatePage: PropTypes.func,
|
||||
onDeletePage: PropTypes.func,
|
||||
onMovePageToFolder: PropTypes.func,
|
||||
onMovePage: PropTypes.func,
|
||||
isOnlyOnePage: PropTypes.bool,
|
||||
onMoveFolder: PropTypes.func,
|
||||
pathStr: PropTypes.string,
|
||||
currentPageId: PropTypes.string,
|
||||
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 PropTypes from 'prop-types';
|
||||
import { DropdownItem } from 'reactstrap';
|
||||
import { DropTarget, DragLayer } from 'react-dnd';
|
||||
import html5DragDropContext from './html5DragDropContext';
|
||||
import DraggedFolderItem from './folders/dragged-folder-item';
|
||||
import DraggedPageItem from './pages/dragged-page-item';
|
||||
import WikiNavFooter from './wiki-nav-footer';
|
||||
import { repoID } from '../../../utils/constants';
|
||||
|
||||
import '../css/wiki-nav.css';
|
||||
@@ -17,26 +14,17 @@ class WikiNav extends Component {
|
||||
navigation: PropTypes.array,
|
||||
pages: PropTypes.array,
|
||||
onTogglePinViewList: PropTypes.func,
|
||||
onToggleAddPage: PropTypes.func,
|
||||
onToggleAddFolder: PropTypes.func,
|
||||
onModifyFolder: PropTypes.func,
|
||||
onDeleteFolder: PropTypes.func,
|
||||
onMoveFolder: PropTypes.func,
|
||||
setCurrentPage: PropTypes.func,
|
||||
onUpdatePage: PropTypes.func,
|
||||
onDeletePage: PropTypes.func,
|
||||
onMovePage: PropTypes.func,
|
||||
moveFolderToFolder: PropTypes.func,
|
||||
movePageOut: PropTypes.func,
|
||||
duplicatePage: PropTypes.func,
|
||||
onSetFolderId: PropTypes.func,
|
||||
currentPageId: PropTypes.string,
|
||||
addPageInside: PropTypes.func,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.folderClassNameCache = '';
|
||||
this.idFoldedStatusMap = this.getFoldedFromLocal();
|
||||
}
|
||||
|
||||
@@ -49,145 +37,63 @@ class WikiNav extends Component {
|
||||
window.localStorage.setItem(`wiki-folded-${repoID}`, JSON.stringify(items));
|
||||
};
|
||||
|
||||
getFoldState = (folderId) => {
|
||||
return this.idFoldedStatusMap[folderId];
|
||||
getFoldState = (pageId) => {
|
||||
return this.idFoldedStatusMap[pageId];
|
||||
};
|
||||
|
||||
toggleExpand = (folderId) => {
|
||||
toggleExpand = (pageId) => {
|
||||
const idFoldedStatusMap = this.getFoldedFromLocal();
|
||||
if (idFoldedStatusMap[folderId]) {
|
||||
delete idFoldedStatusMap[folderId];
|
||||
if (idFoldedStatusMap[pageId]) {
|
||||
delete idFoldedStatusMap[pageId];
|
||||
} else {
|
||||
idFoldedStatusMap[folderId] = true;
|
||||
idFoldedStatusMap[pageId] = true;
|
||||
}
|
||||
this.saveFoldedToLocal(idFoldedStatusMap);
|
||||
this.idFoldedStatusMap = idFoldedStatusMap;
|
||||
};
|
||||
|
||||
onMovePageToFolder = (source_page_folder_id, moved_page_id, target_page_folder_id) => {
|
||||
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) => {
|
||||
renderPage = (page, index, pagesLength, isOnlyOnePage, id_page_map, layerDragProps) => {
|
||||
const { isEditMode, pages } = this.props;
|
||||
const id = page.id;
|
||||
if (!pages.find(item => item.id === id)) return;
|
||||
const folderId = null; // Pages in the root directory, no folders, use null
|
||||
return (
|
||||
<DraggedPageItem
|
||||
key={id}
|
||||
pagesLength={pagesLength}
|
||||
isOnlyOnePage={isOnlyOnePage}
|
||||
infolder={false}
|
||||
page={Object.assign({}, pages.find(item => item.id === id), page)}
|
||||
pages={pages}
|
||||
pageIndex={index}
|
||||
folderId={folderId}
|
||||
isEditMode={isEditMode}
|
||||
renderFolderMenuItems={this.renderFolderMenuItems}
|
||||
duplicatePage={this.props.duplicatePage}
|
||||
onSetFolderId={this.props.onSetFolderId}
|
||||
setCurrentPage={this.props.setCurrentPage}
|
||||
onUpdatePage={this.props.onUpdatePage}
|
||||
onDeletePage={this.props.onDeletePage}
|
||||
onMovePageToFolder={(targetFolderId) => {
|
||||
this.onMovePageToFolder(folderId, page.id, targetFolderId);
|
||||
}}
|
||||
onMovePage={this.props.onMovePage}
|
||||
onMoveFolder={this.props.onMoveFolder}
|
||||
pathStr={page.id}
|
||||
currentPageId={this.props.currentPageId}
|
||||
addPageInside={this.props.addPageInside}
|
||||
getFoldState={this.getFoldState}
|
||||
toggleExpand={this.toggleExpand}
|
||||
id_page_map={id_page_map}
|
||||
layerDragProps={layerDragProps}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line
|
||||
renderStructureBody = React.forwardRef((layerDragProps, ref) => {
|
||||
const { navigation, pages, isEditMode } = this.props;
|
||||
const { navigation, pages } = this.props;
|
||||
let isOnlyOnePage = false;
|
||||
if (pages.length === 1) {
|
||||
isOnlyOnePage = true;
|
||||
}
|
||||
const pagesLength = pages.length;
|
||||
let id_page_map = {};
|
||||
pages.forEach(page => id_page_map[page.id] = page);
|
||||
const style = { maxHeight: isEditMode ? 'calc(100% - 40px)' : '100%' };
|
||||
return (
|
||||
<div className='wiki-nav-body' style={style}>
|
||||
<div className='wiki-nav-body'>
|
||||
{navigation.map((item, index) => {
|
||||
return item.type === 'folder' ?
|
||||
this.renderFolder(item, index, pagesLength, isOnlyOnePage, id_page_map, layerDragProps) :
|
||||
this.renderPage(item, index, pagesLength, isOnlyOnePage, id_page_map);
|
||||
return this.renderPage(item, index, pages.length, isOnlyOnePage, id_page_map, layerDragProps);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
@@ -211,12 +117,6 @@ class WikiNav extends Component {
|
||||
return (
|
||||
<div className='wiki-nav'>
|
||||
<StructureBody />
|
||||
{(this.props.isEditMode) &&
|
||||
<WikiNavFooter
|
||||
onToggleAddPage={this.props.onToggleAddPage}
|
||||
onToggleAddFolder={this.props.onToggleAddFolder}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
Reference in New Issue
Block a user