1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-24 04:48:03 +00:00

sortable directory tree (#8069)

Co-authored-by: zhouwenxuan <aries@Mac.local>
This commit is contained in:
Aries
2025-07-22 13:46:29 +08:00
committed by GitHub
parent cf22d27d4a
commit 37a422d1ef
7 changed files with 65 additions and 15 deletions

View File

@@ -29,7 +29,8 @@ const propTypes = {
getMenuContainerSize: PropTypes.func,
updateDirent: PropTypes.func,
updateTreeNode: PropTypes.func,
updateRepoInfo: PropTypes.func
updateRepoInfo: PropTypes.func,
sortTreeNode: PropTypes.func,
};
class DirColumnNav extends React.Component {
@@ -67,6 +68,7 @@ class DirColumnNav extends React.Component {
onItemsMove={this.props.onItemsMove}
updateDirent={this.props.updateDirent}
updateTreeNode={this.props.updateTreeNode}
sortTreeNode={this.props.sortTreeNode}
/>
<DirViews repoID={repoID} currentPath={currentPath} userPerm={userPerm} currentRepoInfo={currentRepoInfo} />
<DirTags repoID={repoID} currentPath={currentPath} userPerm={userPerm} currentRepoInfo={currentRepoInfo} />

View File

@@ -79,7 +79,8 @@ const propTypes = {
updateCurrentPath: PropTypes.func,
toggleShowDirentToolbar: PropTypes.func,
updateTreeNode: PropTypes.func,
updateRepoInfo: PropTypes.func
updateRepoInfo: PropTypes.func,
sortTreeNode: PropTypes.func,
};
class DirColumnView extends React.Component {
@@ -177,6 +178,7 @@ class DirColumnView extends React.Component {
direntList={this.props.direntList}
updateDirent={this.props.updateDirent}
updateTreeNode={this.props.updateTreeNode}
sortTreeNode={this.props.sortTreeNode}
/>
<ResizeBar
resizeBarRef={this.resizeBarRef}

View File

@@ -1,11 +1,12 @@
import React from 'react';
import PropTypes from 'prop-types';
import cookie from 'react-cookies';
import TreeView from '../tree-view/tree-view';
import ModalPortal from '../modal-portal';
import ImageDialog from '../dialog/image-dialog';
import toaster from '../toast';
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 TextTranslation from '../../utils/text-translation';
import TreeSection from '../tree-section';
@@ -31,6 +32,12 @@ const propTypes = {
onItemMove: PropTypes.func.isRequired,
onItemsMove: PropTypes.func.isRequired,
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 {
@@ -46,12 +53,20 @@ class DirFiles extends React.Component {
imageIndex: 0,
operationList: [],
isDisplayFiles: localStorage.getItem('sf_display_files') === 'true' || false,
sortKey: TextTranslation.ASCENDING_BY_NAME.key,
};
this.isNodeMenuShow = true;
this.imageItemsSnapshot = [];
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) {
if (prevProps.direntList.length < this.props.direntList.length && this.state.isNodeImagePopupOpen) {
if (this.state.imageNodeItems.length === 0) {
@@ -73,6 +88,7 @@ class DirFiles extends React.Component {
getMenuList = () => {
const { userPerm } = this.props;
const { sortKey } = this.state;
const list = [];
if (userPerm == 'rw') {
list.push(
@@ -80,7 +96,10 @@ class DirFiles extends React.Component {
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;
};
@@ -142,6 +161,16 @@ class DirFiles extends React.Component {
case 'Display files':
this.onDisplayFilesToggle();
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' }}
toggleClass="sf3-font sf3-font-more"
menuStyle={isMobile ? { zIndex: 1050 } : {}}
isDisplayFiles={this.state.isDisplayFiles}
getMenuList={this.getMenuList}
onMenuItemClick={this.onMoreOperationClick}
/>

View File

@@ -1,6 +1,5 @@
import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { Dropdown, DropdownMenu, DropdownToggle, DropdownItem } from 'reactstrap';
import listener from '../context-menu/globalEventListener';
import { gettext } from '../../utils/constants';
@@ -20,7 +19,6 @@ const propTypes = {
freezeItem: PropTypes.func,
unfreezeItem: PropTypes.func,
menuStyle: PropTypes.object,
isDisplayFiles: PropTypes.bool,
};
class ItemDropdownMenu extends React.Component {
@@ -279,16 +277,13 @@ class ItemDropdownMenu extends React.Component {
return (
<DropdownItem
key={index}
className={classnames({
'pl-5': this.props.isDisplayFiles != undefined,
'position-relative': this.props.isDisplayFiles
})}
className="position-relative pl-5"
data-toggle={menuItem.key}
onClick={this.onMenuItemClick}
onKeyDown={this.onMenuItemKeyDown}
onMouseMove={this.onDropDownMouseMove}
>
{menuItem.key === 'Display files' && this.props.isDisplayFiles && (
{menuItem.tick && (
<i className="dropdown-item-tick sf2-icon-tick"></i>
)}
{menuItem.icon_dom || null}

View File

@@ -9,7 +9,7 @@ import { Modal } from 'reactstrap';
import { navigate } from '@gatsbyjs/reach-router';
import { DndProvider } from 'react-dnd';
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 { Utils } from '../../utils/utils';
import { Dirent, FileTag, RepoTag, RepoInfo } from '../../models';
@@ -2095,7 +2095,9 @@ class LibContentView extends React.Component {
let direntList = list.map(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 => {
return new TreeNode({ object });
@@ -2186,11 +2188,18 @@ class LibContentView extends React.Component {
cookie.save('seafile-repo-dir-sort-order', sortOrder);
const sortedDirentList = Utils.sortDirents(this.state.direntList, sortBy, sortOrder);
const sortedTreeData = treeHelper.sortTreeNodes(this.state.treeData, sortBy, sortOrder);
this.setState({
sortBy: sortBy,
sortOrder: sortOrder,
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,
});
};
@@ -2551,6 +2560,7 @@ class LibContentView extends React.Component {
updateCurrentPath={this.updatePath}
toggleShowDirentToolbar={this.toggleShowDirentToolbar}
updateTreeNode={this.updateTreeNode}
sortTreeNode={this.sortTreeNode}
/>
:
<div className="message err-tip">{gettext('Folder does not exist.')}</div>

View File

@@ -233,3 +233,6 @@ export const MimetypesKind = {
export const LARGE_DIALOG_STYLE = { maxWidth: '980px' };
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';

View File

@@ -278,6 +278,16 @@ const TextTranslation = {
key: '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;