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:
@@ -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 && (
|
||||||
|
@@ -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>
|
||||||
|
@@ -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}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
@@ -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}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@@ -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') {
|
||||||
|
@@ -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>
|
||||||
|
Reference in New Issue
Block a user