mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-24 12:58:34 +00:00
sortable directory tree (#8069)
Co-authored-by: zhouwenxuan <aries@Mac.local>
This commit is contained in:
@@ -29,7 +29,8 @@ const propTypes = {
|
|||||||
getMenuContainerSize: PropTypes.func,
|
getMenuContainerSize: PropTypes.func,
|
||||||
updateDirent: PropTypes.func,
|
updateDirent: PropTypes.func,
|
||||||
updateTreeNode: PropTypes.func,
|
updateTreeNode: PropTypes.func,
|
||||||
updateRepoInfo: PropTypes.func
|
updateRepoInfo: PropTypes.func,
|
||||||
|
sortTreeNode: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
class DirColumnNav extends React.Component {
|
class DirColumnNav extends React.Component {
|
||||||
@@ -67,6 +68,7 @@ class DirColumnNav extends React.Component {
|
|||||||
onItemsMove={this.props.onItemsMove}
|
onItemsMove={this.props.onItemsMove}
|
||||||
updateDirent={this.props.updateDirent}
|
updateDirent={this.props.updateDirent}
|
||||||
updateTreeNode={this.props.updateTreeNode}
|
updateTreeNode={this.props.updateTreeNode}
|
||||||
|
sortTreeNode={this.props.sortTreeNode}
|
||||||
/>
|
/>
|
||||||
<DirViews repoID={repoID} currentPath={currentPath} userPerm={userPerm} currentRepoInfo={currentRepoInfo} />
|
<DirViews repoID={repoID} currentPath={currentPath} userPerm={userPerm} currentRepoInfo={currentRepoInfo} />
|
||||||
<DirTags repoID={repoID} currentPath={currentPath} userPerm={userPerm} currentRepoInfo={currentRepoInfo} />
|
<DirTags repoID={repoID} currentPath={currentPath} userPerm={userPerm} currentRepoInfo={currentRepoInfo} />
|
||||||
|
@@ -79,7 +79,8 @@ const propTypes = {
|
|||||||
updateCurrentPath: PropTypes.func,
|
updateCurrentPath: PropTypes.func,
|
||||||
toggleShowDirentToolbar: PropTypes.func,
|
toggleShowDirentToolbar: PropTypes.func,
|
||||||
updateTreeNode: PropTypes.func,
|
updateTreeNode: PropTypes.func,
|
||||||
updateRepoInfo: PropTypes.func
|
updateRepoInfo: PropTypes.func,
|
||||||
|
sortTreeNode: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
class DirColumnView extends React.Component {
|
class DirColumnView extends React.Component {
|
||||||
@@ -177,6 +178,7 @@ class DirColumnView extends React.Component {
|
|||||||
direntList={this.props.direntList}
|
direntList={this.props.direntList}
|
||||||
updateDirent={this.props.updateDirent}
|
updateDirent={this.props.updateDirent}
|
||||||
updateTreeNode={this.props.updateTreeNode}
|
updateTreeNode={this.props.updateTreeNode}
|
||||||
|
sortTreeNode={this.props.sortTreeNode}
|
||||||
/>
|
/>
|
||||||
<ResizeBar
|
<ResizeBar
|
||||||
resizeBarRef={this.resizeBarRef}
|
resizeBarRef={this.resizeBarRef}
|
||||||
|
@@ -1,11 +1,12 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import cookie from 'react-cookies';
|
||||||
import TreeView from '../tree-view/tree-view';
|
import TreeView from '../tree-view/tree-view';
|
||||||
import ModalPortal from '../modal-portal';
|
import ModalPortal from '../modal-portal';
|
||||||
import ImageDialog from '../dialog/image-dialog';
|
import ImageDialog from '../dialog/image-dialog';
|
||||||
import toaster from '../toast';
|
import toaster from '../toast';
|
||||||
import ItemDropdownMenu from '../dropdown-menu/item-dropdown-menu';
|
import ItemDropdownMenu from '../dropdown-menu/item-dropdown-menu';
|
||||||
import { fileServerRoot, gettext, siteRoot, thumbnailSizeForOriginal, thumbnailDefaultSize } from '../../utils/constants';
|
import { fileServerRoot, gettext, siteRoot, thumbnailSizeForOriginal, thumbnailDefaultSize, SF_DIRECTORY_TREE_SORT_BY_KEY, SF_DIRECTORY_TREE_SORT_ORDER_KEY } from '../../utils/constants';
|
||||||
import { isMobile, Utils } from '../../utils/utils';
|
import { isMobile, Utils } from '../../utils/utils';
|
||||||
import TextTranslation from '../../utils/text-translation';
|
import TextTranslation from '../../utils/text-translation';
|
||||||
import TreeSection from '../tree-section';
|
import TreeSection from '../tree-section';
|
||||||
@@ -31,6 +32,12 @@ const propTypes = {
|
|||||||
onItemMove: PropTypes.func.isRequired,
|
onItemMove: PropTypes.func.isRequired,
|
||||||
onItemsMove: PropTypes.func.isRequired,
|
onItemsMove: PropTypes.func.isRequired,
|
||||||
updateDirent: PropTypes.func,
|
updateDirent: PropTypes.func,
|
||||||
|
sortTreeNode: PropTypes.func,
|
||||||
|
};
|
||||||
|
|
||||||
|
const SORT_KEY_MAP = {
|
||||||
|
'name-asc': TextTranslation.ASCENDING_BY_NAME.key,
|
||||||
|
'name-desc': TextTranslation.DESCENDING_BY_NAME.key,
|
||||||
};
|
};
|
||||||
|
|
||||||
class DirFiles extends React.Component {
|
class DirFiles extends React.Component {
|
||||||
@@ -46,12 +53,20 @@ class DirFiles extends React.Component {
|
|||||||
imageIndex: 0,
|
imageIndex: 0,
|
||||||
operationList: [],
|
operationList: [],
|
||||||
isDisplayFiles: localStorage.getItem('sf_display_files') === 'true' || false,
|
isDisplayFiles: localStorage.getItem('sf_display_files') === 'true' || false,
|
||||||
|
sortKey: TextTranslation.ASCENDING_BY_NAME.key,
|
||||||
};
|
};
|
||||||
this.isNodeMenuShow = true;
|
this.isNodeMenuShow = true;
|
||||||
this.imageItemsSnapshot = [];
|
this.imageItemsSnapshot = [];
|
||||||
this.imageIndexSnapshot = 0;
|
this.imageIndexSnapshot = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const sortBy = cookie.load(SF_DIRECTORY_TREE_SORT_BY_KEY) || 'name';
|
||||||
|
const sortOrder = cookie.load(SF_DIRECTORY_TREE_SORT_ORDER_KEY) || 'asc';
|
||||||
|
const sortKey = SORT_KEY_MAP[`${sortBy}-${sortOrder}`];
|
||||||
|
this.setState({ sortKey });
|
||||||
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
if (prevProps.direntList.length < this.props.direntList.length && this.state.isNodeImagePopupOpen) {
|
if (prevProps.direntList.length < this.props.direntList.length && this.state.isNodeImagePopupOpen) {
|
||||||
if (this.state.imageNodeItems.length === 0) {
|
if (this.state.imageNodeItems.length === 0) {
|
||||||
@@ -73,6 +88,7 @@ class DirFiles extends React.Component {
|
|||||||
|
|
||||||
getMenuList = () => {
|
getMenuList = () => {
|
||||||
const { userPerm } = this.props;
|
const { userPerm } = this.props;
|
||||||
|
const { sortKey } = this.state;
|
||||||
const list = [];
|
const list = [];
|
||||||
if (userPerm == 'rw') {
|
if (userPerm == 'rw') {
|
||||||
list.push(
|
list.push(
|
||||||
@@ -80,7 +96,10 @@ class DirFiles extends React.Component {
|
|||||||
TextTranslation.NEW_FILE
|
TextTranslation.NEW_FILE
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
list.push(TextTranslation.DISPLAY_FILES);
|
list.push({ ...TextTranslation.DISPLAY_FILES, tick: this.state.isDisplayFiles });
|
||||||
|
list.push('Divider');
|
||||||
|
list.push({ ...TextTranslation.ASCENDING_BY_NAME, tick: sortKey === TextTranslation.ASCENDING_BY_NAME.key });
|
||||||
|
list.push({ ...TextTranslation.DESCENDING_BY_NAME, tick: sortKey === TextTranslation.DESCENDING_BY_NAME.key });
|
||||||
return list;
|
return list;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -142,6 +161,16 @@ class DirFiles extends React.Component {
|
|||||||
case 'Display files':
|
case 'Display files':
|
||||||
this.onDisplayFilesToggle();
|
this.onDisplayFilesToggle();
|
||||||
break;
|
break;
|
||||||
|
case 'Ascending by name': {
|
||||||
|
this.props.sortTreeNode('name', 'asc');
|
||||||
|
this.setState({ sortKey: TextTranslation.ASCENDING_BY_NAME.key });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'Descending by name': {
|
||||||
|
this.props.sortTreeNode('name', 'desc');
|
||||||
|
this.setState({ sortKey: TextTranslation.DESCENDING_BY_NAME.key });
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -306,7 +335,6 @@ class DirFiles extends React.Component {
|
|||||||
item={{ name: 'files' }}
|
item={{ name: 'files' }}
|
||||||
toggleClass="sf3-font sf3-font-more"
|
toggleClass="sf3-font sf3-font-more"
|
||||||
menuStyle={isMobile ? { zIndex: 1050 } : {}}
|
menuStyle={isMobile ? { zIndex: 1050 } : {}}
|
||||||
isDisplayFiles={this.state.isDisplayFiles}
|
|
||||||
getMenuList={this.getMenuList}
|
getMenuList={this.getMenuList}
|
||||||
onMenuItemClick={this.onMoreOperationClick}
|
onMenuItemClick={this.onMoreOperationClick}
|
||||||
/>
|
/>
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classnames from 'classnames';
|
|
||||||
import { Dropdown, DropdownMenu, DropdownToggle, DropdownItem } from 'reactstrap';
|
import { Dropdown, DropdownMenu, DropdownToggle, DropdownItem } from 'reactstrap';
|
||||||
import listener from '../context-menu/globalEventListener';
|
import listener from '../context-menu/globalEventListener';
|
||||||
import { gettext } from '../../utils/constants';
|
import { gettext } from '../../utils/constants';
|
||||||
@@ -20,7 +19,6 @@ const propTypes = {
|
|||||||
freezeItem: PropTypes.func,
|
freezeItem: PropTypes.func,
|
||||||
unfreezeItem: PropTypes.func,
|
unfreezeItem: PropTypes.func,
|
||||||
menuStyle: PropTypes.object,
|
menuStyle: PropTypes.object,
|
||||||
isDisplayFiles: PropTypes.bool,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class ItemDropdownMenu extends React.Component {
|
class ItemDropdownMenu extends React.Component {
|
||||||
@@ -279,16 +277,13 @@ class ItemDropdownMenu extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<DropdownItem
|
<DropdownItem
|
||||||
key={index}
|
key={index}
|
||||||
className={classnames({
|
className="position-relative pl-5"
|
||||||
'pl-5': this.props.isDisplayFiles != undefined,
|
|
||||||
'position-relative': this.props.isDisplayFiles
|
|
||||||
})}
|
|
||||||
data-toggle={menuItem.key}
|
data-toggle={menuItem.key}
|
||||||
onClick={this.onMenuItemClick}
|
onClick={this.onMenuItemClick}
|
||||||
onKeyDown={this.onMenuItemKeyDown}
|
onKeyDown={this.onMenuItemKeyDown}
|
||||||
onMouseMove={this.onDropDownMouseMove}
|
onMouseMove={this.onDropDownMouseMove}
|
||||||
>
|
>
|
||||||
{menuItem.key === 'Display files' && this.props.isDisplayFiles && (
|
{menuItem.tick && (
|
||||||
<i className="dropdown-item-tick sf2-icon-tick"></i>
|
<i className="dropdown-item-tick sf2-icon-tick"></i>
|
||||||
)}
|
)}
|
||||||
{menuItem.icon_dom || null}
|
{menuItem.icon_dom || null}
|
||||||
|
@@ -9,7 +9,7 @@ import { Modal } from 'reactstrap';
|
|||||||
import { navigate } from '@gatsbyjs/reach-router';
|
import { navigate } from '@gatsbyjs/reach-router';
|
||||||
import { DndProvider } from 'react-dnd';
|
import { DndProvider } from 'react-dnd';
|
||||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||||
import { gettext, siteRoot, username } from '../../utils/constants';
|
import { gettext, SF_DIRECTORY_TREE_SORT_BY_KEY, SF_DIRECTORY_TREE_SORT_ORDER_KEY, siteRoot, username } from '../../utils/constants';
|
||||||
import { seafileAPI } from '../../utils/seafile-api';
|
import { seafileAPI } from '../../utils/seafile-api';
|
||||||
import { Utils } from '../../utils/utils';
|
import { Utils } from '../../utils/utils';
|
||||||
import { Dirent, FileTag, RepoTag, RepoInfo } from '../../models';
|
import { Dirent, FileTag, RepoTag, RepoInfo } from '../../models';
|
||||||
@@ -2095,7 +2095,9 @@ class LibContentView extends React.Component {
|
|||||||
let direntList = list.map(item => {
|
let direntList = list.map(item => {
|
||||||
return new Dirent(item);
|
return new Dirent(item);
|
||||||
});
|
});
|
||||||
direntList = Utils.sortDirents(direntList, this.state.sortBy, this.state.sortOrder);
|
const sortBy = cookie.load(SF_DIRECTORY_TREE_SORT_BY_KEY) || 'name';
|
||||||
|
const sortOrder = cookie.load(SF_DIRECTORY_TREE_SORT_ORDER_KEY) || 'asc';
|
||||||
|
direntList = Utils.sortDirents(direntList, sortBy, sortOrder);
|
||||||
|
|
||||||
let nodeList = direntList.map(object => {
|
let nodeList = direntList.map(object => {
|
||||||
return new TreeNode({ object });
|
return new TreeNode({ object });
|
||||||
@@ -2186,11 +2188,18 @@ class LibContentView extends React.Component {
|
|||||||
cookie.save('seafile-repo-dir-sort-order', sortOrder);
|
cookie.save('seafile-repo-dir-sort-order', sortOrder);
|
||||||
|
|
||||||
const sortedDirentList = Utils.sortDirents(this.state.direntList, sortBy, sortOrder);
|
const sortedDirentList = Utils.sortDirents(this.state.direntList, sortBy, sortOrder);
|
||||||
const sortedTreeData = treeHelper.sortTreeNodes(this.state.treeData, sortBy, sortOrder);
|
|
||||||
this.setState({
|
this.setState({
|
||||||
sortBy: sortBy,
|
sortBy: sortBy,
|
||||||
sortOrder: sortOrder,
|
sortOrder: sortOrder,
|
||||||
direntList: sortedDirentList,
|
direntList: sortedDirentList,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
sortTreeNode = (sortBy, sortOrder) => {
|
||||||
|
cookie.save(SF_DIRECTORY_TREE_SORT_BY_KEY, sortBy);
|
||||||
|
cookie.save(SF_DIRECTORY_TREE_SORT_ORDER_KEY, sortOrder);
|
||||||
|
const sortedTreeData = treeHelper.sortTreeNodes(this.state.treeData, sortBy, sortOrder);
|
||||||
|
this.setState({
|
||||||
treeData: sortedTreeData,
|
treeData: sortedTreeData,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -2551,6 +2560,7 @@ class LibContentView extends React.Component {
|
|||||||
updateCurrentPath={this.updatePath}
|
updateCurrentPath={this.updatePath}
|
||||||
toggleShowDirentToolbar={this.toggleShowDirentToolbar}
|
toggleShowDirentToolbar={this.toggleShowDirentToolbar}
|
||||||
updateTreeNode={this.updateTreeNode}
|
updateTreeNode={this.updateTreeNode}
|
||||||
|
sortTreeNode={this.sortTreeNode}
|
||||||
/>
|
/>
|
||||||
:
|
:
|
||||||
<div className="message err-tip">{gettext('Folder does not exist.')}</div>
|
<div className="message err-tip">{gettext('Folder does not exist.')}</div>
|
||||||
|
@@ -233,3 +233,6 @@ export const MimetypesKind = {
|
|||||||
export const LARGE_DIALOG_STYLE = { maxWidth: '980px' };
|
export const LARGE_DIALOG_STYLE = { maxWidth: '980px' };
|
||||||
|
|
||||||
export const SF_COLOR_MODE = 'sf_color_mode';
|
export const SF_COLOR_MODE = 'sf_color_mode';
|
||||||
|
|
||||||
|
export const SF_DIRECTORY_TREE_SORT_BY_KEY = 'sf_directory_tree_sort_by';
|
||||||
|
export const SF_DIRECTORY_TREE_SORT_ORDER_KEY = 'sf_directory_tree_sort_order';
|
||||||
|
@@ -278,6 +278,16 @@ const TextTranslation = {
|
|||||||
key: 'New child tag',
|
key: 'New child tag',
|
||||||
value: gettext('New child tag'),
|
value: gettext('New child tag'),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// directory op
|
||||||
|
ASCENDING_BY_NAME: {
|
||||||
|
key: 'Ascending by name',
|
||||||
|
value: gettext('Ascending by name'),
|
||||||
|
},
|
||||||
|
DESCENDING_BY_NAME: {
|
||||||
|
key: 'Descending by name',
|
||||||
|
value: gettext('Descending by name'),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default TextTranslation;
|
export default TextTranslation;
|
||||||
|
Reference in New Issue
Block a user