From 37a422d1ef5a576799c84ce203a57969e135cc2a Mon Sep 17 00:00:00 2001 From: Aries Date: Tue, 22 Jul 2025 13:46:29 +0800 Subject: [PATCH] sortable directory tree (#8069) Co-authored-by: zhouwenxuan --- .../dir-view-mode/dir-column-nav/index.js | 4 ++- .../dir-view-mode/dir-column-view.js | 4 ++- .../src/components/dir-view-mode/dir-files.js | 34 +++++++++++++++++-- .../dropdown-menu/item-dropdown-menu.js | 9 ++--- .../lib-content-view/lib-content-view.js | 16 +++++++-- frontend/src/utils/constants.js | 3 ++ frontend/src/utils/text-translation.js | 10 ++++++ 7 files changed, 65 insertions(+), 15 deletions(-) diff --git a/frontend/src/components/dir-view-mode/dir-column-nav/index.js b/frontend/src/components/dir-view-mode/dir-column-nav/index.js index 582b4ea60c..a6bdb533c4 100644 --- a/frontend/src/components/dir-view-mode/dir-column-nav/index.js +++ b/frontend/src/components/dir-view-mode/dir-column-nav/index.js @@ -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} /> diff --git a/frontend/src/components/dir-view-mode/dir-column-view.js b/frontend/src/components/dir-view-mode/dir-column-view.js index ac179188f2..bbb4cbf0ad 100644 --- a/frontend/src/components/dir-view-mode/dir-column-view.js +++ b/frontend/src/components/dir-view-mode/dir-column-view.js @@ -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} /> { 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} /> diff --git a/frontend/src/components/dropdown-menu/item-dropdown-menu.js b/frontend/src/components/dropdown-menu/item-dropdown-menu.js index 17eda13c3a..7b624d8ca5 100644 --- a/frontend/src/components/dropdown-menu/item-dropdown-menu.js +++ b/frontend/src/components/dropdown-menu/item-dropdown-menu.js @@ -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 ( - {menuItem.key === 'Display files' && this.props.isDisplayFiles && ( + {menuItem.tick && ( )} {menuItem.icon_dom || null} diff --git a/frontend/src/pages/lib-content-view/lib-content-view.js b/frontend/src/pages/lib-content-view/lib-content-view.js index 34067c9cf4..c778f3ab97 100644 --- a/frontend/src/pages/lib-content-view/lib-content-view.js +++ b/frontend/src/pages/lib-content-view/lib-content-view.js @@ -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} /> :
{gettext('Folder does not exist.')}
diff --git a/frontend/src/utils/constants.js b/frontend/src/utils/constants.js index 357dacd8f6..f2dcda7803 100644 --- a/frontend/src/utils/constants.js +++ b/frontend/src/utils/constants.js @@ -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'; diff --git a/frontend/src/utils/text-translation.js b/frontend/src/utils/text-translation.js index c02c8286fb..c99466d2ef 100644 --- a/frontend/src/utils/text-translation.js +++ b/frontend/src/utils/text-translation.js @@ -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;