1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-10 11:21:29 +00:00

Feature/improve interactivity of searched list in move dialog (#6597)

* improve interactivity of search result

* clean up redundant code

* clean up redundant code
This commit is contained in:
Aries
2024-08-21 11:35:50 +08:00
committed by GitHub
parent a8b61af868
commit c08abe8fc1
6 changed files with 137 additions and 24 deletions

View File

@@ -57,6 +57,8 @@ class FileChooser extends React.Component {
searchInfo: '', searchInfo: '',
searchResults: [], searchResults: [],
selectedItemInfo: {}, selectedItemInfo: {},
isBrowsing: false,
browsingPath: '',
}; };
this.inputValue = ''; this.inputValue = '';
this.timer = null; this.timer = null;
@@ -115,6 +117,8 @@ class FileChooser extends React.Component {
this.setState({ this.setState({
isSearching: false, isSearching: false,
isResultGot: false, isResultGot: false,
isBrowsing: false,
browsingPath: '',
searchInfo: '', searchInfo: '',
searchResults: [], searchResults: [],
}); });
@@ -182,8 +186,12 @@ class FileChooser extends React.Component {
this.setState({ this.setState({
isSearching: false, isSearching: false,
isResultGot: false, isResultGot: false,
isBrowsing: false,
browsingPath: '',
searchInfo: '', searchInfo: '',
searchResults: [], searchResults: [],
selectedPath: this.props.currentPath,
selectedItemInfo: {},
}); });
this.inputValue = ''; this.inputValue = '';
this.timer = null; this.timer = null;
@@ -204,7 +212,7 @@ class FileChooser extends React.Component {
} }
this.inputValue = searchInfo; this.inputValue = searchInfo;
if (this.inputValue === '' || this.getValueLength(this.inputValue) < 3) { if (this.inputValue === '') {
this.setState({ this.setState({
isResultGot: false, isResultGot: false,
}); });
@@ -242,18 +250,27 @@ class FileChooser extends React.Component {
}; };
sendRequest = (queryData, cancelToken) => { sendRequest = (queryData, cancelToken) => {
const filterCurrentRepo = (results) => {
if (this.props.mode === 'only_other_libraries') {
return results.filter(item => item.repo_id !== this.state.currentRepoInfo.repo_id);
}
return results;
};
if (isPro && enableSeasearch && !enableElasticsearch) { if (isPro && enableSeasearch && !enableElasticsearch) {
seafileAPI.aiSearchFiles(queryData, cancelToken).then(res => { seafileAPI.aiSearchFiles(queryData, cancelToken).then(res => {
const filteredResults = filterCurrentRepo(res.data.results.filter(item => item.is_dir));
this.setState({ this.setState({
searchResults: res.data.results.length > 0 ? this.formatResultItems(res.data.results.filter(item => item.is_dir)) : [], searchResults: filteredResults.length > 0 ? this.formatResultItems(filteredResults) : [],
isResultGot: true isResultGot: true
}); });
this.source = null; this.source = null;
}); });
} else { } else {
seafileAPI.searchFiles(queryData, cancelToken).then(res => { seafileAPI.searchFiles(queryData, cancelToken).then(res => {
const filteredResults = filterCurrentRepo(res.data.results);
this.setState({ this.setState({
searchResults: res.data.total ? this.formatResultItems(res.data.results) : [], searchResults: res.data.total ? this.formatResultItems(filteredResults) : [],
isResultGot: true isResultGot: true
}); });
this.source = null; this.source = null;
@@ -306,10 +323,6 @@ class FileChooser extends React.Component {
}; };
renderSearchedView = () => { renderSearchedView = () => {
if (this.getValueLength(this.inputValue) < 3) {
return '';
}
if (!this.state.isResultGot) { if (!this.state.isResultGot) {
return (<Loading />); return (<Loading />);
} }
@@ -409,7 +422,16 @@ class FileChooser extends React.Component {
} }
} }
this.onCloseSearching(); this.setState({
isSearching: false,
isResultGot: false,
searchResults: [],
isBrowsing: true,
browsingPath: item.path.substring(0, item.path.length - 1),
});
this.inputValue = '';
this.timer = null;
this.source = null;
}; };
onScroll = (event) => { onScroll = (event) => {
event.stopPropagation(); event.stopPropagation();
@@ -481,6 +503,8 @@ class FileChooser extends React.Component {
isShowFile={isShowFile} isShowFile={isShowFile}
fileSuffixes={fileSuffixes} fileSuffixes={fileSuffixes}
selectedItemInfo={selectedItemInfo} selectedItemInfo={selectedItemInfo}
isBrowsing={this.state.isBrowsing}
browsingPath={this.state.browsingPath}
/> />
</div> </div>
)} )}
@@ -517,6 +541,8 @@ class FileChooser extends React.Component {
isShowFile={isShowFile} isShowFile={isShowFile}
fileSuffixes={fileSuffixes} fileSuffixes={fileSuffixes}
selectedItemInfo={selectedItemInfo} selectedItemInfo={selectedItemInfo}
isBrowsing={this.state.isBrowsing}
browsingPath={this.state.browsingPath}
/> />
</div> </div>
)} )}
@@ -534,7 +560,7 @@ class FileChooser extends React.Component {
}; };
render() { render() {
const { repoID } = this.props; const { repoID, mode } = this.props;
const { selectedRepo, searchInfo, isSearching } = this.state; const { selectedRepo, searchInfo, isSearching } = this.state;
if (!selectedRepo && repoID) { if (!selectedRepo && repoID) {
@@ -543,7 +569,7 @@ class FileChooser extends React.Component {
return ( return (
<Fragment> <Fragment>
{isPro && this.props.mode !== 'recently_used' && ( {isPro && mode !== 'recently_used' && (
<div className="file-chooser-search-input"> <div className="file-chooser-search-input">
<Input className="search-input" placeholder={gettext('Search')} type='text' value={searchInfo} onChange={this.onSearchInfoChanged}></Input> <Input className="search-input" placeholder={gettext('Search')} type='text' value={searchInfo} onChange={this.onSearchInfoChanged}></Input>
{searchInfo.length !== 0 && ( {searchInfo.length !== 0 && (

View File

@@ -21,6 +21,8 @@ const propTypes = {
fileSuffixes: PropTypes.array, fileSuffixes: PropTypes.array,
selectedItemInfo: PropTypes.object, selectedItemInfo: PropTypes.object,
hideLibraryName: PropTypes.bool, hideLibraryName: PropTypes.bool,
isBrowsing: PropTypes.bool,
browsingPath: PropTypes.string,
}; };
class RepoListItem extends React.Component { class RepoListItem extends React.Component {
@@ -63,6 +65,30 @@ class RepoListItem extends React.Component {
} }
} }
componentDidUpdate(prevProps) {
if (prevProps.isBrowsing && !this.props.isBrowsing) {
this.setState({
treeData: treeHelper.buildTree(),
isShowChildren: this.props.initToShowChildren,
});
const { isCurrentRepo, currentPath, repo, selectedItemInfo } = this.props;
const { repoID } = selectedItemInfo || {};
if (isCurrentRepo && !repoID) {
this.loadRepoDirentList(repo);
setTimeout(() => {
const repoID = repo.repo_id;
if (isCurrentRepo && currentPath && currentPath != '/') {
const expandNode = true;
this.loadNodeAndParentsByPath(repoID, currentPath, expandNode);
}
}, 0);
}
}
}
componentWillUnmount() { componentWillUnmount() {
this.setState({ isMounted: false }); this.setState({ isMounted: false });
} }
@@ -205,7 +231,7 @@ class RepoListItem extends React.Component {
return ( return (
<li> <li>
{!this.props.hideLibraryName && {!this.props.hideLibraryName && !this.props.isBrowsing &&
<div className={`${repoActive ? 'item-active' : ''} item-info`} onClick={this.onRepoItemClick}> <div className={`${repoActive ? 'item-active' : ''} item-info`} onClick={this.onRepoItemClick}>
<div className="item-left-icon"> <div className="item-left-icon">
<span className={`item-toggle icon sf3-font ${this.state.isShowChildren ? 'sf3-font-down' : 'sf3-font-down rotate-270 d-inline-block'}`} onClick={this.onToggleClick}></span> <span className={`item-toggle icon sf3-font ${this.state.isShowChildren ? 'sf3-font-down' : 'sf3-font-down rotate-270 d-inline-block'}`} onClick={this.onToggleClick}></span>
@@ -228,6 +254,8 @@ class RepoListItem extends React.Component {
treeData={this.state.treeData} treeData={this.state.treeData}
onNodeCollapse={this.onNodeCollapse} onNodeCollapse={this.onNodeCollapse}
onNodeExpanded={this.onNodeExpanded} onNodeExpanded={this.onNodeExpanded}
isBrowsing={this.props.isBrowsing}
browsingPath={this.props.browsingPath}
/> />
)} )}
</li> </li>

View File

@@ -15,6 +15,8 @@ const propTypes = {
fileSuffixes: PropTypes.array, fileSuffixes: PropTypes.array,
selectedItemInfo: PropTypes.object, selectedItemInfo: PropTypes.object,
currentPath: PropTypes.string, currentPath: PropTypes.string,
isBrowsing: PropTypes.bool,
browsingPath: PropTypes.string,
}; };
class RepoListView extends React.Component { class RepoListView extends React.Component {
@@ -43,6 +45,8 @@ class RepoListView extends React.Component {
isShowFile={this.props.isShowFile} isShowFile={this.props.isShowFile}
fileSuffixes={this.props.fileSuffixes} fileSuffixes={this.props.fileSuffixes}
selectedItemInfo={this.props.selectedItemInfo} selectedItemInfo={this.props.selectedItemInfo}
isBrowsing={this.props.isBrowsing}
browsingPath={this.props.browsingPath}
/> />
); );
})} })}

View File

@@ -13,12 +13,44 @@ class SearchedListView extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
currentItem: null, currentItem: props.searchResults.length > 0 ? props.searchResults[0] : null,
currentIndex: props.searchResults.length > 0 ? 0 : -1,
}; };
this.itemRef = React.createRef();
} }
onItemClick = (item) => { componentDidMount() {
this.setState({ currentItem: item }); document.addEventListener('keydown', this.handleKeyDown);
}
componentWillUnmount() {
document.removeEventListener('keydown', this.handleKeyDown);
}
handleKeyDown = (event) => {
const { searchResults } = this.props;
const { currentIndex } = this.state;
if (event.key === 'ArrowDown') {
const nextIndex = (currentIndex + 1) % searchResults.length;
this.setState({
currentItem: searchResults[nextIndex],
currentIndex: nextIndex,
});
} else if (event.key === 'ArrowUp') {
const prevIndex = (currentIndex - 1 + searchResults.length) % searchResults.length;
this.setState({
currentItem: searchResults[prevIndex],
currentIndex: prevIndex,
});
} else if (event.key === 'Enter') {
this.onItemClick(searchResults[currentIndex], currentIndex);
this.props.onSearchedItemDoubleClick(searchResults[currentIndex]);
}
};
onItemClick = (item, index) => {
this.setState({ currentItem: item, currentIndex: index });
this.props.onItemClick(item); this.props.onItemClick(item);
}; };
@@ -36,9 +68,10 @@ class SearchedListView extends React.Component {
return ( return (
<SearchedListItem <SearchedListItem
key={index} key={index}
ref={this.itemRef}
item={item} item={item}
currentItem={this.state.currentItem} currentItem={this.state.currentItem}
onItemClick={this.onItemClick} onItemClick={() => this.onItemClick(item, index)}
onSearchedItemDoubleClick={this.props.onSearchedItemDoubleClick} onSearchedItemDoubleClick={this.props.onSearchedItemDoubleClick}
/> />
); );

View File

@@ -12,6 +12,7 @@ const propTypes = {
filePath: PropTypes.string, filePath: PropTypes.string,
fileSuffixes: PropTypes.array, fileSuffixes: PropTypes.array,
level: PropTypes.number, level: PropTypes.number,
isBrowsing: PropTypes.bool,
}; };
class TreeViewItem extends React.Component { class TreeViewItem extends React.Component {
@@ -96,7 +97,7 @@ class TreeViewItem extends React.Component {
if (this.props.selectedRepo) { if (this.props.selectedRepo) {
isCurrentRepo = this.props.selectedRepo.repo_id === this.props.repo.repo_id; isCurrentRepo = this.props.selectedRepo.repo_id === this.props.repo.repo_id;
} }
let isCurrentPath = this.props.selectedPath === this.state.filePath; let isCurrentPath = this.props.selectedPath === this.state.filePath || this.props.selectedPath === node.path;
const fileName = node.object.name; const fileName = node.object.name;
if (this.props.fileSuffixes && fileName && node.object.type === 'file') { if (this.props.fileSuffixes && fileName && node.object.type === 'file') {

View File

@@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import TreeListItem from './tree-list-item'; import TreeListItem from './tree-list-item';
import treeHelper from '../tree-view/tree-helper';
const propTypes = { const propTypes = {
selectedPath: PropTypes.string, selectedPath: PropTypes.string,
@@ -11,22 +12,42 @@ const propTypes = {
onNodeCollapse: PropTypes.func.isRequired, onNodeCollapse: PropTypes.func.isRequired,
onNodeExpanded: PropTypes.func.isRequired, onNodeExpanded: PropTypes.func.isRequired,
fileSuffixes: PropTypes.array, fileSuffixes: PropTypes.array,
isBrowsing: PropTypes.bool,
browsingPath: PropTypes.string,
}; };
class TreeListView extends React.Component { class TreeListView extends React.Component {
render() { render() {
const {
isBrowsing,
browsingPath,
treeData,
selectedPath,
onNodeCollapse,
onNodeExpanded,
repo,
onDirentItemClick,
selectedRepo,
fileSuffixes
} = this.props;
const browsingNode = treeHelper.findNodeByPath(treeData, browsingPath);
if (isBrowsing && !browsingNode) return null;
const node = isBrowsing ? browsingNode : treeData.root;
return ( return (
<div className="list-view-content"> <div className="list-view-content">
<TreeListItem <TreeListItem
node={this.props.treeData.root} node={node}
onNodeCollapse={this.props.onNodeCollapse} onNodeCollapse={onNodeCollapse}
onNodeExpanded={this.props.onNodeExpanded} onNodeExpanded={onNodeExpanded}
repo={this.props.repo} repo={repo}
onDirentItemClick={this.props.onDirentItemClick} onDirentItemClick={onDirentItemClick}
selectedRepo={this.props.selectedRepo} selectedRepo={selectedRepo}
selectedPath={this.props.selectedPath} selectedPath={selectedPath}
fileSuffixes={this.props.fileSuffixes} fileSuffixes={fileSuffixes}
level={0} level={0}
/> />
</div> </div>